use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::time::{Duration, SystemTime};
use uuid::Uuid;
use xmltree::Element;
use crate::fs::FsResult;
use crate::ls::*;
use crate::tree;
use crate::webpath::WebPath;
type Tree = tree::Tree<Vec<u8>, Vec<DavLock>>;
#[derive(Debug, Clone)]
pub struct MemLs(Arc<Mutex<MemLsInner>>);
#[derive(Debug)]
struct MemLsInner {
tree: Tree,
locks: HashMap<Vec<u8>, u64>,
}
impl MemLs {
pub fn new() -> Box<MemLs> {
let inner = MemLsInner {
tree: Tree::new(Vec::new()),
locks: HashMap::new(),
};
Box::new(MemLs(Arc::new(Mutex::new(inner))))
}
}
impl DavLockSystem for MemLs {
fn lock(
&self,
path: &WebPath,
principal: Option<&str>,
owner: Option<&Element>,
timeout: Option<Duration>,
shared: bool,
deep: bool,
) -> Result<DavLock, DavLock>
{
let inner = &mut *self.0.lock().unwrap();
let rc = check_locks_to_path(&inner.tree, path, None, true, &Vec::new(), shared);
debug!("lock: check_locks_to_path: {:?}", rc);
rc?;
if deep {
let rc = check_locks_from_path(&inner.tree, path, None, true, &Vec::new(), shared);
debug!("lock: check_locks_from_path: {:?}", rc);
rc?;
}
let node = get_or_create_path_node(&mut inner.tree, path);
let timeout_at = match timeout {
None => None,
Some(d) => Some(SystemTime::now() + d),
};
let lock = DavLock {
token: Uuid::new_v4().to_urn().to_string(),
path: path.clone(),
principal: principal.map(|s| s.to_string()),
owner: owner.cloned(),
timeout_at: timeout_at,
timeout: timeout,
shared: shared,
deep: deep,
};
debug!("lock {} created", &lock.token);
let slock = lock.clone();
node.push(slock);
Ok(lock)
}
fn unlock(&self, path: &WebPath, token: &str) -> Result<(), ()> {
let inner = &mut *self.0.lock().unwrap();
let node_id = match lookup_lock(&inner.tree, path, token) {
None => {
debug!("unlock: {} not found at {}", token, path);
return Err(());
},
Some(n) => n,
};
let len = {
let node = inner.tree.get_node_mut(node_id).unwrap();
let idx = node.iter().position(|n| n.token.as_str() == token).unwrap();
node.remove(idx);
node.len()
};
if len == 0 {
inner.tree.delete_node(node_id).ok();
}
Ok(())
}
fn refresh(&self, path: &WebPath, token: &str, timeout: Option<Duration>) -> Result<DavLock, ()> {
debug!("refresh lock {}", token);
let inner = &mut *self.0.lock().unwrap();
let node_id = match lookup_lock(&inner.tree, path, token) {
None => {
debug!("lock not found");
return Err(());
},
Some(n) => n,
};
let node = (&mut inner.tree).get_node_mut(node_id).unwrap();
let idx = node.iter().position(|n| n.token.as_str() == token).unwrap();
let lock = &mut node[idx];
let timeout_at = match timeout {
None => None,
Some(d) => Some(SystemTime::now() + d),
};
lock.timeout = timeout;
lock.timeout_at = timeout_at;
Ok(lock.clone())
}
fn check(
&self,
path: &WebPath,
principal: Option<&str>,
ignore_principal: bool,
deep: bool,
submitted_tokens: Vec<&str>,
) -> Result<(), DavLock>
{
let inner = &*self.0.lock().unwrap();
let _st = submitted_tokens.clone();
let rc = check_locks_to_path(
&inner.tree,
path,
principal,
ignore_principal,
&submitted_tokens,
false,
);
debug!("check: check_lock_to_path: {:?}: {:?}", _st, rc);
rc?;
if deep {
let rc = check_locks_from_path(
&inner.tree,
path,
principal,
ignore_principal,
&submitted_tokens,
false,
);
debug!("check: check_locks_from_path: {:?}", rc);
rc?;
}
Ok(())
}
fn discover(&self, path: &WebPath) -> Vec<DavLock> {
let inner = &*self.0.lock().unwrap();
list_locks(&inner.tree, path)
}
fn delete(&self, path: &WebPath) -> Result<(), ()> {
let inner = &mut *self.0.lock().unwrap();
if let Some(node_id) = lookup_node(&inner.tree, path) {
(&mut inner.tree).delete_subtree(node_id).ok();
}
Ok(())
}
}
fn check_locks_to_path(
tree: &Tree,
path: &WebPath,
principal: Option<&str>,
ignore_principal: bool,
submitted_tokens: &Vec<&str>,
shared_ok: bool,
) -> Result<(), DavLock>
{
let segs = path_to_segs(path, true);
let last_seg = segs.len() - 1;
let mut holds_lock = false;
let mut first_lock_seen: Option<&DavLock> = None;
let mut node_id = tree::ROOT_ID;
for (i, seg) in segs.into_iter().enumerate() {
node_id = match get_child(tree, node_id, seg) {
Ok(n) => n,
Err(_) => break,
};
let node_locks = match tree.get_node(node_id) {
Ok(n) => n,
Err(_) => break,
};
for nl in node_locks {
if i < last_seg && !nl.deep {
continue;
}
if submitted_tokens.iter().any(|t| &nl.token == t) &&
(ignore_principal || principal == nl.principal.as_ref().map(|p| p.as_str()))
{
holds_lock = true;
} else {
if !nl.shared {
return Err(nl.to_owned());
}
if !shared_ok {
first_lock_seen.get_or_insert(nl);
}
}
}
}
if !holds_lock && first_lock_seen.is_some() {
return Err(first_lock_seen.unwrap().to_owned());
}
Ok(())
}
fn check_locks_from_path(
tree: &Tree,
path: &WebPath,
principal: Option<&str>,
ignore_principal: bool,
submitted_tokens: &Vec<&str>,
shared_ok: bool,
) -> Result<(), DavLock>
{
let node_id = match lookup_node(tree, path) {
Some(id) => id,
None => return Ok(()),
};
check_locks_from_node(
tree,
node_id,
principal,
ignore_principal,
submitted_tokens,
shared_ok,
)
}
fn check_locks_from_node(
tree: &Tree,
node_id: u64,
principal: Option<&str>,
ignore_principal: bool,
submitted_tokens: &Vec<&str>,
shared_ok: bool,
) -> Result<(), DavLock>
{
let node_locks = match tree.get_node(node_id) {
Ok(n) => n,
Err(_) => return Ok(()),
};
for nl in node_locks {
if !nl.shared || !shared_ok {
if !submitted_tokens.iter().any(|t| t == &nl.token) ||
(!ignore_principal && principal != nl.principal.as_ref().map(|p| p.as_str()))
{
return Err(nl.to_owned());
}
}
}
if let Ok(children) = tree.get_children(node_id) {
for (_, node_id) in children {
if let Err(l) = check_locks_from_node(
tree,
node_id,
principal,
ignore_principal,
submitted_tokens,
shared_ok,
) {
return Err(l);
}
}
}
Ok(())
}
fn get_or_create_path_node<'a>(tree: &'a mut Tree, path: &WebPath) -> &'a mut Vec<DavLock> {
let mut node_id = tree::ROOT_ID;
for seg in path_to_segs(path, false) {
node_id = match tree.get_child(node_id, seg) {
Ok(n) => n,
Err(_) => tree.add_child(node_id, seg.to_vec(), Vec::new(), false).unwrap(),
};
}
tree.get_node_mut(node_id).unwrap()
}
fn lookup_lock(tree: &Tree, path: &WebPath, token: &str) -> Option<u64> {
debug!("lookup_lock: {}", token);
let mut node_id = tree::ROOT_ID;
for seg in path_to_segs(path, true) {
debug!(
"lookup_lock: node {} seg {}",
node_id,
String::from_utf8_lossy(seg)
);
node_id = match get_child(tree, node_id, seg) {
Ok(n) => n,
Err(_) => break,
};
let node = tree.get_node(node_id).unwrap();
debug!("lookup_lock: locks here: {:?}", &node);
if node.iter().any(|n| n.token == token) {
return Some(node_id);
}
}
debug!("lookup_lock: fail");
None
}
fn lookup_node(tree: &Tree, path: &WebPath) -> Option<u64> {
let mut node_id = tree::ROOT_ID;
for seg in path_to_segs(path, false) {
node_id = match tree.get_child(node_id, seg) {
Ok(n) => n,
Err(_) => return None,
};
}
Some(node_id)
}
fn list_locks(tree: &Tree, path: &WebPath) -> Vec<DavLock> {
let mut locks = Vec::new();
let mut node_id = tree::ROOT_ID;
if let Ok(node) = tree.get_node(node_id) {
locks.extend_from_slice(node);
}
for seg in path_to_segs(path, false) {
node_id = match tree.get_child(node_id, seg) {
Ok(n) => n,
Err(_) => break,
};
if let Ok(node) = tree.get_node(node_id) {
locks.extend_from_slice(node);
}
}
locks
}
fn path_to_segs(path: &WebPath, include_root: bool) -> Vec<&[u8]> {
let path = path.as_bytes();
let mut segs: Vec<&[u8]> = path.split(|&c| c == b'/').filter(|s| s.len() > 0).collect();
if include_root {
segs.insert(0, b"");
}
segs
}
fn get_child(tree: &Tree, node_id: u64, seg: &[u8]) -> FsResult<u64> {
if seg.len() == 0 {
return Ok(node_id);
}
tree.get_child(node_id, seg)
}