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