aster_forge_webdav/
deltav.rs1use http::header::{CACHE_CONTROL, CONTENT_TYPE};
4use http::{HeaderValue, StatusCode};
5
6use crate::{
7 DavErrorCondition, DavResourceKind, DavResponse, DavVersionXml, DavXmlError, dav_error_element,
8 dav_version_multistatus_element, parse_report_root,
9};
10
11#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
13pub enum DavVersionTreeReportError {
14 #[error(transparent)]
16 Xml(#[from] DavXmlError),
17 #[error("unsupported DeltaV REPORT type: {name}")]
19 Unsupported { name: String },
20}
21
22pub fn validate_version_tree_report(body: &[u8]) -> Result<(), DavVersionTreeReportError> {
24 let root = parse_report_root(body)?;
25 if root.name == "version-tree" && root.namespace.as_deref() == Some("DAV:") {
26 Ok(())
27 } else {
28 Err(DavVersionTreeReportError::Unsupported { name: root.name })
29 }
30}
31
32pub fn version_tree_report_error_response(
34 error: &DavVersionTreeReportError,
35) -> Result<DavResponse, DavXmlError> {
36 match error {
37 DavVersionTreeReportError::Xml(DavXmlError::ExternalEntity) => xml_response(
38 StatusCode::FORBIDDEN,
39 dav_error_element(&DavErrorCondition::NoExternalEntities),
40 ),
41 DavVersionTreeReportError::Xml(DavXmlError::TooLarge) => Ok(text_response(
42 StatusCode::PAYLOAD_TOO_LARGE,
43 "WebDAV XML body too large",
44 )),
45 DavVersionTreeReportError::Xml(
46 DavXmlError::TooDeep | DavXmlError::Malformed | DavXmlError::InvalidGrammar,
47 ) => Ok(text_response(StatusCode::BAD_REQUEST, "Invalid XML body")),
48 DavVersionTreeReportError::Unsupported { name } => Ok(text_response(
49 StatusCode::NOT_IMPLEMENTED,
50 format!("Unsupported REPORT type: {name}"),
51 )),
52 }
53}
54
55#[must_use]
57pub fn version_tree_non_file_response() -> DavResponse {
58 text_response(
59 StatusCode::CONFLICT,
60 "Version history is only available for files",
61 )
62}
63
64pub fn version_tree_response(versions: Vec<DavVersionXml>) -> Result<DavResponse, DavXmlError> {
66 xml_response(
67 StatusCode::MULTI_STATUS,
68 dav_version_multistatus_element(versions),
69 )
70}
71
72#[must_use]
74pub fn version_control_response(kind: DavResourceKind) -> DavResponse {
75 match kind {
76 DavResourceKind::File => text_response(StatusCode::OK, "Already under version control"),
77 DavResourceKind::Collection => text_response(
78 StatusCode::METHOD_NOT_ALLOWED,
79 "Only files support version control",
80 ),
81 }
82}
83
84fn text_response(status: StatusCode, body: impl Into<String>) -> DavResponse {
85 let mut response = DavResponse::bytes(status, body.into());
86 response.headers.insert(
87 CONTENT_TYPE,
88 HeaderValue::from_static("text/plain; charset=utf-8"),
89 );
90 if status.is_client_error() || status.is_server_error() {
91 response
92 .headers
93 .insert(CACHE_CONTROL, HeaderValue::from_static("no-store"));
94 }
95 response
96}
97
98fn xml_response(
99 status: StatusCode,
100 root: crate::DavXmlElement,
101) -> Result<DavResponse, DavXmlError> {
102 let mut response = DavResponse::bytes(status, root.to_bytes()?);
103 response.headers.insert(
104 CONTENT_TYPE,
105 HeaderValue::from_static("application/xml; charset=utf-8"),
106 );
107 if status.is_client_error() || status.is_server_error() {
108 response
109 .headers
110 .insert(CACHE_CONTROL, HeaderValue::from_static("no-store"));
111 }
112 Ok(response)
113}