diff --git a/crates/caldav/src/calendar/methods/mkcalendar.rs b/crates/caldav/src/calendar/methods/mkcalendar.rs index 32f942b..ab36b08 100644 --- a/crates/caldav/src/calendar/methods/mkcalendar.rs +++ b/crates/caldav/src/calendar/methods/mkcalendar.rs @@ -5,7 +5,6 @@ use rustical_store::auth::User; use rustical_store::model::Calendar; use rustical_store::CalendarStore; use serde::{Deserialize, Serialize}; -use tokio::sync::RwLock; #[derive(Deserialize, Serialize, Clone, Debug)] #[serde(rename_all = "kebab-case")] @@ -57,7 +56,7 @@ pub async fn route_mkcalendar( path: Path<(String, String)>, body: String, user: User, - store: Data>, + store: Data, ) -> Result { let (principal, cal_id) = path.into_inner(); if principal != user.id { @@ -79,7 +78,7 @@ pub async fn route_mkcalendar( synctoken: 0, }; - match store.read().await.get_calendar(&principal, &cal_id).await { + match store.get_calendar(&principal, &cal_id).await { Err(rustical_store::Error::NotFound) => { // No conflict, no worries } @@ -93,7 +92,7 @@ pub async fn route_mkcalendar( } } - match store.write().await.insert_calendar(calendar).await { + match store.insert_calendar(calendar).await { // The spec says we should return a mkcalendar-response but I don't know what goes into it. // However, it works without one but breaks on iPadOS when using an empty one :) Ok(()) => Ok(HttpResponse::Created() diff --git a/crates/caldav/src/calendar/methods/report/calendar_multiget.rs b/crates/caldav/src/calendar/methods/report/calendar_multiget.rs index df8ff75..efa4be3 100644 --- a/crates/caldav/src/calendar/methods/report/calendar_multiget.rs +++ b/crates/caldav/src/calendar/methods/report/calendar_multiget.rs @@ -18,7 +18,6 @@ use rustical_dav::{ }; use rustical_store::{model::object::CalendarObject, CalendarStore}; use serde::Deserialize; -use tokio::sync::RwLock; #[derive(Deserialize, Clone, Debug)] #[serde(rename_all = "kebab-case")] @@ -35,7 +34,7 @@ pub async fn get_objects_calendar_multiget( principal_url: &str, principal: &str, cal_id: &str, - store: &RwLock, + store: &C, ) -> Result<(Vec, Vec), Error> { let resource_def = ResourceDef::prefix(principal_url).join(&ResourceDef::new("/{cal_id}/{object_id}")); @@ -43,7 +42,6 @@ pub async fn get_objects_calendar_multiget( let mut result = vec![]; let mut not_found = vec![]; - let store = store.read().await; for href in &cal_query.href { let mut path = Path::new(href.as_str()); if !resource_def.capture_match_info(&mut path) { @@ -69,7 +67,7 @@ pub async fn handle_calendar_multiget( req: HttpRequest, principal: &str, cal_id: &str, - cal_store: &RwLock, + cal_store: &C, ) -> Result, String>, Error> { let principal_url = PrincipalResource::get_url(req.resource_map(), vec![principal]).unwrap(); let (objects, not_found) = diff --git a/crates/caldav/src/calendar/methods/report/calendar_query.rs b/crates/caldav/src/calendar/methods/report/calendar_query.rs index 2ed5e84..9fbb915 100644 --- a/crates/caldav/src/calendar/methods/report/calendar_query.rs +++ b/crates/caldav/src/calendar/methods/report/calendar_query.rs @@ -7,7 +7,6 @@ use rustical_dav::{ }; use rustical_store::{model::object::CalendarObject, CalendarStore}; use serde::Deserialize; -use tokio::sync::RwLock; use crate::{ calendar_object::resource::{CalendarObjectProp, CalendarObjectResource}, @@ -195,9 +194,9 @@ pub async fn get_objects_calendar_query( cal_query: &CalendarQueryRequest, principal: &str, cal_id: &str, - store: &RwLock, + store: &C, ) -> Result, Error> { - let mut objects = store.read().await.get_objects(principal, cal_id).await?; + let mut objects = store.get_objects(principal, cal_id).await?; if let Some(filter) = &cal_query.filter { objects.retain(|object| filter.matches(object)); } @@ -209,7 +208,7 @@ pub async fn handle_calendar_query( req: HttpRequest, principal: &str, cal_id: &str, - cal_store: &RwLock, + cal_store: &C, ) -> Result, String>, Error> { let objects = get_objects_calendar_query(&cal_query, principal, cal_id, cal_store).await?; diff --git a/crates/caldav/src/calendar/methods/report/mod.rs b/crates/caldav/src/calendar/methods/report/mod.rs index b652d51..c54ccb5 100644 --- a/crates/caldav/src/calendar/methods/report/mod.rs +++ b/crates/caldav/src/calendar/methods/report/mod.rs @@ -8,7 +8,6 @@ use calendar_query::{handle_calendar_query, CalendarQueryRequest}; use rustical_store::{auth::User, CalendarStore}; use serde::{Deserialize, Serialize}; use sync_collection::{handle_sync_collection, SyncCollectionRequest}; -use tokio::sync::RwLock; use tracing::instrument; mod calendar_multiget; @@ -37,7 +36,7 @@ pub async fn route_report_calendar( body: String, user: User, req: HttpRequest, - cal_store: Data>, + cal_store: Data, ) -> Result { let (principal, cal_id) = path.into_inner(); if principal != user.id { @@ -48,13 +47,21 @@ pub async fn route_report_calendar( Ok(match request.clone() { ReportRequest::CalendarQuery(cal_query) => { - handle_calendar_query(cal_query, req, &principal, &cal_id, &cal_store).await? + handle_calendar_query(cal_query, req, &principal, &cal_id, cal_store.as_ref()).await? } ReportRequest::CalendarMultiget(cal_multiget) => { - handle_calendar_multiget(cal_multiget, req, &principal, &cal_id, &cal_store).await? + handle_calendar_multiget(cal_multiget, req, &principal, &cal_id, cal_store.as_ref()) + .await? } ReportRequest::SyncCollection(sync_collection) => { - handle_sync_collection(sync_collection, req, &principal, &cal_id, &cal_store).await? + handle_sync_collection( + sync_collection, + req, + &principal, + &cal_id, + cal_store.as_ref(), + ) + .await? } }) } diff --git a/crates/caldav/src/calendar/methods/report/sync_collection.rs b/crates/caldav/src/calendar/methods/report/sync_collection.rs index 201c8e7..7d40c02 100644 --- a/crates/caldav/src/calendar/methods/report/sync_collection.rs +++ b/crates/caldav/src/calendar/methods/report/sync_collection.rs @@ -12,7 +12,6 @@ use rustical_store::{ CalendarStore, }; use serde::Deserialize; -use tokio::sync::RwLock; use crate::{ calendar_object::resource::{CalendarObjectProp, CalendarObjectResource}, @@ -47,7 +46,7 @@ pub async fn handle_sync_collection( req: HttpRequest, principal: &str, cal_id: &str, - cal_store: &RwLock, + cal_store: &C, ) -> Result, String>, Error> { let props = match sync_collection.prop { PropfindType::Allprop => { @@ -62,8 +61,6 @@ pub async fn handle_sync_collection( let old_synctoken = parse_synctoken(&sync_collection.sync_token).unwrap_or(0); let (new_objects, deleted_objects, new_synctoken) = cal_store - .read() - .await .sync_changes(principal, cal_id, old_synctoken) .await?; diff --git a/crates/caldav/src/calendar/resource.rs b/crates/caldav/src/calendar/resource.rs index f5e7a7e..0975bb3 100644 --- a/crates/caldav/src/calendar/resource.rs +++ b/crates/caldav/src/calendar/resource.rs @@ -21,10 +21,9 @@ use serde::{Deserialize, Serialize}; use std::str::FromStr; use std::sync::Arc; use strum::{EnumString, VariantNames}; -use tokio::sync::RwLock; pub struct CalendarResourceService { - pub cal_store: Arc>, + pub cal_store: Arc, pub path: String, pub principal: String, pub calendar_id: String, @@ -261,8 +260,6 @@ impl ResourceService for CalendarResourceService { } let calendar = self .cal_store - .read() - .await .get_calendar(&self.principal, &self.calendar_id) .await .map_err(|_e| Error::NotFound)?; @@ -275,8 +272,6 @@ impl ResourceService for CalendarResourceService { ) -> Result, Self::Error> { Ok(self .cal_store - .read() - .await .get_objects(&self.principal, &self.calendar_id) .await? .into_iter() @@ -298,7 +293,7 @@ impl ResourceService for CalendarResourceService { path_components: Self::PathComponents, ) -> Result { let cal_store = req - .app_data::>>() + .app_data::>() .expect("no calendar store in app_data!") .clone() .into_inner(); @@ -313,8 +308,6 @@ impl ResourceService for CalendarResourceService { async fn save_resource(&self, file: Self::Resource) -> Result<(), Self::Error> { self.cal_store - .write() - .await .update_calendar( self.principal.to_owned(), self.calendar_id.to_owned(), @@ -326,8 +319,6 @@ impl ResourceService for CalendarResourceService { async fn delete_resource(&self, use_trashbin: bool) -> Result<(), Self::Error> { self.cal_store - .write() - .await .delete_calendar(&self.principal, &self.calendar_id, use_trashbin) .await?; Ok(()) diff --git a/crates/caldav/src/calendar_object/methods.rs b/crates/caldav/src/calendar_object/methods.rs index 40f4cf7..8b6d4da 100644 --- a/crates/caldav/src/calendar_object/methods.rs +++ b/crates/caldav/src/calendar_object/methods.rs @@ -7,7 +7,6 @@ use actix_web::HttpResponse; use rustical_store::auth::User; use rustical_store::model::CalendarObject; use rustical_store::CalendarStore; -use tokio::sync::RwLock; use tracing::instrument; use tracing_actix_web::RootSpan; @@ -16,7 +15,7 @@ use super::resource::CalendarObjectPathComponents; #[instrument(parent = root_span.id(), skip(store, root_span))] pub async fn get_event( path: Path, - store: Data>, + store: Data, user: User, root_span: RootSpan, ) -> Result { @@ -30,16 +29,12 @@ pub async fn get_event( return Ok(HttpResponse::Unauthorized().body("")); } - let calendar = store.read().await.get_calendar(&principal, &cal_id).await?; + let calendar = store.get_calendar(&principal, &cal_id).await?; if user.id != calendar.principal { return Ok(HttpResponse::Unauthorized().body("")); } - let event = store - .read() - .await - .get_object(&principal, &cal_id, &object_id) - .await?; + let event = store.get_object(&principal, &cal_id, &object_id).await?; Ok(HttpResponse::Ok() .insert_header(("ETag", event.get_etag())) @@ -50,7 +45,7 @@ pub async fn get_event( #[instrument(parent = root_span.id(), skip(store, req, root_span))] pub async fn put_event( path: Path, - store: Data>, + store: Data, body: String, user: User, req: HttpRequest, @@ -66,37 +61,16 @@ pub async fn put_event( return Ok(HttpResponse::Unauthorized().body("")); } - let calendar = store.read().await.get_calendar(&principal, &cal_id).await?; - if user.id != calendar.principal { - return Ok(HttpResponse::Unauthorized().body("")); - } - // TODO: implement If-Match // - let mut store_write = store.write().await; - if Some(&HeaderValue::from_static("*")) == req.headers().get(header::IF_NONE_MATCH) { - // Only write if not existing - match store_write - .get_object(&principal, &cal_id, &object_id) - .await - { - Ok(_) => { - // Conflict - return Ok(HttpResponse::Conflict().body("Resource with this URI already exists")); - } - Err(rustical_store::Error::NotFound) => { - // Path unused, we can proceed - } - Err(err) => { - // Some unknown error :( - return Err(err.into()); - } - } - } + let overwrite = + Some(&HeaderValue::from_static("*")) != req.headers().get(header::IF_NONE_MATCH); let object = CalendarObject::from_ics(object_id, body)?; - store_write.put_object(principal, cal_id, object).await?; + store + .put_object(principal, cal_id, object, overwrite) + .await?; Ok(HttpResponse::Created().body("")) } diff --git a/crates/caldav/src/calendar_object/resource.rs b/crates/caldav/src/calendar_object/resource.rs index 9477745..715c002 100644 --- a/crates/caldav/src/calendar_object/resource.rs +++ b/crates/caldav/src/calendar_object/resource.rs @@ -1,3 +1,4 @@ +use super::methods::{get_event, put_event}; use crate::Error; use actix_web::{dev::ResourceMap, web::Data, HttpRequest}; use async_trait::async_trait; @@ -8,12 +9,9 @@ use rustical_store::CalendarStore; use serde::{Deserialize, Serialize}; use std::sync::Arc; use strum::{EnumString, VariantNames}; -use tokio::sync::RwLock; - -use super::methods::{get_event, put_event}; pub struct CalendarObjectResourceService { - pub cal_store: Arc>, + pub cal_store: Arc, pub path: String, pub principal: String, pub cal_id: String, @@ -121,7 +119,7 @@ impl ResourceService for CalendarObjectResourceServic } = path_components; let cal_store = req - .app_data::>>() + .app_data::>() .expect("no calendar store in app_data!") .clone() .into_inner(); @@ -141,8 +139,6 @@ impl ResourceService for CalendarObjectResourceServic } let event = self .cal_store - .read() - .await .get_object(&self.principal, &self.cal_id, &self.object_id) .await?; Ok(event.into()) @@ -154,8 +150,6 @@ impl ResourceService for CalendarObjectResourceServic async fn delete_resource(&self, use_trashbin: bool) -> Result<(), Self::Error> { self.cal_store - .write() - .await .delete_object(&self.principal, &self.cal_id, &self.object_id, use_trashbin) .await?; Ok(()) diff --git a/crates/caldav/src/error.rs b/crates/caldav/src/error.rs index 5edb3de..270684f 100644 --- a/crates/caldav/src/error.rs +++ b/crates/caldav/src/error.rs @@ -29,7 +29,7 @@ impl actix_web::ResponseError for Error { match self { Error::StoreError(err) => match err { rustical_store::Error::NotFound => StatusCode::NOT_FOUND, - rustical_store::Error::InvalidIcs(_) => StatusCode::BAD_REQUEST, + rustical_store::Error::InvalidData(_) => StatusCode::BAD_REQUEST, _ => StatusCode::INTERNAL_SERVER_ERROR, }, Error::DavError(err) => err.status_code(), diff --git a/crates/caldav/src/lib.rs b/crates/caldav/src/lib.rs index 540443f..93aa7ea 100644 --- a/crates/caldav/src/lib.rs +++ b/crates/caldav/src/lib.rs @@ -11,7 +11,6 @@ use rustical_dav::resource::ResourceService; use rustical_store::auth::{AuthenticationMiddleware, AuthenticationProvider}; use rustical_store::CalendarStore; use std::sync::Arc; -use tokio::sync::RwLock; pub mod calendar; pub mod calendar_object; @@ -28,7 +27,7 @@ pub fn configure_well_known(cfg: &mut web::ServiceConfig, caldav_root: String) { pub fn configure_dav( cfg: &mut web::ServiceConfig, auth_provider: Arc, - store: Arc>, + store: Arc, ) { cfg.service( web::scope("") diff --git a/crates/caldav/src/principal/mod.rs b/crates/caldav/src/principal/mod.rs index a35b19c..80c5737 100644 --- a/crates/caldav/src/principal/mod.rs +++ b/crates/caldav/src/principal/mod.rs @@ -1,3 +1,4 @@ +use crate::calendar::resource::CalendarResource; use crate::Error; use actix_web::dev::ResourceMap; use actix_web::web::Data; @@ -9,13 +10,10 @@ use rustical_store::CalendarStore; use serde::{Deserialize, Serialize}; use std::sync::Arc; use strum::{EnumString, VariantNames}; -use tokio::sync::RwLock; - -use crate::calendar::resource::CalendarResource; pub struct PrincipalResourceService { principal: String, - cal_store: Arc>, + cal_store: Arc, } #[derive(Clone)] @@ -113,7 +111,7 @@ impl ResourceService for PrincipalResourceService (principal,): Self::PathComponents, ) -> Result { let cal_store = req - .app_data::>>() + .app_data::>() .expect("no calendar store in app_data!") .clone() .into_inner(); @@ -137,12 +135,7 @@ impl ResourceService for PrincipalResourceService &self, rmap: &ResourceMap, ) -> Result, Self::Error> { - let calendars = self - .cal_store - .read() - .await - .get_calendars(&self.principal) - .await?; + let calendars = self.cal_store.get_calendars(&self.principal).await?; Ok(calendars .into_iter() .map(|cal| { diff --git a/crates/carddav/src/address_object/methods.rs b/crates/carddav/src/address_object/methods.rs index 25a40ee..91b061f 100644 --- a/crates/carddav/src/address_object/methods.rs +++ b/crates/carddav/src/address_object/methods.rs @@ -8,14 +8,13 @@ use actix_web::HttpResponse; use rustical_store::auth::User; use rustical_store::model::AddressObject; use rustical_store::AddressbookStore; -use tokio::sync::RwLock; use tracing::instrument; use tracing_actix_web::RootSpan; #[instrument(parent = root_span.id(), skip(store, root_span))] pub async fn get_object( path: Path, - store: Data>, + store: Data, user: User, root_span: RootSpan, ) -> Result { @@ -29,20 +28,12 @@ pub async fn get_object( return Ok(HttpResponse::Unauthorized().body("")); } - let addressbook = store - .read() - .await - .get_addressbook(&principal, &cal_id) - .await?; + let addressbook = store.get_addressbook(&principal, &cal_id).await?; if user.id != addressbook.principal { return Ok(HttpResponse::Unauthorized().body("")); } - let object = store - .read() - .await - .get_object(&principal, &cal_id, &object_id) - .await?; + let object = store.get_object(&principal, &cal_id, &object_id).await?; Ok(HttpResponse::Ok() .insert_header(("ETag", object.get_etag())) @@ -53,7 +44,7 @@ pub async fn get_object( #[instrument(parent = root_span.id(), skip(store, req, root_span))] pub async fn put_object( path: Path, - store: Data>, + store: Data, body: String, user: User, req: HttpRequest, @@ -69,42 +60,15 @@ pub async fn put_object( return Ok(HttpResponse::Unauthorized().body("")); } - let addressbook = store - .read() - .await - .get_addressbook(&principal, &addressbook_id) - .await?; - if user.id != addressbook.principal { - return Ok(HttpResponse::Unauthorized().body("")); - } - // TODO: implement If-Match // - let mut store_write = store.write().await; - if Some(&HeaderValue::from_static("*")) == req.headers().get(header::IF_NONE_MATCH) { - // Only write if not existing - match store_write - .get_object(&principal, &addressbook_id, &object_id) - .await - { - Ok(_) => { - // Conflict - return Ok(HttpResponse::Conflict().body("Resource with this URI already exists")); - } - Err(rustical_store::Error::NotFound) => { - // Path unused, we can proceed - } - Err(err) => { - // Some unknown error :( - return Err(err.into()); - } - } - } + let overwrite = + Some(&HeaderValue::from_static("*")) != req.headers().get(header::IF_NONE_MATCH); let object = AddressObject::from_vcf(object_id, body)?; - store_write - .put_object(principal, addressbook_id, object) + store + .put_object(principal, addressbook_id, object, overwrite) .await?; Ok(HttpResponse::Created().body("")) diff --git a/crates/carddav/src/address_object/resource.rs b/crates/carddav/src/address_object/resource.rs index 7f10749..9426449 100644 --- a/crates/carddav/src/address_object/resource.rs +++ b/crates/carddav/src/address_object/resource.rs @@ -7,12 +7,11 @@ use rustical_store::{model::AddressObject, AddressbookStore}; use serde::{Deserialize, Serialize}; use std::sync::Arc; use strum::{EnumString, VariantNames}; -use tokio::sync::RwLock; use super::methods::{get_object, put_object}; pub struct AddressObjectResourceService { - pub addr_store: Arc>, + pub addr_store: Arc, pub path: String, pub principal: String, pub cal_id: String, @@ -120,7 +119,7 @@ impl ResourceService for AddressObjectResourceSer } = path_components; let addr_store = req - .app_data::>>() + .app_data::>() .expect("no addressbook store in app_data!") .clone() .into_inner(); @@ -140,8 +139,6 @@ impl ResourceService for AddressObjectResourceSer } let event = self .addr_store - .read() - .await .get_object(&self.principal, &self.cal_id, &self.object_id) .await?; Ok(event.into()) @@ -153,8 +150,6 @@ impl ResourceService for AddressObjectResourceSer async fn delete_resource(&self, use_trashbin: bool) -> Result<(), Self::Error> { self.addr_store - .write() - .await .delete_object(&self.principal, &self.cal_id, &self.object_id, use_trashbin) .await?; Ok(()) diff --git a/crates/carddav/src/addressbook/methods/mkcol.rs b/crates/carddav/src/addressbook/methods/mkcol.rs index 47c6806..ce04e36 100644 --- a/crates/carddav/src/addressbook/methods/mkcol.rs +++ b/crates/carddav/src/addressbook/methods/mkcol.rs @@ -4,7 +4,6 @@ use actix_web::{web::Data, HttpResponse}; use rustical_store::model::Addressbook; use rustical_store::{auth::User, AddressbookStore}; use serde::{Deserialize, Serialize}; -use tokio::sync::RwLock; #[derive(Deserialize, Serialize, Clone, Debug)] #[serde(rename_all = "kebab-case")] @@ -38,7 +37,7 @@ pub async fn route_mkcol( path: Path<(String, String)>, body: String, user: User, - store: Data>, + store: Data, ) -> Result { let (principal, addressbook_id) = path.into_inner(); if principal != user.id { @@ -57,12 +56,7 @@ pub async fn route_mkcol( synctoken: 0, }; - match store - .read() - .await - .get_addressbook(&principal, &addressbook_id) - .await - { + match store.get_addressbook(&principal, &addressbook_id).await { Err(rustical_store::Error::NotFound) => { // No conflict, no worries } @@ -76,7 +70,7 @@ pub async fn route_mkcol( } } - match store.write().await.insert_addressbook(addressbook).await { + match store.insert_addressbook(addressbook).await { // TODO: The spec says we should return a mkcol-response. // However, it works without one but breaks on iPadOS when using an empty one :) Ok(()) => Ok(HttpResponse::Created() diff --git a/crates/carddav/src/addressbook/methods/report/addressbook_multiget.rs b/crates/carddav/src/addressbook/methods/report/addressbook_multiget.rs index b4c2f25..a361b5e 100644 --- a/crates/carddav/src/addressbook/methods/report/addressbook_multiget.rs +++ b/crates/carddav/src/addressbook/methods/report/addressbook_multiget.rs @@ -18,7 +18,6 @@ use rustical_dav::{ }; use rustical_store::{model::AddressObject, AddressbookStore}; use serde::Deserialize; -use tokio::sync::RwLock; #[derive(Deserialize, Clone, Debug)] #[serde(rename_all = "kebab-case")] @@ -34,7 +33,7 @@ pub async fn get_objects_addressbook_multiget( principal_url: &str, principal: &str, addressbook_id: &str, - store: &RwLock, + store: &AS, ) -> Result<(Vec, Vec), Error> { let resource_def = ResourceDef::prefix(principal_url).join(&ResourceDef::new("/{addressbook_id}/{object_id}")); @@ -42,7 +41,6 @@ pub async fn get_objects_addressbook_multiget( let mut result = vec![]; let mut not_found = vec![]; - let store = store.read().await; for href in &addressbook_multiget.href { let mut path = Path::new(href.as_str()); if !resource_def.capture_match_info(&mut path) { @@ -68,7 +66,7 @@ pub async fn handle_addressbook_multiget( req: HttpRequest, principal: &str, cal_id: &str, - addr_store: &RwLock, + addr_store: &AS, ) -> Result, String>, Error> { let principal_url = PrincipalResource::get_url(req.resource_map(), vec![principal]).unwrap(); let (objects, not_found) = get_objects_addressbook_multiget( diff --git a/crates/carddav/src/addressbook/methods/report/mod.rs b/crates/carddav/src/addressbook/methods/report/mod.rs index d3b13da..34b21ec 100644 --- a/crates/carddav/src/addressbook/methods/report/mod.rs +++ b/crates/carddav/src/addressbook/methods/report/mod.rs @@ -7,7 +7,6 @@ use addressbook_multiget::{handle_addressbook_multiget, AddressbookMultigetReque use rustical_store::{auth::User, AddressbookStore}; use serde::{Deserialize, Serialize}; use sync_collection::{handle_sync_collection, SyncCollectionRequest}; -use tokio::sync::RwLock; use tracing::instrument; mod addressbook_multiget; @@ -34,7 +33,7 @@ pub async fn route_report_addressbook( body: String, user: User, req: HttpRequest, - addr_store: Data>, + addr_store: Data, ) -> Result { let (principal, addressbook_id) = path.into_inner(); if principal != user.id { @@ -50,7 +49,7 @@ pub async fn route_report_addressbook( req, &principal, &addressbook_id, - &addr_store, + addr_store.as_ref(), ) .await? } @@ -60,7 +59,7 @@ pub async fn route_report_addressbook( req, &principal, &addressbook_id, - &addr_store, + addr_store.as_ref(), ) .await? } diff --git a/crates/carddav/src/addressbook/methods/report/sync_collection.rs b/crates/carddav/src/addressbook/methods/report/sync_collection.rs index 0487c49..b5bb8b8 100644 --- a/crates/carddav/src/addressbook/methods/report/sync_collection.rs +++ b/crates/carddav/src/addressbook/methods/report/sync_collection.rs @@ -16,7 +16,6 @@ use rustical_store::{ AddressbookStore, }; use serde::Deserialize; -use tokio::sync::RwLock; #[derive(Deserialize, Clone, Debug)] #[serde(rename_all = "kebab-case")] @@ -45,7 +44,7 @@ pub async fn handle_sync_collection( req: HttpRequest, principal: &str, addressbook_id: &str, - addr_store: &RwLock, + addr_store: &AS, ) -> Result, String>, Error> { let props = match sync_collection.prop { PropfindType::Allprop => { @@ -60,8 +59,6 @@ pub async fn handle_sync_collection( let old_synctoken = parse_synctoken(&sync_collection.sync_token).unwrap_or(0); let (new_objects, deleted_objects, new_synctoken) = addr_store - .read() - .await .sync_changes(principal, addressbook_id, old_synctoken) .await?; diff --git a/crates/carddav/src/addressbook/resource.rs b/crates/carddav/src/addressbook/resource.rs index f1443d2..a9673f9 100644 --- a/crates/carddav/src/addressbook/resource.rs +++ b/crates/carddav/src/addressbook/resource.rs @@ -18,10 +18,9 @@ use serde::{Deserialize, Serialize}; use std::str::FromStr; use std::sync::Arc; use strum::{EnumString, VariantNames}; -use tokio::sync::RwLock; pub struct AddressbookResourceService { - pub addr_store: Arc>, + pub addr_store: Arc, pub path: String, pub principal: String, pub addressbook_id: String, @@ -204,8 +203,6 @@ impl ResourceService for AddressbookResourceServi } let addressbook = self .addr_store - .read() - .await .get_addressbook(&self.principal, &self.addressbook_id) .await .map_err(|_e| Error::NotFound)?; @@ -218,8 +215,6 @@ impl ResourceService for AddressbookResourceServi ) -> Result, Self::Error> { Ok(self .addr_store - .read() - .await .get_objects(&self.principal, &self.addressbook_id) .await? .into_iter() @@ -241,7 +236,7 @@ impl ResourceService for AddressbookResourceServi path_components: Self::PathComponents, ) -> Result { let addr_store = req - .app_data::>>() + .app_data::>() .expect("no addressbook store in app_data!") .clone() .into_inner(); @@ -256,8 +251,6 @@ impl ResourceService for AddressbookResourceServi async fn save_resource(&self, file: Self::Resource) -> Result<(), Self::Error> { self.addr_store - .write() - .await .update_addressbook( self.principal.to_owned(), self.addressbook_id.to_owned(), @@ -269,8 +262,6 @@ impl ResourceService for AddressbookResourceServi async fn delete_resource(&self, use_trashbin: bool) -> Result<(), Self::Error> { self.addr_store - .write() - .await .delete_addressbook(&self.principal, &self.addressbook_id, use_trashbin) .await?; Ok(()) diff --git a/crates/carddav/src/error.rs b/crates/carddav/src/error.rs index 5edb3de..270684f 100644 --- a/crates/carddav/src/error.rs +++ b/crates/carddav/src/error.rs @@ -29,7 +29,7 @@ impl actix_web::ResponseError for Error { match self { Error::StoreError(err) => match err { rustical_store::Error::NotFound => StatusCode::NOT_FOUND, - rustical_store::Error::InvalidIcs(_) => StatusCode::BAD_REQUEST, + rustical_store::Error::InvalidData(_) => StatusCode::BAD_REQUEST, _ => StatusCode::INTERNAL_SERVER_ERROR, }, Error::DavError(err) => err.status_code(), diff --git a/crates/carddav/src/lib.rs b/crates/carddav/src/lib.rs index 3e4104a..bc750bd 100644 --- a/crates/carddav/src/lib.rs +++ b/crates/carddav/src/lib.rs @@ -18,7 +18,6 @@ use rustical_store::{ AddressbookStore, }; use std::sync::Arc; -use tokio::sync::RwLock; pub mod address_object; pub mod addressbook; @@ -33,7 +32,7 @@ pub fn configure_well_known(cfg: &mut web::ServiceConfig, carddav_root: String) pub fn configure_dav( cfg: &mut web::ServiceConfig, auth_provider: Arc, - store: Arc>, + store: Arc, ) { cfg.service( web::scope("") diff --git a/crates/carddav/src/principal/mod.rs b/crates/carddav/src/principal/mod.rs index 5bbd323..42a9736 100644 --- a/crates/carddav/src/principal/mod.rs +++ b/crates/carddav/src/principal/mod.rs @@ -10,11 +10,10 @@ use rustical_store::AddressbookStore; use serde::{Deserialize, Serialize}; use std::sync::Arc; use strum::{EnumString, VariantNames}; -use tokio::sync::RwLock; pub struct PrincipalResourceService { principal: String, - addr_store: Arc>, + addr_store: Arc, } #[derive(Clone)] @@ -111,7 +110,7 @@ impl ResourceService for PrincipalResourceService< (principal,): Self::PathComponents, ) -> Result { let addr_store = req - .app_data::>>() + .app_data::>() .expect("no addressbook store in app_data!") .clone() .into_inner(); @@ -135,12 +134,7 @@ impl ResourceService for PrincipalResourceService< &self, rmap: &ResourceMap, ) -> Result, Self::Error> { - let addressbooks = self - .addr_store - .read() - .await - .get_addressbooks(&self.principal) - .await?; + let addressbooks = self.addr_store.get_addressbooks(&self.principal).await?; Ok(addressbooks .into_iter() .map(|addressbook| { diff --git a/crates/frontend/src/lib.rs b/crates/frontend/src/lib.rs index 14cf4e4..7fa01b0 100644 --- a/crates/frontend/src/lib.rs +++ b/crates/frontend/src/lib.rs @@ -13,7 +13,6 @@ use rustical_store::{ CalendarStore, }; use std::sync::Arc; -use tokio::sync::RwLock; mod config; mod routes; @@ -29,10 +28,9 @@ struct UserPage { async fn route_user( path: Path, - store: Data>, + store: Data, user: User, ) -> impl Responder { - let store = store.read().await; let user_id = path.into_inner(); UserPage { calendars: store.get_calendars(&user.id).await.unwrap(), @@ -49,10 +47,9 @@ struct CalendarPage { async fn route_calendar( path: Path<(String, String)>, - store: Data>, + store: Data, user: User, ) -> impl Responder { - let store = store.read().await; let (owner, cal_id) = path.into_inner(); CalendarPage { owner: owner.to_owned(), @@ -63,7 +60,7 @@ async fn route_calendar( pub fn configure_frontend( cfg: &mut web::ServiceConfig, auth_provider: Arc, - store: Arc>, + store: Arc, ) { cfg.service( web::scope("") diff --git a/crates/store/src/addressbook_store.rs b/crates/store/src/addressbook_store.rs index d0e7fa9..53a8eec 100644 --- a/crates/store/src/addressbook_store.rs +++ b/crates/store/src/addressbook_store.rs @@ -10,19 +10,19 @@ pub trait AddressbookStore: Send + Sync + 'static { async fn get_addressbooks(&self, principal: &str) -> Result, Error>; async fn update_addressbook( - &mut self, + &self, principal: String, id: String, addressbook: Addressbook, ) -> Result<(), Error>; - async fn insert_addressbook(&mut self, addressbook: Addressbook) -> Result<(), Error>; + async fn insert_addressbook(&self, addressbook: Addressbook) -> Result<(), Error>; async fn delete_addressbook( - &mut self, + &self, principal: &str, name: &str, use_trashbin: bool, ) -> Result<(), Error>; - async fn restore_addressbook(&mut self, principal: &str, name: &str) -> Result<(), Error>; + async fn restore_addressbook(&self, principal: &str, name: &str) -> Result<(), Error>; async fn sync_changes( &self, @@ -43,20 +43,21 @@ pub trait AddressbookStore: Send + Sync + 'static { object_id: &str, ) -> Result; async fn put_object( - &mut self, + &self, principal: String, addressbook_id: String, object: AddressObject, + overwrite: bool, ) -> Result<(), Error>; async fn delete_object( - &mut self, + &self, principal: &str, addressbook_id: &str, object_id: &str, use_trashbin: bool, ) -> Result<(), Error>; async fn restore_object( - &mut self, + &self, principal: &str, addressbook_id: &str, object_id: &str, diff --git a/crates/store/src/calendar_store.rs b/crates/store/src/calendar_store.rs index e7b5b5e..954906a 100644 --- a/crates/store/src/calendar_store.rs +++ b/crates/store/src/calendar_store.rs @@ -9,19 +9,19 @@ pub trait CalendarStore: Send + Sync + 'static { async fn get_calendars(&self, principal: &str) -> Result, Error>; async fn update_calendar( - &mut self, + &self, principal: String, id: String, calendar: Calendar, ) -> Result<(), Error>; - async fn insert_calendar(&mut self, calendar: Calendar) -> Result<(), Error>; + async fn insert_calendar(&self, calendar: Calendar) -> Result<(), Error>; async fn delete_calendar( - &mut self, + &self, principal: &str, name: &str, use_trashbin: bool, ) -> Result<(), Error>; - async fn restore_calendar(&mut self, principal: &str, name: &str) -> Result<(), Error>; + async fn restore_calendar(&self, principal: &str, name: &str) -> Result<(), Error>; async fn sync_changes( &self, @@ -42,20 +42,21 @@ pub trait CalendarStore: Send + Sync + 'static { object_id: &str, ) -> Result; async fn put_object( - &mut self, + &self, principal: String, cal_id: String, object: CalendarObject, + overwrite: bool, ) -> Result<(), Error>; async fn delete_object( - &mut self, + &self, principal: &str, cal_id: &str, object_id: &str, use_trashbin: bool, ) -> Result<(), Error>; async fn restore_object( - &mut self, + &self, principal: &str, cal_id: &str, object_id: &str, diff --git a/crates/store/src/error.rs b/crates/store/src/error.rs index eda4127..33d400f 100644 --- a/crates/store/src/error.rs +++ b/crates/store/src/error.rs @@ -3,8 +3,11 @@ pub enum Error { #[error("Not found")] NotFound, - #[error("Invalid ics input: {0}")] - InvalidIcs(String), + #[error("Resource already exists and overwrite=false")] + AlreadyExists, + + #[error("Invalid ics/vcf input: {0}")] + InvalidData(String), #[error(transparent)] SqlxError(sqlx::Error), diff --git a/crates/store/src/model/object.rs b/crates/store/src/model/object.rs index ece295a..3ee3484 100644 --- a/crates/store/src/model/object.rs +++ b/crates/store/src/model/object.rs @@ -68,7 +68,7 @@ impl CalendarObject { let mut parser = ical::IcalParser::new(BufReader::new(ics.as_bytes())); let cal = parser.next().ok_or(Error::NotFound)??; if parser.next().is_some() { - return Err(Error::InvalidIcs( + return Err(Error::InvalidData( "multiple calendars, only one allowed".to_owned(), )); } @@ -80,7 +80,7 @@ impl CalendarObject { != 1 { // https://datatracker.ietf.org/doc/html/rfc4791#section-4.1 - return Err(Error::InvalidIcs( + return Err(Error::InvalidData( "iCalendar object is only allowed to have exactly one component".to_owned(), )); } @@ -114,7 +114,7 @@ impl CalendarObject { }); } - Err(Error::InvalidIcs( + Err(Error::InvalidData( "iCalendar component type not supported :(".to_owned(), )) } diff --git a/crates/store/src/sqlite_store/addressbook_store.rs b/crates/store/src/sqlite_store/addressbook_store.rs index 927ccfd..16308df 100644 --- a/crates/store/src/sqlite_store/addressbook_store.rs +++ b/crates/store/src/sqlite_store/addressbook_store.rs @@ -99,7 +99,7 @@ impl AddressbookStore for SqliteStore { #[instrument] async fn update_addressbook( - &mut self, + &self, principal: String, id: String, addressbook: Addressbook, @@ -123,7 +123,7 @@ impl AddressbookStore for SqliteStore { } #[instrument] - async fn insert_addressbook(&mut self, addressbook: Addressbook) -> Result<(), Error> { + async fn insert_addressbook(&self, addressbook: Addressbook) -> Result<(), Error> { sqlx::query!( r#"INSERT INTO addressbooks (principal, id, displayname, description) VALUES (?, ?, ?, ?)"#, @@ -139,7 +139,7 @@ impl AddressbookStore for SqliteStore { #[instrument] async fn delete_addressbook( - &mut self, + &self, principal: &str, addressbook_id: &str, use_trashbin: bool, @@ -168,7 +168,7 @@ impl AddressbookStore for SqliteStore { #[instrument] async fn restore_addressbook( - &mut self, + &self, principal: &str, addressbook_id: &str, ) -> Result<(), Error> { @@ -264,10 +264,12 @@ impl AddressbookStore for SqliteStore { #[instrument] async fn put_object( - &mut self, + &self, principal: String, addressbook_id: String, object: AddressObject, + // TODO: implement + overwrite: bool, ) -> Result<(), Error> { let mut tx = self.db.begin().await?; @@ -298,7 +300,7 @@ impl AddressbookStore for SqliteStore { #[instrument] async fn delete_object( - &mut self, + &self, principal: &str, addressbook_id: &str, object_id: &str, @@ -341,7 +343,7 @@ impl AddressbookStore for SqliteStore { #[instrument] async fn restore_object( - &mut self, + &self, principal: &str, addressbook_id: &str, object_id: &str, diff --git a/crates/store/src/sqlite_store/calendar_store.rs b/crates/store/src/sqlite_store/calendar_store.rs index 9031ef7..66a6bca 100644 --- a/crates/store/src/sqlite_store/calendar_store.rs +++ b/crates/store/src/sqlite_store/calendar_store.rs @@ -98,7 +98,7 @@ impl CalendarStore for SqliteStore { } #[instrument] - async fn insert_calendar(&mut self, calendar: Calendar) -> Result<(), Error> { + async fn insert_calendar(&self, calendar: Calendar) -> Result<(), Error> { sqlx::query!( r#"INSERT INTO calendars (principal, id, displayname, description, "order", color, timezone) VALUES (?, ?, ?, ?, ?, ?, ?)"#, @@ -117,7 +117,7 @@ impl CalendarStore for SqliteStore { #[instrument] async fn update_calendar( - &mut self, + &self, principal: String, id: String, calendar: Calendar, @@ -144,7 +144,7 @@ impl CalendarStore for SqliteStore { // Does not actually delete the calendar but just disables it #[instrument] async fn delete_calendar( - &mut self, + &self, principal: &str, id: &str, use_trashbin: bool, @@ -172,7 +172,7 @@ impl CalendarStore for SqliteStore { } #[instrument] - async fn restore_calendar(&mut self, principal: &str, id: &str) -> Result<(), Error> { + async fn restore_calendar(&self, principal: &str, id: &str) -> Result<(), Error> { sqlx::query!( r"UPDATE calendars SET deleted_at = NULL WHERE (principal, id) = (?, ?)", principal, @@ -223,10 +223,12 @@ impl CalendarStore for SqliteStore { #[instrument] async fn put_object( - &mut self, + &self, principal: String, cal_id: String, object: CalendarObject, + // TODO: implement + overwrite: bool, ) -> Result<(), Error> { let mut tx = self.db.begin().await?; @@ -257,7 +259,7 @@ impl CalendarStore for SqliteStore { #[instrument] async fn delete_object( - &mut self, + &self, principal: &str, cal_id: &str, id: &str, @@ -300,7 +302,7 @@ impl CalendarStore for SqliteStore { #[instrument] async fn restore_object( - &mut self, + &self, principal: &str, cal_id: &str, object_id: &str, diff --git a/crates/store/src/timestamp.rs b/crates/store/src/timestamp.rs index 3ded9c2..93e66c2 100644 --- a/crates/store/src/timestamp.rs +++ b/crates/store/src/timestamp.rs @@ -92,7 +92,7 @@ impl CalDateTime { if let Ok(tz) = olson_name.parse::() { Some(tz) } else { - return Err(Error::InvalidIcs(format!( + return Err(Error::InvalidData(format!( "Timezone has X-LIC-LOCATION property to specify a timezone from the Olson database, however it's value {olson_name} is invalid" ))); } @@ -109,7 +109,7 @@ impl CalDateTime { } } else { // TZID refers to timezone that does not exist - return Err(Error::InvalidIcs(format!( + return Err(Error::InvalidData(format!( "No timezone specified with TZID={tzid}" ))); } @@ -167,7 +167,7 @@ impl From for DateTime { pub fn parse_duration(string: &str) -> Result { let captures = RE_DURATION .captures(string) - .ok_or(Error::InvalidIcs("Invalid duration format".to_owned()))?; + .ok_or(Error::InvalidData("Invalid duration format".to_owned()))?; let mut duration = Duration::zero(); if let Some(weeks) = captures.name("W") { diff --git a/src/app.rs b/src/app.rs index 4116692..d23e1a1 100644 --- a/src/app.rs +++ b/src/app.rs @@ -6,12 +6,11 @@ use rustical_frontend::configure_frontend; use rustical_store::auth::AuthenticationProvider; use rustical_store::{AddressbookStore, CalendarStore}; use std::sync::Arc; -use tokio::sync::RwLock; use tracing_actix_web::TracingLogger; pub fn make_app( - addr_store: Arc>, - cal_store: Arc>, + addr_store: Arc, + cal_store: Arc, auth_provider: Arc, ) -> App< impl ServiceFactory< diff --git a/src/main.rs b/src/main.rs index b1be647..d9db7a4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,7 +19,6 @@ use rustical_store::{AddressbookStore, CalendarStore}; use std::fs; use std::sync::Arc; use std::time::Duration; -use tokio::sync::RwLock; use tracing::level_filters::LevelFilter; use tracing_opentelemetry::OpenTelemetryLayer; use tracing_subscriber::layer::SubscriberExt; @@ -41,14 +40,11 @@ struct Args { async fn get_data_stores( migrate: bool, config: &DataStoreConfig, -) -> Result<( - Arc>, - Arc>, -)> { +) -> Result<(Arc, Arc)> { Ok(match &config { DataStoreConfig::Sqlite(SqliteDataStoreConfig { db_url }) => { let db = create_db_pool(db_url, migrate).await?; - let sqlite_store = Arc::new(RwLock::new(SqliteStore::new(db))); + let sqlite_store = Arc::new(SqliteStore::new(db)); (sqlite_store.clone(), sqlite_store.clone()) } })