1use http::{HeaderMap, Method, Uri};
4
5use crate::DavPath;
6use crate::event::DavOperation;
7use crate::protocol::{
8 DavProtocolError, Depth, Destination, IfHeader, destination_relative_path, parse_copy_depth,
9 parse_delete_depth, parse_if_header, parse_lock_depth, parse_move_depth, parse_overwrite,
10 parse_propfind_depth, strip_mount_prefix,
11};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum DavMethod {
16 Options,
17 Propfind,
18 Proppatch,
19 Get,
20 Head,
21 Put,
22 Patch,
23 Mkcol,
24 Delete,
25 Copy,
26 Move,
27 Lock,
28 Unlock,
29 Acl,
30 Report,
31 VersionControl,
32 Checkout,
33 Checkin,
34 Uncheckout,
35 Mkworkspace,
36 Update,
37 Label,
38 Merge,
39 BaselineControl,
40 Mkactivity,
41 Search,
42 Orderpatch,
43 Mkredirectref,
44 Updateredirectref,
45 Bind,
46 Unbind,
47 Rebind,
48 Post,
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53pub enum DavBodyPolicy {
54 Empty,
56 BoundedXml { maximum: usize },
58 Bounded { maximum: usize },
60 Stream,
62 Unused,
64}
65
66impl DavMethod {
67 pub const ALL: [Self; 33] = [
69 Self::Options,
70 Self::Get,
71 Self::Head,
72 Self::Post,
73 Self::Put,
74 Self::Patch,
75 Self::Delete,
76 Self::Mkcol,
77 Self::Copy,
78 Self::Move,
79 Self::Propfind,
80 Self::Proppatch,
81 Self::Lock,
82 Self::Unlock,
83 Self::Acl,
84 Self::Report,
85 Self::VersionControl,
86 Self::Checkout,
87 Self::Checkin,
88 Self::Uncheckout,
89 Self::Mkworkspace,
90 Self::Update,
91 Self::Label,
92 Self::Merge,
93 Self::BaselineControl,
94 Self::Mkactivity,
95 Self::Search,
96 Self::Orderpatch,
97 Self::Mkredirectref,
98 Self::Updateredirectref,
99 Self::Bind,
100 Self::Unbind,
101 Self::Rebind,
102 ];
103
104 #[must_use]
105 pub const fn index(self) -> u32 {
106 match self {
107 Self::Options => 0,
108 Self::Get => 1,
109 Self::Head => 2,
110 Self::Post => 3,
111 Self::Put => 4,
112 Self::Patch => 5,
113 Self::Delete => 6,
114 Self::Mkcol => 7,
115 Self::Copy => 8,
116 Self::Move => 9,
117 Self::Propfind => 10,
118 Self::Proppatch => 11,
119 Self::Lock => 12,
120 Self::Unlock => 13,
121 Self::Acl => 14,
122 Self::Report => 15,
123 Self::VersionControl => 16,
124 Self::Checkout => 17,
125 Self::Checkin => 18,
126 Self::Uncheckout => 19,
127 Self::Mkworkspace => 20,
128 Self::Update => 21,
129 Self::Label => 22,
130 Self::Merge => 23,
131 Self::BaselineControl => 24,
132 Self::Mkactivity => 25,
133 Self::Search => 26,
134 Self::Orderpatch => 27,
135 Self::Mkredirectref => 28,
136 Self::Updateredirectref => 29,
137 Self::Bind => 30,
138 Self::Unbind => 31,
139 Self::Rebind => 32,
140 }
141 }
142
143 #[must_use]
144 pub const fn as_str(self) -> &'static str {
145 match self {
146 Self::Options => "OPTIONS",
147 Self::Propfind => "PROPFIND",
148 Self::Proppatch => "PROPPATCH",
149 Self::Get => "GET",
150 Self::Head => "HEAD",
151 Self::Post => "POST",
152 Self::Put => "PUT",
153 Self::Patch => "PATCH",
154 Self::Mkcol => "MKCOL",
155 Self::Delete => "DELETE",
156 Self::Copy => "COPY",
157 Self::Move => "MOVE",
158 Self::Lock => "LOCK",
159 Self::Unlock => "UNLOCK",
160 Self::Acl => "ACL",
161 Self::Report => "REPORT",
162 Self::VersionControl => "VERSION-CONTROL",
163 Self::Checkout => "CHECKOUT",
164 Self::Checkin => "CHECKIN",
165 Self::Uncheckout => "UNCHECKOUT",
166 Self::Mkworkspace => "MKWORKSPACE",
167 Self::Update => "UPDATE",
168 Self::Label => "LABEL",
169 Self::Merge => "MERGE",
170 Self::BaselineControl => "BASELINE-CONTROL",
171 Self::Mkactivity => "MKACTIVITY",
172 Self::Search => "SEARCH",
173 Self::Orderpatch => "ORDERPATCH",
174 Self::Mkredirectref => "MKREDIRECTREF",
175 Self::Updateredirectref => "UPDATEREDIRECTREF",
176 Self::Bind => "BIND",
177 Self::Unbind => "UNBIND",
178 Self::Rebind => "REBIND",
179 }
180 }
181
182 #[must_use]
184 pub fn from_method(method: &Method) -> Option<Self> {
185 Self::from_name(method.as_str())
186 }
187
188 #[must_use]
190 pub fn from_name(method: &str) -> Option<Self> {
191 match method {
192 "OPTIONS" => Some(Self::Options),
193 "PROPFIND" => Some(Self::Propfind),
194 "PROPPATCH" => Some(Self::Proppatch),
195 "GET" => Some(Self::Get),
196 "HEAD" => Some(Self::Head),
197 "POST" => Some(Self::Post),
198 "PUT" => Some(Self::Put),
199 "PATCH" => Some(Self::Patch),
200 "MKCOL" => Some(Self::Mkcol),
201 "DELETE" => Some(Self::Delete),
202 "COPY" => Some(Self::Copy),
203 "MOVE" => Some(Self::Move),
204 "LOCK" => Some(Self::Lock),
205 "UNLOCK" => Some(Self::Unlock),
206 "ACL" => Some(Self::Acl),
207 "REPORT" => Some(Self::Report),
208 "VERSION-CONTROL" => Some(Self::VersionControl),
209 "CHECKOUT" => Some(Self::Checkout),
210 "CHECKIN" => Some(Self::Checkin),
211 "UNCHECKOUT" => Some(Self::Uncheckout),
212 "MKWORKSPACE" => Some(Self::Mkworkspace),
213 "UPDATE" => Some(Self::Update),
214 "LABEL" => Some(Self::Label),
215 "MERGE" => Some(Self::Merge),
216 "BASELINE-CONTROL" => Some(Self::BaselineControl),
217 "MKACTIVITY" => Some(Self::Mkactivity),
218 "SEARCH" => Some(Self::Search),
219 "ORDERPATCH" => Some(Self::Orderpatch),
220 "MKREDIRECTREF" => Some(Self::Mkredirectref),
221 "UPDATEREDIRECTREF" => Some(Self::Updateredirectref),
222 "BIND" => Some(Self::Bind),
223 "UNBIND" => Some(Self::Unbind),
224 "REBIND" => Some(Self::Rebind),
225 _ => None,
226 }
227 }
228
229 #[must_use]
231 pub const fn operation(self) -> DavOperation {
232 match self {
233 Self::Options => DavOperation::Options,
234 Self::Propfind => DavOperation::Propfind,
235 Self::Proppatch => DavOperation::Proppatch,
236 Self::Get => DavOperation::Get,
237 Self::Head => DavOperation::Head,
238 Self::Post => DavOperation::Post,
239 Self::Put => DavOperation::Put,
240 Self::Patch => DavOperation::Patch,
241 Self::Mkcol => DavOperation::Mkcol,
242 Self::Delete => DavOperation::Delete,
243 Self::Copy => DavOperation::Copy,
244 Self::Move => DavOperation::Move,
245 Self::Lock => DavOperation::Lock,
246 Self::Unlock => DavOperation::Unlock,
247 Self::Acl => DavOperation::Acl,
248 Self::Report => DavOperation::Report,
249 Self::VersionControl => DavOperation::VersionControl,
250 Self::Checkout => DavOperation::Checkout,
251 Self::Checkin => DavOperation::Checkin,
252 Self::Uncheckout => DavOperation::Uncheckout,
253 Self::Mkworkspace => DavOperation::Mkworkspace,
254 Self::Update => DavOperation::Update,
255 Self::Label => DavOperation::Label,
256 Self::Merge => DavOperation::Merge,
257 Self::BaselineControl => DavOperation::BaselineControl,
258 Self::Mkactivity => DavOperation::Mkactivity,
259 Self::Search => DavOperation::Search,
260 Self::Orderpatch => DavOperation::Orderpatch,
261 Self::Mkredirectref => DavOperation::Mkredirectref,
262 Self::Updateredirectref => DavOperation::Updateredirectref,
263 Self::Bind => DavOperation::Bind,
264 Self::Unbind => DavOperation::Unbind,
265 Self::Rebind => DavOperation::Rebind,
266 }
267 }
268}
269
270#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
272pub struct DavMethodSet(u64);
273
274impl DavMethodSet {
275 #[must_use]
276 pub const fn empty() -> Self {
277 Self(0)
278 }
279
280 #[must_use]
281 pub const fn from_methods(methods: &[DavMethod]) -> Self {
282 let mut set = Self::empty();
283 let mut index = 0;
284 while index < methods.len() {
285 set = set.with(methods[index]);
286 index += 1;
287 }
288 set
289 }
290
291 #[must_use]
292 pub const fn with(self, method: DavMethod) -> Self {
293 Self(self.0 | (1u64 << method.index()))
294 }
295
296 #[must_use]
297 pub const fn without(self, method: DavMethod) -> Self {
298 Self(self.0 & !(1u64 << method.index()))
299 }
300
301 #[must_use]
302 pub const fn union(self, other: Self) -> Self {
303 Self(self.0 | other.0)
304 }
305
306 #[must_use]
307 pub const fn contains(self, method: DavMethod) -> bool {
308 self.0 & (1u64 << method.index()) != 0
309 }
310
311 #[must_use]
312 pub const fn is_subset_of(self, other: Self) -> bool {
313 self.0 & !other.0 == 0
314 }
315
316 #[must_use]
317 pub const fn is_empty(self) -> bool {
318 self.0 == 0
319 }
320
321 #[must_use]
322 pub const fn iter(self) -> DavMethodSetIter {
323 DavMethodSetIter {
324 set: self,
325 index: 0,
326 }
327 }
328
329 #[must_use]
330 pub fn render(self) -> String {
331 let mut rendered = String::new();
332 for method in self.iter() {
333 if !rendered.is_empty() {
334 rendered.push_str(", ");
335 }
336 rendered.push_str(method.as_str());
337 }
338 rendered
339 }
340}
341
342#[derive(Debug, Clone)]
344pub struct DavMethodSetIter {
345 set: DavMethodSet,
346 index: usize,
347}
348
349impl Iterator for DavMethodSetIter {
350 type Item = DavMethod;
351
352 fn next(&mut self) -> Option<Self::Item> {
353 while self.index < DavMethod::ALL.len() {
354 let method = DavMethod::ALL[self.index];
355 self.index += 1;
356 if self.set.contains(method) {
357 return Some(method);
358 }
359 }
360 None
361 }
362}
363
364#[derive(Debug, Clone, PartialEq, Eq)]
366pub struct DavRequestOrigin {
367 pub scheme: String,
368 pub host: String,
369}
370
371#[derive(Debug, Clone, PartialEq, Eq)]
373pub struct DavRequestTarget<'a> {
374 pub target: DavPath,
375 pub origin: DavRequestOrigin,
376 pub mount_path: &'a str,
377}
378
379#[derive(Debug, Clone, PartialEq, Eq)]
381pub struct DavRequestHead {
382 pub method: DavMethod,
383 pub target: DavPath,
384 pub origin: DavRequestOrigin,
385 pub depth: Option<Depth>,
386 pub overwrite: Option<bool>,
387 pub destination: Option<Destination>,
388 pub if_header: Option<IfHeader>,
389}
390
391impl DavRequestHead {
392 pub fn parse_target<'a>(
398 uri: &Uri,
399 mount_path: &'a str,
400 origin: &DavRequestOrigin,
401 ) -> Result<DavRequestTarget<'a>, DavProtocolError> {
402 let relative = strip_mount_prefix(uri.path(), mount_path).ok_or_else(|| {
403 DavProtocolError::bad_request("Request target must stay under WebDAV prefix")
404 })?;
405 let target = DavPath::new(relative)
406 .map_err(|_| DavProtocolError::bad_request("Invalid request path"))?;
407 Ok(DavRequestTarget {
408 target,
409 origin: origin.clone(),
410 mount_path,
411 })
412 }
413
414 pub fn parse_known_method(
420 method: DavMethod,
421 request_target: &DavRequestTarget<'_>,
422 headers: &HeaderMap,
423 ) -> Result<Self, DavProtocolError> {
424 let depth = match method {
425 DavMethod::Propfind => Some(parse_propfind_depth(headers)?),
426 DavMethod::Copy => Some(parse_copy_depth(headers)?),
427 DavMethod::Move => Some(parse_move_depth(headers)?),
428 DavMethod::Delete => Some(parse_delete_depth(headers)?),
429 DavMethod::Lock => Some(parse_lock_depth(headers)?),
430 _ => None,
431 };
432 let (overwrite, destination) = match method {
433 DavMethod::Copy | DavMethod::Move => (
434 Some(parse_overwrite(headers)?),
435 Some(destination_relative_path(
436 headers,
437 request_target.mount_path,
438 &request_target.origin.scheme,
439 &request_target.origin.host,
440 )?),
441 ),
442 _ => (None, None),
443 };
444
445 Ok(Self {
446 method,
447 target: request_target.target.clone(),
448 origin: request_target.origin.clone(),
449 depth,
450 overwrite,
451 destination,
452 if_header: parse_if_header(headers)?,
453 })
454 }
455}