use std::path::{Path, PathBuf};
use super::parse_activation_dat;
use crate::{KeyError, Result};
const ADE_SUBDIR: &str = "Library/Application Support/Adobe/Digital Editions";
const ACTIVATION_DAT: &str = "activation.dat";
const MAX_SEARCH_DEPTH: usize = 8;
pub(super) fn extract_keys() -> Result<Vec<Vec<u8>>> {
let path = find_activation_dat()?;
let xml = std::fs::read_to_string(&path)
.map_err(|e| KeyError::Invalid(format!("read {}: {e}", path.display())))?;
let keys = parse_activation_dat(&xml)?;
if keys.is_empty() {
return Err(KeyError::NotFound(
"no adept:privateLicenseKey in activation.dat".into(),
));
}
Ok(keys)
}
fn find_activation_dat() -> Result<PathBuf> {
let home =
std::env::var_os("HOME").ok_or_else(|| KeyError::NotFound("HOME is not set".into()))?;
let root = Path::new(&home).join(ADE_SUBDIR);
find_named(&root, ACTIVATION_DAT, MAX_SEARCH_DEPTH).ok_or_else(|| {
KeyError::NotFound(format!(
"activation.dat not found under {} (is Adobe Digital Editions activated?)",
root.display()
))
})
}
fn find_named(dir: &Path, name: &str, depth: usize) -> Option<PathBuf> {
let entries = std::fs::read_dir(dir).ok()?;
let mut subdirs = Vec::new();
for entry in entries.flatten() {
let path = entry.path();
let file_type = match entry.file_type() {
Ok(t) => t,
Err(_) => continue,
};
if file_type.is_file() {
if path.file_name().is_some_and(|n| n == name) {
return Some(path);
}
} else if file_type.is_dir() && depth > 0 {
subdirs.push(path);
}
}
for sub in subdirs {
if let Some(found) = find_named(&sub, name, depth - 1) {
return Some(found);
}
}
None
}