use crate::{
dav::{check_status, FoundCollection, WebDavClient, WebDavError},
names,
xmlutils::get_unquoted_href,
CheckSupportError, PropertyName,
};
use http::{Method, Request, Response};
use hyper::{body::Incoming, Uri};
use log::debug;
use tower::Service;
pub(crate) fn parse_find_multiple_collections(
body: impl AsRef<[u8]>,
only: &PropertyName<'_, '_>,
) -> Result<Vec<FoundCollection>, WebDavError> {
let body = std::str::from_utf8(body.as_ref())?;
let doc = roxmltree::Document::parse(body)?;
let root = doc.root_element();
let responses = root
.descendants()
.filter(|node| node.tag_name() == names::RESPONSE);
let mut items = Vec::new();
for response in responses {
if !response
.descendants()
.find(|node| node.tag_name() == names::RESOURCETYPE)
.map_or(false, |node| {
node.descendants().any(|node| node.tag_name() == *only)
})
{
continue;
}
let href = get_unquoted_href(&response)?.to_string();
let etag = response
.descendants()
.find(|node| node.tag_name() == names::GETETAG)
.and_then(|node| node.text().map(str::to_string));
let supports_sync = response
.descendants()
.find(|node| node.tag_name() == names::SUPPORTED_REPORT_SET)
.map_or(false, |node| {
node.descendants()
.any(|node| node.tag_name() == names::SYNC_COLLECTION)
});
items.push(FoundCollection {
href,
etag,
supports_sync,
});
}
Ok(items)
}
pub(crate) async fn check_support<C>(
client: &WebDavClient<C>,
uri: &Uri,
expectation: &str,
) -> Result<(), CheckSupportError>
where
C: Service<http::Request<String>, Response = Response<Incoming>> + Sync + Send,
<C as Service<http::Request<String>>>::Error: std::error::Error + Send + Sync,
{
let request = Request::builder()
.method(Method::OPTIONS)
.uri(uri)
.body(String::new())?;
let (head, _body) = client.request(request).await?;
check_status(head.status)?;
let header = head
.headers
.get("DAV")
.ok_or(CheckSupportError::MissingHeader)?
.to_str()?;
debug!("DAV header: '{}'", header);
if header.split(',').any(|part| part.trim() == expectation) {
Ok(())
} else {
Err(CheckSupportError::NotAdvertised)
}
}
#[derive(thiserror::Error, Debug)]
pub enum ServiceForUrlError {
#[error("missing scheme in URL")]
MissingScheme,
#[error("unknown scheme in URL")]
UnknownScheme,
}