use crate::{BiometricKind, BiometricStatus};
fn fprintd_status() -> Option<BiometricStatus> {
let output = std::process::Command::new("dbus-send")
.args([
"--system",
"--dest=net.reactivated.Fprint",
"--type=method_call",
"--print-reply",
"/net/reactivated/Fprint/Manager",
"net.reactivated.Fprint.Manager.GetDefaultDevice",
])
.output()
.ok()?;
if !output.status.success() {
return None;
}
let stdout = std::str::from_utf8(&output.stdout).ok()?;
let device_path = parse_dbus_object_path(stdout)?;
let username = std::env::var("USER")
.or_else(|_| std::env::var("LOGNAME"))
.unwrap_or_default();
if username.is_empty() {
return Some(BiometricStatus::Available(BiometricKind::Fingerprint));
}
let list_output = std::process::Command::new("dbus-send")
.args([
"--system",
"--dest=net.reactivated.Fprint",
"--type=method_call",
"--print-reply",
&device_path,
"net.reactivated.Fprint.Device.ListEnrolledFingers",
&format!("string:{}", username),
])
.output()
.ok()?;
if list_output.status.success() {
let list_stdout = std::str::from_utf8(&list_output.stdout).ok()?;
if list_stdout.contains("string") {
Some(BiometricStatus::Available(BiometricKind::Fingerprint))
} else {
Some(BiometricStatus::Available(BiometricKind::Fingerprint))
}
} else {
Some(BiometricStatus::Available(BiometricKind::Fingerprint))
}
}
fn polkit_available() -> bool {
let output = std::process::Command::new("dbus-send")
.args([
"--system",
"--dest=org.freedesktop.PolicyKit1",
"--type=method_call",
"--print-reply",
"/org/freedesktop/PolicyKit1/Authority",
"org.freedesktop.DBus.Properties.Get",
"string:org.freedesktop.PolicyKit1.Authority",
"string:BackendVersion",
])
.output();
match output {
Ok(o) => o.status.success(),
Err(_) => false,
}
}
pub fn biometric_status() -> BiometricStatus {
if let Some(status) = fprintd_status() {
return status;
}
BiometricStatus::Unavailable
}
fn authenticate_fprintd(reason: &str, callback: Box<dyn FnOnce(bool) + Send>) {
let _ = reason;
let output = std::process::Command::new("fprintd-verify").output();
match output {
Ok(o) => callback(o.status.success()),
Err(_) => callback(false),
}
}
fn authenticate_polkit(reason: &str, callback: Box<dyn FnOnce(bool) + Send>) {
let _ = reason;
let pid = std::process::id();
let output = std::process::Command::new("pkcheck")
.args([
"--action-id",
"org.freedesktop.policykit.exec",
"--process",
&pid.to_string(),
"--allow-user-interaction",
])
.output();
match output {
Ok(o) => callback(o.status.success()),
Err(_) => callback(false),
}
}
pub fn authenticate_biometric(reason: &str, callback: Box<dyn FnOnce(bool) + Send>) {
if fprintd_status().is_some() {
authenticate_fprintd(reason, callback);
return;
}
if polkit_available() {
authenticate_polkit(reason, callback);
return;
}
callback(false);
}
fn parse_dbus_object_path(stdout: &str) -> Option<String> {
for line in stdout.lines() {
let trimmed = line.trim();
if let Some(rest) = trimmed.strip_prefix("object path") {
let path = rest.trim().trim_matches('"');
if path.starts_with('/') {
return Some(path.to_string());
}
}
}
None
}