use anyhow::anyhow;
use getset::Getters;
use hashbrown::HashMap;
use std::sync::{Arc, Weak};
use termtree::Tree;
use crate::{
cache::{MetaDataCache, RecordPointer, SpecialRecords}, cli::OutputOptions, ntds::SdTable, object_tree_entry::ObjectTreeEntry
};
#[derive(Getters)]
pub struct ObjectTree {
#[getset(get = "pub")]
root: Arc<ObjectTreeEntry>,
record_index: HashMap<RecordPointer, Weak<ObjectTreeEntry>>,
}
impl ObjectTree {
pub fn new(metadata: &MetaDataCache, sd_table: Option<Arc<SdTable>>, output_options: &OutputOptions) -> Self {
let mut record_index = HashMap::new();
let root = ObjectTreeEntry::populate_object_tree(metadata, sd_table, &mut record_index, output_options);
Self { root, record_index }
}
pub fn get_special_records(&self) -> anyhow::Result<SpecialRecords> {
log::info!("obtaining special record ids");
let domain_root = ObjectTreeEntry::find_domain_root(&self.root)
.ok_or(anyhow!("db has no domain root"))?;
log::info!("found domain root '{}'", domain_root[0].name());
let configuration = domain_root[0]
.find_child_by_name("Configuration")
.ok_or(anyhow!("db has no `Configuration` entry"))?;
let schema_subpath = configuration
.find_child_by_name("Schema")
.ok_or(anyhow!("db has no `Schema` entry"))?;
let deleted_objects = domain_root[0]
.find_child_by_name("Deleted Objects")
.ok_or(anyhow!("db has no `Deleted Objects` entry"))?;
let extended_rights_container = configuration
.find_child_by_name("Extended-Rights")
.ok_or(anyhow!("db has no `Extended-Rights` entry"))?;
let domain_entry = Arc::clone(&domain_root[0]);
Ok(SpecialRecords::new(
schema_subpath,
deleted_objects,
extended_rights_container,
domain_entry
))
}
pub(crate) fn to_termtree(&self, max_depth: u8) -> Tree<Arc<ObjectTreeEntry>> {
Self::__to_termtree(&self.root, max_depth)
}
pub fn __to_termtree(me: &Arc<ObjectTreeEntry>, max_depth: u8) -> Tree<Arc<ObjectTreeEntry>> {
let tree = Tree::new(Arc::clone(me));
if max_depth > 0 {
let leaves: Vec<Tree<Arc<ObjectTreeEntry>>> = me
.children()
.lock()
.unwrap()
.iter()
.map(|c| Self::__to_termtree(c, max_depth - 1))
.collect();
tree.with_leaves(leaves)
} else {
tree
}
}
pub fn dn_of(&self, ptr: &RecordPointer) -> Option<String> {
match self.record_index.get(ptr) {
Some(record) => Some(
record
.upgrade()
.unwrap_or_else(|| {
panic!("record pointer {ptr} points to already deleted object")
})
.distinguished_name()
.clone(),
),
None => {
log::error!("Missing entry {ptr} in the data_table. This might happen if there is an inconsistency in the link_table. I'll ignore this reference");
None
}
}
}
pub fn relative_distinguished_name_of(&self, ptr: &RecordPointer) -> Option<String> {
match self.record_index.get(ptr) {
Some(record) => Some(
record
.upgrade()
.unwrap_or_else(|| {
panic!("record pointer {ptr} points to already deleted object")
})
.relative_distinguished_name()
.clone(),
),
None => {
log::error!("Missing entry {ptr} in the data_table. This might happen if there is an inconsistency in the link_table. I'll ignore this reference");
None
}
}
}
}