mirror of
https://github.com/stalwartlabs/mail-server.git
synced 2025-10-06 02:34:43 +08:00
211 lines
7.5 KiB
Rust
211 lines
7.5 KiB
Rust
/*
|
|
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
|
|
*
|
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
|
*/
|
|
|
|
use calcard::vcard::VCardVersion;
|
|
use compact_str::{CompactString, ToCompactString};
|
|
use trc::Value;
|
|
|
|
pub mod parser;
|
|
pub mod requests;
|
|
pub mod responses;
|
|
pub mod schema;
|
|
|
|
pub fn xml_pretty_print(xml_string: &str) -> String {
|
|
// Create a reader
|
|
let mut reader = quick_xml::Reader::from_str(xml_string);
|
|
let mut writer = quick_xml::Writer::new_with_indent(std::io::Cursor::new(Vec::new()), b' ', 2);
|
|
let mut buf = Vec::new();
|
|
loop {
|
|
match reader.read_event_into(&mut buf) {
|
|
Ok(quick_xml::events::Event::Eof) => break,
|
|
Ok(event) => {
|
|
writer.write_event(event).unwrap();
|
|
}
|
|
Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e),
|
|
}
|
|
buf.clear();
|
|
}
|
|
|
|
let result = writer.into_inner().into_inner();
|
|
String::from_utf8(result).unwrap()
|
|
}
|
|
|
|
#[derive(Debug, Default, PartialEq, Eq)]
|
|
pub struct RequestHeaders<'x> {
|
|
pub uri: &'x str,
|
|
pub depth: Depth,
|
|
pub timeout: Timeout,
|
|
pub content_type: Option<&'x str>,
|
|
pub destination: Option<&'x str>,
|
|
pub lock_token: Option<&'x str>,
|
|
pub max_vcard_version: Option<VCardVersion>,
|
|
pub no_schedule_reply: bool,
|
|
pub if_schedule_tag: Option<u32>,
|
|
pub overwrite_fail: bool,
|
|
pub no_timezones: bool,
|
|
pub ret: Return,
|
|
pub depth_no_root: bool,
|
|
pub if_: Vec<If<'x>>,
|
|
}
|
|
|
|
pub struct ResourceState<T: AsRef<str>> {
|
|
pub resource: Option<T>,
|
|
pub etag: T,
|
|
pub state_token: T,
|
|
}
|
|
|
|
#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
|
|
pub enum Return {
|
|
Minimal,
|
|
Representation,
|
|
#[default]
|
|
Default,
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
pub struct If<'x> {
|
|
pub resource: Option<&'x str>,
|
|
pub list: Vec<Condition<'x>>,
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
pub enum Condition<'x> {
|
|
StateToken { is_not: bool, token: &'x str },
|
|
ETag { is_not: bool, tag: &'x str },
|
|
Exists { is_not: bool },
|
|
}
|
|
|
|
#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
|
|
#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
|
|
#[cfg_attr(test, serde(tag = "type", content = "data"))]
|
|
pub enum Timeout {
|
|
Infinite,
|
|
Second(u64),
|
|
#[default]
|
|
None,
|
|
}
|
|
|
|
#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
|
|
#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
|
|
pub enum Depth {
|
|
Zero,
|
|
One,
|
|
Infinity,
|
|
#[default]
|
|
None,
|
|
}
|
|
|
|
impl From<&RequestHeaders<'_>> for Value {
|
|
fn from(headers: &RequestHeaders<'_>) -> Self {
|
|
let mut values = Vec::with_capacity(4);
|
|
if headers.depth != Depth::None {
|
|
values.push(Value::String(CompactString::const_new("Depth")));
|
|
values.push(match headers.depth {
|
|
Depth::Zero => Value::Int(0),
|
|
Depth::One => Value::Int(1),
|
|
Depth::Infinity => Value::String(CompactString::const_new("infinity")),
|
|
Depth::None => Value::None,
|
|
});
|
|
}
|
|
if headers.timeout != Timeout::None {
|
|
values.push(Value::String(CompactString::const_new("Timeout")));
|
|
values.push(match headers.timeout {
|
|
Timeout::Infinite => Value::String(CompactString::const_new("infinite")),
|
|
Timeout::Second(n) => Value::Int(n as i64),
|
|
Timeout::None => Value::None,
|
|
});
|
|
}
|
|
for (name, header_value) in [
|
|
("Content-Type", headers.content_type),
|
|
("Destination", headers.destination),
|
|
("Lock-Token", headers.lock_token),
|
|
] {
|
|
if let Some(value) = header_value {
|
|
values.push(CompactString::const_new(name).into());
|
|
values.push(value.to_compact_string().into());
|
|
}
|
|
}
|
|
for (name, is_set) in [
|
|
("Overwrite", headers.overwrite_fail),
|
|
("No-Timezones", headers.no_timezones),
|
|
("Depth-No-Root", headers.depth_no_root),
|
|
] {
|
|
if is_set {
|
|
values.push(CompactString::const_new(name).into());
|
|
}
|
|
}
|
|
for if_ in &headers.if_ {
|
|
values.push(CompactString::const_new("If").into());
|
|
let mut if_values = Vec::with_capacity(if_.list.len() * 2 + 1);
|
|
if let Some(resource) = if_.resource {
|
|
if_values.push(Value::String(resource.to_compact_string()));
|
|
}
|
|
for condition in &if_.list {
|
|
match condition {
|
|
Condition::StateToken { is_not, token } => {
|
|
if *is_not {
|
|
if_values.push(Value::String(CompactString::const_new("!State-Token")));
|
|
} else {
|
|
if_values.push(Value::String(CompactString::const_new("State-Token")));
|
|
}
|
|
if_values.push(Value::String(token.to_compact_string()));
|
|
}
|
|
Condition::ETag { is_not, tag } => {
|
|
if *is_not {
|
|
if_values.push(Value::String(CompactString::const_new("!ETag")));
|
|
} else {
|
|
if_values.push(Value::String(CompactString::const_new("ETag")));
|
|
}
|
|
if_values.push(Value::String(tag.to_compact_string()));
|
|
}
|
|
Condition::Exists { is_not } => {
|
|
if *is_not {
|
|
if_values.push(Value::String(CompactString::const_new("!Exists")));
|
|
} else {
|
|
if_values.push(Value::String(CompactString::const_new("Exists")));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
values.push(Value::Array(if_values));
|
|
}
|
|
|
|
Value::Array(values)
|
|
}
|
|
}
|
|
|
|
/*
|
|
|
|
|
|
Implemented:
|
|
|
|
RFC4918 - HTTP Extensions for Web Distributed Authoring and Versioning (WebDAV)
|
|
RFC5689 - Extended MKCOL for Web Distributed Authoring and Versioning (WebDAV)
|
|
RFC6578 - Collection Synchronization for Web Distributed Authoring and Versioning (WebDAV)
|
|
RFC3744 - Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol
|
|
RFC4331 - Quota and Size Properties for Distributed Authoring and Versioning (DAV) Collections
|
|
RFC5397 - WebDAV Current Principal Extension
|
|
RFC8144 - Use of the Prefer Header Field in Web Distributed Authoring and Versioning (WebDAV)
|
|
RFC4791 - Calendaring Extensions to WebDAV (CalDAV)
|
|
RFC7809 - Calendaring Extensions to WebDAV (CalDAV) Time Zones by Reference
|
|
RFC6638 - Scheduling Extensions to CalDAV
|
|
RFC6352 - CardDAV vCard Extensions to Web Distributed Authoring and Versioning (WebDAV)
|
|
RFC6764 - Locating Services for Calendaring Extensions to WebDAV (CalDAV) and vCard Extensions to WebDAV (CardDAV)
|
|
|
|
Out of scope:
|
|
|
|
RFC5842 - Binding Extensions to Web Distributed Authoring and Versioning (WebDAV)
|
|
RFC4316 - Datatypes for Web Distributed Authoring and Versioning (WebDAV) Properties
|
|
RFC4709 - Mounting Web Distributed Authoring and Versioning (WebDAV) Servers
|
|
RFC3648 - Web Distributed Authoring and Versioning (WebDAV) Ordered Collections Protocol
|
|
RFC4437 - Web Distributed Authoring and Versioning (WebDAV) Redirect Reference Resources
|
|
RFC8607 - Calendaring Extensions to WebDAV (CalDAV) Managed Attachments
|
|
RFC5995 - Using POST to Add Members to Web Distributed Authoring and Versioning (WebDAV) Collections
|
|
RFC3253 - Versioning Extensions to WebDAV (Web Distributed Authoring and Versioning)
|
|
RFC5323 - Web Distributed Authoring and Versioning (WebDAV) SEARCH
|
|
|
|
|
|
*/
|