1extern crate chrono;
13extern crate hyper;
14#[macro_use]
15extern crate log;
16extern crate url;
17extern crate xml;
18
19use std::borrow::{Borrow, Cow};
20use std::time::{UNIX_EPOCH, SystemTime};
21use std::io::{self, Read, Write, ErrorKind};
22use std::fs::{self, Metadata, read_dir, File};
23use std::path::{Path, PathBuf};
24
25use hyper::header::ContentLength;
26use hyper::method::Method;
27use hyper::server::{Handler, Request, Response};
28use hyper::status::StatusCode;
29use hyper::uri::RequestUri;
30use xml::{EmitterConfig, ParserConfig};
31use xml::common::XmlVersion;
32use xml::name::{Name, OwnedName};
33use xml::reader::XmlEvent;
34use xml::writer::EventWriter;
35use xml::writer::XmlEvent as XmlWEvent;
36
37struct ServerPath {
38 url_prefix: Cow<'static, str>,
40 srv_root: Cow<'static, Path>,
42}
43
44impl ServerPath {
45 fn new<U, R>(url_prefix: U, srv_root: R) -> Self
47 where U: Into<Cow<'static, str>>, R: Into<Cow<'static, Path>> {
48 let url_prefix = url_prefix.into();
49 let srv_root = srv_root.into();
50
51 assert_eq!(url_prefix.trim_right_matches("/"), url_prefix);
52 assert!(srv_root.ends_with("/"));
53
54 ServerPath {
55 url_prefix: url_prefix,
56 srv_root: srv_root,
57 }
58 }
59
60 fn file_to_url<P: AsRef<Path>>(&self, path: P) -> String {
61 let path = path.as_ref()
62 .strip_prefix(&self.srv_root)
63 .expect("file_to_url");
64 self.url_prefix.clone().into_owned() + "/" + path.to_str().expect("file_to_url")
65 }
66
67 fn url_to_file<'a>(&'a self, url: &'a str) -> Option<PathBuf> {
68 if url.starts_with(self.url_prefix.borrow() as &str) {
69 let subpath = &url[self.url_prefix.len()..]
70 .trim_left_matches("/")
71 .trim_right_matches("/");
72 let mut ret = self.srv_root.clone().into_owned();
73 ret.push(subpath);
74 Some(ret)
75 } else {
76 None
77 }
78 }
79}
80
81#[test]
82fn test_serverpath() {
83 let s = ServerPath::new("/dav", Path::new("/"));
84 assert_eq!(s.url_to_file("/dav/foo").unwrap().to_str().unwrap(), "/foo");
85 assert_eq!(s.url_to_file("/dav/foo/").unwrap().to_str().unwrap(),
86 "/foo");
87 assert_eq!(s.url_to_file("/dav/foo//").unwrap().to_str().unwrap(),
88 "/foo");
89 assert_eq!(s.url_to_file("/dav//foo//").unwrap().to_str().unwrap(),
90 "/foo");
91 assert_eq!(&s.file_to_url("/foo"), "/dav/foo");
92 assert_eq!(&s.file_to_url("/"), "/dav/");
93}
94
95pub struct Server {
96 serverpath: ServerPath,
97}
98
99impl Server {
100 pub fn new<U, R>(url_prefix: U, srv_root: R) -> Self
114 where U: Into<Cow<'static, str>>, R: Into<Cow<'static, Path>> {
115 Server { serverpath: ServerPath::new(url_prefix, srv_root) }
116 }
117}
118
119#[derive(Debug)]
120enum RequestType {
121 Options,
122 Propfind,
123 Get,
124 Copy,
125 Move,
126 Delete,
127 Put,
128 Mkdir,
129}
130
131#[derive(Debug)]
132enum Error {
133 ParseError,
134 BadPath,
135 XmlReader(xml::reader::Error),
136 XmlWriter(xml::writer::Error),
137 Io(io::Error),
138}
139
140impl From<xml::reader::Error> for Error {
141 fn from(e: xml::reader::Error) -> Self {
142 Error::XmlReader(e)
143 }
144}
145
146impl From<xml::writer::Error> for Error {
147 fn from(e: xml::writer::Error) -> Self {
148 Error::XmlWriter(e)
149 }
150}
151
152impl From<io::Error> for Error {
153 fn from(e: io::Error) -> Self {
154 Error::Io(e)
155 }
156}
157
158fn parse_propfind<R: Read, F: FnMut(OwnedName) -> ()>(mut xml: xml::reader::EventReader<R>,
159 mut f: F)
160 -> Result<(), Error> {
161 enum State {
162 Start,
163 PropFind,
164 Prop,
165 InProp,
166 }
167
168 let mut state = State::Start;
169
170 loop {
171 let event = xml.next()?;
172 match state {
173 State::Start => {
174 match event {
175 XmlEvent::StartDocument { .. } => (),
176 XmlEvent::StartElement { ref name, .. } if name.local_name == "propfind" => {
177 state = State::PropFind;
178 }
179 _ => return Err(Error::ParseError),
180 }
181 }
182 State::PropFind => {
183 match event {
184 XmlEvent::StartElement { ref name, .. } if name.local_name == "prop" => {
185 state = State::Prop;
186 }
187 _ => return Err(Error::ParseError),
188 }
189 }
190 State::Prop => {
191 match event {
192 XmlEvent::StartElement { name, .. } => {
193 state = State::InProp;
194 f(name);
195 }
196 XmlEvent::EndElement { .. } => {
197 return Ok(());
198 }
199 _ => return Err(Error::ParseError),
200 }
201 }
202 State::InProp => {
203 match event {
204 XmlEvent::EndElement { .. } => {
205 state = State::Prop;
206 }
207 _ => return Err(Error::ParseError),
208 }
209 }
210 }
211 }
212}
213
214fn write_client_prop<W: Write>(xmlwriter: &mut EventWriter<W>,
215 prop: Name)
216 -> Result<(), xml::writer::Error> {
217 if let Some(namespace) = prop.namespace {
218 if let Some(prefix) = prop.prefix {
219 if prefix == "D" && namespace != "DAV:" {
221 let newname = Name {
222 local_name: prop.local_name,
223 namespace: Some(namespace),
224 prefix: Some("U"),
225 };
226 return xmlwriter.write(XmlWEvent::start_element(newname).ns("U", namespace));
227 }
228 }
229 }
230 xmlwriter.write(XmlWEvent::start_element(prop))
231}
232
233fn systime_to_format(time: SystemTime) -> String {
234 use chrono::datetime::DateTime;
235 use chrono::naive::datetime::NaiveDateTime;
236 use chrono::offset::utc::UTC;
237
238 let unix = time.duration_since(UNIX_EPOCH).unwrap();
239 let time = DateTime::<UTC>::from_utc(NaiveDateTime::from_timestamp(unix.as_secs() as i64,
240 unix.subsec_nanos()),
241 UTC);
242 time.to_rfc3339()
243}
244
245fn handle_prop_path<W: Write>(xmlwriter: &mut EventWriter<W>,
246 meta: &Metadata,
247 prop: Name)
248 -> Result<bool, Error> {
249 match (prop.namespace, prop.local_name) {
250 (Some("DAV:"), "resourcetype") => {
251 xmlwriter.write(XmlWEvent::start_element("D:resourcetype"))?;
252 if meta.is_dir() {
253 xmlwriter.write(XmlWEvent::start_element("D:collection"))?;
254 xmlwriter.write(XmlWEvent::end_element())?;
255 }
256 xmlwriter.write(XmlWEvent::end_element())?;
257 Ok(true)
258 }
259 (Some("DAV:"), "creationdate") => {
260 if let Ok(time) = meta.created() {
261 xmlwriter.write(XmlWEvent::start_element("D:creationdate"))?;
262 xmlwriter
263 .write(XmlWEvent::characters(&systime_to_format(time)))?;
264 xmlwriter.write(XmlWEvent::end_element())?;
265 Ok(true)
266 } else {
267 Ok(false)
268 }
269 }
270 (Some("DAV:"), "getlastmodified") => {
271 if let Ok(time) = meta.modified() {
272 xmlwriter
273 .write(XmlWEvent::start_element("D:getlastmodified"))?;
274 xmlwriter
275 .write(XmlWEvent::characters(&systime_to_format(time)))?;
276 xmlwriter.write(XmlWEvent::end_element())?;
277 Ok(true)
278 } else {
279 Ok(false)
280 }
281 }
282 (Some("DAV:"), "getcontentlength") => {
283 xmlwriter
284 .write(XmlWEvent::start_element("D:getcontentlength"))?;
285 xmlwriter
286 .write(XmlWEvent::characters(&meta.len().to_string()))?;
287 xmlwriter.write(XmlWEvent::end_element())?;
288 Ok(true)
289 }
290 (Some("DAV:"), "getcontenttype") => {
291 xmlwriter
292 .write(XmlWEvent::start_element("D:getcontenttype"))?;
293 if meta.is_dir() {
294 xmlwriter
295 .write(XmlWEvent::characters("httpd/unix-directory"))?;
296 } else {
297 xmlwriter.write(XmlWEvent::characters("text/plain"))?;
298 }
299 xmlwriter.write(XmlWEvent::end_element())?;
300 Ok(true)
301 }
302 _ => Ok(false),
303 }
304}
305
306fn handle_propfind_path<W: Write>(xmlwriter: &mut EventWriter<W>,
307 url: &str,
308 meta: &Metadata,
309 props: &[OwnedName])
310 -> Result<(), Error> {
311 xmlwriter.write(XmlWEvent::start_element("D:response"))?;
312
313 xmlwriter.write(XmlWEvent::start_element("D:href"))?;
314 xmlwriter.write(XmlWEvent::characters(url))?;
315 xmlwriter.write(XmlWEvent::end_element())?; let mut failed_props = Vec::with_capacity(props.len());
318 xmlwriter.write(XmlWEvent::start_element("D:propstat"))?;
319 xmlwriter.write(XmlWEvent::start_element("D:prop"))?;
320 for prop in props {
321 if !handle_prop_path(xmlwriter, meta, prop.borrow())? {
322 failed_props.push(prop);
323 }
324 }
325 xmlwriter.write(XmlWEvent::end_element())?; xmlwriter.write(XmlWEvent::start_element("D:status"))?;
327 if failed_props.len() >= props.len() {
328 xmlwriter
330 .write(XmlWEvent::characters("HTTP/1.1 404 Not Found"))?;
331 xmlwriter.write(XmlWEvent::end_element())?; xmlwriter.write(XmlWEvent::end_element())?; xmlwriter.write(XmlWEvent::end_element())?; return Ok(());
335 }
336 xmlwriter.write(XmlWEvent::characters("HTTP/1.1 200 OK"))?;
337 xmlwriter.write(XmlWEvent::end_element())?; xmlwriter.write(XmlWEvent::end_element())?; xmlwriter.write(XmlWEvent::start_element("D:propstat"))?;
342 xmlwriter.write(XmlWEvent::start_element("D:prop"))?;
343 for prop in failed_props {
344 write_client_prop(xmlwriter, prop.borrow())?;
345 xmlwriter.write(XmlWEvent::end_element())?;
346 }
347 xmlwriter.write(XmlWEvent::end_element())?; xmlwriter.write(XmlWEvent::start_element("D:status"))?;
349 xmlwriter
350 .write(XmlWEvent::characters("HTTP/1.1 404 Not Found"))?;
351 xmlwriter.write(XmlWEvent::end_element())?; xmlwriter.write(XmlWEvent::end_element())?; xmlwriter.write(XmlWEvent::end_element())?; Ok(())
355}
356
357fn io_error_to_status(e: io::Error, res: &mut Response<hyper::net::Fresh>) -> io::Error {
358 if e.kind() == ErrorKind::NotFound {
359 *res.status_mut() = StatusCode::NotFound;
360 } else {
361 *res.status_mut() = StatusCode::InternalServerError;
362 }
363 e
364}
365
366impl Server {
367 fn handle_propfind_path_recursive<W: Write>(&self,
368 path: &Path,
369 depth: u32,
370 xmlwriter: &mut EventWriter<W>,
371 props: &[OwnedName])
372 -> Result<(), Error> {
373 if depth == 0 {
374 return Ok(());
375 }
376 for f in read_dir(path)? {
377 let f = match f {
378 Ok(f) => f,
379 Err(e) => {
380 error!("Read dir error. Skipping {:?}", e);
381 continue;
382 }
383 };
384 let path = f.path();
385 let meta = match f.metadata() {
386 Ok(meta) => meta,
387 Err(e) => {
388 error!("Metadata error on {:?}. Skipping {:?}", path, e);
389 continue;
390 }
391 };
392 handle_propfind_path(xmlwriter, &self.serverpath.file_to_url(&path), &meta, props)?;
393 let _ = self.handle_propfind_path_recursive(&path, depth - 1, xmlwriter, props);
397 }
398 Ok(())
399 }
400
401 fn uri_to_path(&self,
402 req: &Request,
403 res: &mut Response<hyper::net::Fresh>)
404 -> Result<PathBuf, Error> {
405 if let RequestUri::AbsolutePath(ref s) = req.uri {
406 let s = url::percent_encoding::percent_decode(s.as_bytes())
408 .decode_utf8()
409 .expect("percent decode");
410 self.serverpath.url_to_file(s.borrow())
411 } else {
412 None
413 }
414 .ok_or_else(|| {
415 *res.status_mut() = StatusCode::NotFound;
416 Error::BadPath
417 })
418 }
419
420 fn uri_to_src_dst(&self,
421 req: &Request,
422 res: &mut Response<hyper::net::Fresh>)
423 -> Result<(PathBuf, PathBuf), Error> {
424 let src = self.uri_to_path(req, res)?;
426
427 let dst = req.headers
429 .get_raw("Destination")
430 .and_then(|vec| vec.get(0))
431 .and_then(|vec| std::str::from_utf8(vec).ok())
432 .and_then(|s| url::Url::parse(s).ok())
433 .ok_or(Error::BadPath)
434 .map_err(|e| {
435 *res.status_mut() = StatusCode::BadRequest;
436 e
437 })?;
438 let dst = url::percent_encoding::percent_decode(dst.path().as_bytes())
439 .decode_utf8()
440 .map_err(|_| Error::BadPath)
441 .and_then(|dst| {
442 self.serverpath
443 .url_to_file(dst.borrow())
444 .ok_or(Error::BadPath)
445 })
446 .map_err(|e| {
447 *res.status_mut() = StatusCode::BadRequest;
448 e
449 })?;
450
451 if src == dst {
452 *res.status_mut() == StatusCode::Forbidden;
453 return Err(Error::BadPath);
454 }
455
456 Ok((src, dst))
457 }
458
459 fn handle_propfind(&self,
460 mut req: Request,
461 mut res: Response<hyper::net::Fresh>)
462 -> Result<(), Error> {
463 let path = self.uri_to_path(&req, &mut res)?;
465
466 let depth = req.headers
468 .get_raw("Depth")
469 .and_then(|vec| vec.get(0))
470 .and_then(|vec| std::str::from_utf8(vec).ok())
471 .and_then(|s| s.parse::<u32>().ok())
472 .unwrap_or(0);
473
474 let xml = xml::reader::EventReader::new_with_config(&mut req,
475 ParserConfig {
476 trim_whitespace: true,
477 ..Default::default()
478 });
479 let mut props = Vec::new();
480 if let Err(e) = parse_propfind(xml, |prop| { props.push(prop); }) {
481 *res.status_mut() = StatusCode::BadRequest;
482 return Err(e);
483 }
484
485 debug!("Propfind {:?} {:?}", path, props);
486
487 let meta = path.metadata()
488 .map_err(|e| io_error_to_status(e, &mut res))?;
489 *res.status_mut() = StatusCode::MultiStatus;
490
491 let mut xmlwriter = EventWriter::new_with_config(res.start()?,
492 EmitterConfig {
493 perform_indent: true,
494 ..Default::default()
495 });
496 xmlwriter
497 .write(XmlWEvent::StartDocument {
498 version: XmlVersion::Version10,
499 encoding: Some("utf-8"),
500 standalone: None,
501 })?;
502 xmlwriter
503 .write(XmlWEvent::start_element("D:multistatus").ns("D", "DAV:"))?;
504
505 handle_propfind_path(&mut xmlwriter,
506 &self.serverpath.file_to_url(&path),
507 &meta,
508 &props)?;
509
510 if meta.is_dir() {
511 self.handle_propfind_path_recursive(&path, depth, &mut xmlwriter, &props)?;
512 }
513
514 xmlwriter.write(XmlWEvent::end_element())?;
515 Ok(())
516 }
517
518 fn handle_get(&self, req: Request, mut res: Response<hyper::net::Fresh>) -> Result<(), Error> {
519 let path = self.uri_to_path(&req, &mut res)?;
521 let mut file = File::open(path)
522 .map_err(|e| io_error_to_status(e, &mut res))?;
523 let size = file.metadata()
524 .map(|m| m.len())
525 .map_err(|e| io_error_to_status(e, &mut res))?;
526
527 if size > 0 {
529 res.headers_mut().set(ContentLength(size))
530 }
531
532 io::copy(&mut file, &mut res.start()?)?;
534 Ok(())
535 }
536
537 fn handle_put(&self,
538 mut req: Request,
539 mut res: Response<hyper::net::Fresh>)
540 -> Result<(), Error> {
541 let path = self.uri_to_path(&req, &mut res)?;
542 let mut file = File::create(path)
543 .map_err(|e| io_error_to_status(e, &mut res))?;
544 io::copy(&mut req, &mut file)?;
545 Ok(())
546 }
547
548 fn handle_copy(&self, req: Request, mut res: Response<hyper::net::Fresh>) -> Result<(), Error> {
549 let (src, dst) = self.uri_to_src_dst(&req, &mut res)?;
550 debug!("Copy {:?} -> {:?}", src, dst);
551
552 fs::copy(src, dst)
555 .map_err(|e| io_error_to_status(e, &mut res))?;
556 *res.status_mut() == StatusCode::Created;
557 Ok(())
558 }
559
560 fn handle_move(&self, req: Request, mut res: Response<hyper::net::Fresh>) -> Result<(), Error> {
561 let (src, dst) = self.uri_to_src_dst(&req, &mut res)?;
562 debug!("Move {:?} -> {:?}", src, dst);
563
564 fs::rename(src, dst)
566 .map_err(|e| io_error_to_status(e, &mut res))?;
567 *res.status_mut() == StatusCode::Created;
568 Ok(())
569 }
570
571 fn handle_delete(&self,
572 req: Request,
573 mut res: Response<hyper::net::Fresh>)
574 -> Result<(), Error> {
575 let path = self.uri_to_path(&req, &mut res)?;
577 let meta = path.metadata()
578 .map_err(|e| io_error_to_status(e, &mut res))?;
579 if meta.is_dir() {
580 fs::remove_dir_all(path)
581 } else {
582 fs::remove_file(path)
583 }
584 .map_err(|e| io_error_to_status(e, &mut res))?;
585 Ok(())
586 }
587
588 fn handle_mkdir(&self,
589 req: Request,
590 mut res: Response<hyper::net::Fresh>)
591 -> Result<(), Error> {
592 let path = self.uri_to_path(&req, &mut res)?;
593 let ret = fs::create_dir(path);
594 match ret {
595 Ok(_) => *res.status_mut() = StatusCode::Created,
596 Err(ref e) if e.kind() == ErrorKind::NotFound => {
597 *res.status_mut() = StatusCode::Conflict;
598 }
599 Err(_) => *res.status_mut() = StatusCode::InternalServerError,
600 };
601 ret.map_err(Into::into)
602 }
603}
604
605impl Handler for Server {
606 fn handle<'a, 'k>(&'a self, req: Request<'a, 'k>, mut res: Response<'a, hyper::net::Fresh>) {
607 debug!("Request {:?}", req.method);
608
609 let reqtype = match req.method {
610 Method::Options => RequestType::Options,
611 Method::Get => RequestType::Get,
612 Method::Put => RequestType::Put,
613 Method::Delete => RequestType::Delete,
614 Method::Extension(ref s) if s == "PROPFIND" => RequestType::Propfind,
615 Method::Extension(ref s) if s == "COPY" => RequestType::Copy,
616 Method::Extension(ref s) if s == "MOVE" => RequestType::Move,
617 Method::Extension(ref s) if s == "MKCOL" => RequestType::Mkdir,
618 _ => {
619 *res.status_mut() = StatusCode::BadRequest;
620 return;
621 }
622 };
623
624 if let Err(e) = match reqtype {
625 RequestType::Options => {
626 res.headers_mut()
627 .set(hyper::header::Allow(vec![Method::Options,
628 Method::Get,
629 Method::Put,
630 Method::Delete,
631 Method::Extension("PROPFIND".into()),
632 Method::Extension("COPY".into()),
633 Method::Extension("MOVE".into()),
634 Method::Extension("MKCOL".into())]));
635 res.headers_mut().set_raw("DAV", vec![b"1".to_vec()]);
636 Ok(())
637 }
638 RequestType::Propfind => self.handle_propfind(req, res),
639 RequestType::Get => self.handle_get(req, res),
640 RequestType::Put => self.handle_put(req, res),
641 RequestType::Copy => self.handle_copy(req, res),
642 RequestType::Move => self.handle_move(req, res),
643 RequestType::Delete => self.handle_delete(req, res),
644 RequestType::Mkdir => self.handle_mkdir(req, res),
645 } {
646 error!("Request error {:?}", e)
647 }
648 }
649}