aboutsummaryrefslogtreecommitdiffstats
path: root/src/webdav_fs.rs
blob: cf434ba0f2f2ae71f57fd2141b2ef81f06cfb094 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
//! Helper types and functions to allow configuring hidden files visibility
//! for WebDAV handlers

use dav_server::{davpath::DavPath, fs::*, localfs::LocalFs};
use futures::{future::ready, StreamExt, TryFutureExt};
use std::path::{Component, Path};

/// A dav_server local filesystem backend that can be configured to deny access
/// to files and directories with names starting with a dot.
#[derive(Clone)]
pub struct RestrictedFs {
    local: Box<LocalFs>,
    show_hidden: bool,
}

impl RestrictedFs {
    /// Creates a new RestrictedFs serving the local path at "base".
    /// If "show_hidden" is false, access to hidden files is prevented.
    pub fn new<P: AsRef<Path>>(base: P, show_hidden: bool) -> Box<RestrictedFs> {
        let local = LocalFs::new(base, false, false, false);
        Box::new(RestrictedFs { local, show_hidden })
    }
}

/// true if any normal component of path either starts with dot or can't be turned into a str
fn path_has_hidden_components(path: &DavPath) -> bool {
    path.as_pathbuf().components().any(|c| match c {
        Component::Normal(name) => name.to_str().is_none_or(|s| s.starts_with('.')),
        _ => false,
    })
}

impl DavFileSystem for RestrictedFs {
    fn open<'a>(
        &'a self,
        path: &'a DavPath,
        options: OpenOptions,
    ) -> FsFuture<'a, Box<dyn DavFile>> {
        if !path_has_hidden_components(path) || self.show_hidden {
            self.local.open(path, options)
        } else {
            Box::pin(ready(Err(FsError::NotFound)))
        }
    }

    fn read_dir<'a>(
        &'a self,
        path: &'a DavPath,
        meta: ReadDirMeta,
    ) -> FsFuture<'a, FsStream<Box<dyn DavDirEntry>>> {
        if self.show_hidden {
            self.local.read_dir(path, meta)
        } else if !path_has_hidden_components(path) {
            Box::pin(self.local.read_dir(path, meta).map_ok(|stream| {
                let dyn_stream: FsStream<Box<dyn DavDirEntry>> = Box::pin(stream.filter(|entry| {
                    ready(match entry {
                        Ok(ref e) => !e.name().starts_with(b"."),
                        _ => false,
                    })
                }));
                dyn_stream
            }))
        } else {
            Box::pin(ready(Err(FsError::NotFound)))
        }
    }

    fn metadata<'a>(&'a self, path: &'a DavPath) -> FsFuture<'a, Box<dyn DavMetaData>> {
        if !path_has_hidden_components(path) || self.show_hidden {
            self.local.metadata(path)
        } else {
            Box::pin(ready(Err(FsError::NotFound)))
        }
    }

    fn symlink_metadata<'a>(&'a self, path: &'a DavPath) -> FsFuture<'a, Box<dyn DavMetaData>> {
        if !path_has_hidden_components(path) || self.show_hidden {
            self.local.symlink_metadata(path)
        } else {
            Box::pin(ready(Err(FsError::NotFound)))
        }
    }
}