diff --git a/src/core/AutoTypeAssociations.cpp b/src/core/AutoTypeAssociations.cpp index 5ec4eb3b3..04221733f 100644 --- a/src/core/AutoTypeAssociations.cpp +++ b/src/core/AutoTypeAssociations.cpp @@ -103,6 +103,15 @@ int AutoTypeAssociations::size() const return m_associations.size(); } +int AutoTypeAssociations::associationsSize() const +{ + int size = 0; + for (const Association &association : m_associations) { + size += association.sequence.toUtf8().size() + association.window.toUtf8().size(); + } + return size; +} + void AutoTypeAssociations::clear() { m_associations.clear(); diff --git a/src/core/AutoTypeAssociations.h b/src/core/AutoTypeAssociations.h index 61ef3fd4a..31e58cda0 100644 --- a/src/core/AutoTypeAssociations.h +++ b/src/core/AutoTypeAssociations.h @@ -43,6 +43,7 @@ public: AutoTypeAssociations::Association get(int index) const; QList getAll() const; int size() const; + int associationsSize() const; void clear(); private: diff --git a/src/core/Entry.cpp b/src/core/Entry.cpp index ed8860835..2f4105fc6 100644 --- a/src/core/Entry.cpp +++ b/src/core/Entry.cpp @@ -582,22 +582,24 @@ void Entry::truncateHistory() int histMaxSize = db->metadata()->historyMaxSize(); if (histMaxSize > -1) { int size = 0; - QSet foundAttachments = attachments()->values().toSet(); + QSet foundAttachments = attachments()->values(); QMutableListIterator i(m_history); i.toBack(); + const QRegularExpression delimiter(",|:|;"); while (i.hasPrevious()) { Entry* historyItem = i.previous(); // don't calculate size if it's already above the maximum if (size <= histMaxSize) { size += historyItem->attributes()->attributesSize(); - - const QSet newAttachments = historyItem->attachments()->values().toSet() - foundAttachments; - for (const QByteArray& attachment : newAttachments) { - size += attachment.size(); + size += historyItem->autoTypeAssociations()->associationsSize(); + size += historyItem->attachments()->attachmentsSize(); + const QStringList tags = historyItem->tags().split(delimiter, QString::SkipEmptyParts); + for (const QString& tag : tags) { + size += tag.toUtf8().size(); } - foundAttachments += newAttachments; + foundAttachments += historyItem->attachments()->values(); } if (size > histMaxSize) { @@ -633,7 +635,7 @@ Entry* Entry::clone(CloneFlags flags) const entry->m_attributes->set(EntryAttributes::PasswordKey, password.toUpper(), m_attributes->isProtected(EntryAttributes::PasswordKey)); } - entry->m_autoTypeAssociations->copyDataFrom(this->m_autoTypeAssociations); + entry->m_autoTypeAssociations->copyDataFrom(m_autoTypeAssociations); if (flags & CloneIncludeHistory) { for (Entry* historyItem : m_history) { Entry* historyItemClone = historyItem->clone(flags & ~CloneIncludeHistory & ~CloneNewUuid); @@ -679,6 +681,7 @@ void Entry::beginUpdate() m_tmpHistoryItem->m_data = m_data; m_tmpHistoryItem->m_attributes->copyDataFrom(m_attributes); m_tmpHistoryItem->m_attachments->copyDataFrom(m_attachments); + m_tmpHistoryItem->m_autoTypeAssociations->copyDataFrom(m_autoTypeAssociations); m_modifiedSinceBegin = false; } diff --git a/src/core/EntryAttachments.cpp b/src/core/EntryAttachments.cpp index 957558609..4dcc0262b 100644 --- a/src/core/EntryAttachments.cpp +++ b/src/core/EntryAttachments.cpp @@ -18,6 +18,7 @@ #include "EntryAttachments.h" #include +#include EntryAttachments::EntryAttachments(QObject* parent) : QObject(parent) @@ -34,9 +35,9 @@ bool EntryAttachments::hasKey(const QString& key) const return m_attachments.contains(key); } -QList EntryAttachments::values() const +QSet EntryAttachments::values() const { - return m_attachments.values(); + return m_attachments.values().toSet(); } QByteArray EntryAttachments::value(const QString& key) const @@ -151,3 +152,12 @@ bool EntryAttachments::operator!=(const EntryAttachments& other) const { return m_attachments != other.m_attachments; } + +int EntryAttachments::attachmentsSize() const +{ + int size = 0; + for (auto it = m_attachments.constBegin(); it != m_attachments.constEnd(); ++it) { + size += it.key().toUtf8().size() + it.value().size(); + } + return size; +} diff --git a/src/core/EntryAttachments.h b/src/core/EntryAttachments.h index 0dba9543f..c2fa55b44 100644 --- a/src/core/EntryAttachments.h +++ b/src/core/EntryAttachments.h @@ -31,7 +31,7 @@ public: explicit EntryAttachments(QObject* parent = nullptr); QList keys() const; bool hasKey(const QString& key) const; - QList values() const; + QSet values() const; QByteArray value(const QString& key) const; void set(const QString& key, const QByteArray& value); void remove(const QString& key); @@ -41,6 +41,7 @@ public: void copyDataFrom(const EntryAttachments* other); bool operator==(const EntryAttachments& other) const; bool operator!=(const EntryAttachments& other) const; + int attachmentsSize() const; signals: void modified(); diff --git a/src/core/EntryAttributes.cpp b/src/core/EntryAttributes.cpp index 8cc7f2f0a..629d8e802 100644 --- a/src/core/EntryAttributes.cpp +++ b/src/core/EntryAttributes.cpp @@ -283,14 +283,11 @@ void EntryAttributes::clear() emit modified(); } -int EntryAttributes::attributesSize() +int EntryAttributes::attributesSize() const { int size = 0; - - QMapIterator i(m_attributes); - while (i.hasNext()) { - i.next(); - size += i.value().toUtf8().size(); + for (auto it = m_attributes.constBegin(); it != m_attributes.constEnd(); ++it) { + size += it.key().toUtf8().size() + it.value().toUtf8().size(); } return size; } diff --git a/src/core/EntryAttributes.h b/src/core/EntryAttributes.h index f483b8a9b..3eca9171c 100644 --- a/src/core/EntryAttributes.h +++ b/src/core/EntryAttributes.h @@ -45,7 +45,7 @@ public: void copyCustomKeysFrom(const EntryAttributes* other); bool areCustomKeysDifferent(const EntryAttributes* other); void clear(); - int attributesSize(); + int attributesSize() const; void copyDataFrom(const EntryAttributes* other); bool operator==(const EntryAttributes& other) const; bool operator!=(const EntryAttributes& other) const; diff --git a/tests/TestEntry.cpp b/tests/TestEntry.cpp index 47082d12f..598ad33fd 100644 --- a/tests/TestEntry.cpp +++ b/tests/TestEntry.cpp @@ -20,7 +20,6 @@ #include -#include "core/Database.h" #include "core/Entry.h" #include "core/Group.h" #include "crypto/Crypto.h" @@ -48,6 +47,7 @@ void TestEntry::testHistoryItemDeletion() delete entry; } + void TestEntry::testCopyDataFrom() { Entry* entry = new Entry(); diff --git a/tests/TestModified.cpp b/tests/TestModified.cpp index 70a4a4844..6bc447270 100644 --- a/tests/TestModified.cpp +++ b/tests/TestModified.cpp @@ -288,14 +288,14 @@ void TestModified::testEntrySets() delete db; } -void TestModified::testHistoryItem() +void TestModified::testHistoryItems() { - Entry* entry = new Entry(); + QScopedPointer entry(new Entry()); QDateTime created = entry->timeInfo().creationTime(); entry->setUuid(Uuid::random()); entry->setTitle("a"); entry->setTags("a"); - EntryAttributes* attributes = new EntryAttributes(); + QScopedPointer attributes(new EntryAttributes()); attributes->copyCustomKeysFrom(entry->attributes()); Entry* historyEntry; @@ -338,15 +338,12 @@ void TestModified::testHistoryItem() attributes->set("k", "myvalue"); entry->beginUpdate(); - entry->attributes()->copyCustomKeysFrom(attributes); + entry->attributes()->copyCustomKeysFrom(attributes.data()); entry->endUpdate(); QCOMPARE(entry->historyItems().size(), ++historyItemsSize); QVERIFY(!entry->historyItems().at(historyItemsSize - 1)->attributes()->keys().contains("k")); - delete attributes; - delete entry; - - Database* db = new Database(); + QScopedPointer db(new Database()); Group* root = db->rootGroup(); db->metadata()->setHistoryMaxItems(3); db->metadata()->setHistoryMaxSize(-1); @@ -400,12 +397,16 @@ void TestModified::testHistoryItem() entry2->endUpdate(); QCOMPARE(entry2->historyItems().size(), 0); - db->metadata()->setHistoryMaxItems(-1); - db->metadata()->setHistoryMaxSize(17000); + const int historyMaxSize = 19000; + db->metadata()->setHistoryMaxItems(-1); + db->metadata()->setHistoryMaxSize(historyMaxSize); + + const QString key("test"); entry2->beginUpdate(); - entry2->attachments()->set("test", QByteArray(18000, 'X')); + entry2->attachments()->set(key, QByteArray(18000, 'X')); entry2->endUpdate(); + QCOMPARE(entry2->attachments()->attachmentsSize(), 18000 + key.size()); QCOMPARE(entry2->historyItems().size(), 1); historyEntry2 = entry2->historyItems().at(0); @@ -417,53 +418,167 @@ void TestModified::testHistoryItem() QCOMPARE(entry2->historyItems().size(), 2); entry2->beginUpdate(); - entry2->attachments()->remove("test"); + entry2->attachments()->remove(key); entry2->endUpdate(); - QCOMPARE(entry2->historyItems().size(), 0); + QCOMPARE(entry2->attachments()->attachmentsSize(), 0); + QCOMPARE(entry2->historyItems().size(), 1); entry2->beginUpdate(); entry2->attachments()->set("test2", QByteArray(6000, 'a')); entry2->endUpdate(); - QCOMPARE(entry2->historyItems().size(), 1); + QCOMPARE(entry2->attachments()->attachmentsSize(), 6000 + key.size() + 1); + QCOMPARE(entry2->historyItems().size(), 2); entry2->beginUpdate(); entry2->attachments()->set("test3", QByteArray(6000, 'b')); entry2->endUpdate(); + QCOMPARE(entry2->attachments()->attachmentsSize(), 12000 + (key.size() + 1) * 2); QCOMPARE(entry2->historyItems().size(), 2); entry2->beginUpdate(); entry2->attachments()->set("test4", QByteArray(6000, 'c')); entry2->endUpdate(); + QCOMPARE(entry2->attachments()->attachmentsSize(), 18000 + (key.size() + 1) * 3); QCOMPARE(entry2->historyItems().size(), 3); entry2->beginUpdate(); entry2->attachments()->set("test5", QByteArray(6000, 'd')); entry2->endUpdate(); - QCOMPARE(entry2->historyItems().size(), 4); - - Entry* entry3 = new Entry(); - entry3->setGroup(root); - QCOMPARE(entry3->historyItems().size(), 0); - - entry3->beginUpdate(); - entry3->attachments()->set("test", QByteArray(6000, 'a')); - entry3->endUpdate(); - QCOMPARE(entry3->historyItems().size(), 1); - - entry3->beginUpdate(); - entry3->attachments()->set("test", QByteArray(6000, 'b')); - entry3->endUpdate(); - QCOMPARE(entry3->historyItems().size(), 2); - - entry3->beginUpdate(); - entry3->attachments()->set("test", QByteArray(6000, 'c')); - entry3->endUpdate(); - QCOMPARE(entry3->historyItems().size(), 3); - - entry3->beginUpdate(); - entry3->attachments()->set("test", QByteArray(6000, 'd')); - entry3->endUpdate(); - QCOMPARE(entry3->historyItems().size(), 2); - - delete db; + QCOMPARE(entry2->attachments()->attachmentsSize(), 24000 + (key.size() + 1) * 4); + QCOMPARE(entry2->historyItems().size(), 1); +} + +void TestModified::testHistoryMaxSize() +{ + QScopedPointer db(new Database()); + const QString key("test"); + + + auto entry1 = new Entry(); + entry1->setGroup(db->rootGroup()); + QCOMPARE(entry1->historyItems().size(), 0); + + const int reservedSize1 = entry1->attributes()->attributesSize(); + db->metadata()->setHistoryMaxItems(-1); + db->metadata()->setHistoryMaxSize(18000 + key.size() * 3 + reservedSize1 * 4); + + entry1->beginUpdate(); + entry1->attachments()->set(key, QByteArray(6000, 'a')); + entry1->endUpdate(); + QCOMPARE(entry1->attachments()->attachmentsSize(), 6000 + key.size()); + QCOMPARE(entry1->historyItems().size(), 1); + + entry1->beginUpdate(); + entry1->attachments()->set(key, QByteArray(6000, 'b')); + entry1->endUpdate(); + QCOMPARE(entry1->attachments()->attachmentsSize(), 6000 + key.size()); + QCOMPARE(entry1->historyItems().size(), 2); + + entry1->beginUpdate(); + entry1->attachments()->set(key, QByteArray(6000, 'c')); + entry1->endUpdate(); + QCOMPARE(entry1->attachments()->attachmentsSize(), 6000 + key.size()); + QCOMPARE(entry1->historyItems().size(), 3); + + entry1->beginUpdate(); + entry1->attachments()->set(key, QByteArray(6000, 'd')); + entry1->endUpdate(); + QCOMPARE(entry1->attachments()->attachmentsSize(), 6000 + key.size()); + QCOMPARE(entry1->historyItems().size(), 4); + + + auto entry2 = new Entry(); + entry2->setGroup(db->rootGroup()); + QCOMPARE(entry2->historyItems().size(), 0); + + const int historyMaxSize = 17000; + const int reservedSize2 = entry2->attributes()->attributesSize(); + db->metadata()->setHistoryMaxSize(historyMaxSize); + + entry2->beginUpdate(); + entry2->attachments()->set(key, QByteArray(historyMaxSize - key.size() - reservedSize2 + 1, 'a')); + entry2->endUpdate(); + QCOMPARE(entry2->attachments()->attachmentsSize(), historyMaxSize - reservedSize2 + 1); + QCOMPARE(entry2->historyItems().size(), 1); + + // history size overflow + entry2->beginUpdate(); + entry2->attachments()->set(key, QByteArray(historyMaxSize - key.size() - reservedSize2 + 1, 'b')); + entry2->endUpdate(); + QCOMPARE(entry2->historyItems().size(), 0); + + entry2->beginUpdate(); + entry2->attachments()->remove(key); + entry2->endUpdate(); + QCOMPARE(entry2->attachments()->attachmentsSize(), 0); + QCOMPARE(entry2->historyItems().size(), 0); + + entry2->beginUpdate(); + entry2->attachments()->set(key, QByteArray(historyMaxSize - key.size() - reservedSize2 + 1, 'a')); + entry2->endUpdate(); + QCOMPARE(entry2->attachments()->attachmentsSize(), historyMaxSize - reservedSize2 + 1); + QCOMPARE(entry2->historyItems().size(), 1); + + // history size overflow + entry2->beginUpdate(); + entry2->attachments()->set(key, QByteArray(historyMaxSize - key.size() - reservedSize2 + 1, 'b')); + entry2->endUpdate(); + QCOMPARE(entry2->historyItems().size(), 0); + + entry2->beginUpdate(); + entry2->attachments()->remove(key); + entry2->endUpdate(); + QCOMPARE(entry2->attachments()->attachmentsSize(), 0); + QCOMPARE(entry2->historyItems().size(), 0); + + entry2->beginUpdate(); + entry2->setTags(QByteArray(historyMaxSize - reservedSize2 + 1, 'a')); + entry2->endUpdate(); + QCOMPARE(entry2->tags().size(), historyMaxSize - reservedSize2 + 1); + QCOMPARE(entry2->historyItems().size(), 1); + + // history size overflow + entry2->beginUpdate(); + entry2->setTags(QByteArray(historyMaxSize - reservedSize2 + 1, 'b')); + entry2->endUpdate(); + QCOMPARE(entry2->historyItems().size(), 0); + + entry2->beginUpdate(); + entry2->setTags(""); + entry2->endUpdate(); + QCOMPARE(entry2->historyItems().size(), 0); + + entry2->beginUpdate(); + entry2->attributes()->set(key, QByteArray(historyMaxSize - key.size() - reservedSize2 + 1, 'a')); + entry2->endUpdate(); + QCOMPARE(entry2->attributes()->attributesSize(), historyMaxSize + 1); + QCOMPARE(entry2->historyItems().size(), 1); + + // history size overflow + entry2->beginUpdate(); + entry2->attributes()->set(key, QByteArray(historyMaxSize - key.size() - reservedSize2 + 1, 'b')); + entry2->endUpdate(); + QCOMPARE(entry2->attributes()->attributesSize(), historyMaxSize + 1); + QCOMPARE(entry2->historyItems().size(), 0); + + entry2->beginUpdate(); + entry2->attributes()->remove(key); + entry2->endUpdate(); + QCOMPARE(entry2->attributes()->attributesSize(), reservedSize2); + QCOMPARE(entry2->historyItems().size(), 0); + + entry2->beginUpdate(); + AutoTypeAssociations::Association association; + association.window = key; + association.sequence = QByteArray(historyMaxSize - key.size() - reservedSize2 + 1, 'a'); + entry2->autoTypeAssociations()->add(association); + entry2->endUpdate(); + QCOMPARE(entry2->autoTypeAssociations()->associationsSize(), historyMaxSize - reservedSize2 + 1); + QCOMPARE(entry2->historyItems().size(), 1); + + entry2->beginUpdate(); + entry2->autoTypeAssociations()->remove(0); + entry2->endUpdate(); + QCOMPARE(entry2->autoTypeAssociations()->associationsSize(), 0); + QCOMPARE(entry2->historyItems().size(), 0); } diff --git a/tests/TestModified.h b/tests/TestModified.h index 518bea7c0..51c429871 100644 --- a/tests/TestModified.h +++ b/tests/TestModified.h @@ -29,7 +29,8 @@ private slots: void testSignals(); void testGroupSets(); void testEntrySets(); - void testHistoryItem(); + void testHistoryItems(); + void testHistoryMaxSize(); }; #endif // KEEPASSX_TESTMODIFIED_H