Support KeePass format 3.00 (used by KeePass2 >= 2.15).

Closes #6

Attachments are now stored in a pool under Metadata instead of in entries.
The protected flag of attachments isn't supported anymore.
New metadata attributes: color, historyMaxItems and historyMaxSize.
Dropped metadata attribute: autoEnableVisualHiding.
This commit is contained in:
Felix Geyer 2012-04-21 16:45:46 +02:00
parent e8ac70120b
commit 8acd6f74d8
23 changed files with 376 additions and 81 deletions

View file

@ -32,12 +32,7 @@ QByteArray EntryAttachments::value(const QString& key) const
return m_attachments.value(key);
}
bool EntryAttachments::isProtected(const QString& key) const
{
return m_protectedAttachments.contains(key);
}
void EntryAttachments::set(const QString& key, const QByteArray& value, bool protect)
void EntryAttachments::set(const QString& key, const QByteArray& value)
{
bool emitModified = false;
bool addAttachment = !m_attachments.contains(key);
@ -51,17 +46,6 @@ void EntryAttachments::set(const QString& key, const QByteArray& value, bool pro
emitModified = true;
}
if (protect != m_protectedAttachments.contains(key)) {
if (protect) {
m_protectedAttachments.insert(key);
}
else {
m_protectedAttachments.remove(key);
}
emitModified = true;
}
if (addAttachment) {
Q_EMIT added(key);
}
@ -84,7 +68,6 @@ void EntryAttachments::remove(const QString& key)
Q_EMIT aboutToBeRemoved(key);
m_attachments.remove(key);
m_protectedAttachments.remove(key);
Q_EMIT removed(key);
Q_EMIT modified();
@ -96,13 +79,9 @@ void EntryAttachments::copyFrom(const EntryAttachments* other)
Q_EMIT aboutToBeReset();
m_attachments.clear();
m_protectedAttachments.clear();
Q_FOREACH (const QString& key, other->keys()) {
m_attachments.insert(key, other->value(key));
if (other->isProtected(key)) {
m_protectedAttachments.insert(key);
}
}
Q_EMIT reset();
@ -119,7 +98,6 @@ void EntryAttachments::clear()
Q_EMIT aboutToBeReset();
m_attachments.clear();
m_protectedAttachments.clear();
Q_EMIT reset();
Q_EMIT modified();
@ -127,5 +105,5 @@ void EntryAttachments::clear()
bool EntryAttachments::operator!=(const EntryAttachments& other) const
{
return m_attachments != other.m_attachments || m_protectedAttachments != other.m_protectedAttachments;
return m_attachments != other.m_attachments;
}

View file

@ -29,8 +29,7 @@ public:
explicit EntryAttachments(QObject* parent = 0);
QList<QString> keys() const;
QByteArray value(const QString& key) const;
bool isProtected(const QString& key) const;
void set(const QString& key, const QByteArray& value, bool protect = false);
void set(const QString& key, const QByteArray& value);
void remove(const QString& key);
void copyFrom(const EntryAttachments* other);
void clear();
@ -48,7 +47,6 @@ Q_SIGNALS:
private:
QMap<QString, QByteArray> m_attachments;
QSet<QString> m_protectedAttachments;
};
#endif // KEEPASSX_ENTRYATTACHMENTS_H

View file

