onenote_parser/fsshttpb/data/
stream_object.rs

1use crate::errors::{ErrorKind, Result};
2use crate::fsshttpb::data::compact_u64::CompactU64;
3use crate::fsshttpb::data::object_types::ObjectType;
4use crate::Reader;
5use num_traits::{FromPrimitive, ToPrimitive};
6
7/// A FSSHTTPB stream object header.
8///
9/// See [\[MS-FSSHTTPB\] 2.2.1.5].
10///
11/// [\[MS-FSSHTTPB\] 2.2.1.5]: https://docs.microsoft.com/en-us/openspecs/sharepoint_protocols/ms-fsshttpb/5faee10f-8e55-43f8-935a-d6e4294856fc
12#[derive(Debug)]
13pub struct ObjectHeader {
14    pub compound: bool,
15    pub object_type: ObjectType,
16    pub length: u64,
17}
18
19impl ObjectHeader {
20    pub(crate) fn try_parse(reader: Reader, object_type: ObjectType) -> Result<()> {
21        Self::try_parse_start(reader, object_type, Self::parse)
22    }
23
24    /// Parse a 16-bit or 32-bit stream object header.
25    pub(crate) fn parse(reader: Reader) -> Result<ObjectHeader> {
26        let header_type = reader.bytes().first().ok_or(ErrorKind::UnexpectedEof)?;
27
28        match header_type & 0b11 {
29            0x0 => Self::parse_16(reader),
30            0x2 => Self::parse_32(reader),
31            _ => Err(ErrorKind::MalformedFssHttpBData(
32                format!("unexpected object header type: {:x}", header_type).into(),
33            )
34            .into()),
35        }
36    }
37
38    pub(crate) fn try_parse_16(reader: Reader, object_type: ObjectType) -> Result<()> {
39        Self::try_parse_start(reader, object_type, Self::parse_16)
40    }
41
42    /// Parse a 16 bit stream object header.
43    ///
44    /// See [\[MS-FSSHTTPB\] 2.2.1.5.1]
45    ///
46    /// [\[MS-FSSHTTPB\] 2.2.1.5.1]: https://docs.microsoft.com/en-us/openspecs/sharepoint_protocols/ms-fsshttpb/a1017f48-a888-49ff-b71d-cc3c707f753a
47    pub(crate) fn parse_16(reader: Reader) -> Result<ObjectHeader> {
48        let data = reader.get_u16()?;
49
50        let header_type = data & 0b11;
51        if header_type != 0x0 {
52            return Err(ErrorKind::MalformedFssHttpBData(
53                format!(
54                    "unexpected object header type for 16 bit header: 0x{:x}",
55                    header_type
56                )
57                .into(),
58            )
59            .into());
60        }
61
62        let compound = data & 0x4 == 0x4;
63        let object_type_value = (data >> 3) & 0x3f;
64        let object_type = if let Some(object_type) = ObjectType::from_u16(object_type_value) {
65            object_type
66        } else {
67            return Err(ErrorKind::MalformedFssHttpBData(
68                format!("invalid object type: 0x{:x}", object_type_value).into(),
69            )
70            .into());
71        };
72        let length = (data >> 9) as u64;
73
74        Ok(ObjectHeader {
75            compound,
76            object_type,
77            length,
78        })
79    }
80
81    pub(crate) fn try_parse_32(reader: Reader, object_type: ObjectType) -> Result<()> {
82        Self::try_parse_start(reader, object_type, Self::parse_32)
83    }
84
85    /// Parse a 32 bit stream object header.
86    ///
87    /// See [\[MS-FSSHTTPB\] 2.2.1.5.2]
88    ///
89    /// [\[MS-FSSHTTPB\] 2.2.1.5.2]: https://docs.microsoft.com/en-us/openspecs/sharepoint_protocols/ms-fsshttpb/ac629d63-60a1-49b2-9db2-fa3c19971cc9
90    fn parse_32(reader: Reader) -> Result<ObjectHeader> {
91        let data = reader.get_u32()?;
92
93        let header_type = data & 0b11;
94        if header_type != 0x2 {
95            return Err(ErrorKind::MalformedFssHttpBData(
96                format!(
97                    "unexpected object header type for 32 bit header: 0x{:x}",
98                    header_type
99                )
100                .into(),
101            )
102            .into());
103        }
104
105        let compound = data & 0x4 == 0x4;
106        let object_type_value = (data >> 3) & 0x3fff;
107        let object_type = if let Some(object_type) = ObjectType::from_u32(object_type_value) {
108            object_type
109        } else {
110            return Err(ErrorKind::MalformedFssHttpBData(
111                format!("invalid object type: 0x{:x}", object_type_value).into(),
112            )
113            .into());
114        };
115        let mut length = (data >> 17) as u64;
116
117        if length == 0x7fff {
118            length = CompactU64::parse(reader)?.value();
119        }
120
121        Ok(ObjectHeader {
122            compound,
123            object_type,
124            length,
125        })
126    }
127
128    pub(crate) fn try_parse_end_16(reader: Reader, object_type: ObjectType) -> Result<()> {
129        Self::try_parse_end(reader, object_type, Self::parse_end_16)
130    }
131
132    /// Parse a 16-bit stream object header end.
133    ///
134    /// See [\[MS-FSSHTTPB\] 2.2.1.5.4]
135    ///
136    /// [\[MS-FSSHTTPB\] 2.2.1.5.4]: https://docs.microsoft.com/en-us/openspecs/sharepoint_protocols/ms-fsshttpb/d8cedbb8-073b-4711-8867-f88b887ab0a9
137    fn parse_end_16(reader: Reader) -> Result<ObjectType> {
138        let data = reader.get_u16()?;
139        let header_type = data & 0b11;
140        if header_type != 0x3 {
141            return Err(ErrorKind::MalformedFssHttpBData(
142                format!(
143                    "unexpected object header type for 16 bit end header: {:x}",
144                    header_type
145                )
146                .into(),
147            )
148            .into());
149        }
150
151        let object_type_value = data >> 2;
152
153        if let Some(object_type) = ObjectType::from_u16(object_type_value) {
154            Ok(object_type)
155        } else {
156            Err(ErrorKind::MalformedFssHttpBData(
157                format!("invalid object type: 0x{:x}", object_type_value).into(),
158            )
159            .into())
160        }
161    }
162
163    pub(crate) fn try_parse_end_8(reader: Reader, object_type: ObjectType) -> Result<()> {
164        Self::try_parse_end(reader, object_type, Self::parse_end_8)
165    }
166
167    /// Parse a 8-bit stream object header end.
168    ///
169    /// See [\[MS-FSSHTTPB\] 2.2.1.5.3]
170    ///
171    /// [\[MS-FSSHTTPB\] 2.2.1.5.3]: https://docs.microsoft.com/en-us/openspecs/sharepoint_protocols/ms-fsshttpb/544ce81a-44e3-48ff-b094-0e51c7207aa1
172    fn parse_end_8(reader: Reader) -> Result<ObjectType> {
173        let data = reader.get_u8()?;
174        let header_type = data & 0b11;
175        if header_type != 0x1 {
176            return Err(ErrorKind::MalformedFssHttpBData(
177                format!(
178                    "unexpected object header type for 8 bit end header: {:x}",
179                    header_type
180                )
181                .into(),
182            )
183            .into());
184        }
185
186        let object_type_value = data >> 2;
187
188        if let Some(object_type) = ObjectType::from_u8(object_type_value) {
189            Ok(object_type)
190        } else {
191            Err(ErrorKind::MalformedFssHttpBData(
192                format!("invalid object type: 0x{:x}", object_type_value).into(),
193            )
194            .into())
195        }
196    }
197
198    pub(crate) fn has_end_8(reader: Reader, object_type: ObjectType) -> Result<bool> {
199        let data = reader.bytes().first().ok_or(ErrorKind::UnexpectedEof)?;
200
201        Ok(data & 0b11 == 0x1 && data >> 2 == object_type.to_u8().unwrap())
202    }
203
204    fn try_parse_start(
205        reader: Reader,
206        object_type: ObjectType,
207        parse: fn(Reader) -> Result<ObjectHeader>,
208    ) -> Result<()> {
209        match parse(reader) {
210            Ok(header) if header.object_type == object_type => Ok(()),
211            Ok(header) => Err(ErrorKind::MalformedFssHttpBData(
212                format!("unexpected object type: {:x}", header.object_type).into(),
213            )
214            .into()),
215            Err(e) => Err(e),
216        }
217    }
218
219    fn try_parse_end(
220        reader: Reader,
221        object_type: ObjectType,
222        parse: fn(Reader) -> Result<ObjectType>,
223    ) -> Result<()> {
224        match parse(reader) {
225            Ok(header) if header == object_type => Ok(()),
226            Ok(header) => Err(ErrorKind::MalformedFssHttpBData(
227                format!("unexpected object type: {:x}", header).into(),
228            )
229            .into()),
230            Err(e) => Err(e),
231        }
232    }
233}