imessage_database/util/
plist.rs1use plist::{Dictionary, Value};
26
27use crate::error::plist::PlistParseError;
28
29pub fn parse_ns_keyed_archiver(plist: &Value) -> Result<Value, PlistParseError> {
67 let body = plist_as_dictionary(plist)?;
68 let objects = extract_array_key(body, "$objects")?;
69
70 let root = extract_uid_key(extract_dictionary(body, "$top")?, "root")?;
72
73 follow_uid(objects, root, None, None)
74}
75
76fn follow_uid<'a>(
79 objects: &'a [Value],
80 root: usize,
81 parent: Option<&str>,
82 item: Option<&'a Value>,
83) -> Result<Value, PlistParseError> {
84 let item = match item {
85 Some(item) => item,
86 None => objects
87 .get(root)
88 .ok_or(PlistParseError::NoValueAtIndex(root))?,
89 };
90
91 match item {
92 Value::Array(arr) => {
93 let mut array = vec![];
94 for item in arr {
95 if let Some(idx) = item.as_uid() {
96 array.push(follow_uid(objects, idx.get() as usize, parent, None)?);
97 }
98 }
99 Ok(plist::Value::Array(array))
100 }
101 Value::Dictionary(dict) => {
102 let mut dictionary = Dictionary::new();
103 if let Some(relative) = dict.get("NS.relative") {
105 if let Some(idx) = relative.as_uid() {
106 if let Some(p) = &parent {
107 dictionary.insert(
108 (*p).to_string(),
109 follow_uid(objects, idx.get() as usize, Some(p), None)?,
110 );
111 }
112 }
113 }
114 else if dict.contains_key("NS.keys") && dict.contains_key("NS.objects") {
116 let keys = extract_array_key(dict, "NS.keys")?;
117 let values = extract_array_key(dict, "NS.objects")?;
119 if keys.len() != values.len() {
121 return Err(PlistParseError::InvalidDictionarySize(
122 keys.len(),
123 values.len(),
124 ));
125 }
126
127 for idx in 0..keys.len() {
128 let key_index = extract_uid_idx(keys, idx)?;
129 let value_index = extract_uid_idx(values, idx)?;
130 let key = extract_string_idx(objects, key_index)?;
131
132 dictionary.insert(
133 String::from(key),
134 follow_uid(objects, value_index, Some(key), None)?,
135 );
136 }
137 }
138 else {
140 for (key, val) in dict {
141 if key == "$class" {
143 continue;
144 }
145 if let Some(idx) = val.as_uid() {
147 dictionary.insert(
148 String::from(key),
149 follow_uid(objects, idx.get() as usize, Some(key), None)?,
150 );
151 }
152 else if let Some(p) = parent {
154 dictionary.insert(
155 String::from(p),
156 follow_uid(objects, root, Some(p), Some(val))?,
157 );
158 }
159 }
160 }
161 Ok(plist::Value::Dictionary(dictionary))
162 }
163 Value::Uid(uid) => follow_uid(objects, uid.get() as usize, None, None),
164 _ => Ok(item.to_owned()),
165 }
166}
167
168pub fn plist_as_dictionary(plist: &Value) -> Result<&Dictionary, PlistParseError> {
170 plist
171 .as_dictionary()
172 .ok_or_else(|| PlistParseError::InvalidType("body".to_string(), "dictionary".to_string()))
173}
174
175pub fn extract_dictionary<'a>(
177 body: &'a Dictionary,
178 key: &str,
179) -> Result<&'a Dictionary, PlistParseError> {
180 body.get(key)
181 .ok_or_else(|| PlistParseError::MissingKey(key.to_string()))?
182 .as_dictionary()
183 .ok_or_else(|| PlistParseError::InvalidType(key.to_string(), "dictionary".to_string()))
184}
185
186pub fn extract_array_key<'a>(
188 body: &'a Dictionary,
189 key: &str,
190) -> Result<&'a Vec<Value>, PlistParseError> {
191 body.get(key)
192 .ok_or_else(|| PlistParseError::MissingKey(key.to_string()))?
193 .as_array()
194 .ok_or_else(|| PlistParseError::InvalidType(key.to_string(), "array".to_string()))
195}
196
197fn extract_uid_key(body: &Dictionary, key: &str) -> Result<usize, PlistParseError> {
199 Ok(body
200 .get(key)
201 .ok_or_else(|| PlistParseError::MissingKey(key.to_string()))?
202 .as_uid()
203 .ok_or_else(|| PlistParseError::InvalidType(key.to_string(), "uid".to_string()))?
204 .get() as usize)
205}
206
207pub fn extract_bytes_key<'a>(body: &'a Dictionary, key: &str) -> Result<&'a [u8], PlistParseError> {
209 body.get(key)
210 .ok_or_else(|| PlistParseError::MissingKey(key.to_string()))?
211 .as_data()
212 .ok_or_else(|| PlistParseError::InvalidType(key.to_string(), "data".to_string()))
213}
214
215pub fn extract_int_key(body: &Dictionary, key: &str) -> Result<i64, PlistParseError> {
217 Ok(body
218 .get(key)
219 .ok_or_else(|| PlistParseError::MissingKey(key.to_string()))?
220 .as_real()
221 .ok_or_else(|| PlistParseError::InvalidType(key.to_string(), "int".to_string()))?
222 as i64)
223}
224
225fn extract_uid_idx(body: &[Value], idx: usize) -> Result<usize, PlistParseError> {
227 Ok(body
228 .get(idx)
229 .ok_or(PlistParseError::NoValueAtIndex(idx))?
230 .as_uid()
231 .ok_or_else(|| PlistParseError::InvalidTypeIndex(idx, "uid".to_string()))?
232 .get() as usize)
233}
234
235fn extract_string_idx(body: &[Value], idx: usize) -> Result<&str, PlistParseError> {
237 body.get(idx)
238 .ok_or(PlistParseError::NoValueAtIndex(idx))?
239 .as_string()
240 .ok_or_else(|| PlistParseError::InvalidTypeIndex(idx, "string".to_string()))
241}
242
243#[must_use]
245pub fn get_string_from_dict<'a>(payload: &'a Value, key: &'a str) -> Option<&'a str> {
246 payload
247 .as_dictionary()?
248 .get(key)?
249 .as_string()
250 .filter(|s| !s.is_empty())
251}
252
253#[must_use]
255pub fn get_value_from_dict<'a>(payload: &'a Value, key: &'a str) -> Option<&'a Value> {
256 payload.as_dictionary()?.get(key)
257}
258
259#[must_use]
261pub fn get_bool_from_dict<'a>(payload: &'a Value, key: &'a str) -> Option<bool> {
262 payload.as_dictionary()?.get(key)?.as_boolean()
263}
264
265#[must_use]
267pub fn get_string_from_nested_dict<'a>(payload: &'a Value, key: &'a str) -> Option<&'a str> {
268 payload
269 .as_dictionary()?
270 .get(key)?
271 .as_dictionary()?
272 .get(key)?
273 .as_string()
274 .filter(|s| !s.is_empty())
275}
276
277#[must_use]
279pub fn get_float_from_nested_dict<'a>(payload: &'a Value, key: &'a str) -> Option<f64> {
280 payload
281 .as_dictionary()?
282 .get(key)?
283 .as_dictionary()?
284 .get(key)?
285 .as_real()
286}