keepass-ng 0.10.5

KeePass .kdbx database file parser with ehancements
Documentation
use base64::{Engine as _, engine::general_purpose as base64_engine};
use xml::writer::{EventWriter, XmlEvent as WriterEvent};

use crate::{
    compression::{Compression, GZipCompression},
    crypt::ciphers::Cipher,
    db::meta::{BinaryAttachment, BinaryAttachments, CustomIcons, Icon, MemoryProtection, Meta},
    xml_db::dump::{DumpXml, SimpleTag},
};

impl DumpXml for Meta {
    fn dump_xml<E: std::io::Write>(&self, writer: &mut EventWriter<E>, inner_cipher: &mut dyn Cipher) -> Result<(), xml::writer::Error> {
        writer.write(WriterEvent::start_element("Meta"))?;

        if let Some(ref value) = self.generator {
            SimpleTag("Generator", value).dump_xml(writer, inner_cipher)?;
        }

        if let Some(ref value) = self.database_name {
            SimpleTag("DatabaseName", value).dump_xml(writer, inner_cipher)?;
        }

        if let Some(ref value) = self.database_name_changed {
            SimpleTag("DatabaseNameChanged", value).dump_xml(writer, inner_cipher)?;
        }

        if let Some(ref value) = self.database_description {
            SimpleTag("DatabaseDescription", value).dump_xml(writer, inner_cipher)?;
        }

        if let Some(ref value) = self.database_description_changed {
            SimpleTag("DatabaseDescriptionChanged", value).dump_xml(writer, inner_cipher)?;
        }

        if let Some(ref value) = self.default_username {
            SimpleTag("DefaultUserName", value).dump_xml(writer, inner_cipher)?;
        }

        if let Some(ref value) = self.default_username_changed {
            SimpleTag("DefaultUserNameChanged", value).dump_xml(writer, inner_cipher)?;
        }

        if let Some(value) = self.maintenance_history_days {
            SimpleTag("MaintenanceHistoryDays", value).dump_xml(writer, inner_cipher)?;
        }

        if let Some(ref value) = self.color {
            SimpleTag("Color", value).dump_xml(writer, inner_cipher)?;
        }

        if let Some(ref value) = self.master_key_changed {
            SimpleTag("MasterKeyChanged", value).dump_xml(writer, inner_cipher)?;
        }

        if let Some(value) = self.master_key_change_rec {
            SimpleTag("MasterKeyChangeRec", value).dump_xml(writer, inner_cipher)?;
        }

        if let Some(value) = self.master_key_change_force {
            SimpleTag("MasterKeyChangeForce", value).dump_xml(writer, inner_cipher)?;
        }

        if let Some(ref value) = self.memory_protection {
            value.dump_xml(writer, inner_cipher)?;
        }

        self.custom_icons.dump_xml(writer, inner_cipher)?;

        if let Some(value) = self.recyclebin_enabled {
            SimpleTag("RecycleBinEnabled", value).dump_xml(writer, inner_cipher)?;
        }

        if let Some(ref value) = self.recyclebin_uuid {
            SimpleTag("RecycleBinUUID", value).dump_xml(writer, inner_cipher)?;
        }

        if let Some(ref value) = self.recyclebin_changed {
            SimpleTag("RecycleBinChanged", value).dump_xml(writer, inner_cipher)?;
        }

        if let Some(ref value) = self.entry_templates_group {
            SimpleTag("EntryTemplatesGroup", value).dump_xml(writer, inner_cipher)?;
        }

        if let Some(ref value) = self.entry_templates_group_changed {
            SimpleTag("EntryTemplatesGroupChanged", value).dump_xml(writer, inner_cipher)?;
        }

        if let Some(ref value) = self.last_selected_group {
            SimpleTag("LastSelectedGroup", value).dump_xml(writer, inner_cipher)?;
        }

        if let Some(ref value) = self.last_top_visible_group {
            SimpleTag("LastTopVisibleGroup", value).dump_xml(writer, inner_cipher)?;
        }

        if let Some(value) = self.history_max_items {
            SimpleTag("HistoryMaxItems", value).dump_xml(writer, inner_cipher)?;
        }

        if let Some(value) = self.history_max_size {
            SimpleTag("HistoryMaxSize", value).dump_xml(writer, inner_cipher)?;
        }

        if let Some(ref value) = self.settings_changed {
            SimpleTag("SettingsChanged", value).dump_xml(writer, inner_cipher)?;
        }

        self.binaries.dump_xml(writer, inner_cipher)?;

        self.custom_data.dump_xml(writer, inner_cipher)?;

        writer.write(WriterEvent::end_element())?;

        Ok(())
    }
}

