use std::path::PathBuf;
use std::sync::LazyLock;
use netrc_rs::Netrc;
use crate::config::Settings;
use crate::dirs;
static NETRC: LazyLock<Option<Netrc>> = LazyLock::new(|| {
let settings = Settings::get();
if !settings.netrc {
return None;
}
let path = netrc_path();
if !path.exists() {
return None;
}
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
if let Ok(metadata) = std::fs::metadata(&path) {
let mode = metadata.permissions().mode();
if mode & 0o077 != 0 {
warn!(
"netrc file {} has insecure permissions (mode: {:o}). Should be 0600 or 0400",
path.display(),
mode & 0o777
);
}
}
}
match std::fs::read_to_string(&path) {
Ok(content) => match Netrc::parse(content, false) {
Ok(netrc) => {
debug!("Loaded netrc from {}", path.display());
Some(netrc)
}
Err(e) => {
warn!("Failed to parse netrc file {}: {}", path.display(), e);
None
}
},
Err(e) => {
warn!("Failed to read netrc file {}: {}", path.display(), e);
None
}
}
});
fn netrc_path() -> PathBuf {
let settings = Settings::get();
if let Some(path) = &settings.netrc_file {
return path.clone();
}
#[cfg(windows)]
{
let windows_netrc = dirs::HOME.join("_netrc");
if windows_netrc.exists() {
return windows_netrc;
}
}
dirs::HOME.join(".netrc")
}
pub fn get_credentials(host: &str) -> Option<(String, String)> {
let netrc = NETRC.as_ref()?;
if let Some(machine) = netrc.machines.iter().find(|m| {
m.name
.as_ref()
.is_some_and(|name| name.eq_ignore_ascii_case(host))
}) && let (Some(login), Some(password)) = (&machine.login, &machine.password)
{
trace!("Found netrc credentials for host: {}", host);
return Some((login.clone(), password.clone()));
}
if let Some(machine) = netrc.machines.iter().find(|m| m.name.is_none())
&& let (Some(login), Some(password)) = (&machine.login, &machine.password)
{
trace!("Using default netrc credentials for host: {}", host);
return Some((login.clone(), password.clone()));
}
None
}