libdav/lib.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
#![deny(clippy::pedantic)]
#![deny(clippy::unwrap_used)]
// Copyright 2023-2024 Hugo Osvaldo Barrera
//
// SPDX-License-Identifier: EUPL-1.2
//! This library contains caldav and carddav clients.
//!
//! See [`CalDavClient`] and [`CardDavClient`] as a useful entry points.
//!
//! Both clients wrap a [`dav::WebDavClient`], and implement `Deref<Target = DavClient>`, so all
//! of `WebDavClient`'s associated functions for are usable directly.
//!
//! # Service discovery
//!
//! DNS-based service discovery is implemented in [`sd::find_context_url`].
//!
//! The implementation does not validate DNSSEC signatures. Because of this, discovery must only be
//! used with a validating DNS resolver (as defined in [rfc4033][rfc4033]), or with domains served
//! from a local, trusted networks.
//!
//! [rfc4033]: https://www.rfc-editor.org/rfc/rfc4033
//!
//! # Hrefs
//!
//! All `href` strings returned by the server are unquoted by this library before being returned to
//! consumers. I.e.: you should assume that all `href`s have been url-decoded for you.
//!
//! All functions that take a parameter named `href` (or similar ones like `calendar_href`) expect
//! their input to NOT be URL-encoded. I.e.: you do not need to perform any quoting.
//!
//! # Thanks
//!
//! Special thanks to the [NLnet foundation][nlnet] and the [NGI Zero Entrust program][ngi0] of the
//! European Commission, which helped secure funding for the work on [pimsync] and related projects
//! such a this one.
//!
//! [nlnet]: https://nlnet.nl/project/vdirsyncer/
//! [ngi0]: https://www.ngi.eu/ngi-projects/ngi-zero-entrust/
//! [pimsync]: https://git.sr.ht/~whynothugo/pimsync
//!
//! # See also
//!
//! The source code is currently hosted at <https://git.sr.ht/~whynothugo/libdav>.
use dav::RequestError;
use dav::WebDavError;
use http::StatusCode;
mod caldav;
mod carddav;
mod common;
pub mod dav;
pub mod names;
pub mod sd;
pub mod xmlutils;
pub use caldav::service_for_url as caldav_service_for_url;
pub use caldav::CalDavClient;
pub use carddav::service_for_url as carddav_service_for_url;
pub use carddav::CardDavClient;
use roxmltree::ExpandedName;
/// A WebDAV property with a `namespace` and `name`.
///
/// See the [names] module for a variety of constants often used in CalDAV and CardDAV.
#[derive(Debug, PartialEq)]
pub struct PropertyName<'ns, 'name> {
namespace: &'ns str,
name: &'name str,
}
impl<'ns, 'name> PropertyName<'ns, 'name> {
/// Create an property instance.
#[must_use]
pub const fn new(namespace: &'ns str, name: &'name str) -> PropertyName<'ns, 'name> {
PropertyName { namespace, name }
}
}
impl<'ns, 'name> PropertyName<'ns, 'name> {
/// Returns the name of this property.
#[must_use]
pub fn name(&self) -> &'name str {
self.name
}
/// Returns the namespace of this property.
#[must_use]
pub fn namespace(&self) -> &'ns str {
self.namespace
}
}
impl PartialEq<ExpandedName<'_, '_>> for PropertyName<'_, '_> {
fn eq(&self, other: &ExpandedName<'_, '_>) -> bool {
other.name() == self.name && other.namespace() == Some(self.namespace)
}
}
impl PartialEq<PropertyName<'_, '_>> for ExpandedName<'_, '_> {
fn eq(&self, other: &PropertyName<'_, '_>) -> bool {
self.name() == other.name && self.namespace() == Some(other.namespace)
}
}
/// Error type for [`CalDavClient::find_calendar_home_set`] and [`CardDavClient::find_address_book_home_set`].
#[derive(thiserror::Error, Debug)]
#[error("error finding home set collection: {0}")]
pub struct FindHomeSetError(#[source] pub WebDavError);
/// See [`FetchedResource`]
#[derive(Debug, PartialEq, Eq)]
pub struct FetchedResourceContent {
/// Raw resource data, with lines separated by `\r\n`.
pub data: String,
pub etag: String,
}
/// Parsed resource fetched from a server.
#[derive(Debug, PartialEq, Eq)]
pub struct FetchedResource {
/// Absolute path to the resource in the server.
pub href: String,
/// Contents of the resource if available, or the status code if unavailable.
pub content: Result<FetchedResourceContent, StatusCode>,
}
/// Error type for [`CalDavClient::check_support`] and [`CardDavClient::check_support`].
///
/// Returned when checking support for a feature encounters an error.
#[derive(thiserror::Error, Debug)]
pub enum CheckSupportError {
/// The `DAV` header was missing from the response received.
#[error("the DAV header was missing from the response")]
MissingHeader,
/// The server does not advertise the queried capability.
#[error("the requested support is not advertised by the server")]
NotAdvertised,
/// Failed to parse the `DAV` header as a UTF-8 string.
#[error("the DAV header is not a valid string: {0}")]
HeaderNotAscii(#[from] http::header::ToStrError),
/// Error sending HTTP request.
#[error(transparent)]
Request(#[from] RequestError),
/// The provided URL is not acceptable.
#[error("invalid input URL: {0}")]
InvalidInput(#[from] http::Error),
/// The server returned a non-success status code.
#[error("http request returned {0}")]
BadStatusCode(http::StatusCode),
}
impl From<StatusCode> for CheckSupportError {
fn from(status: StatusCode) -> Self {
CheckSupportError::BadStatusCode(status)
}
}
/// Details of a single item that are returned when listing them.
///
/// This does not include actual item data, it only includes their metadata.
#[derive(Debug, PartialEq, Eq, Default)]
pub struct ItemDetails {
pub content_type: Option<String>,
pub etag: Option<String>,
pub resource_type: ResourceType,
}
/// Resource type for an item.
///
/// This type requires further work and will likely change in future versions.
// TODO: Support unknown types too.
// Keeping all the `String` instances can be inefficient when listing thousands of resources.
// Perhaps de-duplicated strings?
#[derive(Default, Debug, PartialEq, Eq)]
pub struct ResourceType {
pub is_collection: bool,
pub is_calendar: bool,
pub is_address_book: bool,
}