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);