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#[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
33pub(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
58impl 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 http::method::Method::from_bytes(b"").unwrap_err()
66 })
67 }
68}
69
70#[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 pub fn all() -> DavMethodSet {
85 DavMethodSet(0xffffffff)
86 }
87
88 pub fn none() -> DavMethodSet {
90 DavMethodSet(0)
91 }
92
93 pub fn add(&mut self, m: DavMethod) -> &Self {
95 self.0 |= m as u32;
96 self
97 }
98
99 pub fn remove(&mut self, m: DavMethod) -> &Self {
101 self.0 &= !(m as u32);
102 self
103 }
104
105 pub fn contains(&self, m: DavMethod) -> bool {
107 self.0 & (m as u32) > 0
108 }
109
110 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 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 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#[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}