diff --git a/Cargo.lock b/Cargo.lock index d8c76e5..6ca13cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1390,9 +1390,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "6feda4ab8376c63216ba34f883b3ebdc1b610dcb47b7bf4f4b5871323d996040" [[package]] name = "httpdate" @@ -1460,6 +1460,124 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f8ac670d7422d7f76b32e17a5db556510825b29ec9154f235977c9caba61036" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -1468,12 +1586,14 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.5.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "4716a3a0933a1d01c2f72450e89596eb51dd34ef3c211ccd875acdf1f8fe47ed" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "icu_normalizer", + "icu_properties", + "smallvec", + "utf8_iter", ] [[package]] @@ -1630,6 +1750,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "litemap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" + [[package]] name = "lnk" version = "0.5.1" @@ -2683,6 +2809,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" @@ -2757,6 +2889,17 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "system-configuration" version = "0.5.1" @@ -2905,20 +3048,15 @@ dependencies = [ ] [[package]] -name = "tinyvec" -version = "1.6.0" +name = "tinystr" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ - "tinyvec_macros", + "displaydoc", + "zerovec", ] -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "tokio" version = "1.38.0" @@ -3024,27 +3162,12 @@ dependencies = [ "version_check", ] -[[package]] -name = "unicode-bidi" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" - [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" -[[package]] -name = "unicode-normalization" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" -dependencies = [ - "tinyvec", -] - [[package]] name = "unicode-width" version = "0.1.13" @@ -3053,15 +3176,27 @@ checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "url" -version = "2.5.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "f7c25da092f0a868cdf09e8674cd3b7ef3a7d92a24253e663a2fb85e2496de56" dependencies = [ "form_urlencoded", "idna", "percent-encoding", ] +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -3471,6 +3606,18 @@ dependencies = [ "thiserror", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "yansi" version = "0.5.1" @@ -3483,6 +3630,30 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" +[[package]] +name = "yoke" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.7.34" @@ -3503,6 +3674,27 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "zerofrom" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", + "synstructure", +] + [[package]] name = "zeroize" version = "1.8.1" @@ -3523,6 +3715,28 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "zerovec" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2cc8827d6c0994478a15c53f374f46fbd41bea663d809b14744bc42e6b109c" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97cf56601ee5052b4417d90c8755c6683473c926039908196cf35d99f893ebe7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "zip" version = "2.1.3" diff --git a/src/bin/mactime2/application.rs b/src/bin/mactime2/application.rs index e4fffcd..05d57e8 100644 --- a/src/bin/mactime2/application.rs +++ b/src/bin/mactime2/application.rs @@ -40,7 +40,6 @@ pub (crate) enum OutputFormat { pub struct Mactime2Application { format: OutputFormat, bodyfile: Input, - src_zone: Tz, dst_zone: Tz, strict_mode: bool, } @@ -52,7 +51,6 @@ impl Mactime2Application { ) -> Box>> { let options = RunOptions { strict_mode: self.strict_mode, - src_zone: self.src_zone, }; if matches!(self.format, OutputFormat::Json) { @@ -62,8 +60,8 @@ impl Mactime2Application { BodyfileSorter::default().with_receiver(decoder.get_receiver(), options); sorter = sorter.with_output(match self.format { - OutputFormat::Csv => Box::new(CsvOutput::new(self.src_zone, self.dst_zone)), - OutputFormat::Txt => Box::new(TxtOutput::new(self.src_zone, self.dst_zone)), + OutputFormat::Csv => Box::new(CsvOutput::new(self.dst_zone)), + OutputFormat::Txt => Box::new(TxtOutput::new(self.dst_zone)), _ => panic!("invalid execution path"), }); Box::new(sorter) @@ -73,7 +71,6 @@ impl Mactime2Application { pub fn run(&self) -> anyhow::Result<()> { let options = RunOptions { strict_mode: self.strict_mode, - src_zone: self.src_zone, }; let mut reader = >::from(self.bodyfile.clone())?; @@ -106,7 +103,6 @@ impl From for Mactime2Application { Self { format, bodyfile: cli.input_file, - src_zone: cli.src_zone.into_tz().unwrap(), dst_zone: cli.dst_zone.into_tz().unwrap(), strict_mode: cli.strict_mode, } diff --git a/src/bin/mactime2/cli.rs b/src/bin/mactime2/cli.rs index ce87c36..6f2211f 100644 --- a/src/bin/mactime2/cli.rs +++ b/src/bin/mactime2/cli.rs @@ -13,9 +13,17 @@ const BODYFILE_HELP: &str = #[cfg(not(feature = "gzip"))] const BODYFILE_HELP: &str = "path to input file or '-' for stdin"; -/// replacement for `mactime` +/// Replacement for `mactime` #[derive(Parser)] -#[clap(name="mactime2", author, version, long_about = None)] +#[clap(name="mactime2", author, version, long_about = None, after_help= +r##" +╭────────────────────────────────────────────────────────────────────────────╮ +│IMPORTANT: │ +│ │ +│Note that POSIX specifies that all UNIX timestamps are UTC timestamps. It is│ +│up to you to ensure that the bodyfile only contains UNIX timestamps that │ +│comply with the POSIX standard. │ +╰────────────────────────────────────────────────────────────────────────────╯"##)] pub struct Cli { #[clap(short('b'), value_parser, value_hint=ValueHint::FilePath, default_value="-", help=BODYFILE_HELP, display_order(100))] @@ -40,10 +48,6 @@ pub struct Cli { #[clap(short('j'), display_order(620))] pub(crate) json_format: bool, - /// name of offset of source timezone (or 'list' to display all possible values - #[clap(short('f'), long("from-timezone"), display_order(300), default_value_t=TzArgument::Tz(Tz::UTC))] - pub src_zone: TzArgument, - /// name of offset of destination timezone (or 'list' to display all possible values #[clap(short('t'), long("to-timezone"), display_order(400), default_value_t=TzArgument::Tz(Tz::UTC))] pub dst_zone: TzArgument, diff --git a/src/bin/mactime2/filter.rs b/src/bin/mactime2/filter.rs index bd186a0..29b677a 100644 --- a/src/bin/mactime2/filter.rs +++ b/src/bin/mactime2/filter.rs @@ -1,11 +1,8 @@ use std::sync::mpsc::{Sender, Receiver}; -use chrono_tz::Tz; - #[derive(Copy, Clone)] pub struct RunOptions { pub strict_mode: bool, - pub src_zone: Tz } pub trait Provider: Joinable { diff --git a/src/bin/mactime2/main.rs b/src/bin/mactime2/main.rs index 55769d3..34923e3 100644 --- a/src/bin/mactime2/main.rs +++ b/src/bin/mactime2/main.rs @@ -15,12 +15,11 @@ use dfir_toolkit::common::{FancyParser, TzArgument}; fn main() -> Result<()> { let cli: Cli = Cli::parse_cli(); - if cli.src_zone.is_list() || cli.dst_zone.is_list() { + if cli.dst_zone.is_list() { TzArgument::display_zones(); return Ok(()); } debug_assert!(cli.dst_zone.is_tz()); - debug_assert!(cli.src_zone.is_tz()); let app: Mactime2Application = cli.into(); diff --git a/src/bin/mactime2/output/csv_output.rs b/src/bin/mactime2/output/csv_output.rs index 7bf74c7..4347b4c 100644 --- a/src/bin/mactime2/output/csv_output.rs +++ b/src/bin/mactime2/output/csv_output.rs @@ -4,19 +4,18 @@ use dfir_toolkit::common::ForensicsTimestamp; use crate::bodyfile::{ListEntry, Mactime2Writer}; pub(crate) struct CsvOutput { - src_zone: Tz, dst_zone: Tz, } impl CsvOutput { - pub fn new(src_zone: Tz, dst_zone: Tz) -> Self { - Self { src_zone, dst_zone } + pub fn new(dst_zone: Tz) -> Self { + Self { dst_zone } } } impl Mactime2Writer for CsvOutput { fn fmt(&self, timestamp: &i64, entry: &ListEntry) -> String { - let timestamp = ForensicsTimestamp::new(*timestamp, self.src_zone, self.dst_zone); + let timestamp = ForensicsTimestamp::from(*timestamp).with_timezone(self.dst_zone); format!( "{},{},{},{},{},{},{},\"{}\"", timestamp, @@ -52,7 +51,7 @@ mod tests { #[allow(non_snake_case)] #[test] fn test_correct_ts_UTC() { - let output = CsvOutput::new(Tz::UTC, Tz::UTC); + let output = CsvOutput::new(Tz::UTC); for _ in 1..10 { let unix_ts = rand::random::() as i64; let bf_line = Bodyfile3Line::new().with_crtime(unix_ts.into()); @@ -63,14 +62,12 @@ mod tests { let out_line = output.fmt(&unix_ts, &entry); let out_ts = out_line.split(',').next().unwrap(); - let rfc3339 = DateTime::parse_from_rfc3339(out_ts).expect(out_ts); + let rfc3339 = DateTime::parse_from_rfc3339(out_ts) + .expect(out_ts) + .timestamp(); assert_eq!( - unix_ts, - rfc3339.timestamp(), - "Timestamp {} converted to '{}' and back to {}", - unix_ts, - out_ts, - rfc3339.timestamp() + unix_ts, rfc3339, + "Timestamp {unix_ts} converted to '{out_ts}' and back to {rfc3339}", ); } } @@ -80,7 +77,7 @@ mod tests { fn test_correct_ts_random_tz() -> Result<(), String> { for _ in 1..100 { let tz = random_tz(); - let output = CsvOutput::new(tz, tz); + let output = CsvOutput::new(tz); let unix_ts = rand::random::() as i64; let bf_line = Bodyfile3Line::new().with_crtime(unix_ts.into()); let entry = ListEntry { @@ -94,12 +91,10 @@ mod tests { Ok(ts) => ts, Err(e) => return Err(format!("error while parsing '{}': {}", out_ts, e)), }; - let offset = rfc3339.offset().local_minus_utc() as i64; - let calculated_ts = rfc3339.timestamp() + offset; + let calculated_ts = rfc3339.timestamp(); assert_eq!( unix_ts, calculated_ts, - "Timestamp {} converted to '{}' and back to {} (offset was {}s)", - unix_ts, out_ts, calculated_ts, offset + "Timestamp {unix_ts} converted to '{out_ts}' and back to {calculated_ts}", ); } Ok(()) diff --git a/src/bin/mactime2/output/json_sorter.rs b/src/bin/mactime2/output/json_sorter.rs index a001a1b..a80b754 100644 --- a/src/bin/mactime2/output/json_sorter.rs +++ b/src/bin/mactime2/output/json_sorter.rs @@ -5,7 +5,6 @@ use std::{ thread::JoinHandle, }; -use chrono_tz::Tz; use dfir_toolkit::{ common::bodyfile::Bodyfile3Line, es4forensics::{objects::PosixFile, Timestamp, TimelineObject}, @@ -20,7 +19,6 @@ use crate::{ pub struct JsonSorter { worker: Option>>, receiver: Option>, - src_zone: Tz, } impl Joinable> for JsonSorter { @@ -30,11 +28,10 @@ impl Joinable> for JsonSorter { } impl Consumer for JsonSorter { - fn with_receiver(previous: Receiver, options: RunOptions) -> Self { + fn with_receiver(previous: Receiver, _options: RunOptions) -> Self { Self { receiver: Some(previous), worker: None, - src_zone: options.src_zone, } } } @@ -45,9 +42,8 @@ impl Runnable for JsonSorter { .receiver .take() .expect("no receiver provided; please call with_receiver()"); - let src_zone = self.src_zone; self.worker = Some(std::thread::spawn(move || { - Self::json_worker(receiver, src_zone) + Self::json_worker(receiver) })); } } @@ -55,7 +51,7 @@ impl Runnable for JsonSorter { impl Sorter> for JsonSorter {} impl JsonSorter { - fn json_worker(decoder: Receiver, src_zone: Tz) -> Result<(), MactimeError> { + fn json_worker(decoder: Receiver) -> Result<(), MactimeError> { let mut entries: BTreeMap> = BTreeMap::new(); loop { let line = Arc::new(match decoder.recv() { @@ -66,7 +62,7 @@ impl JsonSorter { }); let bfline: &Bodyfile3Line = line.borrow(); - let pf = PosixFile::try_from((bfline, &src_zone)).unwrap(); + let pf = PosixFile::try_from(bfline).unwrap(); let lines: Vec<(Timestamp, String)> = pf .into_tuples() diff --git a/src/bin/mactime2/output/txt_output.rs b/src/bin/mactime2/output/txt_output.rs index 97330eb..d108d9a 100644 --- a/src/bin/mactime2/output/txt_output.rs +++ b/src/bin/mactime2/output/txt_output.rs @@ -5,16 +5,14 @@ use std::cell::RefCell; use crate::bodyfile::{ListEntry, Mactime2Writer}; pub struct TxtOutput { - src_zone: Tz, dst_zone: Tz, last_ts: (RefCell, RefCell), empty_ts: RefCell, } impl TxtOutput { - pub fn new(src_zone: Tz, dst_zone: Tz) -> Self { + pub fn new(dst_zone: Tz) -> Self { Self { - src_zone, dst_zone, last_ts: (RefCell::new(i64::MIN), RefCell::new("".to_owned())), empty_ts: RefCell::new(" ".to_owned()), @@ -25,8 +23,9 @@ impl TxtOutput { impl Mactime2Writer for TxtOutput { fn fmt(&self, timestamp: &i64, entry: &ListEntry) -> String { let ts = if *timestamp != *self.last_ts.0.borrow() { - *self.last_ts.1.borrow_mut() = - ForensicsTimestamp::new(*timestamp, self.src_zone, self.dst_zone).to_string(); + *self.last_ts.1.borrow_mut() = ForensicsTimestamp::from(*timestamp) + .with_timezone(self.dst_zone) + .to_string(); *self.last_ts.0.borrow_mut() = *timestamp; self.last_ts.1.borrow() } else { @@ -65,7 +64,7 @@ mod tests { #[allow(non_snake_case)] #[test] fn test_correct_ts_UTC() { - let output = TxtOutput::new(Tz::UTC, Tz::UTC); + let output = TxtOutput::new(Tz::UTC); for _ in 1..10 { let unix_ts = rand::random::() as i64; let bf_line = Bodyfile3Line::new().with_crtime(Created::from(unix_ts)); @@ -79,14 +78,12 @@ mod tests { assert!(out_line2.starts_with(' ')); let out_ts = out_line.split(' ').next().unwrap(); - let rfc3339 = DateTime::parse_from_rfc3339(out_ts).expect(out_ts); + let rfc3339 = DateTime::parse_from_rfc3339(out_ts) + .expect(out_ts) + .timestamp(); assert_eq!( - unix_ts, - rfc3339.timestamp(), - "Timestamp {} converted to '{}' and back to {}", - unix_ts, - out_ts, - rfc3339.timestamp() + unix_ts, rfc3339, + "Timestamp {unix_ts} converted to '{out_ts}' and back to {rfc3339}", ); } } @@ -96,7 +93,7 @@ mod tests { fn test_correct_ts_random_tz() -> Result<(), String> { for _ in 1..100 { let tz = random_tz(); - let output = TxtOutput::new(tz, tz); + let output = TxtOutput::new(tz); let unix_ts = rand::random::() as i64; let bf_line = Bodyfile3Line::new().with_crtime(Created::from(unix_ts)); let entry = ListEntry { @@ -113,12 +110,10 @@ mod tests { Ok(ts) => ts, Err(e) => return Err(format!("error while parsing '{}': {}", out_ts, e)), }; - let offset = rfc3339.offset().local_minus_utc() as i64; - let calculated_ts = rfc3339.timestamp() + offset; + let calculated_ts = rfc3339.timestamp(); assert_eq!( unix_ts, calculated_ts, - "Timestamp {} converted to '{}' and back to {} (offset was {}s)", - unix_ts, out_ts, calculated_ts, offset + "Timestamp {unix_ts} converted to '{out_ts}' and back to {calculated_ts}", ); } Ok(()) diff --git a/src/bin/ts2date/main.rs b/src/bin/ts2date/main.rs index b60c7f8..3d5ea71 100644 --- a/src/bin/ts2date/main.rs +++ b/src/bin/ts2date/main.rs @@ -28,8 +28,7 @@ fn main() -> Result<()> { let out = match re.captures(&content) { Some(caps) => { - //let ndt = NaiveDateTime::from_timestamp_opt(caps.name("ts").unwrap().as_str().parse::().unwrap(),0).unwrap(); - let ts = ForensicsTimestamp::new(caps.name("ts").unwrap().as_str().parse::().unwrap(),cli.src_zone.into_tz().unwrap(), cli.dst_zone.into_tz().unwrap()); + let ts = ForensicsTimestamp::from(caps.name("ts").unwrap().as_str().parse::().unwrap()).with_timezone(cli.dst_zone.into_tz().unwrap()); format!("{}{}{}", caps.name("lhs").unwrap().as_str(), ts, caps.name("rhs").unwrap().as_str()) diff --git a/src/common/bodyfile/bodyfile3.rs b/src/common/bodyfile/bodyfile3.rs index 009c810..e21440e 100644 --- a/src/common/bodyfile/bodyfile3.rs +++ b/src/common/bodyfile/bodyfile3.rs @@ -75,7 +75,7 @@ impl Bodyfile3Line { [with_mode] [mode_as_string]; )] pub fn method_name(mut self, attribute_name: &str) -> Self { - self.attribute_name = attribute_name.to_owned(); + attribute_name.clone_into(&mut self.attribute_name); self } diff --git a/src/common/forensics_timestamp.rs b/src/common/forensics_timestamp.rs index 85eed29..2b7e09e 100644 --- a/src/common/forensics_timestamp.rs +++ b/src/common/forensics_timestamp.rs @@ -2,7 +2,7 @@ use std::fmt::Display; use chrono::format::StrftimeItems; use chrono::offset::TimeZone; -use chrono::{DateTime, FixedOffset}; +use chrono::{DateTime, FixedOffset, Utc}; use chrono_tz::Tz; use lazy_static::lazy_static; @@ -35,20 +35,40 @@ lazy_static! { } pub struct ForensicsTimestamp { - unix_ts: i64, - src_zone: Tz, + timestamp: DateTime, dst_zone: Tz, } +impl From for ForensicsTimestamp { + fn from(value: i64) -> Self { + let timestamp = match DateTime::from_timestamp(value, 0) { + Some(ts) => ts, + None => panic!("unable to convert '{value}' into unix timestamp"), + }; + Self { + timestamp, + dst_zone: Tz::UTC, + } + } +} + impl ForensicsTimestamp { - pub fn new(unix_ts: i64, src_zone: Tz, dst_zone: Tz) -> Self { + pub fn new(unix_ts: i64, dst_zone: Tz) -> Self { + let timestamp = match DateTime::from_timestamp(unix_ts, 0) { + Some(ts) => ts, + None => panic!("unable to convert '{unix_ts}' into unix timestamp"), + }; Self { - unix_ts, - src_zone, + timestamp, dst_zone, } } + pub fn with_timezone(mut self, dst_zone: Tz) -> Self { + self.dst_zone = dst_zone; + self + } + fn display_datetime( dt: &DateTime, f: &mut std::fmt::Formatter<'_>, @@ -65,17 +85,7 @@ impl ForensicsTimestamp { impl Display for ForensicsTimestamp { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if self.unix_ts >= 0 { - let src_timestamp = match DateTime::from_timestamp(1715845546, 0) { - Some(ts) => ts, - None => panic!("unable to convert '{}' into unix timestamp", self.unix_ts), - } - .with_timezone(&self.src_zone); - - Self::display_datetime(&src_timestamp.with_timezone(&self.dst_zone), f) - } else { - Self::display_datetime(&*ZERO, f) - } + Self::display_datetime(&self.timestamp.with_timezone(&self.dst_zone), f) } } @@ -87,10 +97,10 @@ mod tests { #[test] fn test_time_import() { - let ts = ForensicsTimestamp::new(1715845546, Europe::Berlin, Europe::Berlin); + let ts = ForensicsTimestamp::from(1715845546).with_timezone(Europe::Berlin); assert_eq!(ts.to_string(), "2024-05-16T09:45:46+02:00"); - let ts = ForensicsTimestamp::new(1715845546, Europe::Berlin, UTC); + let ts = ForensicsTimestamp::from(1715845546).with_timezone(UTC); assert_eq!(ts.to_string(), "2024-05-16T07:45:46+00:00"); } } diff --git a/src/es4forensics/ecs/objects/posix_file.rs b/src/es4forensics/ecs/objects/posix_file.rs index 5b6d961..60ee71c 100644 --- a/src/es4forensics/ecs/objects/posix_file.rs +++ b/src/es4forensics/ecs/objects/posix_file.rs @@ -86,13 +86,6 @@ impl IntoIterator for PosixFile { } } -impl TryFrom<(Bodyfile3Line, &Tz)> for PosixFile { - type Error = anyhow::Error; - fn try_from((bfline, src_tz): (Bodyfile3Line, &Tz)) -> Result { - Self::try_from((&bfline, src_tz)) - } -} - impl TryFrom for PosixFile { type Error = anyhow::Error; fn try_from(bfline: Bodyfile3Line) -> Result { diff --git a/tests/ts2date.rs b/tests/ts2date.rs index bf8db3e..df6c640 100644 --- a/tests/ts2date.rs +++ b/tests/ts2date.rs @@ -49,29 +49,6 @@ fn ts2date_utc2berlin() { ); } -#[test] -fn ts2date_berlin2utc() { - const SAMPLE_TIMELINE_OUT: &str = r#"2023-08-30T14:08:37+00:00|REG|||App Paths - protocolhandler.exe - C:\Program Files\WindowsApps\Microsoft.Office.Desktop_16051.16626.20170.0_x86__8wekyb3d8bbwe\Office16\protocolhandler.exe -2023-08-30T14:08:37+00:00|REG|||App Paths - sdxhelper.exe - C:\Program Files\WindowsApps\Microsoft.Office.Desktop_16051.16626.20170.0_x86__8wekyb3d8bbwe\Office16\SDXHelper.exe -2023-08-30T14:08:37+00:00|REG|||App Paths - selfcert.exe - C:\Program Files\WindowsApps\Microsoft.Office.Desktop_16051.16626.20170.0_x86__8wekyb3d8bbwe\Office16\SELFCERT.exe -2023-08-30T14:06:21+00:00|REG|||App Paths - msaccess.exe - C:\Program Files\WindowsApps\Microsoft.Office.Desktop.Access_16051.16626.20170.0_x86__8wekyb3d8bbwe\Office16\MSACCESS.exe -"#; - - let mut cmd = Command::cargo_bin("ts2date").unwrap(); - let result = cmd - .arg("-f") - .arg("Europe/Berlin") - .arg("-t") - .arg("UTC") - .write_stdin(SAMPLE_TIMELINE) - .ok(); - assert!(result.is_ok()); - - assert_eq!( - SAMPLE_TIMELINE_OUT, - String::from_utf8(result.unwrap().stdout).unwrap() - ); -} #[test] fn ts2date_list1() {