kitchen-freezer/src/data/client.rs

154 lines
4.1 KiB
Rust
Raw Normal View History

2021-02-20 00:10:05 +01:00
//! Code to connect to a Caldav server
//!
//! Some of it comes from https://github.com/marshalshi/caldav-client-rust.git
use std::error::Error;
use reqwest::Method;
use reqwest::header::CONTENT_TYPE;
use minidom::Element;
use url::Url;
static DAVCLIENT_BODY: &str = r#"
<d:propfind xmlns:d="DAV:">
<d:prop>
<d:current-user-principal />
</d:prop>
</d:propfind>
"#;
static HOMESET_BODY: &str = r#"
<d:propfind xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav" >
<d:self/>
<d:prop>
<c:calendar-home-set />
</d:prop>
</d:propfind>
"#;
pub struct Client {
url: Url,
username: String,
password: String,
principal: Option<Url>,
calendar_home_set: Option<Url>,
}
2021-02-18 12:02:04 +01:00
impl Client {
2021-02-20 00:10:05 +01:00
pub fn new<S: AsRef<str>, T: ToString, U: ToString>(url: S, username: T, password: U) -> Result<Self, Box<dyn Error>> {
let url = Url::parse(url.as_ref())?;
Ok(Self{
url,
username: username.to_string(),
password: password.to_string(),
principal: None,
calendar_home_set: None,
})
}
2021-02-20 00:18:51 +01:00
async fn sub_request(&self, url: &Url, body: String, item1: &str, item2: &str) -> Result<String, Box<dyn Error>> {
2021-02-20 00:10:05 +01:00
let method = Method::from_bytes(b"PROPFIND")
.expect("cannot create PROPFIND method.");
let res = reqwest::Client::new()
2021-02-20 00:18:51 +01:00
.request(method, url.as_str())
2021-02-20 00:10:05 +01:00
.header("Depth", 0)
.header(CONTENT_TYPE, "application/xml")
.basic_auth(self.username.clone(), Some(self.password.clone()))
2021-02-20 00:18:51 +01:00
.body(body)
2021-02-20 00:10:05 +01:00
.send()
.await?;
let text = res.text().await?;
let root: Element = text.parse().unwrap();
2021-02-20 00:18:51 +01:00
let elem1 = find_elem(&root, item1.to_string()).unwrap();
let elem2 = find_elem(elem1, item2.to_string()).unwrap();
let text = elem2.text();
Ok(text)
}
2021-02-20 00:10:05 +01:00
2021-02-20 00:18:51 +01:00
/// Return the Principal URL, or fetch it from server if not known yet
async fn get_principal(&mut self) -> Result<Url, Box<dyn Error>> {
if let Some(p) = &self.principal {
return Ok(p.clone());
}
2021-02-20 00:10:05 +01:00
2021-02-20 00:18:51 +01:00
let href = self.sub_request(&self.url, DAVCLIENT_BODY.into(), "current-user-principal", "href").await?;
2021-02-20 00:10:05 +01:00
let mut principal_url = self.url.clone();
2021-02-20 00:18:51 +01:00
principal_url.set_path(&href);
2021-02-20 00:10:05 +01:00
self.principal = Some(principal_url.clone());
2021-02-20 00:18:51 +01:00
println!("URL is {}", href);
2021-02-20 00:10:05 +01:00
return Ok(principal_url);
}
/// Return the Homeset URL, or fetch it from server if not known yet
async fn get_cal_home_set(&mut self) -> Result<Url, Box<dyn Error>> {
if let Some(h) = &self.calendar_home_set {
return Ok(h.clone());
}
let principal_url = self.get_principal().await?;
2021-02-20 00:18:51 +01:00
let href = self.sub_request(&principal_url, HOMESET_BODY.into(), "calendar-home-set", "href").await?;
2021-02-20 00:10:05 +01:00
let mut chs_url = self.url.clone();
2021-02-20 00:18:51 +01:00
chs_url.set_path(&href);
2021-02-20 00:10:05 +01:00
self.calendar_home_set = Some(chs_url.clone());
2021-02-20 00:18:51 +01:00
println!("Calendar home set {:?}", chs_url.path());
2021-02-20 00:10:05 +01:00
Ok(chs_url)
}
}
pub fn find_elems(root: &Element, tag: String) -> Vec<&Element> {
let mut elems: Vec<&Element> = Vec::new();
for el in root.children() {
if el.name() == tag {
elems.push(el);
} else {
let ret = find_elems(el, tag.clone());
elems.extend(ret);
}
}
elems
}
pub fn find_elem(root: &Element, tag: String) -> Option<&Element> {
if root.name() == tag {
return Some(root);
}
for el in root.children() {
if el.name() == tag {
return Some(el);
} else {
let ret = find_elem(el, tag.clone());
if ret.is_some() {
return ret;
}
}
}
None
}
#[cfg(test)]
mod test {
use super::*;
use crate::settings::URL;
use crate::settings::USERNAME;
use crate::settings::PASSWORD;
#[tokio::test]
async fn test_client() {
let mut client = Client::new(URL, USERNAME, PASSWORD).unwrap();
client.get_cal_home_set().await.unwrap();
2021-02-18 12:02:04 +01:00
}
2021-02-20 00:10:05 +01:00
}