@ -305,6 +305,25 @@ const QList<Entry*>& Group::entries() const
return m_entries;
}
QList<Entry*> Group::entriesRecursive(bool includeHistoryItems)
{
QList<Entry*> entryList;
entryList.append(m_entries);
if (includeHistoryItems) {
Q_FOREACH (Entry* entry, m_entries) {
entryList.append(entry->historyItems());
}
}
Q_FOREACH (Group* group, m_children) {
entryList.append(group->entriesRecursive(includeHistoryItems));
}
return entryList;
}
void Group::addEntry(Entry *entry)
{
Q_ASSERT(entry);

View file

@ -74,6 +74,7 @@ public:
const QList<Group*>& children() const;
QList<Entry*> entries();
const QList<Entry*>& entries() const;
QList<Entry*> entriesRecursive(bool includeHistoryItems = false);
Q_SIGNALS:
void dataChanged(Group* group);

View file

@ -34,6 +34,8 @@ Metadata::Metadata(Database* parent)
m_recycleBinEnabled = true;
m_masterKeyChangeRec = -1;
m_masterKeyChangeForce = -1;
m_historyMaxItems = 10;
m_historyMaxSize = 6291456;
QDateTime now = QDateTime::currentDateTimeUtc();
m_nameChanged = now;
@ -48,7 +50,7 @@ Metadata::Metadata(Database* parent)
m_protectPassword = true;
m_protectUrl = false;
m_protectNotes = false;
m_autoEnableVisualHiding = false;
// m_autoEnableVisualHiding = false;
m_updateDatetime = true;
}
@ -122,6 +124,11 @@ int Metadata::maintenanceHistoryDays() const
return m_maintenanceHistoryDays;
}
QColor Metadata::color() const
{
return m_color;
}
bool Metadata::protectTitle() const
{
return m_protectTitle;
@ -147,10 +154,10 @@ bool Metadata::protectNotes() const
return m_protectNotes;
}
bool Metadata::autoEnableVisualHiding() const
/*bool Metadata::autoEnableVisualHiding() const
{
return m_autoEnableVisualHiding;
}
}*/
QImage Metadata::customIcon(const Uuid& uuid) const
{
@ -217,6 +224,16 @@ int Metadata::masterKeyChangeForce() const
return m_masterKeyChangeForce;
}
int Metadata::historyMaxItems() const
{
return m_historyMaxItems;
}
int Metadata::historyMaxSize() const
{
return m_historyMaxSize;
}
QHash<QString, QString> Metadata::customFields() const
{
return m_customFields;
@ -264,6 +281,11 @@ void Metadata::setMaintenanceHistoryDays(int value)
set(m_maintenanceHistoryDays, value);
}
void Metadata::setColor(const QColor& value)
{
set(m_color, value);
}
void Metadata::setProtectTitle(bool value)
{
set(m_protectTitle, value);
@ -289,10 +311,10 @@ void Metadata::setProtectNotes(bool value)
set(m_protectNotes, value);
}
void Metadata::setAutoEnableVisualHiding(bool value)
/*void Metadata::setAutoEnableVisualHiding(bool value)
{
set(m_autoEnableVisualHiding, value);
}
}*/
void Metadata::addCustomIcon(const Uuid& uuid, const QImage& icon)
{
@ -362,6 +384,16 @@ void Metadata::setMasterKeyChangeForce(int value)
set(m_masterKeyChangeForce, value);
}
void Metadata::setHistoryMaxItems(int value)
{
set(m_historyMaxItems, value);
}
void Metadata::setHistoryMaxSize(int value)
{
set(m_historyMaxSize, value);
}
void Metadata::addCustomField(const QString& key, const QString& value)
{
Q_ASSERT(!m_customFields.contains(key));

View file

@ -20,6 +20,7 @@
#include <QtCore/QDateTime>
#include <QtCore/QHash>
#include <QtGui/QColor>
#include <QtGui/QImage>
#include "core/Uuid.h"
@ -42,12 +43,13 @@ public:
QString defaultUserName() const;
QDateTime defaultUserNameChanged() const;
int maintenanceHistoryDays() const;
QColor color() const;
bool protectTitle() const;
bool protectUsername() const;
bool protectPassword() const;
bool protectUrl() const;
bool protectNotes() const;
bool autoEnableVisualHiding() const;
// bool autoEnableVisualHiding() const;
QImage customIcon(const Uuid& uuid) const;
QHash<Uuid, QImage> customIcons() const;
bool recycleBinEnabled() const;
@ -61,6 +63,8 @@ public:
QDateTime masterKeyChanged() const;
int masterKeyChangeRec() const;
int masterKeyChangeForce() const;
int historyMaxItems() const;
int historyMaxSize() const;
QHash<QString, QString> customFields() const;
void setGenerator(const QString& value);
@ -71,12 +75,13 @@ public:
void setDefaultUserName(const QString& value);
void setDefaultUserNameChanged(const QDateTime& value);
void setMaintenanceHistoryDays(int value);
void setColor(const QColor& value);
void setProtectTitle(bool value);
void setProtectUsername(bool value);
void setProtectPassword(bool value);
void setProtectUrl(bool value);
void setProtectNotes(bool value);
void setAutoEnableVisualHiding(bool value);
// void setAutoEnableVisualHiding(bool value);
void addCustomIcon(const Uuid& uuid, const QImage& icon);
void removeCustomIcon(const Uuid& uuid);
void setRecycleBinEnabled(bool value);
@ -89,6 +94,8 @@ public:
void setMasterKeyChanged(const QDateTime& value);
void setMasterKeyChangeRec(int value);
void setMasterKeyChangeForce(int value);
void setHistoryMaxItems(int value);
void setHistoryMaxSize(int value);
void addCustomField(const QString& key, const QString& value);
void removeCustomField(const QString& key);
void setUpdateDatetime(bool value);
@ -111,13 +118,14 @@ private:
QString m_defaultUserName;
QDateTime m_defaultUserNameChanged;
int m_maintenanceHistoryDays;
QColor m_color;
bool m_protectTitle;
bool m_protectUsername;
bool m_protectPassword;
bool m_protectUrl;
bool m_protectNotes;
bool m_autoEnableVisualHiding;
// bool m_autoEnableVisualHiding;
QHash<Uuid, QImage> m_customIcons;
@ -132,6 +140,8 @@ private:
QDateTime m_masterKeyChanged;
int m_masterKeyChangeRec;
int m_masterKeyChangeForce;
int m_historyMaxItems;
int m_historyMaxSize;
QHash<QString, QString> m_customFields;

View file

@ -26,7 +26,8 @@ namespace KeePass2
{
const quint32 SIGNATURE_1 = 0x9AA2D903;
const quint32 SIGNATURE_2 = 0xB54BFB67;
const quint32 FILE_VERSION = 0x00020000;
const quint32 FILE_VERSION = 0x00030000;
const quint32 FILE_VERSION_MIN = 0x00020000;
const quint32 FILE_VERSION_CRITICAL_MASK = 0xFFFF0000;
const QSysInfo::Endian BYTEORDER = QSysInfo::LittleEndian;

View file

@ -60,9 +60,8 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& ke
}
quint32 version = Endian::readUInt32(m_device, KeePass2::BYTEORDER, &ok) & KeePass2::FILE_VERSION_CRITICAL_MASK;
quint32 expectedVersion = KeePass2::FILE_VERSION & KeePass2::FILE_VERSION_CRITICAL_MASK;
// TODO do we support old Kdbx versions?
if (!ok || (version != expectedVersion)) {
quint32 maxVersion = KeePass2::FILE_VERSION & KeePass2::FILE_VERSION_CRITICAL_MASK;
if (!ok || (version < KeePass2::FILE_VERSION_MIN) || (version > maxVersion)) {
raiseError(tr("Unsupported KeePass database version."));
return 0;
}

View file

@ -17,6 +17,7 @@
#include "KeePass2XmlReader.h"
#include <QtCore/QBuffer>
#include <QtCore/QFile>
#include "core/Database.h"
@ -24,6 +25,7 @@
#include "core/Group.h"
#include "core/Metadata.h"
#include "format/KeePass2RandomStream.h"
#include "streams/QtIOCompressor"
KeePass2XmlReader::KeePass2XmlReader()
: m_randomStream(0)
@ -63,6 +65,27 @@ void KeePass2XmlReader::readDatabase(QIODevice* device, Database* db, KeePass2Ra
}
}
QSet<QString> poolKeys = m_binaryPool.keys().toSet();
QSet<QString> entryKeys = m_binaryMap.keys().toSet();
QSet<QString> unmappedKeys = entryKeys - poolKeys;
QSet<QString> unusedKeys = poolKeys - entryKeys;
if (!unmappedKeys.isEmpty()) {
raiseError(17);
}
if (!m_xml.error()) {
Q_FOREACH (const QString& key, unusedKeys) {
qWarning("KeePass2XmlReader::readDatabase: found unused key \"%s\"", qPrintable(key));
}
}
QHash<QString, QPair<Entry*, QString> >::const_iterator i;
for (i = m_binaryMap.constBegin(); i != m_binaryMap.constEnd(); ++i) {
QPair<Entry*, QString> target = i.value();
target.first->attachments()->set(target.second, m_binaryPool[i.key()]);
}
m_meta->setUpdateDatetime(true);
Q_FOREACH (Group* group, m_groups) {
group->setUpdateTimeinfo(true);
@ -151,6 +174,9 @@ void KeePass2XmlReader::parseMeta()
else if (m_xml.name() == "MaintenanceHistoryDays") {
m_meta->setMaintenanceHistoryDays(readNumber());
}
else if (m_xml.name() == "Color") {
m_meta->setColor(readColor());
}
else if (m_xml.name() == "MasterKeyChanged") {
m_meta->setMasterKeyChanged(readDateTime());
}
@ -187,6 +213,15 @@ void KeePass2XmlReader::parseMeta()
else if (m_xml.name() == "LastTopVisibleGroup") {
m_meta->setLastTopVisibleGroup(getGroup(readUuid()));
}
else if (m_xml.name() == "HistoryMaxItems") {
m_meta->setHistoryMaxItems(readNumber());
}
else if (m_xml.name() == "HistoryMaxSize") {
m_meta->setHistoryMaxSize(readNumber());
}
else if (m_xml.name() == "Binaries") {
parseBinaries();
}
else if (m_xml.name() == "CustomData") {
parseCustomData();
}
@ -216,9 +251,9 @@ void KeePass2XmlReader::parseMemoryProtection()
else if (m_xml.name() == "ProtectNotes") {
m_meta->setProtectNotes(readBool());
}
else if (m_xml.name() == "AutoEnableVisualHiding") {
/*else if (m_xml.name() == "AutoEnableVisualHiding") {
m_meta->setAutoEnableVisualHiding(readBool());
}
}*/
else {
skipCurrentElement();
}
@ -259,6 +294,37 @@ void KeePass2XmlReader::parseIcon()
}
}
void KeePass2XmlReader::parseBinaries()
{
Q_ASSERT(m_xml.isStartElement() && m_xml.name() == "Binaries");
while (!m_xml.error() && m_xml.readNextStartElement()) {
if (m_xml.name() == "Binary") {
QXmlStreamAttributes attr = m_xml.attributes();
QString id = attr.value("ID").toString();
QByteArray data;
if (attr.value("Compressed").compare("True", Qt::CaseInsensitive) == 0) {
data = readCompressedBinary();
}
else {
data = readBinary();
}
if (m_binaryPool.contains(id)) {
qWarning("KeePass2XmlReader::parseBinaries: overwriting binary item \"%s\"",
qPrintable(id));
}
m_binaryPool.insert(id, data);
}
else {
skipCurrentElement();
}
}
}
void KeePass2XmlReader::parseCustomData()
{
Q_ASSERT(m_xml.isStartElement() && m_xml.name() == "CustomData");
@ -547,7 +613,8 @@ void KeePass2XmlReader::parseEntryString(Entry *entry)
QXmlStreamAttributes attr = m_xml.attributes();
QString value = readString();
bool isProtected = attr.hasAttribute("Protected") && (attr.value("Protected") == "True");
bool isProtected = attr.value("Protected") == "True";
bool protectInMemory = attr.value("ProtectInMemory") == "True";
if (isProtected && !value.isEmpty()) {
if (m_randomStream) {
@ -558,7 +625,7 @@ void KeePass2XmlReader::parseEntryString(Entry *entry)
}
}
entry->attributes()->set(key, value, isProtected);
entry->attributes()->set(key, value, isProtected || protectInMemory);
}
else {
skipCurrentElement();
@ -576,16 +643,24 @@ void KeePass2XmlReader::parseEntryBinary(Entry *entry)
key = readString();
}
else if (m_xml.name() == "Value") {
QByteArray value = readBinary();
QXmlStreamAttributes attr = m_xml.attributes();
bool isProtected = attr.hasAttribute("Protected") && (attr.value("Protected") == "True");
if (isProtected && !value.isEmpty()) {
m_randomStream->processInPlace(value);
if (attr.hasAttribute("Ref")) {
m_binaryMap.insertMulti(attr.value("Ref").toString(), qMakePair(entry, key));
m_xml.skipCurrentElement();
}
else {
// format compatbility
QByteArray value = readBinary();
bool isProtected = attr.hasAttribute("Protected")
&& (attr.value("Protected") == "True");
entry->attachments()->set(key, value, isProtected);
if (isProtected && !value.isEmpty()) {
m_randomStream->processInPlace(value);
}
entry->attachments()->set(key, value);
}
}
else {
skipCurrentElement();
@ -782,6 +857,38 @@ QByteArray KeePass2XmlReader::readBinary()
return QByteArray::fromBase64(readString().toAscii());
}
QByteArray KeePass2XmlReader::readCompressedBinary()
{
QByteArray rawData = readBinary();
QBuffer buffer(&rawData);
buffer.open(QIODevice::ReadOnly);
QtIOCompressor compressor(&buffer);
compressor.setStreamFormat(QtIOCompressor::GzipFormat);
compressor.open(QIODevice::ReadOnly);
QByteArray result;
qint64 readBytes = 0;
qint64 readResult;
do {
result.resize(result.size() + 16384);
readResult = compressor.read(result.data() + readBytes, result.size() - readBytes);
if (readResult > 0) {
readBytes += readResult;
}
} while (readResult > 0);
if (readResult == -1) {
raiseError(16);
return QByteArray();
}
else {
result.resize(static_cast<int>(readBytes));
return result;
}
}
Group* KeePass2XmlReader::getGroup(const Uuid& uuid)
{
if (uuid.isNull()) {

View file

@ -20,6 +20,8 @@
#include <QtCore/QCoreApplication>
#include <QtCore/QDateTime>
#include <QtCore/QHash>
#include <QtCore/QPair>
#include <QtCore/QXmlStreamReader>
#include <QtGui/QColor>
@ -51,6 +53,7 @@ private:
void parseMemoryProtection();
void parseCustomIcons();
void parseIcon();
void parseBinaries();
void parseCustomData();
void parseCustomDataItem();
void parseRoot();
@ -72,6 +75,7 @@ private:
int readNumber();
Uuid readUuid();
QByteArray readBinary();
QByteArray readCompressedBinary();
Group* getGroup(const Uuid& uuid);
Entry* getEntry(const Uuid& uuid);
@ -85,6 +89,8 @@ private:
Group* m_tmpParent;
QList<Group*> m_groups;
QList<Entry*> m_entries;
QHash<QString, QByteArray> m_binaryPool;
QHash<QString, QPair<Entry*, QString> > m_binaryMap;
};
#endif // KEEPASSX_KEEPASS2XMLREADER_H

View file

@ -22,6 +22,7 @@
#include "core/Metadata.h"
#include "format/KeePass2RandomStream.h"
#include "streams/QtIOCompressor"
KeePass2XmlWriter::KeePass2XmlWriter()
: m_db(0)
@ -39,6 +40,8 @@ void KeePass2XmlWriter::writeDatabase(QIODevice* device, Database* db, KeePass2R
m_meta = db->metadata();
m_randomStream = randomStream;
generateIdMap();
m_xml.setDevice(device);
m_xml.writeStartDocument("1.0", true);
@ -60,6 +63,21 @@ void KeePass2XmlWriter::writeDatabase(const QString& filename, Database* db, Kee
writeDatabase(&file, db, randomStream);
}
void KeePass2XmlWriter::generateIdMap()
{
QList<Entry*> allEntries = m_db->rootGroup()->entriesRecursive(true);
int nextId = 0;
Q_FOREACH (Entry* entry, allEntries) {
Q_FOREACH (const QString& key, entry->attachments()->keys()) {
QByteArray data = entry->attachments()->value(key);
if (!m_idMap.contains(data)) {
m_idMap.insert(data, nextId++);
}
}
}
}
void KeePass2XmlWriter::writeMetadata()
{
m_xml.writeStartElement("Meta");
@ -72,6 +90,7 @@ void KeePass2XmlWriter::writeMetadata()
writeString("DefaultUserName", m_meta->defaultUserName());
writeDateTime("DefaultUserNameChanged", m_meta->defaultUserNameChanged());
writeNumber("MaintenanceHistoryDays", m_meta->maintenanceHistoryDays());
writeColor("Color", m_meta->color());
writeDateTime("MasterKeyChanged", m_meta->masterKeyChanged());
writeNumber("MasterKeyChangeRec", m_meta->masterKeyChangeRec());
writeNumber("MasterKeyChangeForce", m_meta->masterKeyChangeForce());
@ -84,6 +103,9 @@ void KeePass2XmlWriter::writeMetadata()
writeDateTime("EntryTemplatesGroupChanged", m_meta->entryTemplatesGroupChanged());
writeUuid("LastSelectedGroup", m_meta->lastSelectedGroup());
writeUuid("LastTopVisibleGroup", m_meta->lastTopVisibleGroup());
writeNumber("HistoryMaxItems", m_meta->historyMaxItems());
writeNumber("HistoryMaxSize", m_meta->historyMaxSize());
writeBinaries();
writeCustomData();
m_xml.writeEndElement();
@ -98,7 +120,7 @@ void KeePass2XmlWriter::writeMemoryProtection()
writeBool("ProtectPassword", m_meta->protectPassword());
writeBool("ProtectURL", m_meta->protectUrl());
writeBool("ProtectNotes", m_meta->protectNotes());
writeBool("AutoEnableVisualHiding", m_meta->autoEnableVisualHiding());
// writeBool("AutoEnableVisualHiding", m_meta->autoEnableVisualHiding());
m_xml.writeEndElement();
}
@ -132,6 +154,43 @@ void KeePass2XmlWriter::writeIcon(const Uuid& uuid, const QImage& icon)
m_xml.writeEndElement();
}
void KeePass2XmlWriter::writeBinaries()
{
m_xml.writeStartElement("Binaries");
QHash<QByteArray, int>::const_iterator i;
for (i = m_idMap.constBegin(); i != m_idMap.constEnd(); ++i) {
m_xml.writeStartElement("Binary");
m_xml.writeAttribute("ID", QString::number(i.value()));
if (m_db->compressionAlgo() == Database::CompressionGZip) {
m_xml.writeAttribute("Compressed", "True");
QBuffer buffer;
buffer.open(QIODevice::ReadWrite);
QtIOCompressor compressor(&buffer);
compressor.setStreamFormat(QtIOCompressor::GzipFormat);
compressor.open(QIODevice::WriteOnly);
qint64 bytesWritten = compressor.write(i.key());
Q_ASSERT(bytesWritten == i.key().size());
compressor.close();
buffer.seek(0);
m_xml.writeCharacters(QString::fromAscii(buffer.readAll().toBase64()));
}
else {
m_xml.writeCharacters(QString::fromAscii(i.key().toBase64()));
}
m_xml.writeEndElement();
}
m_xml.writeEndElement();
}
void KeePass2XmlWriter::writeCustomData()
{
m_xml.writeStartElement("CustomData");
@ -262,8 +321,7 @@ void KeePass2XmlWriter::writeEntry(const Entry* entry)
((key == "Password") && m_meta->protectPassword()) ||
((key == "URL") && m_meta->protectUrl()) ||
((key == "Notes") && m_meta->protectNotes()) ||
entry->attributes()->isProtected(key) ) &&
m_randomStream;
entry->attributes()->isProtected(key) );
writeString("Key", key);
@ -271,9 +329,15 @@ void KeePass2XmlWriter::writeEntry(const Entry* entry)
QString value;
if (protect) {
m_xml.writeAttribute("Protected", "True");
QByteArray rawData = m_randomStream->process(entry->attributes()->value(key).toUtf8());
value = QString::fromAscii(rawData.toBase64());
if (m_randomStream) {
m_xml.writeAttribute("Protected", "True");
QByteArray rawData = m_randomStream->process(entry->attributes()->value(key).toUtf8());
value = QString::fromAscii(rawData.toBase64());
}
else {
m_xml.writeAttribute("ProtectInMemory", "True");
value = entry->attributes()->value(key);
}
}
else {
value = entry->attributes()->value(key);
@ -288,23 +352,10 @@ void KeePass2XmlWriter::writeEntry(const Entry* entry)
Q_FOREACH (const QString& key, entry->attachments()->keys()) {
m_xml.writeStartElement("Binary");
bool protect = entry->attachments()->isProtected(key) && m_randomStream;
writeString("Key", key);
m_xml.writeStartElement("Value");
QString value;
if (protect) {
m_xml.writeAttribute("Protected", "True");
QByteArray rawData = m_randomStream->process(entry->attachments()->value(key));
value = QString::fromAscii(rawData.toBase64());
}
else {
value = entry->attachments()->value(key);
}
m_xml.writeCharacters(value);
m_xml.writeAttribute("Ref", QString::number(m_idMap[entry->attachments()->value(key)]));
m_xml.writeEndElement();
m_xml.writeEndElement();

View file

@ -43,10 +43,13 @@ public:
QString errorString();
private:
void generateIdMap();
void writeMetadata();
void writeMemoryProtection();
void writeCustomIcons();
void writeIcon(const Uuid& uuid, const QImage& icon);
void writeBinaries();
void writeCustomData();
void writeCustomDataItem(const QString& key, const QString& value);
void writeRoot();
@ -75,6 +78,7 @@ private:
Database* m_db;
Metadata* m_meta;
KeePass2RandomStream* m_randomStream;
QHash<QByteArray, int> m_idMap;
};
#endif // KEEPASSX_KEEPASS2XMLWRITER_H