use crate::compression::gzip;
use crate::crypto::salsa20::{self, Salsa20};
use crate::types::BinaryId;
use crate::types::BinaryKey;
use crate::types::BinaryValue;
use crate::types::Color;
use crate::types::CustomIconUuid;
use crate::types::Error;
use crate::types::Icon;
use crate::types::Obfuscation;
use crate::types::Result;
use crate::types::StringKey;
use crate::types::StringValue;
use base64::{engine::general_purpose, Engine as _};
use chrono::{DateTime, Utc};
use secstr::SecStr;
use std::io::{Read, Write};
use uuid::Uuid;
use xml::attribute::OwnedAttribute;
use xml::common::Position;
use xml::reader::{self, EventReader};
use xml::writer::{self, EventWriter};
pub fn read_binary<R: Read>(reader: &mut EventReader<R>) -> Result<Vec<u8>> {
match read_binary_opt(reader)? {
Some(bytes) => Ok(bytes),
None => Ok(Vec::new()),
}
}
pub fn read_binary_key_opt<R: Read>(reader: &mut EventReader<R>) -> Result<Option<BinaryKey>> {
match read_string_opt(reader)? {
Some(string) => Ok(Some(BinaryKey(string))),
None => Ok(None),
}
}
pub fn read_binary_opt<R: Read>(reader: &mut EventReader<R>) -> Result<Option<Vec<u8>>> {
match read_string_opt(reader)? {
Some(string) => match general_purpose::STANDARD.decode(&string) {
Ok(bin) => Ok(Some(bin)),
Err(err) => read_err(reader, format!("Base64 {}", err)),
},
None => Ok(None),
}
}
pub fn read_binary_value_opt<R: Read>(
reader: &mut EventReader<R>,
cipher: &mut Salsa20,
attrs: &Vec<OwnedAttribute>,
) -> Result<Option<BinaryValue>> {
let ref_value = search_attr_value(attrs, "ref");
let protected = get_protected_attr_value(reader, attrs)?;
match ref_value {
Some(string) => Ok(Some(BinaryValue::Ref(BinaryId(string)))),
None => match read_binary_opt(reader)? {
Some(bytes) => {
if protected {
let pbytes = salsa20::decrypt(cipher, &bytes);
let secstr = SecStr::new(pbytes);
Ok(Some(BinaryValue::Protected(secstr)))
} else {
Ok(Some(BinaryValue::Plain(bytes)))
}
}
None => {
if protected {
Ok(Some(BinaryValue::Protected(SecStr::new(Vec::new()))))
} else {
Ok(Some(BinaryValue::Plain(Vec::new())))
}
}
},
}
}
pub fn read_bool<R: Read>(reader: &mut EventReader<R>) -> Result<bool> {
match read_bool_opt(reader)? {
Some(b) => Ok(b),
None => read_err(reader, "No Bool value found"),
}
}
pub fn read_bool_opt<R: Read>(reader: &mut EventReader<R>) -> Result<Option<bool>> {
match read_string_opt(reader)? {
Some(string) => match string.to_lowercase().as_str() {
"false" => Ok(Some(false)),
"null" => Ok(None),
"true" => Ok(Some(true)),
val => read_err(reader, format!("Bool invalid value: {}", val)),
},
None => Ok(None),
}
}
pub fn read_color_opt<R: Read>(reader: &mut EventReader<R>) -> Result<Option<Color>> {
match read_string_opt(reader)? {
Some(string) => match Color::from_hex_string(&string) {
Ok(color) => Ok(Some(color)),
Err(err) => read_err(reader, format!("Color {}", err)),
},
None => Ok(None),
}
}
pub fn read_custom_icon_uuid_opt<R: Read>(
reader: &mut EventReader<R>,
) -> Result<Option<CustomIconUuid>> {
match read_uuid_opt(reader)? {
Some(uuid) => Ok(Some(CustomIconUuid(uuid))),
None => Ok(None),
}
}
pub fn read_datetime<R: Read>(reader: &mut EventReader<R>) -> Result<DateTime<Utc>> {
match read_string_opt(reader)? {
Some(string) => match string.parse::<DateTime<Utc>>() {
Ok(datetime) => Ok(datetime),
Err(err) => read_err(reader, format!("DateTime {}", err)),
},
None => read_err(reader, "No DateTime value found"),
}
}
pub fn read_err<S, R, X>(reader: &mut EventReader<R>, msg: S) -> Result<X>
where
R: Read,
S: Into<String>,
{
let msg: String = msg.into();
let pos = reader.position();
let err = format!("{} {}", pos, msg);
Err(Error::XmlError(err))
}
pub fn read_gzip<R: Read>(reader: &mut EventReader<R>) -> Result<Vec<u8>> {
match read_binary_opt(reader)? {
Some(bytes) => {
let decompressed = gzip::decode(&bytes)?;
Ok(decompressed)
}
None => Ok(Vec::new()),
}
}
pub fn read_i32<R: Read>(reader: &mut EventReader<R>) -> Result<i32> {
match read_i32_opt(reader)? {
Some(num) => Ok(num),
None => read_err(reader, "No Number value found"),
}
}
pub fn read_i32_opt<R: Read>(reader: &mut EventReader<R>) -> Result<Option<i32>> {
match read_string_opt(reader)? {
Some(string) => match string.parse::<i32>() {
Ok(num) => Ok(Some(num)),
Err(err) => read_err(reader, format!("Number {}", err)),
},
None => Ok(None),
}
}
pub fn read_icon<R: Read>(reader: &mut EventReader<R>) -> Result<Icon> {
match read_i32_opt(reader)? {
Some(num) => match Icon::from_i32(num) {
Ok(icon) => Ok(icon),
Err(err) => read_err(reader, format!("{}", err)),
},
None => read_err(reader, "No Icon value found"),
}
}
pub fn read_obfuscation<R: Read>(reader: &mut EventReader<R>) -> Result<Obfuscation> {
match read_i32_opt(reader)? {
Some(num) => match Obfuscation::from_i32(num) {
Ok(val) => Ok(val),
Err(err) => read_err(reader, format!("{}", err)),
},
None => read_err(reader, "No Obfuscation value found"),
}
}
pub fn read_string<R: Read>(reader: &mut EventReader<R>) -> Result<String> {
match read_string_opt(reader)? {
Some(string) => Ok(string),
None => Ok(String::new()),
}
}
pub fn read_string_key_opt<R: Read>(reader: &mut EventReader<R>) -> Result<Option<StringKey>> {
match read_string_opt(reader)? {
Some(string) => Ok(Some(StringKey::from_string(&string))),
None => Ok(None),
}
}
pub fn read_string_opt<R: Read>(reader: &mut EventReader<R>) -> Result<Option<String>> {
let event = reader.next()?;
match event {
reader::XmlEvent::Characters(val) => Ok(Some(val)),
reader::XmlEvent::EndElement { .. } => Ok(None),
_ => {
let _: Result<Option<String>> =
read_err(reader, "No characters found").map_err(|err| {
eprintln!("{}", err);
err
});
Ok(None)
}
}
}
pub fn read_string_value_opt<R: Read>(
reader: &mut EventReader<R>,
cipher: &mut Salsa20,
attrs: &Vec<OwnedAttribute>,
) -> Result<Option<StringValue>> {
let pmem = get_protect_in_memory_attr_value(reader, attrs)?;
let pxml = get_protected_attr_value(reader, attrs)?;
let protected = pmem || pxml;
if pxml {
match read_binary_opt(reader)? {
Some(bytes) => {
let pbytes = salsa20::decrypt(cipher, &bytes);
match String::from_utf8(pbytes) {
Ok(string) => Ok(Some(StringValue::new(string, protected))),
Err(err) => read_err(reader, format!("UTF8 {}", err)),
}
}
None => Ok(Some(StringValue::new("", protected))),
}
} else {
match read_string_opt(reader)? {
Some(string) => Ok(Some(StringValue::new(string, protected))),
None => Ok(Some(StringValue::new("", protected))),
}
}
}
pub fn read_uuid<R: Read>(reader: &mut EventReader<R>) -> Result<Uuid> {
match read_uuid_opt(reader)? {
Some(uuid) => Ok(uuid),
None => read_err(reader, "No UUID value found"),
}
}
pub fn read_uuid_opt<R: Read>(reader: &mut EventReader<R>) -> Result<Option<Uuid>> {
match read_binary_opt(reader)? {
Some(bytes) => match Uuid::from_slice(bytes.as_slice()) {
Ok(uuid) => Ok(Some(uuid)),
Err(err) => read_err(reader, format!("UUID {}", err)),
},
None => Ok(None),
}
}
pub fn search_attr_value(attrs: &Vec<OwnedAttribute>, name: &str) -> Option<String> {
for attr in attrs {
if attr.name.local_name.to_lowercase() == name.to_lowercase() {
return Some(attr.value.clone());
}
}
None
}
pub fn write_binary<W: Write>(writer: &mut EventWriter<W>, data: &[u8]) -> Result<()> {
write_string(writer, &general_purpose::STANDARD.encode(&data))
}
pub fn write_binary_tag<W: Write>(
writer: &mut EventWriter<W>,
tag: &str,
value: &[u8],
) -> Result<()> {
write_string_tag(writer, tag, &general_purpose::STANDARD.encode(&value))
}
pub fn write_bool_opt_tag<W: Write>(
writer: &mut EventWriter<W>,
tag: &str,
value: &Option<bool>,
) -> Result<()> {
match *value {
Some(false) => write_string_tag(writer, tag, &String::from("false")),
Some(true) => write_string_tag(writer, tag, &String::from("true")),
None => write_string_tag(writer, tag, &String::from("null")),
}
}
pub fn write_bool_tag<W: Write>(writer: &mut EventWriter<W>, tag: &str, value: bool) -> Result<()> {
write_bool_opt_tag(writer, tag, &Some(value))
}
pub fn write_color_tag<W: Write>(
writer: &mut EventWriter<W>,
tag: &str,
value: &Option<Color>,
) -> Result<()> {
match *value {
Some(ref c) => write_string_tag(writer, tag, &c.to_hex_string()),
None => write_null_tag(writer, tag),
}
}
pub fn write_custom_icon_uuid_tag<W: Write>(
writer: &mut EventWriter<W>,
tag: &str,
value: &Option<CustomIconUuid>,
) -> Result<()> {
match *value {
Some(ref u) => write_uuid_tag(writer, tag, &u.0),
None => write_null_tag(writer, tag),
}
}
pub fn write_datetime_tag<W: Write>(
writer: &mut EventWriter<W>,
tag: &str,
value: &DateTime<Utc>,
) -> Result<()> {
write_string_tag(writer, tag, &format!("{:?}", value))
}
pub fn write_end_tag<W: Write>(writer: &mut EventWriter<W>) -> Result<()> {
writer.write(writer::XmlEvent::end_element())?;
Ok(())
}
pub fn write_gzip<W: Write>(writer: &mut EventWriter<W>, data: &[u8]) -> Result<()> {
let compressed = gzip::encode(data)?;
write_binary(writer, &compressed)
}
pub fn write_i32_tag<W: Write>(writer: &mut EventWriter<W>, tag: &str, value: i32) -> Result<()> {
write_string_tag(writer, tag, &format!("{}", value))
}
pub fn write_null_tag<W: Write>(writer: &mut EventWriter<W>, tag: &str) -> Result<()> {
write_start_tag(writer, tag)?;
write_end_tag(writer)?;
Ok(())
}
pub fn write_start_tag<W: Write>(writer: &mut EventWriter<W>, tag: &str) -> Result<()> {
writer.write(writer::XmlEvent::start_element(tag))?;
Ok(())
}
pub fn write_string<W: Write>(writer: &mut EventWriter<W>, value: &String) -> Result<()> {
writer.write(value.as_str())?;
Ok(())
}
pub fn write_string_tag<W: Write>(
writer: &mut EventWriter<W>,
tag: &str,
value: &String,
) -> Result<()> {
write_start_tag(writer, tag)?;
write_string(writer, value)?;
write_end_tag(writer)?;
Ok(())
}
pub fn write_uuid_tag<W: Write>(writer: &mut EventWriter<W>, tag: &str, uuid: &Uuid) -> Result<()> {
write_binary_tag(writer, tag, uuid.as_bytes())
}
fn get_protect_in_memory_attr_value<R: Read>(
reader: &mut EventReader<R>,
attrs: &Vec<OwnedAttribute>,
) -> Result<bool> {
match search_attr_value(attrs, "protectinmemory") {
Some(string) => match string.to_lowercase().parse::<bool>() {
Ok(val) => Ok(val),
Err(_) => read_err(reader, "Attribute ProtectInMemory invalid value"),
},
None => Ok(false),
}
}
fn get_protected_attr_value<R: Read>(
reader: &mut EventReader<R>,
attrs: &Vec<OwnedAttribute>,
) -> Result<bool> {
match search_attr_value(attrs, "protected") {
Some(string) => match string.to_lowercase().parse::<bool>() {
Ok(val) => Ok(val),
Err(_) => read_err(reader, "Attribute Protected invalid value"),
},
None => Ok(false),
}
}