diff --git a/crates/dav/src/propfind.rs b/crates/dav/src/propfind.rs index ee5700c..5c5e745 100644 --- a/crates/dav/src/propfind.rs +++ b/crates/dav/src/propfind.rs @@ -70,6 +70,29 @@ pub fn write_invalid_props_response( Ok(()) } +pub fn write_propstat_element( + writer: &mut Writer, + status: StatusCode, + prop_closure: F, +) -> Result<(), quick_xml::Error> +where + F: FnOnce(&mut Writer) -> Result<(), quick_xml::Error>, +{ + writer + .create_element("propstat") + .write_inner_content(|writer| { + writer + .create_element("prop") + .write_inner_content(prop_closure)?; + + writer + .create_element("status") + .write_text_content(BytesText::new(&format!("HTTP/1.1 {}", status)))?; + Ok(()) + })?; + Ok(()) +} + // Writes a propstat response into a multistatus // closure hooks into the element pub fn write_propstat_response( @@ -88,18 +111,7 @@ where .create_element("href") .write_text_content(BytesText::new(href))?; - writer - .create_element("propstat") - .write_inner_content(|writer| { - writer - .create_element("prop") - .write_inner_content(prop_closure)?; - - writer - .create_element("status") - .write_text_content(BytesText::new(&format!("HTTP/1.1 {}", status)))?; - Ok(()) - })?; + write_propstat_element(writer, status, prop_closure)?; Ok(()) })?; @@ -124,3 +136,22 @@ where std::str::from_utf8(&output_buffer)? )) } + +pub fn generate_mkcol_response<'a, F, A>(namespaces: A, closure: F) -> Result +where + F: FnOnce(&mut Writer<&mut Vec>) -> Result<(), quick_xml::Error>, + A: IntoIterator, + A::Item: Into>, +{ + let mut output_buffer = Vec::new(); + let mut writer = Writer::new_with_indent(&mut output_buffer, b' ', 2); + writer + .create_element("mkcol-response") + .with_attributes(namespaces) + .write_inner_content(closure)?; + + Ok(format!( + "\n{}", + std::str::from_utf8(&output_buffer)? + )) +} diff --git a/crates/dav/src/routes/calendar.rs b/crates/dav/src/routes/calendar.rs index de65a36..53aa847 100644 --- a/crates/dav/src/routes/calendar.rs +++ b/crates/dav/src/routes/calendar.rs @@ -1,5 +1,7 @@ use crate::namespace::Namespace; -use crate::propfind::{generate_multistatus, write_propstat_response}; +use crate::propfind::{ + generate_mkcol_response, generate_multistatus, write_propstat_element, write_propstat_response, +}; use crate::proptypes::write_string_prop; use crate::{CalDavContext, Error}; use actix_web::http::header::ContentType; @@ -167,24 +169,26 @@ pub async fn route_mkcol_calendar( _ => return Err(Error::BadRequest), } - for set_node in mkcol_node.children() { - if set_node.tag_name().name() != "set" { - return Err(Error::BadRequest); - } - let prop_node = set_node.first_element_child().ok_or(Error::BadRequest)?; - if prop_node.tag_name().name() != "prop" { - return Err(Error::BadRequest); - } - handle_mkcol_calendar_set( - &context.store, - prop_node, - cid.clone(), - auth.inner.user_id.clone(), - ) - .await - .map_err(|_e| Error::InternalError)?; + // TODO: Why does the spec (rfc5689) allow multiple elements but only one resource? :/ + // Well, for now just bother with the first one + let set_node = mkcol_node.first_element_child().ok_or(Error::BadRequest)?; + match set_node.tag_name().name() { + "set" => {} + _ => return Err(Error::BadRequest), } - println!("{body}"); - Err(Error::InternalError) + let prop_node = set_node.first_element_child().ok_or(Error::BadRequest)?; + if prop_node.tag_name().name() != "prop" { + return Err(Error::BadRequest); + } + handle_mkcol_calendar_set( + &context.store, + prop_node, + cid.clone(), + auth.inner.user_id.clone(), + ) + .await + .map_err(|_e| Error::InternalError)?; + + Ok(HttpResponse::Created().body("")) }