use crate::{
crypt::ciphers::Cipher,
db::{AutoType, AutoTypeAssociation, Color, Entry, History, Times, Value, iconid::IconId},
xml_db::parse::{CustomData, FromXml, IgnoreSubfield, SimpleTag, SimpleXmlEvent, XmlParseError, bad_event},
};
use base64::{Engine as _, engine::general_purpose as base64_engine};
use secstr::SecStr;
use std::{convert::TryFrom, iter::Peekable};
use uuid::Uuid;
fn decode_xml(input: &str) -> String {
input
.replace("&", "&")
.replace("<", "<")
.replace(">", ">")
.replace(""", "\"")
.replace("'", "'")
}
impl FromXml for Entry {
type Parses = Self;
fn from_xml<I: Iterator<Item = SimpleXmlEvent>>(
iterator: &mut Peekable<I>,
inner_cipher: &mut dyn Cipher,
) -> Result<Self::Parses, XmlParseError> {
let open_tag = iterator.next().ok_or(XmlParseError::Eof)?;
if !matches!(open_tag, SimpleXmlEvent::Start(ref tag, _) if tag == "Entry") {
return Err(bad_event("Open entry tag", open_tag));
}
let mut out = Entry::default();
while let Some(event) = iterator.peek() {
match event {
SimpleXmlEvent::Start(name, _) => match &name[..] {
"UUID" => {
out.uuid = SimpleTag::<Uuid>::from_xml(iterator, inner_cipher)?.value;
}
"Tags" => {
if let Some(tags) = SimpleTag::<Option<String>>::from_xml(iterator, inner_cipher)?.value {
out.tags = tags.split([';', ',']).map(std::borrow::ToOwned::to_owned).collect();
}
}
"String" => {
let field = StringField::from_xml(iterator, inner_cipher)?;
if let Some(value) = field.value {
out.fields.insert(field.key, value);
}
}
"CustomData" => {
out.custom_data = CustomData::from_xml(iterator, inner_cipher)?;
}
"Binary" => {
let _field = BinaryField::from_xml(iterator, inner_cipher)?;
}
"AutoType" => {
out.autotype = Some(AutoType::from_xml(iterator, inner_cipher)?);
}
"Times" => {
out.times = Times::from_xml(iterator, inner_cipher)?;
}
"IconID" => {
out.icon_id = SimpleTag::<Option<usize>>::from_xml(iterator, inner_cipher)?
.value
.map(|id| IconId::try_from(id).unwrap_or(IconId::KEY));
}
"CustomIconUUID" => {
out.custom_icon_uuid = SimpleTag::<Option<Uuid>>::from_xml(iterator, inner_cipher)?.value;
}
"ForegroundColor" => {
out.foreground_color = SimpleTag::<Option<Color>>::from_xml(iterator, inner_cipher)?.value;
}
"BackgroundColor" => {
out.background_color = SimpleTag::<Option<Color>>::from_xml(iterator, inner_cipher)?.value;
}
"OverrideURL" => {
out.override_url = SimpleTag::<Option<String>>::from_xml(iterator, inner_cipher)?.value;
}
"QualityCheck" => {
out.quality_check = SimpleTag::<Option<bool>>::from_xml(iterator, inner_cipher)?.value;
}
"History" => {
out.history = Some(History::from_xml(iterator, inner_cipher)?);
}
_ => IgnoreSubfield::from_xml(iterator, inner_cipher)?,
},
SimpleXmlEvent::End(name) if name == "Entry" => break,
_ => return Err(bad_event("start tag or close entry", event.clone())),
}
}
let _close_tag = iterator.next().ok_or(XmlParseError::Eof)?;
Ok(out)
}
}
#[derive(Debug, Default)]
pub(crate) struct StringField {
key: String,
value: Option<Value>,
}
impl FromXml for StringField {
type Parses = StringField;
fn from_xml<I: Iterator<Item = SimpleXmlEvent>>(
iterator: &mut Peekable<I>,
inner_cipher: &mut dyn Cipher,
) -> Result<Self::Parses, XmlParseError> {
let open_tag = iterator.next().ok_or(XmlParseError::Eof)?;
if !matches!(open_tag, SimpleXmlEvent::Start(ref tag, _) if tag == "String") {
return Err(bad_event("Open string tag", open_tag));
}
let mut out = StringField::default();
while let Some(event) = iterator.peek() {
match event {
SimpleXmlEvent::Start(name, _) => match &name[..] {
"Key" => {
out.key = decode_xml(&SimpleTag::<String>::from_xml(iterator, inner_cipher)?.value);
}
"Value" => {
let value = Value::from_xml(iterator, inner_cipher)?;
if !value.is_empty() {
out.value = Some(value);
}
}
_ => IgnoreSubfield::from_xml(iterator, inner_cipher)?,
},
SimpleXmlEvent::End(name) if name == "String" => break,
_ => return Err(bad_event("start tag or close String", event.clone())),
}
}
let _close_tag = iterator.next().ok_or(XmlParseError::Eof)?;
Ok(out)
}
}
#[derive(Debug)]
#[allow(dead_code)]
pub(crate) struct BinaryField {
pub key: String,
pub identifier: String,
}
impl FromXml for BinaryField {
type Parses = Self;
fn from_xml<I: Iterator<Item = SimpleXmlEvent>>(
iterator: &mut Peekable<I>,
inner_cipher: &mut dyn Cipher,
) -> Result<Self::Parses, XmlParseError> {
let open_tag = iterator.next().ok_or(XmlParseError::Eof)?;
if !matches!(open_tag, SimpleXmlEvent::Start(ref tag, _) if tag == "Binary") {
return Err(bad_event("Open Binary tag", open_tag));
}
let key = SimpleTag::<String>::from_xml(iterator, inner_cipher)?.value;
if key.is_empty() {
return Err(bad_event("Key tag with content", open_tag));
}
let value_event = iterator.next().ok_or(XmlParseError::Eof)?;
let identifier = match value_event {
SimpleXmlEvent::Start(ref name, ref attributes) if name == "Value" => attributes.get("Ref").cloned(),
_ => None,
}
.ok_or(bad_event("Open Value tag with \"Ref\" attribute", value_event))?;
let close_value_tag = iterator.next().ok_or(XmlParseError::Eof)?;
if !matches!(close_value_tag, SimpleXmlEvent::End(ref tag) if tag == "Value") {
return Err(bad_event("Close Value tag", close_value_tag));
}
let _close_tag = iterator.next().ok_or(XmlParseError::Eof)?;
Ok(BinaryField { key, identifier })
}
}
impl FromXml for Value {
type Parses = Value;
fn from_xml<I: Iterator<Item = SimpleXmlEvent>>(
iterator: &mut Peekable<I>,
inner_cipher: &mut dyn Cipher,
) -> Result<Self::Parses, XmlParseError> {
let open_tag = iterator.next().ok_or(XmlParseError::Eof)?;
if let SimpleXmlEvent::Start(ref tag, ref attributes) = open_tag {
if tag == "Value" {
let protected: bool = attributes
.get("Protected")
.map_or(Ok(false), |v| v.to_lowercase().parse::<bool>())?;
let content = Option::<String>::from_xml(iterator, inner_cipher)?.unwrap_or(String::new());
let decoded_content = decode_xml(&content);
let value = if protected {
let buf = base64_engine::STANDARD.decode(&decoded_content)?;
let buf_decrypted = inner_cipher.decrypt(&buf)?;
let value = String::from_utf8_lossy(&buf_decrypted).to_string();
Value::Protected(SecStr::from(value))
} else {
Value::Unprotected(decoded_content)
};
let close_value_tag = iterator.next().ok_or(XmlParseError::Eof)?;
if !matches!(close_value_tag, SimpleXmlEvent::End(ref tag) if tag == "Value") {
return Err(bad_event("Close Value tag", close_value_tag));
}
return Ok(value);
}
}
Err(bad_event("Open value tag", open_tag))
}
}
impl FromXml for AutoType {
type Parses = Self;
fn from_xml<I: Iterator<Item = SimpleXmlEvent>>(
iterator: &mut Peekable<I>,
inner_cipher: &mut dyn Cipher,
) -> Result<Self::Parses, XmlParseError> {
let open_tag = iterator.next().ok_or(XmlParseError::Eof)?;
if !matches!(open_tag, SimpleXmlEvent::Start(ref tag, _) if tag == "AutoType") {
return Err(bad_event("Open AutoType tag", open_tag));
}
let mut out = AutoType::default();
while let Some(event) = iterator.peek() {
match event {
SimpleXmlEvent::Start(name, _) => match &name[..] {
"Enabled" => {
out.enabled = SimpleTag::<bool>::from_xml(iterator, inner_cipher)?.value;
}
"DefaultSequence" => {
let sequence = SimpleTag::<Option<String>>::from_xml(iterator, inner_cipher)?.value;
out.sequence = sequence.map(|s| decode_xml(&s));
}
"DataTransferObfuscation" => {
let _value = SimpleTag::<Option<usize>>::from_xml(iterator, inner_cipher)?.value;
}
"Association" => {
let ata = AutoTypeAssociation::from_xml(iterator, inner_cipher)?;
out.associations.push(ata);
}
_ => IgnoreSubfield::from_xml(iterator, inner_cipher)?,
},
SimpleXmlEvent::End(name) if name == "AutoType" => break,
_ => return Err(bad_event("start tag or close AutoType", event.clone())),
}
}
let _close_tag = iterator.next().ok_or(XmlParseError::Eof)?;
Ok(out)
}
}
impl FromXml for AutoTypeAssociation {
type Parses = Self;
fn from_xml<I: Iterator<Item = SimpleXmlEvent>>(
iterator: &mut Peekable<I>,
inner_cipher: &mut dyn Cipher,
) -> Result<Self::Parses, XmlParseError> {
let open_tag = iterator.next().ok_or(XmlParseError::Eof)?;
if !matches!(open_tag, SimpleXmlEvent::Start(ref tag, _) if tag == "Association") {
return Err(bad_event("Open Association tag", open_tag));
}
let mut out = AutoTypeAssociation::default();
while let Some(event) = iterator.peek() {
match event {
SimpleXmlEvent::Start(name, _) => match &name[..] {
"Window" => {
let window = SimpleTag::<Option<String>>::from_xml(iterator, inner_cipher)?.value;
out.window = window.map(|w| decode_xml(&w));
}
"KeystrokeSequence" => {
let sequence = SimpleTag::<Option<String>>::from_xml(iterator, inner_cipher)?.value;
out.sequence = sequence.map(|s| decode_xml(&s));
}
_ => IgnoreSubfield::from_xml(iterator, inner_cipher)?,
},
SimpleXmlEvent::End(name) if name == "Association" => break,
_ => return Err(bad_event("start tag or close Association", event.clone())),
}
}
let _close_tag = iterator.next().ok_or(XmlParseError::Eof)?;
Ok(out)
}
}
impl FromXml for History {
type Parses = Self;
fn from_xml<I: Iterator<Item = SimpleXmlEvent>>(
iterator: &mut Peekable<I>,
inner_cipher: &mut dyn Cipher,
) -> Result<Self::Parses, XmlParseError> {
let open_tag = iterator.next().ok_or(XmlParseError::Eof)?;
if !matches!(open_tag, SimpleXmlEvent::Start(ref tag, _) if tag == "History") {
return Err(bad_event("Open History tag", open_tag));
}
let mut entries = Vec::new();
while let Some(event) = iterator.peek() {
match event {
SimpleXmlEvent::Start(name, _) => match &name[..] {
"Entry" => {
let entry = Entry::from_xml(iterator, inner_cipher)?;
entries.push(entry);
}
_ => IgnoreSubfield::from_xml(iterator, inner_cipher)?,
},
SimpleXmlEvent::End(name) if name == "History" => break,
_ => return Err(bad_event("start tag or close History", event.clone())),
}
}
let _close_tag = iterator.next().ok_or(XmlParseError::Eof)?;
Ok(Self { entries })
}
}