diff --git a/Cargo.lock b/Cargo.lock index 4102512..cbd6671 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -328,18 +328,17 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.26" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "serde", - "time 0.1.45", "wasm-bindgen", - "winapi 0.3.9", + "windows-targets 0.48.1", ] [[package]] @@ -1179,7 +1178,7 @@ checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", ] [[package]] @@ -1595,7 +1594,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "windows-sys 0.48.0", ] @@ -2405,7 +2404,7 @@ checksum = "acee08041c5de3d5048c8b3f6f13fafb3026b24ba43c6a695a0c76179b844369" dependencies = [ "log", "termcolor", - "time 0.3.22", + "time", ] [[package]] @@ -2611,17 +2610,6 @@ dependencies = [ "syn 2.0.23", ] -[[package]] -name = "time" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi 0.3.9", -] - [[package]] name = "time" version = "0.3.22" @@ -2864,12 +2852,6 @@ dependencies = [ "try-lock", ] -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index ec6149f..c9d96ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,9 +67,14 @@ name = "ipgrep" path = "src/bin/ipgrep/main.rs" required-features = ["ipgrep"] +[[bin]] +name = "ts2date" +path = "src/bin/ts2date/main.rs" +required-features = ["ts2date"] + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -default = ["pol_export", "mactime2", "evtxtools", "regdump", "hivescan", "cleanhive", "ipgrep"] +default = ["pol_export", "mactime2", "evtxtools", "regdump", "hivescan", "cleanhive", "ipgrep", "ts2date"] mactime2 = ["gzip", "elastic", "chrono-tz", "thiserror", "bitflags", "encoding_rs_io"] gzip = ["flate2"] elastic = ["elasticsearch", "tokio", "futures", "serde_json", "sha2", "base64", "num-traits", "num-derive", "strum", "strum_macros", "tokio-async-drop"] @@ -81,6 +86,7 @@ evtxls = ["evtx", "colored", "lazy-regex", "regex", "sigpipe", "dfirtk-eventdata evtxanalyze = ["evtx", "dfirtk-sessionevent-derive", "dfirtk-eventdata"] evtx2bodyfile = ["evtx", "getset", "ouroboros", "indicatif"] ipgrep = [] +ts2date = ["regex"] regdump = ["nt_hive2"] hivescan = ["nt_hive2"] @@ -98,6 +104,7 @@ log = {version = "0.4", features = [ "release_max_level_info" ]} serde = { version = "1.0", features = ["derive"] } simplelog = "0.12" winstructs = "0.3.0" +regex = {version = "1", optional=true} clap-markdown = "0.1.3" clap_complete = "4" @@ -119,7 +126,6 @@ evtx={version="0.8", optional=true} colored_json = {version="3", optional=true} term-table = {version = "1.3", optional=true} termsize = {version = "0.1", optional=true} -regex = {version = "1", optional=true} colored = {version = "2", optional=true} lazy-regex = {version = "3.0.0", optional=true} sigpipe = {version = "0", optional=true} diff --git a/README.md b/README.md index ebd589d..fcf6aa9 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ - [x] [`regdump`](#regdump) - [ ] [`regls`](https://github.com/janstarke/regls) - [ ] [`regview`](https://github.com/janstarke/regview) - - [ ] [`ts2date`](https://github.com/janstarke/ts2date) + - [x] [`ts2date`](#ts2date) - [ ] [`usnjrnl_dump`](https://github.com/janstarke/usnjrnl) # Overview of timelining tools @@ -94,7 +94,7 @@ This document contains the help content for the `es4forensics` command-line prog ## `es4forensics` -CLI tools for digital forensics and incident response +This crates provides structs and functions to insert timeline data into an elasticsearch index **Usage:** `es4forensics [OPTIONS] --index --password ` @@ -172,7 +172,7 @@ This document contains the help content for the `evtx2bodyfile` command-line pro ## `evtx2bodyfile` -CLI tools for digital forensics and incident response +Parses a lot of evtx files and prints a bodyfile **Usage:** `evtx2bodyfile [OPTIONS] [EVTX_FILES]...` @@ -209,7 +209,7 @@ This document contains the help content for the `evtxanalyze` command-line progr ## `evtxanalyze` -CLI tools for digital forensics and incident response +crate provide functions to analyze evtx files **Usage:** `evtxanalyze [OPTIONS] ` @@ -294,7 +294,7 @@ This document contains the help content for the `evtxcat` command-line program. ## `evtxcat` -CLI tools for digital forensics and incident response +Display one or more events from an evtx file **Usage:** `evtxcat [OPTIONS] ` @@ -336,7 +336,7 @@ This document contains the help content for the `evtxls` command-line program. ## `evtxls` -CLI tools for digital forensics and incident response +Display one or more events from an evtx file **Usage:** `evtxls [OPTIONS] [EVTX_FILES]...` @@ -406,7 +406,7 @@ This document contains the help content for the `evtxscan` command-line program. ## `evtxscan` -CLI tools for digital forensics and incident response +Find time skews in an evtx file **Usage:** `evtxscan [OPTIONS] ` @@ -476,7 +476,7 @@ This document contains the help content for the `ipgrep` command-line program. ## `ipgrep` -CLI tools for digital forensics and incident response +search for IP addresses in text files **Usage:** `ipgrep [OPTIONS] [FILE]...` @@ -518,7 +518,7 @@ This document contains the help content for the `mactime2` command-line program. ## `mactime2` -CLI tools for digital forensics and incident response +replacement for `mactime` **Usage:** `mactime2 [OPTIONS]` @@ -562,7 +562,7 @@ This document contains the help content for the `pol_export` command-line progra ## `pol_export` -CLI tools for digital forensics and incident response +Exporter for Windows Registry Policy Files **Usage:** `pol_export [OPTIONS] ` @@ -594,7 +594,7 @@ This document contains the help content for the `regdump` command-line program. ## `regdump` -CLI tools for digital forensics and incident response +parses registry hive files and prints a bodyfile **Usage:** `regdump [OPTIONS] ` @@ -613,6 +613,43 @@ CLI tools for digital forensics and incident response +
+ + + This document was generated automatically by + clap-markdown. + + +# Command-Line Help for `ts2date` + +This document contains the help content for the `ts2date` command-line program. + +**Command Overview:** + +* [`ts2date`↴](#ts2date) + +## `ts2date` + +replaces UNIX timestamps in a stream by a formatted date + +**Usage:** `ts2date [OPTIONS] [INPUT_FILE] [OUTPUT_FILE]` + +###### **Arguments:** + +* `` — name of the file to read (default from stdin) + + Default value: `-` +* `` — name of the file to write (default to stdout) + + Default value: `-` + +###### **Options:** + +* `-v`, `--verbose` — More output per occurrence +* `-q`, `--quiet` — Less output per occurrence + + +
diff --git a/scripts/update-md.sh b/scripts/update-md.sh index e9cb88b..5fd8b62 100755 --- a/scripts/update-md.sh +++ b/scripts/update-md.sh @@ -29,7 +29,7 @@ cat >README.md <<'EOF' - [x] [`regdump`](#regdump) - [ ] [`regls`](https://github.com/janstarke/regls) - [ ] [`regview`](https://github.com/janstarke/regview) - - [ ] [`ts2date`](https://github.com/janstarke/ts2date) + - [x] [`ts2date`](#ts2date) - [ ] [`usnjrnl_dump`](https://github.com/janstarke/usnjrnl) # Overview of timelining tools diff --git a/src/apps/mactime2/cli.rs b/src/apps/mactime2/cli.rs index 41012d6..cbe175e 100644 --- a/src/apps/mactime2/cli.rs +++ b/src/apps/mactime2/cli.rs @@ -3,9 +3,9 @@ use clio::Input; use log::LevelFilter; use chrono_tz::Tz; -use crate::common::HasVerboseFlag; +use crate::common::{HasVerboseFlag,TzArgument}; -use super::{OutputFormat, TzArgument}; +use super::OutputFormat; #[cfg(feature = "gzip")] const BODYFILE_HELP: &str = @@ -13,8 +13,9 @@ const BODYFILE_HELP: &str = #[cfg(not(feature = "gzip"))] const BODYFILE_HELP: &str = "path to input file or '-' for stdin"; +/// replacement for `mactime` #[derive(Parser)] -#[clap(name="mactime2", author, version, about, long_about = None)] +#[clap(name="mactime2", author, version, long_about = None)] pub struct Cli { #[clap(short('b'), value_parser, value_hint=ValueHint::FilePath, default_value="-", help=BODYFILE_HELP, display_order(100))] diff --git a/src/apps/mactime2/mod.rs b/src/apps/mactime2/mod.rs index eeb4776..3274639 100644 --- a/src/apps/mactime2/mod.rs +++ b/src/apps/mactime2/mod.rs @@ -5,8 +5,6 @@ pub mod error; pub mod filter; mod output; mod cli; -mod tzargument; pub use application::*; pub use cli::*; -pub (crate) use tzargument::*; \ No newline at end of file diff --git a/src/bin/es4forensics/cli.rs b/src/bin/es4forensics/cli.rs index ca58ed1..b118e30 100644 --- a/src/bin/es4forensics/cli.rs +++ b/src/bin/es4forensics/cli.rs @@ -26,8 +26,9 @@ pub(crate) enum Action { }, } +/// This crates provides structs and functions to insert timeline data into an elasticsearch index. #[derive(Parser)] -#[clap(name=env!("CARGO_BIN_NAME"), author, version, about, long_about = None)] +#[clap(name=env!("CARGO_BIN_NAME"), author, version, long_about = None)] pub struct Cli { #[command(subcommand)] pub(crate) action: Action, diff --git a/src/bin/evtx2bodyfile/cli.rs b/src/bin/evtx2bodyfile/cli.rs index ba5c6e8..83a8f7a 100644 --- a/src/bin/evtx2bodyfile/cli.rs +++ b/src/bin/evtx2bodyfile/cli.rs @@ -5,8 +5,9 @@ use dfir_toolkit::common::HasVerboseFlag; use getset::Getters; use log::LevelFilter; +/// creates bodyfile from Windows evtx files #[derive(Parser, Clone, Getters)] -#[clap(name=env!("CARGO_BIN_NAME"), author, version, about, long_about = None)] +#[clap(name=env!("CARGO_BIN_NAME"), author, version, long_about = None)] #[getset(get = "pub (crate)")] pub(crate) struct Cli { /// names of the evtx files diff --git a/src/bin/evtxanalyze/cli.rs b/src/bin/evtxanalyze/cli.rs index eb61c96..11189cc 100644 --- a/src/bin/evtxanalyze/cli.rs +++ b/src/bin/evtxanalyze/cli.rs @@ -57,8 +57,9 @@ pub enum Command { }, } +/// crate provide functions to analyze evtx files #[derive(Parser)] -#[clap(name=env!("CARGO_BIN_NAME"), author, version, about, long_about = None)] +#[clap(name=env!("CARGO_BIN_NAME"), author, version, long_about = None)] pub(crate) struct Cli { #[command(subcommand)] pub(crate) command: Command, diff --git a/src/bin/evtxcat/cli.rs b/src/bin/evtxcat/cli.rs index 65af151..267e2d8 100644 --- a/src/bin/evtxcat/cli.rs +++ b/src/bin/evtxcat/cli.rs @@ -6,7 +6,7 @@ use crate::output_format::OutputFormat; /// Display one or more events from an evtx file #[derive(Parser)] -#[clap(name=env!("CARGO_BIN_NAME"),author,version,about)] +#[clap(name=env!("CARGO_BIN_NAME"), author, version)] pub (crate) struct Cli { /// Name of the evtx file to read from pub (crate) evtx_file: String, diff --git a/src/bin/evtxls/cli.rs b/src/bin/evtxls/cli.rs index 553eb65..b86b8f0 100644 --- a/src/bin/evtxls/cli.rs +++ b/src/bin/evtxls/cli.rs @@ -20,7 +20,7 @@ pub(crate) enum SortOrder { /// Display one or more events from an evtx file #[derive(Parser)] -#[clap(name=env!("CARGO_BIN_NAME"), author,version,about,long_about=None)] +#[clap(name=env!("CARGO_BIN_NAME"), author, version,long_about=None)] pub(crate) struct Cli { /// Name of the evtx files to read from pub(crate) evtx_files: Vec, diff --git a/src/bin/evtxscan/cli.rs b/src/bin/evtxscan/cli.rs index a234c69..cf35c33 100644 --- a/src/bin/evtxscan/cli.rs +++ b/src/bin/evtxscan/cli.rs @@ -5,7 +5,7 @@ use log::LevelFilter; /// Find time skews in an evtx file #[derive(Parser)] -#[clap(name=env!("CARGO_BIN_NAME"), author, version, about)] +#[clap(name=env!("CARGO_BIN_NAME"), author, version)] pub (crate) struct Cli { /// name of the evtx file to scan pub (crate) evtx_file: String, diff --git a/src/bin/ipgrep/cli.rs b/src/bin/ipgrep/cli.rs index 6bec20c..40e46ee 100644 --- a/src/bin/ipgrep/cli.rs +++ b/src/bin/ipgrep/cli.rs @@ -6,8 +6,9 @@ use log::LevelFilter; use crate::{ip_filter::IpFilter, format_ipv4}; +/// search for IP addresses in text files #[derive(Parser)] -#[clap(name=env!("CARGO_BIN_NAME"), author,version,about,long_about=None)] +#[clap(name=env!("CARGO_BIN_NAME"), author, version,long_about=None)] pub(crate) struct Cli { pub(crate) file: Vec, diff --git a/src/bin/mactime2/main.rs b/src/bin/mactime2/main.rs index 6c242ae..da2dab3 100644 --- a/src/bin/mactime2/main.rs +++ b/src/bin/mactime2/main.rs @@ -1,6 +1,5 @@ use anyhow::Result; -use chrono_tz::TZ_VARIANTS; -use dfir_toolkit::common::FancyParser; +use dfir_toolkit::common::{FancyParser, TzArgument}; use dfir_toolkit::apps::mactime2::Cli; use dfir_toolkit::apps::mactime2::Mactime2Application; @@ -9,7 +8,7 @@ fn main() -> Result<()> { let cli: Cli = Cli::parse_cli(); if cli.src_zone.is_list() || cli.dst_zone.is_list() { - display_zones(); + TzArgument::display_zones(); return Ok(()); } debug_assert!(cli.dst_zone.is_tz()); @@ -19,9 +18,3 @@ fn main() -> Result<()> { app.run() } - -fn display_zones() { - for v in TZ_VARIANTS.iter() { - println!("{}", v); - } -} diff --git a/src/bin/pol_export/cli.rs b/src/bin/pol_export/cli.rs index f5ec6a7..fb8a867 100644 --- a/src/bin/pol_export/cli.rs +++ b/src/bin/pol_export/cli.rs @@ -2,9 +2,9 @@ use clap::Parser; use dfir_toolkit::common::HasVerboseFlag; use log::LevelFilter; - +/// Exporter for Windows Registry Policy Files #[derive(Parser, Debug)] -#[clap(name=env!("CARGO_BIN_NAME"), author, version, about, long_about = None)] +#[clap(name=env!("CARGO_BIN_NAME"), author, version, long_about = None)] pub (crate) struct Cli { /// Name of the file to read #[clap()] diff --git a/src/bin/regdump/cli.rs b/src/bin/regdump/cli.rs index 4e91bcb..b667ca5 100644 --- a/src/bin/regdump/cli.rs +++ b/src/bin/regdump/cli.rs @@ -5,9 +5,9 @@ use dfir_toolkit::common::HasVerboseFlag; use log::LevelFilter; use nt_hive2::{HiveParseMode, Hive}; - +/// parses registry hive files and prints a bodyfile #[derive(Parser)] -#[clap(name=env!("CARGO_BIN_NAME"), author, version, about, long_about = None)] +#[clap(name=env!("CARGO_BIN_NAME"), author, version, long_about = None)] pub (crate) struct Cli { /// name of the file to dump #[arg(value_parser = validate_file)] diff --git a/src/bin/ts2date/cli.rs b/src/bin/ts2date/cli.rs new file mode 100644 index 0000000..c8d4e7f --- /dev/null +++ b/src/bin/ts2date/cli.rs @@ -0,0 +1,36 @@ +use clap::{Parser, ValueHint}; +use log::LevelFilter; +use clio::{Input,Output}; +use chrono_tz::Tz; + +use dfir_toolkit::common::{HasVerboseFlag, TzArgument}; + +/// replaces UNIX timestamps in a stream by a formatted date +#[derive(Parser, Debug)] +#[clap(name=env!("CARGO_BIN_NAME"), author, version, long_about = None)] +pub (crate) struct Cli { + /// name of the file to read (default from stdin) + #[clap(value_parser, value_hint=ValueHint::FilePath, default_value="-", display_order(100))] + pub(crate) input_file: Input, + + #[clap(flatten)] + pub (crate) verbose: clap_verbosity_flag::Verbosity, + + /// name of the file to write (default to stdout) + #[clap(default_value="-", value_hint=ValueHint::FilePath, value_parser)] + pub(crate) output_file: Output, + + /// 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(crate) 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, +} + +impl HasVerboseFlag for Cli { + fn log_level_filter(&self) -> LevelFilter { + self.verbose.log_level_filter() + } +} \ No newline at end of file diff --git a/src/bin/ts2date/main.rs b/src/bin/ts2date/main.rs new file mode 100644 index 0000000..49c28f4 --- /dev/null +++ b/src/bin/ts2date/main.rs @@ -0,0 +1,45 @@ +use std::io::{BufRead, Write}; +use regex::Regex; +use anyhow::{bail, Result}; +use dfir_toolkit::common::{ForensicsTimestamp,FancyParser,TzArgument}; +use cli::Cli; + +mod cli; + + +fn main() -> Result<()> { + let cli = Cli::parse_cli(); + + let mut input = cli.input_file; + let mut output = cli.output_file; + + if cli.src_zone.is_list() || cli.dst_zone.is_list() { + TzArgument::display_zones(); + return Ok(()); + } + + let re = Regex::new(r"^(?P.*?)(?P\d{10})(?P.*)$").unwrap(); + + for line in input.lock().lines() { + let content = match line { + Ok(line) => line, + Err(_) => bail!("content of input file need to be in UTF-8 (not in UTF-16)"), + }; + + 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()); + format!("{}{}{}", caps.name("lhs").unwrap().as_str(), + ts.format_date(), + caps.name("rhs").unwrap().as_str()) + + } + None => content + }; + + output.lock().write_all((out+ "\n").as_bytes())?; + + } + Ok(()) +} \ No newline at end of file diff --git a/src/common/forensics_timestamp.rs b/src/common/forensics_timestamp.rs new file mode 100644 index 0000000..e59ba23 --- /dev/null +++ b/src/common/forensics_timestamp.rs @@ -0,0 +1,36 @@ +use chrono::{NaiveDateTime, LocalResult}; +use chrono_tz::Tz; +use chrono::offset::TimeZone; + +pub struct ForensicsTimestamp { + unix_ts: i64, + src_zone: Tz, + dst_zone: Tz, +} + +impl ForensicsTimestamp { + + pub fn new(unix_ts: i64, src_zone: Tz, dst_zone: Tz) -> Self { + Self { + unix_ts, src_zone, dst_zone + } + } + + pub fn format_date(&self) -> String { + if self.unix_ts >= 0 { + let src_timestamp = match self.src_zone + .from_local_datetime(&NaiveDateTime::from_timestamp_opt(self.unix_ts, 0).unwrap()) + { + LocalResult::None => { + return "INVALID DATETIME".to_owned(); + } + LocalResult::Single(t) => t, + LocalResult::Ambiguous(t1, _t2) => t1, + }; + let dst_timestamp = src_timestamp.with_timezone(&self.dst_zone); + dst_timestamp.to_rfc3339() + } else { + "0000-00-00T00:00:00+00:00".to_owned() + } + } +} \ No newline at end of file diff --git a/src/common/mod.rs b/src/common/mod.rs index 5449d56..bc7ae23 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -1,8 +1,13 @@ -mod rfc3339_datetime; pub mod bodyfile; +mod forensics_timestamp; mod parse_cli; +mod rfc3339_datetime; +mod tzargument; mod file_input; -pub use rfc3339_datetime::*; +pub use forensics_timestamp::*; pub use parse_cli::*; +pub use rfc3339_datetime::*; +pub use tzargument::*; + pub use file_input::*; \ No newline at end of file diff --git a/src/common/rfc3339_datetime.rs b/src/common/rfc3339_datetime.rs index bc0de1a..914647f 100644 --- a/src/common/rfc3339_datetime.rs +++ b/src/common/rfc3339_datetime.rs @@ -17,17 +17,17 @@ impl From<&str> for Rfc3339Datetime { } if let Ok(timestamp) = NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S") { - return Self{timestamp: DateTime::::from_utc(timestamp, Utc)} + return Self{timestamp: DateTime::::from_naive_utc_and_offset(timestamp, Utc)} } if let Ok(timestamp) = NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S") { - return Self{timestamp: DateTime::::from_utc(timestamp, Utc)} + return Self{timestamp: DateTime::::from_naive_utc_and_offset(timestamp, Utc)} } if let Ok(timestamp) = NaiveDate::parse_from_str(s, "%Y-%m-%d") { let time = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); let timestamp = NaiveDateTime::new(timestamp, time); - return Self{timestamp: DateTime::::from_utc(timestamp, Utc)} + return Self{timestamp: DateTime::::from_naive_utc_and_offset(timestamp, Utc)} } panic!("invalid timestamp: '{s}'"); diff --git a/src/apps/mactime2/tzargument.rs b/src/common/tzargument.rs similarity index 84% rename from src/apps/mactime2/tzargument.rs rename to src/common/tzargument.rs index e5f2edd..764c45a 100644 --- a/src/apps/mactime2/tzargument.rs +++ b/src/common/tzargument.rs @@ -1,8 +1,8 @@ use std::{fmt::Display, str::FromStr}; - +use chrono_tz::TZ_VARIANTS; use chrono_tz::Tz; -#[derive(Clone)] +#[derive(Clone, Debug, Copy)] pub enum TzArgument { List, Tz(Tz), @@ -29,6 +29,7 @@ impl Display for TzArgument { } } + impl TzArgument { pub fn is_list(&self) -> bool { matches!(self, Self::List) @@ -42,4 +43,9 @@ impl TzArgument { TzArgument::Tz(tz) => Some(tz), } } + pub fn display_zones() { + for v in TZ_VARIANTS.iter() { + println!("{}", v); + } + } } \ No newline at end of file diff --git a/tests/mod.rs b/tests/mod.rs index c73323e..9d99919 100644 --- a/tests/mod.rs +++ b/tests/mod.rs @@ -1 +1,2 @@ -mod mactime2; \ No newline at end of file +mod mactime2; +mod ts2date; \ No newline at end of file diff --git a/tests/ts2date.rs b/tests/ts2date.rs new file mode 100644 index 0000000..ea39b4e --- /dev/null +++ b/tests/ts2date.rs @@ -0,0 +1,72 @@ +use assert_cmd::Command; + +const SAMPLE_TIMELINE: &str = r#"1693411717|REG|||App Paths - protocolhandler.exe - C:\Program Files\WindowsApps\Microsoft.Office.Desktop_16051.16626.20170.0_x86__8wekyb3d8bbwe\Office16\protocolhandler.exe +1693411717|REG|||App Paths - sdxhelper.exe - C:\Program Files\WindowsApps\Microsoft.Office.Desktop_16051.16626.20170.0_x86__8wekyb3d8bbwe\Office16\SDXHelper.exe +1693411717|REG|||App Paths - selfcert.exe - C:\Program Files\WindowsApps\Microsoft.Office.Desktop_16051.16626.20170.0_x86__8wekyb3d8bbwe\Office16\SELFCERT.exe +1693411581|REG|||App Paths - msaccess.exe - C:\Program Files\WindowsApps\Microsoft.Office.Desktop.Access_16051.16626.20170.0_x86__8wekyb3d8bbwe\Office16\MSACCESS.exe"#; + +#[test] +fn ts2date_simple() { + const SAMPLE_TIMELINE_OUT: &str = r#"2023-08-30T16: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-30T16: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-30T16: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-30T16: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.write_stdin(SAMPLE_TIMELINE).ok(); + assert!(result.is_ok()); + + assert_eq!( + SAMPLE_TIMELINE_OUT, + String::from_utf8(result.unwrap().stdout).unwrap() + ); +} + +#[test] +fn ts2date_utc2berlin() { + const SAMPLE_TIMELINE_OUT: &str = r#"2023-08-30T18:08:37+02:00|REG|||App Paths - protocolhandler.exe - C:\Program Files\WindowsApps\Microsoft.Office.Desktop_16051.16626.20170.0_x86__8wekyb3d8bbwe\Office16\protocolhandler.exe +2023-08-30T18:08:37+02:00|REG|||App Paths - sdxhelper.exe - C:\Program Files\WindowsApps\Microsoft.Office.Desktop_16051.16626.20170.0_x86__8wekyb3d8bbwe\Office16\SDXHelper.exe +2023-08-30T18:08:37+02:00|REG|||App Paths - selfcert.exe - C:\Program Files\WindowsApps\Microsoft.Office.Desktop_16051.16626.20170.0_x86__8wekyb3d8bbwe\Office16\SELFCERT.exe +2023-08-30T18:06:21+02: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("UTC") + .arg("-t") + .arg("Europe/Berlin") + .write_stdin(SAMPLE_TIMELINE) + .ok(); + assert!(result.is_ok()); + + assert_eq!( + SAMPLE_TIMELINE_OUT, + String::from_utf8(result.unwrap().stdout).unwrap() + ); +} + +#[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() + ); +}