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
#![deny(clippy::pedantic)]
// Copyright 2023 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 implement `Deref<Target = DavClient>`, so all the associated
//! functions for [`dav::WebDavClient`] are usable directly.
//!
//! # 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.

use crate::auth::Auth;
use dav::DavError;
use dav::FindCurrentUserPrincipalError;
use dav::RequestError;
use dns::TxtError;
use domain::resolv::lookup::srv::SrvError;
use http::StatusCode;

pub mod auth;
pub mod builder;
mod caldav;
mod carddav;
mod common;
pub mod dav;
pub mod dns;
pub mod names;
pub mod xmlutils;

pub use caldav::CalDavClient;
pub use carddav::CardDavClient;

/// A WebDav property with a `namespace` and `name`.
///
/// This is currently an alias of [`roxmltree::ExpandedName`].
pub type Property<'ns, 'name> = roxmltree::ExpandedName<'ns, 'name>;

/// A supplied Url was not valid.
#[derive(thiserror::Error, Debug)]
pub enum InvalidUrl {
    #[error("missing scheme")]
    MissingScheme,

    #[error("scheme is not valid for service type")]
    InvalidScheme,

    #[error("missing host")]
    MissingHost,

    #[error("the host is not a valid domain")]
    InvalidDomain(domain::base::name::FromStrError),
}

/// An error automatically bootstrapping a new client.
#[derive(thiserror::Error, Debug)]
pub enum BootstrapError {
    #[error("the input URL is not valid: {0}")]
    InvalidUrl(#[from] InvalidUrl),

    #[error("error resolving DNS SRV records")]
    DnsError(#[from] SrvError),

    #[error("SRV records returned domain/port pair that failed to parse")]
    UnusableSrv(http::Error),

    #[error("error resolving context path via TXT records")]
    TxtError(#[from] TxtError),

    #[error(transparent)]
    HomeSet(#[from] FindHomeSetError),

    #[error("error querying current user principal")]
    CurrentPrincipal(#[from] FindCurrentUserPrincipalError),

    #[error(transparent)]
    DavError(#[from] DavError),

    /// The service is decidedly not available.
    ///
    /// See <https://www.rfc-editor.org/rfc/rfc2782>, page 4
    #[error("the service is decidedly not available")]
    NotAvailable,
}

/// Error finding home set.
#[derive(thiserror::Error, Debug)]
#[error("error finding home set collection")]
pub struct FindHomeSetError(#[source] pub DavError);

/// See [`FetchedResource`]
#[derive(Debug, PartialEq, Eq)]
pub struct FetchedResourceContent {
    pub data: String,
    pub etag: String,
}

/// A parsed resource fetched from a server.
#[derive(Debug, PartialEq, Eq)]
pub struct FetchedResource {
    /// The absolute path to the resource in the server.
    pub href: String,
    /// The contents of the resource if available, or the status code if unavailable.
    pub content: Result<FetchedResourceContent, StatusCode>,
}

/// Returned when checking support for a feature encounters an error.
#[derive(thiserror::Error, Debug)]
pub enum CheckSupportError {
    #[error("the DAV header was missing from the response")]
    MissingHeader,

    #[error("the requested support is not advertised by the server")]
    NotAdvertised,

    #[error("the DAV header is not a valid string")]
    HeaderNotAscii(#[from] http::header::ToStrError),

    #[error("error performing http request")]
    Request(#[from] RequestError),

    #[error("invalid input URL")]
    InvalidInput(#[from] http::Error),

    #[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,
}

#[derive(Default, Debug, PartialEq, Eq)]
// TODO: support unknown ones too...?
pub struct ResourceType {
    pub is_collection: bool,
    pub is_calendar: bool,
    pub is_address_book: bool,
}