dav_server/
util.rs

1use std::io::{Cursor, Write};
2use std::time::{SystemTime, UNIX_EPOCH};
3
4use bytes::Bytes;
5use headers::Header;
6use http::method::InvalidMethod;
7use time::format_description::well_known::Rfc3339;
8use time::macros::offset;
9
10use crate::body::Body;
11use crate::errors::DavError;
12use crate::DavResult;
13
14/// HTTP Methods supported by DavHandler.
15#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
16#[repr(u32)]
17pub enum DavMethod {
18    Head = 0x0001,
19    Get = 0x0002,
20    Put = 0x0004,
21    Patch = 0x0008,
22    Options = 0x0010,
23    PropFind = 0x0020,
24    PropPatch = 0x0040,
25    MkCol = 0x0080,
26    Copy = 0x0100,
27    Move = 0x0200,
28    Delete = 0x0400,
29    Lock = 0x0800,
30    Unlock = 0x1000,
31}
32
33// translate method into our own enum that has webdav methods as well.
34pub(crate) fn dav_method(m: &http::Method) -> DavResult<DavMethod> {
35    let m = match *m {
36        http::Method::HEAD => DavMethod::Head,
37        http::Method::GET => DavMethod::Get,
38        http::Method::PUT => DavMethod::Put,
39        http::Method::PATCH => DavMethod::Patch,
40        http::Method::DELETE => DavMethod::Delete,
41        http::Method::OPTIONS => DavMethod::Options,
42        _ => match m.as_str() {
43            "PROPFIND" => DavMethod::PropFind,
44            "PROPPATCH" => DavMethod::PropPatch,
45            "MKCOL" => DavMethod::MkCol,
46            "COPY" => DavMethod::Copy,
47            "MOVE" => DavMethod::Move,
48            "LOCK" => DavMethod::Lock,
49            "UNLOCK" => DavMethod::Unlock,
50            _ => {
51                return Err(DavError::UnknownDavMethod);
52            }
53        },
54    };
55    Ok(m)
56}
57
58// for external use.
59impl std::convert::TryFrom<&http::Method> for DavMethod {
60    type Error = InvalidMethod;
61
62    fn try_from(value: &http::Method) -> Result<Self, Self::Error> {
63        dav_method(value).map_err(|_| {
64            // A trick to get at the value of http::method::InvalidMethod.
65            http::method::Method::from_bytes(b"").unwrap_err()
66        })
67    }
68}
69
70/// A set of allowed [`DavMethod`]s.
71///
72/// [`DavMethod`]: enum.DavMethod.html
73#[derive(Clone, Copy, Debug)]
74pub struct DavMethodSet(u32);
75
76impl DavMethodSet {
77    pub const HTTP_RO: DavMethodSet =
78        DavMethodSet(DavMethod::Get as u32 | DavMethod::Head as u32 | DavMethod::Options as u32);
79    pub const HTTP_RW: DavMethodSet = DavMethodSet(Self::HTTP_RO.0 | DavMethod::Put as u32);
80    pub const WEBDAV_RO: DavMethodSet = DavMethodSet(Self::HTTP_RO.0 | DavMethod::PropFind as u32);
81    pub const WEBDAV_RW: DavMethodSet = DavMethodSet(0xffffffff);
82
83    /// New set, all methods allowed.
84    pub fn all() -> DavMethodSet {
85        DavMethodSet(0xffffffff)
86    }
87
88    /// New empty set.
89    pub fn none() -> DavMethodSet {
90        DavMethodSet(0)
91    }
92
93    /// Add a method.
94    pub fn add(&mut self, m: DavMethod) -> &Self {
95        self.0 |= m as u32;
96        self
97    }
98
99    /// Remove a method.
100    pub fn remove(&mut self, m: DavMethod) -> &Self {
101        self.0 &= !(m as u32);
102        self
103    }
104
105    /// Check if a method is in the set.
106    pub fn contains(&self, m: DavMethod) -> bool {
107        self.0 & (m as u32) > 0
108    }
109
110    /// Generate an DavMethodSet from a list of words.
111    pub fn from_vec(v: Vec<impl AsRef<str>>) -> Result<DavMethodSet, InvalidMethod> {
112        let mut m: u32 = 0;
113        for w in &v {
114            m |= match w.as_ref().to_lowercase().as_str() {
115                "head" => DavMethod::Head as u32,
116                "get" => DavMethod::Get as u32,
117                "put" => DavMethod::Put as u32,
118                "patch" => DavMethod::Patch as u32,
119                "delete" => DavMethod::Delete as u32,
120                "options" => DavMethod::Options as u32,
121                "propfind" => DavMethod::PropFind as u32,
122                "proppatch" => DavMethod::PropPatch as u32,
123                "mkcol" => DavMethod::MkCol as u32,
124                "copy" => DavMethod::Copy as u32,
125                "move" => DavMethod::Move as u32,
126                "lock" => DavMethod::Lock as u32,
127                "unlock" => DavMethod::Unlock as u32,
128                "http-ro" => Self::HTTP_RO.0,
129                "http-rw" => Self::HTTP_RW.0,
130                "webdav-ro" => Self::WEBDAV_RO.0,
131                "webdav-rw" => Self::WEBDAV_RW.0,
132                _ => {
133                    // A trick to get at the value of http::method::InvalidMethod.
134                    let invalid_method = http::method::Method::from_bytes(b"").unwrap_err();
135                    return Err(invalid_method);
136                }
137            };
138        }
139        Ok(DavMethodSet(m))
140    }
141}
142
143pub(crate) fn dav_xml_error(body: &str) -> Body {
144    let xml = format!(
145        "{}\n{}\n{}\n{}\n",
146        r#"<?xml version="1.0" encoding="utf-8" ?>"#,
147        r#"<D:error xmlns:D="DAV:">"#,
148        body,
149        r#"</D:error>"#
150    );
151    Body::from(xml)
152}
153
154pub(crate) fn systemtime_to_offsetdatetime(t: SystemTime) -> time::OffsetDateTime {
155    match t.duration_since(UNIX_EPOCH) {
156        Ok(t) => {
157            let tm = time::OffsetDateTime::from_unix_timestamp(t.as_secs() as i64).unwrap();
158            tm.to_offset(offset!(UTC))
159        }
160        Err(_) => time::OffsetDateTime::UNIX_EPOCH.to_offset(offset!(UTC)),
161    }
162}
163
164pub(crate) fn systemtime_to_httpdate(t: SystemTime) -> String {
165    let d = headers::Date::from(t);
166    let mut v = Vec::new();
167    d.encode(&mut v);
168    v[0].to_str().unwrap().to_owned()
169}
170
171pub(crate) fn systemtime_to_rfc3339_without_nanosecond(t: SystemTime) -> String {
172    // 1996-12-19T16:39:57Z
173    systemtime_to_offsetdatetime(t)
174        .replace_nanosecond(0)
175        .ok()
176        .and_then(|x| x.format(&Rfc3339).ok())
177        .unwrap_or("1970-01-01T00:00:00Z".into())
178}
179
180// A buffer that implements "Write".
181#[derive(Clone)]
182pub(crate) struct MemBuffer(Cursor<Vec<u8>>);
183
184impl MemBuffer {
185    pub fn new() -> MemBuffer {
186        MemBuffer(Cursor::new(Vec::new()))
187    }
188
189    pub fn take(&mut self) -> Bytes {
190        let buf = std::mem::take(self.0.get_mut());
191        self.0.set_position(0);
192        Bytes::from(buf)
193    }
194}
195
196impl Write for MemBuffer {
197    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
198        self.0.write(buf)
199    }
200
201    fn flush(&mut self) -> std::io::Result<()> {
202        Ok(())
203    }
204}
205
206#[cfg(test)]
207mod tests {
208    use super::*;
209    use std::time::UNIX_EPOCH;
210
211    #[test]
212    fn test_rfc3339_no_nanosecond() {
213        let t = UNIX_EPOCH + std::time::Duration::new(1, 5);
214        assert!(systemtime_to_rfc3339_without_nanosecond(t) == "1970-01-01T00:00:01Z");
215    }
216}