1use std::error::Error as StdError;
6use std::io;
7use std::pin::pin;
8use std::sync::Arc;
9
10use bytes::{self, buf::Buf};
11use derive_where::derive_where;
12use futures_util::stream::Stream;
13use headers::HeaderMapExt;
14use http::{Request, Response, StatusCode};
15use http_body::Body as HttpBody;
16use http_body_util::BodyExt;
17
18use crate::body::{Body, StreamBody};
19use crate::davheaders;
20use crate::davpath::DavPath;
21use crate::util::{DavMethod, DavMethodSet, dav_method};
22
23use crate::DavResult;
24use crate::errors::DavError;
25use crate::fs::*;
26use crate::ls::*;
27use crate::voidfs::{VoidFs, is_voidfs};
28
29#[derive(Clone)]
37#[derive_where(Default)]
38pub struct DavHandler<C = ()> {
39 pub(crate) config: Arc<DavConfig<C>>,
40}
41
42#[derive(Clone)]
44#[derive_where(Default)]
45pub struct DavConfig<C = ()> {
46 pub(crate) prefix: Option<String>,
48 pub(crate) fs: Option<Box<dyn GuardedFileSystem<C>>>,
50 pub(crate) ls: Option<Box<dyn DavLockSystem>>,
52 pub(crate) allow: Option<DavMethodSet>,
54 pub(crate) principal: Option<String>,
57 pub(crate) hide_symlinks: Option<bool>,
59 pub(crate) autoindex: Option<bool>,
61 pub(crate) indexfile: Option<String>,
63 pub(crate) read_buf_size: Option<usize>,
65 pub(crate) redirect: Option<bool>,
67}
68
69impl<C> DavConfig<C> {
70 pub fn new() -> Self {
72 Self::default()
73 }
74
75 pub fn build_handler(self) -> DavHandler<C> {
77 DavHandler {
78 config: Arc::new(self),
79 }
80 }
81
82 pub fn strip_prefix(self, prefix: impl Into<String>) -> Self {
85 let mut this = self;
86 this.prefix = Some(prefix.into());
87 this
88 }
89
90 pub fn filesystem(self, fs: Box<dyn GuardedFileSystem<C>>) -> Self {
92 let mut this = self;
93 this.fs = Some(fs);
94 this
95 }
96
97 pub fn locksystem(self, ls: Box<dyn DavLockSystem>) -> Self {
99 let mut this = self;
100 this.ls = Some(ls);
101 this
102 }
103
104 pub fn methods(self, allow: DavMethodSet) -> Self {
106 let mut this = self;
107 this.allow = Some(allow);
108 this
109 }
110
111 pub fn principal(self, principal: impl Into<String>) -> Self {
113 let mut this = self;
114 this.principal = Some(principal.into());
115 this
116 }
117
118 pub fn hide_symlinks(self, hide: bool) -> Self {
120 let mut this = self;
121 this.hide_symlinks = Some(hide);
122 this
123 }
124
125 pub fn autoindex(self, autoindex: bool) -> Self {
127 let mut this = self;
128 this.autoindex = Some(autoindex);
129 this
130 }
131
132 pub fn indexfile(self, indexfile: impl Into<String>) -> Self {
134 let mut this = self;
135 this.indexfile = Some(indexfile.into());
136 this
137 }
138
139 pub fn read_buf_size(self, size: usize) -> Self {
141 let mut this = self;
142 this.read_buf_size = Some(size);
143 this
144 }
145
146 pub fn redirect(self, redirect: bool) -> Self {
147 let mut this = self;
148 this.redirect = Some(redirect);
149 this
150 }
151
152 fn merge(&self, new: Self) -> Self {
153 Self {
154 prefix: new.prefix.or_else(|| self.prefix.clone()),
155 fs: new.fs.or_else(|| self.fs.clone()),
156 ls: new.ls.or_else(|| self.ls.clone()),
157 allow: new.allow.or(self.allow),
158 principal: new.principal.or_else(|| self.principal.clone()),
159 hide_symlinks: new.hide_symlinks.or(self.hide_symlinks),
160 autoindex: new.autoindex.or(self.autoindex),
161 indexfile: new.indexfile.or_else(|| self.indexfile.clone()),
162 read_buf_size: new.read_buf_size.or(self.read_buf_size),
163 redirect: new.redirect.or(self.redirect),
164 }
165 }
166}
167
168pub(crate) struct DavInner<C> {
173 pub prefix: String,
174 pub fs: Box<dyn GuardedFileSystem<C>>,
175 pub ls: Option<Box<dyn DavLockSystem>>,
176 pub allow: Option<DavMethodSet>,
177 pub principal: Option<String>,
178 pub hide_symlinks: Option<bool>,
179 pub autoindex: Option<bool>,
180 pub indexfile: Option<String>,
181 pub read_buf_size: Option<usize>,
182 pub redirect: Option<bool>,
183 pub credentials: C,
184}
185
186impl<C: Clone + Send + Sync + 'static> DavHandler<C> {
187 pub fn new() -> Self {
194 Self {
195 config: Default::default(),
196 }
197 }
198
199 pub fn builder() -> DavConfig<C> {
201 DavConfig::new()
202 }
203
204 pub async fn handle_guarded<ReqBody, ReqData, ReqError>(
206 &self,
207 req: Request<ReqBody>,
208 credentials: C,
209 ) -> Response<Body>
210 where
211 ReqData: Buf + Send + 'static,
212 ReqError: StdError + Send + Sync + 'static,
213 ReqBody: HttpBody<Data = ReqData, Error = ReqError>,
214 {
215 let inner = DavInner::new(self.config.as_ref().clone(), credentials);
216 inner.handle(req).await
217 }
218}
219
220impl DavHandler {
221 pub async fn handle<ReqBody, ReqData, ReqError>(&self, req: Request<ReqBody>) -> Response<Body>
223 where
224 ReqData: Buf + Send + 'static,
225 ReqError: StdError + Send + Sync + 'static,
226 ReqBody: HttpBody<Data = ReqData, Error = ReqError>,
227 {
228 let inner = DavInner::new(self.config.as_ref().clone(), ());
229 inner.handle(req).await
230 }
231
232 pub async fn handle_with<ReqBody, ReqData, ReqError>(
240 &self,
241 config: DavConfig,
242 req: Request<ReqBody>,
243 ) -> Response<Body>
244 where
245 ReqData: Buf + Send + 'static,
246 ReqError: StdError + Send + Sync + 'static,
247 ReqBody: HttpBody<Data = ReqData, Error = ReqError>,
248 {
249 let inner = DavInner::new(self.config.merge(config), ());
250 inner.handle(req).await
251 }
252
253 #[doc(hidden)]
257 pub async fn handle_stream<ReqBody, ReqData, ReqError>(
258 &self,
259 req: Request<ReqBody>,
260 ) -> Response<Body>
261 where
262 ReqData: Buf + Send + 'static,
263 ReqError: StdError + Send + Sync + 'static,
264 ReqBody: Stream<Item = Result<ReqData, ReqError>>,
265 {
266 let req = {
267 let (parts, body) = req.into_parts();
268 Request::from_parts(parts, StreamBody::new(body))
269 };
270 let inner = DavInner::new(self.config.as_ref().clone(), ());
271 inner.handle(req).await
272 }
273
274 #[doc(hidden)]
276 pub async fn handle_stream_with<ReqBody, ReqData, ReqError>(
277 &self,
278 config: DavConfig,
279 req: Request<ReqBody>,
280 ) -> Response<Body>
281 where
282 ReqData: Buf + Send + 'static,
283 ReqError: StdError + Send + Sync + 'static,
284 ReqBody: Stream<Item = Result<ReqData, ReqError>>,
285 {
286 let req = {
287 let (parts, body) = req.into_parts();
288 Request::from_parts(parts, StreamBody::new(body))
289 };
290 let inner = DavInner::new(self.config.merge(config), ());
291 inner.handle(req).await
292 }
293}
294
295impl<C> DavInner<C>
296where
297 C: Clone + Send + Sync + 'static,
298{
299 pub fn new(cfg: DavConfig<C>, credentials: C) -> Self {
300 let DavConfig {
301 prefix,
302 fs,
303 ls,
304 allow,
305 principal,
306 hide_symlinks,
307 autoindex,
308 indexfile,
309 read_buf_size,
310 redirect,
311 } = cfg;
312 Self {
313 prefix: prefix.unwrap_or_default(),
314 fs: fs.unwrap_or_else(|| VoidFs::<C>::new()),
315 ls,
316 allow,
317 principal,
318 hide_symlinks,
319 autoindex,
320 indexfile,
321 read_buf_size,
322 redirect,
323 credentials,
324 }
325 }
326
327 pub(crate) async fn has_parent<'a>(&'a self, path: &'a DavPath) -> bool {
329 let p = path.parent();
330 self.fs
331 .metadata(&p, &self.credentials)
332 .await
333 .map(|m| m.is_dir())
334 .unwrap_or(false)
335 }
336
337 pub(crate) fn path(&self, req: &Request<()>) -> DavPath {
339 DavPath::from_uri_and_prefix(req.uri(), &self.prefix).unwrap()
341 }
342
343 pub(crate) fn fixpath(
346 &self,
347 res: &mut Response<Body>,
348 path: &mut DavPath,
349 meta: Box<dyn DavMetaData>,
350 ) -> Box<dyn DavMetaData> {
351 if meta.is_dir() && !path.is_collection() {
352 path.add_slash();
353 let newloc = path.with_prefix().as_url_string();
354 res.headers_mut()
355 .typed_insert(davheaders::ContentLocation(newloc));
356 }
357 meta
358 }
359
360 pub(crate) async fn read_request<ReqBody, ReqData, ReqError>(
362 &self,
363 body: ReqBody,
364 max_size: usize,
365 ) -> DavResult<Vec<u8>>
366 where
367 ReqBody: HttpBody<Data = ReqData, Error = ReqError>,
368 ReqData: Buf + Send + 'static,
369 ReqError: StdError + Send + Sync + 'static,
370 {
371 let mut data = Vec::new();
372 let mut body = pin!(body);
373
374 while let Some(res) = body.frame().await {
375 let mut data_frame = res.map_err(|_| {
376 DavError::IoError(io::Error::new(
377 io::ErrorKind::UnexpectedEof,
378 "UnexpectedEof",
379 ))
380 })?;
381
382 let Some(buf) = data_frame.data_mut() else {
383 continue;
384 };
385
386 while buf.has_remaining() {
387 if data.len() + buf.remaining() > max_size {
388 return Err(StatusCode::PAYLOAD_TOO_LARGE.into());
389 }
390 let b = buf.chunk();
391 let l = b.len();
392 data.extend_from_slice(b);
393 buf.advance(l);
394 }
395 }
396 Ok(data)
397 }
398
399 async fn handle<ReqBody, ReqData, ReqError>(self, req: Request<ReqBody>) -> Response<Body>
401 where
402 ReqBody: HttpBody<Data = ReqData, Error = ReqError>,
403 ReqData: Buf + Send + 'static,
404 ReqError: StdError + Send + Sync + 'static,
405 {
406 let is_ms = req
407 .headers()
408 .get("user-agent")
409 .and_then(|s| s.to_str().ok())
410 .map(|s| s.contains("Microsoft"))
411 .unwrap_or(false);
412
413 match self.handle2(req).await {
415 Ok(resp) => {
416 debug!("== END REQUEST result OK");
417 resp
418 }
419 Err(err) => {
420 debug!("== END REQUEST result {err:?}");
421 let mut resp = Response::builder();
422 if is_ms && err.statuscode() == StatusCode::NOT_FOUND {
423 resp = resp
433 .header("Cache-Control", "no-store, no-cache, must-revalidate")
434 .header("Progma", "no-cache")
435 .header("Expires", "0")
436 .header("Vary", "*");
437 }
438 resp = resp.header("Content-Length", "0").status(err.statuscode());
439 if err.must_close() {
440 resp = resp.header("connection", "close");
441 }
442 resp.body(Body::empty()).unwrap()
443 }
444 }
445 }
446
447 async fn handle2<ReqBody, ReqData, ReqError>(
449 mut self,
450 req: Request<ReqBody>,
451 ) -> DavResult<Response<Body>>
452 where
453 ReqBody: HttpBody<Data = ReqData, Error = ReqError>,
454 ReqData: Buf + Send + 'static,
455 ReqError: StdError + Send + Sync + 'static,
456 {
457 let (req, body) = {
458 let (parts, body) = req.into_parts();
459 (Request::from_parts(parts, ()), body)
460 };
461
462 if log_enabled!(log::Level::Debug)
464 && let Some(t) = req.headers().typed_get::<davheaders::XLitmus>()
465 {
466 debug!("X-Litmus: {t:?}");
467 }
468
469 let method = match dav_method(req.method()) {
471 Ok(m) => m,
472 Err(e) => {
473 debug!("refusing method {} request {}", req.method(), req.uri());
474 return Err(e);
475 }
476 };
477
478 if is_voidfs::<C>(&self.fs) {
480 match method {
481 DavMethod::Options => {
482 if self
483 .allow
484 .as_ref()
485 .map(|a| a.contains(DavMethod::Options))
486 .unwrap_or(true)
487 {
488 let mut a = DavMethodSet::none();
489 a.add(DavMethod::Options);
490 self.allow = Some(a);
491 }
492 }
493 _ => {
494 debug!("no filesystem: method not allowed on request {}", req.uri());
495 return Err(DavError::StatusClose(StatusCode::METHOD_NOT_ALLOWED));
496 }
497 }
498 }
499
500 if let Some(ref a) = self.allow
502 && !a.contains(method)
503 {
504 debug!(
505 "method {} not allowed on request {}",
506 req.method(),
507 req.uri()
508 );
509 return Err(DavError::StatusClose(StatusCode::METHOD_NOT_ALLOWED));
510 }
511
512 let path = DavPath::from_uri_and_prefix(req.uri(), &self.prefix)?;
514
515 let (body_strm, body_data) = match method {
518 DavMethod::Put | DavMethod::Patch => (Some(body), Vec::new()),
519 _ => (None, self.read_request(body, 65536).await?),
520 };
521
522 match method {
524 DavMethod::Put
525 | DavMethod::Patch
526 | DavMethod::PropFind
527 | DavMethod::PropPatch
528 | DavMethod::Lock
529 | DavMethod::Report
530 | DavMethod::MkCalendar
531 | DavMethod::MkAddressbook => {}
532 _ => {
533 if !body_data.is_empty() {
534 return Err(StatusCode::UNSUPPORTED_MEDIA_TYPE.into());
535 }
536 }
537 }
538
539 debug!("== START REQUEST {method:?} {path}");
540
541 match method {
542 DavMethod::Options => self.handle_options(&req).await,
543 DavMethod::PropFind => self.handle_propfind(&req, &body_data).await,
544 DavMethod::PropPatch => self.handle_proppatch(&req, &body_data).await,
545 DavMethod::MkCol => self.handle_mkcol(&req).await,
546 DavMethod::Delete => self.handle_delete(&req).await,
547 DavMethod::Lock => self.handle_lock(&req, &body_data).await,
548 DavMethod::Unlock => self.handle_unlock(&req).await,
549 DavMethod::Head | DavMethod::Get => self.handle_get(&req).await,
550 DavMethod::Copy | DavMethod::Move => self.handle_copymove(&req, method).await,
551 DavMethod::Put | DavMethod::Patch => self.handle_put(&req, body_strm.unwrap()).await,
552 #[cfg(any(feature = "caldav", feature = "carddav"))]
553 DavMethod::Report => self.handle_report(&req, &body_data).await,
554 #[cfg(feature = "caldav")]
555 DavMethod::MkCalendar => self.handle_mkcalendar(&req, &body_data).await,
556 #[cfg(feature = "carddav")]
557 DavMethod::MkAddressbook => self.handle_mkaddressbook(&req, &body_data).await,
558 #[cfg(not(any(feature = "caldav", feature = "carddav")))]
559 DavMethod::Report => Err(DavError::StatusClose(StatusCode::NOT_IMPLEMENTED)),
560 #[cfg(not(feature = "caldav"))]
561 DavMethod::MkCalendar => Err(DavError::StatusClose(StatusCode::NOT_IMPLEMENTED)),
562 #[cfg(not(feature = "carddav"))]
563 DavMethod::MkAddressbook => Err(DavError::StatusClose(StatusCode::NOT_IMPLEMENTED)),
564 }
565 }
566}