2024-02-28 21:00:47 +00:00
|
|
|
use std::io::Cursor;
|
2024-02-28 09:20:28 +00:00
|
|
|
|
2024-02-29 19:40:40 +00:00
|
|
|
use quick_xml::Error as QError;
|
2024-02-28 21:00:47 +00:00
|
|
|
use quick_xml::events::{Event, BytesEnd, BytesStart, BytesText};
|
|
|
|
use quick_xml::writer::{ElementWriter, Writer};
|
|
|
|
use quick_xml::name::PrefixDeclaration;
|
|
|
|
use tokio::io::AsyncWrite;
|
|
|
|
use super::types::*;
|
|
|
|
|
2024-02-28 09:20:28 +00:00
|
|
|
|
2024-02-29 19:40:40 +00:00
|
|
|
//-------------- TRAITS ----------------------
|
|
|
|
|
|
|
|
/// Basic encode trait to make a type encodable
|
|
|
|
pub trait QuickWritable<E: Extension, C: Context<E>> {
|
|
|
|
async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError>;
|
|
|
|
}
|
|
|
|
|
2024-02-29 21:32:07 +00:00
|
|
|
/// Encoding context
|
2024-02-29 19:40:40 +00:00
|
|
|
pub trait Context<E: Extension> {
|
|
|
|
fn child(&self) -> Self;
|
|
|
|
fn create_dav_element(&self, name: &str) -> BytesStart;
|
|
|
|
async fn hook_error(&self, err: &E::Error, xml: &mut Writer<impl AsyncWrite+Unpin>) -> Result<(), QError>;
|
|
|
|
}
|
|
|
|
|
2024-02-29 21:32:07 +00:00
|
|
|
/// -------------- NoExtension Encoding Context
|
|
|
|
/// (Might be tied to the type maybe)
|
2024-02-29 19:40:40 +00:00
|
|
|
pub struct NoExtCtx {
|
|
|
|
root: bool
|
|
|
|
}
|
|
|
|
impl Context<NoExtension> for NoExtCtx {
|
|
|
|
fn child(&self) -> Self {
|
|
|
|
Self { root: false }
|
|
|
|
}
|
|
|
|
fn create_dav_element(&self, name: &str) -> BytesStart {
|
|
|
|
let mut start = BytesStart::new(format!("D:{}", name));
|
|
|
|
if self.root {
|
|
|
|
start.push_attribute(("xmlns:D", "DAV:"));
|
|
|
|
}
|
|
|
|
start
|
|
|
|
}
|
|
|
|
async fn hook_error(&self, err: &Disabled, xml: &mut Writer<impl AsyncWrite+Unpin>) -> Result<(), QError> {
|
|
|
|
unreachable!();
|
|
|
|
}
|
2024-02-28 21:00:47 +00:00
|
|
|
}
|
|
|
|
|
2024-02-29 19:40:40 +00:00
|
|
|
|
|
|
|
//--------------------- ENCODING --------------------
|
|
|
|
|
|
|
|
// --- XML ROOTS
|
|
|
|
impl<E: Extension, C: Context<E>> QuickWritable<E,C> for Multistatus<E> {
|
|
|
|
async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> {
|
|
|
|
let start = ctx.create_dav_element("multistatus");
|
|
|
|
let end = start.to_end();
|
|
|
|
|
|
|
|
xml.write_event_async(Event::Start(start.clone())).await?;
|
|
|
|
for response in self.responses.iter() {
|
|
|
|
response.write(xml, ctx.child()).await?;
|
|
|
|
}
|
|
|
|
if let Some(description) = &self.responsedescription {
|
|
|
|
description.write(xml, ctx.child()).await?;
|
|
|
|
}
|
|
|
|
|
|
|
|
xml.write_event_async(Event::End(end)).await?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// --- XML inner elements
|
|
|
|
impl<E: Extension, C: Context<E>> QuickWritable<E,C> for Href {
|
2024-02-29 21:32:07 +00:00
|
|
|
async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> {
|
|
|
|
let start = ctx.create_dav_element("href");
|
|
|
|
let end = start.to_end();
|
|
|
|
|
|
|
|
xml.write_event_async(Event::Start(start.clone())).await?;
|
|
|
|
xml.write_event_async(Event::Text(BytesText::new(&self.0))).await?;
|
|
|
|
xml.write_event_async(Event::End(end)).await?;
|
|
|
|
|
2024-02-29 09:17:46 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
2024-02-28 21:00:47 +00:00
|
|
|
|
2024-02-29 19:40:40 +00:00
|
|
|
impl<E: Extension, C: Context<E>> QuickWritable<E,C> for Response<E> {
|
|
|
|
async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> {
|
2024-02-29 21:32:07 +00:00
|
|
|
let start = ctx.create_dav_element("href");
|
|
|
|
let end = start.to_end();
|
2024-02-29 19:40:40 +00:00
|
|
|
|
2024-02-29 21:32:07 +00:00
|
|
|
xml.write_event_async(Event::Start(start.clone())).await?;
|
|
|
|
self.href.write(xml, ctx.child()).await?;
|
|
|
|
self.status_or_propstat.write(xml, ctx.child()).await?;
|
|
|
|
if let Some(error) = &self.error {
|
|
|
|
error.write(xml, ctx.child()).await?;
|
|
|
|
}
|
|
|
|
if let Some(responsedescription) = &self.responsedescription {
|
|
|
|
responsedescription.write(xml, ctx.child()).await?;
|
|
|
|
}
|
|
|
|
if let Some(location) = &self.location {
|
|
|
|
location.write(xml, ctx.child()).await?;
|
|
|
|
}
|
|
|
|
xml.write_event_async(Event::End(end)).await?;
|
2024-02-29 19:40:40 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<E: Extension, C: Context<E>> QuickWritable<E,C> for StatusOrPropstat<E> {
|
|
|
|
async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> {
|
|
|
|
match self {
|
|
|
|
Self::Status(status) => status.write(xml, ctx.child()).await,
|
|
|
|
Self::PropStat(propstat_list) => {
|
|
|
|
for propstat in propstat_list.iter() {
|
|
|
|
propstat.write(xml, ctx.child()).await?;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<E: Extension, C: Context<E>> QuickWritable<E,C> for Status {
|
|
|
|
async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> {
|
2024-02-29 21:32:07 +00:00
|
|
|
let start = ctx.create_dav_element("status");
|
|
|
|
let end = start.to_end();
|
|
|
|
|
|
|
|
xml.write_event_async(Event::Start(start.clone())).await?;
|
|
|
|
|
|
|
|
let txt = format!("HTTP/1.1 {} {}", self.0.as_str(), self.0.canonical_reason().unwrap_or("No reason"));
|
|
|
|
xml.write_event_async(Event::Text(BytesText::new(&txt))).await?;
|
|
|
|
|
|
|
|
xml.write_event_async(Event::End(end)).await?;
|
|
|
|
|
2024-02-28 21:00:47 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
2024-02-28 09:20:28 +00:00
|
|
|
}
|
|
|
|
|
2024-02-29 19:40:40 +00:00
|
|
|
impl<E: Extension, C: Context<E>> QuickWritable<E,C> for ResponseDescription {
|
|
|
|
async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> {
|
|
|
|
let start = ctx.create_dav_element("responsedescription");
|
|
|
|
let end = start.to_end();
|
|
|
|
|
|
|
|
xml.write_event_async(Event::Start(start.clone())).await?;
|
|
|
|
xml.write_event_async(Event::Text(BytesText::new(&self.0))).await?;
|
|
|
|
xml.write_event_async(Event::End(end)).await?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<E: Extension, C: Context<E>> QuickWritable<E,C> for Location {
|
|
|
|
async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> {
|
2024-02-29 09:17:46 +00:00
|
|
|
unimplemented!();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-29 19:40:40 +00:00
|
|
|
impl<E: Extension, C: Context<E>> QuickWritable<E,C> for PropStat<E> {
|
|
|
|
async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> {
|
|
|
|
unimplemented!();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<E: Extension, C: Context<E>> QuickWritable<E,C> for Error<E> {
|
|
|
|
async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> {
|
2024-02-29 21:32:07 +00:00
|
|
|
let start = ctx.create_dav_element("error");
|
|
|
|
let end = start.to_end();
|
2024-02-29 19:40:40 +00:00
|
|
|
|
2024-02-29 21:32:07 +00:00
|
|
|
xml.write_event_async(Event::Start(start.clone())).await?;
|
|
|
|
for violation in &self.0 {
|
|
|
|
violation.write(xml, ctx.child()).await?;
|
|
|
|
}
|
|
|
|
xml.write_event_async(Event::End(end)).await?;
|
2024-02-29 19:40:40 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<E: Extension, C: Context<E>> QuickWritable<E,C> for Violation<E> {
|
|
|
|
async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> {
|
|
|
|
match self {
|
2024-02-29 21:32:07 +00:00
|
|
|
Violation::LockTokenMatchesRequestUri => xml.write_event_async(Event::Empty(ctx.create_dav_element("lock-token-matches-request-uri"))).await?,
|
|
|
|
Violation::LockTokenSubmitted(hrefs) => {
|
|
|
|
let start = ctx.create_dav_element("lock-token-submitted");
|
|
|
|
let end = start.to_end();
|
|
|
|
|
|
|
|
xml.write_event_async(Event::Start(start.clone())).await?;
|
|
|
|
for href in hrefs {
|
|
|
|
href.write(xml, ctx.child()).await?;
|
2024-02-29 19:40:40 +00:00
|
|
|
}
|
2024-02-29 21:32:07 +00:00
|
|
|
xml.write_event_async(Event::End(end)).await?;
|
|
|
|
},
|
|
|
|
Violation::NoConflictingLock(hrefs) => {
|
|
|
|
let start = ctx.create_dav_element("no-conflicting-lock");
|
|
|
|
let end = start.to_end();
|
|
|
|
|
|
|
|
xml.write_event_async(Event::Start(start.clone())).await?;
|
|
|
|
for href in hrefs {
|
|
|
|
href.write(xml, ctx.child()).await?;
|
2024-02-29 19:40:40 +00:00
|
|
|
}
|
2024-02-29 21:32:07 +00:00
|
|
|
xml.write_event_async(Event::End(end)).await?;
|
|
|
|
},
|
|
|
|
Violation::NoExternalEntities => xml.write_event_async(Event::Empty(ctx.create_dav_element("no-external-entities"))).await?,
|
|
|
|
Violation::PreservedLiveProperties => xml.write_event_async(Event::Empty(ctx.create_dav_element("preserved-live-properties"))).await?,
|
|
|
|
Violation::PropfindFiniteDepth => xml.write_event_async(Event::Empty(ctx.create_dav_element("propfind-finite-depth"))).await?,
|
|
|
|
Violation::CannotModifyProtectedProperty => xml.write_event_async(Event::Empty(ctx.create_dav_element("cannot-modify-protected-property"))).await?,
|
2024-02-29 19:40:40 +00:00
|
|
|
Violation::Extension(inner) => {
|
|
|
|
ctx.hook_error(inner, xml).await?;
|
|
|
|
},
|
|
|
|
};
|
2024-02-29 09:17:46 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
2024-02-28 09:20:28 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
2024-02-28 21:00:47 +00:00
|
|
|
use tokio::io::AsyncWriteExt;
|
|
|
|
|
|
|
|
/// To run only the unit tests and avoid the behavior ones:
|
|
|
|
/// cargo test --bin aerogramme
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn test_href() {
|
|
|
|
let mut buffer = Vec::new();
|
|
|
|
let mut tokio_buffer = tokio::io::BufWriter::new(&mut buffer);
|
|
|
|
let mut writer = Writer::new_with_indent(&mut tokio_buffer, b' ', 4);
|
|
|
|
|
2024-02-29 21:32:07 +00:00
|
|
|
let ctx = NoExtCtx{ root: false };
|
2024-02-29 19:40:40 +00:00
|
|
|
Href("/SOGo/dav/so/".into()).write(&mut writer, ctx).await.expect("xml serialization");
|
2024-02-28 21:00:47 +00:00
|
|
|
tokio_buffer.flush().await.expect("tokio buffer flush");
|
2024-02-28 09:20:28 +00:00
|
|
|
|
2024-02-28 21:00:47 +00:00
|
|
|
assert_eq!(buffer.as_slice(), &b"<D:href>/SOGo/dav/so/</D:href>"[..]);
|
2024-02-28 09:20:28 +00:00
|
|
|
}
|
2024-02-29 09:17:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn test_multistatus() {
|
|
|
|
let mut buffer = Vec::new();
|
|
|
|
let mut tokio_buffer = tokio::io::BufWriter::new(&mut buffer);
|
|
|
|
let mut writer = Writer::new_with_indent(&mut tokio_buffer, b' ', 4);
|
|
|
|
|
2024-02-29 19:40:40 +00:00
|
|
|
let ctx = NoExtCtx{ root: true };
|
|
|
|
let xml: Multistatus<NoExtension> = Multistatus { responses: vec![], responsedescription: Some(ResponseDescription("Hello world".into())) };
|
|
|
|
xml.write(&mut writer, ctx).await.expect("xml serialization");
|
2024-02-29 09:17:46 +00:00
|
|
|
tokio_buffer.flush().await.expect("tokio buffer flush");
|
|
|
|
|
|
|
|
let expected = r#"<D:multistatus xmlns:D="DAV:">
|
|
|
|
<D:responsedescription>Hello world</D:responsedescription>
|
|
|
|
</D:multistatus>"#;
|
|
|
|
let got = std::str::from_utf8(buffer.as_slice()).unwrap();
|
|
|
|
|
|
|
|
assert_eq!(got, expected);
|
|
|
|
}
|
2024-02-28 09:20:28 +00:00
|
|
|
}
|