53 const std::string& root_xml,
const std::vector<mtom_part>& parts,
55 if (root_xml.size() > kMaxRootXmlBytes) {
56 return kcenon::common::make_error<document_response>(
58 "RetrieveDocumentSetResponse root part exceeds 8 MiB cap (" +
59 std::to_string(root_xml.size()) +
" bytes)",
63 pugi::xml_document doc;
64 auto parse = doc.load_buffer(root_xml.data(), root_xml.size(),
65 pugi::parse_default, pugi::encoding_utf8);
67 return kcenon::common::make_error<document_response>(
69 std::string(
"failed to parse response envelope: ") +
80 "//*[local-name()='RetrieveDocumentSetResponse']").node();
82 return kcenon::common::make_error<document_response>(
84 "response contains no RetrieveDocumentSetResponse element",
88 auto registry_response =
89 rds_response.select_node(
"*[local-name()='RegistryResponse']").node();
90 if (!registry_response) {
91 return kcenon::common::make_error<document_response>(
93 "response has no RegistryResponse",
96 const std::string status =
97 registry_response.attribute(
"status").as_string();
99 return kcenon::common::make_error<document_response>(
101 "RegistryResponse is missing status attribute",
104 if (status == kPartialSuccessStatus) {
105 return kcenon::common::make_error<document_response>(
107 "repository reported PartialSuccess: " + status,
110 if (status != kSuccessStatus) {
114 return kcenon::common::make_error<document_response>(
116 "repository reported non-success status: " + status,
123 pugi::xml_node
match;
125 rds_response.select_nodes(
"*[local-name()='DocumentResponse']")) {
126 auto node = dr.node();
127 const std::string
uid =
128 node.select_node(
"*[local-name()='DocumentUniqueId']")
138 return kcenon::common::make_error<document_response>(
140 "no DocumentResponse matched requested uid " +
145 const std::string mime_type =
146 match.select_node(
"*[local-name()='mimeType']").node().text().as_string();
149 match.select_node(
"*[local-name()='Document']").node();
150 if (!document_node) {
151 return kcenon::common::make_error<document_response>(
153 "matching DocumentResponse has no Document element",
162 document_node.select_node(
"*[local-name()='Include']").node();
165 out.registry_response_status = status;
168 out.mime_type = mime_type;
171 const std::string raw_href = include.attribute(
"href").as_string();
172 const std::string cid = strip_cid_href(raw_href);
174 return kcenon::common::make_error<document_response>(
177 "xop:Include has empty href",
181 for (
const auto& p : parts) {
182 if (p.content_id == cid) {
187 if (found ==
nullptr) {
188 return kcenon::common::make_error<document_response>(
191 "xop:Include references unknown cid: " + cid,
195 if (
out.mime_type.empty()) {
202 const std::string inline_b64 = document_node.text().as_string();
203 if (inline_b64.empty()) {
204 return kcenon::common::make_error<document_response>(
206 "Document element has neither xop:Include nor inline content",
214 if (
out.content.empty()) {
215 return kcenon::common::make_error<document_response>(
217 "inline Document base64 decode failed or yielded empty bytes",
kcenon::common::Result< document_response > parse_retrieve_response(const std::string &root_xml, const std::vector< mtom_part > &parts, const retrieve_request &req)
Parse a RetrieveDocumentSetResponse envelope and resolve the attachment that matches req.