1use std::error::Error as StdError;
6use std::io;
7use std::sync::Arc;
8
9use bytes::{self, buf::Buf};
10use futures::stream::Stream;
11use headers::HeaderMapExt;
12use http::{Request, Response, StatusCode};
13use http_body::Body as HttpBody;
14
15use crate::body::{Body, StreamBody};
16use crate::davheaders;
17use crate::davpath::DavPath;
18use crate::util::{dav_method, DavMethod, DavMethodSet};
19
20use crate::errors::DavError;
21use crate::fs::*;
22use crate::ls::*;
23use crate::voidfs::{is_voidfs, VoidFs};
24use crate::DavResult;
25
26#[derive(Clone)]
32pub struct DavHandler {
33 pub(crate) config: Arc<DavConfig>,
34}
35
36#[derive(Default)]
38pub struct DavConfig {
39 pub(crate) prefix: Option<String>,
41 pub(crate) fs: Option<Box<dyn DavFileSystem>>,
43 pub(crate) ls: Option<Box<dyn DavLockSystem>>,
45 pub(crate) allow: Option<DavMethodSet>,
47 pub(crate) principal: Option<String>,
50 pub(crate) hide_symlinks: Option<bool>,
52 pub(crate) autoindex: Option<bool>,
54 pub(crate) indexfile: Option<String>,
56}
57
58impl DavConfig {
59 pub fn new() -> DavConfig {
61 DavConfig::default()
62 }
63
64 pub fn build_handler(self) -> DavHandler {
66 DavHandler {
67 config: Arc::new(self),
68 }
69 }
70
71 pub fn strip_prefix(self, prefix: impl Into<String>) -> Self {
74 let mut this = self;
75 this.prefix = Some(prefix.into());
76 this
77 }
78
79 pub fn filesystem(self, fs: Box<dyn DavFileSystem>) -> Self {
81 let mut this = self;
82 this.fs = Some(fs);
83 this
84 }
85
86 pub fn locksystem(self, ls: Box<dyn DavLockSystem>) -> Self {
88 let mut this = self;
89 this.ls = Some(ls);
90 this
91 }
92
93 pub fn methods(self, allow: DavMethodSet) -> Self {
95 let mut this = self;
96 this.allow = Some(allow);
97 this
98 }
99
100 pub fn principal(self, principal: impl Into<String>) -> Self {
102 let mut this = self;
103 this.principal = Some(principal.into());
104 this
105 }
106
107 pub fn hide_symlinks(self, hide: bool) -> Self {
109 let mut this = self;
110 this.hide_symlinks = Some(hide);
111 this
112 }
113
114 pub fn autoindex(self, autoindex: bool) -> Self {
116 let mut this = self;
117 this.autoindex = Some(autoindex);
118 this
119 }
120
121 pub fn indexfile(self, indexfile: impl Into<String>) -> Self {
123 let mut this = self;
124 this.indexfile = Some(indexfile.into());
125 this
126 }
127
128 fn merge(&self, new: DavConfig) -> DavConfig {
129 DavConfig {
130 prefix: new.prefix.or(self.prefix.clone()),
131 fs: new.fs.or(self.fs.clone()),
132 ls: new.ls.or(self.ls.clone()),
133 allow: new.allow.or(self.allow.clone()),
134 principal: new.principal.or(self.principal.clone()),
135 hide_symlinks: new.hide_symlinks.or(self.hide_symlinks.clone()),
136 autoindex: new.autoindex.or(self.autoindex.clone()),
137 indexfile: new.indexfile.or(self.indexfile.clone()),
138 }
139 }
140}
141
142pub(crate) struct DavInner {
147 pub prefix: String,
148 pub fs: Box<dyn DavFileSystem>,
149 pub ls: Option<Box<dyn DavLockSystem>>,
150 pub allow: Option<DavMethodSet>,
151 pub principal: Option<String>,
152 pub hide_symlinks: Option<bool>,
153 pub autoindex: Option<bool>,
154 pub indexfile: Option<String>,
155}
156
157impl From<DavConfig> for DavInner {
158 fn from(cfg: DavConfig) -> Self {
159 DavInner {
160 prefix: cfg.prefix.unwrap_or("".to_string()),
161 fs: cfg.fs.unwrap_or(VoidFs::new()),
162 ls: cfg.ls,
163 allow: cfg.allow,
164 principal: cfg.principal,
165 hide_symlinks: cfg.hide_symlinks,
166 autoindex: cfg.autoindex,
167 indexfile: cfg.indexfile,
168 }
169 }
170}
171
172impl From<&DavConfig> for DavInner {
173 fn from(cfg: &DavConfig) -> Self {
174 DavInner {
175 prefix: cfg
176 .prefix
177 .as_ref()
178 .map(|p| p.to_owned())
179 .unwrap_or("".to_string()),
180 fs: cfg.fs.clone().unwrap(),
181 ls: cfg.ls.clone(),
182 allow: cfg.allow,
183 principal: cfg.principal.clone(),
184 hide_symlinks: cfg.hide_symlinks.clone(),
185 autoindex: cfg.autoindex.clone(),
186 indexfile: cfg.indexfile.clone(),
187 }
188 }
189}
190
191impl Clone for DavInner {
192 fn clone(&self) -> Self {
193 DavInner {
194 prefix: self.prefix.clone(),
195 fs: self.fs.clone(),
196 ls: self.ls.clone(),
197 allow: self.allow.clone(),
198 principal: self.principal.clone(),
199 hide_symlinks: self.hide_symlinks.clone(),
200 autoindex: self.autoindex.clone(),
201 indexfile: self.indexfile.clone(),
202 }
203 }
204}
205
206impl DavHandler {
207 pub fn new() -> DavHandler {
214 DavHandler {
215 config: Arc::new(DavConfig::default()),
216 }
217 }
218
219 pub fn builder() -> DavConfig {
221 DavConfig::new()
222 }
223
224 pub async fn handle<ReqBody, ReqData, ReqError>(&self, req: Request<ReqBody>) -> Response<Body>
226 where
227 ReqData: Buf + Send + 'static,
228 ReqError: StdError + Send + Sync + 'static,
229 ReqBody: HttpBody<Data = ReqData, Error = ReqError>,
230 {
231 let inner = DavInner::from(&*self.config);
232 inner.handle(req).await
233 }
234
235 pub async fn handle_with<ReqBody, ReqData, ReqError>(
243 &self,
244 config: DavConfig,
245 req: Request<ReqBody>,
246 ) -> Response<Body>
247 where
248 ReqData: Buf + Send + 'static,
249 ReqError: StdError + Send + Sync + 'static,
250 ReqBody: HttpBody<Data = ReqData, Error = ReqError>,
251 {
252 let inner = DavInner::from(self.config.merge(config));
253 inner.handle(req).await
254 }
255
256 #[doc(hidden)]
260 pub async fn handle_stream<ReqBody, ReqData, ReqError>(&self, req: Request<ReqBody>) -> 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::from(&*self.config);
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::from(self.config.merge(config));
291 inner.handle(req).await
292 }
293}
294
295impl DavInner {
296 pub(crate) async fn has_parent<'a>(&'a self, path: &'a DavPath) -> bool {
298 let p = path.parent();
299 self.fs.metadata(&p).await.map(|m| m.is_dir()).unwrap_or(false)
300 }
301
302 pub(crate) fn path(&self, req: &Request<()>) -> DavPath {
304 DavPath::from_uri_and_prefix(req.uri(), &self.prefix).unwrap()
306 }
307
308 pub(crate) fn fixpath(
311 &self,
312 res: &mut Response<Body>,
313 path: &mut DavPath,
314 meta: Box<dyn DavMetaData>,
315 ) -> Box<dyn DavMetaData>
316 {
317 if meta.is_dir() && !path.is_collection() {
318 path.add_slash();
319 let newloc = path.with_prefix().as_url_string();
320 res.headers_mut()
321 .typed_insert(davheaders::ContentLocation(newloc));
322 }
323 meta
324 }
325
326 pub(crate) async fn read_request<'a, ReqBody, ReqData, ReqError>(
328 &'a self,
329 body: ReqBody,
330 max_size: usize,
331 ) -> DavResult<Vec<u8>>
332 where
333 ReqBody: HttpBody<Data = ReqData, Error = ReqError>,
334 ReqData: Buf + Send + 'static,
335 ReqError: StdError + Send + Sync + 'static,
336 {
337 let mut data = Vec::new();
338 pin_utils::pin_mut!(body);
339 while let Some(res) = body.data().await {
340 let mut buf = res.map_err(|_| {
341 DavError::IoError(io::Error::new(io::ErrorKind::UnexpectedEof, "UnexpectedEof"))
342 })?;
343 while buf.has_remaining() {
344 if data.len() + buf.remaining() > max_size {
345 return Err(StatusCode::PAYLOAD_TOO_LARGE.into());
346 }
347 let b = buf.chunk();
348 let l = b.len();
349 data.extend_from_slice(b);
350 buf.advance(l);
351 }
352 }
353 Ok(data)
354 }
355
356 async fn handle<ReqBody, ReqData, ReqError>(self, req: Request<ReqBody>) -> Response<Body>
358 where
359 ReqBody: HttpBody<Data = ReqData, Error = ReqError>,
360 ReqData: Buf + Send + 'static,
361 ReqError: StdError + Send + Sync + 'static,
362 {
363 let is_ms = req
364 .headers()
365 .get("user-agent")
366 .and_then(|s| s.to_str().ok())
367 .map(|s| s.contains("Microsoft"))
368 .unwrap_or(false);
369
370 match self.handle2(req).await {
372 Ok(resp) => {
373 debug!("== END REQUEST result OK");
374 resp
375 },
376 Err(err) => {
377 debug!("== END REQUEST result {:?}", err);
378 let mut resp = Response::builder();
379 if is_ms && err.statuscode() == StatusCode::NOT_FOUND {
380 resp = resp
390 .header("Cache-Control", "no-store, no-cache, must-revalidate")
391 .header("Progma", "no-cache")
392 .header("Expires", "0")
393 .header("Vary", "*");
394 }
395 resp = resp.header("Content-Length", "0").status(err.statuscode());
396 if err.must_close() {
397 resp = resp.header("connection", "close");
398 }
399 resp.body(Body::empty()).unwrap()
400 },
401 }
402 }
403
404 async fn handle2<ReqBody, ReqData, ReqError>(mut self, req: Request<ReqBody>) -> DavResult<Response<Body>>
406 where
407 ReqBody: HttpBody<Data = ReqData, Error = ReqError>,
408 ReqData: Buf + Send + 'static,
409 ReqError: StdError + Send + Sync + 'static,
410 {
411 let (req, body) = {
412 let (parts, body) = req.into_parts();
413 (Request::from_parts(parts, ()), body)
414 };
415
416 if log_enabled!(log::Level::Debug) {
418 if let Some(t) = req.headers().typed_get::<davheaders::XLitmus>() {
419 debug!("X-Litmus: {:?}", t);
420 }
421 }
422
423 let method = match dav_method(req.method()) {
425 Ok(m) => m,
426 Err(e) => {
427 debug!("refusing method {} request {}", req.method(), req.uri());
428 return Err(e);
429 },
430 };
431
432 if is_voidfs(&self.fs) {
434 match method {
435 DavMethod::Options => {
436 if self
437 .allow
438 .as_ref()
439 .map(|a| a.contains(DavMethod::Options))
440 .unwrap_or(true)
441 {
442 let mut a = DavMethodSet::none();
443 a.add(DavMethod::Options);
444 self.allow = Some(a);
445 }
446 },
447 _ => {
448 debug!("no filesystem: method not allowed on request {}", req.uri());
449 return Err(DavError::StatusClose(StatusCode::METHOD_NOT_ALLOWED));
450 },
451 }
452 }
453
454 if let Some(ref a) = self.allow {
456 if !a.contains(method) {
457 debug!("method {} not allowed on request {}", req.method(), req.uri());
458 return Err(DavError::StatusClose(StatusCode::METHOD_NOT_ALLOWED));
459 }
460 }
461
462 let path = DavPath::from_uri_and_prefix(req.uri(), &self.prefix)?;
464
465 let (body_strm, body_data) = match method {
468 DavMethod::Put | DavMethod::Patch => (Some(body), Vec::new()),
469 _ => (None, self.read_request(body, 65536).await?),
470 };
471
472 match method {
474 DavMethod::Put |
475 DavMethod::Patch |
476 DavMethod::PropFind |
477 DavMethod::PropPatch |
478 DavMethod::Lock => {},
479 _ => {
480 if body_data.len() > 0 {
481 return Err(StatusCode::UNSUPPORTED_MEDIA_TYPE.into());
482 }
483 },
484 }
485
486 debug!("== START REQUEST {:?} {}", method, path);
487
488 let res = match method {
489 DavMethod::Options => self.handle_options(&req).await,
490 DavMethod::PropFind => self.handle_propfind(&req, &body_data).await,
491 DavMethod::PropPatch => self.handle_proppatch(&req, &body_data).await,
492 DavMethod::MkCol => self.handle_mkcol(&req).await,
493 DavMethod::Delete => self.handle_delete(&req).await,
494 DavMethod::Lock => self.handle_lock(&req, &body_data).await,
495 DavMethod::Unlock => self.handle_unlock(&req).await,
496 DavMethod::Head | DavMethod::Get => self.handle_get(&req).await,
497 DavMethod::Copy | DavMethod::Move => self.handle_copymove(&req, method).await,
498 DavMethod::Put | DavMethod::Patch => self.handle_put(&req, body_strm.unwrap()).await,
499 };
500 res
501 }
502}