mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-12-27 00:09:53 -05:00
Merge branch 'release/2.3.0' into develop
This commit is contained in:
commit
148dbc5d41
4
COPYING
4
COPYING
@ -223,8 +223,8 @@ Copyright: 2009-2010, Iowa State University
|
||||
License: Boost-1.0
|
||||
|
||||
Files: src/zxcvbn/zxcvbn.*
|
||||
Copyright: 2015, Tony Evans
|
||||
License: BSD 3-clause
|
||||
Copyright: 2015-2017, Tony Evans
|
||||
License: MIT
|
||||
|
||||
Files: src/http/qhttp/*
|
||||
Copyright: 2014, Amir Zamani
|
||||
|
@ -29,7 +29,7 @@ endif()
|
||||
|
||||
find_library(ZXCVBN_LIBRARIES zxcvbn)
|
||||
if(NOT ZXCVBN_LIBRARIES)
|
||||
add_library(zxcvbn STATIC zxcvbn/zxcvbn.cpp)
|
||||
add_library(zxcvbn STATIC zxcvbn/zxcvbn.c)
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/zxcvbn)
|
||||
set(ZXCVBN_LIBRARIES zxcvbn)
|
||||
endif(NOT ZXCVBN_LIBRARIES)
|
||||
@ -42,6 +42,7 @@ set(keepassx_SOURCES
|
||||
core/AutoTypeMatch.cpp
|
||||
core/Config.cpp
|
||||
core/CsvParser.cpp
|
||||
core/CustomData.cpp
|
||||
core/Database.cpp
|
||||
core/DatabaseIcons.cpp
|
||||
core/Entry.cpp
|
||||
|
152
src/core/CustomData.cpp
Normal file
152
src/core/CustomData.cpp
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "CustomData.h"
|
||||
|
||||
CustomData::CustomData(QObject* parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
QList<QString> CustomData::keys() const
|
||||
{
|
||||
return m_data.keys();
|
||||
}
|
||||
|
||||
bool CustomData::hasKey(const QString& key) const
|
||||
{
|
||||
return m_data.contains(key);
|
||||
}
|
||||
|
||||
QString CustomData::value(const QString& key) const
|
||||
{
|
||||
return m_data.value(key);
|
||||
}
|
||||
|
||||
bool CustomData::contains(const QString& key) const
|
||||
{
|
||||
return m_data.contains(key);
|
||||
}
|
||||
|
||||
bool CustomData::containsValue(const QString& value) const
|
||||
{
|
||||
return m_data.values().contains(value);
|
||||
}
|
||||
|
||||
void CustomData::set(const QString& key, const QString& value)
|
||||
{
|
||||
bool addAttribute = !m_data.contains(key);
|
||||
bool changeValue = !addAttribute && (m_data.value(key) != value);
|
||||
|
||||
if (addAttribute) {
|
||||
emit aboutToBeAdded(key);
|
||||
}
|
||||
|
||||
if (addAttribute || changeValue) {
|
||||
m_data.insert(key, value);
|
||||
emit modified();
|
||||
}
|
||||
|
||||
if (addAttribute) {
|
||||
emit added(key);
|
||||
}
|
||||
}
|
||||
|
||||
void CustomData::remove(const QString& key)
|
||||
{
|
||||
emit aboutToBeRemoved(key);
|
||||
|
||||
m_data.remove(key);
|
||||
|
||||
emit removed(key);
|
||||
emit modified();
|
||||
}
|
||||
|
||||
void CustomData::rename(const QString& oldKey, const QString& newKey)
|
||||
{
|
||||
const bool containsOldKey = m_data.contains(oldKey);
|
||||
const bool containsNewKey = m_data.contains(newKey);
|
||||
Q_ASSERT(containsOldKey && !containsNewKey);
|
||||
if (!containsOldKey || containsNewKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString data = value(oldKey);
|
||||
|
||||
emit aboutToRename(oldKey, newKey);
|
||||
|
||||
m_data.remove(oldKey);
|
||||
m_data.insert(newKey, data);
|
||||
|
||||
emit modified();
|
||||
emit renamed(oldKey, newKey);
|
||||
}
|
||||
|
||||
void CustomData::copyDataFrom(const CustomData* other)
|
||||
{
|
||||
if (*this == *other) {
|
||||
return;
|
||||
}
|
||||
|
||||
emit aboutToBeReset();
|
||||
|
||||
m_data = other->m_data;
|
||||
|
||||
emit reset();
|
||||
emit modified();
|
||||
}
|
||||
bool CustomData::operator==(const CustomData& other) const
|
||||
{
|
||||
return (m_data == other.m_data);
|
||||
}
|
||||
|
||||
bool CustomData::operator!=(const CustomData& other) const
|
||||
{
|
||||
return (m_data != other.m_data);
|
||||
}
|
||||
|
||||
void CustomData::clear()
|
||||
{
|
||||
emit aboutToBeReset();
|
||||
|
||||
m_data.clear();
|
||||
|
||||
emit reset();
|
||||
emit modified();
|
||||
}
|
||||
|
||||
bool CustomData::isEmpty() const
|
||||
{
|
||||
return m_data.isEmpty();
|
||||
}
|
||||
|
||||
int CustomData::size() const
|
||||
{
|
||||
return m_data.size();
|
||||
}
|
||||
|
||||
int CustomData::dataSize() const
|
||||
{
|
||||
int size = 0;
|
||||
|
||||
QHashIterator<QString, QString> i(m_data);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
size += i.key().toUtf8().size() + i.value().toUtf8().size();
|
||||
}
|
||||
return size;
|
||||
}
|
64
src/core/CustomData.h
Normal file
64
src/core/CustomData.h
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSXC_CUSTOMDATA_H
|
||||
#define KEEPASSXC_CUSTOMDATA_H
|
||||
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
#include <QSet>
|
||||
#include <QStringList>
|
||||
|
||||
class CustomData : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CustomData(QObject* parent = nullptr);
|
||||
QList<QString> keys() const;
|
||||
bool hasKey(const QString& key) const;
|
||||
QString value(const QString& key) const;
|
||||
bool contains(const QString& key) const;
|
||||
bool containsValue(const QString& value) const;
|
||||
void set(const QString& key, const QString& value);
|
||||
void remove(const QString& key);
|
||||
void rename(const QString& oldKey, const QString& newKey);
|
||||
void clear();
|
||||
bool isEmpty() const;
|
||||
int size() const;
|
||||
int dataSize() const;
|
||||
void copyDataFrom(const CustomData* other);
|
||||
bool operator==(const CustomData& other) const;
|
||||
bool operator!=(const CustomData& other) const;
|
||||
|
||||
|
||||
signals:
|
||||
void modified();
|
||||
void aboutToBeAdded(const QString& key);
|
||||
void added(const QString& key);
|
||||
void aboutToBeRemoved(const QString& key);
|
||||
void removed(const QString& key);
|
||||
void aboutToRename(const QString& oldKey, const QString& newKey);
|
||||
void renamed(const QString& oldKey, const QString& newKey);
|
||||
void aboutToBeReset();
|
||||
void reset();
|
||||
|
||||
private:
|
||||
QHash<QString, QString> m_data;
|
||||
};
|
||||
|
||||
#endif // KEEPASSXC_CUSTOMDATA_H
|
@ -320,6 +320,17 @@ bool Database::verifyKey(const CompositeKey& key) const
|
||||
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()
|
||||
{
|
||||
Group* recycleBin = Group::createRecycleBin();
|
||||
@ -578,14 +589,6 @@ void Database::setKdf(QSharedPointer<Kdf> 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)
|
||||
{
|
||||
kdf->randomizeSeed();
|
||||
|
@ -59,12 +59,12 @@ public:
|
||||
Uuid cipher;
|
||||
CompressionAlgorithm compressionAlgo;
|
||||
QByteArray transformedMasterKey;
|
||||
QByteArray publicCustomData;
|
||||
QSharedPointer<Kdf> kdf;
|
||||
CompositeKey key;
|
||||
bool hasKey;
|
||||
QByteArray masterSeed;
|
||||
QByteArray challengeResponseKey;
|
||||
QVariantMap publicCustomData;
|
||||
};
|
||||
|
||||
Database();
|
||||
@ -93,7 +93,6 @@ public:
|
||||
Uuid cipher() const;
|
||||
Database::CompressionAlgorithm compressionAlgo() const;
|
||||
QSharedPointer<Kdf> kdf() const;
|
||||
QByteArray publicCustomData() const;
|
||||
QByteArray transformedMasterKey() const;
|
||||
const CompositeKey& key() const;
|
||||
QByteArray challengeResponseKey() const;
|
||||
@ -102,11 +101,12 @@ public:
|
||||
void setCipher(const Uuid& cipher);
|
||||
void setCompressionAlgo(Database::CompressionAlgorithm algo);
|
||||
void setKdf(QSharedPointer<Kdf> kdf);
|
||||
void setPublicCustomData(QByteArray data);
|
||||
bool setKey(const CompositeKey& key, bool updateChangedTime = true,
|
||||
bool updateTransformSalt = false);
|
||||
bool hasKey() const;
|
||||
bool verifyKey(const CompositeKey& key) const;
|
||||
QVariantMap publicCustomData() const;
|
||||
void setPublicCustomData(const QVariantMap& customData);
|
||||
void recycleEntry(Entry* entry);
|
||||
void recycleGroup(Group* group);
|
||||
void emptyRecycleBin();
|
||||
|
@ -37,6 +37,7 @@ Entry::Entry()
|
||||
: m_attributes(new EntryAttributes(this))
|
||||
, m_attachments(new EntryAttachments(this))
|
||||
, m_autoTypeAssociations(new AutoTypeAssociations(this))
|
||||
, m_customData(new CustomData(this))
|
||||
, m_tmpHistoryItem(nullptr)
|
||||
, m_modifiedSinceBegin(false)
|
||||
, m_updateTimeinfo(true)
|
||||
@ -52,6 +53,7 @@ Entry::Entry()
|
||||
connect(m_attributes, SIGNAL(defaultKeyModified()), SLOT(emitDataChanged()));
|
||||
connect(m_attachments, SIGNAL(modified()), this, SIGNAL(modified()));
|
||||
connect(m_autoTypeAssociations, SIGNAL(modified()), SIGNAL(modified()));
|
||||
connect(m_customData, SIGNAL(modified()), this, SIGNAL(modified()));
|
||||
|
||||
connect(this, SIGNAL(modified()), SLOT(updateTimeinfo()));
|
||||
connect(this, SIGNAL(modified()), SLOT(updateModifiedSinceBegin()));
|
||||
@ -340,6 +342,16 @@ const EntryAttachments* Entry::attachments() const
|
||||
return m_attachments;
|
||||
}
|
||||
|
||||
CustomData* Entry::customData()
|
||||
{
|
||||
return m_customData;
|
||||
}
|
||||
|
||||
const CustomData* Entry::customData() const
|
||||
{
|
||||
return m_customData;
|
||||
}
|
||||
|
||||
bool Entry::hasTotp() const
|
||||
{
|
||||
return m_attributes->hasKey("TOTP Seed") || m_attributes->hasKey("otp");
|
||||
@ -606,6 +618,7 @@ void Entry::truncateHistory()
|
||||
size += historyItem->attributes()->attributesSize();
|
||||
size += historyItem->autoTypeAssociations()->associationsSize();
|
||||
size += historyItem->attachments()->attachmentsSize();
|
||||
size += historyItem->customData()->dataSize();
|
||||
const QStringList tags = historyItem->tags().split(delimiter, QString::SkipEmptyParts);
|
||||
for (const QString& tag : tags) {
|
||||
size += tag.toUtf8().size();
|
||||
@ -632,6 +645,7 @@ Entry* Entry::clone(CloneFlags flags) const
|
||||
entry->m_uuid = m_uuid;
|
||||
}
|
||||
entry->m_data = m_data;
|
||||
entry->m_customData->copyDataFrom(m_customData);
|
||||
entry->m_attributes->copyDataFrom(m_attributes);
|
||||
entry->m_attachments->copyDataFrom(m_attachments);
|
||||
|
||||
@ -676,6 +690,7 @@ void Entry::copyDataFrom(const Entry* other)
|
||||
{
|
||||
setUpdateTimeinfo(false);
|
||||
m_data = other->m_data;
|
||||
m_customData->copyDataFrom(other->m_customData);
|
||||
m_attributes->copyDataFrom(other->m_attributes);
|
||||
m_attachments->copyDataFrom(other->m_attachments);
|
||||
m_autoTypeAssociations->copyDataFrom(other->m_autoTypeAssociations);
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <QUrl>
|
||||
|
||||
#include "core/AutoTypeAssociations.h"
|
||||
#include "core/CustomData.h"
|
||||
#include "core/EntryAttachments.h"
|
||||
#include "core/EntryAttributes.h"
|
||||
#include "core/TimeInfo.h"
|
||||
@ -107,6 +108,8 @@ public:
|
||||
const EntryAttributes* attributes() const;
|
||||
EntryAttachments* attachments();
|
||||
const EntryAttachments* attachments() const;
|
||||
CustomData* customData();
|
||||
const CustomData* customData() const;
|
||||
|
||||
static const int DefaultIconNumber;
|
||||
static const int ResolveMaximumDepth;
|
||||
@ -228,9 +231,10 @@ private:
|
||||
|
||||
Uuid m_uuid;
|
||||
EntryData m_data;
|
||||
EntryAttributes* const m_attributes;
|
||||
EntryAttachments* const m_attachments;
|
||||
AutoTypeAssociations* const m_autoTypeAssociations;
|
||||
QPointer<EntryAttributes> m_attributes;
|
||||
QPointer<EntryAttachments> m_attachments;
|
||||
QPointer<AutoTypeAssociations> m_autoTypeAssociations;
|
||||
QPointer<CustomData> m_customData;
|
||||
|
||||
QList<Entry*> m_history;
|
||||
Entry* m_tmpHistoryItem;
|
||||
|
@ -33,13 +33,17 @@ Entry::CloneFlags Group::DefaultEntryCloneFlags = static_cast<Entry::CloneFlags>
|
||||
Entry::CloneNewUuid | Entry::CloneResetTimeInfo);
|
||||
|
||||
Group::Group()
|
||||
: m_updateTimeinfo(true)
|
||||
: m_customData(new CustomData(this))
|
||||
, m_updateTimeinfo(true)
|
||||
{
|
||||
m_data.iconNumber = DefaultIconNumber;
|
||||
m_data.isExpanded = true;
|
||||
m_data.autoTypeEnabled = Inherit;
|
||||
m_data.searchingEnabled = Inherit;
|
||||
m_data.mergeMode = ModeInherit;
|
||||
|
||||
connect(m_customData, SIGNAL(modified()), this, SIGNAL(modified()));
|
||||
connect(this, SIGNAL(modified()), SLOT(updateTimeinfo()));
|
||||
}
|
||||
|
||||
Group::~Group()
|
||||
@ -80,7 +84,6 @@ Group* Group::createRecycleBin()
|
||||
template <class P, class V> inline bool Group::set(P& property, const V& value) {
|
||||
if (property != value) {
|
||||
property = value;
|
||||
updateTimeinfo();
|
||||
emit modified();
|
||||
return true;
|
||||
} else {
|
||||
@ -251,6 +254,16 @@ bool Group::isExpired() const
|
||||
return m_data.timeInfo.expires() && m_data.timeInfo.expiryTime() < QDateTime::currentDateTimeUtc();
|
||||
}
|
||||
|
||||
CustomData* Group::customData()
|
||||
{
|
||||
return m_customData;
|
||||
}
|
||||
|
||||
const CustomData* Group::customData() const
|
||||
{
|
||||
return m_customData;
|
||||
}
|
||||
|
||||
void Group::setUuid(const Uuid& uuid)
|
||||
{
|
||||
set(m_uuid, uuid);
|
||||
@ -275,8 +288,6 @@ void Group::setIcon(int iconNumber)
|
||||
if (m_data.iconNumber != iconNumber || !m_data.customIcon.isNull()) {
|
||||
m_data.iconNumber = iconNumber;
|
||||
m_data.customIcon = Uuid();
|
||||
|
||||
updateTimeinfo();
|
||||
emit modified();
|
||||
emit dataChanged(this);
|
||||
}
|
||||
@ -289,8 +300,6 @@ void Group::setIcon(const Uuid& uuid)
|
||||
if (m_data.customIcon != uuid) {
|
||||
m_data.customIcon = uuid;
|
||||
m_data.iconNumber = 0;
|
||||
|
||||
updateTimeinfo();
|
||||
emit modified();
|
||||
emit dataChanged(this);
|
||||
}
|
||||
@ -305,8 +314,8 @@ void Group::setExpanded(bool expanded)
|
||||
{
|
||||
if (m_data.isExpanded != expanded) {
|
||||
m_data.isExpanded = expanded;
|
||||
updateTimeinfo();
|
||||
if (config()->get("IgnoreGroupExpansion").toBool()) {
|
||||
updateTimeinfo();
|
||||
return;
|
||||
}
|
||||
emit modified();
|
||||
@ -337,7 +346,6 @@ void Group::setExpires(bool value)
|
||||
{
|
||||
if (m_data.timeInfo.expires() != value) {
|
||||
m_data.timeInfo.setExpires(value);
|
||||
updateTimeinfo();
|
||||
emit modified();
|
||||
}
|
||||
}
|
||||
@ -346,7 +354,6 @@ void Group::setExpiryTime(const QDateTime& dateTime)
|
||||
{
|
||||
if (m_data.timeInfo.expiryTime() != dateTime) {
|
||||
m_data.timeInfo.setExpiryTime(dateTime);
|
||||
updateTimeinfo();
|
||||
emit modified();
|
||||
}
|
||||
}
|
||||
@ -768,6 +775,7 @@ Group* Group::clone(Entry::CloneFlags entryFlags, Group::CloneFlags groupFlags)
|
||||
}
|
||||
|
||||
clonedGroup->m_data = m_data;
|
||||
clonedGroup->m_customData->copyDataFrom(m_customData);
|
||||
|
||||
if (groupFlags & Group::CloneIncludeEntries) {
|
||||
const QList<Entry*> entryList = entries();
|
||||
@ -799,6 +807,7 @@ Group* Group::clone(Entry::CloneFlags entryFlags, Group::CloneFlags groupFlags)
|
||||
void Group::copyDataFrom(const Group* other)
|
||||
{
|
||||
m_data = other->m_data;
|
||||
m_customData->copyDataFrom(other->m_customData);
|
||||
m_lastTopVisibleEntry = other->m_lastTopVisibleEntry;
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
|
||||
#include "core/Database.h"
|
||||
#include "core/Entry.h"
|
||||
#include "core/CustomData.h"
|
||||
#include "core/TimeInfo.h"
|
||||
#include "core/Uuid.h"
|
||||
|
||||
@ -83,6 +84,8 @@ public:
|
||||
bool resolveAutoTypeEnabled() const;
|
||||
Entry* lastTopVisibleEntry() const;
|
||||
bool isExpired() const;
|
||||
CustomData* customData();
|
||||
const CustomData* customData() const;
|
||||
|
||||
static const int DefaultIconNumber;
|
||||
static const int RecycleBinIconNumber;
|
||||
@ -164,6 +167,9 @@ signals:
|
||||
|
||||
void modified();
|
||||
|
||||
private slots:
|
||||
void updateTimeinfo();
|
||||
|
||||
private:
|
||||
template <class P, class V> bool set(P& property, const V& value);
|
||||
|
||||
@ -177,7 +183,6 @@ private:
|
||||
void recSetDatabase(Database* db);
|
||||
void cleanupParent();
|
||||
void recCreateDelObjects();
|
||||
void updateTimeinfo();
|
||||
|
||||
QPointer<Database> m_db;
|
||||
Uuid m_uuid;
|
||||
@ -186,6 +191,8 @@ private:
|
||||
QList<Group*> m_children;
|
||||
QList<Entry*> m_entries;
|
||||
|
||||
QPointer<CustomData> m_customData;
|
||||
|
||||
QPointer<Group> m_parent;
|
||||
|
||||
bool m_updateTimeinfo;
|
||||
|
@ -27,6 +27,7 @@ const int Metadata::DefaultHistoryMaxSize = 6 * 1024 * 1024;
|
||||
|
||||
Metadata::Metadata(QObject* parent)
|
||||
: QObject(parent)
|
||||
, m_customData(new CustomData(this))
|
||||
, m_updateDatetime(true)
|
||||
{
|
||||
m_data.generator = "KeePassXC";
|
||||
@ -50,6 +51,8 @@ Metadata::Metadata(QObject* parent)
|
||||
m_entryTemplatesGroupChanged = now;
|
||||
m_masterKeyChanged = now;
|
||||
m_settingsChanged = now;
|
||||
|
||||
connect(m_customData, SIGNAL(modified()), this, SIGNAL(modified()));
|
||||
}
|
||||
|
||||
template <class P, class V> bool Metadata::set(P& property, const V& value)
|
||||
@ -291,9 +294,14 @@ int Metadata::historyMaxSize() const
|
||||
return m_data.historyMaxSize;
|
||||
}
|
||||
|
||||
QHash<QString, QString> Metadata::customFields() const
|
||||
CustomData* Metadata::customData()
|
||||
{
|
||||
return m_customFields;
|
||||
return m_customData;
|
||||
}
|
||||
|
||||
const CustomData* Metadata::customData() const
|
||||
{
|
||||
return m_customData;
|
||||
}
|
||||
|
||||
void Metadata::setGenerator(const QString& value)
|
||||
@ -511,27 +519,13 @@ void Metadata::setHistoryMaxSize(int value)
|
||||
set(m_data.historyMaxSize, value);
|
||||
}
|
||||
|
||||
void Metadata::addCustomField(const QString& key, const QString& value)
|
||||
QDateTime Metadata::settingsChanged() const
|
||||
{
|
||||
Q_ASSERT(!m_customFields.contains(key));
|
||||
|
||||
m_customFields.insert(key, value);
|
||||
emit modified();
|
||||
}
|
||||
|
||||
void Metadata::removeCustomField(const QString& key)
|
||||
{
|
||||
Q_ASSERT(m_customFields.contains(key));
|
||||
|
||||
m_customFields.remove(key);
|
||||
emit modified();
|
||||
}
|
||||
|
||||
QDateTime Metadata::settingsChanged() const {
|
||||
return m_settingsChanged;
|
||||
}
|
||||
|
||||
void Metadata::setSettingsChanged(const QDateTime& value) {
|
||||
void Metadata::setSettingsChanged(const QDateTime& value)
|
||||
{
|
||||
Q_ASSERT(value.timeSpec() == Qt::UTC);
|
||||
m_settingsChanged = value;
|
||||
}
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <QPointer>
|
||||
|
||||
#include "core/Uuid.h"
|
||||
#include "core/CustomData.h"
|
||||
|
||||
class Database;
|
||||
class Group;
|
||||
@ -97,7 +98,8 @@ public:
|
||||
int masterKeyChangeForce() const;
|
||||
int historyMaxItems() const;
|
||||
int historyMaxSize() const;
|
||||
QHash<QString, QString> customFields() const;
|
||||
CustomData* customData();
|
||||
const CustomData* customData() const;
|
||||
|
||||
static const int DefaultHistoryMaxItems;
|
||||
static const int DefaultHistoryMaxSize;
|
||||
@ -134,8 +136,6 @@ public:
|
||||
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);
|
||||
/*
|
||||
* Copy all attributes from other except:
|
||||
@ -175,7 +175,7 @@ private:
|
||||
QDateTime m_masterKeyChanged;
|
||||
QDateTime m_settingsChanged;
|
||||
|
||||
QHash<QString, QString> m_customFields;
|
||||
QPointer<CustomData> m_customData;
|
||||
|
||||
bool m_updateDatetime;
|
||||
};
|
||||
|
@ -209,9 +209,14 @@ bool Kdbx4Reader::readHeaderField(StoreDataStream& device)
|
||||
break;
|
||||
}
|
||||
|
||||
case KeePass2::HeaderFieldID::PublicCustomData:
|
||||
m_db->setPublicCustomData(fieldData);
|
||||
case KeePass2::HeaderFieldID::PublicCustomData: {
|
||||
QBuffer variantBuffer;
|
||||
variantBuffer.setBuffer(&fieldData);
|
||||
variantBuffer.open(QBuffer::ReadOnly);
|
||||
QVariantMap data = readVariantMap(&variantBuffer);
|
||||
m_db->setPublicCustomData(data);
|
||||
break;
|
||||
}
|
||||
|
||||
case KeePass2::HeaderFieldID::ProtectedStreamKey:
|
||||
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
|
||||
* @return filled variant map
|
||||
* @return de-serialized variant map
|
||||
*/
|
||||
QVariantMap Kdbx4Reader::readVariantMap(QIODevice* device)
|
||||
{
|
||||
|
@ -22,6 +22,8 @@
|
||||
|
||||
#include "streams/HmacBlockStream.h"
|
||||
#include "core/Database.h"
|
||||
#include "core/Metadata.h"
|
||||
#include "core/CustomData.h"
|
||||
#include "crypto/CryptoHash.h"
|
||||
#include "crypto/Random.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"));
|
||||
return false;
|
||||
}
|
||||
QByteArray publicCustomData = db->publicCustomData();
|
||||
|
||||
CHECK_RETURN_FALSE(writeHeaderField<quint32>(&header, KeePass2::HeaderFieldID::KdfParameters, kdfParamBytes));
|
||||
QVariantMap publicCustomData = db->publicCustomData();
|
||||
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));
|
||||
@ -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 outputBytes output byte array
|
||||
|
@ -295,7 +295,7 @@ void KdbxXmlReader::parseMeta()
|
||||
} else if (m_xml.name() == "Binaries") {
|
||||
parseBinaries();
|
||||
} else if (m_xml.name() == "CustomData") {
|
||||
parseCustomData();
|
||||
parseCustomData(m_meta->customData());
|
||||
} else if (m_xml.name() == "SettingsChanged") {
|
||||
m_meta->setSettingsChanged(readDateTime());
|
||||
} else {
|
||||
@ -397,20 +397,20 @@ void KdbxXmlReader::parseBinaries()
|
||||
}
|
||||
}
|
||||
|
||||
void KdbxXmlReader::parseCustomData()
|
||||
void KdbxXmlReader::parseCustomData(CustomData* customData)
|
||||
{
|
||||
Q_ASSERT(m_xml.isStartElement() && m_xml.name() == "CustomData");
|
||||
|
||||
while (!m_xml.hasError() && m_xml.readNextStartElement()) {
|
||||
if (m_xml.name() == "Item") {
|
||||
parseCustomDataItem();
|
||||
parseCustomDataItem(customData);
|
||||
continue;
|
||||
}
|
||||
skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
void KdbxXmlReader::parseCustomDataItem()
|
||||
void KdbxXmlReader::parseCustomDataItem(CustomData* customData)
|
||||
{
|
||||
Q_ASSERT(m_xml.isStartElement() && m_xml.name() == "Item");
|
||||
|
||||
@ -432,7 +432,7 @@ void KdbxXmlReader::parseCustomDataItem()
|
||||
}
|
||||
|
||||
if (keySet && valueSet) {
|
||||
m_meta->addCustomField(key, value);
|
||||
customData->set(key, value);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -583,6 +583,10 @@ Group* KdbxXmlReader::parseGroup()
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (m_xml.name() == "CustomData") {
|
||||
parseCustomData(group->customData());
|
||||
continue;
|
||||
}
|
||||
|
||||
skipCurrentElement();
|
||||
}
|
||||
@ -744,7 +748,10 @@ Entry* KdbxXmlReader::parseEntry(bool history)
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_xml.name() == "CustomData") {
|
||||
parseCustomData(entry->customData());
|
||||
continue;
|
||||
}
|
||||
skipCurrentElement();
|
||||
}
|
||||
|
||||
|
@ -66,8 +66,8 @@ protected:
|
||||
virtual void parseCustomIcons();
|
||||
virtual void parseIcon();
|
||||
virtual void parseBinaries();
|
||||
virtual void parseCustomData();
|
||||
virtual void parseCustomDataItem();
|
||||
virtual void parseCustomData(CustomData* customData);
|
||||
virtual void parseCustomDataItem(CustomData* customData);
|
||||
virtual bool parseRoot();
|
||||
virtual Group* parseGroup();
|
||||
virtual void parseDeletedObjects();
|
||||
|
@ -129,7 +129,9 @@ void KdbxXmlWriter::writeMetadata()
|
||||
if (m_kdbxVersion < KeePass2::FILE_VERSION_4) {
|
||||
writeBinaries();
|
||||
}
|
||||
writeCustomData();
|
||||
if (m_kdbxVersion >= KeePass2::FILE_VERSION_4) {
|
||||
writeCustomData(m_meta->customData());
|
||||
}
|
||||
|
||||
m_xml.writeEndElement();
|
||||
}
|
||||
@ -218,14 +220,16 @@ void KdbxXmlWriter::writeBinaries()
|
||||
m_xml.writeEndElement();
|
||||
}
|
||||
|
||||
void KdbxXmlWriter::writeCustomData()
|
||||
void KdbxXmlWriter::writeCustomData(const CustomData* customData)
|
||||
{
|
||||
if (customData->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
m_xml.writeStartElement("CustomData");
|
||||
|
||||
QHash<QString, QString> customFields = m_meta->customFields();
|
||||
const QList<QString> keyList = customFields.keys();
|
||||
const QList<QString> keyList = customData->keys();
|
||||
for (const QString& key : keyList) {
|
||||
writeCustomDataItem(key, customFields.value(key));
|
||||
writeCustomDataItem(key, customData->value(key));
|
||||
}
|
||||
|
||||
m_xml.writeEndElement();
|
||||
@ -277,6 +281,10 @@ void KdbxXmlWriter::writeGroup(const Group* group)
|
||||
|
||||
writeUuid("LastTopVisibleEntry", group->lastTopVisibleEntry());
|
||||
|
||||
if (m_kdbxVersion >= KeePass2::FILE_VERSION_4) {
|
||||
writeCustomData(group->customData());
|
||||
}
|
||||
|
||||
const QList<Entry*>& entryList = group->entries();
|
||||
for (const Entry* entry : entryList) {
|
||||
writeEntry(entry);
|
||||
@ -401,6 +409,11 @@ void KdbxXmlWriter::writeEntry(const Entry* entry)
|
||||
}
|
||||
|
||||
writeAutoType(entry);
|
||||
|
||||
if (m_kdbxVersion >= KeePass2::FILE_VERSION_4) {
|
||||
writeCustomData(entry->customData());
|
||||
}
|
||||
|
||||
// write history only for entries that are not history items
|
||||
if (entry->parent()) {
|
||||
writeEntryHistory(entry);
|
||||
|
@ -51,7 +51,7 @@ private:
|
||||
void writeCustomIcons();
|
||||
void writeIcon(const Uuid& uuid, const QImage& icon);
|
||||
void writeBinaries();
|
||||
void writeCustomData();
|
||||
void writeCustomData(const CustomData* customData);
|
||||
void writeCustomDataItem(const QString& key, const QString& value);
|
||||
void writeRoot();
|
||||
void writeGroup(const Group* group);
|
||||
|
@ -19,6 +19,8 @@
|
||||
#include <QFile>
|
||||
|
||||
#include "core/Database.h"
|
||||
#include "core/Group.h"
|
||||
#include "core/Metadata.h"
|
||||
#include "crypto/kdf/AesKdf.h"
|
||||
#include "format/KeePass2Writer.h"
|
||||
#include "format/Kdbx3Writer.h"
|
||||
@ -48,12 +50,24 @@ bool KeePass2Writer::writeDatabase(const QString& filename, Database* db)
|
||||
* @param db source database
|
||||
* @return true on success
|
||||
*/
|
||||
|
||||
bool KeePass2Writer::writeDatabase(QIODevice* device, Database* db) {
|
||||
m_error = false;
|
||||
m_errorStr.clear();
|
||||
|
||||
// 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_writer.reset(new Kdbx3Writer());
|
||||
} else {
|
||||
|
@ -17,21 +17,30 @@
|
||||
|
||||
#include "EditWidgetProperties.h"
|
||||
#include "ui_EditWidgetProperties.h"
|
||||
#include "MessageBox.h"
|
||||
|
||||
EditWidgetProperties::EditWidgetProperties(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, m_ui(new Ui::EditWidgetProperties())
|
||||
, m_customData(new CustomData(this))
|
||||
, m_customDataModel(new QStandardItemModel(this))
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
m_ui->removeCustomDataButton->setEnabled(false);
|
||||
m_ui->customDataTable->setModel(m_customDataModel);
|
||||
|
||||
connect(m_ui->customDataTable->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
|
||||
SLOT(toggleRemoveButton(QItemSelection)));
|
||||
connect(m_ui->removeCustomDataButton, SIGNAL(clicked()), SLOT(removeSelectedPluginData()));
|
||||
}
|
||||
|
||||
EditWidgetProperties::~EditWidgetProperties()
|
||||
{
|
||||
}
|
||||
|
||||
void EditWidgetProperties::setFields(TimeInfo timeInfo, Uuid uuid)
|
||||
void EditWidgetProperties::setFields(const TimeInfo& timeInfo, const Uuid& uuid)
|
||||
{
|
||||
QString timeFormat("d MMM yyyy HH:mm:ss");
|
||||
static const QString timeFormat("d MMM yyyy HH:mm:ss");
|
||||
m_ui->modifiedEdit->setText(
|
||||
timeInfo.lastModificationTime().toLocalTime().toString(timeFormat));
|
||||
m_ui->createdEdit->setText(
|
||||
@ -40,3 +49,56 @@ void EditWidgetProperties::setFields(TimeInfo timeInfo, Uuid uuid)
|
||||
timeInfo.lastAccessTime().toLocalTime().toString(timeFormat));
|
||||
m_ui->uuidEdit->setText(uuid.toHex());
|
||||
}
|
||||
|
||||
void EditWidgetProperties::setCustomData(const CustomData* customData)
|
||||
{
|
||||
Q_ASSERT(customData);
|
||||
m_customData->copyDataFrom(customData);
|
||||
|
||||
updateModel();
|
||||
}
|
||||
|
||||
const CustomData* EditWidgetProperties::customData() const
|
||||
{
|
||||
return m_customData;
|
||||
}
|
||||
|
||||
void EditWidgetProperties::removeSelectedPluginData()
|
||||
{
|
||||
if (QMessageBox::Yes != MessageBox::question(this,
|
||||
tr("Delete plugin data?"),
|
||||
tr("Do you really want to delete the selected plugin data?\n"
|
||||
"This may cause the affected plugins to malfunction."),
|
||||
QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QItemSelectionModel* itemSelectionModel = m_ui->customDataTable->selectionModel();
|
||||
if (itemSelectionModel) {
|
||||
for (const QModelIndex& index : itemSelectionModel->selectedRows(0)) {
|
||||
const QString key = index.data().toString();
|
||||
m_customData->remove(key);
|
||||
}
|
||||
updateModel();
|
||||
}
|
||||
}
|
||||
|
||||
void EditWidgetProperties::toggleRemoveButton(const QItemSelection& selected)
|
||||
{
|
||||
m_ui->removeCustomDataButton->setEnabled(!selected.isEmpty());
|
||||
}
|
||||
|
||||
void EditWidgetProperties::updateModel()
|
||||
{
|
||||
m_customDataModel->clear();
|
||||
|
||||
m_customDataModel->setHorizontalHeaderLabels({tr("Key"), tr("Value")});
|
||||
|
||||
for (const QString& key : m_customData->keys()) {
|
||||
m_customDataModel->appendRow(QList<QStandardItem*>()
|
||||
<< new QStandardItem(key)
|
||||
<< new QStandardItem(m_customData->value(key)));
|
||||
}
|
||||
|
||||
m_ui->removeCustomDataButton->setEnabled(false);
|
||||
}
|
||||
|
@ -18,8 +18,12 @@
|
||||
#ifndef KEEPASSX_EDITWIDGETPROPERTIES_H
|
||||
#define KEEPASSX_EDITWIDGETPROPERTIES_H
|
||||
|
||||
#include <QStandardItemModel>
|
||||
#include <QItemSelection>
|
||||
#include <QPointer>
|
||||
#include <QWidget>
|
||||
|
||||
#include "core/CustomData.h"
|
||||
#include "core/TimeInfo.h"
|
||||
#include "core/Uuid.h"
|
||||
|
||||
@ -35,11 +39,22 @@ public:
|
||||
explicit EditWidgetProperties(QWidget* parent = nullptr);
|
||||
~EditWidgetProperties();
|
||||
|
||||
void setFields(TimeInfo timeInfo, Uuid uuid);
|
||||
void setFields(const TimeInfo& timeInfo, const Uuid& uuid);
|
||||
void setCustomData(const CustomData* customData);
|
||||
|
||||
const CustomData* customData() const;
|
||||
|
||||
private slots:
|
||||
void removeSelectedPluginData();
|
||||
void toggleRemoveButton(const QItemSelection& selected);
|
||||
|
||||
private:
|
||||
void updateModel();
|
||||
|
||||
const QScopedPointer<Ui::EditWidgetProperties> m_ui;
|
||||
|
||||
QPointer<CustomData> m_customData;
|
||||
QPointer<QStandardItemModel> m_customDataModel;
|
||||
Q_DISABLE_COPY(EditWidgetProperties)
|
||||
};
|
||||
|
||||
|
@ -10,9 +10,12 @@
|
||||
<height>328</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::ExpandingFieldsGrow</enum>
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0">
|
||||
<property name="spacing">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
@ -23,72 +26,128 @@
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="createdEdit">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="labelCreated">
|
||||
<property name="text">
|
||||
<string>Created:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="createdEdit">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="labelModfied">
|
||||
<property name="text">
|
||||
<string>Modified:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="modifiedEdit">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="labelAccessed">
|
||||
<property name="text">
|
||||
<string>Accessed:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLineEdit" name="accessedEdit">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="labelUuid">
|
||||
<property name="text">
|
||||
<string>Uuid:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLineEdit" name="uuidEdit">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="modifiedEdit">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Created:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Modified:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Accessed:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLineEdit" name="accessedEdit">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Uuid:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLineEdit" name="uuidEdit">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Plugin Data</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QTableView" name="customDataTable">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderDefaultSectionSize">
|
||||
<number>200</number>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QPushButton" name="removeCustomDataButton">
|
||||
<property name="text">
|
||||
<string>Remove</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -73,4 +73,4 @@ void WelcomeWidget::refreshLastDatabases()
|
||||
itm->setText(database);
|
||||
m_ui->recentListWidget->addItem(itm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -666,6 +666,7 @@ void EditEntryWidget::setForms(const Entry* entry, bool restore)
|
||||
#endif
|
||||
|
||||
m_editWidgetProperties->setFields(entry->timeInfo(), entry->uuid());
|
||||
m_editWidgetProperties->setCustomData(entry->customData());
|
||||
|
||||
if (!m_history && !restore) {
|
||||
m_historyModel->setEntries(entry->historyItems());
|
||||
@ -771,7 +772,7 @@ void EditEntryWidget::updateEntryData(Entry* entry) const
|
||||
{
|
||||
entry->attributes()->copyCustomKeysFrom(m_entryAttributes);
|
||||
entry->attachments()->copyDataFrom(m_advancedUi->attachmentsWidget->entryAttachments());
|
||||
|
||||
entry->customData()->copyDataFrom(m_editWidgetProperties->customData());
|
||||
entry->setTitle(m_mainUi->titleEdit->text());
|
||||
entry->setUsername(m_mainUi->usernameEdit->text());
|
||||
entry->setUrl(m_mainUi->urlEdit->text());
|
||||
|
@ -95,6 +95,7 @@ void EditGroupWidget::loadGroup(Group* group, bool create, Database* database)
|
||||
m_editGroupWidgetIcons->load(group->uuid(), database, iconStruct);
|
||||
|
||||
m_editWidgetProperties->setFields(group->timeInfo(), group->uuid());
|
||||
m_editWidgetProperties->setCustomData(group->customData());
|
||||
|
||||
setCurrentPage(0);
|
||||
|
||||
@ -118,6 +119,8 @@ void EditGroupWidget::apply()
|
||||
m_group->setSearchingEnabled(triStateFromIndex(m_mainUi->searchComboBox->currentIndex()));
|
||||
m_group->setAutoTypeEnabled(triStateFromIndex(m_mainUi->autotypeComboBox->currentIndex()));
|
||||
|
||||
m_group->customData()->copyDataFrom(m_editWidgetProperties->customData());
|
||||
|
||||
if (m_mainUi->autoTypeSequenceInherit->isChecked()) {
|
||||
m_group->setDefaultAutoTypeSequence(QString());
|
||||
}
|
||||
|
26174
src/zxcvbn/dict-src.h
26174
src/zxcvbn/dict-src.h
File diff suppressed because it is too large
Load Diff
@ -1,42 +1,41 @@
|
||||
/**********************************************************************************
|
||||
* C implementation of the zxcvbn password strength estimation method.
|
||||
* Copyright (c) 2015, Tony Evans
|
||||
* All rights reserved.
|
||||
* Copyright (c) 2015-2017 Tony Evans
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list
|
||||
* of conditions and the following disclaimer.
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this
|
||||
* list of conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without specific
|
||||
* prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
**********************************************************************************/
|
||||
|
||||
#include "zxcvbn.h"
|
||||
#include <zxcvbn.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
|
||||
/* printf */
|
||||
#ifdef __cplusplus
|
||||
#include <cstdio>
|
||||
#else
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_DICT_FILE
|
||||
#if defined(USE_FILE_IO) || !defined(__cplusplus)
|
||||
#include <stdio.h>
|
||||
@ -46,11 +45,9 @@
|
||||
#endif
|
||||
|
||||
/* For pre-compiled headers under windows */
|
||||
#ifdef _WIN32
|
||||
#ifndef __MINGW32__
|
||||
#ifdef _WIN32
|
||||
#include "stdafx.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Minimum number of characters in a incrementing/decrementing sequence match */
|
||||
#define MIN_SEQUENCE_LEN 3
|
||||
@ -228,13 +225,12 @@ static void AddMatchRepeats(ZxcMatch_t **Result, ZxcMatch_t *Match, const uint8_
|
||||
|
||||
while(MaxLen >= (Len * RepeatCount))
|
||||
{
|
||||
if (strncmp(reinterpret_cast<const char *>(Passwd),
|
||||
reinterpret_cast<const char *>(Rpt), Len) == 0)
|
||||
if (strncmp((const char *)Passwd, (const char *)Rpt, Len) == 0)
|
||||
{
|
||||
/* Found a repeat */
|
||||
ZxcMatch_t *p = AllocMatch();
|
||||
p->Entrpy = Match->Entrpy + log(RepeatCount);
|
||||
p->Type = static_cast<ZxcTypeMatch_t>(Match->Type + MULTIPLE_MATCH);
|
||||
p->Type = (ZxcTypeMatch_t)(Match->Type + MULTIPLE_MATCH);
|
||||
p->Length = Len * RepeatCount;
|
||||
p->Begin = Match->Begin;
|
||||
AddResult(Result, p, MaxLen);
|
||||
@ -494,9 +490,8 @@ typedef struct
|
||||
uint8_t Leeted[sizeof L33TChr];
|
||||
uint8_t UnLeet[sizeof L33TChr];
|
||||
uint8_t LeetCnv[sizeof L33TCnv / LEET_NORM_MAP_SIZE + 1];
|
||||
/* uint8_t LeetChr[3]; */
|
||||
uint8_t First;
|
||||
uint8_t PossChars[49];
|
||||
uint8_t PossChars[CHARSET_SIZE];
|
||||
} DictWork_t;
|
||||
|
||||
/**********************************************************************************
|
||||
@ -618,7 +613,7 @@ static void DictionaryEntropy(ZxcMatch_t *m, DictMatchInfo_t *Extra, const uint8
|
||||
e += d;
|
||||
}
|
||||
/* Add entropy due to word's rank */
|
||||
e += log(static_cast<double>(Extra->Rank));
|
||||
e += log((double)Extra->Rank);
|
||||
m->Entrpy = e;
|
||||
}
|
||||
|
||||
@ -795,7 +790,7 @@ static void UserMatch(ZxcMatch_t **Result, const char *Words[], const uint8_t *P
|
||||
int Caps = 0;
|
||||
int Lowers = 0;
|
||||
int Leets = 0;
|
||||
const uint8_t *Wrd = reinterpret_cast<const uint8_t *>(Words[Rank]);
|
||||
const uint8_t *Wrd = (const uint8_t *)(Words[Rank]);
|
||||
const uint8_t *Pwd = Passwd;
|
||||
memset(Extra.Leeted, 0, sizeof Extra.Leeted);
|
||||
memset(Extra.UnLeet, 0, sizeof Extra.UnLeet);
|
||||
@ -944,19 +939,19 @@ static const uint8_t UK_Shift[] = "!1\"2$4%5&7(9)0*8:;<,>.?/@'AaBbCcDdEeFfGgHhIi
|
||||
static const uint8_t US_Shift[] = "!1\"'#3$4%5&7(9)0*8:;<,>.?/@2AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz^6_-{[|\\}]~`";
|
||||
|
||||
|
||||
/* Neighour tables */
|
||||
/* Neighbour tables */
|
||||
static const uint8_t UK_Qwerty[48*7] =
|
||||
{
|
||||
/* key, left, up-left, up-right, right, down-right, down-left */
|
||||
'#', '\'',']', 0, 0, 0, 0, '\'',';', '[', ']', '#', 0, '/',
|
||||
',', 'm', 'k', 'l', '.', 0, 0, '-', '0', 0, 0, '-', 'p', 'o',
|
||||
'.', ',', 'l', ';', '/', 0, 0, '/', '.', ';', '\'', 0, 0, 0,
|
||||
'0', '9', 0, 0, '-', 'p', 'o', '1', '`', 0, 0, '2', 'q', 0,
|
||||
',', 'm', 'k', 'l', '.', 0, 0, '-', '0', 0, 0, '=', '[', 'p',
|
||||
'.', ',', 'l', ';', '/', 0, 0, '/', '.', ';', '\'', 0, 0, 0,
|
||||
'0', '9', 0, 0, '-', 'p', 'o', '1', '`', 0, 0, '2', 'q', 0,
|
||||
'2', '1', 0, 0, '3', 'w', 'q', '3', '2', 0, 0, '4', 'e', 'w',
|
||||
'4', '3', 0, 0, '5', 'r', 'e', '5', '4', 0, 0, '6', 't', 'r',
|
||||
'6', '5', 0, 0, '7', 'y', 't', '7', '6', 0, 0, '8', 'u', 'y',
|
||||
'8', '7', 0, 0, '9', 'i', 'u', '9', '8', 0, 0, '0', 'o', 'i',
|
||||
';', 'l', 'o', 'p','\'', '/', '.', '=', '-', 0, 0, 0, ']', '[',
|
||||
';', 'l', 'p', '[','\'', '/', '.', '=', '-', 0, 0, 0, ']', '[',
|
||||
'[', 'p', '-', '=', ']', '\'',';', '\\', 0, 0, 'a', 'z', 0, 0,
|
||||
']', '[', '=', 0, 0, '#','\'', '`', 0, 0, 0, '1', 0, 0,
|
||||
'a', 0, 'q', 'w', 's', 'z','\\', 'b', 'v', 'g', 'h', 'n', 0, 0,
|
||||
@ -1002,7 +997,7 @@ static const uint8_t US_Qwerty[47*7] =
|
||||
'x', 'z', 's', 'd', 'c', 0, 0, 'y', 't', '6', '7', 'u', 'h', 'g',
|
||||
'z', 0, 'a', 's', 'x', 0, 0,
|
||||
};
|
||||
static const uint8_t Dvorak[48*7] =
|
||||
static const uint8_t Dvorak[47*7] =
|
||||
{
|
||||
'\'', 0, '1', '2', ',', 'a', 0, ',','\'', '2', '3', '.', 'o', 'a',
|
||||
'-', 's', '/', '=', 0, 0, 'z', '.', ',', '3', '4', 'p', 'e', 'o',
|
||||
@ -1160,9 +1155,9 @@ static void SpatialMatch(ZxcMatch_t **Result, const uint8_t *Passwd, int Start,
|
||||
for(CurLen = MaxLen; CurLen >= MIN_SPATIAL_LEN;CurLen = Len - 1)
|
||||
{
|
||||
Len = 0;
|
||||
memset(&Extra, 0, sizeof Extra);
|
||||
for(k = Keyboards, Indx = 0; Indx < (sizeof Keyboards / sizeof Keyboards[0]); ++Indx, ++k)
|
||||
{
|
||||
memset(&Extra, 0, sizeof Extra);
|
||||
Len = DoSptlMatch(Passwd, CurLen, k, &Extra);
|
||||
if (Len > 0)
|
||||
{
|
||||
@ -1170,7 +1165,7 @@ static void SpatialMatch(ZxcMatch_t **Result, const uint8_t *Passwd, int Start,
|
||||
int i, j, s;
|
||||
double Degree, Entropy;
|
||||
ZxcMatch_t *p;
|
||||
Degree = (k->NumNear-1) - static_cast<double>(k->NumBlank) / static_cast<double>(k->NumKeys);
|
||||
Degree = (k->NumNear-1) - (double)k->NumBlank / (double)k->NumKeys;
|
||||
s = k->NumKeys;
|
||||
if (k->Shifts)
|
||||
s *= 2;
|
||||
@ -1213,7 +1208,6 @@ static void SpatialMatch(ZxcMatch_t **Result, const uint8_t *Passwd, int Start,
|
||||
p->Length = Len;
|
||||
AddMatchRepeats(Result, p, Passwd, MaxLen);
|
||||
AddResult(Result, p, MaxLen);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1406,13 +1400,13 @@ static void RepeatMatch(ZxcMatch_t **Result, const uint8_t *Passwd, int Start, i
|
||||
int RepeatCount = 2;
|
||||
while(MaxLen >= (Len * RepeatCount))
|
||||
{
|
||||
if (strncmp(reinterpret_cast<const char *>(Passwd), reinterpret_cast<const char *>(Rpt), Len) == 0)
|
||||
if (strncmp((const char *)Passwd, (const char *)Rpt, Len) == 0)
|
||||
{
|
||||
/* Found a repeat */
|
||||
int c = Cardinality(Passwd, Len);
|
||||
ZxcMatch_t *p = AllocMatch();
|
||||
p->Entrpy = log(static_cast<double>(c)) * Len + log(RepeatCount);
|
||||
p->Type = static_cast<ZxcTypeMatch_t>(BRUTE_MATCH + MULTIPLE_MATCH);
|
||||
p->Entrpy = log((double)c) * Len + log(RepeatCount);
|
||||
p->Type = (ZxcTypeMatch_t)(BRUTE_MATCH + MULTIPLE_MATCH);
|
||||
p->Length = Len * RepeatCount;
|
||||
p->Begin = Start;
|
||||
AddResult(Result, p, MaxLen);
|
||||
@ -1486,7 +1480,7 @@ static void SequenceMatch(ZxcMatch_t **Result, const uint8_t *Passwd, int Start,
|
||||
Next = Passwd[0] + Dir;
|
||||
if (IsDigits && (Dir > 0) && (Next == ('9' + 1)) && (Passwd[1] == '0'))
|
||||
{
|
||||
/* Incrementing digits, consider '0' to be same as a 'ten' character */
|
||||
/* Incrementing digits, consider '0' to be same as a 'ten' character */
|
||||
++Len;
|
||||
++Passwd;
|
||||
break;
|
||||
@ -1495,8 +1489,9 @@ static void SequenceMatch(ZxcMatch_t **Result, const uint8_t *Passwd, int Start,
|
||||
{
|
||||
++Len;
|
||||
++Passwd;
|
||||
break;
|
||||
}
|
||||
else if ((Next > SetHigh) || (Next < SetLow) || (Passwd[1] != Next))
|
||||
if ((Next > SetHigh) || (Next < SetLow) || (Passwd[1] != Next))
|
||||
break;
|
||||
++Len;
|
||||
++Passwd;
|
||||
@ -1528,7 +1523,7 @@ static void SequenceMatch(ZxcMatch_t **Result, const uint8_t *Passwd, int Start,
|
||||
p->Type = SEQUENCE_MATCH;
|
||||
p->Begin = Start;
|
||||
p->Length = i;
|
||||
p->Entrpy = e + log(static_cast<double>(i));
|
||||
p->Entrpy = e + log((double)i);
|
||||
AddMatchRepeats(Result, p, Pwd, MaxLen);
|
||||
AddResult(Result, p, MaxLen);
|
||||
}
|
||||
@ -1557,7 +1552,7 @@ static void SequenceMatch(ZxcMatch_t **Result, const uint8_t *Passwd, int Start,
|
||||
*
|
||||
* Dijkstra's algorithm finds the combination of these part matches (or paths)
|
||||
* which gives the lowest entropy (or smallest distance) from begining to end
|
||||
* of the password.
|
||||
* of the password.
|
||||
*/
|
||||
|
||||
/* Struct to hold the data of a node (imaginary point between password characters) */
|
||||
@ -1579,13 +1574,13 @@ double ZxcvbnMatch(const char *Pwd, const char *UserDict[], ZxcMatch_t **Info)
|
||||
Node_t *Np;
|
||||
double e;
|
||||
int Len = strlen(Pwd);
|
||||
const uint8_t *Passwd = reinterpret_cast<const uint8_t *>(Pwd);
|
||||
const uint8_t *Passwd = (const uint8_t *)Pwd;
|
||||
uint8_t *RevPwd;
|
||||
/* Create the paths */
|
||||
Node_t *Nodes = MallocFn(Node_t, Len+1);
|
||||
memset(Nodes, 0, (Len+1) * sizeof *Nodes);
|
||||
i = Cardinality(Passwd, Len);
|
||||
e = log(static_cast<double>(i));
|
||||
e = log((double)i);
|
||||
|
||||
/* Do matching for all parts of the password */
|
||||
for(i = 0; i < Len; ++i)
|
@ -2,34 +2,26 @@
|
||||
#define ZXCVBN_H_F98183CE2A01_INCLUDED
|
||||
/**********************************************************************************
|
||||
* C implementation of the zxcvbn password strength estimation method.
|
||||
* Copyright (c) 2015, Tony Evans
|
||||
* All rights reserved.
|
||||
* Copyright (c) 2015-2017 Tony Evans
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list
|
||||
* of conditions and the following disclaimer.
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this
|
||||
* list of conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without specific
|
||||
* prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
**********************************************************************************/
|
||||
|
||||
/* If this is defined, the dictiononary data is read from file. When undefined */
|
||||
|
@ -33,7 +33,6 @@ QTEST_GUILESS_MAIN(TestKdbx3)
|
||||
|
||||
void TestKdbx3::initTestCaseImpl()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Database* TestKdbx3::readXml(const QString& path, bool strictMode, bool& hasError, QString& errorString)
|
||||
|
@ -131,6 +131,7 @@ void TestKdbx4::testFormat400Upgrade()
|
||||
{
|
||||
QFETCH(Uuid, kdfUuid);
|
||||
QFETCH(Uuid, cipherUuid);
|
||||
QFETCH(bool, addCustomData);
|
||||
QFETCH(quint32, expectedVersion);
|
||||
|
||||
QScopedPointer<Database> sourceDb(new Database());
|
||||
@ -147,6 +148,12 @@ void TestKdbx4::testFormat400Upgrade()
|
||||
// upgrade to KDBX 4 by changing KDF and Cipher
|
||||
sourceDb->changeKdf(KeePass2::uuidToKdf(kdfUuid));
|
||||
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;
|
||||
writer.writeDatabase(&buffer, sourceDb.data());
|
||||
if (writer.hasError()) {
|
||||
@ -165,26 +172,129 @@ void TestKdbx4::testFormat400Upgrade()
|
||||
QCOMPARE(targetDb->metadata()->name(), sourceDb->metadata()->name());
|
||||
|
||||
QCOMPARE(reader.version(), expectedVersion);
|
||||
QCOMPARE(targetDb->kdf()->uuid(), sourceDb->kdf()->uuid());
|
||||
QCOMPARE(targetDb->cipher(), cipherUuid);
|
||||
QCOMPARE(*targetDb->metadata()->customData(), *sourceDb->metadata()->customData());
|
||||
QCOMPARE(*targetDb->rootGroup()->customData(), *sourceDb->rootGroup()->customData());
|
||||
}
|
||||
|
||||
void TestKdbx4::testFormat400Upgrade_data()
|
||||
{
|
||||
QTest::addColumn<Uuid>("kdfUuid");
|
||||
QTest::addColumn<Uuid>("cipherUuid");
|
||||
QTest::addColumn<bool>("addCustomData");
|
||||
QTest::addColumn<quint32>("expectedVersion");
|
||||
|
||||
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;
|
||||
|
||||
QTest::newRow("Argon2 + AES") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_AES << kdbx4;
|
||||
QTest::newRow("AES-KDF + AES") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_AES << kdbx4;
|
||||
QTest::newRow("AES-KDF (legacy) + AES") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_AES << kdbx3;
|
||||
QTest::newRow("Argon2 + ChaCha20") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_CHACHA20 << kdbx4;
|
||||
QTest::newRow("AES-KDF + ChaCha20") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_CHACHA20 << kdbx4;
|
||||
QTest::newRow("AES-KDF (legacy) + ChaCha20") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_CHACHA20 << kdbx3;
|
||||
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("AES-KDF (legacy) + Twofish") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_TWOFISH << kdbx3;
|
||||
QTest::newRow("Argon2 + AES") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_AES << false << 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 << false << kdbx3;
|
||||
QTest::newRow("Argon2 + AES + CustomData") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_AES << true << kdbx4;
|
||||
QTest::newRow("AES-KDF + AES + CustomData") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_AES << true << kdbx4;
|
||||
QTest::newRow("AES-KDF (legacy) + AES + CustomData") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_AES << true << kdbx4;
|
||||
|
||||
QTest::newRow("Argon2 + ChaCha20") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_CHACHA20 << false << kdbx4;
|
||||
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;
|
||||
}
|
||||
|
||||
void TestKdbx4::testCustomData()
|
||||
{
|
||||
Database db;
|
||||
|
||||
// test public custom data
|
||||
QVariantMap publicCustomData;
|
||||
publicCustomData.insert("CD1", 123);
|
||||
publicCustomData.insert("CD2", true);
|
||||
publicCustomData.insert("CD3", "abcäöü");
|
||||
publicCustomData.insert("CD4", QByteArray::fromHex("ababa123ff"));
|
||||
db.setPublicCustomData(publicCustomData);
|
||||
QCOMPARE(db.publicCustomData(), publicCustomData);
|
||||
|
||||
const QString customDataKey1 = "CD1";
|
||||
const QString customDataKey2 = "CD2";
|
||||
const QString customData1 = "abcäöü";
|
||||
const QString customData2 = "Hello World";
|
||||
const int dataSize = customDataKey1.toUtf8().size() + customDataKey1.toUtf8().size() +
|
||||
customData1.toUtf8().size() + customData2.toUtf8().size();
|
||||
|
||||
// test custom database data
|
||||
db.metadata()->customData()->set(customDataKey1, customData1);
|
||||
db.metadata()->customData()->set(customDataKey2, customData2);
|
||||
QCOMPARE(db.metadata()->customData()->size(), 2);
|
||||
QCOMPARE(db.metadata()->customData()->dataSize(), dataSize);
|
||||
|
||||
// test custom root group data
|
||||
Group* root = db.rootGroup();
|
||||
root->customData()->set(customDataKey1, customData1);
|
||||
root->customData()->set(customDataKey2, customData2);
|
||||
QCOMPARE(root->customData()->size(), 2);
|
||||
QCOMPARE(root->customData()->dataSize(), dataSize);
|
||||
|
||||
// test copied custom group data
|
||||
auto* group = new Group();
|
||||
group->setParent(root);
|
||||
group->setUuid(Uuid::random());
|
||||
group->customData()->copyDataFrom(root->customData());
|
||||
QCOMPARE(*group->customData(), *root->customData());
|
||||
|
||||
// test copied custom entry data
|
||||
auto* entry = new Entry();
|
||||
entry->setGroup(group);
|
||||
entry->setUuid(Uuid::random());
|
||||
entry->customData()->copyDataFrom(group->customData());
|
||||
QCOMPARE(*entry->customData(), *root->customData());
|
||||
|
||||
// test custom data deletion
|
||||
entry->customData()->set("additional item", "foobar");
|
||||
QCOMPARE(entry->customData()->size(), 3);
|
||||
entry->customData()->remove("additional item");
|
||||
QCOMPARE(entry->customData()->size(), 2);
|
||||
QCOMPARE(entry->customData()->dataSize(), dataSize);
|
||||
|
||||
// test custom data on cloned groups
|
||||
QScopedPointer<Group> clonedGroup(group->clone());
|
||||
QCOMPARE(*clonedGroup->customData(), *group->customData());
|
||||
|
||||
// test custom data on cloned entries
|
||||
QScopedPointer<Entry> clonedEntry(entry->clone(Entry::CloneNoFlags));
|
||||
QCOMPARE(*clonedEntry->customData(), *entry->customData());
|
||||
|
||||
QBuffer buffer;
|
||||
buffer.open(QBuffer::ReadWrite);
|
||||
KeePass2Writer writer;
|
||||
writer.writeDatabase(&buffer, &db);
|
||||
|
||||
// read buffer back
|
||||
buffer.seek(0);
|
||||
KeePass2Reader reader;
|
||||
QSharedPointer<Database> newDb(reader.readDatabase(&buffer, CompositeKey()));
|
||||
|
||||
// test all custom data are read back successfully from KDBX
|
||||
QCOMPARE(newDb->publicCustomData(), publicCustomData);
|
||||
|
||||
QCOMPARE(newDb->metadata()->customData()->value(customDataKey1), customData1);
|
||||
QCOMPARE(newDb->metadata()->customData()->value(customDataKey2), customData2);
|
||||
|
||||
QCOMPARE(newDb->rootGroup()->customData()->value(customDataKey1), customData1);
|
||||
QCOMPARE(newDb->rootGroup()->customData()->value(customDataKey2), customData2);
|
||||
|
||||
auto* newGroup = newDb->rootGroup()->children()[0];
|
||||
QCOMPARE(newGroup->customData()->value(customDataKey1), customData1);
|
||||
QCOMPARE(newGroup->customData()->value(customDataKey2), customData2);
|
||||
|
||||
auto* newEntry = newDb->rootGroup()->children()[0]->entries()[0];
|
||||
QCOMPARE(newEntry->customData()->value(customDataKey1), customData1);
|
||||
QCOMPARE(newEntry->customData()->value(customDataKey2), customData2);
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ private slots:
|
||||
void testFormat400();
|
||||
void testFormat400Upgrade();
|
||||
void testFormat400Upgrade_data();
|
||||
void testCustomData();
|
||||
|
||||
protected:
|
||||
void initTestCaseImpl() override;
|
||||
|
@ -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()
|
||||
{
|
||||
const Group* group = m_xmlDb->rootGroup();
|
||||
@ -155,7 +146,6 @@ void TestKeePass2Format::testXmlGroupRoot()
|
||||
QCOMPARE(group->autoTypeEnabled(), Group::Inherit);
|
||||
QCOMPARE(group->searchingEnabled(), Group::Inherit);
|
||||
QCOMPARE(group->lastTopVisibleEntry()->uuid().toBase64(), QString("+wSUOv6qf0OzW8/ZHAs2sA=="));
|
||||
|
||||
QCOMPARE(group->children().size(), 3);
|
||||
QVERIFY(m_xmlDb->metadata()->recycleBin() == m_xmlDb->rootGroup()->children().at(2));
|
||||
|
||||
|
@ -40,7 +40,6 @@ private slots:
|
||||
*/
|
||||
void testXmlMetadata();
|
||||
void testXmlCustomIcons();
|
||||
void testXmlCustomData();
|
||||
void testXmlGroupRoot();
|
||||
void testXmlGroup1();
|
||||
void testXmlGroup2();
|
||||
|
@ -40,33 +40,33 @@ void TestModified::testSignals()
|
||||
|
||||
CompositeKey compositeKey;
|
||||
|
||||
Database* db = new Database();
|
||||
Group* root = db->rootGroup();
|
||||
QSignalSpy spyModified(db, SIGNAL(modifiedImmediate()));
|
||||
QScopedPointer<Database> db(new Database());
|
||||
auto* root = db->rootGroup();
|
||||
QSignalSpy spyModified(db.data(), SIGNAL(modifiedImmediate()));
|
||||
|
||||
db->setKey(compositeKey);
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
|
||||
Group* g1 = new Group();
|
||||
g1->setParent(root);
|
||||
auto* group1 = new Group();
|
||||
group1->setParent(root);
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
|
||||
Group* g2 = new Group();
|
||||
g2->setParent(root);
|
||||
auto* group2 = new Group();
|
||||
group2->setParent(root);
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
|
||||
g2->setParent(root, 0);
|
||||
group2->setParent(root, 0);
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
|
||||
Entry* entry1 = new Entry();
|
||||
entry1->setGroup(g1);
|
||||
auto* entry1 = new Entry();
|
||||
entry1->setGroup(group1);
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
|
||||
Database* db2 = new Database();
|
||||
Group* root2 = db2->rootGroup();
|
||||
QSignalSpy spyModified2(db2, SIGNAL(modifiedImmediate()));
|
||||
QScopedPointer<Database> db2(new Database());
|
||||
auto* root2 = db2->rootGroup();
|
||||
QSignalSpy spyModified2(db2.data(), SIGNAL(modifiedImmediate()));
|
||||
|
||||
g1->setParent(root2);
|
||||
group1->setParent(root2);
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
QCOMPARE(spyModified2.count(), ++spyCount2);
|
||||
|
||||
@ -74,8 +74,8 @@ void TestModified::testSignals()
|
||||
QCOMPARE(spyModified.count(), spyCount);
|
||||
QCOMPARE(spyModified2.count(), ++spyCount2);
|
||||
|
||||
Entry* entry2 = new Entry();
|
||||
entry2->setGroup(g2);
|
||||
auto* entry2 = new Entry();
|
||||
entry2->setGroup(group2);
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
QCOMPARE(spyModified2.count(), spyCount2);
|
||||
|
||||
@ -87,15 +87,15 @@ void TestModified::testSignals()
|
||||
QCOMPARE(spyModified.count(), spyCount);
|
||||
QCOMPARE(spyModified2.count(), ++spyCount2);
|
||||
|
||||
Group* g3 = new Group();
|
||||
g3->setParent(root);
|
||||
auto* group3 = new Group();
|
||||
group3->setParent(root);
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
|
||||
Group* g4 = new Group();
|
||||
g4->setParent(g3);
|
||||
auto* group4 = new Group();
|
||||
group4->setParent(group3);
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
|
||||
delete g4;
|
||||
delete group4;
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
|
||||
delete entry2;
|
||||
@ -103,21 +103,18 @@ void TestModified::testSignals()
|
||||
|
||||
QCOMPARE(spyModified.count(), spyCount);
|
||||
QCOMPARE(spyModified2.count(), spyCount2);
|
||||
|
||||
delete db;
|
||||
delete db2;
|
||||
}
|
||||
|
||||
void TestModified::testGroupSets()
|
||||
{
|
||||
int spyCount = 0;
|
||||
Database* db = new Database();
|
||||
Group* root = db->rootGroup();
|
||||
QScopedPointer<Database> db(new Database());
|
||||
auto* root = db->rootGroup();
|
||||
|
||||
Group* g = new Group();
|
||||
g->setParent(root);
|
||||
auto* group = new Group();
|
||||
group->setParent(root);
|
||||
|
||||
QSignalSpy spyModified(db, SIGNAL(modifiedImmediate()));
|
||||
QSignalSpy spyModified(db.data(), SIGNAL(modifiedImmediate()));
|
||||
|
||||
root->setUuid(Uuid::random());
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
@ -145,46 +142,44 @@ void TestModified::testGroupSets()
|
||||
QCOMPARE(spyModified.count(), spyCount);
|
||||
|
||||
|
||||
g->setUuid(Uuid::random());
|
||||
group->setUuid(Uuid::random());
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
g->setUuid(g->uuid());
|
||||
group->setUuid(group->uuid());
|
||||
QCOMPARE(spyModified.count(), spyCount);
|
||||
|
||||
g->setName("test");
|
||||
group->setName("test");
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
g->setName(g->name());
|
||||
group->setName(group->name());
|
||||
QCOMPARE(spyModified.count(), spyCount);
|
||||
|
||||
g->setNotes("test");
|
||||
group->setNotes("test");
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
g->setNotes(g->notes());
|
||||
group->setNotes(group->notes());
|
||||
QCOMPARE(spyModified.count(), spyCount);
|
||||
|
||||
g->setIcon(1);
|
||||
group->setIcon(1);
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
g->setIcon(g->iconNumber());
|
||||
group->setIcon(group->iconNumber());
|
||||
QCOMPARE(spyModified.count(), spyCount);
|
||||
|
||||
g->setIcon(Uuid::random());
|
||||
group->setIcon(Uuid::random());
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
g->setIcon(g->iconUuid());
|
||||
group->setIcon(group->iconUuid());
|
||||
QCOMPARE(spyModified.count(), spyCount);
|
||||
|
||||
delete db;
|
||||
}
|
||||
|
||||
void TestModified::testEntrySets()
|
||||
{
|
||||
int spyCount = 0;
|
||||
Database* db = new Database();
|
||||
Group* root = db->rootGroup();
|
||||
QScopedPointer<Database> db(new Database());
|
||||
auto* root = db->rootGroup();
|
||||
|
||||
Group* g = new Group();
|
||||
g->setParent(root);
|
||||
Entry* entry = new Entry();
|
||||
entry->setGroup(g);
|
||||
auto* group = new Group();
|
||||
group->setParent(root);
|
||||
auto* entry = new Entry();
|
||||
entry->setGroup(group);
|
||||
|
||||
QSignalSpy spyModified(db, SIGNAL(modifiedImmediate()));
|
||||
QSignalSpy spyModified(db.data(), SIGNAL(modifiedImmediate()));
|
||||
|
||||
entry->setUuid(Uuid::random());
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
@ -284,8 +279,6 @@ void TestModified::testEntrySets()
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
entry->attributes()->set("test key2", entry->attributes()->value("test key2"), true);
|
||||
QCOMPARE(spyModified.count(), spyCount);
|
||||
|
||||
delete db;
|
||||
}
|
||||
|
||||
void TestModified::testHistoryItems()
|
||||
@ -298,8 +291,6 @@ void TestModified::testHistoryItems()
|
||||
QScopedPointer<EntryAttributes> attributes(new EntryAttributes());
|
||||
attributes->copyCustomKeysFrom(entry->attributes());
|
||||
|
||||
Entry* historyEntry;
|
||||
|
||||
int historyItemsSize = 0;
|
||||
|
||||
entry->beginUpdate();
|
||||
@ -315,7 +306,7 @@ void TestModified::testHistoryItems()
|
||||
entry->setTitle("b");
|
||||
entry->endUpdate();
|
||||
QCOMPARE(entry->historyItems().size(), ++historyItemsSize);
|
||||
historyEntry = entry->historyItems().at(historyItemsSize - 1);
|
||||
auto *historyEntry = entry->historyItems().at(historyItemsSize - 1);
|
||||
QCOMPARE(historyEntry->title(), QString("a"));
|
||||
QCOMPARE(historyEntry->uuid(), entry->uuid());
|
||||
QCOMPARE(historyEntry->tags(), entry->tags());
|
||||
@ -344,12 +335,11 @@ void TestModified::testHistoryItems()
|
||||
QVERIFY(!entry->historyItems().at(historyItemsSize - 1)->attributes()->keys().contains("k"));
|
||||
|
||||
QScopedPointer<Database> db(new Database());
|
||||
Group* root = db->rootGroup();
|
||||
auto* root = db->rootGroup();
|
||||
db->metadata()->setHistoryMaxItems(3);
|
||||
db->metadata()->setHistoryMaxSize(-1);
|
||||
|
||||
Entry* historyEntry2;
|
||||
Entry* entry2 = new Entry();
|
||||
auto* entry2 = new Entry();
|
||||
entry2->setGroup(root);
|
||||
entry2->beginUpdate();
|
||||
entry2->setTitle("1");
|
||||
@ -375,7 +365,7 @@ void TestModified::testHistoryItems()
|
||||
entry2->endUpdate();
|
||||
QCOMPARE(entry2->historyItems().size(), 1);
|
||||
|
||||
historyEntry2 = entry2->historyItems().at(0);
|
||||
auto* historyEntry2 = entry2->historyItems().at(0);
|
||||
QCOMPARE(historyEntry2->title(), QString("4"));
|
||||
|
||||
db->metadata()->setHistoryMaxItems(-1);
|
||||
@ -453,7 +443,6 @@ void TestModified::testHistoryMaxSize()
|
||||
QScopedPointer<Database> db(new Database());
|
||||
const QString key("test");
|
||||
|
||||
|
||||
auto entry1 = new Entry();
|
||||
entry1->setGroup(db->rootGroup());
|
||||
QCOMPARE(entry1->historyItems().size(), 0);
|
||||
@ -582,3 +571,32 @@ void TestModified::testHistoryMaxSize()
|
||||
QCOMPARE(entry2->autoTypeAssociations()->associationsSize(), 0);
|
||||
QCOMPARE(entry2->historyItems().size(), 0);
|
||||
}
|
||||
|
||||
void TestModified::testCustomData()
|
||||
{
|
||||
int spyCount = 0;
|
||||
QScopedPointer<Database> db(new Database());
|
||||
auto* root = db->rootGroup();
|
||||
|
||||
auto* group = new Group();
|
||||
group->setParent(root);
|
||||
auto* entry = new Entry();
|
||||
entry->setGroup(group);
|
||||
|
||||
QSignalSpy spyModified(db.data(), SIGNAL(modifiedImmediate()));
|
||||
|
||||
db->metadata()->customData()->set("Key", "Value");
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
db->metadata()->customData()->set("Key", "Value");
|
||||
QCOMPARE(spyModified.count(), spyCount);
|
||||
|
||||
entry->customData()->set("Key", "Value");
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
entry->customData()->set("Key", "Value");
|
||||
QCOMPARE(spyModified.count(), spyCount);
|
||||
|
||||
group->customData()->set("Key", "Value");
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
group->customData()->set("Key", "Value");
|
||||
QCOMPARE(spyModified.count(), spyCount);
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ private slots:
|
||||
void testEntrySets();
|
||||
void testHistoryItems();
|
||||
void testHistoryMaxSize();
|
||||
void testCustomData();
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_TESTMODIFIED_H
|
||||
|
@ -39,16 +39,6 @@
|
||||
<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>
|
||||
</Binaries>
|
||||
<CustomData>
|
||||
<Item>
|
||||
<Key>A Sample Test Key</Key>
|
||||
<Value>valu</Value>
|
||||
</Item>
|
||||
<Item>
|
||||
<Key>custom key</Key>
|
||||
<Value>blub</Value>
|
||||
</Item>
|
||||
</CustomData>
|
||||
</Meta>
|
||||
<Root>
|
||||
<Group>
|
||||
@ -479,4 +469,4 @@
|
||||
</DeletedObject>
|
||||
</DeletedObjects>
|
||||
</Root>
|
||||
</KeePassFile>
|
||||
</KeePassFile>
|
||||
|
@ -545,15 +545,13 @@ void TestGui::testPasswordEntryEntropy()
|
||||
|
||||
editNewPassword->setText("");
|
||||
QTest::keyClicks(editNewPassword, "YQC3kbXbjC652dTDH");
|
||||
QCOMPARE(entropyLabel->text(), QString("Entropy: 96.07 bit"));
|
||||
QCOMPARE(entropyLabel->text(), QString("Entropy: 95.83 bit"));
|
||||
QCOMPARE(strengthLabel->text(), QString("Password Quality: Good"));
|
||||
|
||||
editNewPassword->setText("");
|
||||
QTest::keyClicks(editNewPassword, "Bs5ZFfthWzR8DGFEjaCM6bGqhmCT4km");
|
||||
QCOMPARE(entropyLabel->text(), QString("Entropy: 174.59 bit"));
|
||||
QCOMPARE(strengthLabel->text(), QString("Password Quality: Excellent"));
|
||||
|
||||
// We are done
|
||||
}
|
||||
|
||||
void TestGui::testDicewareEntryEntropy()
|
||||
|
Loading…
Reference in New Issue
Block a user