mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-02-04 08:55:31 -05:00
Properly save custom header data
Ensure adding custom data upgrades to KDBX4 Implement review feedback
This commit is contained in:
parent
114f00e1e8
commit
5410d78bbb
@ -1,6 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
|
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
|
||||||
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -50,21 +49,15 @@ bool CustomData::containsValue(const QString& value) const
|
|||||||
|
|
||||||
void CustomData::set(const QString& key, const QString& value)
|
void CustomData::set(const QString& key, const QString& value)
|
||||||
{
|
{
|
||||||
bool emitModified = false;
|
|
||||||
|
|
||||||
bool addAttribute = !m_data.contains(key);
|
bool addAttribute = !m_data.contains(key);
|
||||||
bool changeValue = !addAttribute && (m_data.value(key) != value);
|
bool changeValue = !addAttribute && (m_data.value(key) != value);
|
||||||
|
|
||||||
if (addAttribute ) {
|
if (addAttribute) {
|
||||||
emit aboutToBeAdded(key);
|
emit aboutToBeAdded(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addAttribute || changeValue) {
|
if (addAttribute || changeValue) {
|
||||||
m_data.insert(key, value);
|
m_data.insert(key, value);
|
||||||
emitModified = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (emitModified) {
|
|
||||||
emit modified();
|
emit modified();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,13 +78,10 @@ void CustomData::remove(const QString& key)
|
|||||||
|
|
||||||
void CustomData::rename(const QString& oldKey, const QString& newKey)
|
void CustomData::rename(const QString& oldKey, const QString& newKey)
|
||||||
{
|
{
|
||||||
if (!m_data.contains(oldKey)) {
|
const bool containsOldKey = m_data.contains(oldKey);
|
||||||
Q_ASSERT(false);
|
const bool containsNewKey = m_data.contains(newKey);
|
||||||
return;
|
Q_ASSERT(containsOldKey && !containsNewKey);
|
||||||
}
|
if (!containsOldKey || containsNewKey) {
|
||||||
|
|
||||||
if (m_data.contains(newKey)) {
|
|
||||||
Q_ASSERT(false);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,16 +98,17 @@ void CustomData::rename(const QString& oldKey, const QString& newKey)
|
|||||||
|
|
||||||
void CustomData::copyDataFrom(const CustomData* other)
|
void CustomData::copyDataFrom(const CustomData* other)
|
||||||
{
|
{
|
||||||
if (*this != *other) {
|
if (*this == *other) {
|
||||||
emit aboutToBeReset();
|
return;
|
||||||
|
|
||||||
m_data = other->m_data;
|
|
||||||
|
|
||||||
emit reset();
|
|
||||||
emit modified();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
emit aboutToBeReset();
|
||||||
|
|
||||||
|
m_data = other->m_data;
|
||||||
|
|
||||||
|
emit reset();
|
||||||
|
emit modified();
|
||||||
|
}
|
||||||
bool CustomData::operator==(const CustomData& other) const
|
bool CustomData::operator==(const CustomData& other) const
|
||||||
{
|
{
|
||||||
return (m_data == other.m_data);
|
return (m_data == other.m_data);
|
||||||
@ -143,7 +134,12 @@ bool CustomData::isEmpty() const
|
|||||||
return m_data.isEmpty();
|
return m_data.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
int CustomData::dataSize()
|
int CustomData::size() const
|
||||||
|
{
|
||||||
|
return m_data.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
int CustomData::dataSize() const
|
||||||
{
|
{
|
||||||
int size = 0;
|
int size = 0;
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
|
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
|
||||||
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -16,17 +15,17 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef KEEPASSX_CUSTOMDATA_H
|
#ifndef KEEPASSXC_CUSTOMDATA_H
|
||||||
#define KEEPASSX_CUSTOMDATA_H
|
#define KEEPASSXC_CUSTOMDATA_H
|
||||||
|
|
||||||
#include <QMap>
|
#include <QHash>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
|
||||||
class CustomData : public QObject
|
class CustomData : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit CustomData(QObject* parent = nullptr);
|
explicit CustomData(QObject* parent = nullptr);
|
||||||
@ -40,7 +39,8 @@ public:
|
|||||||
void rename(const QString& oldKey, const QString& newKey);
|
void rename(const QString& oldKey, const QString& newKey);
|
||||||
void clear();
|
void clear();
|
||||||
bool isEmpty() const;
|
bool isEmpty() const;
|
||||||
int dataSize();
|
int size() const;
|
||||||
|
int dataSize() const;
|
||||||
void copyDataFrom(const CustomData* other);
|
void copyDataFrom(const CustomData* other);
|
||||||
bool operator==(const CustomData& other) const;
|
bool operator==(const CustomData& other) const;
|
||||||
bool operator!=(const CustomData& other) const;
|
bool operator!=(const CustomData& other) const;
|
||||||
@ -61,4 +61,4 @@ private:
|
|||||||
QHash<QString, QString> m_data;
|
QHash<QString, QString> m_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSX_CUSTOMDATA_H
|
#endif // KEEPASSXC_CUSTOMDATA_H
|
||||||
|
@ -320,6 +320,17 @@ bool Database::verifyKey(const CompositeKey& key) const
|
|||||||
return (m_data.key.rawKey() == key.rawKey());
|
return (m_data.key.rawKey() == key.rawKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVariantMap Database::publicCustomData() const
|
||||||
|
{
|
||||||
|
return m_data.publicCustomData;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Database::setPublicCustomData(const QVariantMap& customData)
|
||||||
|
{
|
||||||
|
m_data.publicCustomData = customData;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Database::createRecycleBin()
|
void Database::createRecycleBin()
|
||||||
{
|
{
|
||||||
Group* recycleBin = Group::createRecycleBin();
|
Group* recycleBin = Group::createRecycleBin();
|
||||||
@ -578,14 +589,6 @@ void Database::setKdf(QSharedPointer<Kdf> kdf)
|
|||||||
m_data.kdf = std::move(kdf);
|
m_data.kdf = std::move(kdf);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Database::setPublicCustomData(QByteArray data) {
|
|
||||||
m_data.publicCustomData = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray Database::publicCustomData() const {
|
|
||||||
return m_data.publicCustomData;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Database::changeKdf(QSharedPointer<Kdf> kdf)
|
bool Database::changeKdf(QSharedPointer<Kdf> kdf)
|
||||||
{
|
{
|
||||||
kdf->randomizeSeed();
|
kdf->randomizeSeed();
|
||||||
|
@ -59,12 +59,12 @@ public:
|
|||||||
Uuid cipher;
|
Uuid cipher;
|
||||||
CompressionAlgorithm compressionAlgo;
|
CompressionAlgorithm compressionAlgo;
|
||||||
QByteArray transformedMasterKey;
|
QByteArray transformedMasterKey;
|
||||||
QByteArray publicCustomData;
|
|
||||||
QSharedPointer<Kdf> kdf;
|
QSharedPointer<Kdf> kdf;
|
||||||
CompositeKey key;
|
CompositeKey key;
|
||||||
bool hasKey;
|
bool hasKey;
|
||||||
QByteArray masterSeed;
|
QByteArray masterSeed;
|
||||||
QByteArray challengeResponseKey;
|
QByteArray challengeResponseKey;
|
||||||
|
QVariantMap publicCustomData;
|
||||||
};
|
};
|
||||||
|
|
||||||
Database();
|
Database();
|
||||||
@ -93,7 +93,6 @@ public:
|
|||||||
Uuid cipher() const;
|
Uuid cipher() const;
|
||||||
Database::CompressionAlgorithm compressionAlgo() const;
|
Database::CompressionAlgorithm compressionAlgo() const;
|
||||||
QSharedPointer<Kdf> kdf() const;
|
QSharedPointer<Kdf> kdf() const;
|
||||||
QByteArray publicCustomData() const;
|
|
||||||
QByteArray transformedMasterKey() const;
|
QByteArray transformedMasterKey() const;
|
||||||
const CompositeKey& key() const;
|
const CompositeKey& key() const;
|
||||||
QByteArray challengeResponseKey() const;
|
QByteArray challengeResponseKey() const;
|
||||||
@ -102,11 +101,12 @@ public:
|
|||||||
void setCipher(const Uuid& cipher);
|
void setCipher(const Uuid& cipher);
|
||||||
void setCompressionAlgo(Database::CompressionAlgorithm algo);
|
void setCompressionAlgo(Database::CompressionAlgorithm algo);
|
||||||
void setKdf(QSharedPointer<Kdf> kdf);
|
void setKdf(QSharedPointer<Kdf> kdf);
|
||||||
void setPublicCustomData(QByteArray data);
|
|
||||||
bool setKey(const CompositeKey& key, bool updateChangedTime = true,
|
bool setKey(const CompositeKey& key, bool updateChangedTime = true,
|
||||||
bool updateTransformSalt = false);
|
bool updateTransformSalt = false);
|
||||||
bool hasKey() const;
|
bool hasKey() const;
|
||||||
bool verifyKey(const CompositeKey& key) const;
|
bool verifyKey(const CompositeKey& key) const;
|
||||||
|
QVariantMap publicCustomData() const;
|
||||||
|
void setPublicCustomData(const QVariantMap& customData);
|
||||||
void recycleEntry(Entry* entry);
|
void recycleEntry(Entry* entry);
|
||||||
void recycleGroup(Group* group);
|
void recycleGroup(Group* group);
|
||||||
void emptyRecycleBin();
|
void emptyRecycleBin();
|
||||||
|
@ -342,12 +342,12 @@ const EntryAttachments* Entry::attachments() const
|
|||||||
return m_attachments;
|
return m_attachments;
|
||||||
}
|
}
|
||||||
|
|
||||||
CustomData *Entry::customData()
|
CustomData* Entry::customData()
|
||||||
{
|
{
|
||||||
return m_customData;
|
return m_customData;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CustomData *Entry::customData() const
|
const CustomData* Entry::customData() const
|
||||||
{
|
{
|
||||||
return m_customData;
|
return m_customData;
|
||||||
}
|
}
|
||||||
@ -618,7 +618,7 @@ void Entry::truncateHistory()
|
|||||||
size += historyItem->attributes()->attributesSize();
|
size += historyItem->attributes()->attributesSize();
|
||||||
size += historyItem->autoTypeAssociations()->associationsSize();
|
size += historyItem->autoTypeAssociations()->associationsSize();
|
||||||
size += historyItem->attachments()->attachmentsSize();
|
size += historyItem->attachments()->attachmentsSize();
|
||||||
size += customData()->dataSize();
|
size += historyItem->customData()->dataSize();
|
||||||
const QStringList tags = historyItem->tags().split(delimiter, QString::SkipEmptyParts);
|
const QStringList tags = historyItem->tags().split(delimiter, QString::SkipEmptyParts);
|
||||||
for (const QString& tag : tags) {
|
for (const QString& tag : tags) {
|
||||||
size += tag.toUtf8().size();
|
size += tag.toUtf8().size();
|
||||||
|
@ -108,8 +108,8 @@ public:
|
|||||||
const EntryAttributes* attributes() const;
|
const EntryAttributes* attributes() const;
|
||||||
EntryAttachments* attachments();
|
EntryAttachments* attachments();
|
||||||
const EntryAttachments* attachments() const;
|
const EntryAttachments* attachments() const;
|
||||||
CustomData *customData();
|
CustomData* customData();
|
||||||
const CustomData *customData() const;
|
const CustomData* customData() const;
|
||||||
|
|
||||||
static const int DefaultIconNumber;
|
static const int DefaultIconNumber;
|
||||||
static const int ResolveMaximumDepth;
|
static const int ResolveMaximumDepth;
|
||||||
@ -231,11 +231,10 @@ private:
|
|||||||
|
|
||||||
Uuid m_uuid;
|
Uuid m_uuid;
|
||||||
EntryData m_data;
|
EntryData m_data;
|
||||||
EntryAttributes* const m_attributes;
|
QPointer<EntryAttributes> m_attributes;
|
||||||
EntryAttachments* const m_attachments;
|
QPointer<EntryAttachments> m_attachments;
|
||||||
AutoTypeAssociations* const m_autoTypeAssociations;
|
QPointer<AutoTypeAssociations> m_autoTypeAssociations;
|
||||||
|
QPointer<CustomData> m_customData;
|
||||||
CustomData* const m_customData;
|
|
||||||
|
|
||||||
QList<Entry*> m_history;
|
QList<Entry*> m_history;
|
||||||
Entry* m_tmpHistoryItem;
|
Entry* m_tmpHistoryItem;
|
||||||
|
@ -254,12 +254,12 @@ bool Group::isExpired() const
|
|||||||
return m_data.timeInfo.expires() && m_data.timeInfo.expiryTime() < QDateTime::currentDateTimeUtc();
|
return m_data.timeInfo.expires() && m_data.timeInfo.expiryTime() < QDateTime::currentDateTimeUtc();
|
||||||
}
|
}
|
||||||
|
|
||||||
CustomData *Group::customData()
|
CustomData* Group::customData()
|
||||||
{
|
{
|
||||||
return m_customData;
|
return m_customData;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CustomData *Group::customData() const
|
const CustomData* Group::customData() const
|
||||||
{
|
{
|
||||||
return m_customData;
|
return m_customData;
|
||||||
}
|
}
|
||||||
|
@ -84,8 +84,8 @@ public:
|
|||||||
bool resolveAutoTypeEnabled() const;
|
bool resolveAutoTypeEnabled() const;
|
||||||
Entry* lastTopVisibleEntry() const;
|
Entry* lastTopVisibleEntry() const;
|
||||||
bool isExpired() const;
|
bool isExpired() const;
|
||||||
CustomData *customData();
|
CustomData* customData();
|
||||||
const CustomData *customData() const;
|
const CustomData* customData() const;
|
||||||
|
|
||||||
static const int DefaultIconNumber;
|
static const int DefaultIconNumber;
|
||||||
static const int RecycleBinIconNumber;
|
static const int RecycleBinIconNumber;
|
||||||
@ -191,7 +191,7 @@ private:
|
|||||||
QList<Group*> m_children;
|
QList<Group*> m_children;
|
||||||
QList<Entry*> m_entries;
|
QList<Entry*> m_entries;
|
||||||
|
|
||||||
CustomData *const m_customData;
|
QPointer<CustomData> m_customData;
|
||||||
|
|
||||||
QPointer<Group> m_parent;
|
QPointer<Group> m_parent;
|
||||||
|
|
||||||
|
@ -294,12 +294,12 @@ int Metadata::historyMaxSize() const
|
|||||||
return m_data.historyMaxSize;
|
return m_data.historyMaxSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
CustomData *Metadata::customData()
|
CustomData* Metadata::customData()
|
||||||
{
|
{
|
||||||
return m_customData;
|
return m_customData;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CustomData *Metadata::customData() const
|
const CustomData* Metadata::customData() const
|
||||||
{
|
{
|
||||||
return m_customData;
|
return m_customData;
|
||||||
}
|
}
|
||||||
|
@ -98,8 +98,8 @@ public:
|
|||||||
int masterKeyChangeForce() const;
|
int masterKeyChangeForce() const;
|
||||||
int historyMaxItems() const;
|
int historyMaxItems() const;
|
||||||
int historyMaxSize() const;
|
int historyMaxSize() const;
|
||||||
CustomData *customData();
|
CustomData* customData();
|
||||||
const CustomData *customData() const;
|
const CustomData* customData() const;
|
||||||
|
|
||||||
static const int DefaultHistoryMaxItems;
|
static const int DefaultHistoryMaxItems;
|
||||||
static const int DefaultHistoryMaxSize;
|
static const int DefaultHistoryMaxSize;
|
||||||
@ -175,7 +175,7 @@ private:
|
|||||||
QDateTime m_masterKeyChanged;
|
QDateTime m_masterKeyChanged;
|
||||||
QDateTime m_settingsChanged;
|
QDateTime m_settingsChanged;
|
||||||
|
|
||||||
CustomData *const m_customData;
|
QPointer<CustomData> m_customData;
|
||||||
|
|
||||||
bool m_updateDatetime;
|
bool m_updateDatetime;
|
||||||
};
|
};
|
||||||
|
@ -209,9 +209,14 @@ bool Kdbx4Reader::readHeaderField(StoreDataStream& device)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case KeePass2::HeaderFieldID::PublicCustomData:
|
case KeePass2::HeaderFieldID::PublicCustomData: {
|
||||||
m_db->setPublicCustomData(fieldData);
|
QBuffer variantBuffer;
|
||||||
|
variantBuffer.setBuffer(&fieldData);
|
||||||
|
variantBuffer.open(QBuffer::ReadOnly);
|
||||||
|
QVariantMap data = readVariantMap(&variantBuffer);
|
||||||
|
m_db->setPublicCustomData(data);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case KeePass2::HeaderFieldID::ProtectedStreamKey:
|
case KeePass2::HeaderFieldID::ProtectedStreamKey:
|
||||||
case KeePass2::HeaderFieldID::TransformRounds:
|
case KeePass2::HeaderFieldID::TransformRounds:
|
||||||
@ -291,10 +296,10 @@ bool Kdbx4Reader::readInnerHeaderField(QIODevice* device)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper method for reading KDF parameters into variant map.
|
* Helper method for reading a serialized variant map.
|
||||||
*
|
*
|
||||||
* @param device input device
|
* @param device input device
|
||||||
* @return filled variant map
|
* @return de-serialized variant map
|
||||||
*/
|
*/
|
||||||
QVariantMap Kdbx4Reader::readVariantMap(QIODevice* device)
|
QVariantMap Kdbx4Reader::readVariantMap(QIODevice* device)
|
||||||
{
|
{
|
||||||
|
@ -22,6 +22,8 @@
|
|||||||
|
|
||||||
#include "streams/HmacBlockStream.h"
|
#include "streams/HmacBlockStream.h"
|
||||||
#include "core/Database.h"
|
#include "core/Database.h"
|
||||||
|
#include "core/Metadata.h"
|
||||||
|
#include "core/CustomData.h"
|
||||||
#include "crypto/CryptoHash.h"
|
#include "crypto/CryptoHash.h"
|
||||||
#include "crypto/Random.h"
|
#include "crypto/Random.h"
|
||||||
#include "format/KeePass2RandomStream.h"
|
#include "format/KeePass2RandomStream.h"
|
||||||
@ -86,10 +88,14 @@ bool Kdbx4Writer::writeDatabase(QIODevice* device, Database* db)
|
|||||||
raiseError(tr("Failed to serialize KDF parameters variant map"));
|
raiseError(tr("Failed to serialize KDF parameters variant map"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
QByteArray publicCustomData = db->publicCustomData();
|
|
||||||
CHECK_RETURN_FALSE(writeHeaderField<quint32>(&header, KeePass2::HeaderFieldID::KdfParameters, kdfParamBytes));
|
CHECK_RETURN_FALSE(writeHeaderField<quint32>(&header, KeePass2::HeaderFieldID::KdfParameters, kdfParamBytes));
|
||||||
|
QVariantMap publicCustomData = db->publicCustomData();
|
||||||
if (!publicCustomData.isEmpty()) {
|
if (!publicCustomData.isEmpty()) {
|
||||||
CHECK_RETURN_FALSE(writeHeaderField<quint32>(&header, KeePass2::HeaderFieldID::PublicCustomData, publicCustomData));
|
QByteArray serialized;
|
||||||
|
serializeVariantMap(publicCustomData, serialized);
|
||||||
|
CHECK_RETURN_FALSE(writeHeaderField<quint32>(
|
||||||
|
&header, KeePass2::HeaderFieldID::PublicCustomData, serialized));
|
||||||
}
|
}
|
||||||
|
|
||||||
CHECK_RETURN_FALSE(writeHeaderField<quint32>(&header, KeePass2::HeaderFieldID::EndOfHeader, endOfHeader));
|
CHECK_RETURN_FALSE(writeHeaderField<quint32>(&header, KeePass2::HeaderFieldID::EndOfHeader, endOfHeader));
|
||||||
@ -230,7 +236,7 @@ void Kdbx4Writer::writeAttachments(QIODevice* device, Database* db)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serialize KDF parameter variant map to byte array.
|
* Serialize variant map to byte array.
|
||||||
*
|
*
|
||||||
* @param map input variant map
|
* @param map input variant map
|
||||||
* @param outputBytes output byte array
|
* @param outputBytes output byte array
|
||||||
|
@ -397,7 +397,7 @@ void KdbxXmlReader::parseBinaries()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void KdbxXmlReader::parseCustomData(CustomData *customData)
|
void KdbxXmlReader::parseCustomData(CustomData* customData)
|
||||||
{
|
{
|
||||||
Q_ASSERT(m_xml.isStartElement() && m_xml.name() == "CustomData");
|
Q_ASSERT(m_xml.isStartElement() && m_xml.name() == "CustomData");
|
||||||
|
|
||||||
@ -410,7 +410,7 @@ void KdbxXmlReader::parseCustomData(CustomData *customData)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void KdbxXmlReader::parseCustomDataItem(CustomData *customData)
|
void KdbxXmlReader::parseCustomDataItem(CustomData* customData)
|
||||||
{
|
{
|
||||||
Q_ASSERT(m_xml.isStartElement() && m_xml.name() == "Item");
|
Q_ASSERT(m_xml.isStartElement() && m_xml.name() == "Item");
|
||||||
|
|
||||||
@ -748,7 +748,7 @@ Entry* KdbxXmlReader::parseEntry(bool history)
|
|||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (m_xml.name() == "CustomData" ){
|
if (m_xml.name() == "CustomData") {
|
||||||
parseCustomData(entry->customData());
|
parseCustomData(entry->customData());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -66,8 +66,8 @@ protected:
|
|||||||
virtual void parseCustomIcons();
|
virtual void parseCustomIcons();
|
||||||
virtual void parseIcon();
|
virtual void parseIcon();
|
||||||
virtual void parseBinaries();
|
virtual void parseBinaries();
|
||||||
virtual void parseCustomData(CustomData *customData);
|
virtual void parseCustomData(CustomData* customData);
|
||||||
virtual void parseCustomDataItem(CustomData *customData);
|
virtual void parseCustomDataItem(CustomData* customData);
|
||||||
virtual bool parseRoot();
|
virtual bool parseRoot();
|
||||||
virtual Group* parseGroup();
|
virtual Group* parseGroup();
|
||||||
virtual void parseDeletedObjects();
|
virtual void parseDeletedObjects();
|
||||||
|
@ -129,7 +129,9 @@ void KdbxXmlWriter::writeMetadata()
|
|||||||
if (m_kdbxVersion < KeePass2::FILE_VERSION_4) {
|
if (m_kdbxVersion < KeePass2::FILE_VERSION_4) {
|
||||||
writeBinaries();
|
writeBinaries();
|
||||||
}
|
}
|
||||||
writeCustomData(m_meta->customData());
|
if (m_kdbxVersion >= KeePass2::FILE_VERSION_4) {
|
||||||
|
writeCustomData(m_meta->customData());
|
||||||
|
}
|
||||||
|
|
||||||
m_xml.writeEndElement();
|
m_xml.writeEndElement();
|
||||||
}
|
}
|
||||||
@ -218,8 +220,11 @@ void KdbxXmlWriter::writeBinaries()
|
|||||||
m_xml.writeEndElement();
|
m_xml.writeEndElement();
|
||||||
}
|
}
|
||||||
|
|
||||||
void KdbxXmlWriter::writeCustomData(const CustomData *customData)
|
void KdbxXmlWriter::writeCustomData(const CustomData* customData)
|
||||||
{
|
{
|
||||||
|
if (customData->isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
m_xml.writeStartElement("CustomData");
|
m_xml.writeStartElement("CustomData");
|
||||||
|
|
||||||
const QList<QString> keyList = customData->keys();
|
const QList<QString> keyList = customData->keys();
|
||||||
@ -276,7 +281,7 @@ void KdbxXmlWriter::writeGroup(const Group* group)
|
|||||||
|
|
||||||
writeUuid("LastTopVisibleEntry", group->lastTopVisibleEntry());
|
writeUuid("LastTopVisibleEntry", group->lastTopVisibleEntry());
|
||||||
|
|
||||||
if (!group->customData()->isEmpty()){
|
if (m_kdbxVersion >= KeePass2::FILE_VERSION_4) {
|
||||||
writeCustomData(group->customData());
|
writeCustomData(group->customData());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -405,7 +410,7 @@ void KdbxXmlWriter::writeEntry(const Entry* entry)
|
|||||||
|
|
||||||
writeAutoType(entry);
|
writeAutoType(entry);
|
||||||
|
|
||||||
if (!entry->customData()->isEmpty()){
|
if (m_kdbxVersion >= KeePass2::FILE_VERSION_4) {
|
||||||
writeCustomData(entry->customData());
|
writeCustomData(entry->customData());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ private:
|
|||||||
void writeCustomIcons();
|
void writeCustomIcons();
|
||||||
void writeIcon(const Uuid& uuid, const QImage& icon);
|
void writeIcon(const Uuid& uuid, const QImage& icon);
|
||||||
void writeBinaries();
|
void writeBinaries();
|
||||||
void writeCustomData(const CustomData *customData);
|
void writeCustomData(const CustomData* customData);
|
||||||
void writeCustomDataItem(const QString& key, const QString& value);
|
void writeCustomDataItem(const QString& key, const QString& value);
|
||||||
void writeRoot();
|
void writeRoot();
|
||||||
void writeGroup(const Group* group);
|
void writeGroup(const Group* group);
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
|
||||||
#include "core/Database.h"
|
#include "core/Database.h"
|
||||||
|
#include "core/Group.h"
|
||||||
|
#include "core/Metadata.h"
|
||||||
#include "crypto/kdf/AesKdf.h"
|
#include "crypto/kdf/AesKdf.h"
|
||||||
#include "format/KeePass2Writer.h"
|
#include "format/KeePass2Writer.h"
|
||||||
#include "format/Kdbx3Writer.h"
|
#include "format/Kdbx3Writer.h"
|
||||||
@ -48,12 +50,24 @@ bool KeePass2Writer::writeDatabase(const QString& filename, Database* db)
|
|||||||
* @param db source database
|
* @param db source database
|
||||||
* @return true on success
|
* @return true on success
|
||||||
*/
|
*/
|
||||||
|
|
||||||
bool KeePass2Writer::writeDatabase(QIODevice* device, Database* db) {
|
bool KeePass2Writer::writeDatabase(QIODevice* device, Database* db) {
|
||||||
m_error = false;
|
m_error = false;
|
||||||
m_errorStr.clear();
|
m_errorStr.clear();
|
||||||
|
|
||||||
// determine KDBX3 vs KDBX4
|
// determine KDBX3 vs KDBX4
|
||||||
if (db->kdf()->uuid() == KeePass2::KDF_AES_KDBX3 && db->publicCustomData().isEmpty()) {
|
bool hasCustomData = !db->publicCustomData().isEmpty() || (db->metadata()->customData() && !db->metadata()->customData()->isEmpty());
|
||||||
|
if (!hasCustomData) {
|
||||||
|
for (const auto& entry: db->rootGroup()->entriesRecursive(true)) {
|
||||||
|
if ((entry->customData() && !entry->customData()->isEmpty()) ||
|
||||||
|
(entry->group() && entry->group()->customData() && !entry->group()->customData()->isEmpty())) {
|
||||||
|
hasCustomData = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (db->kdf()->uuid() == KeePass2::KDF_AES_KDBX3 && !hasCustomData) {
|
||||||
m_version = KeePass2::FILE_VERSION_3_1;
|
m_version = KeePass2::FILE_VERSION_3_1;
|
||||||
m_writer.reset(new Kdbx3Writer());
|
m_writer.reset(new Kdbx3Writer());
|
||||||
} else {
|
} else {
|
||||||
|
@ -18,8 +18,6 @@
|
|||||||
#include "EditWidgetProperties.h"
|
#include "EditWidgetProperties.h"
|
||||||
#include "ui_EditWidgetProperties.h"
|
#include "ui_EditWidgetProperties.h"
|
||||||
|
|
||||||
#include <QStandardItemModel>
|
|
||||||
|
|
||||||
EditWidgetProperties::EditWidgetProperties(QWidget* parent)
|
EditWidgetProperties::EditWidgetProperties(QWidget* parent)
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
, m_ui(new Ui::EditWidgetProperties())
|
, m_ui(new Ui::EditWidgetProperties())
|
||||||
@ -29,14 +27,14 @@ EditWidgetProperties::EditWidgetProperties(QWidget* parent)
|
|||||||
m_ui->setupUi(this);
|
m_ui->setupUi(this);
|
||||||
m_ui->customDataTable->setModel(m_customDataModel);
|
m_ui->customDataTable->setModel(m_customDataModel);
|
||||||
|
|
||||||
this->connect( m_ui->removeCustomDataButton, SIGNAL(clicked()), SLOT(removeSelectedPluginData()));
|
connect(m_ui->removeCustomDataButton, SIGNAL(clicked()), SLOT(removeSelectedPluginData()));
|
||||||
}
|
}
|
||||||
|
|
||||||
EditWidgetProperties::~EditWidgetProperties()
|
EditWidgetProperties::~EditWidgetProperties()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditWidgetProperties::setFields(const TimeInfo &timeInfo, const Uuid &uuid)
|
void EditWidgetProperties::setFields(const TimeInfo& timeInfo, const Uuid& uuid)
|
||||||
{
|
{
|
||||||
static const QString timeFormat("d MMM yyyy HH:mm:ss");
|
static const QString timeFormat("d MMM yyyy HH:mm:ss");
|
||||||
m_ui->modifiedEdit->setText(
|
m_ui->modifiedEdit->setText(
|
||||||
@ -48,35 +46,37 @@ void EditWidgetProperties::setFields(const TimeInfo &timeInfo, const Uuid &uuid)
|
|||||||
m_ui->uuidEdit->setText(uuid.toHex());
|
m_ui->uuidEdit->setText(uuid.toHex());
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditWidgetProperties::setCustomData(const CustomData *customData)
|
void EditWidgetProperties::setCustomData(const CustomData* customData)
|
||||||
{
|
{
|
||||||
Q_ASSERT(customData != nullptr);
|
Q_ASSERT(customData);
|
||||||
m_customData->copyDataFrom(customData);
|
m_customData->copyDataFrom(customData);
|
||||||
|
|
||||||
this->updateModel();
|
this->updateModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
const CustomData *EditWidgetProperties::customData() const
|
const CustomData* EditWidgetProperties::customData() const
|
||||||
{
|
{
|
||||||
return m_customData;
|
return m_customData;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditWidgetProperties::removeSelectedPluginData()
|
void EditWidgetProperties::removeSelectedPluginData()
|
||||||
{
|
{
|
||||||
const QItemSelectionModel *pSelectionModel = m_ui->customDataTable->selectionModel();
|
const QItemSelectionModel* itemSelectionModel = m_ui->customDataTable->selectionModel();
|
||||||
if (pSelectionModel){
|
if (itemSelectionModel) {
|
||||||
for( const QModelIndex& index : pSelectionModel->selectedRows(0) ){
|
for (const QModelIndex& index : itemSelectionModel->selectedRows(0)) {
|
||||||
const QString key = index.data().toString();
|
const QString key = index.data().toString();
|
||||||
m_customData->remove(key);
|
m_customData->remove(key);
|
||||||
}
|
}
|
||||||
this->updateModel();
|
updateModel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditWidgetProperties::updateModel()
|
void EditWidgetProperties::updateModel()
|
||||||
{
|
{
|
||||||
m_customDataModel->clear();
|
m_customDataModel->clear();
|
||||||
for( const QString& key : m_customData->keys() ){
|
for (const QString& key : m_customData->keys()) {
|
||||||
m_customDataModel->appendRow(QList<QStandardItem*>() << new QStandardItem( key ) << new QStandardItem( m_customData->value( key ) ));
|
m_customDataModel->appendRow(QList<QStandardItem*>()
|
||||||
|
<< new QStandardItem(key)
|
||||||
|
<< new QStandardItem(m_customData->value(key)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,10 +38,10 @@ public:
|
|||||||
explicit EditWidgetProperties(QWidget* parent = nullptr);
|
explicit EditWidgetProperties(QWidget* parent = nullptr);
|
||||||
~EditWidgetProperties();
|
~EditWidgetProperties();
|
||||||
|
|
||||||
void setFields(const TimeInfo &timeInfo, const Uuid &uuid);
|
void setFields(const TimeInfo& timeInfo, const Uuid& uuid);
|
||||||
void setCustomData(const CustomData *customData);
|
void setCustomData(const CustomData* customData);
|
||||||
|
|
||||||
const CustomData *customData() const;
|
const CustomData* customData() const;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void removeSelectedPluginData();
|
void removeSelectedPluginData();
|
||||||
|
@ -73,4 +73,4 @@ void WelcomeWidget::refreshLastDatabases()
|
|||||||
itm->setText(database);
|
itm->setText(database);
|
||||||
m_ui->recentListWidget->addItem(itm);
|
m_ui->recentListWidget->addItem(itm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,6 @@ QTEST_GUILESS_MAIN(TestKdbx3)
|
|||||||
|
|
||||||
void TestKdbx3::initTestCaseImpl()
|
void TestKdbx3::initTestCaseImpl()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Database* TestKdbx3::readXml(const QString& path, bool strictMode, bool& hasError, QString& errorString)
|
Database* TestKdbx3::readXml(const QString& path, bool strictMode, bool& hasError, QString& errorString)
|
||||||
|
@ -131,6 +131,7 @@ void TestKdbx4::testFormat400Upgrade()
|
|||||||
{
|
{
|
||||||
QFETCH(Uuid, kdfUuid);
|
QFETCH(Uuid, kdfUuid);
|
||||||
QFETCH(Uuid, cipherUuid);
|
QFETCH(Uuid, cipherUuid);
|
||||||
|
QFETCH(bool, addCustomData);
|
||||||
QFETCH(quint32, expectedVersion);
|
QFETCH(quint32, expectedVersion);
|
||||||
|
|
||||||
QScopedPointer<Database> sourceDb(new Database());
|
QScopedPointer<Database> sourceDb(new Database());
|
||||||
@ -147,6 +148,12 @@ void TestKdbx4::testFormat400Upgrade()
|
|||||||
// upgrade to KDBX 4 by changing KDF and Cipher
|
// upgrade to KDBX 4 by changing KDF and Cipher
|
||||||
sourceDb->changeKdf(KeePass2::uuidToKdf(kdfUuid));
|
sourceDb->changeKdf(KeePass2::uuidToKdf(kdfUuid));
|
||||||
sourceDb->setCipher(cipherUuid);
|
sourceDb->setCipher(cipherUuid);
|
||||||
|
|
||||||
|
if (addCustomData) {
|
||||||
|
sourceDb->metadata()->customData()->set("CustomPublicData", "Hey look, I turned myself into a pickle!");
|
||||||
|
sourceDb->rootGroup()->customData()->set("CustomGroupData", "I just killed my family! I don't care who they were!");
|
||||||
|
}
|
||||||
|
|
||||||
KeePass2Writer writer;
|
KeePass2Writer writer;
|
||||||
writer.writeDatabase(&buffer, sourceDb.data());
|
writer.writeDatabase(&buffer, sourceDb.data());
|
||||||
if (writer.hasError()) {
|
if (writer.hasError()) {
|
||||||
@ -165,26 +172,39 @@ void TestKdbx4::testFormat400Upgrade()
|
|||||||
QCOMPARE(targetDb->metadata()->name(), sourceDb->metadata()->name());
|
QCOMPARE(targetDb->metadata()->name(), sourceDb->metadata()->name());
|
||||||
|
|
||||||
QCOMPARE(reader.version(), expectedVersion);
|
QCOMPARE(reader.version(), expectedVersion);
|
||||||
QCOMPARE(targetDb->kdf()->uuid(), sourceDb->kdf()->uuid());
|
|
||||||
QCOMPARE(targetDb->cipher(), cipherUuid);
|
QCOMPARE(targetDb->cipher(), cipherUuid);
|
||||||
|
QCOMPARE(*targetDb->metadata()->customData(), *sourceDb->metadata()->customData());
|
||||||
|
QCOMPARE(*targetDb->rootGroup()->customData(), *sourceDb->rootGroup()->customData());
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestKdbx4::testFormat400Upgrade_data()
|
void TestKdbx4::testFormat400Upgrade_data()
|
||||||
{
|
{
|
||||||
QTest::addColumn<Uuid>("kdfUuid");
|
QTest::addColumn<Uuid>("kdfUuid");
|
||||||
QTest::addColumn<Uuid>("cipherUuid");
|
QTest::addColumn<Uuid>("cipherUuid");
|
||||||
|
QTest::addColumn<bool>("addCustomData");
|
||||||
QTest::addColumn<quint32>("expectedVersion");
|
QTest::addColumn<quint32>("expectedVersion");
|
||||||
|
|
||||||
auto constexpr kdbx3 = KeePass2::FILE_VERSION_3_1 & KeePass2::FILE_VERSION_CRITICAL_MASK;
|
auto constexpr kdbx3 = KeePass2::FILE_VERSION_3_1 & KeePass2::FILE_VERSION_CRITICAL_MASK;
|
||||||
auto constexpr kdbx4 = KeePass2::FILE_VERSION_4 & KeePass2::FILE_VERSION_CRITICAL_MASK;
|
auto constexpr kdbx4 = KeePass2::FILE_VERSION_4 & KeePass2::FILE_VERSION_CRITICAL_MASK;
|
||||||
|
|
||||||
QTest::newRow("Argon2 + AES") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_AES << kdbx4;
|
QTest::newRow("Argon2 + AES") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_AES << false << kdbx4;
|
||||||
QTest::newRow("AES-KDF + AES") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_AES << kdbx4;
|
QTest::newRow("AES-KDF + AES") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_AES << false << kdbx4;
|
||||||
QTest::newRow("AES-KDF (legacy) + AES") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_AES << kdbx3;
|
QTest::newRow("AES-KDF (legacy) + AES") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_AES << false << kdbx3;
|
||||||
QTest::newRow("Argon2 + ChaCha20") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_CHACHA20 << kdbx4;
|
QTest::newRow("Argon2 + AES + CustomData") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_AES << true << kdbx4;
|
||||||
QTest::newRow("AES-KDF + ChaCha20") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_CHACHA20 << kdbx4;
|
QTest::newRow("AES-KDF + AES + CustomData") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_AES << true << kdbx4;
|
||||||
QTest::newRow("AES-KDF (legacy) + ChaCha20") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_CHACHA20 << kdbx3;
|
QTest::newRow("AES-KDF (legacy) + AES + CustomData") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_AES << true << kdbx4;
|
||||||
QTest::newRow("Argon2 + Twofish") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_TWOFISH << kdbx4;
|
|
||||||
QTest::newRow("AES-KDF + Twofish") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_TWOFISH << kdbx4;
|
QTest::newRow("Argon2 + ChaCha20") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_CHACHA20 << false << kdbx4;
|
||||||
QTest::newRow("AES-KDF (legacy) + Twofish") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_TWOFISH << kdbx3;
|
QTest::newRow("AES-KDF + ChaCha20") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_CHACHA20 << false << kdbx4;
|
||||||
|
QTest::newRow("AES-KDF (legacy) + ChaCha20") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_CHACHA20 << false << kdbx3;
|
||||||
|
QTest::newRow("Argon2 + ChaCha20 + CustomData") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_CHACHA20 << true << kdbx4;
|
||||||
|
QTest::newRow("AES-KDF + ChaCha20 + CustomData") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_CHACHA20 << true << kdbx4;
|
||||||
|
QTest::newRow("AES-KDF (legacy) + ChaCha20 + CustomData") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_CHACHA20 << true << kdbx4;
|
||||||
|
|
||||||
|
QTest::newRow("Argon2 + Twofish") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_TWOFISH << false << kdbx4;
|
||||||
|
QTest::newRow("AES-KDF + Twofish") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_TWOFISH << false << kdbx4;
|
||||||
|
QTest::newRow("AES-KDF (legacy) + Twofish") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_TWOFISH << false << kdbx3;
|
||||||
|
QTest::newRow("Argon2 + Twofish + CustomData") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_TWOFISH << true << kdbx4;
|
||||||
|
QTest::newRow("AES-KDF + Twofish + CustomData") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_TWOFISH << true << kdbx4;
|
||||||
|
QTest::newRow("AES-KDF (legacy) + Twofish + CustomData") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_TWOFISH << true << kdbx4;
|
||||||
}
|
}
|
||||||
|
@ -124,15 +124,6 @@ void TestKeePass2Format::testXmlCustomIcons()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestKeePass2Format::testXmlCustomData()
|
|
||||||
{
|
|
||||||
QHash<QString, QString> customFields = m_xmlDb->metadata()->customFields();
|
|
||||||
|
|
||||||
QCOMPARE(customFields.size(), 2);
|
|
||||||
QCOMPARE(customFields.value("A Sample Test Key"), QString("valu"));
|
|
||||||
QCOMPARE(customFields.value("custom key"), QString("blub"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestKeePass2Format::testXmlGroupRoot()
|
void TestKeePass2Format::testXmlGroupRoot()
|
||||||
{
|
{
|
||||||
const Group* group = m_xmlDb->rootGroup();
|
const Group* group = m_xmlDb->rootGroup();
|
||||||
@ -155,7 +146,6 @@ void TestKeePass2Format::testXmlGroupRoot()
|
|||||||
QCOMPARE(group->autoTypeEnabled(), Group::Inherit);
|
QCOMPARE(group->autoTypeEnabled(), Group::Inherit);
|
||||||
QCOMPARE(group->searchingEnabled(), Group::Inherit);
|
QCOMPARE(group->searchingEnabled(), Group::Inherit);
|
||||||
QCOMPARE(group->lastTopVisibleEntry()->uuid().toBase64(), QString("+wSUOv6qf0OzW8/ZHAs2sA=="));
|
QCOMPARE(group->lastTopVisibleEntry()->uuid().toBase64(), QString("+wSUOv6qf0OzW8/ZHAs2sA=="));
|
||||||
|
|
||||||
QCOMPARE(group->children().size(), 3);
|
QCOMPARE(group->children().size(), 3);
|
||||||
QVERIFY(m_xmlDb->metadata()->recycleBin() == m_xmlDb->rootGroup()->children().at(2));
|
QVERIFY(m_xmlDb->metadata()->recycleBin() == m_xmlDb->rootGroup()->children().at(2));
|
||||||
|
|
||||||
|
@ -40,7 +40,6 @@ private slots:
|
|||||||
*/
|
*/
|
||||||
void testXmlMetadata();
|
void testXmlMetadata();
|
||||||
void testXmlCustomIcons();
|
void testXmlCustomIcons();
|
||||||
void testXmlCustomData();
|
|
||||||
void testXmlGroupRoot();
|
void testXmlGroupRoot();
|
||||||
void testXmlGroup1();
|
void testXmlGroup1();
|
||||||
void testXmlGroup2();
|
void testXmlGroup2();
|
||||||
|
@ -40,31 +40,31 @@ void TestModified::testSignals()
|
|||||||
|
|
||||||
CompositeKey compositeKey;
|
CompositeKey compositeKey;
|
||||||
|
|
||||||
Database* db = new Database();
|
QScopedPointer<Database> db(new Database());
|
||||||
Group* root = db->rootGroup();
|
auto* root = db->rootGroup();
|
||||||
QSignalSpy spyModified(db, SIGNAL(modifiedImmediate()));
|
QSignalSpy spyModified(db.data(), SIGNAL(modifiedImmediate()));
|
||||||
|
|
||||||
db->setKey(compositeKey);
|
db->setKey(compositeKey);
|
||||||
QCOMPARE(spyModified.count(), ++spyCount);
|
QCOMPARE(spyModified.count(), ++spyCount);
|
||||||
|
|
||||||
Group* group1 = new Group();
|
auto* group1 = new Group();
|
||||||
group1->setParent(root);
|
group1->setParent(root);
|
||||||
QCOMPARE(spyModified.count(), ++spyCount);
|
QCOMPARE(spyModified.count(), ++spyCount);
|
||||||
|
|
||||||
Group* group2 = new Group();
|
auto* group2 = new Group();
|
||||||
group2->setParent(root);
|
group2->setParent(root);
|
||||||
QCOMPARE(spyModified.count(), ++spyCount);
|
QCOMPARE(spyModified.count(), ++spyCount);
|
||||||
|
|
||||||
group2->setParent(root, 0);
|
group2->setParent(root, 0);
|
||||||
QCOMPARE(spyModified.count(), ++spyCount);
|
QCOMPARE(spyModified.count(), ++spyCount);
|
||||||
|
|
||||||
Entry* entry1 = new Entry();
|
auto* entry1 = new Entry();
|
||||||
entry1->setGroup(group1);
|
entry1->setGroup(group1);
|
||||||
QCOMPARE(spyModified.count(), ++spyCount);
|
QCOMPARE(spyModified.count(), ++spyCount);
|
||||||
|
|
||||||
Database* db2 = new Database();
|
QScopedPointer<Database> db2(new Database());
|
||||||
Group* root2 = db2->rootGroup();
|
auto* root2 = db2->rootGroup();
|
||||||
QSignalSpy spyModified2(db2, SIGNAL(modifiedImmediate()));
|
QSignalSpy spyModified2(db2.data(), SIGNAL(modifiedImmediate()));
|
||||||
|
|
||||||
group1->setParent(root2);
|
group1->setParent(root2);
|
||||||
QCOMPARE(spyModified.count(), ++spyCount);
|
QCOMPARE(spyModified.count(), ++spyCount);
|
||||||
@ -74,7 +74,7 @@ void TestModified::testSignals()
|
|||||||
QCOMPARE(spyModified.count(), spyCount);
|
QCOMPARE(spyModified.count(), spyCount);
|
||||||
QCOMPARE(spyModified2.count(), ++spyCount2);
|
QCOMPARE(spyModified2.count(), ++spyCount2);
|
||||||
|
|
||||||
Entry* entry2 = new Entry();
|
auto* entry2 = new Entry();
|
||||||
entry2->setGroup(group2);
|
entry2->setGroup(group2);
|
||||||
QCOMPARE(spyModified.count(), ++spyCount);
|
QCOMPARE(spyModified.count(), ++spyCount);
|
||||||
QCOMPARE(spyModified2.count(), spyCount2);
|
QCOMPARE(spyModified2.count(), spyCount2);
|
||||||
@ -87,11 +87,11 @@ void TestModified::testSignals()
|
|||||||
QCOMPARE(spyModified.count(), spyCount);
|
QCOMPARE(spyModified.count(), spyCount);
|
||||||
QCOMPARE(spyModified2.count(), ++spyCount2);
|
QCOMPARE(spyModified2.count(), ++spyCount2);
|
||||||
|
|
||||||
Group* group3 = new Group();
|
auto* group3 = new Group();
|
||||||
group3->setParent(root);
|
group3->setParent(root);
|
||||||
QCOMPARE(spyModified.count(), ++spyCount);
|
QCOMPARE(spyModified.count(), ++spyCount);
|
||||||
|
|
||||||
Group* group4 = new Group();
|
auto* group4 = new Group();
|
||||||
group4->setParent(group3);
|
group4->setParent(group3);
|
||||||
QCOMPARE(spyModified.count(), ++spyCount);
|
QCOMPARE(spyModified.count(), ++spyCount);
|
||||||
|
|
||||||
@ -103,21 +103,18 @@ void TestModified::testSignals()
|
|||||||
|
|
||||||
QCOMPARE(spyModified.count(), spyCount);
|
QCOMPARE(spyModified.count(), spyCount);
|
||||||
QCOMPARE(spyModified2.count(), spyCount2);
|
QCOMPARE(spyModified2.count(), spyCount2);
|
||||||
|
|
||||||
delete db;
|
|
||||||
delete db2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestModified::testGroupSets()
|
void TestModified::testGroupSets()
|
||||||
{
|
{
|
||||||
int spyCount = 0;
|
int spyCount = 0;
|
||||||
Database* db = new Database();
|
QScopedPointer<Database> db(new Database());
|
||||||
Group* root = db->rootGroup();
|
auto* root = db->rootGroup();
|
||||||
|
|
||||||
Group* group = new Group();
|
auto* group = new Group();
|
||||||
group->setParent(root);
|
group->setParent(root);
|
||||||
|
|
||||||
QSignalSpy spyModified(db, SIGNAL(modifiedImmediate()));
|
QSignalSpy spyModified(db.data(), SIGNAL(modifiedImmediate()));
|
||||||
|
|
||||||
root->setUuid(Uuid::random());
|
root->setUuid(Uuid::random());
|
||||||
QCOMPARE(spyModified.count(), ++spyCount);
|
QCOMPARE(spyModified.count(), ++spyCount);
|
||||||
@ -169,22 +166,20 @@ void TestModified::testGroupSets()
|
|||||||
QCOMPARE(spyModified.count(), ++spyCount);
|
QCOMPARE(spyModified.count(), ++spyCount);
|
||||||
group->setIcon(group->iconUuid());
|
group->setIcon(group->iconUuid());
|
||||||
QCOMPARE(spyModified.count(), spyCount);
|
QCOMPARE(spyModified.count(), spyCount);
|
||||||
|
|
||||||
delete db;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestModified::testEntrySets()
|
void TestModified::testEntrySets()
|
||||||
{
|
{
|
||||||
int spyCount = 0;
|
int spyCount = 0;
|
||||||
Database* db = new Database();
|
QScopedPointer<Database> db(new Database());
|
||||||
Group* root = db->rootGroup();
|
auto* root = db->rootGroup();
|
||||||
|
|
||||||
Group* group = new Group();
|
auto* group = new Group();
|
||||||
group->setParent(root);
|
group->setParent(root);
|
||||||
Entry* entry = new Entry();
|
auto* entry = new Entry();
|
||||||
entry->setGroup(group);
|
entry->setGroup(group);
|
||||||
|
|
||||||
QSignalSpy spyModified(db, SIGNAL(modifiedImmediate()));
|
QSignalSpy spyModified(db.data(), SIGNAL(modifiedImmediate()));
|
||||||
|
|
||||||
entry->setUuid(Uuid::random());
|
entry->setUuid(Uuid::random());
|
||||||
QCOMPARE(spyModified.count(), ++spyCount);
|
QCOMPARE(spyModified.count(), ++spyCount);
|
||||||
@ -284,8 +279,6 @@ void TestModified::testEntrySets()
|
|||||||
QCOMPARE(spyModified.count(), ++spyCount);
|
QCOMPARE(spyModified.count(), ++spyCount);
|
||||||
entry->attributes()->set("test key2", entry->attributes()->value("test key2"), true);
|
entry->attributes()->set("test key2", entry->attributes()->value("test key2"), true);
|
||||||
QCOMPARE(spyModified.count(), spyCount);
|
QCOMPARE(spyModified.count(), spyCount);
|
||||||
|
|
||||||
delete db;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestModified::testHistoryItems()
|
void TestModified::testHistoryItems()
|
||||||
@ -313,7 +306,7 @@ void TestModified::testHistoryItems()
|
|||||||
entry->setTitle("b");
|
entry->setTitle("b");
|
||||||
entry->endUpdate();
|
entry->endUpdate();
|
||||||
QCOMPARE(entry->historyItems().size(), ++historyItemsSize);
|
QCOMPARE(entry->historyItems().size(), ++historyItemsSize);
|
||||||
Entry *historyEntry = entry->historyItems().at(historyItemsSize - 1);
|
auto *historyEntry = entry->historyItems().at(historyItemsSize - 1);
|
||||||
QCOMPARE(historyEntry->title(), QString("a"));
|
QCOMPARE(historyEntry->title(), QString("a"));
|
||||||
QCOMPARE(historyEntry->uuid(), entry->uuid());
|
QCOMPARE(historyEntry->uuid(), entry->uuid());
|
||||||
QCOMPARE(historyEntry->tags(), entry->tags());
|
QCOMPARE(historyEntry->tags(), entry->tags());
|
||||||
@ -342,12 +335,11 @@ void TestModified::testHistoryItems()
|
|||||||
QVERIFY(!entry->historyItems().at(historyItemsSize - 1)->attributes()->keys().contains("k"));
|
QVERIFY(!entry->historyItems().at(historyItemsSize - 1)->attributes()->keys().contains("k"));
|
||||||
|
|
||||||
QScopedPointer<Database> db(new Database());
|
QScopedPointer<Database> db(new Database());
|
||||||
Group* root = db->rootGroup();
|
auto* root = db->rootGroup();
|
||||||
db->metadata()->setHistoryMaxItems(3);
|
db->metadata()->setHistoryMaxItems(3);
|
||||||
db->metadata()->setHistoryMaxSize(-1);
|
db->metadata()->setHistoryMaxSize(-1);
|
||||||
|
|
||||||
Entry* historyEntry2;
|
auto* entry2 = new Entry();
|
||||||
Entry* entry2 = new Entry();
|
|
||||||
entry2->setGroup(root);
|
entry2->setGroup(root);
|
||||||
entry2->beginUpdate();
|
entry2->beginUpdate();
|
||||||
entry2->setTitle("1");
|
entry2->setTitle("1");
|
||||||
@ -373,7 +365,7 @@ void TestModified::testHistoryItems()
|
|||||||
entry2->endUpdate();
|
entry2->endUpdate();
|
||||||
QCOMPARE(entry2->historyItems().size(), 1);
|
QCOMPARE(entry2->historyItems().size(), 1);
|
||||||
|
|
||||||
historyEntry2 = entry2->historyItems().at(0);
|
auto* historyEntry2 = entry2->historyItems().at(0);
|
||||||
QCOMPARE(historyEntry2->title(), QString("4"));
|
QCOMPARE(historyEntry2->title(), QString("4"));
|
||||||
|
|
||||||
db->metadata()->setHistoryMaxItems(-1);
|
db->metadata()->setHistoryMaxItems(-1);
|
||||||
@ -451,7 +443,6 @@ void TestModified::testHistoryMaxSize()
|
|||||||
QScopedPointer<Database> db(new Database());
|
QScopedPointer<Database> db(new Database());
|
||||||
const QString key("test");
|
const QString key("test");
|
||||||
|
|
||||||
|
|
||||||
auto entry1 = new Entry();
|
auto entry1 = new Entry();
|
||||||
entry1->setGroup(db->rootGroup());
|
entry1->setGroup(db->rootGroup());
|
||||||
QCOMPARE(entry1->historyItems().size(), 0);
|
QCOMPARE(entry1->historyItems().size(), 0);
|
||||||
@ -584,15 +575,15 @@ void TestModified::testHistoryMaxSize()
|
|||||||
void TestModified::testCustomData()
|
void TestModified::testCustomData()
|
||||||
{
|
{
|
||||||
int spyCount = 0;
|
int spyCount = 0;
|
||||||
Database* db = new Database();
|
QScopedPointer<Database> db(new Database());
|
||||||
Group* root = db->rootGroup();
|
auto* root = db->rootGroup();
|
||||||
|
|
||||||
Group* group = new Group();
|
auto* group = new Group();
|
||||||
group->setParent(root);
|
group->setParent(root);
|
||||||
Entry* entry = new Entry();
|
auto* entry = new Entry();
|
||||||
entry->setGroup(group);
|
entry->setGroup(group);
|
||||||
|
|
||||||
QSignalSpy spyModified(db, SIGNAL(modifiedImmediate()));
|
QSignalSpy spyModified(db.data(), SIGNAL(modifiedImmediate()));
|
||||||
|
|
||||||
db->metadata()->customData()->set("Key", "Value");
|
db->metadata()->customData()->set("Key", "Value");
|
||||||
QCOMPARE(spyModified.count(), ++spyCount);
|
QCOMPARE(spyModified.count(), ++spyCount);
|
||||||
@ -608,6 +599,4 @@ void TestModified::testCustomData()
|
|||||||
QCOMPARE(spyModified.count(), ++spyCount);
|
QCOMPARE(spyModified.count(), ++spyCount);
|
||||||
group->customData()->set("Key", "Value");
|
group->customData()->set("Key", "Value");
|
||||||
QCOMPARE(spyModified.count(), spyCount);
|
QCOMPARE(spyModified.count(), spyCount);
|
||||||
|
|
||||||
delete db;
|
|
||||||
}
|
}
|
||||||
|
@ -39,16 +39,6 @@
|
|||||||
<Binary ID="0" Compressed="True">H4sIAAAAAAAEAO29B2AcSZYlJi9tynt/SvVK1+B0oQiAYBMk2JBAEOzBiM3mkuwdaUcjKasqgcplVmVdZhZAzO2dvPfee++999577733ujudTif33/8/XGZkAWz2zkrayZ4hgKrIHz9+fB8/InZ29+7t3//0wcHD/wfGx4SmCgAAAA==</Binary>
|
<Binary ID="0" Compressed="True">H4sIAAAAAAAEAO29B2AcSZYlJi9tynt/SvVK1+B0oQiAYBMk2JBAEOzBiM3mkuwdaUcjKasqgcplVmVdZhZAzO2dvPfee++999577733ujudTif33/8/XGZkAWz2zkrayZ4hgKrIHz9+fB8/InZ29+7t3//0wcHD/wfGx4SmCgAAAA==</Binary>
|
||||||
<Binary ID="1" Compressed="True">H4sIAAAAAAAEAO29B2AcSZYlJi9tynt/SvVK1+B0oQiAYBMk2JBAEOzBiM3mkuwdaUcjKasqgcplVmVdZhZAzO2dvPfee++999577733ujudTif33/8/XGZkAWz2zkrayZ4hgKrIHz9+fB8/IrLJdJafX8yLn377/wCfD1fOCwAAAA==</Binary>
|
<Binary ID="1" Compressed="True">H4sIAAAAAAAEAO29B2AcSZYlJi9tynt/SvVK1+B0oQiAYBMk2JBAEOzBiM3mkuwdaUcjKasqgcplVmVdZhZAzO2dvPfee++999577733ujudTif33/8/XGZkAWz2zkrayZ4hgKrIHz9+fB8/IrLJdJafX8yLn377/wCfD1fOCwAAAA==</Binary>
|
||||||
</Binaries>
|
</Binaries>
|
||||||
<CustomData>
|
|
||||||
<Item>
|
|
||||||
<Key>A Sample Test Key</Key>
|
|
||||||
<Value>valu</Value>
|
|
||||||
</Item>
|
|
||||||
<Item>
|
|
||||||
<Key>custom key</Key>
|
|
||||||
<Value>blub</Value>
|
|
||||||
</Item>
|
|
||||||
</CustomData>
|
|
||||||
</Meta>
|
</Meta>
|
||||||
<Root>
|
<Root>
|
||||||
<Group>
|
<Group>
|
||||||
@ -479,4 +469,4 @@
|
|||||||
</DeletedObject>
|
</DeletedObject>
|
||||||
</DeletedObjects>
|
</DeletedObjects>
|
||||||
</Root>
|
</Root>
|
||||||
</KeePassFile>
|
</KeePassFile>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user