diff --git a/.gitignore b/.gitignore
index 77211ffa6..2e9cb860c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
CMakeLists.txt.*
+build/
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3ec602313..eccd81490 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -13,6 +13,12 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
+if(NOT CMAKE_BUILD_TYPE)
+ set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING
+ "Choose the type of build, options are: None Debug Release RelWithDebInfo Debug Debugfull Profile MinSizeRel."
+ FORCE)
+endif()
+
project(KeePassX)
cmake_minimum_required(VERSION 2.6.4)
@@ -58,7 +64,7 @@ endmacro(add_gcc_compiler_flags)
add_definitions(-DQT_NO_KEYWORDS -DQT_NO_EXCEPTIONS -DQT_NO_STL -DQT_STRICT_ITERATORS -DQT_NO_CAST_TO_ASCII)
-add_gcc_compiler_flags("-fno-common -fstack-protector --param=ssp-buffer-size=4 -D_FORTIFY_SOURCE=2")
+add_gcc_compiler_flags("-fno-common -fstack-protector --param=ssp-buffer-size=4")
add_gcc_compiler_flags("-Wall -Wextra -Wundef -Wpointer-arith -Wno-long-long")
add_gcc_compiler_flags("-Wformat=2 -Wmissing-format-attribute")
add_gcc_compiler_flags("-fvisibility=hidden")
@@ -68,6 +74,11 @@ add_gcc_compiler_cxxflags("-fno-exceptions -fno-rtti")
add_gcc_compiler_cxxflags("-Wnon-virtual-dtor -Wold-style-cast -Woverloaded-virtual")
add_gcc_compiler_cflags("-Wchar-subscripts -Wwrite-strings")
+string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWER)
+if (CMAKE_BUILD_TYPE_LOWER MATCHES (release|relwithdebinfo|minsizerel))
+ add_gcc_compiler_flags("-D_FORTIFY_SOURCE=2")
+endif()
+
check_c_compiler_flag("-Werror=format-security -Werror=implicit-function-declaration" WERROR_C_AVAILABLE)
check_cxx_compiler_flag("-Werror=format-security" WERROR_CXX_AVAILABLE)
if(WERROR_C_AVAILABLE AND WERROR_CXX_AVAILABLE)
diff --git a/src/core/Database.cpp b/src/core/Database.cpp
index f125a2786..0394051fc 100644
--- a/src/core/Database.cpp
+++ b/src/core/Database.cpp
@@ -32,13 +32,14 @@ QHash Database::m_uuidMap;
Database::Database()
: m_metadata(new Metadata(this))
, m_timer(new QTimer(this))
- , m_cipher(KeePass2::CIPHER_AES)
- , m_compressionAlgo(CompressionGZip)
- , m_transformRounds(50000)
- , m_hasKey(false)
, m_emitModified(false)
, m_uuid(Uuid::random())
{
+ m_data.cipher = KeePass2::CIPHER_AES;
+ m_data.compressionAlgo = CompressionGZip;
+ m_data.transformRounds = 50000;
+ m_data.hasKey = false;
+
setRootGroup(new Group());
rootGroup()->setUuid(Uuid::random());
m_timer->setSingleShot(true);
@@ -150,60 +151,60 @@ void Database::addDeletedObject(const Uuid& uuid)
Uuid Database::cipher() const
{
- return m_cipher;
+ return m_data.cipher;
}
Database::CompressionAlgorithm Database::compressionAlgo() const
{
- return m_compressionAlgo;
+ return m_data.compressionAlgo;
}
QByteArray Database::transformSeed() const
{
- return m_transformSeed;
+ return m_data.transformSeed;
}
quint64 Database::transformRounds() const
{
- return m_transformRounds;
+ return m_data.transformRounds;
}
QByteArray Database::transformedMasterKey() const
{
- return m_transformedMasterKey;
+ return m_data.transformedMasterKey;
}
void Database::setCipher(const Uuid& cipher)
{
Q_ASSERT(!cipher.isNull());
- m_cipher = cipher;
+ m_data.cipher = cipher;
}
void Database::setCompressionAlgo(Database::CompressionAlgorithm algo)
{
Q_ASSERT(static_cast(algo) <= CompressionAlgorithmMax);
- m_compressionAlgo = algo;
+ m_data.compressionAlgo = algo;
}
void Database::setTransformRounds(quint64 rounds)
{
- if (m_transformRounds != rounds) {
- m_transformRounds = rounds;
+ if (m_data.transformRounds != rounds) {
+ m_data.transformRounds = rounds;
- if (m_hasKey) {
- setKey(m_key);
+ if (m_data.hasKey) {
+ setKey(m_data.key);
}
}
}
void Database::setKey(const CompositeKey& key, const QByteArray& transformSeed, bool updateChangedTime)
{
- m_key = key;
- m_transformSeed = transformSeed;
- m_transformedMasterKey = key.transform(transformSeed, transformRounds());
- m_hasKey = true;
+ m_data.key = key;
+ m_data.transformSeed = transformSeed;
+ m_data.transformedMasterKey = key.transform(transformSeed, transformRounds());
+ m_data.hasKey = true;
if (updateChangedTime) {
m_metadata->setMasterKeyChanged(Tools::currentDateTimeUtc());
}
@@ -217,14 +218,14 @@ void Database::setKey(const CompositeKey& key)
bool Database::hasKey() const
{
- return m_hasKey;
+ return m_data.hasKey;
}
bool Database::verifyKey(const CompositeKey& key) const
{
Q_ASSERT(hasKey());
- return (m_key.rawKey() == key.rawKey());
+ return (m_data.key.rawKey() == key.rawKey());
}
void Database::createRecycleBin()
@@ -269,6 +270,12 @@ void Database::setEmitModified(bool value)
m_emitModified = value;
}
+void Database::copyAttributesFrom(const Database* other)
+{
+ m_data = other->m_data;
+ m_metadata->copyAttributesFrom(other->m_metadata);
+}
+
Uuid Database::uuid()
{
return m_uuid;
diff --git a/src/core/Database.h b/src/core/Database.h
index 267d6b0b4..0ee9a9659 100644
--- a/src/core/Database.h
+++ b/src/core/Database.h
@@ -49,6 +49,17 @@ public:
};
static const quint32 CompressionAlgorithmMax = CompressionGZip;
+ struct DatabaseData
+ {
+ Uuid cipher;
+ CompressionAlgorithm compressionAlgo;
+ QByteArray transformSeed;
+ quint64 transformRounds;
+ QByteArray transformedMasterKey;
+ CompositeKey key;
+ bool hasKey;
+ };
+
Database();
~Database();
Group* rootGroup();
@@ -91,6 +102,7 @@ public:
void recycleEntry(Entry* entry);
void recycleGroup(Group* group);
void setEmitModified(bool value);
+ void copyAttributesFrom(const Database* other);
/**
* Returns a unique id that is only valid as long as the Database exists.
@@ -124,15 +136,7 @@ private:
Group* m_rootGroup;
QList m_deletedObjects;
QTimer* m_timer;
-
- Uuid m_cipher;
- CompressionAlgorithm m_compressionAlgo;
- QByteArray m_transformSeed;
- quint64 m_transformRounds;
- QByteArray m_transformedMasterKey;
-
- CompositeKey m_key;
- bool m_hasKey;
+ DatabaseData m_data;
bool m_emitModified;
Uuid m_uuid;
diff --git a/src/core/Entry.cpp b/src/core/Entry.cpp
index 7848300a8..41616cb82 100644
--- a/src/core/Entry.cpp
+++ b/src/core/Entry.cpp
@@ -94,8 +94,14 @@ QImage Entry::icon() const
return databaseIcons()->icon(m_data.iconNumber);
}
else {
- // TODO: check if database() is 0
- return database()->metadata()->customIcon(m_data.customIcon);
+ Q_ASSERT(database());
+
+ if (database()) {
+ return database()->metadata()->customIcon(m_data.customIcon);
+ }
+ else {
+ return QImage();
+ }
}
}
@@ -105,9 +111,10 @@ QPixmap Entry::iconPixmap() const
return databaseIcons()->iconPixmap(m_data.iconNumber);
}
else {
+ Q_ASSERT(database());
+
QPixmap pixmap;
- if (!QPixmapCache::find(m_pixmapCacheKey, &pixmap)) {
- // TODO: check if database() is 0
+ if (database() && !QPixmapCache::find(m_pixmapCacheKey, &pixmap)) {
pixmap = QPixmap::fromImage(database()->metadata()->customIcon(m_data.customIcon));
m_pixmapCacheKey = QPixmapCache::insert(pixmap);
}
@@ -432,22 +439,40 @@ void Entry::truncateHistory()
}
}
-Entry* Entry::clone() const
+Entry* Entry::clone(CloneFlags flags) const
{
Entry* entry = new Entry();
entry->setUpdateTimeinfo(false);
- entry->m_uuid = Uuid::random();
+ if (flags & CloneNewUuid) {
+ entry->m_uuid = Uuid::random();
+ }
+ else {
+ entry->m_uuid = m_uuid;
+ }
entry->m_data = m_data;
entry->m_attributes->copyDataFrom(m_attributes);
entry->m_attachments->copyDataFrom(m_attachments);
entry->m_autoTypeAssociations->copyDataFrom(this->m_autoTypeAssociations);
+ if (flags & CloneIncludeHistory) {
+ Q_FOREACH (Entry* historyItem, m_history) {
+ Entry* historyItemClone = historyItem->clone(flags & ~CloneIncludeHistory & ~CloneNewUuid);
+ historyItemClone->setUpdateTimeinfo(false);
+ historyItemClone->setUuid(entry->uuid());
+ historyItemClone->setUpdateTimeinfo(true);
+ entry->addHistoryItem(historyItemClone);
+ }
+ }
entry->setUpdateTimeinfo(true);
- QDateTime now = Tools::currentDateTimeUtc();
- entry->m_data.timeInfo.setCreationTime(now);
- entry->m_data.timeInfo.setLastModificationTime(now);
- entry->m_data.timeInfo.setLastAccessTime(now);
- entry->m_data.timeInfo.setLocationChanged(now);
+ if (flags & CloneResetTimeInfo) {
+ QDateTime now = Tools::currentDateTimeUtc();
+ entry->m_data.timeInfo.setCreationTime(now);
+ entry->m_data.timeInfo.setLastModificationTime(now);
+ entry->m_data.timeInfo.setLastAccessTime(now);
+ entry->m_data.timeInfo.setLocationChanged(now);
+ }
+
+
return entry;
}
diff --git a/src/core/Entry.h b/src/core/Entry.h
index d4a52933f..c2c2938c1 100644
--- a/src/core/Entry.h
+++ b/src/core/Entry.h
@@ -110,7 +110,22 @@ public:
void addHistoryItem(Entry* entry);
void removeHistoryItems(const QList& historyEntries);
void truncateHistory();
- Entry* clone() const;
+
+ enum CloneFlag {
+ CloneNoFlags = 0,
+ CloneNewUuid = 1, // generate a random uuid for the clone
+ CloneResetTimeInfo = 2, // set all TimeInfo attributes to the current time
+ CloneIncludeHistory = 4 // clone the history items
+ };
+ Q_DECLARE_FLAGS(CloneFlags, CloneFlag)
+
+ /**
+ * Creates a duplicate of this entry except that the returned entry isn't
+ * part of any group.
+ * Note that you need to copy the custom icons manually when inserting the
+ * new entry into another database.
+ */
+ Entry* clone(CloneFlags flags) const;
void copyDataFrom(const Entry* other);
QString resolvePlaceholders(const QString& str) const;
@@ -160,4 +175,6 @@ private:
bool m_updateTimeinfo;
};
+Q_DECLARE_OPERATORS_FOR_FLAGS(Entry::CloneFlags)
+
#endif // KEEPASSX_ENTRY_H
diff --git a/src/core/Group.cpp b/src/core/Group.cpp
index 0868a82ad..ada9e9723 100644
--- a/src/core/Group.cpp
+++ b/src/core/Group.cpp
@@ -115,8 +115,14 @@ QImage Group::icon() const
return databaseIcons()->icon(m_data.iconNumber);
}
else {
- // TODO: check if m_db is 0
- return m_db->metadata()->customIcon(m_data.customIcon);
+ Q_ASSERT(m_db);
+
+ if (m_db) {
+ return m_db->metadata()->customIcon(m_data.customIcon);
+ }
+ else {
+ return QImage();
+ }
}
}
@@ -126,9 +132,10 @@ QPixmap Group::iconPixmap() const
return databaseIcons()->iconPixmap(m_data.iconNumber);
}
else {
+ Q_ASSERT(m_db);
+
QPixmap pixmap;
- if (!QPixmapCache::find(m_pixmapCacheKey, &pixmap)) {
- // TODO: check if m_db is 0
+ if (m_db && !QPixmapCache::find(m_pixmapCacheKey, &pixmap)) {
pixmap = QPixmap::fromImage(m_db->metadata()->customIcon(m_data.customIcon));
m_pixmapCacheKey = QPixmapCache::insert(pixmap);
}
@@ -457,11 +464,8 @@ QSet Group::customIconsRecursive() const
return result;
}
-Group* Group::clone() const
+Group* Group::clone(Entry::CloneFlags entryFlags) const
{
- // TODO: what to do about custom icons?
- // they won't be available when changing the database later
-
Group* clonedGroup = new Group();
clonedGroup->setUpdateTimeinfo(false);
@@ -470,7 +474,7 @@ Group* Group::clone() const
clonedGroup->m_data = m_data;
Q_FOREACH (Entry* entry, entries()) {
- Entry* clonedEntry = entry->clone();
+ Entry* clonedEntry = entry->clone(entryFlags);
clonedEntry->setGroup(clonedGroup);
}
@@ -496,6 +500,22 @@ void Group::copyDataFrom(const Group* other)
m_lastTopVisibleEntry = other->m_lastTopVisibleEntry;
}
+Database* Group::exportToDb()
+{
+ Q_ASSERT(database());
+
+ Database* db = new Database();
+ Group* clonedGroup = clone(Entry::CloneNewUuid | Entry::CloneIncludeHistory);
+ clonedGroup->setParent(db->rootGroup());
+
+ QSet customIcons = customIconsRecursive();
+ db->metadata()->copyCustomIcons(customIcons, database()->metadata());
+
+ db->copyAttributesFrom(database());
+
+ return db;
+}
+
void Group::addEntry(Entry* entry)
{
Q_ASSERT(entry);
diff --git a/src/core/Group.h b/src/core/Group.h
index b1a51d181..558bebcc9 100644
--- a/src/core/Group.h
+++ b/src/core/Group.h
@@ -100,8 +100,16 @@ public:
QList entriesRecursive(bool includeHistoryItems = false) const;
QList groupsRecursive(bool includeSelf) const;
QSet customIconsRecursive() const;
- Group* clone() const;
+ /**
+ * Creates a duplicate of this group including all child entries and groups.
+ * The exceptions are that the returned group doesn't have a parent group
+ * and all TimeInfo attributes are set to the current time.
+ * Note that you need to copy the custom icons manually when inserting the
+ * new group into another database.
+ */
+ Group* clone(Entry::CloneFlags entryFlags = Entry::CloneNewUuid | Entry::CloneResetTimeInfo) const;
void copyDataFrom(const Group* other);
+ Database* exportToDb();
QList search(const QString& searchTerm, Qt::CaseSensitivity caseSensitivity,
bool resolveInherit = true);
diff --git a/src/core/Metadata.cpp b/src/core/Metadata.cpp
index 5947255e5..074291c73 100644
--- a/src/core/Metadata.cpp
+++ b/src/core/Metadata.cpp
@@ -26,25 +26,26 @@ const int Metadata::DefaultHistoryMaxSize = 6 * 1024 * 1024;
Metadata::Metadata(QObject* parent)
: QObject(parent)
- , m_generator("KeePassX")
- , m_maintenanceHistoryDays(365)
- , m_protectTitle(false)
- , m_protectUsername(false)
- , m_protectPassword(true)
- , m_protectUrl(false)
- , m_protectNotes(false)
- // , m_autoEnableVisualHiding(false)
- , m_recycleBinEnabled(true)
- , m_masterKeyChangeRec(-1)
- , m_masterKeyChangeForce(-1)
- , m_historyMaxItems(DefaultHistoryMaxItems)
- , m_historyMaxSize(DefaultHistoryMaxSize)
, m_updateDatetime(true)
{
+ m_data.generator = "KeePassX";
+ m_data.maintenanceHistoryDays = 365;
+ m_data.masterKeyChangeRec = -1;
+ m_data.masterKeyChangeForce = -1;
+ m_data.historyMaxItems = DefaultHistoryMaxItems;
+ m_data.historyMaxSize = DefaultHistoryMaxSize;
+ m_data.recycleBinEnabled = true;
+ m_data.protectTitle = false;
+ m_data.protectUsername = false;
+ m_data.protectPassword = true;
+ m_data.protectUrl = false;
+ m_data.protectNotes = false;
+ // m_data.autoEnableVisualHiding = false;
+
QDateTime now = Tools::currentDateTimeUtc();
- m_nameChanged = now;
- m_descriptionChanged = now;
- m_defaultUserNameChanged = now;
+ m_data.nameChanged = now;
+ m_data.descriptionChanged = now;
+ m_data.defaultUserNameChanged = now;
m_recycleBinChanged = now;
m_entryTemplatesGroupChanged = now;
m_masterKeyChanged = now;
@@ -81,74 +82,79 @@ void Metadata::setUpdateDatetime(bool value)
m_updateDatetime = value;
}
+void Metadata::copyAttributesFrom(const Metadata* other)
+{
+ m_data = other->m_data;
+}
+
QString Metadata::generator() const
{
- return m_generator;
+ return m_data.generator;
}
QString Metadata::name() const
{
- return m_name;
+ return m_data.name;
}
QDateTime Metadata::nameChanged() const
{
- return m_nameChanged;
+ return m_data.nameChanged;
}
QString Metadata::description() const
{
- return m_description;
+ return m_data.description;
}
QDateTime Metadata::descriptionChanged() const
{
- return m_descriptionChanged;
+ return m_data.descriptionChanged;
}
QString Metadata::defaultUserName() const
{
- return m_defaultUserName;
+ return m_data.defaultUserName;
}
QDateTime Metadata::defaultUserNameChanged() const
{
- return m_defaultUserNameChanged;
+ return m_data.defaultUserNameChanged;
}
int Metadata::maintenanceHistoryDays() const
{
- return m_maintenanceHistoryDays;
+ return m_data.maintenanceHistoryDays;
}
QColor Metadata::color() const
{
- return m_color;
+ return m_data.color;
}
bool Metadata::protectTitle() const
{
- return m_protectTitle;
+ return m_data.protectTitle;
}
bool Metadata::protectUsername() const
{
- return m_protectUsername;
+ return m_data.protectUsername;
}
bool Metadata::protectPassword() const
{
- return m_protectPassword;
+ return m_data.protectPassword;
}
bool Metadata::protectUrl() const
{
- return m_protectUrl;
+ return m_data.protectUrl;
}
bool Metadata::protectNotes() const
{
- return m_protectNotes;
+ return m_data.protectNotes;
}
/*bool Metadata::autoEnableVisualHiding() const
@@ -178,7 +184,7 @@ QList Metadata::customIconsOrder() const
bool Metadata::recycleBinEnabled() const
{
- return m_recycleBinEnabled;
+ return m_data.recycleBinEnabled;
}
Group* Metadata::recycleBin()
@@ -223,22 +229,22 @@ QDateTime Metadata::masterKeyChanged() const
int Metadata::masterKeyChangeRec() const
{
- return m_masterKeyChangeRec;
+ return m_data.masterKeyChangeRec;
}
int Metadata::masterKeyChangeForce() const
{
- return m_masterKeyChangeForce;
+ return m_data.masterKeyChangeForce;
}
int Metadata::historyMaxItems() const
{
- return m_historyMaxItems;
+ return m_data.historyMaxItems;
}
int Metadata::historyMaxSize() const
{
- return m_historyMaxSize;
+ return m_data.historyMaxSize;
}
QHash Metadata::customFields() const
@@ -248,12 +254,12 @@ QHash Metadata::customFields() const
void Metadata::setGenerator(const QString& value)
{
- set(m_generator, value);
+ set(m_data.generator, value);
}
void Metadata::setName(const QString& value)
{
- if (set(m_name, value, m_nameChanged)) {
+ if (set(m_data.name, value, m_data.nameChanged)) {
Q_EMIT nameTextChanged();
}
}
@@ -261,64 +267,64 @@ void Metadata::setName(const QString& value)
void Metadata::setNameChanged(const QDateTime& value)
{
Q_ASSERT(value.timeSpec() == Qt::UTC);
- m_nameChanged = value;
+ m_data.nameChanged = value;
}
void Metadata::setDescription(const QString& value)
{
- set(m_description, value, m_descriptionChanged);
+ set(m_data.description, value, m_data.descriptionChanged);
}
void Metadata::setDescriptionChanged(const QDateTime& value)
{
Q_ASSERT(value.timeSpec() == Qt::UTC);
- m_descriptionChanged = value;
+ m_data.descriptionChanged = value;
}
void Metadata::setDefaultUserName(const QString& value)
{
- set(m_defaultUserName, value, m_defaultUserNameChanged);
+ set(m_data.defaultUserName, value, m_data.defaultUserNameChanged);
}
void Metadata::setDefaultUserNameChanged(const QDateTime& value)
{
Q_ASSERT(value.timeSpec() == Qt::UTC);
- m_defaultUserNameChanged = value;
+ m_data.defaultUserNameChanged = value;
}
void Metadata::setMaintenanceHistoryDays(int value)
{
- set(m_maintenanceHistoryDays, value);
+ set(m_data.maintenanceHistoryDays, value);
}
void Metadata::setColor(const QColor& value)
{
- set(m_color, value);
+ set(m_data.color, value);
}
void Metadata::setProtectTitle(bool value)
{
- set(m_protectTitle, value);
+ set(m_data.protectTitle, value);
}
void Metadata::setProtectUsername(bool value)
{
- set(m_protectUsername, value);
+ set(m_data.protectUsername, value);
}
void Metadata::setProtectPassword(bool value)
{
- set(m_protectPassword, value);
+ set(m_data.protectPassword, value);
}
void Metadata::setProtectUrl(bool value)
{
- set(m_protectUrl, value);
+ set(m_data.protectUrl, value);
}
void Metadata::setProtectNotes(bool value)
{
- set(m_protectNotes, value);
+ set(m_data.protectNotes, value);
}
/*void Metadata::setAutoEnableVisualHiding(bool value)
@@ -361,7 +367,7 @@ void Metadata::copyCustomIcons(const QSet& iconList, const Metadata* other
void Metadata::setRecycleBinEnabled(bool value)
{
- set(m_recycleBinEnabled, value);
+ set(m_data.recycleBinEnabled, value);
}
void Metadata::setRecycleBin(Group* group)
@@ -404,22 +410,22 @@ void Metadata::setMasterKeyChanged(const QDateTime& value)
void Metadata::setMasterKeyChangeRec(int value)
{
- set(m_masterKeyChangeRec, value);
+ set(m_data.masterKeyChangeRec, value);
}
void Metadata::setMasterKeyChangeForce(int value)
{
- set(m_masterKeyChangeForce, value);
+ set(m_data.masterKeyChangeForce, value);
}
void Metadata::setHistoryMaxItems(int value)
{
- set(m_historyMaxItems, value);
+ set(m_data.historyMaxItems, value);
}
void Metadata::setHistoryMaxSize(int value)
{
- set(m_historyMaxSize, value);
+ set(m_data.historyMaxSize, value);
}
void Metadata::addCustomField(const QString& key, const QString& value)
diff --git a/src/core/Metadata.h b/src/core/Metadata.h
index ec3c692e6..cde7e26ae 100644
--- a/src/core/Metadata.h
+++ b/src/core/Metadata.h
@@ -37,6 +37,31 @@ class Metadata : public QObject
public:
explicit Metadata(QObject* parent = Q_NULLPTR);
+ struct MetadataData
+ {
+ QString generator;
+ QString name;
+ QDateTime nameChanged;
+ QString description;
+ QDateTime descriptionChanged;
+ QString defaultUserName;
+ QDateTime defaultUserNameChanged;
+ int maintenanceHistoryDays;
+ QColor color;
+ bool recycleBinEnabled;
+ int historyMaxItems;
+ int historyMaxSize;
+ int masterKeyChangeRec;
+ int masterKeyChangeForce;
+
+ bool protectTitle;
+ bool protectUsername;
+ bool protectPassword;
+ bool protectUrl;
+ bool protectNotes;
+ // bool autoEnableVisualHiding;
+ };
+
QString generator() const;
QString name() const;
QDateTime nameChanged() const;
@@ -107,6 +132,14 @@ public:
void addCustomField(const QString& key, const QString& value);
void removeCustomField(const QString& key);
void setUpdateDatetime(bool value);
+ /*
+ * Copy all attributes from other except:
+ * - Group pointers/uuids
+ * - Master key changed date
+ * - Custom icons
+ * - Custom fields
+ */
+ void copyAttributesFrom(const Metadata* other);
Q_SIGNALS:
void nameTextChanged();
@@ -116,27 +149,11 @@ private:
template bool set(P& property, const V& value);
template bool set(P& property, const V& value, QDateTime& dateTime);
- QString m_generator;
- QString m_name;
- QDateTime m_nameChanged;
- QString m_description;
- QDateTime m_descriptionChanged;
- QString m_defaultUserName;
- QDateTime m_defaultUserNameChanged;
- int m_maintenanceHistoryDays;
- QColor m_color;
-
- bool m_protectTitle;
- bool m_protectUsername;
- bool m_protectPassword;
- bool m_protectUrl;
- bool m_protectNotes;
- // bool m_autoEnableVisualHiding;
+ MetadataData m_data;
QHash m_customIcons;
QList m_customIconsOrder;
- bool m_recycleBinEnabled;
QPointer m_recycleBin;
QDateTime m_recycleBinChanged;
QPointer m_entryTemplatesGroup;
@@ -145,10 +162,6 @@ private:
QPointer m_lastTopVisibleGroup;
QDateTime m_masterKeyChanged;
- int m_masterKeyChangeRec;
- int m_masterKeyChangeForce;
- int m_historyMaxItems;
- int m_historyMaxSize;
QHash m_customFields;
diff --git a/src/core/Tools.cpp b/src/core/Tools.cpp
index 37ef50c42..01ec316e2 100644
--- a/src/core/Tools.cpp
+++ b/src/core/Tools.cpp
@@ -141,7 +141,7 @@ QString imageReaderFilter()
}
}
- formatsStringList.append("*." + QString::fromAscii(format).toLower());
+ formatsStringList.append("*." + QString::fromLatin1(format).toLower());
}
return formatsStringList.join(" ");
diff --git a/src/core/Uuid.cpp b/src/core/Uuid.cpp
index e832d1e84..700f59641 100644
--- a/src/core/Uuid.cpp
+++ b/src/core/Uuid.cpp
@@ -42,12 +42,12 @@ Uuid Uuid::random()
QString Uuid::toBase64() const
{
- return QString::fromAscii(m_data.toBase64());
+ return QString::fromLatin1(m_data.toBase64());
}
QString Uuid::toHex() const
{
- return QString::fromAscii(m_data.toHex());
+ return QString::fromLatin1(m_data.toHex());
}
QByteArray Uuid::toByteArray() const
@@ -85,7 +85,7 @@ bool Uuid::operator!=(const Uuid& other) const
Uuid Uuid::fromBase64(const QString& str)
{
- QByteArray data = QByteArray::fromBase64(str.toAscii());
+ QByteArray data = QByteArray::fromBase64(str.toLatin1());
return Uuid(data);
}
diff --git a/src/format/KeePass2XmlReader.cpp b/src/format/KeePass2XmlReader.cpp
index 6e9aa582d..383269f68 100644
--- a/src/format/KeePass2XmlReader.cpp
+++ b/src/format/KeePass2XmlReader.cpp
@@ -779,7 +779,7 @@ void KeePass2XmlReader::parseEntryString(Entry* entry)
if (isProtected && !value.isEmpty()) {
if (m_randomStream) {
- value = QString::fromUtf8(m_randomStream->process(QByteArray::fromBase64(value.toAscii())));
+ value = QString::fromUtf8(m_randomStream->process(QByteArray::fromBase64(value.toLatin1())));
}
else {
raiseError("Unable to decrypt entry string");
@@ -1053,7 +1053,7 @@ Uuid KeePass2XmlReader::readUuid()
QByteArray KeePass2XmlReader::readBinary()
{
- return QByteArray::fromBase64(readString().toAscii());
+ return QByteArray::fromBase64(readString().toLatin1());
}
QByteArray KeePass2XmlReader::readCompressedBinary()
diff --git a/src/format/KeePass2XmlWriter.cpp b/src/format/KeePass2XmlWriter.cpp
index fa7c4d956..603649812 100644
--- a/src/format/KeePass2XmlWriter.cpp
+++ b/src/format/KeePass2XmlWriter.cpp
@@ -192,7 +192,7 @@ void KeePass2XmlWriter::writeBinaries()
}
if (!data.isEmpty()) {
- m_xml.writeCharacters(QString::fromAscii(data.toBase64()));
+ m_xml.writeCharacters(QString::fromLatin1(data.toBase64()));
}
m_xml.writeEndElement();
}
@@ -341,7 +341,7 @@ void KeePass2XmlWriter::writeEntry(const Entry* entry)
if (m_randomStream) {
m_xml.writeAttribute("Protected", "True");
QByteArray rawData = m_randomStream->process(entry->attributes()->value(key).toUtf8());
- value = QString::fromAscii(rawData.toBase64());
+ value = QString::fromLatin1(rawData.toBase64());
}
else {
m_xml.writeAttribute("ProtectInMemory", "True");
@@ -485,7 +485,7 @@ void KeePass2XmlWriter::writeUuid(const QString& qualifiedName, const Entry* ent
void KeePass2XmlWriter::writeBinary(const QString& qualifiedName, const QByteArray& ba)
{
- writeString(qualifiedName, QString::fromAscii(ba.toBase64()));
+ writeString(qualifiedName, QString::fromLatin1(ba.toBase64()));
}
void KeePass2XmlWriter::writeColor(const QString& qualifiedName, const QColor& color)
diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp
index d6484478d..8c635a70f 100644
--- a/src/gui/DatabaseWidget.cpp
+++ b/src/gui/DatabaseWidget.cpp
@@ -224,7 +224,7 @@ void DatabaseWidget::cloneEntry()
return;
}
- Entry* entry = currentEntry->clone();
+ Entry* entry = currentEntry->clone(Entry::CloneNewUuid | Entry::CloneResetTimeInfo);
entry->setGroup(currentEntry->group());
m_entryView->setFocus();
m_entryView->setCurrentEntry(entry);
diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp
index 7bf4c62ed..a80267790 100644
--- a/src/gui/entry/EditEntryWidget.cpp
+++ b/src/gui/entry/EditEntryWidget.cpp
@@ -26,6 +26,7 @@
#include
#include
+#include "core/Config.h"
#include "core/Database.h"
#include "core/Entry.h"
#include "core/Metadata.h"
@@ -605,9 +606,11 @@ void EditEntryWidget::insertAttachment()
{
Q_ASSERT(!m_history);
- // TODO: save last used dir
- QString filename = fileDialog()->getOpenFileName(this, tr("Select file"),
- QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation));
+ QString defaultDir = config()->get("LastAttachmentDir").toString();
+ if (defaultDir.isEmpty() || !QDir(defaultDir).exists()) {
+ defaultDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation);
+ }
+ QString filename = fileDialog()->getOpenFileName(this, tr("Select file"), defaultDir);
if (filename.isEmpty() || !QFile::exists(filename)) {
return;
}
@@ -637,8 +640,11 @@ void EditEntryWidget::saveCurrentAttachment()
}
QString filename = m_attachmentsModel->keyByIndex(index);
- // TODO: save last used dir
- QDir dir(QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation));
+ QString defaultDirName = config()->get("LastAttachmentDir").toString();
+ if (defaultDirName.isEmpty() || !QDir(defaultDirName).exists()) {
+ defaultDirName = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation);
+ }
+ QDir dir(defaultDirName);
QString savePath = fileDialog()->getSaveFileName(this, tr("Save attachment"),
dir.filePath(filename));
if (!savePath.isEmpty()) {
diff --git a/src/gui/group/GroupModel.cpp b/src/gui/group/GroupModel.cpp
index 73b9a59d9..b33a5ff54 100644
--- a/src/gui/group/GroupModel.cpp
+++ b/src/gui/group/GroupModel.cpp
@@ -298,7 +298,7 @@ bool GroupModel::dropMimeData(const QMimeData* data, Qt::DropAction action,
entry = dragEntry;
}
else {
- entry = dragEntry->clone();
+ entry = dragEntry->clone(Entry::CloneNewUuid | Entry::CloneResetTimeInfo);
}
Database* sourceDb = dragEntry->group()->database();
diff --git a/src/keys/FileKey.cpp b/src/keys/FileKey.cpp
index e8cf2697e..f03a69536 100644
--- a/src/keys/FileKey.cpp
+++ b/src/keys/FileKey.cpp
@@ -122,7 +122,7 @@ void FileKey::create(QIODevice* device)
xmlWriter.writeStartElement("Key");
QByteArray data = randomGen()->randomArray(32);
- xmlWriter.writeTextElement("Data", QString::fromAscii(data.toBase64()));
+ xmlWriter.writeTextElement("Data", QString::fromLatin1(data.toBase64()));
xmlWriter.writeEndElement();
@@ -211,7 +211,7 @@ QByteArray FileKey::loadXmlKey(QXmlStreamReader& xmlReader)
while (!xmlReader.error() && xmlReader.readNextStartElement()) {
if (xmlReader.name() == "Data") {
// TODO: do we need to enforce a specific data.size()?
- data = QByteArray::fromBase64(xmlReader.readElementText().toAscii());
+ data = QByteArray::fromBase64(xmlReader.readElementText().toLatin1());
}
}
diff --git a/tests/TestCryptoHash.cpp b/tests/TestCryptoHash.cpp
index 8ff3c367d..4f258a179 100644
--- a/tests/TestCryptoHash.cpp
+++ b/tests/TestCryptoHash.cpp
@@ -37,13 +37,13 @@ void TestCryptoHash::test()
QCOMPARE(cryptoHash1.result(),
QByteArray::fromHex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"));
- QByteArray source2 = QString("KeePassX").toAscii();
+ QByteArray source2 = QString("KeePassX").toLatin1();
QByteArray result2 = CryptoHash::hash(source2, CryptoHash::Sha256);
QCOMPARE(result2, QByteArray::fromHex("0b56e5f65263e747af4a833bd7dd7ad26a64d7a4de7c68e52364893dca0766b4"));
CryptoHash cryptoHash3(CryptoHash::Sha256);
- cryptoHash3.addData(QString("KeePa").toAscii());
- cryptoHash3.addData(QString("ssX").toAscii());
+ cryptoHash3.addData(QString("KeePa").toLatin1());
+ cryptoHash3.addData(QString("ssX").toLatin1());
QCOMPARE(cryptoHash3.result(),
QByteArray::fromHex("0b56e5f65263e747af4a833bd7dd7ad26a64d7a4de7c68e52364893dca0766b4"));
}
diff --git a/tests/TestEntry.cpp b/tests/TestEntry.cpp
index e3c46b100..9c027b6a2 100644
--- a/tests/TestEntry.cpp
+++ b/tests/TestEntry.cpp
@@ -21,6 +21,12 @@
#include "tests.h"
#include "core/Entry.h"
+#include "crypto/Crypto.h"
+
+void TestEntry::initTestCase()
+{
+ Crypto::init();
+}
void TestEntry::testHistoryItemDeletion()
{
@@ -74,4 +80,46 @@ void TestEntry::testCopyDataFrom()
QCOMPARE(entry2->autoTypeAssociations()->get(1).window, QString("3"));
}
+void TestEntry::testClone()
+{
+ Entry* entryOrg = new Entry();
+ entryOrg->setUuid(Uuid::random());
+ entryOrg->setTitle("Original Title");
+ entryOrg->beginUpdate();
+ entryOrg->setTitle("New Title");
+ entryOrg->endUpdate();
+ TimeInfo entryOrgTime = entryOrg->timeInfo();
+ QDateTime dateTime;
+ dateTime.setTimeSpec(Qt::UTC);
+ dateTime.setMSecsSinceEpoch(60);
+ entryOrgTime.setCreationTime(dateTime);
+ entryOrg->setTimeInfo(entryOrgTime);
+
+ Entry* entryCloneNone = entryOrg->clone(Entry::CloneNoFlags);
+ QCOMPARE(entryCloneNone->uuid(), entryOrg->uuid());
+ QCOMPARE(entryCloneNone->title(), QString("New Title"));
+ QCOMPARE(entryCloneNone->historyItems().size(), 0);
+ QCOMPARE(entryCloneNone->timeInfo().creationTime(), entryOrg->timeInfo().creationTime());
+
+ Entry* entryCloneNewUuid = entryOrg->clone(Entry::CloneNewUuid);
+ QVERIFY(entryCloneNewUuid->uuid() != entryOrg->uuid());
+ QVERIFY(!entryCloneNewUuid->uuid().isNull());
+ QCOMPARE(entryCloneNewUuid->title(), QString("New Title"));
+ QCOMPARE(entryCloneNewUuid->historyItems().size(), 0);
+ QCOMPARE(entryCloneNewUuid->timeInfo().creationTime(), entryOrg->timeInfo().creationTime());
+
+ Entry* entryCloneResetTime = entryOrg->clone(Entry::CloneResetTimeInfo);
+ QCOMPARE(entryCloneNone->uuid(), entryOrg->uuid());
+ QCOMPARE(entryCloneResetTime->title(), QString("New Title"));
+ QCOMPARE(entryCloneResetTime->historyItems().size(), 0);
+ QVERIFY(entryCloneResetTime->timeInfo().creationTime() != entryOrg->timeInfo().creationTime());
+
+ Entry* entryCloneHistory = entryOrg->clone(Entry::CloneIncludeHistory);
+ QCOMPARE(entryCloneNone->uuid(), entryOrg->uuid());
+ QCOMPARE(entryCloneHistory->title(), QString("New Title"));
+ QCOMPARE(entryCloneHistory->historyItems().size(), 1);
+ QCOMPARE(entryCloneHistory->historyItems().first()->title(), QString("Original Title"));
+ QCOMPARE(entryCloneHistory->timeInfo().creationTime(), entryOrg->timeInfo().creationTime());
+}
+
QTEST_GUILESS_MAIN(TestEntry)
diff --git a/tests/TestEntry.h b/tests/TestEntry.h
index fe303a609..ed772d505 100644
--- a/tests/TestEntry.h
+++ b/tests/TestEntry.h
@@ -27,8 +27,10 @@ class TestEntry : public QObject
Q_OBJECT
private Q_SLOTS:
+ void initTestCase();
void testHistoryItemDeletion();
void testCopyDataFrom();
+ void testClone();
};
#endif // KEEPASSX_TESTENTRY_H
diff --git a/tests/TestGroup.cpp b/tests/TestGroup.cpp
index 22845af11..86b55b706 100644
--- a/tests/TestGroup.cpp
+++ b/tests/TestGroup.cpp
@@ -441,8 +441,11 @@ void TestGroup::testClone()
Entry* originalGroupEntry = new Entry();
originalGroupEntry->setGroup(originalGroup);
- originalGroupEntry->setTitle("GroupEntry");
+ originalGroupEntry->setTitle("GroupEntryOld");
originalGroupEntry->setIcon(43);
+ originalGroupEntry->beginUpdate();
+ originalGroupEntry->setTitle("GroupEntry");
+ originalGroupEntry->endUpdate();
Group* subGroup = new Group();
subGroup->setParent(originalGroup);
@@ -465,6 +468,7 @@ void TestGroup::testClone()
QVERIFY(clonedGroupEntry->uuid() != originalGroupEntry->uuid());
QCOMPARE(clonedGroupEntry->title(), QString("GroupEntry"));
QCOMPARE(clonedGroupEntry->iconNumber(), 43);
+ QCOMPARE(clonedGroupEntry->historyItems().size(), 0);
Group* clonedSubGroup = clonedGroup->children().at(0);
QVERIFY(clonedSubGroup->uuid() != subGroup->uuid());
@@ -533,4 +537,48 @@ void TestGroup::testCopyCustomIcons()
QCOMPARE(metaTarget->customIcon(group2Icon).pixel(0, 0), qRgb(4, 5, 6));
}
+void TestGroup::testExportToDb()
+{
+ QImage iconImage(1, 1, QImage::Format_RGB32);
+ iconImage.setPixel(0, 0, qRgb(1, 2, 3));
+ Uuid iconUuid = Uuid::random();
+
+ QImage iconUnusedImage(1, 1, QImage::Format_RGB32);
+ iconUnusedImage.setPixel(0, 0, qRgb(1, 2, 3));
+ Uuid iconUnusedUuid = Uuid::random();
+
+ Database* dbOrg = new Database();
+ Group* groupOrg = new Group();
+ groupOrg->setParent(dbOrg->rootGroup());
+ groupOrg->setName("GTEST");
+ Entry* entryOrg = new Entry();
+ entryOrg->setGroup(groupOrg);
+ entryOrg->setTitle("ETEST");
+ dbOrg->metadata()->addCustomIcon(iconUuid, iconImage);
+ dbOrg->metadata()->addCustomIcon(iconUnusedUuid, iconUnusedImage);
+ entryOrg->setIcon(iconUuid);
+ entryOrg->beginUpdate();
+ entryOrg->setIcon(Entry::DefaultIconNumber);
+ entryOrg->endUpdate();
+
+ Database* dbExp = groupOrg->exportToDb();
+ QCOMPARE(dbExp->rootGroup()->children().size(), 1);
+ Group* groupExp = dbExp->rootGroup()->children().first();
+ QVERIFY(groupExp != groupOrg);
+ QCOMPARE(groupExp->name(), groupOrg->name());
+ QCOMPARE(groupExp->entries().size(), 1);
+
+ Entry* entryExp = groupExp->entries().first();
+ QCOMPARE(entryExp->title(), entryOrg->title());
+ QCOMPARE(dbExp->metadata()->customIcons().size(), 1);
+ QVERIFY(dbExp->metadata()->containsCustomIcon(iconUuid));
+ QCOMPARE(entryExp->iconNumber(), entryOrg->iconNumber());
+
+ QCOMPARE(entryExp->historyItems().size(), 1);
+ QCOMPARE(entryExp->historyItems().first()->iconUuid(), iconUuid);
+
+ delete dbOrg;
+ delete dbExp;
+}
+
QTEST_GUILESS_MAIN(TestGroup)
diff --git a/tests/TestGroup.h b/tests/TestGroup.h
index 40351468b..895c2cc5a 100644
--- a/tests/TestGroup.h
+++ b/tests/TestGroup.h
@@ -35,6 +35,7 @@ private Q_SLOTS:
void testAndConcatenationInSearch();
void testClone();
void testCopyCustomIcons();
+ void testExportToDb();
};
#endif // KEEPASSX_TESTGROUP_H
diff --git a/tests/TestKeePass2XmlReader.cpp b/tests/TestKeePass2XmlReader.cpp
index 3f338fd46..ca57db958 100644
--- a/tests/TestKeePass2XmlReader.cpp
+++ b/tests/TestKeePass2XmlReader.cpp
@@ -33,7 +33,7 @@ namespace QTest {
char* toString(const Uuid& uuid)
{
QByteArray ba = "Uuid(";
- ba += uuid.toBase64().toAscii().constData();
+ ba += uuid.toBase64().toLatin1().constData();
ba += ")";
return qstrdup(ba.constData());
}
@@ -296,7 +296,7 @@ void TestKeePass2XmlReader::testEntry2()
QVERIFY(attrs.isEmpty());
QCOMPARE(entry->attachments()->keys().size(), 1);
- QCOMPARE(QString::fromAscii(entry->attachments()->value("myattach.txt")), QString("abcdefghijk"));
+ QCOMPARE(QString::fromLatin1(entry->attachments()->value("myattach.txt")), QString("abcdefghijk"));
QCOMPARE(entry->autoTypeEnabled(), true);
QCOMPARE(entry->autoTypeObfuscation(), 1);