diff --git a/crates/caldav/src/calendar/methods/mkcalendar.rs b/crates/caldav/src/calendar/methods/mkcalendar.rs new file mode 100644 index 0000000..ec4a4b2 --- /dev/null +++ b/crates/caldav/src/calendar/methods/mkcalendar.rs @@ -0,0 +1,84 @@ +use crate::CalDavContext; +use crate::Error; +use actix_web::web::{Data, Path}; +use actix_web::HttpResponse; +use anyhow::Result; +use rustical_auth::{AuthInfoExtractor, CheckAuthentication}; +use rustical_store::calendar::{Calendar, CalendarStore}; +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize, Serialize, Clone, Debug)] +#[serde(rename_all = "kebab-case")] +pub struct Resourcetype { + #[serde(rename = "C:calendar", alias = "calendar")] + calendar: Option<()>, + collection: Option<()>, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(rename_all = "kebab-case")] +pub struct MkcolCalendarProp { + resourcetype: Resourcetype, + displayname: Option, + calendar_description: Option, + calendar_color: Option, + calendar_timezone: Option, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct PropElement { + prop: T, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(rename_all = "kebab-case")] +#[serde(rename = "mkcalendar")] +struct MkcalendarRequest { + set: PropElement, +} + +// TODO: Not sure yet what to send back :) +#[derive(Serialize, Clone, Debug)] +#[serde(rename = "mkcalendar-response")] +struct MkcalendarResponse; + +pub async fn route_mkcol_calendar( + path: Path<(String, String)>, + body: String, + auth: AuthInfoExtractor, + context: Data>, +) -> Result { + let (principal, cid) = path.into_inner(); + if principal != auth.inner.user_id { + return Err(Error::Unauthorized); + } + + let request: MkcalendarRequest = quick_xml::de::from_str(&body).map_err(|e| { + dbg!(e.to_string()); + Error::BadRequest + })?; + let request = request.set.prop; + + let calendar = Calendar { + id: cid.to_owned(), + owner: principal, + name: request.displayname, + timezone: request.calendar_timezone, + color: request.calendar_color, + description: request.calendar_description, + }; + + match context + .store + .write() + .await + .insert_calendar(cid, calendar) + .await + { + Ok(()) => { + let response = quick_xml::se::to_string(&MkcalendarResponse).unwrap(); + Ok(HttpResponse::Created().body(response)) + } + Err(_err) => Ok(HttpResponse::InternalServerError().body("")), + } +} diff --git a/crates/caldav/src/calendar/methods.rs b/crates/caldav/src/calendar/methods/mod.rs similarity index 52% rename from crates/caldav/src/calendar/methods.rs rename to crates/caldav/src/calendar/methods/mod.rs index 41bf64f..9f6b2f7 100644 --- a/crates/caldav/src/calendar/methods.rs +++ b/crates/caldav/src/calendar/methods/mod.rs @@ -11,28 +11,10 @@ use rustical_dav::namespace::Namespace; use rustical_dav::propfind::ServicePrefix; use rustical_dav::resource::HandlePropfind; use rustical_dav::xml_snippets::generate_multistatus; -use rustical_store::calendar::{Calendar, CalendarStore}; +use rustical_store::calendar::CalendarStore; use rustical_store::event::Event; -use tokio::sync::RwLock; -async fn _parse_filter(filter_node: &Node<'_, '_>) { - for comp_filter_node in filter_node.children() { - if comp_filter_node.tag_name().name() != "comp-filter" { - dbg!("wtf", comp_filter_node.tag_name().name()); - continue; - } - - for filter in filter_node.children() { - match filter.tag_name().name() { - // {} - _ => { - dbg!("unknown filter", filter.tag_name()); - } - } - } - } -} +pub mod mkcalendar; async fn handle_report_calendar_query( query_node: Node<'_, '_>, @@ -107,81 +89,6 @@ pub async fn route_report_calendar( - store: &RwLock, - prop_node: Node<'_, '_>, - cid: String, - owner: String, -) -> Result<()> { - let mut cal = Calendar { - owner, - id: cid.clone(), - ..Default::default() - }; - for prop in prop_node.children() { - match prop.tag_name().name() { - "displayname" => { - cal.name = prop.text().map(str::to_string); - } - "timezone" => { - cal.timezone = prop.text().map(str::to_string); - } - "calendar-color" => { - cal.color = prop.text().map(str::to_string); - } - "calendar-description" => { - cal.description = prop.text().map(str::to_string); - } - "calendar-timezone" => { - cal.timezone = prop.text().map(str::to_string); - } - _ => { - println!("unsupported mkcol tag: {}", prop.tag_name().name()) - } - } - } - - store.write().await.insert_calendar(cid, cal).await?; - Ok(()) -} - -pub async fn route_mkcol_calendar( - path: Path<(String, String)>, - body: String, - auth: AuthInfoExtractor, - context: Data>, -) -> Result { - let (_principal, cid) = path.into_inner(); - let doc = roxmltree::Document::parse(&body).map_err(|_e| Error::BadRequest)?; - let mkcol_node = doc.root_element(); - match mkcol_node.tag_name().name() { - "mkcol" => {} - _ => return Err(Error::BadRequest), - } - - // 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), - } - - 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?; - - Ok(HttpResponse::Created().body("")) -} - pub async fn delete_calendar( context: Data>, path: Path<(String, String)>, diff --git a/crates/caldav/src/lib.rs b/crates/caldav/src/lib.rs index 68d857b..30a7c14 100644 --- a/crates/caldav/src/lib.rs +++ b/crates/caldav/src/lib.rs @@ -34,7 +34,7 @@ pub fn configure_dav( ) { let propfind_method = || web::method(Method::from_str("PROPFIND").unwrap()); let report_method = || web::method(Method::from_str("REPORT").unwrap()); - let mkcol_method = || web::method(Method::from_str("MKCOL").unwrap()); + let mkcalendar_method = || web::method(Method::from_str("MKCALENDAR").unwrap()); cfg.app_data(Data::new(CalDavContext { store: store.clone(), @@ -57,7 +57,9 @@ pub fn configure_dav( web::resource("/{principal}/{calendar}") .route(report_method().to(calendar::methods::route_report_calendar::)) .route(propfind_method().to(handle_propfind::>)) - .route(mkcol_method().to(calendar::methods::route_mkcol_calendar::)) + .route( + mkcalendar_method().to(calendar::methods::mkcalendar::route_mkcol_calendar::), + ) .route(web::method(Method::DELETE).to(calendar::methods::delete_calendar::)), ) .service(