mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-14 16:32:29 +00:00
carddav: Implement DAV Push
This commit is contained in:
@@ -1,2 +1,3 @@
|
||||
pub mod mkcol;
|
||||
pub mod post;
|
||||
pub mod report;
|
||||
|
||||
59
crates/carddav/src/addressbook/methods/post.rs
Normal file
59
crates/carddav/src/addressbook/methods/post.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
use crate::Error;
|
||||
use actix_web::http::header;
|
||||
use actix_web::web::{Data, Path};
|
||||
use actix_web::{HttpRequest, HttpResponse};
|
||||
use rustical_dav::push::PushRegister;
|
||||
use rustical_store::auth::User;
|
||||
use rustical_store::{AddressbookStore, Subscription, SubscriptionStore};
|
||||
use rustical_xml::XmlDocument;
|
||||
use tracing::instrument;
|
||||
use tracing_actix_web::RootSpan;
|
||||
|
||||
#[instrument(parent = root_span.id(), skip(store, subscription_store, root_span, req))]
|
||||
pub async fn route_post<A: AddressbookStore + ?Sized, S: SubscriptionStore + ?Sized>(
|
||||
path: Path<(String, String)>,
|
||||
body: String,
|
||||
user: User,
|
||||
store: Data<A>,
|
||||
subscription_store: Data<S>,
|
||||
root_span: RootSpan,
|
||||
req: HttpRequest,
|
||||
) -> Result<HttpResponse, Error> {
|
||||
let (principal, addressbook_id) = path.into_inner();
|
||||
if principal != user.id {
|
||||
return Err(Error::Unauthorized);
|
||||
}
|
||||
|
||||
let addressbook = store.get_addressbook(&principal, &addressbook_id).await?;
|
||||
let request = PushRegister::parse_str(&body)?;
|
||||
let sub_id = uuid::Uuid::new_v4().to_string();
|
||||
|
||||
let expires = if let Some(expires) = request.expires {
|
||||
chrono::DateTime::parse_from_rfc2822(&expires)
|
||||
.map_err(|err| crate::Error::Other(err.into()))?
|
||||
} else {
|
||||
chrono::Utc::now().fixed_offset() + chrono::Duration::weeks(1)
|
||||
};
|
||||
|
||||
let subscription = Subscription {
|
||||
id: sub_id.to_owned(),
|
||||
push_resource: request
|
||||
.subscription
|
||||
.web_push_subscription
|
||||
.push_resource
|
||||
.to_owned(),
|
||||
topic: addressbook.push_topic,
|
||||
expiration: expires.naive_local(),
|
||||
};
|
||||
subscription_store.upsert_subscription(subscription).await?;
|
||||
|
||||
let location = req
|
||||
.resource_map()
|
||||
.url_for(&req, "subscription", &[sub_id])
|
||||
.unwrap();
|
||||
|
||||
Ok(HttpResponse::Created()
|
||||
.append_header((header::LOCATION, location.to_string()))
|
||||
.append_header((header::EXPIRES, expires.to_rfc2822()))
|
||||
.finish())
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
use super::methods::mkcol::route_mkcol;
|
||||
use super::methods::post::route_post;
|
||||
use super::methods::report::route_report_addressbook;
|
||||
use super::prop::{SupportedAddressData, SupportedReportSet};
|
||||
use crate::address_object::resource::AddressObjectResource;
|
||||
@@ -10,22 +11,29 @@ use actix_web::web;
|
||||
use async_trait::async_trait;
|
||||
use derive_more::derive::{From, Into};
|
||||
use rustical_dav::privileges::UserPrivilegeSet;
|
||||
use rustical_dav::push::Transports;
|
||||
use rustical_dav::resource::{Resource, ResourceService};
|
||||
use rustical_dav::xml::{Resourcetype, ResourcetypeInner};
|
||||
use rustical_store::auth::User;
|
||||
use rustical_store::{Addressbook, AddressbookStore};
|
||||
use rustical_store::{Addressbook, AddressbookStore, SubscriptionStore};
|
||||
use rustical_xml::{XmlDeserialize, XmlSerialize};
|
||||
use std::marker::PhantomData;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use strum::{EnumDiscriminants, EnumString, IntoStaticStr, VariantNames};
|
||||
|
||||
pub struct AddressbookResourceService<AS: AddressbookStore + ?Sized> {
|
||||
pub struct AddressbookResourceService<AS: AddressbookStore + ?Sized, S: SubscriptionStore + ?Sized>
|
||||
{
|
||||
addr_store: Arc<AS>,
|
||||
__phantom_sub: PhantomData<S>,
|
||||
}
|
||||
|
||||
impl<A: AddressbookStore + ?Sized> AddressbookResourceService<A> {
|
||||
impl<A: AddressbookStore + ?Sized, S: SubscriptionStore + ?Sized> AddressbookResourceService<A, S> {
|
||||
pub fn new(addr_store: Arc<A>) -> Self {
|
||||
Self { addr_store }
|
||||
Self {
|
||||
addr_store,
|
||||
__phantom_sub: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +50,13 @@ pub enum AddressbookProp {
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAV", skip_deserializing)]
|
||||
Getcontenttype(&'static str),
|
||||
|
||||
// WebDav Push
|
||||
#[xml(skip_deserializing)]
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")]
|
||||
Transports(Transports),
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")]
|
||||
Topic(String),
|
||||
|
||||
// CardDAV (RFC 6352)
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV")]
|
||||
AddressbookDescription(Option<String>),
|
||||
@@ -90,6 +105,8 @@ impl Resource for AddressbookResource {
|
||||
AddressbookPropName::Getcontenttype => {
|
||||
AddressbookProp::Getcontenttype("text/vcard;charset=utf-8")
|
||||
}
|
||||
AddressbookPropName::Transports => AddressbookProp::Transports(Default::default()),
|
||||
AddressbookPropName::Topic => AddressbookProp::Topic(self.0.push_topic.to_owned()),
|
||||
AddressbookPropName::MaxResourceSize => AddressbookProp::MaxResourceSize(10000000),
|
||||
AddressbookPropName::SupportedReportSet => {
|
||||
AddressbookProp::SupportedReportSet(SupportedReportSet::default())
|
||||
@@ -116,6 +133,8 @@ impl Resource for AddressbookResource {
|
||||
Ok(())
|
||||
}
|
||||
AddressbookProp::Getcontenttype(_) => Err(rustical_dav::Error::PropReadOnly),
|
||||
AddressbookProp::Transports(_) => Err(rustical_dav::Error::PropReadOnly),
|
||||
AddressbookProp::Topic(_) => Err(rustical_dav::Error::PropReadOnly),
|
||||
AddressbookProp::MaxResourceSize(_) => Err(rustical_dav::Error::PropReadOnly),
|
||||
AddressbookProp::SupportedReportSet(_) => Err(rustical_dav::Error::PropReadOnly),
|
||||
AddressbookProp::SupportedAddressData(_) => Err(rustical_dav::Error::PropReadOnly),
|
||||
@@ -135,6 +154,8 @@ impl Resource for AddressbookResource {
|
||||
Ok(())
|
||||
}
|
||||
AddressbookPropName::Getcontenttype => Err(rustical_dav::Error::PropReadOnly),
|
||||
AddressbookPropName::Transports => Err(rustical_dav::Error::PropReadOnly),
|
||||
AddressbookPropName::Topic => Err(rustical_dav::Error::PropReadOnly),
|
||||
AddressbookPropName::MaxResourceSize => Err(rustical_dav::Error::PropReadOnly),
|
||||
AddressbookPropName::SupportedReportSet => Err(rustical_dav::Error::PropReadOnly),
|
||||
AddressbookPropName::SupportedAddressData => Err(rustical_dav::Error::PropReadOnly),
|
||||
@@ -153,7 +174,9 @@ impl Resource for AddressbookResource {
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl<AS: AddressbookStore + ?Sized> ResourceService for AddressbookResourceService<AS> {
|
||||
impl<AS: AddressbookStore + ?Sized, S: SubscriptionStore + ?Sized> ResourceService
|
||||
for AddressbookResourceService<AS, S>
|
||||
{
|
||||
type MemberType = AddressObjectResource;
|
||||
type PathComponents = (String, String); // principal, addressbook_id
|
||||
type Resource = AddressbookResource;
|
||||
@@ -220,5 +243,6 @@ impl<AS: AddressbookStore + ?Sized> ResourceService for AddressbookResourceServi
|
||||
let report_method = web::method(Method::from_str("REPORT").unwrap());
|
||||
res.route(mkcol_method.to(route_mkcol::<AS>))
|
||||
.route(report_method.to(route_report_addressbook::<AS>))
|
||||
.post(route_post::<AS, S>)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user