sccp_protocol/phone/xml/
document.rs1use quick_xml::events::Event;
9use quick_xml::reader::Reader;
10use serde::Serialize;
11use serde::de::DeserializeOwned;
12
13use super::{
14 CiscoIpPhoneDirectory, CiscoIpPhoneExecute, CiscoIpPhoneIconFileMenu, CiscoIpPhoneIconMenu,
15 CiscoIpPhoneImageList, CiscoIpPhoneInput, CiscoIpPhoneMenu, CiscoIpPhoneStatus,
16 CiscoIpPhoneStatusFile, CiscoIpPhoneText, PHONE_BACKGROUND_LIST_MAX_BYTES,
17 PHONE_DIRECTORY_MAX_BYTES, PHONE_EXECUTE_MAX_BYTES, PHONE_INPUT_MAX_BYTES,
18 PHONE_MENU_MAX_BYTES, PHONE_STATUS_MAX_BYTES, PHONE_TEXT_MAX_BYTES, PhoneXmlError,
19 decoding_reader, from_bytes, to_string,
20};
21
22mod sealed {
23 pub trait Sealed {}
24}
25
26pub trait PhoneXmlDocument: sealed::Sealed + Sized + Serialize + DeserializeOwned {
33 const ROOT: &'static [u8];
35 const MAXIMUM_BYTES: usize;
37
38 fn validate_document(&self) -> Result<(), PhoneXmlError>;
40
41 fn parse_xml(document: &[u8]) -> Result<Self, PhoneXmlError> {
43 Self::parse_xml_with_limit(document, Self::MAXIMUM_BYTES)
44 }
45
46 fn parse_xml_with_limit(document: &[u8], maximum_bytes: usize) -> Result<Self, PhoneXmlError> {
51 let parsed: Self = from_bytes(document, maximum_bytes)?;
52 validate_root(document, Self::ROOT)?;
53 parsed.validate_document()?;
54 Ok(parsed)
55 }
56
57 fn serialize_xml(&self) -> Result<String, PhoneXmlError> {
59 self.serialize_xml_with_limit(Self::MAXIMUM_BYTES)
60 }
61
62 fn serialize_xml_with_limit(&self, maximum_bytes: usize) -> Result<String, PhoneXmlError> {
64 self.validate_document()?;
65 to_string(self, maximum_bytes)
66 }
67}
68
69macro_rules! impl_phone_xml_document {
70 ($document:ty, $root:literal, $maximum:expr) => {
71 impl sealed::Sealed for $document {}
72
73 impl PhoneXmlDocument for $document {
74 const ROOT: &'static [u8] = $root;
75 const MAXIMUM_BYTES: usize = $maximum;
76
77 fn validate_document(&self) -> Result<(), PhoneXmlError> {
78 <$document>::validate(self)
79 }
80 }
81 };
82}
83
84impl_phone_xml_document!(CiscoIpPhoneText, b"CiscoIPPhoneText", PHONE_TEXT_MAX_BYTES);
85impl_phone_xml_document!(
86 CiscoIpPhoneInput,
87 b"CiscoIPPhoneInput",
88 PHONE_INPUT_MAX_BYTES
89);
90impl_phone_xml_document!(
91 CiscoIpPhoneExecute,
92 b"CiscoIPPhoneExecute",
93 PHONE_EXECUTE_MAX_BYTES
94);
95impl_phone_xml_document!(
96 CiscoIpPhoneImageList,
97 b"CiscoIPPhoneImageList",
98 PHONE_BACKGROUND_LIST_MAX_BYTES
99);
100impl_phone_xml_document!(
101 CiscoIpPhoneStatus,
102 b"CiscoIPPhoneStatus",
103 PHONE_STATUS_MAX_BYTES
104);
105impl_phone_xml_document!(
106 CiscoIpPhoneStatusFile,
107 b"CiscoIPPhoneStatusFile",
108 PHONE_STATUS_MAX_BYTES
109);
110impl_phone_xml_document!(
111 CiscoIpPhoneDirectory,
112 b"CiscoIPPhoneDirectory",
113 PHONE_DIRECTORY_MAX_BYTES
114);
115impl_phone_xml_document!(CiscoIpPhoneMenu, b"CiscoIPPhoneMenu", PHONE_MENU_MAX_BYTES);
116impl_phone_xml_document!(
117 CiscoIpPhoneIconMenu,
118 b"CiscoIPPhoneIconMenu",
119 PHONE_MENU_MAX_BYTES
120);
121impl_phone_xml_document!(
122 CiscoIpPhoneIconFileMenu,
123 b"CiscoIPPhoneIconFileMenu",
124 PHONE_MENU_MAX_BYTES
125);
126
127fn validate_root(document: &[u8], expected: &[u8]) -> Result<(), PhoneXmlError> {
128 let mut reader = Reader::from_reader(decoding_reader(document));
129 let mut buffer = Vec::new();
130 loop {
131 match reader.read_event_into(&mut buffer) {
132 Ok(Event::Start(element) | Event::Empty(element)) => {
133 return if element.name().as_ref().as_bytes() == expected {
134 Ok(())
135 } else {
136 Err(PhoneXmlError::InvalidField {
137 field: "phone XML document root",
138 expected: "the schema root for this document type",
139 })
140 };
141 }
142 Ok(Event::Eof) => {
143 return Err(PhoneXmlError::InvalidField {
144 field: "phone XML document root",
145 expected: "one schema root element",
146 });
147 }
148 Ok(_) => {}
149 Err(error) => return Err(PhoneXmlError::Malformed(error)),
150 }
151 buffer.clear();
152 }
153}