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