webdav_handler/
util.rs

1use std::io::{Cursor, Write};
2use std::time::{SystemTime, UNIX_EPOCH};
3
4use bytes::Bytes;
5use headers::Header;
6use http::method::InvalidMethod;
7
8use crate::body::Body;
9use crate::errors::DavError;
10use crate::DavResult;
11
12/// HTTP Methods supported by DavHandler.
13#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
14#[repr(u32)]
15pub enum DavMethod {
16    Head      = 0x0001,
17    Get       = 0x0002,
18    Put       = 0x0004,
19    Patch     = 0x0008,
20    Options   = 0x0010,
21    PropFind  = 0x0020,
22    PropPatch = 0x0040,
23    MkCol     = 0x0080,
24    Copy      = 0x0100,
25    Move      = 0x0200,
26    Delete    = 0x0400,
27    Lock      = 0x0800,
28    Unlock    = 0x1000,
29}
30
31// translate method into our own enum that has webdav methods as well.
32pub(crate) fn dav_method(m: &http::Method) -> DavResult<DavMethod> {
33    let m = match m {
34        &http::Method::HEAD => DavMethod::Head,
35        &http::Method::GET => DavMethod::Get,
36        &http::Method::PUT => DavMethod::Put,
37        &http::Method::PATCH => DavMethod::Patch,
38        &http::Method::DELETE => DavMethod::Delete,
39        &http::Method::OPTIONS => DavMethod::Options,
40        _ => {
41            match m.as_str() {
42                "PROPFIND" => DavMethod::PropFind,
43                "PROPPATCH" => DavMethod::PropPatch,
44                "MKCOL" => DavMethod::MkCol,
45                "COPY" => DavMethod::Copy,
46                "MOVE" => DavMethod::Move,
47                "LOCK" => DavMethod::Lock,
48                "UNLOCK" => DavMethod::Unlock,
49                _ => {
50                    return Err(DavError::UnknownDavMethod);
51                },
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" ?>"#, r#"<D:error xmlns:D="DAV:">"#, body, r#"</D:error>"#
147    );
148    Body::from(xml)
149}
150
151pub(crate) fn systemtime_to_offsetdatetime(t: SystemTime) -> time::OffsetDateTime {
152    match t.duration_since(UNIX_EPOCH) {
153        Ok(t) => {
154            let tm = time::OffsetDateTime::from_unix_timestamp(t.as_secs() as i64);
155            tm.to_offset(time::offset!(UTC))
156        },
157        Err(_) => time::OffsetDateTime::unix_epoch().to_offset(time::offset!(UTC)),
158    }
159}
160
161pub(crate) fn systemtime_to_httpdate(t: SystemTime) -> String {
162    let d = headers::Date::from(t);
163    let mut v = Vec::new();
164    d.encode(&mut v);
165    v[0].to_str().unwrap().to_owned()
166}
167
168pub(crate) fn systemtime_to_rfc3339(t: SystemTime) -> String {
169    // 1996-12-19T16:39:57Z
170    systemtime_to_offsetdatetime(t).format("%FT%H:%M:%SZ")
171}
172
173// A buffer that implements "Write".
174#[derive(Clone)]
175pub(crate) struct MemBuffer(Cursor<Vec<u8>>);
176
177impl MemBuffer {
178    pub fn new() -> MemBuffer {
179        MemBuffer(Cursor::new(Vec::new()))
180    }
181
182    pub fn take(&mut self) -> Bytes {
183        let buf = std::mem::replace(self.0.get_mut(), Vec::new());
184        self.0.set_position(0);
185        Bytes::from(buf)
186    }
187}
188
189impl Write for MemBuffer {
190    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
191        self.0.write(buf)
192    }
193
194    fn flush(&mut self) -> std::io::Result<()> {
195        Ok(())
196    }
197}
198
199#[cfg(test)]
200mod tests {
201    use super::*;
202    use std::time::UNIX_EPOCH;
203
204    #[test]
205    fn test_rfc3339() {
206        assert!(systemtime_to_rfc3339(UNIX_EPOCH) == "1970-01-01T00:00:00Z");
207    }
208}