impl DumpXml for MemoryProtection {
    fn dump_xml<E: std::io::Write>(&self, writer: &mut EventWriter<E>, inner_cipher: &mut dyn Cipher) -> Result<(), xml::writer::Error> {
        writer.write(WriterEvent::start_element("MemoryProtection"))?;

        SimpleTag("ProtectTitle", self.protect_title).dump_xml(writer, inner_cipher)?;
        SimpleTag("ProtectUserName", self.protect_username).dump_xml(writer, inner_cipher)?;
        SimpleTag("ProtectPassword", self.protect_password).dump_xml(writer, inner_cipher)?;
        SimpleTag("ProtectURL", self.protect_url).dump_xml(writer, inner_cipher)?;
        SimpleTag("ProtectNotes", self.protect_notes).dump_xml(writer, inner_cipher)?;

        writer.write(WriterEvent::end_element())?;
        Ok(())
    }
}

impl DumpXml for BinaryAttachments {
    fn dump_xml<E: std::io::Write>(&self, writer: &mut EventWriter<E>, inner_cipher: &mut dyn Cipher) -> Result<(), xml::writer::Error> {
        writer.write(WriterEvent::start_element("Binaries"))?;

        for bin in &self.binaries {
            bin.dump_xml(writer, inner_cipher)?;
        }

        writer.write(WriterEvent::end_element())?;
        Ok(())
    }
}

impl DumpXml for BinaryAttachment {
    fn dump_xml<E: std::io::Write>(&self, writer: &mut EventWriter<E>, _inner_cipher: &mut dyn Cipher) -> Result<(), xml::writer::Error> {
        let start_tag = WriterEvent::start_element("Binary");

        let start_tag = if let Some(ref id) = self.identifier {
            start_tag.attr("ID", id)
        } else {
            start_tag
        };

        let start_tag = if self.compressed {
            start_tag.attr("Compressed", "True")
        } else {
            start_tag
        };

        writer.write(start_tag)?;

        let data = if self.compressed {
            GZipCompression.compress(&self.content)?
        } else {
            self.content.clone()
        };

        let buf = base64_engine::STANDARD.encode(data);

        writer.write(WriterEvent::characters(&buf))?;

        writer.write(WriterEvent::end_element())?;
        Ok(())
    }
}

impl DumpXml for CustomIcons {
    fn dump_xml<E: std::io::Write>(&self, writer: &mut EventWriter<E>, inner_cipher: &mut dyn Cipher) -> Result<(), xml::writer::Error> {
        writer.write(WriterEvent::start_element("CustomIcons"))?;

        for icon in &self.icons {
            icon.dump_xml(writer, inner_cipher)?;
        }

        writer.write(WriterEvent::end_element())?;
        Ok(())
    }
}

impl DumpXml for Icon {
    fn dump_xml<E: std::io::Write>(&self, writer: &mut EventWriter<E>, inner_cipher: &mut dyn Cipher) -> Result<(), xml::writer::Error> {
        writer.write(WriterEvent::start_element("Icon"))?;

        SimpleTag("UUID", &self.uuid).dump_xml(writer, inner_cipher)?;

        let buf = base64_engine::STANDARD.encode(&self.data);
        SimpleTag("Data", &buf).dump_xml(writer, inner_cipher)?;

        writer.write(WriterEvent::end_element())?;
        Ok(())
    }
}