Skip to main content

dav_server/
util.rs

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