1use super::decoders::{decode_datetime, decode_uuid};
2use crate::database::{
3 Database, Entry, Field, Group, History, MemoryProtection, Meta, Times, Value,
4};
5use base64::prelude::{Engine, BASE64_STANDARD};
6use chrono::NaiveDateTime;
7use cipher::StreamCipher;
8use std::io::Read;
9use thiserror::Error;
10use uuid::Uuid;
11use xml::reader::{EventReader, XmlEvent};
12
13#[derive(Debug, Error)]
14pub enum Error {
16 #[error("Error parsing database XML: {0}")]
18 Xml(String),
19 #[error("A field has an empty name")]
21 KeyEmptyName,
22 #[error("UUID is not valid")]
24 InvalidUuid,
25 #[error("Datetime is not valid")]
27 InvalidDatetime,
28 #[error("Invalid numeric value")]
30 InvalidNumber,
31 #[error("Could not decrypt value for Key {0:?}")]
33 DecryptFailed(String),
34}
35
36pub type Result<T> = std::result::Result<T, Error>;
37
38impl From<xml::reader::Error> for Error {
39 fn from(e: xml::reader::Error) -> Error {
40 Error::Xml(e.msg().to_string())
41 }
42}
43
44fn parse_string<R: Read>(xml_event_reader: &mut EventReader<R>) -> Result<Option<String>> {
45 let mut content = None;
46 loop {
47 match xml_event_reader.next()? {
48 XmlEvent::Characters(chardata) => {
49 content = content.map_or_else(
50 || Some(chardata.clone()),
51 |mut existing: String| {
52 existing.push_str(&chardata);
53 Some(existing)
54 },
55 )
56 }
57 XmlEvent::EndElement { .. } => break,
58 _ => {}
59 }
60 }
61 Ok(content)
62}
63
64macro_rules! parse_numeric_type {
65 ($name:ident, $ty:ty) => {
66 fn $name<R: Read>(xml_event_reader: &mut EventReader<R>) -> Result<Option<$ty>> {
67 parse_string(xml_event_reader)?
68 .map(|num| num.parse())
69 .transpose()
70 .map_err(|_| Error::InvalidNumber)
71 }
72 };
73}
74
75parse_numeric_type!(parse_u32, u32);
76
77fn parse_uuid<R: Read>(xml_event_reader: &mut EventReader<R>) -> Result<Uuid> {
78 parse_string(xml_event_reader)?
79 .and_then(|uuid| decode_uuid(&uuid))
80 .ok_or(Error::InvalidUuid)
81}
82
83fn parse_datetime<R: Read>(xml_event_reader: &mut EventReader<R>) -> Result<NaiveDateTime> {
84 parse_string(xml_event_reader)?
85 .and_then(|dt| decode_datetime(&dt))
86 .ok_or(Error::InvalidDatetime)
87}
88
89fn parse_bool<R: Read>(xml_event_reader: &mut EventReader<R>) -> Result<bool> {
90 Ok(parse_string(xml_event_reader)?
91 .map(|b| b.to_lowercase() == "true")
92 .unwrap_or_default())
93}
94
95fn parse_field<R: Read, S: StreamCipher + ?Sized>(
96 xml_event_reader: &mut EventReader<R>,
97 tag_name: &str,
98 stream_cipher: &mut S,
99) -> Result<Field> {
100 let mut field = Field::default();
101 loop {
102 match xml_event_reader.next()? {
103 XmlEvent::StartElement { name, .. } if &name.local_name == "Key" => {
104 let parse_result = parse_string(xml_event_reader)?;
105 let val = parse_result.ok_or(Error::KeyEmptyName)?;
106 field.key = val;
107 }
108 XmlEvent::StartElement {
109 name, attributes, ..
110 } if &name.local_name == "Value" => {
111 let protected = attributes.iter().any(|attr| {
112 attr.name.local_name == "Protected" && attr.value.to_lowercase() == "true"
113 });
114 field.value = if let Some(contents) = parse_string(xml_event_reader)? {
115 if protected {
116 let key_clone = field.key.clone();
118 match BASE64_STANDARD.decode(&contents) {
119 Ok(mut decoded) => {
120 stream_cipher
121 .try_apply_keystream(decoded.as_mut())
122 .map_err(|e| {
123 Error::DecryptFailed(format!(
124 "Failed to apply stream cipher: {}",
125 e
126 ))
127 })?;
128 let to_str = String::from_utf8(decoded)
129 .map_err(|_| Error::DecryptFailed(key_clone))?;
130 Value::Protected(to_str)
131 }
132 Err(_) => return Err(Error::DecryptFailed(key_clone)),
133 }
134 } else {
135 Value::Standard(contents)
136 }
137 } else {
138 Value::Empty
139 }
140 }
141 XmlEvent::EndElement { name, .. } if name.local_name == tag_name => break,
142 _ => {}
143 }
144 }
145 Ok(field)
146}
147
148fn parse_history<R: Read, S: StreamCipher + ?Sized>(
149 xml_event_reader: &mut EventReader<R>,
150 stream_cipher: &mut S,
151) -> Result<History> {
152 let mut history = History::default();
153 loop {
154 match xml_event_reader.next()? {
155 XmlEvent::StartElement { name, .. } if &name.local_name == "Entry" => {
156 history.push(parse_entry(xml_event_reader, stream_cipher)?);
157 }
158 XmlEvent::EndElement { name, .. } if &name.local_name == "History" => break,
159 _ => {}
160 }
161 }
162 Ok(history)
163}
164
165fn parse_times<R: Read>(xml_event_reader: &mut EventReader<R>) -> Result<Times> {
166 let mut times = Times::default();
167 loop {
168 match xml_event_reader.next()? {
169 XmlEvent::StartElement { name, .. } => {
170 if &name.local_name == "LastModificationTime" {
171 times.last_modification_time = parse_datetime(xml_event_reader)?;
172 } else if &name.local_name == "LastAccessTime" {
173 times.last_access_time = parse_datetime(xml_event_reader)?;
174 } else if &name.local_name == "CreationTime" {
175 times.creation_time = parse_datetime(xml_event_reader)?;
176 } else if &name.local_name == "ExpiryTime" {
177 times.expiry_time = parse_datetime(xml_event_reader)?;
178 } else if &name.local_name == "LocationChanged" {
179 times.location_changed = parse_datetime(xml_event_reader)?;
180 } else if &name.local_name == "Expires" {
181 times.expires = parse_bool(xml_event_reader)?;
182 } else if &name.local_name == "UsageCount" {
183 times.usage_count = parse_u32(xml_event_reader)?.unwrap_or_default();
184 }
185 }
186 XmlEvent::EndElement { name, .. } if &name.local_name == "Times" => break,
187 _ => {}
188 }
189 }
190 Ok(times)
191}
192
193fn parse_entry<R: Read, S: StreamCipher + ?Sized>(
194 xml_event_reader: &mut EventReader<R>,
195 stream_cipher: &mut S,
196) -> Result<Entry> {
197 let mut entry = Entry::default();
198 loop {
199 match xml_event_reader.next()? {
200 XmlEvent::StartElement { name, .. } => {
201 if &name.local_name == "History" {
202 entry.history = parse_history(xml_event_reader, stream_cipher)?;
203 } else if &name.local_name == "String" {
204 entry.add_field(parse_field(xml_event_reader, "String", stream_cipher)?);
205 } else if &name.local_name == "UUID" {
206 entry.set_uuid(parse_uuid(xml_event_reader)?);
207 } else if &name.local_name == "Times" {
208 entry.times = parse_times(xml_event_reader)?;
209 }
210 }
211 XmlEvent::EndElement { name, .. } if &name.local_name == "Entry" => break,
212 _ => {}
213 }
214 }
215 Ok(entry)
216}
217
218fn parse_group<R: Read, S: StreamCipher + ?Sized>(
219 xml_event_reader: &mut EventReader<R>,
220 stream_cipher: &mut S,
221) -> Result<Group> {
222 let mut group = Group::default();
223 loop {
224 match xml_event_reader.next()? {
225 XmlEvent::StartElement { name, .. } => {
226 if &name.local_name == "Group" {
227 group.add_group(parse_group(xml_event_reader, stream_cipher)?);
228 } else if &name.local_name == "Entry" {
229 group.add_entry(parse_entry(xml_event_reader, stream_cipher)?);
230 } else if &name.local_name == "UUID" {
231 group.set_uuid(parse_uuid(xml_event_reader)?);
232 } else if &name.local_name == "Name" {
233 group.set_name(parse_string(xml_event_reader)?.unwrap_or_default());
234 } else if &name.local_name == "Times" {
235 group.times = parse_times(xml_event_reader)?;
236 }
237 }
238 XmlEvent::EndElement { name, .. } if &name.local_name == "Group" => break,
239 _ => {}
240 }
241 }
242 Ok(group)
243}
244
245fn parse_root<R: Read, S: StreamCipher + ?Sized>(
246 xml_event_reader: &mut EventReader<R>,
247 stream_cipher: &mut S,
248) -> Result<Vec<Group>> {
249 let mut groups = Vec::new();
250 loop {
251 match xml_event_reader.next()? {
252 XmlEvent::StartElement { name, .. } if &name.local_name == "Group" => {
253 groups.push(parse_group(xml_event_reader, stream_cipher)?);
254 }
255 XmlEvent::EndElement { name, .. } if &name.local_name == "Root" => break,
256 _ => {}
257 }
258 }
259 Ok(groups)
260}
261
262fn parse_custom_data<R: Read, S: StreamCipher + ?Sized>(
263 xml_event_reader: &mut EventReader<R>,
264 stream_cipher: &mut S,
265) -> Result<Vec<Field>> {
266 let mut fields = Vec::new();
267 loop {
268 match xml_event_reader.next()? {
269 XmlEvent::StartElement { name, .. } if &name.local_name == "Item" => {
270 fields.push(parse_field(xml_event_reader, "Item", stream_cipher)?);
271 }
272 XmlEvent::EndElement { name, .. } if &name.local_name == "CustomData" => break,
273 _ => {}
274 }
275 }
276 Ok(fields)
277}
278
279fn parse_memory_protection<R: Read>(
280 xml_event_reader: &mut EventReader<R>,
281) -> Result<MemoryProtection> {
282 let mut protection = MemoryProtection::default();
283 loop {
284 match xml_event_reader.next()? {
285 XmlEvent::StartElement { name, .. } => match name.local_name.as_ref() {
286 "ProtectTitle" => {
287 protection.protect_title = parse_bool(xml_event_reader)?;
288 }
289 "ProtectUserName" => {
290 protection.protect_user_name = parse_bool(xml_event_reader)?;
291 }
292 "ProtectPassword" => {
293 protection.protect_password = parse_bool(xml_event_reader)?;
294 }
295 "ProtectURL" => {
296 protection.protect_url = parse_bool(xml_event_reader)?;
297 }
298 "ProtectNotes" => {
299 protection.protect_notes = parse_bool(xml_event_reader)?;
300 }
301 _ => {}
302 },
303 XmlEvent::EndElement { name, .. } if &name.local_name == "MemoryProtection" => break,
304 _ => {}
305 }
306 }
307 Ok(protection)
308}
309
310fn parse_meta<R: Read, S: StreamCipher + ?Sized>(
311 xml_event_reader: &mut EventReader<R>,
312 stream_cipher: &mut S,
313) -> Result<Meta> {
314 let mut meta = Meta::default();
315 loop {
316 match xml_event_reader.next()? {
317 XmlEvent::StartElement { name, .. } => match name.local_name.as_ref() {
318 "Generator" => {
319 meta.generator = parse_string(xml_event_reader)?.unwrap_or_default();
320 }
321 "DatabaseName" => {
322 meta.database_name = parse_string(xml_event_reader)?.unwrap_or_default();
323 }
324 "DatabaseDescription" => {
325 meta.database_description = parse_string(xml_event_reader)?.unwrap_or_default();
326 }
327 "CustomData" => {
328 meta.custom_data = parse_custom_data(xml_event_reader, stream_cipher)?;
329 }
330 "MemoryProtection" => {
331 meta.memory_protection = parse_memory_protection(xml_event_reader)?;
332 }
333 _ => {}
334 },
335 XmlEvent::EndElement { name, .. } if &name.local_name == "Meta" => break,
336 _ => {}
337 }
338 }
339 Ok(meta)
340}
341
342fn parse_file<R: Read, S: StreamCipher + ?Sized>(
343 xml_event_reader: &mut EventReader<R>,
344 stream_cipher: &mut S,
345) -> Result<Database> {
346 let mut db = Database::default();
347 loop {
348 match xml_event_reader.next()? {
349 XmlEvent::StartElement { name, .. } if &name.local_name == "Root" => {
350 db.groups = parse_root(xml_event_reader, stream_cipher)?;
351 }
352 XmlEvent::StartElement { name, .. } if &name.local_name == "Meta" => {
353 db.meta = parse_meta(xml_event_reader, stream_cipher)?;
354 }
355 XmlEvent::EndElement { name, .. } if &name.local_name == "KeePassFile" => break,
356 _ => {}
357 }
358 }
359 Ok(db)
360}
361
362pub fn parse_xml<R: Read, S: StreamCipher + ?Sized>(
369 xml_data: R,
370 stream_cipher: &mut S,
371) -> Result<Database> {
372 let xml_config = xml::ParserConfig::new()
373 .trim_whitespace(true)
374 .cdata_to_characters(true);
375 let mut xml_event_reader = EventReader::new_with_config(xml_data, xml_config);
376 parse_file(&mut xml_event_reader, stream_cipher)
377}