diff --git a/rosenpass/src/config.rs b/rosenpass/src/config.rs index fc51c18c..338b9de9 100644 --- a/rosenpass/src/config.rs +++ b/rosenpass/src/config.rs @@ -112,10 +112,32 @@ impl Rosenpass { /// /// NOTE: no validation is conducted, e.g. the paths specified in the configuration are not /// checked whether they even exist. + /// + /// ## TODO + /// - consider using a different algorithm to determine home directory – the below one may + /// behave unexpectedly on Windows pub fn load>(p: P) -> anyhow::Result { + // read file and deserialize let mut config: Self = toml::from_str(&fs::read_to_string(&p)?)?; + // resolve `~` (see https://github.com/rosenpass/rosenpass/issues/237) + use util::resolve_path_with_tilde; + resolve_path_with_tilde(&mut config.public_key); + resolve_path_with_tilde(&mut config.secret_key); + for peer in config.peers.iter_mut() { + resolve_path_with_tilde(&mut peer.public_key); + if let Some(ref mut psk) = &mut peer.pre_shared_key { + resolve_path_with_tilde(psk); + } + if let Some(ref mut ko) = &mut peer.key_out { + resolve_path_with_tilde(ko); + } + } + + // add path to "self" config.config_file_path = p.as_ref().to_owned(); + + // return Ok(config) } @@ -499,3 +521,67 @@ mod test { ) } } + +pub mod util { + use std::path::PathBuf; + /// takes a path that can potentially start with a `~` and resolves that `~` to the user's home directory + /// + /// ## Example + /// ``` + /// use rosenpass::config::util::resolve_path_with_tilde; + /// std::env::set_var("HOME","/home/dummy"); + /// let mut path = std::path::PathBuf::from("~/foo.toml"); + /// resolve_path_with_tilde(&mut path); + /// assert!(path == std::path::PathBuf::from("/home/dummy/foo.toml")); + /// ``` + pub fn resolve_path_with_tilde(path: &mut PathBuf) { + if let Some(first_segment) = path.iter().next() { + if !path.has_root() && first_segment == "~" { + let home_dir = std::env::home_dir().unwrap_or_else(|| { + log::error!("config file contains \"~\" but can not determine home diretory"); + std::process::exit(1); + }); + let orig_path = path.clone(); + path.clear(); + path.push(home_dir); + for segment in orig_path.iter().skip(1) { + path.push(segment); + } + } + } + } + + #[cfg(test)] + mod test { + use super::*; + #[test] + fn test_resolve_path_with_tilde() { + let test = |path_str: &str, resolved: &str| { + let mut path = PathBuf::from(path_str); + resolve_path_with_tilde(&mut path); + assert!( + path == PathBuf::from(resolved), + "Path {:?} has been resolved to {:?} but should have been resolved to {:?}.", + path_str, + path, + resolved + ); + }; + // set environment because otherwise the test result would depend on the system running this + std::env::set_var("USER", "dummy"); + std::env::set_var("HOME", "/home/dummy"); + + // should resolve + test("~/foo.toml", "/home/dummy/foo.toml"); + test("~//foo", "/home/dummy/foo"); + test("~/../other_user/foo", "/home/dummy/../other_user/foo"); + + // should _not_ resolve + test("~foo/bar", "~foo/bar"); + test(".~/foo", ".~/foo"); + test("/~/foo.toml", "/~/foo.toml"); + test(r"~\foo", r"~\foo"); + test(r"C:\~\foo.toml", r"C:\~\foo.toml"); + } + } +}