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 principal: String,
209 credentials: C,
210 ) -> Response<Body>
211 where
212 ReqData: Buf + Send + 'static,
213 ReqError: StdError + Send + Sync + 'static,
214 ReqBody: HttpBody<Data = ReqData, Error = ReqError>,
215 {
216 let mut inner = DavInner::new(self.config.as_ref().clone(), credentials);
217 inner.principal = Some(principal);
218 inner.handle(req).await
219 }
220}
221
222impl DavHandler {
223 pub async fn handle<ReqBody, ReqData, ReqError>(&self, req: Request<ReqBody>) -> Response<Body>
225 where
226 ReqData: Buf + Send + 'static,
227 ReqError: StdError + Send + Sync + 'static,
228 ReqBody: HttpBody<Data = ReqData, Error = ReqError>,
229 {
230 let inner = DavInner::new(self.config.as_ref().clone(), ());
231 inner.handle(req).await
232 }
233
234 pub async fn handle_with<ReqBody, ReqData, ReqError>(
242 &self,
243 config: DavConfig,
244 req: Request<ReqBody>,
245 ) -> Response<Body>
246 where
247 ReqData: Buf + Send + 'static,
248 ReqError: StdError + Send + Sync + 'static,
249 ReqBody: HttpBody<Data = ReqData, Error = ReqError>,
250 {
251 let inner = DavInner::new(self.config.merge(config), ());
252 inner.handle(req).await
253 }
254
255 #[doc(hidden)]
259 pub async fn handle_stream<ReqBody, ReqData, ReqError>(
260 &self,
261 req: Request<ReqBody>,
262 ) -> Response<Body>
263 where
264 ReqData: Buf + Send + 'static,
265 ReqError: StdError + Send + Sync + 'static,
266 ReqBody: Stream<Item = Result<ReqData, ReqError>>,
267 {
268 let req = {
269 let (parts, body) = req.into_parts();
270 Request::from_parts(parts, StreamBody::new(body))
271 };
272 let inner = DavInner::new(self.config.as_ref().clone(), ());
273 inner.handle(req).await
274 }
275
276 #[doc(hidden)]
278 pub async fn handle_stream_with<ReqBody, ReqData, ReqError>(
279 &self,
280 config: DavConfig,
281 req: Request<ReqBody>,
282 ) -> Response<Body>
283 where
284 ReqData: Buf + Send + 'static,
285 ReqError: StdError + Send + Sync + 'static,
286 ReqBody: Stream<Item = Result<ReqData, ReqError>>,
287 {
288 let req = {
289 let (parts, body) = req.into_parts();
290 Request::from_parts(parts, StreamBody::new(body))
291 };
292 let inner = DavInner::new(self.config.merge(config), ());
293 inner.handle(req).await
294 }
295}
296
297impl<C> DavInner<C>
298where
299 C: Clone + Send + Sync + 'static,
300{
301 pub fn new(cfg: DavConfig<C>, credentials: C) -> Self {
302 let DavConfig {
303 prefix,
304 fs,
305 ls,
306 allow,
307 principal,
308 hide_symlinks,
309 autoindex,
310 indexfile,
311 read_buf_size,
312 redirect,
313 } = cfg;
314 Self {
315 prefix: prefix.unwrap_or_default(),
316 fs: fs.unwrap_or_else(|| VoidFs::<C>::new()),
317 ls,
318 allow,
319 principal,
320 hide_symlinks,
321 autoindex,
322 indexfile,
323 read_buf_size,
324 redirect,
325 credentials,
326 }
327 }
328
329 pub(crate) async fn has_parent<'a>(&'a self, path: &'a DavPath) -> bool {
331 let p = path.parent();
332 self.fs
333 .metadata(&p, &self.credentials)
334 .await
335 .map(|m| m.is_dir())
336 .unwrap_or(false)
337 }
338
339 pub(crate) fn path(&self, req: &Request<()>) -> DavPath {
341 DavPath::from_uri_and_prefix(req.uri(), &self.prefix).unwrap()
343 }
344
345 pub(crate) fn fixpath(
348 &self,
349 res: &mut Response<Body>,
350 path: &mut DavPath,
351 meta: Box<dyn DavMetaData>,
352 ) -> Box<dyn DavMetaData> {
353 if meta.is_dir() && !path.is_collection() {
354 path.add_slash();
355 let newloc = path.with_prefix().as_url_string();
356 res.headers_mut()
357 .typed_insert(davheaders::ContentLocation(newloc));
358 }
359 meta
360 }
361
362 pub(crate) async fn read_request<ReqBody, ReqData, ReqError>(
364 &self,
365 body: ReqBody,
366 max_size: usize,
367 ) -> DavResult<Vec<u8>>
368 where
369 ReqBody: HttpBody<Data = ReqData, Error = ReqError>,
370 ReqData: Buf + Send + 'static,
371 ReqError: StdError + Send + Sync + 'static,
372 {
373 let mut data = Vec::new();
374 let mut body = pin!(body);
375
376 while let Some(res) = body.frame().await {
377 let mut data_frame = res.map_err(|_| {
378 DavError::IoError(io::Error::new(
379 io::ErrorKind::UnexpectedEof,
380 "UnexpectedEof",
381 ))
382 })?;
383
384 let Some(buf) = data_frame.data_mut() else {
385 continue;
386 };
387
388 while buf.has_remaining() {
389 if data.len() + buf.remaining() > max_size {
390 return Err(StatusCode::PAYLOAD_TOO_LARGE.into());
391 }
392 let b = buf.chunk();
393 let l = b.len();
394 data.extend_from_slice(b);
395 buf.advance(l);
396 }
397 }
398 Ok(data)
399 }
400
401 async fn handle<ReqBody, ReqData, ReqError>(self, req: Request<ReqBody>) -> Response<Body>
403 where
404 ReqBody: HttpBody<Data = ReqData, Error = ReqError>,
405 ReqData: Buf + Send + 'static,
406 ReqError: StdError + Send + Sync + 'static,
407 {
408 let is_ms = req
409 .headers()
410 .get("user-agent")
411 .and_then(|s| s.to_str().ok())
412 .map(|s| s.contains("Microsoft"))
413 .unwrap_or(false);
414
415 match self.handle2(req).await {
417 Ok(resp) => {
418 debug!("== END REQUEST result OK");
419 resp
420 }
421 Err(err) => {
422 debug!("== END REQUEST result {err:?}");
423 let mut resp = Response::builder();
424 if is_ms && err.statuscode() == StatusCode::NOT_FOUND {
425 resp = resp
435 .header("Cache-Control", "no-store, no-cache, must-revalidate")
436 .header("Progma", "no-cache")
437 .header("Expires", "0")
438 .header("Vary", "*");
439 }
440 resp = resp.header("Content-Length", "0").status(err.statuscode());
441 if err.must_close() {
442 resp = resp.header("connection", "close");
443 }
444 resp.body(Body::empty()).unwrap()
445 }
446 }
447 }
448
449 async fn handle2<ReqBody, ReqData, ReqError>(
451 mut self,
452 req: Request<ReqBody>,
453 ) -> DavResult<Response<Body>>
454 where
455 ReqBody: HttpBody<Data = ReqData, Error = ReqError>,
456 ReqData: Buf + Send + 'static,
457 ReqError: StdError + Send + Sync + 'static,
458 {
459 let (req, body) = {
460 let (parts, body) = req.into_parts();
461 (Request::from_parts(parts, ()), body)
462 };
463
464 if log_enabled!(log::Level::Debug)
466 && let Some(t) = req.headers().typed_get::<davheaders::XLitmus>()
467 {
468 debug!("X-Litmus: {t:?}");
469 }
470
471 let method = match dav_method(req.method()) {
473 Ok(m) => m,
474 Err(e) => {
475 debug!("refusing method {} request {}", req.method(), req.uri());
476 return Err(e);
477 }
478 };
479
480 if is_voidfs::<C>(&self.fs) {
482 match method {
483 DavMethod::Options => {
484 if self
485 .allow
486 .as_ref()
487 .map(|a| a.contains(DavMethod::Options))
488 .unwrap_or(true)
489 {
490 let mut a = DavMethodSet::none();
491 a.add(DavMethod::Options);
492 self.allow = Some(a);
493 }
494 }
495 _ => {
496 debug!("no filesystem: method not allowed on request {}", req.uri());
497 return Err(DavError::StatusClose(StatusCode::METHOD_NOT_ALLOWED));
498 }
499 }
500 }
501
502 if let Some(ref a) = self.allow
504 && !a.contains(method)
505 {
506 debug!(
507 "method {} not allowed on request {}",
508 req.method(),
509 req.uri()
510 );
511 return Err(DavError::StatusClose(StatusCode::METHOD_NOT_ALLOWED));
512 }
513
514 let path = DavPath::from_uri_and_prefix(req.uri(), &self.prefix)?;
516
517 let (body_strm, body_data) = match method {
520 DavMethod::Put | DavMethod::Patch => (Some(body), Vec::new()),
521 _ => (None, self.read_request(body, 65536).await?),
522 };
523
524 match method {
526 DavMethod::Put
527 | DavMethod::Patch
528 | DavMethod::PropFind
529 | DavMethod::PropPatch
530 | DavMethod::Lock
531 | DavMethod::Report
532 | DavMethod::MkCalendar
533 | DavMethod::MkAddressbook => {}
534 _ => {
535 if !body_data.is_empty() {
536 return Err(StatusCode::UNSUPPORTED_MEDIA_TYPE.into());
537 }
538 }
539 }
540
541 debug!("== START REQUEST {method:?} {path}");
542
543 match method {
544 DavMethod::Options => self.handle_options(&req).await,
545 DavMethod::PropFind => self.handle_propfind(&req, &body_data).await,
546 DavMethod::PropPatch => self.handle_proppatch(&req, &body_data).await,
547 DavMethod::MkCol => self.handle_mkcol(&req).await,
548 DavMethod::Delete => self.handle_delete(&req).await,
549 DavMethod::Lock => self.handle_lock(&req, &body_data).await,
550 DavMethod::Unlock => self.handle_unlock(&req).await,
551 DavMethod::Head | DavMethod::Get => self.handle_get(&req).await,
552 DavMethod::Copy | DavMethod::Move => self.handle_copymove(&req, method).await,
553 DavMethod::Put | DavMethod::Patch => self.handle_put(&req, body_strm.unwrap()).await,
554 #[cfg(any(feature = "caldav", feature = "carddav"))]
555 DavMethod::Report => self.handle_report(&req, &body_data).await,
556 #[cfg(feature = "caldav")]
557 DavMethod::MkCalendar => self.handle_mkcalendar(&req, &body_data).await,
558 #[cfg(feature = "carddav")]
559 DavMethod::MkAddressbook => self.handle_mkaddressbook(&req, &body_data).await,
560 #[cfg(not(any(feature = "caldav", feature = "carddav")))]
561 DavMethod::Report => Err(DavError::StatusClose(StatusCode::NOT_IMPLEMENTED)),
562 #[cfg(not(feature = "caldav"))]
563 DavMethod::MkCalendar => Err(DavError::StatusClose(StatusCode::NOT_IMPLEMENTED)),
564 #[cfg(not(feature = "carddav"))]
565 DavMethod::MkAddressbook => Err(DavError::StatusClose(StatusCode::NOT_IMPLEMENTED)),
566 }
567 }
568}