From ffaf2a8453c9e78d9cd722b7c81c08525adb56f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Revol?= Date: Tue, 20 Nov 2018 23:00:05 +0100 Subject: [PATCH 001/168] Add French translation (untested) --- translations/qtkeychain_fr.ts | 267 ++++++++++++++++++++++++++++++++++ 1 file changed, 267 insertions(+) create mode 100644 translations/qtkeychain_fr.ts diff --git a/translations/qtkeychain_fr.ts b/translations/qtkeychain_fr.ts new file mode 100644 index 00000000..91d0fb39 --- /dev/null +++ b/translations/qtkeychain_fr.ts @@ -0,0 +1,267 @@ + + + + + QKeychain::DeletePasswordJobPrivate + + + Password entry not found + Mot de passe non trouvé + + + + Could not decrypt data + Impossible de déchiffrer les données + + + + + Unknown error + Erreur inconnue + + + + Could not open wallet: %1; %2 + Impossible d'ouvrir le trousseau : %1; %2 + + + + Password not found + Mot de passe non trouvé + + + + QKeychain::JobPrivate + + + Unknown error + Erreur inconnue + + + + Access to keychain denied + Accès au trousseau refusé + + + + QKeychain::PlainTextStore + + + Could not store data in settings: access error + Impossible de stocker les paramètres : Erreur d'accès + + + + Could not store data in settings: format error + Impossible de stocker les paramètres : Erreur de format + + + + Could not delete data from settings: access error + Impossible de supprimer des paramètres : Erreur d'accès + + + + Could not delete data from settings: format error + Impossible de supprimer des paramètres : Erreur de format + + + + Entry not found + Entrée non trouvée + + + + QKeychain::ReadPasswordJobPrivate + + + + Unknown error + Erreur inconnue + + + + D-Bus is not running + D-Bus non disponible + + + + No keychain service available + Aucun service de trousseau disponible + + + + Could not open wallet: %1; %2 + Impossible d'ouvrir le trousseau : %1; %2 + + + + Access to keychain denied + Accès au trousseau refusé + + + + Could not determine data type: %1; %2 + Impossible de déterminer le type de données : %1: %2 + + + + Unsupported entry type 'Map' + Type d'entrée non supporté «Map» + + + + Unknown kwallet entry type '%1' + Type de trousseau inconnu «%1» + + + Could not read password: %1; %2 + Impossible de lire le mot de passe : %1; %2 + + + + + Password not found + Mot de passe non trouvé + + + + Entry not found + Entrée non trouvée + + + + Password entry not found + Entrée non trouvée + + + + + Could not decrypt data + Impossible de déchiffrer les données + + + + QKeychain::WritePasswordJobPrivate + + + + Unknown error + Erreur inconnue + + + + D-Bus is not running + D-Bus non disponible + + + + Could not open wallet: %1; %2 + Impossible d'ouvrir le trousseau : %1; %2 + + + Access to keychain denied + Accès au trousseau refusé + + + Could not delete encrypted data from settings: access error + Impossible de supprimer des données chiffrées dans les paramètres : Erreur d'accès + + + Could not delete encrypted data from settings: format error + Impossible de supprimer des données chiffrées dans les paramètres : Erreur de format + + + + + Encryption failed + Le chiffrement a échoué + + + Could not store encrypted data in settings: access error + Impossible de stocker des données chiffrées dans les paramètres : Erreur d'accès + + + Could not store encrypted data in settings: format error + Impossible de stocker des données chiffrées dans les paramètres : Erreur de format + + + + Password not found + Mot de passe non trouvé + + + + QObject + + + Access to keychain denied + Accès au trousseau refusé + + + + No keyring daemon + Aucun démon de trousseau + + + + Already unlocked + Déjà déverrouillé + + + + No such keyring + Trousseau non trouvé + + + + Bad arguments + Mauvais arguments + + + + I/O error + Erreur d'E/S + + + + Cancelled + Annulé + + + + Keyring already exists + Trousseau déjà existant + + + + No match + Aucune correspondance + + + + Unknown error + Erreur inconnue + + + + OS X Keychain error (OSStatus %1) + + + + + %1 (OSStatus %2) + + + + + Entry not found + Entrée non trouvée + + + + error 0x%1: %2 + Erreur 0x%1 : %2 + + + From 4a9e58b0eac4af17dfca4ccd0796a44244700991 Mon Sep 17 00:00:00 2001 From: Sergey Ilinykh Date: Thu, 28 Mar 2019 14:47:37 +0300 Subject: [PATCH 002/168] Generate Qt 5.12.1+ compatible qt_Qt5Keychain.pri As mentioned in https://github.com/frankosterfeld/qtkeychain/issues/141 --- cmake/Modules/ECMGeneratePriFile.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/Modules/ECMGeneratePriFile.cmake b/cmake/Modules/ECMGeneratePriFile.cmake index a64fa62f..8e00c10b 100644 --- a/cmake/Modules/ECMGeneratePriFile.cmake +++ b/cmake/Modules/ECMGeneratePriFile.cmake @@ -187,6 +187,7 @@ QT.${PRI_TARGET_BASENAME}.MAJOR_VERSION = ${PROJECT_VERSION_MAJOR} QT.${PRI_TARGET_BASENAME}.MINOR_VERSION = ${PROJECT_VERSION_MINOR} QT.${PRI_TARGET_BASENAME}.PATCH_VERSION = ${PROJECT_VERSION_PATCH} QT.${PRI_TARGET_BASENAME}.name = ${PRI_TARGET_LIBNAME} +QT.${PRI_TARGET_BASENAME}.module = ${PRI_TARGET_LIBNAME} QT.${PRI_TARGET_BASENAME}.defines = ${PRI_TARGET_DEFINES} QT.${PRI_TARGET_BASENAME}.includes = ${PRI_TARGET_INCLUDES} QT.${PRI_TARGET_BASENAME}.private_includes = From 89db0fc72d2aad5e88d7da035b27b882453a8d05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert-Andr=C3=A9=20Mauchin?= Date: Sun, 28 Apr 2019 21:46:14 +0200 Subject: [PATCH 003/168] Convert QStringLiteral to QLatin1String to keep compatibility with Qt4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Robert-André Mauchin --- keychain_unix.cpp | 2 +- libsecret.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/keychain_unix.cpp b/keychain_unix.cpp index 958927ab..b9a8e9d1 100644 --- a/keychain_unix.cpp +++ b/keychain_unix.cpp @@ -93,7 +93,7 @@ static bool isKwallet5Available() // a wallet can be opened. iface.setTimeout(500); - QDBusMessage reply = iface.call(QStringLiteral("networkWallet")); + QDBusMessage reply = iface.call(QLatin1String("networkWallet")); return reply.type() == QDBusMessage::ReplyMessage; } diff --git a/libsecret.cpp b/libsecret.cpp index aed91a07..70d1ddff 100644 --- a/libsecret.cpp +++ b/libsecret.cpp @@ -305,7 +305,7 @@ bool LibSecretKeyring::deletePassword(const QString &key, const QString &service } LibSecretKeyring::LibSecretKeyring() - : QLibrary(QStringLiteral("secret-1"), 0) + : QLibrary(QLatin1String("secret-1"), 0) { #ifdef HAVE_LIBSECRET if (load()) { From 95c6021e280ebe4692e7729cf626c7298db9b226 Mon Sep 17 00:00:00 2001 From: Vadim Peretokin Date: Tue, 10 Sep 2019 10:21:40 +0200 Subject: [PATCH 004/168] Don't quote LIBS arguments --- qt5keychain.pri | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qt5keychain.pri b/qt5keychain.pri index ee6d4e88..b3bb6267 100644 --- a/qt5keychain.pri +++ b/qt5keychain.pri @@ -73,11 +73,11 @@ win32 { } macx:!ios { - LIBS += "-framework Security" "-framework Foundation" + LIBS += -framework Security -framework Foundation SOURCES += $$QT5KEYCHAIN_PWD/keychain_mac.cpp } ios { - LIBS += "-framework Security" "-framework Foundation" + LIBS += -framework Security -framework Foundation OBJECTIVE_SOURCES += $$QT5KEYCHAIN_PWD/keychain_ios.mm } From fde6ea435435b556f460141758c586f9af25e38c Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Tue, 17 Dec 2019 17:00:13 +0100 Subject: [PATCH 005/168] Update changelog for 0.10.0 --- ChangeLog | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ChangeLog b/ChangeLog index d04ac0b2..8ae9aed3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,16 @@ ChangeLog ========= +version 0.10.0 (release 2019-12-17) + + * Detect XFCE desktop correctly. (Sandro Knauß ) + * Windows Use CRED_PERSIST_ENTERPRISE (Olivier Goffart ) + * Windows: Improve CredWrite() error handling (Christian Kamm ) + * Fix build with Qt 5.12.x (Sergey Ilinykh ) + * Fix Qt 4 build (Robert-André Mauchin ) + * Translation: Mandarin (Taiwan) (Poren Chiang ) + * Translation: French (François Revol ) + version 0.9.1 (release 2018-08-20) * Windows Credential Store: Use CRED_PERSIST_ENTERPRISE (Olivier Goffart ) * Secret: Don't match the schema name #114 (Christian Kamm ) From 6fe83cfe0d9f8ea16567e3c831be24220eee3781 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Tue, 17 Dec 2019 17:01:59 +0100 Subject: [PATCH 006/168] Set version to 0.10.0 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4449bc2d..43e1744c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ include(FindPkgConfig) ### -set(QTKEYCHAIN_VERSION 0.9.90) +set(QTKEYCHAIN_VERSION 0.10.0) set(QTKEYCHAIN_SOVERSION 1) ### From 7253ccefb32ac33cb7170582347fb4feebf346a0 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Tue, 17 Dec 2019 17:03:36 +0100 Subject: [PATCH 007/168] Bump version in master to 0.10.90 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 43e1744c..c5afd9b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ include(FindPkgConfig) ### -set(QTKEYCHAIN_VERSION 0.10.0) +set(QTKEYCHAIN_VERSION 0.10.90) set(QTKEYCHAIN_SOVERSION 1) ### From 0f7c79be76e1b4651ebdaec781f2031e11b7e1c6 Mon Sep 17 00:00:00 2001 From: daviddavid Date: Sun, 22 Dec 2019 11:27:35 +0100 Subject: [PATCH 008/168] Update and complete French translation - by David Geiger --- translations/qtkeychain_fr.ts | 70 +++++++++++++++++------------------ 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/translations/qtkeychain_fr.ts b/translations/qtkeychain_fr.ts index 91d0fb39..35c60b9b 100644 --- a/translations/qtkeychain_fr.ts +++ b/translations/qtkeychain_fr.ts @@ -1,33 +1,33 @@ - + QKeychain::DeletePasswordJobPrivate Password entry not found - Mot de passe non trouvé + Mot de passe introuvable Could not decrypt data - Impossible de déchiffrer les données + Impossible de déchiffrer les données Unknown error - Erreur inconnue + Erreur inconnue Could not open wallet: %1; %2 - Impossible d'ouvrir le trousseau : %1; %2 + Impossible d'ouvrir le portefeuille : %1; %2 Password not found - Mot de passe non trouvé + Mot de passe introuvable @@ -35,12 +35,12 @@ Unknown error - Erreur inconnue + Erreur inconnue Access to keychain denied - Accès au trousseau refusé + Accès au trousseau refusé @@ -48,27 +48,27 @@ Could not store data in settings: access error - Impossible de stocker les paramètres : Erreur d'accès + Impossible de stocker les données dans les paramètres : Erreur d'accès Could not store data in settings: format error - Impossible de stocker les paramètres : Erreur de format + Impossible de stocker les données dans les paramètres : Erreur de format Could not delete data from settings: access error - Impossible de supprimer des paramètres : Erreur d'accès + Impossible de supprimer les données depuis les paramètres : Erreur d'accès Could not delete data from settings: format error - Impossible de supprimer des paramètres : Erreur de format + Impossible de supprimer les données depuis les paramètres : Erreur de format Entry not found - Entrée non trouvée + Entrée introuvable @@ -82,7 +82,7 @@ D-Bus is not running - D-Bus non disponible + D-Bus n'est pas en cours d'exécution @@ -92,7 +92,7 @@ Could not open wallet: %1; %2 - Impossible d'ouvrir le trousseau : %1; %2 + Impossible d'ouvrir le trousseau : %1; %2 @@ -102,37 +102,37 @@ Could not determine data type: %1; %2 - Impossible de déterminer le type de données : %1: %2 + Impossible de déterminer le type de données : %1: %2 Unsupported entry type 'Map' - Type d'entrée non supporté «Map» + Type d'entrée non supporté 'Map' Unknown kwallet entry type '%1' - Type de trousseau inconnu «%1» + Type de trousseau inconnu '%1' Could not read password: %1; %2 - Impossible de lire le mot de passe : %1; %2 + Impossible de lire le mot de passe : %1; %2 Password not found - Mot de passe non trouvé + Mot de passe introuvable Entry not found - Entrée non trouvée + Entrée introuvable Password entry not found - Entrée non trouvée + Entrée de mot de passe introuvable @@ -152,12 +152,12 @@ D-Bus is not running - D-Bus non disponible + D-Bus n'est pas en cours d'exécution Could not open wallet: %1; %2 - Impossible d'ouvrir le trousseau : %1; %2 + Impossible d'ouvrir le trousseau : %1; %2 Access to keychain denied @@ -165,11 +165,11 @@ Could not delete encrypted data from settings: access error - Impossible de supprimer des données chiffrées dans les paramètres : Erreur d'accès + Impossible de supprimer des données chiffrées dans les paramètres : Erreur d'accès Could not delete encrypted data from settings: format error - Impossible de supprimer des données chiffrées dans les paramètres : Erreur de format + Impossible de supprimer des données chiffrées dans les paramètres : Erreur de format @@ -179,16 +179,16 @@ Could not store encrypted data in settings: access error - Impossible de stocker des données chiffrées dans les paramètres : Erreur d'accès + Impossible de stocker des données chiffrées dans les paramètres : Erreur d'accès Could not store encrypted data in settings: format error - Impossible de stocker des données chiffrées dans les paramètres : Erreur de format + Impossible de stocker des données chiffrées dans les paramètres : Erreur de format Password not found - Mot de passe non trouvé + Mot de passe introuvable @@ -211,7 +211,7 @@ No such keyring - Trousseau non trouvé + Aucun trousseau @@ -221,7 +221,7 @@ I/O error - Erreur d'E/S + Erreur d'E/S @@ -246,22 +246,22 @@ OS X Keychain error (OSStatus %1) - + OS X Keychain error (OSStatus %1) %1 (OSStatus %2) - + %1 (OSStatus %2) Entry not found - Entrée non trouvée + Entrée introuvable error 0x%1: %2 - Erreur 0x%1 : %2 + Erreur 0x%1 : %2 From 4c435a741911a964710073105e2427521341b495 Mon Sep 17 00:00:00 2001 From: Alexander Gorishnyak Date: Thu, 26 Dec 2019 00:45:13 +0600 Subject: [PATCH 009/168] Add Russian translation --- translations/qtkeychain_ru.ts | 241 ++++++++++++++++++++++++++++++++++ 1 file changed, 241 insertions(+) create mode 100644 translations/qtkeychain_ru.ts diff --git a/translations/qtkeychain_ru.ts b/translations/qtkeychain_ru.ts new file mode 100644 index 00000000..1d611291 --- /dev/null +++ b/translations/qtkeychain_ru.ts @@ -0,0 +1,241 @@ + + + + + QKeychain::DeletePasswordJobPrivate + + + + Unknown error + Неизвестная ошибка + + + + Could not open wallet: %1; %2 + Не удалось открыть бумажник: %1; %2 + + + + Password entry not found + Пароль не найден + + + + Could not decrypt data + Не удалось расшифровать данные + + + + QKeychain::JobPrivate + + + Unknown error + Неизвестная ошибка + + + + Access to keychain denied + Доступ к связке ключей запрещён + + + + QKeychain::PlainTextStore + + + Could not store data in settings: access error + Не удалось сохранить данные в настройках: ошибка доступа + + + + Could not store data in settings: format error + Не удалось сохранить данные в настройках: ошибка формата + + + + Could not delete data from settings: access error + Не удалось удалить данные из настроек: ошибка доступа + + + + Could not delete data from settings: format error + Не удалось удалить данные из настроек: ошибка формата + + + + Entry not found + Запись не найдена + + + + QKeychain::ReadPasswordJobPrivate + + + Password not found + Пароль не найден + + + + D-Bus is not running + D-Bus не запущен + + + + + Unknown error + Неизвестная ошибка + + + + No keychain service available + Служба связки ключей недоступна + + + + Could not open wallet: %1; %2 + Не удалось открыть кошелёк: %1; %2 + + + + Access to keychain denied + Доступ к связке ключей запрещён + + + + Could not determine data type: %1; %2 + Не удалось определить тип данных: %1; %2 + + + + Entry not found + Запись не найдена + + + + Unsupported entry type 'Map' + Неподдерживаемый тип записи 'Map' + + + + Unknown kwallet entry type '%1' + Неизвестный тип записи kwallet '%1' + + + + Password entry not found + Пароль не найден + + + + + Could not decrypt data + Не удалось расшифровать данные + + + + QKeychain::WritePasswordJobPrivate + + + D-Bus is not running + D-Bus не запущен + + + + + Unknown error + Неизвестная ошибка + + + + Could not open wallet: %1; %2 + Не удалось открыть кошелёк: %1; %2 + + + + Credential size exceeds maximum size of %1 + Учётные данные превышают максимальный размер %1 + + + + Credential key exceeds maximum size of %1 + Ключ учётных данных превышает максимальный размер %1 + + + + Writing credentials failed: Win32 error code %1 + Не удалось сохранить учётные данные: код ошибки win32 %1 + + + + Encryption failed + Шифрование не удалось + + + + QObject + + + OS X Keychain error (OSStatus %1) + Ошибка связки ключей OS X (OSStatus %1) + + + + %1 (OSStatus %2) + %1 (OSStatus %2) + + + + Access to keychain denied + Доступ к связке ключей запрещён + + + + No keyring daemon + Нет демона связки ключей + + + + Already unlocked + Уже разблокировано + + + + No such keyring + Связка ключей не найдена + + + + Bad arguments + Неверные аргументы + + + + I/O error + Ошибка ввода/вывода + + + + Cancelled + Отменено + + + + Keyring already exists + Связка ключей уже существует + + + + No match + Нет совпадений + + + + Unknown error + Неизвестная ошибка + + + + Entry not found + Запись не найдена + + + From b548f92162210ca8ebafe6b46673ca6b9352d711 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Wed, 25 Dec 2019 20:39:12 +0100 Subject: [PATCH 010/168] Add .ts files to CMakeLists --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index c5afd9b6..8e5320e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -169,7 +169,10 @@ QT_WRAP_CPP(qtkeychain_MOC_OUTFILES keychain.h keychain_p.h gnomekeyring_p.h) set(qtkeychain_TR_FILES translations/qtkeychain_de.ts + translations/qtkeychain_fr.ts translations/qtkeychain_ro.ts + translations/qtkeychain_ru.ts + translations/qtkeychain_zh.ts ) file(GLOB qtkeychain_TR_SOURCES *.cpp *.h *.ui) From b16b442019724b9cc27fedbce0916e2a65011eb1 Mon Sep 17 00:00:00 2001 From: Alexander Gorishnyak Date: Sun, 5 Jan 2020 13:54:24 +0600 Subject: [PATCH 011/168] Fix unused parameter warnings --- libsecret.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/libsecret.cpp b/libsecret.cpp index 70d1ddff..4fcf19f9 100644 --- a/libsecret.cpp +++ b/libsecret.cpp @@ -244,6 +244,9 @@ bool LibSecretKeyring::findPassword(const QString &user, const QString &server, NULL); return true; #else + Q_UNUSED(user) + Q_UNUSED(server) + Q_UNUSED(self) return false; #endif } @@ -281,6 +284,12 @@ bool LibSecretKeyring::writePassword(const QString &display_name, NULL); return true; #else + Q_UNUSED(display_name) + Q_UNUSED(user) + Q_UNUSED(server) + Q_UNUSED(mode) + Q_UNUSED(password) + Q_UNUSED(self) return false; #endif } @@ -300,6 +309,9 @@ bool LibSecretKeyring::deletePassword(const QString &key, const QString &service NULL); return true; #else + Q_UNUSED(key) + Q_UNUSED(service) + Q_UNUSED(self) return false; #endif } From cbc626edcd4920704f74d271acc1fd6dac508b2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Revol?= Date: Tue, 20 Nov 2018 21:16:37 +0100 Subject: [PATCH 012/168] build: KEYCHAIN_DBUS --- CMakeLists.txt | 1 + keychain_p.h | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e5320e4..185fa680 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -160,6 +160,7 @@ if(UNIX AND NOT APPLE AND NOT ANDROID) list(APPEND qtkeychain_LIBRARIES ${LIBSECRET_LIBRARIES}) endif() + add_definitions(-DKEYCHAIN_DBUS=1) list(APPEND qtkeychain_SOURCES keychain_unix.cpp gnomekeyring.cpp libsecret.cpp plaintextstore.cpp) qt_add_dbus_interface(qtkeychain_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/org.kde.KWallet.xml kwallet_interface KWalletInterface) list(APPEND qtkeychain_LIBRARIES ${QTDBUS_LIBRARIES} ) diff --git a/keychain_p.h b/keychain_p.h index ab7f0be5..58c0fd6b 100644 --- a/keychain_p.h +++ b/keychain_p.h @@ -15,7 +15,7 @@ #include #include -#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) && !defined(Q_OS_ANDROID) +#if defined(KEYCHAIN_DBUS) #include @@ -49,7 +49,7 @@ class JobPrivate : public QObject { Mode mode; QByteArray data; -#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) && !defined(Q_OS_ANDROID) +#if defined(KEYCHAIN_DBUS) org::kde::KWallet* iface; int walletHandle; @@ -93,7 +93,7 @@ class ReadPasswordJobPrivate : public JobPrivate { explicit ReadPasswordJobPrivate( const QString &service_, ReadPasswordJob* qq ); void scheduledStart(); -#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) && !defined(Q_OS_ANDROID) +#if defined(KEYCHAIN_DBUS) void fallbackOnError(const QDBusError& err); private Q_SLOTS: @@ -116,7 +116,7 @@ class WritePasswordJobPrivate : public JobPrivate { explicit WritePasswordJobPrivate( const QString &service_, WritePasswordJob* qq ); void scheduledStart(); -#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) && !defined(Q_OS_ANDROID) +#if defined(KEYCHAIN_DBUS) void fallbackOnError(const QDBusError& err); #endif @@ -130,7 +130,7 @@ class DeletePasswordJobPrivate : public JobPrivate { void scheduledStart(); -#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) && !defined(Q_OS_ANDROID) +#if defined(KEYCHAIN_DBUS) void fallbackOnError(const QDBusError& err); #endif From 2f535170e6792dfc48606ebc9d3b6753984f5fca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Revol?= Date: Sat, 17 Nov 2018 18:38:47 +0100 Subject: [PATCH 013/168] Add Haiku support We had to work around QCoreApplication not creating a BApplication: When not used in a GUI application, we must create a BApplication object ourselves as it's required for the API we use. At least until QCoreApplication is fixed to create one maybe. --- CMakeLists.txt | 17 ++++- keychain_haiku.cpp | 187 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 201 insertions(+), 3 deletions(-) create mode 100644 keychain_haiku.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 185fa680..db7273df 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,10 @@ if(CMAKE_SYSTEM_NAME STREQUAL Android) set(ANDROID 1) endif() +if(CMAKE_SYSTEM_NAME STREQUAL Haiku) + set(HAIKU 1) +endif() + if (WIN32) option(USE_CREDENTIAL_STORE "Build with windows CredentialStore support" ON) @@ -42,7 +46,7 @@ endif() if (Qt5Core_FOUND AND NOT BUILD_WITH_QT4) set(QTKEYCHAIN_VERSION_INFIX 5) - if(UNIX AND NOT APPLE AND NOT ANDROID) + if(UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU) find_package(Qt5DBus REQUIRED) include_directories(${Qt5DBus_INCLUDE_DIRS}) set(QTDBUS_LIBRARIES ${Qt5DBus_LIBRARIES}) @@ -147,7 +151,14 @@ if(APPLE) list(APPEND qtkeychain_LIBRARIES ${SECURITY_LIBRARY}) endif() -if(UNIX AND NOT APPLE AND NOT ANDROID) +if(HAIKU) + list(APPEND qtkeychain_SOURCES keychain_haiku.cpp) + + find_library(BE_LIBRARY be REQUIRED) + list(APPEND qtkeychain_LIBRARIES ${BE_LIBRARY}) +endif() + +if(UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU) option(LIBSECRET_SUPPORT "Build with libsecret support" ON) if(LIBSECRET_SUPPORT) @@ -252,7 +263,7 @@ ecm_setup_version("${QTKEYCHAIN_VERSION}" VARIABLE_PREFIX SNORE PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfigVersion.cmake" SOVERSION ${QTKEYCHAIN_VERSION}) -if(UNIX AND NOT APPLE AND NOT ANDROID) +if(UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU) set(PRI_EXTRA_DEPS "dbus") endif() ecm_generate_pri_file(BASE_NAME Qt${QTKEYCHAIN_VERSION_INFIX}Keychain diff --git a/keychain_haiku.cpp b/keychain_haiku.cpp new file mode 100644 index 00000000..58b1ab79 --- /dev/null +++ b/keychain_haiku.cpp @@ -0,0 +1,187 @@ +/****************************************************************************** + * Copyright (C) 2018 François Revol * + * * + * 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. For licensing and distribution * + * details, check the accompanying file 'COPYING'. * + *****************************************************************************/ +#include "keychain_p.h" + +#include + +#include +#include +#include + +#include +#include +#include + +using namespace QKeychain; + +class AutoApp { +public: + AutoApp(); + ~AutoApp(); + BApplication *app; +}; + + +AutoApp::AutoApp() + : app(NULL) +{ + if (be_app != NULL) + return; + + // no BApplication object, probably using QCoreApplication + // but we need one around + + QString appSignature; + + char signature[B_MIME_TYPE_LENGTH]; + signature[0] = '\0'; + + QString appPath = QCoreApplication::applicationFilePath(); + + BFile appFile(appPath.toUtf8(), B_READ_ONLY); + if (appFile.InitCheck() == B_OK) { + BAppFileInfo info(&appFile); + if (info.InitCheck() == B_OK) { + if (info.GetSignature(signature) != B_OK) + signature[0] = '\0'; + } + } + + if (signature[0] != '\0') + appSignature = QLatin1String(signature); + else + appSignature = QLatin1String("application/x-vnd.qtkeychain-") + + QCoreApplication::applicationName().remove("_x86"); + + app = new BApplication(appSignature.toUtf8().constData()); +} + +AutoApp::~AutoApp() +{ + delete app; +} + +static QString strForStatus( status_t os ) { + const char * const buf = strerror(os) ; + return QObject::tr( "error 0x%1: %2" ) + .arg( os, 8, 16 ).arg( QString::fromUtf8( buf, strlen( buf ) ) ); +} + +void ReadPasswordJobPrivate::scheduledStart() +{ + AutoApp aa; + QString errorString; + Error error = NoError; + BKeyStore keyStore; + BPasswordKey password; + + status_t result = keyStore.GetKey(B_KEY_TYPE_PASSWORD, + q->service().toUtf8().constData(), + q->key().toUtf8().constData(), + false, password); + + data = QByteArray(reinterpret_cast(password.Data()), password.DataLength()); + + switch ( result ) { + case B_OK: + q->emitFinished(); + return; + case B_ENTRY_NOT_FOUND: + errorString = tr("Password not found"); + error = EntryNotFound; + break; + default: + errorString = strForStatus( result ); + error = OtherError; + break; + } + + q->emitFinishedWithError( error, errorString ); +} + +void WritePasswordJobPrivate::scheduledStart() +{ + AutoApp aa; + QString errorString; + Error error = NoError; + BKeyStore keyStore; + BPasswordKey password(data.constData(), + B_KEY_PURPOSE_GENERIC, + q->service().toUtf8().constData(), + q->key().toUtf8().constData()); + status_t result = B_OK; + + // re-add as binary if it's not text + if (mode == Binary) + result = password.SetData(reinterpret_cast(data.constData()), data.size()); + + if (result == B_OK) + result = keyStore.AddKey(password); + + if (result == B_NAME_IN_USE) { + BPasswordKey old_password; + result = keyStore.GetKey(B_KEY_TYPE_PASSWORD, + q->service().toUtf8().constData(), + q->key().toUtf8().constData(), + false, old_password); + if (result == B_OK) + result = keyStore.RemoveKey(old_password); + if (result == B_OK) + result = keyStore.AddKey(password); + } + + switch ( result ) { + case B_OK: + q->emitFinished(); + return; + case B_ENTRY_NOT_FOUND: + errorString = tr("Password not found"); + error = EntryNotFound; + break; + default: + errorString = strForStatus( result ); + error = OtherError; + break; + } + + q->emitFinishedWithError( error, errorString ); +} + +void DeletePasswordJobPrivate::scheduledStart() +{ + AutoApp aa; + QString errorString; + Error error = NoError; + BKeyStore keyStore; + BPasswordKey password; + + status_t result = keyStore.GetKey(B_KEY_TYPE_PASSWORD, + q->service().toUtf8().constData(), + q->key().toUtf8().constData(), + false, password); + + if (result == B_OK) + result = keyStore.RemoveKey(password); + + switch ( result ) { + case B_OK: + q->emitFinished(); + return; + case B_ENTRY_NOT_FOUND: + errorString = tr("Password not found"); + error = EntryNotFound; + break; + default: + errorString = strForStatus( result ); + error = CouldNotDeleteEntry; + break; + } + + q->emitFinishedWithError( error, errorString ); +} From 3c97ce3c71fd849c769369eba800440c472edd4d Mon Sep 17 00:00:00 2001 From: Hannah von Reth Date: Fri, 24 Jan 2020 12:56:17 +0100 Subject: [PATCH 014/168] Don't use absolute path to frameworks The generated cmake config files where not portable. Before: set_target_properties(qt5keychain PROPERTIES IMPORTED_LINK_INTERFACE_LIBRARIES_RELWITHDEBINFO "Qt5::Core;/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk/System/Library/Frameworks/CoreFoundation.framework;/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk/System/Library/Frameworks/Security.framework" IMPORTED_LOCATION_RELWITHDEBINFO "${_IMPORT_PREFIX}/lib/libqt5keychain.0.10.90.dylib" IMPORTED_SONAME_RELWITHDEBINFO "/Users/hannah/ownstuff/drone-scripts/macos-64-clang/lib/libqt5keychain.1.dylib" ) After: set_target_properties(qt5keychain PROPERTIES IMPORTED_LINK_INTERFACE_LIBRARIES_RELWITHDEBINFO "Qt5::Core;-framework Foundation;-framework Security" IMPORTED_LOCATION_RELWITHDEBINFO "${_IMPORT_PREFIX}/lib/libqt5keychain.0.10.90.dylib" IMPORTED_SONAME_RELWITHDEBINFO "/Users/hannah/CraftRoot/lib/libqt5keychain.1.dylib" ) --- CMakeLists.txt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index db7273df..ce7fec4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -144,11 +144,7 @@ if(APPLE) list(APPEND qtkeychain_SOURCES keychain_mac.cpp) endif() - find_library(COREFOUNDATION_LIBRARY CoreFoundation REQUIRED) - list(APPEND qtkeychain_LIBRARIES ${COREFOUNDATION_LIBRARY}) - - find_library(SECURITY_LIBRARY Security REQUIRED) - list(APPEND qtkeychain_LIBRARIES ${SECURITY_LIBRARY}) + list(APPEND qtkeychain_LIBRARIES "-framework Foundation" "-framework Security") endif() if(HAIKU) From e741088b4e55a99ff990acb81f830777d1a968bb Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Mon, 28 Oct 2019 14:50:58 +0100 Subject: [PATCH 015/168] build: do not expose libsecret dependency We are adding all dependencies to the PUBLIC part of target_link_libraries invocation, which forces all dependent packages to link against it too through IMPORTED_LINK_INTERFACE_LIBRARIES_RELEASE in $prefix/lib/cmake/Qt5Keychain/Qt5KeychainLibraryDepends-release.cmake. Since it is implementation detail and not a part of the interface, we need to move it to the PRIVATE section instead: https://cmake.org/cmake/help/v3.16/command/target_link_libraries.html#libraries-for-a-target-and-or-its-dependents --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ce7fec4f..337eef5c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -164,7 +164,7 @@ if(UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU) add_definitions(-DHAVE_LIBSECRET=1) endif() INCLUDE_DIRECTORIES(${LIBSECRET_INCLUDE_DIRS}) - list(APPEND qtkeychain_LIBRARIES ${LIBSECRET_LIBRARIES}) + list(APPEND qtkeychain_LIBRARIES_PRIVATE ${LIBSECRET_LIBRARIES}) endif() add_definitions(-DKEYCHAIN_DBUS=1) @@ -214,7 +214,7 @@ else() add_library(${QTKEYCHAIN_TARGET_NAME} STATIC ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES} ${qtkeychain_QM_FILES}) endif() -target_link_libraries(${QTKEYCHAIN_TARGET_NAME} PUBLIC ${qtkeychain_LIBRARIES}) +target_link_libraries(${QTKEYCHAIN_TARGET_NAME} PUBLIC ${qtkeychain_LIBRARIES} PRIVATE ${qtkeychain_LIBRARIES_PRIVATE}) target_include_directories(${QTKEYCHAIN_TARGET_NAME} PUBLIC $) generate_export_header(${QTKEYCHAIN_TARGET_NAME} From db4543cf4d56693ce362527253d526ada799b259 Mon Sep 17 00:00:00 2001 From: Johannes Schultz Date: Tue, 24 Mar 2020 11:18:11 +0100 Subject: [PATCH 016/168] reference correct filename in CMake file for iOS backend --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 337eef5c..18eee3e9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -139,7 +139,7 @@ endif() if(APPLE) if(IOS) - list(APPEND qtkeychain_SOURCES keychain_ios.cpp) + list(APPEND qtkeychain_SOURCES keychain_ios.mm) else() list(APPEND qtkeychain_SOURCES keychain_mac.cpp) endif() From ebbfeba038aff1df2049e8d2c418be1a57c551a9 Mon Sep 17 00:00:00 2001 From: David <48836389+david-antiteum@users.noreply.github.com> Date: Sat, 4 Jul 2020 17:41:53 +0200 Subject: [PATCH 017/168] Add "d" suffix to libraries in Debug configuration Closes case #109 --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 18eee3e9..7acc11dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -213,6 +213,7 @@ if(NOT QTKEYCHAIN_STATIC) else() add_library(${QTKEYCHAIN_TARGET_NAME} STATIC ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES} ${qtkeychain_QM_FILES}) endif() +set_target_properties( ${QTKEYCHAIN_TARGET_NAME} PROPERTIES DEBUG_POSTFIX "d" ) target_link_libraries(${QTKEYCHAIN_TARGET_NAME} PUBLIC ${qtkeychain_LIBRARIES} PRIVATE ${qtkeychain_LIBRARIES_PRIVATE}) target_include_directories(${QTKEYCHAIN_TARGET_NAME} PUBLIC $) From 035c4aa087db4cacc87e9303efccb7035691d90d Mon Sep 17 00:00:00 2001 From: David <48836389+david-antiteum@users.noreply.github.com> Date: Sat, 4 Jul 2020 18:46:07 +0200 Subject: [PATCH 018/168] Only set prefix in Windows Not required in other platforms --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7acc11dd..a1aff8f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -213,7 +213,9 @@ if(NOT QTKEYCHAIN_STATIC) else() add_library(${QTKEYCHAIN_TARGET_NAME} STATIC ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES} ${qtkeychain_QM_FILES}) endif() -set_target_properties( ${QTKEYCHAIN_TARGET_NAME} PROPERTIES DEBUG_POSTFIX "d" ) +if(WIN32) + set_target_properties( ${QTKEYCHAIN_TARGET_NAME} PROPERTIES DEBUG_POSTFIX "d" ) +endif() target_link_libraries(${QTKEYCHAIN_TARGET_NAME} PUBLIC ${qtkeychain_LIBRARIES} PRIVATE ${qtkeychain_LIBRARIES_PRIVATE}) target_include_directories(${QTKEYCHAIN_TARGET_NAME} PUBLIC $) From 775f03c203c89be07132a55e1bb4cf8f9128cd52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20H=C3=B6her?= Date: Sat, 5 Sep 2020 17:37:07 +0200 Subject: [PATCH 019/168] Fix: Windows cross build This change fixes cross building from Linux to Windows when using the mingw tool chain. Using capital library names breaks the build on Linux (whereas for Windows it should not matter, as file names are case insensitive there). --- qt5keychain.pri | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qt5keychain.pri b/qt5keychain.pri index b3bb6267..f5244da2 100644 --- a/qt5keychain.pri +++ b/qt5keychain.pri @@ -59,10 +59,10 @@ win32 { DEFINES += USE_CREDENTIAL_STORE contains(DEFINES, USE_CREDENTIAL_STORE) { !build_pass:message("Windows Credential Store support: on") - LIBS += -lAdvapi32 + LIBS += -ladvapi32 } else { !build_pass:message("Windows Credential Store support: off") - LIBS += -lCrypt32 + LIBS += -lcrypt32 HEADERS += $$QT5KEYCHAIN_PWD/plaintextstore_p.h SOURCES += $$QT5KEYCHAIN_PWD/plaintextstore.cpp } From 4968946c254fae7706761760caa37d11da67c506 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Tue, 8 Sep 2020 14:39:32 +0200 Subject: [PATCH 020/168] CMake: Update ECM Use files from tag v5.74.0-rc1 of upstream repo. --- cmake/Modules/ECMGeneratePriFile.cmake | 55 +++++++++++---------- cmake/Modules/ECMPackageConfigHelpers.cmake | 31 ++---------- cmake/Modules/ECMQueryQmake.cmake | 20 ++++++-- cmake/Modules/ECMSetupVersion.cmake | 39 ++++----------- 4 files changed, 59 insertions(+), 86 deletions(-) diff --git a/cmake/Modules/ECMGeneratePriFile.cmake b/cmake/Modules/ECMGeneratePriFile.cmake index 8e00c10b..f63a0ce2 100644 --- a/cmake/Modules/ECMGeneratePriFile.cmake +++ b/cmake/Modules/ECMGeneratePriFile.cmake @@ -72,37 +72,16 @@ # Since pre-1.0.0. #============================================================================= -# Copyright 2014 David Faure -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# 1. Redistributions of source code must retain the copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. The name of the author may not be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. +# SPDX-FileCopyrightText: 2014 David Faure +# +# SPDX-License-Identifier: BSD-3-Clause # Replicate the logic from KDEInstallDirs.cmake as we can't depend on it # Ask qmake if we're using the same prefix as Qt set(_askqmake OFF) if(NOT DEFINED KDE_INSTALL_USE_QT_SYS_PATHS) include(ECMQueryQmake) - query_qmake(qt_install_prefix_dir QT_INSTALL_PREFIX) + query_qmake(qt_install_prefix_dir QT_INSTALL_PREFIX TRY) if(qt_install_prefix_dir STREQUAL "${CMAKE_INSTALL_PREFIX}") set(_askqmake ON) endif() @@ -159,18 +138,27 @@ function(ECM_GENERATE_PRI_FILE) string(REGEX REPLACE "^[0-9]+\\.([0-9]+)\\.[0-9]+.*" "\\1" PROJECT_VERSION_MINOR "${PROJECT_VERSION_STRING}") string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" PROJECT_VERSION_PATCH "${PROJECT_VERSION_STRING}") + # Prepare the right number of "../.." to go from ECM_MKSPECS_INSTALL_DIR to the install prefix + # This allows to make the generated pri files relocatable (no absolute paths) + if (IS_ABSOLUTE ${ECM_MKSPECS_INSTALL_DIR}) + set(BASEPATH ${CMAKE_INSTALL_PREFIX}) + else() + string(REGEX REPLACE "[^/]+" ".." PRI_ROOT_RELATIVE_TO_MKSPECS ${ECM_MKSPECS_INSTALL_DIR}) + set(BASEPATH "$$PWD/${PRI_ROOT_RELATIVE_TO_MKSPECS}") + endif() + set(PRI_TARGET_BASENAME ${EGPF_BASE_NAME}) set(PRI_TARGET_LIBNAME ${EGPF_LIB_NAME}) set(PRI_TARGET_QTDEPS ${EGPF_DEPS}) if(IS_ABSOLUTE "${EGPF_INCLUDE_INSTALL_DIR}") set(PRI_TARGET_INCLUDES "${EGPF_INCLUDE_INSTALL_DIR}") else() - set(PRI_TARGET_INCLUDES "${CMAKE_INSTALL_PREFIX}/${EGPF_INCLUDE_INSTALL_DIR}") + set(PRI_TARGET_INCLUDES "${BASEPATH}/${EGPF_INCLUDE_INSTALL_DIR}") endif() if(IS_ABSOLUTE "${EGPF_LIB_INSTALL_DIR}") set(PRI_TARGET_LIBS "${EGPF_LIB_INSTALL_DIR}") else() - set(PRI_TARGET_LIBS "${CMAKE_INSTALL_PREFIX}/${EGPF_LIB_INSTALL_DIR}") + set(PRI_TARGET_LIBS "${BASEPATH}/${EGPF_LIB_INSTALL_DIR}") endif() set(PRI_TARGET_DEFINES "") @@ -179,6 +167,18 @@ function(ECM_GENERATE_PRI_FILE) set(${EGPF_FILENAME_VAR} ${PRI_FILENAME} PARENT_SCOPE) endif() + set(PRI_TARGET_MODULE_CONFIG "") + # backward compat: it was not obvious LIB_NAME needs to be a target name, + # and some projects where the target name was not the actual library output name + # passed the output name for LIB_NAME, so .name & .module prperties are correctly set. + # TODO: improve API dox, allow control over module name if target name != output name + if(TARGET ${EGPF_LIB_NAME}) + get_target_property(target_type ${EGPF_LIB_NAME} TYPE) + if (target_type STREQUAL "STATIC_LIBRARY") + set(PRI_TARGET_MODULE_CONFIG "staticlib") + endif() + endif() + file(GENERATE OUTPUT ${PRI_FILENAME} CONTENT @@ -193,6 +193,7 @@ QT.${PRI_TARGET_BASENAME}.includes = ${PRI_TARGET_INCLUDES} QT.${PRI_TARGET_BASENAME}.private_includes = QT.${PRI_TARGET_BASENAME}.libs = ${PRI_TARGET_LIBS} QT.${PRI_TARGET_BASENAME}.depends = ${PRI_TARGET_QTDEPS} +QT.${PRI_TARGET_BASENAME}.module_config = ${PRI_TARGET_MODULE_CONFIG} " ) endfunction() diff --git a/cmake/Modules/ECMPackageConfigHelpers.cmake b/cmake/Modules/ECMPackageConfigHelpers.cmake index a118feae..8d48772b 100644 --- a/cmake/Modules/ECMPackageConfigHelpers.cmake +++ b/cmake/Modules/ECMPackageConfigHelpers.cmake @@ -6,7 +6,7 @@ # # ``write_basic_package_version_file()`` is the same as the one provided by the # `CMakePackageConfigHelpers -# `_ +# `_ # module in CMake; see that module's documentation for # more information. # @@ -23,7 +23,7 @@ # 2.8.12, except that it adds an extra helper macro: find_dependency(). It is # highly recommended that you read the `documentation for # CMakePackageConfigHelpers -# `_ +# `_ # for more information, particularly with regard to the PATH_VARS argument. # # Note that there is no argument that will disable the find_dependency() macro; @@ -50,31 +50,10 @@ # Since pre-1.0.0. #============================================================================= -# Copyright 2014 Alex Merry -# Copyright 2013 Stephen Kelly +# SPDX-FileCopyrightText: 2014 Alex Merry +# SPDX-FileCopyrightText: 2013 Stephen Kelly # -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# 1. Redistributions of source code must retain the copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. The name of the author may not be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. +# SPDX-License-Identifier: BSD-3-Clause include(${CMAKE_ROOT}/Modules/CMakePackageConfigHelpers.cmake) diff --git a/cmake/Modules/ECMQueryQmake.cmake b/cmake/Modules/ECMQueryQmake.cmake index fa0949df..74a6df87 100644 --- a/cmake/Modules/ECMQueryQmake.cmake +++ b/cmake/Modules/ECMQueryQmake.cmake @@ -9,12 +9,26 @@ endif() set(QMAKE_EXECUTABLE ${_qmake_executable_default} CACHE FILEPATH "Location of the Qt5 qmake executable") +# Helper method # This is not public API (yet)! +# Usage: query_qmake( [TRY]) +# Passing TRY will result in the method not failing fatal if no qmake executable +# has been found, but instead simply returning an empty string function(query_qmake result_variable qt_variable) + set(options TRY) + set(oneValueArgs ) + set(multiValueArgs ) + + cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + if(NOT QMAKE_EXECUTABLE) - set(${result_variable} "" PARENT_SCOPE) - message(WARNING "Should specify a qmake Qt5 binary. Can't check ${qt_variable}") - return() + if(ARGS_TRY) + set(${result_variable} "" PARENT_SCOPE) + message(STATUS "No qmake Qt5 binary found. Can't check ${qt_variable}") + return() + else() + message(FATAL_ERROR "No qmake Qt5 binary found. Can't check ${qt_variable} as required") + endif() endif() execute_process( COMMAND ${QMAKE_EXECUTABLE} -query "${qt_variable}" diff --git a/cmake/Modules/ECMSetupVersion.cmake b/cmake/Modules/ECMSetupVersion.cmake index f5a3cce4..65c1688a 100644 --- a/cmake/Modules/ECMSetupVersion.cmake +++ b/cmake/Modules/ECMSetupVersion.cmake @@ -28,7 +28,7 @@ # _SOVERSION - , or if SOVERSION was not given # # If CMake policy CMP0048 is not NEW, the following CMake variables will also -# be set: +# be set:: # # PROJECT_VERSION_MAJOR - # PROJECT_VERSION_MINOR - @@ -75,34 +75,13 @@ # # Since pre-1.0.0. # -# COMPATIBLITY option available since 1.6.0. +# COMPATIBILITY option available since 1.6.0. #============================================================================= -# Copyright 2014 Alex Merry -# Copyright 2012 Alexander Neundorf -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# 1. Redistributions of source code must retain the copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. The name of the author may not be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. +# SPDX-FileCopyrightText: 2014 Alex Merry +# SPDX-FileCopyrightText: 2012 Alexander Neundorf +# +# SPDX-License-Identifier: BSD-3-Clause include(CMakePackageConfigHelpers) @@ -154,9 +133,9 @@ function(ecm_setup_version _version) set(_minor "${PROJECT_VERSION_MINOR}") set(_patch "${PROJECT_VERSION_PATCH}") else() - string(REGEX REPLACE "^([0-9]+)\\.[0-9]+\\.[0-9]+.*" "\\1" _major "${_version}") - string(REGEX REPLACE "^[0-9]+\\.([0-9]+)\\.[0-9]+.*" "\\1" _minor "${_version}") - string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" _patch "${_version}") + string(REGEX REPLACE "^0*([0-9]+)\\.[0-9]+\\.[0-9]+.*" "\\1" _major "${_version}") + string(REGEX REPLACE "^[0-9]+\\.0*([0-9]+)\\.[0-9]+.*" "\\1" _minor "${_version}") + string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.0*([0-9]+).*" "\\1" _patch "${_version}") endif() if(NOT ESV_SOVERSION) From 387aacc59df3c834d8a467b1bae1bbfee672cb28 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Tue, 8 Sep 2020 14:46:58 +0200 Subject: [PATCH 021/168] Update changelog for 0.11.0 --- ChangeLog | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ChangeLog b/ChangeLog index 8ae9aed3..1e8e9e9f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,14 @@ ChangeLog ========= +version 0.11.0 (release 2020-09-08) + + * Important: Debug builds on Windows now get the "d" suffix + * Various build system fixes + * Add Haiku support (François Revol ) + * Translation: Russian (Alexander Gorishnyak ) + * Translation: Update French (David Geiger ) + version 0.10.0 (release 2019-12-17) * Detect XFCE desktop correctly. (Sandro Knauß ) From ee94ca34151ef24c89561f8d8c89f28da8cf3be1 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Tue, 8 Sep 2020 14:48:01 +0200 Subject: [PATCH 022/168] Set version to 0.11.0 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a1aff8f2..a069925d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ include(FindPkgConfig) ### -set(QTKEYCHAIN_VERSION 0.10.90) +set(QTKEYCHAIN_VERSION 0.11.0) set(QTKEYCHAIN_SOVERSION 1) ### From ba46d299d54478f48eecb1fb2f3b813af120d400 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Tue, 8 Sep 2020 14:48:50 +0200 Subject: [PATCH 023/168] Bump version to 0.11.90 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a1aff8f2..e9cfba61 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ include(FindPkgConfig) ### -set(QTKEYCHAIN_VERSION 0.10.90) +set(QTKEYCHAIN_VERSION 0.11.90) set(QTKEYCHAIN_SOVERSION 1) ### From d63e7ad37b1bee0a841414e341bc8a9f1cf7aa63 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 10 Apr 2020 14:48:34 +0200 Subject: [PATCH 024/168] Fix translations installation directory As I say in https://github.com/frankosterfeld/qtkeychain/issues/42#issuecomment-611996859 , the result of `qmake -query QT_INSTALL_TRANSLATIONS` is the place where **Qt** stores its translations, not the target directory to install other translations. This commit introduces `QTKEYCHAIN_TRANSLATIONS_DIR` as a CMake cache variable defaulting to a reasonable location inside `${CMAKE_INSTALL_DATADIR}`. `QT_TRANSLATIONS_DIR` is still checked, in case some packager already relies on it. Closes #42. Closes #54. --- CMakeLists.txt | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e9cfba61..9b7524a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -190,21 +190,19 @@ if ( BUILD_TRANSLATIONS ) add_custom_target(messages DEPENDS ${qtkeychain_MESSAGES}) add_custom_target(translations DEPENDS ${qtkeychain_QM_FILES}) - if(NOT QT_TRANSLATIONS_DIR) - # If this directory is missing, we are in a Qt5 environment. - # Extract the qmake executable location - get_target_property(QT5_QMAKE_EXECUTABLE Qt5::qmake IMPORTED_LOCATION) - # Ask Qt5 where to put the translations - execute_process( COMMAND ${QT5_QMAKE_EXECUTABLE} -query QT_INSTALL_TRANSLATIONS - OUTPUT_VARIABLE qt_translations_dir OUTPUT_STRIP_TRAILING_WHITESPACE ) - # make sure we have / and not \ as qmake gives on windows - file( TO_CMAKE_PATH "${qt_translations_dir}" qt_translations_dir) - set( QT_TRANSLATIONS_DIR ${qt_translations_dir} CACHE PATH - "The location of the Qt translations" FORCE) + if(QTKEYCHAIN_VERSION_INFIX EQUAL 5 AND QT_TRANSLATIONS_DIR AND NOT QTKEYCHAIN_TRANSLATIONS_DIR) + # Back compatibility with pre-0.11 versions + message (WARNING "QT_TRANSLATIONS_DIR is deprecated, use QTKEYCHAIN_TRANSLATIONS_DIR instead") + set(QTKEYCHAIN_TRANSLATIONS_DIR ${QT_TRANSLATIONS_DIR} + CACHE PATH "The location of the QtKeychain translations" FORCE) + else() + set(QTKEYCHAIN_TRANSLATIONS_DIR + ${CMAKE_INSTALL_DATADIR}/qt${QTKEYCHAIN_VERSION_INFIX}keychain/translations + CACHE PATH "The location of the QtKeychain translations" ) endif() install(FILES ${qtkeychain_QM_FILES} - DESTINATION ${QT_TRANSLATIONS_DIR}) + DESTINATION ${QTKEYCHAIN_TRANSLATIONS_DIR}) endif( BUILD_TRANSLATIONS ) set(QTKEYCHAIN_TARGET_NAME qt${QTKEYCHAIN_VERSION_INFIX}keychain) From c7e5a6f26d12b909556095c210fe65534132628e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Revol?= Date: Tue, 4 Feb 2020 00:13:38 +0100 Subject: [PATCH 025/168] Allow overriding install include path for Haiku --- CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b7524a1..7164feae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -216,7 +216,10 @@ if(WIN32) endif() target_link_libraries(${QTKEYCHAIN_TARGET_NAME} PUBLIC ${qtkeychain_LIBRARIES} PRIVATE ${qtkeychain_LIBRARIES_PRIVATE}) -target_include_directories(${QTKEYCHAIN_TARGET_NAME} PUBLIC $) +if(NOT INTERFACE_INCLUDE_SUFFIX) + set(INTERFACE_INCLUDE_SUFFIX include) +endif() +target_include_directories(${QTKEYCHAIN_TARGET_NAME} PUBLIC $) generate_export_header(${QTKEYCHAIN_TARGET_NAME} EXPORT_FILE_NAME qkeychain_export.h From ec301143dea929c885294b36458a471b0f7efa6e Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 10 Apr 2020 14:48:34 +0200 Subject: [PATCH 026/168] Fix translations installation directory As I say in https://github.com/frankosterfeld/qtkeychain/issues/42#issuecomment-611996859 , the result of `qmake -query QT_INSTALL_TRANSLATIONS` is the place where **Qt** stores its translations, not the target directory to install other translations. This commit introduces `QTKEYCHAIN_TRANSLATIONS_DIR` as a CMake cache variable defaulting to a reasonable location inside `${CMAKE_INSTALL_DATADIR}`. `QT_TRANSLATIONS_DIR` is still checked, in case some packager already relies on it. Closes #42. Closes #54. --- CMakeLists.txt | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a069925d..515f8c16 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -190,21 +190,19 @@ if ( BUILD_TRANSLATIONS ) add_custom_target(messages DEPENDS ${qtkeychain_MESSAGES}) add_custom_target(translations DEPENDS ${qtkeychain_QM_FILES}) - if(NOT QT_TRANSLATIONS_DIR) - # If this directory is missing, we are in a Qt5 environment. - # Extract the qmake executable location - get_target_property(QT5_QMAKE_EXECUTABLE Qt5::qmake IMPORTED_LOCATION) - # Ask Qt5 where to put the translations - execute_process( COMMAND ${QT5_QMAKE_EXECUTABLE} -query QT_INSTALL_TRANSLATIONS - OUTPUT_VARIABLE qt_translations_dir OUTPUT_STRIP_TRAILING_WHITESPACE ) - # make sure we have / and not \ as qmake gives on windows - file( TO_CMAKE_PATH "${qt_translations_dir}" qt_translations_dir) - set( QT_TRANSLATIONS_DIR ${qt_translations_dir} CACHE PATH - "The location of the Qt translations" FORCE) + if(QTKEYCHAIN_VERSION_INFIX EQUAL 5 AND QT_TRANSLATIONS_DIR AND NOT QTKEYCHAIN_TRANSLATIONS_DIR) + # Back compatibility with pre-0.11 versions + message (WARNING "QT_TRANSLATIONS_DIR is deprecated, use QTKEYCHAIN_TRANSLATIONS_DIR instead") + set(QTKEYCHAIN_TRANSLATIONS_DIR ${QT_TRANSLATIONS_DIR} + CACHE PATH "The location of the QtKeychain translations" FORCE) + else() + set(QTKEYCHAIN_TRANSLATIONS_DIR + ${CMAKE_INSTALL_DATADIR}/qt${QTKEYCHAIN_VERSION_INFIX}keychain/translations + CACHE PATH "The location of the QtKeychain translations" ) endif() install(FILES ${qtkeychain_QM_FILES} - DESTINATION ${QT_TRANSLATIONS_DIR}) + DESTINATION ${QTKEYCHAIN_TRANSLATIONS_DIR}) endif( BUILD_TRANSLATIONS ) set(QTKEYCHAIN_TARGET_NAME qt${QTKEYCHAIN_VERSION_INFIX}keychain) From df5844e033acfa961a15a19454141c7a29e84cd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Revol?= Date: Tue, 4 Feb 2020 00:13:38 +0100 Subject: [PATCH 027/168] Allow overriding install include path for Haiku --- CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 515f8c16..a2a3d802 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -216,7 +216,10 @@ if(WIN32) endif() target_link_libraries(${QTKEYCHAIN_TARGET_NAME} PUBLIC ${qtkeychain_LIBRARIES} PRIVATE ${qtkeychain_LIBRARIES_PRIVATE}) -target_include_directories(${QTKEYCHAIN_TARGET_NAME} PUBLIC $) +if(NOT INTERFACE_INCLUDE_SUFFIX) + set(INTERFACE_INCLUDE_SUFFIX include) +endif() +target_include_directories(${QTKEYCHAIN_TARGET_NAME} PUBLIC $) generate_export_header(${QTKEYCHAIN_TARGET_NAME} EXPORT_FILE_NAME qkeychain_export.h From eebc76c443abc4b7bf1cacc1d11352bfb1d0ec41 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Tue, 8 Sep 2020 15:12:18 +0200 Subject: [PATCH 028/168] Update changelog for 0.11.1 --- ChangeLog | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ChangeLog b/ChangeLog index 1e8e9e9f..f94fae8e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,10 @@ ChangeLog ========= +version 0.11.1 (release 2020-09-08) + + * Build system fixes + version 0.11.0 (release 2020-09-08) * Important: Debug builds on Windows now get the "d" suffix From 6743abd98586fbabd01da9839f53f61ccfb7f83c Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Tue, 8 Sep 2020 15:13:16 +0200 Subject: [PATCH 029/168] Bump version to 0.11.1 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a2a3d802..5eec8109 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ include(FindPkgConfig) ### -set(QTKEYCHAIN_VERSION 0.11.0) +set(QTKEYCHAIN_VERSION 0.11.1) set(QTKEYCHAIN_SOVERSION 1) ### From 81c11f80ffeb18bda92b2359facf4ce2ed67b67e Mon Sep 17 00:00:00 2001 From: Aaron Bray Date: Tue, 22 Sep 2020 08:10:43 -0400 Subject: [PATCH 030/168] Change Qt5 find to components and required if Qt4 is not selected --- CMakeLists.txt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7164feae..2c9c1e85 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,15 +39,14 @@ if (WIN32) endif() if( NOT BUILD_WITH_QT4 ) - # try Qt5 first, and prefer that if found - find_package(Qt5Core QUIET) + find_package(Qt5 COMPONENTS Core REQUIRED) endif() if (Qt5Core_FOUND AND NOT BUILD_WITH_QT4) set(QTKEYCHAIN_VERSION_INFIX 5) if(UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU) - find_package(Qt5DBus REQUIRED) + find_package(Qt5 COMPONENTS DBus REQUIRED) include_directories(${Qt5DBus_INCLUDE_DIRS}) set(QTDBUS_LIBRARIES ${Qt5DBus_LIBRARIES}) macro(qt_add_dbus_interface) @@ -56,7 +55,7 @@ if (Qt5Core_FOUND AND NOT BUILD_WITH_QT4) endif() if(BUILD_TRANSLATIONS) - find_package(Qt5LinguistTools REQUIRED) + find_package(Qt5 COMPONENTS LinguistTools REQUIRED) macro(qt_add_translation) qt5_add_translation(${ARGN}) endmacro(qt_add_translation) From f39305c8d84ade847db3cb50215bceffc3394c92 Mon Sep 17 00:00:00 2001 From: David Faure Date: Fri, 27 Nov 2020 11:02:47 +0100 Subject: [PATCH 031/168] Increase cmake required version to 3.3 This fixes compilation with recent Qt versions, because Qt5LinguistToolsMacros.cmake uses IN_LIST as an operator, which requires either CMP0057 to be enabled, or cmake >= 3.3 to be required. See my comment in https://codereview.qt-project.org/c/qt/qttools/+/319186 Upgrading to 3.3 as min req gave a warning about ECMPackageConfigHelpers not being needed anymore, we can just use CMakePackageConfigHelpers which is in cmake since 3.0. --- CMakeLists.txt | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c9c1e85..c534e546 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,19 +1,18 @@ -cmake_minimum_required(VERSION 2.8.11) -project(qtkeychain) - -include(FindPkgConfig) - -### +cmake_minimum_required(VERSION 3.3) set(QTKEYCHAIN_VERSION 0.11.90) set(QTKEYCHAIN_SOVERSION 1) +project(qtkeychain VERSION ${QTKEYCHAIN_VERSION}) + +include(FindPkgConfig) + ### set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${PROJECT_SOURCE_DIR}/cmake/Modules") include(GNUInstallDirs) include(GenerateExportHeader) -include(ECMPackageConfigHelpers) +include(CMakePackageConfigHelpers) include(ECMSetupVersion) include(ECMGeneratePriFile) @@ -254,7 +253,7 @@ endif() ### CMake config file ### -ecm_configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/QtKeychainConfig.cmake.in" +configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/QtKeychainConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfig.cmake" INSTALL_DESTINATION Qt${QTKEYCHAIN_VERSION_INFIX}Keychain) From 5c1f008255af44498361838a40de5a7429ffaa11 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 7 Dec 2020 13:04:11 +0100 Subject: [PATCH 032/168] *.cmake.in: Make sure find_dependency() is defined The commit f39305c8 that switches from ECMPackageConfigHelpers to CMakePackageConfigHelpers has broken integration with Qt5Keychain from CMake-based projects, with find_package(Qt5Keychain) leading to a message like: ``` CMake Error at install/lib/cmake/Qt5Keychain/Qt5KeychainConfig.cmake:36 (find_dependency): Unknown CMake command "find_dependency". ``` Apparently, the find_dependency() macro definition was inserted in QtKeychainConfig.cmake by ECM machinery before. Including the respective definition from the CMake library fixes the problem. --- QtKeychainConfig.cmake.in | 1 + 1 file changed, 1 insertion(+) diff --git a/QtKeychainConfig.cmake.in b/QtKeychainConfig.cmake.in index 3196bffe..26a8ca69 100644 --- a/QtKeychainConfig.cmake.in +++ b/QtKeychainConfig.cmake.in @@ -7,6 +7,7 @@ include("${CMAKE_CURRENT_LIST_DIR}/Qt@QTKEYCHAIN_VERSION_INFIX@KeychainLibraryDepends.cmake") +include(CMakeFindDependencyMacro) if("@QTKEYCHAIN_VERSION_INFIX@" STREQUAL "5") find_dependency(Qt5Core) From 1a567436786a22e7100b84c7238cb29d5f5a6d67 Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Fri, 11 Dec 2020 13:55:15 +0100 Subject: [PATCH 033/168] Do not use -Wall on MSVC It'll emit warnings in Qt and in the MSVC STL, both of which aren't clean when /Wall is enabled; see https://docs.microsoft.com/en-us/cpp/preprocessor/compiler-warnings-that-are-off-by-default?view=msvc-160 Instead, enable up to /W4 there, which in turn requires some workarounds for older CMake versions. --- CMakeLists.txt | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c534e546..a13ab824 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -121,7 +121,18 @@ set(qtkeychain_SOURCES keychain.h ) -add_definitions( -Wall ) +if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + # CMake < 3.15 sneaks in /W# flags for us, so we need a replacement, + # or we'll get a warning (cf. CMP0092) + if (CMAKE_CXX_FLAGS MATCHES "/W[0-4]") + string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") + endif() +else() + # MSVC's STL / Qt headers are not MSVC -Wall clean, so don't enable it there + add_definitions( -Wall ) +endif() if(WIN32) list(APPEND qtkeychain_SOURCES keychain_win.cpp) From 6680cf2b85a931c330fa651fbd4e85a93f1d629f Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Mon, 14 Dec 2020 12:42:45 +0100 Subject: [PATCH 034/168] Windows: Fix compiler warnings --- keychain_win.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/keychain_win.cpp b/keychain_win.cpp index 2366b472..27aaa96d 100644 --- a/keychain_win.cpp +++ b/keychain_win.cpp @@ -24,20 +24,20 @@ void ReadPasswordJobPrivate::scheduledStart() { PCREDENTIALW cred; if (!CredReadW(name, CRED_TYPE_GENERIC, 0, &cred)) { - Error error; + Error err; QString msg; switch(GetLastError()) { case ERROR_NOT_FOUND: - error = EntryNotFound; + err = EntryNotFound; msg = tr("Password entry not found"); break; default: - error = OtherError; + err = OtherError; msg = tr("Could not decrypt data"); break; } - q->emitFinishedWithError( error, msg ); + q->emitFinishedWithError( err, msg ); return; } @@ -96,20 +96,20 @@ void DeletePasswordJobPrivate::scheduledStart() { LPCWSTR name = (LPCWSTR)key.utf16(); if (!CredDeleteW(name, CRED_TYPE_GENERIC, 0)) { - Error error; + Error err; QString msg; switch(GetLastError()) { case ERROR_NOT_FOUND: - error = EntryNotFound; + err = EntryNotFound; msg = tr("Password entry not found"); break; default: - error = OtherError; + err = OtherError; msg = tr("Could not decrypt data"); break; } - q->emitFinishedWithError( error, msg ); + q->emitFinishedWithError( err, msg ); } else { q->emitFinished(); } From c8b8cfe963389234169ba8a237fa08ab1df6fac0 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Tue, 15 Dec 2020 12:26:38 +0100 Subject: [PATCH 035/168] Update macOS naming It's called macOS these days, not "Mac OS X". --- ReadMe.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReadMe.txt b/ReadMe.txt index 6fa68b6b..94dae519 100644 --- a/ReadMe.txt +++ b/ReadMe.txt @@ -3,7 +3,7 @@ QtKeychain QtKeychain is a Qt API to store passwords and other secret data securely. How the data is stored depends on the platform: - * **Mac OS X:** Passwords are stored in the OS X Keychain. + * **macOS:** Passwords are stored in the macOS Keychain. * **Linux/Unix:** If running, GNOME Keyring is used, otherwise qtkeychain tries to use KWallet (via D-Bus), if available. From ba6a75c14e4c97fd1d945e1b625b5bcb8ddccd9b Mon Sep 17 00:00:00 2001 From: Mathias Hasselmann Date: Wed, 10 Aug 2016 02:13:47 +0200 Subject: [PATCH 036/168] Store encrypted passwords on Android --- CMakeLists.txt | 17 ++ androidkeystore.cpp | 303 +++++++++++++++++++++++++++++++++++ androidkeystore_p.h | 371 +++++++++++++++++++++++++++++++++++++++++++ keychain_android.cpp | 184 +++++++++++++++++++++ 4 files changed, 875 insertions(+) create mode 100644 androidkeystore.cpp create mode 100644 androidkeystore_p.h create mode 100644 keychain_android.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a13ab824..1d3bb8ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,17 @@ endif() if (Qt5Core_FOUND AND NOT BUILD_WITH_QT4) set(QTKEYCHAIN_VERSION_INFIX 5) + if(ANDROID) + if(Qt5Core_VERSION VERSION_LESS 5.7) + find_package(Qt5 COMPONENTS Core REQUIRED Private) + include_directories(${Qt5Core_PRIVATE_INCLUDE_DIRS}) + endif() + + find_package(Qt5 COMPONENTS AndroidExtras REQUIRED) + include_directories(${Qt5AndroidExtras_INCLUDE_DIRS}) + set(QTANDROIDEXTRAS_LIBRARIES ${Qt5AndroidExtras_LIBRARIES}) + endif() + if(UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU) find_package(Qt5 COMPONENTS DBus REQUIRED) include_directories(${Qt5DBus_INCLUDE_DIRS}) @@ -182,6 +193,12 @@ if(UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU) list(APPEND qtkeychain_LIBRARIES ${QTDBUS_LIBRARIES} ) endif() +if(ANDROID) + set(CMAKE_CXX_STANDARD 11) + list(APPEND qtkeychain_SOURCES keychain_android.cpp androidkeystore.cpp plaintextstore.cpp) + list(APPEND qtkeychain_LIBRARIES ${QTANDROIDEXTRAS_LIBRARIES} ) +endif() + QT_WRAP_CPP(qtkeychain_MOC_OUTFILES keychain.h keychain_p.h gnomekeyring_p.h) set(qtkeychain_TR_FILES diff --git a/androidkeystore.cpp b/androidkeystore.cpp new file mode 100644 index 00000000..cd4297cd --- /dev/null +++ b/androidkeystore.cpp @@ -0,0 +1,303 @@ +#include "androidkeystore_p.h" + +#if QT_VERSION < QT_VERSION_CHECK(5, 7, 0) +#include "private/qjni_p.h" +#endif + +#include + +using namespace QKeychain; + +using namespace android::content; +using namespace android::security; + +using namespace java::io; +using namespace java::lang; +using namespace java::math; +using namespace java::util; +using namespace java::security; +using namespace java::security::spec; + +using namespace javax::crypto; +using namespace javax::security::auth::x500; +using namespace javax::security::cert; + +const BigInteger BigInteger::ONE = BigInteger::getStaticObjectField("java/math/BigInteger", "ONE", "Ljava/math/BigInteger;"); + +const int Calendar::YEAR = Calendar::getStaticField("java/util/Calendar", "YEAR"); + +const int Cipher::DECRYPT_MODE = Cipher::getStaticField("javax/crypto/Cipher", "DECRYPT_MODE"); +const int Cipher::ENCRYPT_MODE = Cipher::getStaticField("javax/crypto/Cipher", "ENCRYPT_MODE"); + +namespace { + +#if QT_VERSION < QT_VERSION_CHECK(5, 7, 0) + +struct JNIObject +{ + JNIObject(QSharedPointer d): d(d) {} + + static JNIObject fromLocalRef(jobject o) + { + return JNIObject(QSharedPointer::create(QJNIObjectPrivate::fromLocalRef(o))); + } + + jobject object() const { return d->object(); } + QSharedPointer d; +}; + +#else + +using JNIObject = QAndroidJniObject; + +#endif + +QByteArray fromArray(const jbyteArray array) +{ + QAndroidJniEnvironment env; + jbyte *const bytes = env->GetByteArrayElements(array, Q_NULLPTR); + const QByteArray result(reinterpret_cast(bytes), env->GetArrayLength(array)); + env->ReleaseByteArrayElements(array, bytes, JNI_ABORT); + return result; +} + +JNIObject toArray(const QByteArray &bytes) +{ + QAndroidJniEnvironment env; + const int length = bytes.length(); + JNIObject array = JNIObject::fromLocalRef(env->NewByteArray(length)); + env->SetByteArrayRegion(static_cast(array.object()), + 0, length, reinterpret_cast(bytes.constData())); + return array; +} + +} + +bool Object::handleExceptions() +{ + QAndroidJniEnvironment env; + + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + return false; + } + + return true; +} + + +KeyPairGenerator KeyPairGenerator::getInstance(const QString &algorithm, const QString &provider) +{ + return handleExceptions(callStaticObjectMethod("java/security/KeyPairGenerator", "getInstance", + "(Ljava/lang/String;Ljava/lang/String;)Ljava/security/KeyPairGenerator;", + fromString(algorithm).object(), fromString(provider).object())); +} + +KeyPair KeyPairGenerator::generateKeyPair() const +{ + return handleExceptions(callObjectMethod("generateKeyPair", "()Ljava/security/KeyPair;")); +} + +bool KeyPairGenerator::initialize(const AlgorithmParameterSpec &spec) const +{ + callMethod("initialize", "(Ljava/security/spec/AlgorithmParameterSpec;)V", spec.object()); + return handleExceptions(); +} + +bool KeyStore::containsAlias(const QString &alias) const +{ + return handleExceptions(callMethod("containsAlias", "(Ljava/lang/String;)Z", + fromString(alias).object())); +} + +bool KeyStore::deleteEntry(const QString &alias) const +{ + callMethod("deleteEntry", "(Ljava/lang/String;)Z", fromString(alias).object()); + return handleExceptions(); +} + +KeyStore KeyStore::getInstance(const QString &type) +{ + return handleExceptions(callStaticObjectMethod("java/security/KeyStore", "getInstance", + "(Ljava/lang/String;)Ljava/security/KeyStore;", + fromString(type).object())); +} + +KeyStore::Entry KeyStore::getEntry(const QString &alias, const KeyStore::ProtectionParameter ¶m) const +{ + return handleExceptions(callObjectMethod("getEntry", + "(Ljava/lang/String;Ljava/security/KeyStore$ProtectionParameter;)Ljava/security/KeyStore$Entry;", + fromString(alias).object(), param.object())); +} + +bool KeyStore::load(const KeyStore::LoadStoreParameter ¶m) const +{ + callMethod("load", "(Ljava/security/KeyStore$LoadStoreParameter;)V", param.object()); + return handleExceptions(); +} + + +Calendar Calendar::getInstance() +{ + return handleExceptions(callStaticObjectMethod("java/util/Calendar", "getInstance", + "()Ljava/util/Calendar;")); + +} + +bool Calendar::add(int field, int amount) const +{ + callMethod("add", "(II)V", field, amount); + return handleExceptions(); +} + +Date Calendar::getTime() const +{ + return handleExceptions(callObjectMethod("getTime", "()Ljava/util/Date;")); +} + +KeyPairGeneratorSpec::Builder::Builder(const Context &context) + : Object(QAndroidJniObject("android/security/KeyPairGeneratorSpec$Builder", + "(Landroid/content/Context;)V", + context.object())) +{ + handleExceptions(); +} + +KeyPairGeneratorSpec::Builder KeyPairGeneratorSpec::Builder::setAlias(const QString &alias) const +{ + return handleExceptions(callObjectMethod("setAlias", + "(Ljava/lang/String;)Landroid/security/KeyPairGeneratorSpec$Builder;", + fromString(alias).object())); +} + +KeyPairGeneratorSpec::Builder KeyPairGeneratorSpec::Builder::setSubject(const X500Principal &subject) const +{ + return handleExceptions(callObjectMethod("setSubject", + "(Ljavax/security/auth/x500/X500Principal;)Landroid/security/KeyPairGeneratorSpec$Builder;", + subject.object())); +} + +KeyPairGeneratorSpec::Builder KeyPairGeneratorSpec::Builder::setSerialNumber(const BigInteger &serial) const +{ + return handleExceptions(callObjectMethod("setSerialNumber", + "(Ljava/math/BigInteger;)Landroid/security/KeyPairGeneratorSpec$Builder;", + serial.object())); +} + +KeyPairGeneratorSpec::Builder KeyPairGeneratorSpec::Builder::setStartDate(const Date &date) const +{ + return handleExceptions(callObjectMethod("setStartDate", + "(Ljava/util/Date;)Landroid/security/KeyPairGeneratorSpec$Builder;", + date.object())); +} + +KeyPairGeneratorSpec::Builder KeyPairGeneratorSpec::Builder::setEndDate(const Date &date) const +{ + return handleExceptions(callObjectMethod("setEndDate", + "(Ljava/util/Date;)Landroid/security/KeyPairGeneratorSpec$Builder;", + date.object())); +} + +KeyPairGeneratorSpec KeyPairGeneratorSpec::Builder::build() const +{ + return handleExceptions(callObjectMethod("build", "()Landroid/security/KeyPairGeneratorSpec;")); +} + +X500Principal::X500Principal(const QString &name) + : Object(QAndroidJniObject("javax/security/auth/x500/X500Principal", + "(Ljava/lang/String;)V", + fromString(name).object())) +{ + handleExceptions(); +} + +Certificate KeyStore::PrivateKeyEntry::getCertificate() const +{ + return handleExceptions(callObjectMethod("getCertificate", "()Ljava/security/cert/Certificate;")); +} + +PrivateKey KeyStore::PrivateKeyEntry::getPrivateKey() const +{ + return handleExceptions(callObjectMethod("getPrivateKey", "()Ljava/security/PrivateKey;")); +} + +PublicKey Certificate::getPublicKey() const +{ + return handleExceptions(callObjectMethod("getPublicKey", "()Ljava/security/PublicKey;")); +} + +ByteArrayInputStream::ByteArrayInputStream(const QByteArray &bytes) + : InputStream(QAndroidJniObject("java/io/ByteArrayInputStream", "([B)V", toArray(bytes).object())) +{ +} + +ByteArrayOutputStream::ByteArrayOutputStream() + : OutputStream(QAndroidJniObject("java/io/ByteArrayOutputStream")) +{ + handleExceptions(); +} + +QByteArray ByteArrayOutputStream::toByteArray() const +{ + const QAndroidJniObject wrapper = callObjectMethod("toByteArray"); + + if (!handleExceptions()) + return QByteArray(); + + return fromArray(static_cast(wrapper.object())); +} + +int InputStream::read() const +{ + return handleExceptions(callMethod("read"), -1); +} + +bool OutputStream::write(const QByteArray &bytes) const +{ + callMethod("write", "([B)V", toArray(bytes).object()); + return handleExceptions(); +} + +bool OutputStream::close() const +{ + callMethod("close"); + return handleExceptions(); +} + +bool OutputStream::flush() const +{ + callMethod("flush"); + return handleExceptions(); +} + +Cipher Cipher::getInstance(const QString &transformation, const QString &provider) +{ + return handleExceptions(callStaticObjectMethod("javax/crypto/Cipher", "getInstance", + "(Ljava/lang/String;Ljava/lang/String;)Ljavax/crypto/Cipher;", + fromString(transformation).object(), + fromString(provider).object())); +} + +bool Cipher::init(int opMode, const Key &key) const +{ + callMethod("init", "(ILjava/security/Key;)V", opMode, key.object()); + return handleExceptions(); +} + + +CipherOutputStream::CipherOutputStream(const OutputStream &stream, const Cipher &cipher) + : FilterOutputStream(QAndroidJniObject("javax/crypto/CipherOutputStream", + "(Ljava/io/OutputStream;Ljavax/crypto/Cipher;)V", + stream.object(), cipher.object())) +{ + handleExceptions(); +} + +CipherInputStream::CipherInputStream(const InputStream &stream, const Cipher &cipher) + : FilterInputStream(QAndroidJniObject("javax/crypto/CipherInputStream", + "(Ljava/io/InputStream;Ljavax/crypto/Cipher;)V", + stream.object(), cipher.object())) +{ + handleExceptions(); +} diff --git a/androidkeystore_p.h b/androidkeystore_p.h new file mode 100644 index 00000000..99b52b32 --- /dev/null +++ b/androidkeystore_p.h @@ -0,0 +1,371 @@ +/****************************************************************************** + * Copyright (C) 2016 Mathias Hasselmann * + * * + * 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. For licensing and distribution * + * details, check the accompanying file 'COPYING'. * + *****************************************************************************/ + +#ifndef QTKEYCHAIN_ANDROIDKEYSTORE_P_H +#define QTKEYCHAIN_ANDROIDKEYSTORE_P_H + +#include + +namespace QKeychain { + +namespace javax { +namespace security { + +namespace auth { namespace x500 { class X500Principal; } } +namespace cert { class Certificate; } + +} +} + +namespace java { +namespace lang { + +class Object : protected QAndroidJniObject +{ +public: + inline Object(jobject object) : QAndroidJniObject(object) {} + inline Object(const QAndroidJniObject &object) : QAndroidJniObject(object) {} + inline operator bool() const { return isValid(); } + + using QAndroidJniObject::object; + using QAndroidJniObject::toString; + +protected: + static bool handleExceptions(); + + template + static T handleExceptions(const T &result, const T &resultOnError = T()); +}; + +template +inline T Object::handleExceptions(const T &result, const T &resultOnError) +{ + if (!handleExceptions()) + return resultOnError; + + return result; +} + +} // namespace lang + +namespace io { + +class InputStream : public java::lang::Object +{ +public: + using Object::Object; + + int read() const; +}; + +class ByteArrayInputStream : public InputStream +{ +public: + using InputStream::InputStream; + + explicit ByteArrayInputStream(const QByteArray &bytes); +}; + +class FilterInputStream : public InputStream +{ +public: + using InputStream::InputStream; +}; + +class OutputStream : public java::lang::Object +{ +public: + using Object::Object; + + bool write(const QByteArray &bytes) const; + bool flush() const; + bool close() const; +}; + +class ByteArrayOutputStream : public OutputStream +{ +public: + using OutputStream::OutputStream; + + ByteArrayOutputStream(); + + QByteArray toByteArray() const; +}; + +class FilterOutputStream : public OutputStream +{ +public: + using OutputStream::OutputStream; +}; + +} // namespace io + +namespace math { + +class BigInteger : public java::lang::Object +{ +public: + using Object::Object; + + static const BigInteger ZERO; + static const BigInteger ONE; + static const BigInteger TEN; +}; + +} // namespace math + +namespace util { + +class Date : public java::lang::Object +{ +public: + using Object::Object; +}; + +class Calendar : public java::lang::Object +{ +public: + using Object::Object; + + static const int YEAR; + static const int MONTH; + static const int DAY; + static const int HOUR; + static const int MINUTE; + static const int SECOND; + static const int MILLISECOND; + + static Calendar getInstance(); + + bool add(int field, int amount) const; + Date getTime() const; +}; + +} // namespace util + +namespace security { +namespace spec { + +class AlgorithmParameterSpec : public java::lang::Object +{ +public: + using Object::Object; +}; + +} // namespace spec + +class Key : public java::lang::Object +{ +public: + using Object::Object; +}; + +class PrivateKey : public Key +{ +public: + using Key::Key; + + PrivateKey(const Key &init): Key(init) {} +}; + +class PublicKey : public Key +{ +public: + using Key::Key; + + PublicKey(const Key &init): Key(init) {} +}; + +class KeyPair : public java::lang::Object +{ +public: + using Object::Object; +}; + +class KeyPairGenerator : public java::lang::Object +{ +public: + using Object::Object; + + static KeyPairGenerator getInstance(const QString &algorithm, const QString &provider); + KeyPair generateKeyPair() const; + bool initialize(const spec::AlgorithmParameterSpec &spec) const; + +}; + +class KeyStore : public java::lang::Object +{ +public: + class Entry : public java::lang::Object + { + public: + using Object::Object; + }; + + class PrivateKeyEntry : public Entry + { + public: + using Entry::Entry; + + inline PrivateKeyEntry(const Entry &init): Entry(init) {} + + javax::security::cert::Certificate getCertificate() const; + java::security::PrivateKey getPrivateKey() const; + }; + + class LoadStoreParameter : public java::lang::Object + { + public: + using Object::Object; + }; + + class ProtectionParameter : public java::lang::Object + { + public: + using Object::Object; + }; + + using Object::Object; + + bool containsAlias(const QString &alias) const; + bool deleteEntry(const QString &alias) const; + static KeyStore getInstance(const QString &type); + Entry getEntry(const QString &alias, const ProtectionParameter ¶m = Q_NULLPTR) const; + bool load(const LoadStoreParameter ¶m = Q_NULLPTR) const; +}; + +namespace interfaces { + +class RSAPrivateKey : public PrivateKey +{ +public: + using PrivateKey::PrivateKey; + + RSAPrivateKey(const PrivateKey &init): PrivateKey(init) {} +}; + +class RSAPublicKey : public PublicKey +{ +public: + using PublicKey::PublicKey; + + RSAPublicKey(const PublicKey &init): PublicKey(init) {} +}; + +} // namespace interfaces + +} // namespace security +} // namespace java + +namespace android { +namespace content { + +class Context : public java::lang::Object +{ +public: + using Object::Object; +}; + +} // namespace content + +namespace security { + +class KeyPairGeneratorSpec : public java::security::spec::AlgorithmParameterSpec +{ +public: + class Builder : public java::lang::Object + { + public: + using Object::Object; + + explicit Builder(const android::content::Context &context); + + Builder setAlias(const QString &alias) const; + Builder setSubject(const javax::security::auth::x500::X500Principal &subject) const; + Builder setSerialNumber(const java::math::BigInteger &serial) const; + Builder setStartDate(const java::util::Date &date) const; + Builder setEndDate(const java::util::Date &date) const; + KeyPairGeneratorSpec build() const; + + }; + + using AlgorithmParameterSpec::AlgorithmParameterSpec; +}; + +} // namespace security +} // namespace android + +namespace javax { +namespace crypto { + +class Cipher : public java::lang::Object +{ +public: + static const int DECRYPT_MODE; + static const int ENCRYPT_MODE; + + using Object::Object; + + static Cipher getInstance(const QString &transformation, const QString &provider); + bool init(int opMode, const java::security::Key &key) const; +}; + +class CipherInputStream : public java::io::FilterInputStream +{ +public: + using FilterInputStream::FilterInputStream; + + explicit CipherInputStream(const InputStream &stream, const Cipher &cipher); +}; + +class CipherOutputStream : public java::io::FilterOutputStream +{ +public: + using FilterOutputStream::FilterOutputStream; + + explicit CipherOutputStream(const OutputStream &stream, const Cipher &cipher); +}; + +} + +namespace security { +namespace auth { +namespace x500 { + +class X500Principal; + +class X500Principal : public java::lang::Object +{ +public: + using Object::Object; + + explicit X500Principal(const QString &name); +}; + +} // namespace x500 +} // namespace auth + +namespace cert { + +class Certificate : public java::lang::Object +{ +public: + using Object::Object; + + java::security::PublicKey getPublicKey() const; +}; + +} // namespace cert + +} // namespace security +} // namespace javax + +} // namespace QKeychain + +#endif // QTKEYCHAIN_ANDROIDKEYSTORE_P_H diff --git a/keychain_android.cpp b/keychain_android.cpp new file mode 100644 index 00000000..850310ad --- /dev/null +++ b/keychain_android.cpp @@ -0,0 +1,184 @@ +/****************************************************************************** + * Copyright (C) 2016 Mathias Hasselmann * + * * + * 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. For licensing and distribution * + * details, check the accompanying file 'COPYING'. * + *****************************************************************************/ + +#include "keychain_p.h" + +#include "androidkeystore_p.h" +#include "plaintextstore_p.h" + +#include + +using namespace QKeychain; + +using android::content::Context; +using android::security::KeyPairGeneratorSpec; + +using java::io::ByteArrayInputStream; +using java::io::ByteArrayOutputStream; +using java::security::interfaces::RSAPrivateKey; +using java::security::interfaces::RSAPublicKey; +using java::security::KeyPair; +using java::security::KeyPairGenerator; +using java::security::KeyStore; +using java::util::Calendar; + +using javax::crypto::Cipher; +using javax::crypto::CipherInputStream; +using javax::crypto::CipherOutputStream; +using javax::security::auth::x500::X500Principal; + +namespace { + +inline QString makeAlias(const QString &service, const QString &key) +{ + return service + QLatin1Char('/') + key; +} + +} + +void ReadPasswordJobPrivate::scheduledStart() +{ + PlainTextStore plainTextStore(q->service(), q->settings()); + + if (!plainTextStore.contains(q->key())) { + q->emitFinishedWithError(Error::EntryNotFound, tr("Entry not found")); + return; + } + + const QByteArray &encryptedData = plainTextStore.readData(q->key()); + const KeyStore keyStore = KeyStore::getInstance(QStringLiteral("AndroidKeyStore")); + + if (!keyStore || !keyStore.load()) { + q->emitFinishedWithError(Error::AccessDenied, tr("Could not open keystore")); + return; + } + + const auto &alias = makeAlias(q->service(), q->key()); + const KeyStore::PrivateKeyEntry entry = keyStore.getEntry(alias); + + if (!entry) { + q->emitFinishedWithError(Error::AccessDenied, tr("Could not retreive private key from keystore")); + return; + } + + const Cipher cipher = Cipher::getInstance(QStringLiteral("RSA/ECB/PKCS1Padding"), + QStringLiteral("AndroidOpenSSL")); + + if (!cipher || !cipher.init(Cipher::DECRYPT_MODE, entry.getPrivateKey())) { + q->emitFinishedWithError(Error::OtherError, tr("Could not create decryption cipher")); + return; + } + + QByteArray plainData; + const CipherInputStream inputStream(ByteArrayInputStream(encryptedData), cipher); + + for (int nextByte; (nextByte = inputStream.read()) != -1; ) + plainData.append(nextByte); + + mode = plainTextStore.readMode(q->key()); + data = plainData; + q->emitFinished(); +} + +void WritePasswordJobPrivate::scheduledStart() +{ + const KeyStore keyStore = KeyStore::getInstance(QStringLiteral("AndroidKeyStore")); + + if (!keyStore || !keyStore.load()) { + q->emitFinishedWithError(Error::AccessDenied, tr("Could not open keystore")); + return; + } + + const auto &alias = makeAlias(q->service(), q->key()); + if (!keyStore.containsAlias(alias)) { + const Calendar start = Calendar::getInstance(); + const Calendar end = Calendar::getInstance(); + end.add(Calendar::YEAR, 99); + + const KeyPairGeneratorSpec spec = + KeyPairGeneratorSpec::Builder(Context(QtAndroid::androidActivity())). + setAlias(alias). + setSubject(X500Principal(QStringLiteral("CN=QtKeychain, O=Android Authority"))). + setSerialNumber(java::math::BigInteger::ONE). + setStartDate(start.getTime()). + setEndDate(end.getTime()). + build(); + + const KeyPairGenerator generator = KeyPairGenerator::getInstance(QStringLiteral("RSA"), + QStringLiteral("AndroidKeyStore")); + + if (!generator) { + q->emitFinishedWithError(Error::OtherError, tr("Could not create private key generator")); + return; + } + + generator.initialize(spec); + + if (!generator.generateKeyPair()) { + q->emitFinishedWithError(Error::OtherError, tr("Could not generate new private key")); + return; + } + } + + const KeyStore::PrivateKeyEntry entry = keyStore.getEntry(alias); + + if (!entry) { + q->emitFinishedWithError(Error::AccessDenied, tr("Could not retreive private key from keystore")); + return; + } + + const RSAPublicKey publicKey = entry.getCertificate().getPublicKey(); + const Cipher cipher = Cipher::getInstance(QStringLiteral("RSA/ECB/PKCS1Padding"), + QStringLiteral("AndroidOpenSSL")); + + if (!cipher || !cipher.init(Cipher::ENCRYPT_MODE, publicKey)) { + q->emitFinishedWithError(Error::OtherError, tr("Could not create encryption cipher")); + return; + } + + ByteArrayOutputStream outputStream; + CipherOutputStream cipherOutputStream(outputStream, cipher); + + if (!cipherOutputStream.write(data) || !cipherOutputStream.close()) { + q->emitFinishedWithError(Error::OtherError, tr("Could not encrypt data")); + return; + } + + PlainTextStore plainTextStore(q->service(), q->settings()); + plainTextStore.write(q->key(), outputStream.toByteArray(), mode); + + if (plainTextStore.error() != NoError) + q->emitFinishedWithError(plainTextStore.error(), plainTextStore.errorString()); + else + q->emitFinished(); +} + +void DeletePasswordJobPrivate::scheduledStart() +{ + const KeyStore keyStore = KeyStore::getInstance(QStringLiteral("AndroidKeyStore")); + + if (!keyStore || !keyStore.load()) { + q->emitFinishedWithError(Error::AccessDenied, tr("Could not open keystore")); + return; + } + + const auto &alias = makeAlias(q->service(), q->key()); + if (!keyStore.deleteEntry(alias)) { + q->emitFinishedWithError(Error::OtherError, tr("Could not remove private key from keystore")); + return; + } + + PlainTextStore plainTextStore(q->service(), q->settings()); + plainTextStore.remove(q->key()); + + if (plainTextStore.error() != NoError) + q->emitFinishedWithError(plainTextStore.error(), plainTextStore.errorString()); + else + q->emitFinished(); +} From c39bb914dd0761ff671620cfdef431b116a0fda2 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Wed, 16 Dec 2020 12:42:51 +0100 Subject: [PATCH 037/168] qt5keychain.pri: Add Android support --- qt5keychain.pri | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/qt5keychain.pri b/qt5keychain.pri index f5244da2..2a367b9d 100644 --- a/qt5keychain.pri +++ b/qt5keychain.pri @@ -18,7 +18,7 @@ HEADERS += \ SOURCES += \ $$QT5KEYCHAIN_PWD/keychain.cpp -unix:!macx:!ios { +unix:!android:!macx:!ios { # Remove the following LIBSECRET_SUPPORT line # to build without libsecret support. DEFINES += LIBSECRET_SUPPORT @@ -52,6 +52,18 @@ unix:!macx:!ios { $$QT5KEYCHAIN_PWD/libsecret.cpp } +android { + QT += androidextras + + HEADERS += \ + $$QT5KEYCHAIN_PWD/androidkeystore_p.h \ + $$QT5KEYCHAIN_PWD/plaintextstore_p.h + SOURCES += \ + $$QT5KEYCHAIN_PWD/androidkeystore.cpp \ + $$QT5KEYCHAIN_PWD/keychain_android.cpp \ + $$QT5KEYCHAIN_PWD/plaintextstore.cpp +} + win32 { # Remove the following USE_CREDENTIAL_STORE line # to use the CryptProtectData Windows API function From f2071cc6a81099a715346e6532479a899e1fffd4 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Wed, 16 Dec 2020 12:59:47 +0100 Subject: [PATCH 038/168] Android: Let the system choose the cipher provider "AndroidOpenSSL" works only for old Android versions, since Android 6 "AndroidAndroidKeyStoreBCWorkaround" is suggested. Simplify this by simply omitting the parameter and let the system choose. --- androidkeystore.cpp | 7 +++---- androidkeystore_p.h | 2 +- keychain_android.cpp | 6 ++---- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/androidkeystore.cpp b/androidkeystore.cpp index cd4297cd..97260bb9 100644 --- a/androidkeystore.cpp +++ b/androidkeystore.cpp @@ -271,12 +271,11 @@ bool OutputStream::flush() const return handleExceptions(); } -Cipher Cipher::getInstance(const QString &transformation, const QString &provider) +Cipher Cipher::getInstance(const QString &transformation) { return handleExceptions(callStaticObjectMethod("javax/crypto/Cipher", "getInstance", - "(Ljava/lang/String;Ljava/lang/String;)Ljavax/crypto/Cipher;", - fromString(transformation).object(), - fromString(provider).object())); + "(Ljava/lang/String;)Ljavax/crypto/Cipher;", + fromString(transformation).object())); } bool Cipher::init(int opMode, const Key &key) const diff --git a/androidkeystore_p.h b/androidkeystore_p.h index 99b52b32..36d00477 100644 --- a/androidkeystore_p.h +++ b/androidkeystore_p.h @@ -312,7 +312,7 @@ class Cipher : public java::lang::Object using Object::Object; - static Cipher getInstance(const QString &transformation, const QString &provider); + static Cipher getInstance(const QString &transformation); bool init(int opMode, const java::security::Key &key) const; }; diff --git a/keychain_android.cpp b/keychain_android.cpp index 850310ad..ea8feb9e 100644 --- a/keychain_android.cpp +++ b/keychain_android.cpp @@ -67,8 +67,7 @@ void ReadPasswordJobPrivate::scheduledStart() return; } - const Cipher cipher = Cipher::getInstance(QStringLiteral("RSA/ECB/PKCS1Padding"), - QStringLiteral("AndroidOpenSSL")); + const Cipher cipher = Cipher::getInstance(QStringLiteral("RSA/ECB/PKCS1Padding")); if (!cipher || !cipher.init(Cipher::DECRYPT_MODE, entry.getPrivateKey())) { q->emitFinishedWithError(Error::OtherError, tr("Could not create decryption cipher")); @@ -134,8 +133,7 @@ void WritePasswordJobPrivate::scheduledStart() } const RSAPublicKey publicKey = entry.getCertificate().getPublicKey(); - const Cipher cipher = Cipher::getInstance(QStringLiteral("RSA/ECB/PKCS1Padding"), - QStringLiteral("AndroidOpenSSL")); + const Cipher cipher = Cipher::getInstance(QStringLiteral("RSA/ECB/PKCS1Padding")); if (!cipher || !cipher.init(Cipher::ENCRYPT_MODE, publicKey)) { q->emitFinishedWithError(Error::OtherError, tr("Could not create encryption cipher")); From 6f438e29ead62ee4a401bf924472a6bb1f945f29 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Wed, 16 Dec 2020 13:16:41 +0100 Subject: [PATCH 039/168] Fix typos --- keychain_android.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/keychain_android.cpp b/keychain_android.cpp index ea8feb9e..c1769133 100644 --- a/keychain_android.cpp +++ b/keychain_android.cpp @@ -63,7 +63,7 @@ void ReadPasswordJobPrivate::scheduledStart() const KeyStore::PrivateKeyEntry entry = keyStore.getEntry(alias); if (!entry) { - q->emitFinishedWithError(Error::AccessDenied, tr("Could not retreive private key from keystore")); + q->emitFinishedWithError(Error::AccessDenied, tr("Could not retrieve private key from keystore")); return; } @@ -128,7 +128,7 @@ void WritePasswordJobPrivate::scheduledStart() const KeyStore::PrivateKeyEntry entry = keyStore.getEntry(alias); if (!entry) { - q->emitFinishedWithError(Error::AccessDenied, tr("Could not retreive private key from keystore")); + q->emitFinishedWithError(Error::AccessDenied, tr("Could not retrieve private key from keystore")); return; } From 3d244ef573d704ab1eb25fc084e6aff072759175 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Wed, 16 Dec 2020 13:56:33 +0100 Subject: [PATCH 040/168] ReadMe: Mention libsecret, iOS and Android. --- ReadMe.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ReadMe.txt b/ReadMe.txt index 94dae519..f1a61fb3 100644 --- a/ReadMe.txt +++ b/ReadMe.txt @@ -5,13 +5,16 @@ QtKeychain is a Qt API to store passwords and other secret data securely. How th * **macOS:** Passwords are stored in the macOS Keychain. - * **Linux/Unix:** If running, GNOME Keyring is used, otherwise qtkeychain tries to use KWallet (via D-Bus), if available. + * **Linux/Unix:** If running, GNOME Keyring is used, otherwise qtkeychain tries to use KWallet (via D-Bus), if available. Libsecret (common API for desktop-specific solutions) + is also supported. * **Windows:** By default, the Windows Credential Store is used (requires Windows 7 or newer). Pass -DUSE_CREDENTIAL_STORE=OFF to cmake use disable it. If disabled, QtKeychain uses the Windows API function [CryptProtectData](http://msdn.microsoft.com/en-us/library/windows/desktop/aa380261%28v=vs.85%29.aspx "CryptProtectData function") to encrypt the password with the user's logon credentials. The encrypted data is then persisted via QSettings. + * **Android and iOS:** Passwords are stored in the Android keystore system and iOS keychain, respectively. + In unsupported environments QtKeychain will report an error. It will not store any data unencrypted unless explicitly requested (setInsecureFallback( true )). **License:** QtKeychain is available under the [Modified BSD License](http://www.gnu.org/licenses/license-list.html#ModifiedBSD). See the file COPYING for details. From bb36d417387f794357d68c495a6eff9f756afc71 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Wed, 16 Dec 2020 15:40:55 +0100 Subject: [PATCH 041/168] Add Qt 6 support, drop Qt 4 --- CMakeLists.txt | 66 +++++++++++++++++++++++--------------------------- 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1d3bb8ee..657b24fc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,7 @@ include(CMakePackageConfigHelpers) include(ECMSetupVersion) include(ECMGeneratePriFile) -option(BUILD_WITH_QT4 "Build qtkeychain with Qt4 no matter if Qt5 was found" OFF) +option(BUILD_WITH_QT6 "Build qtkeychain with Qt 6" OFF) option(BUILD_TEST_APPLICATION "Build test application" ON) option(BUILD_TRANSLATIONS "Build translations" ON) option(QTKEYCHAIN_STATIC "Build static library" OFF) @@ -37,11 +37,11 @@ if (WIN32) endif() endif() -if( NOT BUILD_WITH_QT4 ) +if( NOT BUILD_WITH_QT6 ) find_package(Qt5 COMPONENTS Core REQUIRED) endif() -if (Qt5Core_FOUND AND NOT BUILD_WITH_QT4) +if (Qt5Core_FOUND AND NOT BUILD_WITH_QT6) set(QTKEYCHAIN_VERSION_INFIX 5) if(ANDROID) @@ -80,46 +80,40 @@ if (Qt5Core_FOUND AND NOT BUILD_WITH_QT4) set(QTCORE_LIBRARIES ${Qt5Core_LIBRARIES}) include_directories(${Qt5Core_INCLUDE_DIRS}) +else() + find_package(Qt6 COMPONENTS Core REQUIRED) + set(QTKEYCHAIN_VERSION_INFIX 6) - if (NOT Qt5Core_VERSION VERSION_LESS "5.7.0") - if (CMAKE_COMPILER_IS_GNUCXX) - if ((NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.7.0") AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "6.1.0")) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") - elseif(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.7.0") - message(FATAL_ERROR "Can't build QtKeychain using g++-${CMAKE_CXX_COMPILER_VERSION} and Qt ${Qt5Core_VERSION}: compiler supporting C++11 is required") - endif() - elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") - if (NOT ${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 3.3) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") - else() - message(FATAL_ERROR "Can't build QtKeychain using clang++-${CMAKE_CXX_COMPILER_VERSION} and Qt ${Qt5Core_VERSION}: compiler supporting C++11 is required") - endif() - elseif ((CMAKE_CXX_COMPILER_ID MATCHES "MSVC") AND (MSVC_VERSION LESS 1700)) - message(FATAL_ERROR "Can't build QtKeychain using VC++-${MSVC_VERSION} and Qt ${Qt5Core_VERSION}: compiler supporting C++11 is required") - endif() + if(ANDROID) + find_package(Qt6 COMPONENTS AndroidExtras REQUIRED) + include_directories(${Qt6AndroidExtras_INCLUDE_DIRS}) + set(QTANDROIDEXTRAS_LIBRARIES ${Qt6AndroidExtras_LIBRARIES}) endif() -else() - set(QTKEYCHAIN_VERSION_INFIX "") - if(UNIX AND NOT APPLE) - find_package(Qt4 COMPONENTS QtCore QtDBus REQUIRED) - set(QTDBUS_LIBRARIES ${QT_QTDBUS_LIBRARY}) + + if(UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU) + find_package(Qt6 COMPONENTS DBus REQUIRED) + include_directories(${Qt6DBus_INCLUDE_DIRS}) + set(QTDBUS_LIBRARIES ${Qt6DBus_LIBRARIES}) macro(qt_add_dbus_interface) - qt4_add_dbus_interface(${ARGN}) + qt6_add_dbus_interface(${ARGN}) endmacro() - else() - find_package(Qt4 COMPONENTS QtCore REQUIRED) endif() - include_directories(${QT_INCLUDES}) - set(QTCORE_LIBRARIES ${QT_QTCORE_LIBRARY}) - macro(qt_add_translation) - qt4_add_translation(${ARGN}) - endmacro(qt_add_translation) - macro(qt_create_translation) - qt4_create_translation(${ARGN}) - endmacro(qt_create_translation) + + if(BUILD_TRANSLATIONS) + find_package(Qt6 COMPONENTS LinguistTools REQUIRED) + macro(qt_add_translation) + qt6_add_translation(${ARGN}) + endmacro(qt_add_translation) + macro(qt_create_translation) + qt6_create_translation(${ARGN}) + endmacro(qt_create_translation) + endif() + macro(qt_wrap_cpp) - qt4_wrap_cpp(${ARGN}) + qt6_wrap_cpp(${ARGN}) endmacro() + + set(QTCORE_LIBRARIES ${Qt6Core_LIBRARIES}) endif() From 15a5d0186e429009c6e9eb9db80aa68f01ad1b6d Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Wed, 16 Dec 2020 15:38:51 +0100 Subject: [PATCH 042/168] Require C++11 --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 657b24fc..04038776 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,9 @@ set(QTKEYCHAIN_SOVERSION 1) project(qtkeychain VERSION ${QTKEYCHAIN_VERSION}) +# Enable C++11 +SET(CMAKE_CXX_STANDARD 11) + include(FindPkgConfig) ### @@ -188,7 +191,6 @@ if(UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU) endif() if(ANDROID) - set(CMAKE_CXX_STANDARD 11) list(APPEND qtkeychain_SOURCES keychain_android.cpp androidkeystore.cpp plaintextstore.cpp) list(APPEND qtkeychain_LIBRARIES ${QTANDROIDEXTRAS_LIBRARIES} ) endif() From 87263319590217b59ec15e68f8efb5342144d8c3 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Wed, 16 Dec 2020 16:11:06 +0100 Subject: [PATCH 043/168] Sprinkle some mild C++11 on the interface --- keychain.h | 16 ++++++++-------- keychain_p.h | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/keychain.h b/keychain.h index 1d7a5fd6..45337c6c 100644 --- a/keychain.h +++ b/keychain.h @@ -47,7 +47,7 @@ class JobPrivate; class QKEYCHAIN_EXPORT Job : public QObject { Q_OBJECT public: - ~Job(); + ~Job() override; /** * @return The QSettings instance used as plaintext storage if insecureFallback() is true. @@ -150,7 +150,7 @@ class QKEYCHAIN_EXPORT Job : public QObject { void finished( QKeychain::Job* ); protected: - explicit Job( JobPrivate *q, QObject* parent=0 ); + explicit Job( JobPrivate *q, QObject* parent=nullptr ); Q_INVOKABLE void doStart(); private: @@ -185,8 +185,8 @@ class QKEYCHAIN_EXPORT ReadPasswordJob : public Job { * @param service The service string used by this job (can be empty). * @param parent The parent of this job. */ - explicit ReadPasswordJob( const QString& service, QObject* parent=0 ); - ~ReadPasswordJob(); + explicit ReadPasswordJob( const QString& service, QObject* parent=nullptr ); + ~ReadPasswordJob() override; /** * @return The binary data stored as value of this job's key(). @@ -222,8 +222,8 @@ class QKEYCHAIN_EXPORT WritePasswordJob : public Job { * @param service The service string used by this job (can be empty). * @param parent The parent of this job. */ - explicit WritePasswordJob( const QString& service, QObject* parent=0 ); - ~WritePasswordJob(); + explicit WritePasswordJob( const QString& service, QObject* parent=nullptr ); + ~WritePasswordJob() override; /** * Set the @p data that the job will store in the keychain as binary data. @@ -259,8 +259,8 @@ class QKEYCHAIN_EXPORT DeletePasswordJob : public Job { * @param service The service string used by this job (can be empty). * @param parent The parent of this job. */ - explicit DeletePasswordJob( const QString& service, QObject* parent=0 ); - ~DeletePasswordJob(); + explicit DeletePasswordJob( const QString& service, QObject* parent=nullptr ); + ~DeletePasswordJob() override; private: friend class QKeychain::DeletePasswordJobPrivate; diff --git a/keychain_p.h b/keychain_p.h index 58c0fd6b..44af46a0 100644 --- a/keychain_p.h +++ b/keychain_p.h @@ -91,7 +91,7 @@ class ReadPasswordJobPrivate : public JobPrivate { Q_OBJECT public: explicit ReadPasswordJobPrivate( const QString &service_, ReadPasswordJob* qq ); - void scheduledStart(); + void scheduledStart() override; #if defined(KEYCHAIN_DBUS) void fallbackOnError(const QDBusError& err); @@ -114,7 +114,7 @@ class WritePasswordJobPrivate : public JobPrivate { Q_OBJECT public: explicit WritePasswordJobPrivate( const QString &service_, WritePasswordJob* qq ); - void scheduledStart(); + void scheduledStart() override; #if defined(KEYCHAIN_DBUS) void fallbackOnError(const QDBusError& err); @@ -128,7 +128,7 @@ class DeletePasswordJobPrivate : public JobPrivate { public: explicit DeletePasswordJobPrivate( const QString &service_, DeletePasswordJob* qq ); - void scheduledStart(); + void scheduledStart() override; #if defined(KEYCHAIN_DBUS) void fallbackOnError(const QDBusError& err); From 74e075c1cb93728b55397d93165f329115572568 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Wed, 16 Dec 2020 16:40:49 +0100 Subject: [PATCH 044/168] Readme: Updates and fixes Document Qt 6/C++11, fix some markdown issues and typos. --- ReadMe.markdown | 1 - ReadMe.txt => ReadMe.md | 17 +++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) delete mode 120000 ReadMe.markdown rename ReadMe.txt => ReadMe.md (63%) diff --git a/ReadMe.markdown b/ReadMe.markdown deleted file mode 120000 index 3cefeef2..00000000 --- a/ReadMe.markdown +++ /dev/null @@ -1 +0,0 @@ -ReadMe.txt \ No newline at end of file diff --git a/ReadMe.txt b/ReadMe.md similarity index 63% rename from ReadMe.txt rename to ReadMe.md index f1a61fb3..cf0a2e13 100644 --- a/ReadMe.txt +++ b/ReadMe.md @@ -5,16 +5,25 @@ QtKeychain is a Qt API to store passwords and other secret data securely. How th * **macOS:** Passwords are stored in the macOS Keychain. - * **Linux/Unix:** If running, GNOME Keyring is used, otherwise qtkeychain tries to use KWallet (via D-Bus), if available. Libsecret (common API for desktop-specific solutions) + * **Linux/Unix:** If running, GNOME Keyring is used, otherwise QtKeychain tries to use KWallet (via D-Bus), if available. Libsecret (common API for desktop-specific solutions) is also supported. * **Windows:** By default, the Windows Credential Store is used (requires Windows 7 or newer). -Pass -DUSE_CREDENTIAL_STORE=OFF to cmake use disable it. If disabled, QtKeychain uses the Windows API function +Pass `-DUSE_CREDENTIAL_STORE=OFF` to cmake to disable it. If disabled, QtKeychain uses the Windows API function [CryptProtectData](http://msdn.microsoft.com/en-us/library/windows/desktop/aa380261%28v=vs.85%29.aspx "CryptProtectData function") to encrypt the password with the user's logon credentials. The encrypted data is then persisted via QSettings. * **Android and iOS:** Passwords are stored in the Android keystore system and iOS keychain, respectively. -In unsupported environments QtKeychain will report an error. It will not store any data unencrypted unless explicitly requested (setInsecureFallback( true )). +In unsupported environments QtKeychain will report an error. It will not store any data unencrypted unless explicitly requested (`setInsecureFallback( true )`). -**License:** QtKeychain is available under the [Modified BSD License](http://www.gnu.org/licenses/license-list.html#ModifiedBSD). See the file COPYING for details. + +Requirements +------------ + +QtKeychain 0.12 and newer supports Qt 5 and Qt 6 and requires a compiler with C++11 support. Older versions support Qt 4 and Qt 5. + +License +------- + +QtKeychain is available under the [Modified BSD License](http://www.gnu.org/licenses/license-list.html#ModifiedBSD). See the file COPYING for details. From 815fe610353ff8ad7e2f1121c368a74df8db5eb7 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Wed, 16 Dec 2020 15:29:19 +0100 Subject: [PATCH 045/168] Prepare 0.12 release --- CMakeLists.txt | 2 +- ChangeLog | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 04038776..2d40c049 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.3) -set(QTKEYCHAIN_VERSION 0.11.90) +set(QTKEYCHAIN_VERSION 0.12.0) set(QTKEYCHAIN_SOVERSION 1) project(qtkeychain VERSION ${QTKEYCHAIN_VERSION}) diff --git a/ChangeLog b/ChangeLog index f94fae8e..932fe2b3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,12 @@ ChangeLog ========= +version 0.12.0 (release 2020-12-16) + + * Add Qt 6 support, drop Qt 4 support + * Require C++11 + * Add Android support (Mathias Hasselmann) + version 0.11.1 (release 2020-09-08) * Build system fixes From cd028d3f92d64c125118e5a03a258394dc258d65 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Wed, 16 Dec 2020 16:45:31 +0100 Subject: [PATCH 046/168] Bump version --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d40c049..dd783ff6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.3) -set(QTKEYCHAIN_VERSION 0.12.0) +set(QTKEYCHAIN_VERSION 0.12.90) set(QTKEYCHAIN_SOVERSION 1) project(qtkeychain VERSION ${QTKEYCHAIN_VERSION}) From d85acea1831e38387eb5b5262856f64ba28e9b00 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Tue, 22 Dec 2020 16:16:34 +0100 Subject: [PATCH 047/168] Unify macOS and iOS implementations Use the more generic iOS implementation for macOS, too. It seems compatible with data stored with the previous macOS-specific implementation. Also, it does more extensive mapping of error codes. --- CMakeLists.txt | 7 +- keychain_ios.mm => keychain_apple.mm | 0 keychain_mac.cpp | 163 --------------------------- 3 files changed, 1 insertion(+), 169 deletions(-) rename keychain_ios.mm => keychain_apple.mm (100%) delete mode 100644 keychain_mac.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index dd783ff6..721a3fb0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -155,12 +155,7 @@ if(WIN32) endif() if(APPLE) - if(IOS) - list(APPEND qtkeychain_SOURCES keychain_ios.mm) - else() - list(APPEND qtkeychain_SOURCES keychain_mac.cpp) - endif() - + list(APPEND qtkeychain_SOURCES keychain_apple.mm) list(APPEND qtkeychain_LIBRARIES "-framework Foundation" "-framework Security") endif() diff --git a/keychain_ios.mm b/keychain_apple.mm similarity index 100% rename from keychain_ios.mm rename to keychain_apple.mm diff --git a/keychain_mac.cpp b/keychain_mac.cpp deleted file mode 100644 index dc8365b1..00000000 --- a/keychain_mac.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2011-2015 Frank Osterfeld * - * * - * 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. For licensing and distribution * - * details, check the accompanying file 'COPYING'. * - *****************************************************************************/ -#include "keychain_p.h" - -#include -#include -#include - -using namespace QKeychain; - -template -struct Releaser { - explicit Releaser( const T& v ) : value( v ) {} - ~Releaser() { - CFRelease( value ); - } - - const T value; -}; - -static QString strForStatus( OSStatus os ) { - const Releaser str( SecCopyErrorMessageString( os, 0 ) ); - const char * const buf = CFStringGetCStringPtr( str.value, kCFStringEncodingUTF8 ); - if ( !buf ) - return QObject::tr( "OS X Keychain error (OSStatus %1)" ).arg( os ); - return QObject::tr( "%1 (OSStatus %2)" ) - .arg( QString::fromUtf8( buf, strlen( buf ) ) ).arg( os ); -} - -static OSStatus readPw( QByteArray* pw, - const QString& service, - const QString& account, - SecKeychainItemRef* ref ) { - Q_ASSERT( pw ); - pw->clear(); - const QByteArray serviceData = service.toUtf8(); - const QByteArray accountData = account.toUtf8(); - - void* data = 0; - UInt32 len = 0; - - const OSStatus ret = SecKeychainFindGenericPassword( NULL, // default keychain - serviceData.size(), - serviceData.constData(), - accountData.size(), - accountData.constData(), - &len, - &data, - ref ); - if ( ret == noErr ) { - *pw = QByteArray( reinterpret_cast( data ), len ); - const OSStatus ret2 = SecKeychainItemFreeContent ( 0, data ); - if ( ret2 != noErr ) - qWarning() << "Could not free item content: " << strForStatus( ret2 ); - } - return ret; -} - -void ReadPasswordJobPrivate::scheduledStart() -{ - QString errorString; - Error error = NoError; - const OSStatus ret = readPw( &data, q->service(), q->key(), 0 ); - - switch ( ret ) { - case noErr: - break; - case errSecItemNotFound: - errorString = tr("Password not found"); - error = EntryNotFound; - break; - default: - errorString = strForStatus( ret ); - error = OtherError; - break; - } - q->emitFinishedWithError( error, errorString ); -} - - -static QKeychain::Error deleteEntryImpl( const QString& service, const QString& account, QString* err ) { - SecKeychainItemRef ref; - QByteArray pw; - const OSStatus ret1 = readPw( &pw, service, account, &ref ); - if ( ret1 == errSecItemNotFound ) - return NoError; // No item stored, we're done - if ( ret1 != noErr ) { - *err = strForStatus( ret1 ); - //TODO map error code, set errstr - return OtherError; - } - const Releaser releaser( ref ); - - const OSStatus ret2 = SecKeychainItemDelete( ref ); - - if ( ret2 == noErr ) - return NoError; - //TODO map error code - *err = strForStatus( ret2 ); - return CouldNotDeleteEntry; -} - -static QKeychain::Error writeEntryImpl( const QString& service, - const QString& account, - const QByteArray& data, - QString* err ) { - Q_ASSERT( err ); - err->clear(); - const QByteArray serviceData = service.toUtf8(); - const QByteArray accountData = account.toUtf8(); - const OSStatus ret = SecKeychainAddGenericPassword( NULL, //default keychain - serviceData.size(), - serviceData.constData(), - accountData.size(), - accountData.constData(), - data.size(), - data.constData(), - NULL //item reference - ); - if ( ret != noErr ) { - switch ( ret ) { - case errSecDuplicateItem: - { - Error derr = deleteEntryImpl( service, account, err ); - if ( derr != NoError ) - return CouldNotDeleteEntry; - else - return writeEntryImpl( service, account, data, err ); - } - default: - *err = strForStatus( ret ); - return OtherError; - } - } - - return NoError; -} - -void WritePasswordJobPrivate::scheduledStart() -{ - QString errorString; - Error error = NoError; - - error = writeEntryImpl( q->service(), key, data, &errorString ); - q->emitFinishedWithError( error, errorString ); -} - -void DeletePasswordJobPrivate::scheduledStart() -{ - QString errorString; - Error error = NoError; - - const Error derr = deleteEntryImpl( q->service(), key, &errorString ); - if ( derr != NoError ) - error = CouldNotDeleteEntry; - q->emitFinishedWithError( error, errorString ); -} From 6983b1bdeba8fbede4cd6ac3824b73c263eb4a68 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Mon, 18 Jan 2021 09:10:24 +0100 Subject: [PATCH 048/168] Fix license file Make the license file consistent with the license state in the Readme, i.e. add the third clause (no promotion) of the BSD-3-License. Fixes #147 --- COPYING | 3 +++ 1 file changed, 3 insertions(+) diff --git a/COPYING b/COPYING index cca2a5c9..69f70fff 100644 --- a/COPYING +++ b/COPYING @@ -7,6 +7,9 @@ are met: 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. The name of the author may not be used to + endorse or promote products derived from this software without + specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES From c44f3a7fb8bb09bd18a0ea94d3e5861403bf3c84 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Mon, 18 Jan 2021 10:54:36 +0100 Subject: [PATCH 049/168] Explicitly require Qt 5 in .pri file Fixes #178 --- qt5keychain.pri | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/qt5keychain.pri b/qt5keychain.pri index 2a367b9d..6671e381 100644 --- a/qt5keychain.pri +++ b/qt5keychain.pri @@ -2,6 +2,10 @@ # This file is provided as is without any warranty. # It can break at anytime or be removed without notice. +lessThan(QT_MAJOR_VERSION, 5) { + error("qtkeychain requires Qt 5 or later") +} + QT5KEYCHAIN_PWD = $$PWD CONFIG += depend_includepath From d10b2bc788c78f1a40540452ed0b308b0ecb5518 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Mon, 18 Jan 2021 10:58:05 +0100 Subject: [PATCH 050/168] qtkeychain.pri: Do not suggest this is Qt 5-only qtkeychain never supported only Qt 5, it was Qt4+5 before and now it's Qt 5+6. Thus rename qt5keychain.pri to qtkeychain.pri and rename an internal variable. This might break people's builds, but should be easy to fix (just change the .pri include), as this file is not relevant for deployment, only for the build. (See also #178) --- qt5keychain.pri | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/qt5keychain.pri b/qt5keychain.pri index 6671e381..bf56748e 100644 --- a/qt5keychain.pri +++ b/qt5keychain.pri @@ -6,21 +6,21 @@ lessThan(QT_MAJOR_VERSION, 5) { error("qtkeychain requires Qt 5 or later") } -QT5KEYCHAIN_PWD = $$PWD +QTKEYCHAIN_PWD = $$PWD CONFIG += depend_includepath DEFINES += QTKEYCHAIN_NO_EXPORT INCLUDEPATH += \ $$PWD/.. \ - $$QT5KEYCHAIN_PWD + $$QTKEYCHAIN_PWD HEADERS += \ - $$QT5KEYCHAIN_PWD/keychain_p.h \ - $$QT5KEYCHAIN_PWD/keychain.h + $$QTKEYCHAIN_PWD/keychain_p.h \ + $$QTKEYCHAIN_PWD/keychain.h SOURCES += \ - $$QT5KEYCHAIN_PWD/keychain.cpp + $$QTKEYCHAIN_PWD/keychain.cpp unix:!android:!macx:!ios { # Remove the following LIBSECRET_SUPPORT line @@ -46,26 +46,26 @@ unix:!android:!macx:!ios { DBUS_INTERFACES += kwallet_interface HEADERS += \ - $$QT5KEYCHAIN_PWD/gnomekeyring_p.h \ - $$QT5KEYCHAIN_PWD/plaintextstore_p.h \ - $$QT5KEYCHAIN_PWD/libsecret_p.h + $$QTKEYCHAIN_PWD/gnomekeyring_p.h \ + $$QTKEYCHAIN_PWD/plaintextstore_p.h \ + $$QTKEYCHAIN_PWD/libsecret_p.h SOURCES += \ - $$QT5KEYCHAIN_PWD/keychain_unix.cpp \ - $$QT5KEYCHAIN_PWD/plaintextstore.cpp \ - $$QT5KEYCHAIN_PWD/gnomekeyring.cpp \ - $$QT5KEYCHAIN_PWD/libsecret.cpp + $$QTKEYCHAIN_PWD/keychain_unix.cpp \ + $$QTKEYCHAIN_PWD/plaintextstore.cpp \ + $$QTKEYCHAIN_PWD/gnomekeyring.cpp \ + $$QTKEYCHAIN_PWD/libsecret.cpp } android { QT += androidextras HEADERS += \ - $$QT5KEYCHAIN_PWD/androidkeystore_p.h \ - $$QT5KEYCHAIN_PWD/plaintextstore_p.h + $$QTKEYCHAIN_PWD/androidkeystore_p.h \ + $$QTKEYCHAIN_PWD/plaintextstore_p.h SOURCES += \ - $$QT5KEYCHAIN_PWD/androidkeystore.cpp \ - $$QT5KEYCHAIN_PWD/keychain_android.cpp \ - $$QT5KEYCHAIN_PWD/plaintextstore.cpp + $$QTKEYCHAIN_PWD/androidkeystore.cpp \ + $$QTKEYCHAIN_PWD/keychain_android.cpp \ + $$QTKEYCHAIN_PWD/plaintextstore.cpp } win32 { @@ -79,21 +79,21 @@ win32 { } else { !build_pass:message("Windows Credential Store support: off") LIBS += -lcrypt32 - HEADERS += $$QT5KEYCHAIN_PWD/plaintextstore_p.h - SOURCES += $$QT5KEYCHAIN_PWD/plaintextstore.cpp + HEADERS += $$QTKEYCHAIN_PWD/plaintextstore_p.h + SOURCES += $$QTKEYCHAIN_PWD/plaintextstore.cpp } - HEADERS += $$QT5KEYCHAIN_PWD/libsecret_p.h + HEADERS += $$QTKEYCHAIN_PWD/libsecret_p.h SOURCES += \ - $$QT5KEYCHAIN_PWD/keychain_win.cpp \ - $$QT5KEYCHAIN_PWD/libsecret.cpp + $$QTKEYCHAIN_PWD/keychain_win.cpp \ + $$QTKEYCHAIN_PWD/libsecret.cpp } macx:!ios { LIBS += -framework Security -framework Foundation - SOURCES += $$QT5KEYCHAIN_PWD/keychain_mac.cpp + SOURCES += $$QTKEYCHAIN_PWD/keychain_mac.cpp } ios { LIBS += -framework Security -framework Foundation - OBJECTIVE_SOURCES += $$QT5KEYCHAIN_PWD/keychain_ios.mm + OBJECTIVE_SOURCES += $$QTKEYCHAIN_PWD/keychain_ios.mm } From 7ae399d681f54220cf000f6cd8bc0728e5b7b287 Mon Sep 17 00:00:00 2001 From: Hannah von Reth Date: Thu, 28 Jan 2021 15:53:46 +0100 Subject: [PATCH 051/168] Add LIBSECRET_LIBRARY_DIRS to LINK_DIRECTORIES --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 721a3fb0..04cea577 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -176,6 +176,7 @@ if(UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU) add_definitions(-DHAVE_LIBSECRET=1) endif() INCLUDE_DIRECTORIES(${LIBSECRET_INCLUDE_DIRS}) + LINK_DIRECTORIES(${LIBSECRET_LIBRARY_DIRS}) list(APPEND qtkeychain_LIBRARIES_PRIVATE ${LIBSECRET_LIBRARIES}) endif() From 89c5ed29c72a01a71854153902a69977134afbdf Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Tue, 2 Feb 2021 09:56:12 +0100 Subject: [PATCH 052/168] QMake: Add missing KEYCHAIN_DBUS define Fixes #182 --- qt5keychain.pri | 1 + 1 file changed, 1 insertion(+) diff --git a/qt5keychain.pri b/qt5keychain.pri index bf56748e..b52299a5 100644 --- a/qt5keychain.pri +++ b/qt5keychain.pri @@ -41,6 +41,7 @@ unix:!android:!macx:!ios { } # Generate D-Bus interface: + DEFINES += KEYCHAIN_DBUS QT += dbus kwallet_interface.files = $$PWD/org.kde.KWallet.xml DBUS_INTERFACES += kwallet_interface From ae1f9a479001d519d209d19da1b9319bf4d05a28 Mon Sep 17 00:00:00 2001 From: Nicolas Fella Date: Fri, 5 Feb 2021 15:19:16 +0100 Subject: [PATCH 053/168] Fix QtKeyChainConfig when building against Qt6 6 isn't 5 and thus the resulting QtKeyChainConfig would look for Qt4. Since Qt4 was dropped we can simplify the code. --- QtKeychainConfig.cmake.in | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/QtKeychainConfig.cmake.in b/QtKeychainConfig.cmake.in index 26a8ca69..0c7779d3 100644 --- a/QtKeychainConfig.cmake.in +++ b/QtKeychainConfig.cmake.in @@ -9,14 +9,10 @@ include("${CMAKE_CURRENT_LIST_DIR}/Qt@QTKEYCHAIN_VERSION_INFIX@KeychainLibraryDe include(CMakeFindDependencyMacro) -if("@QTKEYCHAIN_VERSION_INFIX@" STREQUAL "5") - find_dependency(Qt5Core) - - if(UNIX AND NOT APPLE) - find_dependency(Qt5DBus) - endif() -else() - find_dependency(Qt4 COMPONENTS QtCore) +find_dependency(Qt@QTKEYCHAIN_VERSION_INFIX@Core) + +if(UNIX AND NOT APPLE) + find_dependency(Qt@QTKEYCHAIN_VERSION_INFIX@DBus) endif() set(QTKEYCHAIN_LIBRARIES "@QTKEYCHAIN_TARGET_NAME@") From 13f3b4067473a20137a556bcfc4947c1eb78315f Mon Sep 17 00:00:00 2001 From: Kevin Funk Date: Tue, 2 Feb 2021 16:22:43 +0100 Subject: [PATCH 054/168] cmake: Add project(... LANGUAGES CXX) So we dont check the C compiler unnecessarily --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 04cea577..86cb544e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.3) set(QTKEYCHAIN_VERSION 0.12.90) set(QTKEYCHAIN_SOVERSION 1) -project(qtkeychain VERSION ${QTKEYCHAIN_VERSION}) +project(qtkeychain VERSION ${QTKEYCHAIN_VERSION} LANGUAGES CXX) # Enable C++11 SET(CMAKE_CXX_STANDARD 11) From 9a22739ea5d36bb3de46dbb93b22da2b2c119461 Mon Sep 17 00:00:00 2001 From: Kevin Funk Date: Tue, 2 Feb 2021 16:26:23 +0100 Subject: [PATCH 055/168] Minor: Add missing override keywords Eliminates warnings under Clang --- keychain_p.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/keychain_p.h b/keychain_p.h index 44af46a0..d4794a58 100644 --- a/keychain_p.h +++ b/keychain_p.h @@ -94,12 +94,12 @@ class ReadPasswordJobPrivate : public JobPrivate { void scheduledStart() override; #if defined(KEYCHAIN_DBUS) - void fallbackOnError(const QDBusError& err); + void fallbackOnError(const QDBusError& err) override; private Q_SLOTS: - void kwalletOpenFinished( QDBusPendingCallWatcher* watcher ); + void kwalletOpenFinished( QDBusPendingCallWatcher* watcher ) override; void kwalletEntryTypeFinished( QDBusPendingCallWatcher* watcher ); - void kwalletFinished( QDBusPendingCallWatcher* watcher ); + void kwalletFinished( QDBusPendingCallWatcher* watcher ) override; #else //moc's too dumb to respect above macros, so just define empty slot implementations private Q_SLOTS: void kwalletOpenFinished( QDBusPendingCallWatcher* ) {} @@ -117,7 +117,7 @@ class WritePasswordJobPrivate : public JobPrivate { void scheduledStart() override; #if defined(KEYCHAIN_DBUS) - void fallbackOnError(const QDBusError& err); + void fallbackOnError(const QDBusError& err) override; #endif friend class WritePasswordJob; @@ -131,7 +131,7 @@ class DeletePasswordJobPrivate : public JobPrivate { void scheduledStart() override; #if defined(KEYCHAIN_DBUS) - void fallbackOnError(const QDBusError& err); + void fallbackOnError(const QDBusError& err) override; #endif protected: From a1aef5b415e75801973053426cf8babfb10c22b3 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Mon, 22 Feb 2021 22:22:13 -0900 Subject: [PATCH 056/168] Linux: Require libsecret if not explicitly disabled All relevant distributions should have libsecret by now, fail if it is not found, unless -DLIBSECRET_SUPPORT=OFF was explicitly set. --- CMakeLists.txt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 86cb544e..22c6c8ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -170,11 +170,8 @@ if(UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU) option(LIBSECRET_SUPPORT "Build with libsecret support" ON) if(LIBSECRET_SUPPORT) - pkg_check_modules(LIBSECRET libsecret-1) - - if (LIBSECRET_FOUND) - add_definitions(-DHAVE_LIBSECRET=1) - endif() + pkg_check_modules(LIBSECRET REQUIRED libsecret-1) + add_definitions(-DHAVE_LIBSECRET=1) INCLUDE_DIRECTORIES(${LIBSECRET_INCLUDE_DIRS}) LINK_DIRECTORIES(${LIBSECRET_LIBRARY_DIRS}) list(APPEND qtkeychain_LIBRARIES_PRIVATE ${LIBSECRET_LIBRARIES}) From 36108418e381cf75fa0204afd479bd5d43fd597f Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Mon, 22 Feb 2021 22:49:31 -0900 Subject: [PATCH 057/168] Don't build test application by default This is only relevant for developers, which can enable it explicitly. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 22c6c8ab..56031966 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,7 @@ include(ECMSetupVersion) include(ECMGeneratePriFile) option(BUILD_WITH_QT6 "Build qtkeychain with Qt 6" OFF) -option(BUILD_TEST_APPLICATION "Build test application" ON) +option(BUILD_TEST_APPLICATION "Build test application" OFF) option(BUILD_TRANSLATIONS "Build translations" ON) option(QTKEYCHAIN_STATIC "Build static library" OFF) From baa895f88d4aeb7b1eaea53ff455bfe9ab3883c4 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Thu, 25 Feb 2021 01:53:16 -0900 Subject: [PATCH 058/168] libsecret: remove debug leftovers --- libsecret.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libsecret.cpp b/libsecret.cpp index 4fcf19f9..1d580398 100644 --- a/libsecret.cpp +++ b/libsecret.cpp @@ -5,7 +5,6 @@ #include "libsecret_p.h" #include -#include #if defined(HAVE_LIBSECRET) const SecretSchema* qtkeychainSchema(void) { @@ -236,7 +235,6 @@ bool LibSecretKeyring::findPassword(const QString &user, const QString &server, arg->user = user; arg->server = server; - qDebug() << Q_FUNC_INFO; secret_password_lookup_fn (qtkeychainSchema(), NULL, on_password_lookup, arg, "user", user.toUtf8().constData(), "server", server.toUtf8().constData(), @@ -274,7 +272,6 @@ bool LibSecretKeyring::writePassword(const QString &display_name, break; } - qDebug() << Q_FUNC_INFO; secret_password_store_fn (qtkeychainSchema(), SECRET_COLLECTION_DEFAULT, display_name.toUtf8().constData(), pwd.constData(), NULL, on_password_stored, self, @@ -302,7 +299,6 @@ bool LibSecretKeyring::deletePassword(const QString &key, const QString &service return false; } - qDebug() << Q_FUNC_INFO; secret_password_clear_fn (qtkeychainSchema(), NULL, on_password_cleared, self, "user", key.toUtf8().constData(), "server", service.toUtf8().constData(), From 92b49f7a01fb9803155264bc580df36d73bcb1c1 Mon Sep 17 00:00:00 2001 From: Nicolas Fella Date: Wed, 24 Mar 2021 15:08:46 +0100 Subject: [PATCH 059/168] Make QtAndroidExtras dependency private The AndroidExtras usage does not leak into the public API so make the linking private. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 56031966..04785bfb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -185,7 +185,7 @@ endif() if(ANDROID) list(APPEND qtkeychain_SOURCES keychain_android.cpp androidkeystore.cpp plaintextstore.cpp) - list(APPEND qtkeychain_LIBRARIES ${QTANDROIDEXTRAS_LIBRARIES} ) + list(APPEND qtkeychain_LIBRARIES_PRIVATE ${QTANDROIDEXTRAS_LIBRARIES} ) endif() QT_WRAP_CPP(qtkeychain_MOC_OUTFILES keychain.h keychain_p.h gnomekeyring_p.h) From 15c86d845962779e8a945546be579ef4d9d354b4 Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Fri, 26 Feb 2021 12:53:50 +0100 Subject: [PATCH 060/168] CMakeLists: fix macOS Universal builds --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 04785bfb..fb3100a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -203,7 +203,7 @@ if ( BUILD_TRANSLATIONS ) qt_create_translation(qtkeychain_MESSAGES ${qtkeychain_TR_SOURCES} ${qtkeychain_TR_FILES}) qt_add_translation(qtkeychain_QM_FILES ${qtkeychain_TR_FILES}) add_custom_target(messages DEPENDS ${qtkeychain_MESSAGES}) - add_custom_target(translations DEPENDS ${qtkeychain_QM_FILES}) + add_custom_target(translations DEPENDS ${qtkeychain_QM_FILES} messages) if(QTKEYCHAIN_VERSION_INFIX EQUAL 5 AND QT_TRANSLATIONS_DIR AND NOT QTKEYCHAIN_TRANSLATIONS_DIR) # Back compatibility with pre-0.11 versions @@ -226,6 +226,8 @@ if(NOT QTKEYCHAIN_STATIC) else() add_library(${QTKEYCHAIN_TARGET_NAME} STATIC ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES} ${qtkeychain_QM_FILES}) endif() +# https://github.com/frankosterfeld/qtkeychain/issues/185 +add_dependencies(${QTKEYCHAIN_TARGET_NAME} translations) if(WIN32) set_target_properties( ${QTKEYCHAIN_TARGET_NAME} PROPERTIES DEBUG_POSTFIX "d" ) endif() From c729d0bc3a7d0932cbcacc773a98ea8660064fc0 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Wed, 31 Mar 2021 08:36:52 +0100 Subject: [PATCH 061/168] Fix QMake build for macOS and iOS Fixes #188 --- qt5keychain.pri | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/qt5keychain.pri b/qt5keychain.pri index b52299a5..f406e154 100644 --- a/qt5keychain.pri +++ b/qt5keychain.pri @@ -89,12 +89,7 @@ win32 { $$QTKEYCHAIN_PWD/libsecret.cpp } -macx:!ios { +macx|ios { LIBS += -framework Security -framework Foundation - SOURCES += $$QTKEYCHAIN_PWD/keychain_mac.cpp -} - -ios { - LIBS += -framework Security -framework Foundation - OBJECTIVE_SOURCES += $$QTKEYCHAIN_PWD/keychain_ios.mm + OBJECTIVE_SOURCES += $$QTKEYCHAIN_PWD/keychain_apple.mm } From b3a2ec7d5a0fd6b913732f4eacefe3dd4c983a4c Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Fri, 2 Apr 2021 14:20:56 +0200 Subject: [PATCH 062/168] Fix CMake failure with BUILD_TRANSLATIONS=NO --- CMakeLists.txt | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fb3100a4..31ad8d16 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -198,12 +198,24 @@ set(qtkeychain_TR_FILES translations/qtkeychain_zh.ts ) +set(QTKEYCHAIN_TARGET_NAME qt${QTKEYCHAIN_VERSION_INFIX}keychain) +if(NOT QTKEYCHAIN_STATIC) + add_library(${QTKEYCHAIN_TARGET_NAME} SHARED ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES} ${qtkeychain_QM_FILES}) +else() + add_library(${QTKEYCHAIN_TARGET_NAME} STATIC ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES} ${qtkeychain_QM_FILES}) +endif() +if(WIN32) + set_target_properties( ${QTKEYCHAIN_TARGET_NAME} PROPERTIES DEBUG_POSTFIX "d" ) +endif() + file(GLOB qtkeychain_TR_SOURCES *.cpp *.h *.ui) if ( BUILD_TRANSLATIONS ) qt_create_translation(qtkeychain_MESSAGES ${qtkeychain_TR_SOURCES} ${qtkeychain_TR_FILES}) qt_add_translation(qtkeychain_QM_FILES ${qtkeychain_TR_FILES}) add_custom_target(messages DEPENDS ${qtkeychain_MESSAGES}) add_custom_target(translations DEPENDS ${qtkeychain_QM_FILES} messages) + # https://github.com/frankosterfeld/qtkeychain/issues/185 + add_dependencies(${QTKEYCHAIN_TARGET_NAME} translations) if(QTKEYCHAIN_VERSION_INFIX EQUAL 5 AND QT_TRANSLATIONS_DIR AND NOT QTKEYCHAIN_TRANSLATIONS_DIR) # Back compatibility with pre-0.11 versions @@ -216,22 +228,9 @@ if ( BUILD_TRANSLATIONS ) CACHE PATH "The location of the QtKeychain translations" ) endif() - install(FILES ${qtkeychain_QM_FILES} - DESTINATION ${QTKEYCHAIN_TRANSLATIONS_DIR}) + install(FILES ${qtkeychain_QM_FILES} DESTINATION ${QTKEYCHAIN_TRANSLATIONS_DIR}) endif( BUILD_TRANSLATIONS ) -set(QTKEYCHAIN_TARGET_NAME qt${QTKEYCHAIN_VERSION_INFIX}keychain) -if(NOT QTKEYCHAIN_STATIC) - add_library(${QTKEYCHAIN_TARGET_NAME} SHARED ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES} ${qtkeychain_QM_FILES}) -else() - add_library(${QTKEYCHAIN_TARGET_NAME} STATIC ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES} ${qtkeychain_QM_FILES}) -endif() -# https://github.com/frankosterfeld/qtkeychain/issues/185 -add_dependencies(${QTKEYCHAIN_TARGET_NAME} translations) -if(WIN32) - set_target_properties( ${QTKEYCHAIN_TARGET_NAME} PROPERTIES DEBUG_POSTFIX "d" ) -endif() - target_link_libraries(${QTKEYCHAIN_TARGET_NAME} PUBLIC ${qtkeychain_LIBRARIES} PRIVATE ${qtkeychain_LIBRARIES_PRIVATE}) if(NOT INTERFACE_INCLUDE_SUFFIX) set(INTERFACE_INCLUDE_SUFFIX include) From 70a567abc59d11d2b1c87fcb8b7763365b3a5365 Mon Sep 17 00:00:00 2001 From: Nicolas Fella Date: Wed, 14 Apr 2021 19:50:46 +0200 Subject: [PATCH 063/168] Add conventional name for exported target QtKeychain defines an exported CMake target (qt5keychain), but the name does not follow the CMake convention (Foo::Bar). This is confusing at best, especially given the name is the same as the plain library. It is not clear to the reader whether `target_link_libraries(foo PRIVATE qt5keychain)` links to an imported target or a plain library. Add an alias Qt5KeyChain::Qt5KeyChain with a more conventional name. This preserves compatibility for users that rely on the qt5keychain target. --- QtKeychainConfig.cmake.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/QtKeychainConfig.cmake.in b/QtKeychainConfig.cmake.in index 0c7779d3..73afe7f3 100644 --- a/QtKeychainConfig.cmake.in +++ b/QtKeychainConfig.cmake.in @@ -2,6 +2,9 @@ # It defines the following variables # QTKEYCHAIN_INCLUDE_DIRS - include directories for QtKeychain # QTKEYCHAIN_LIBRARIES - libraries to link against +# as well as the following imported targets +# qt5keychain / qt6keychain +# Qt5Keychain::Qt5Keychain / Qt6Keychain::Qt6Keychain @PACKAGE_INIT@ @@ -17,3 +20,5 @@ endif() set(QTKEYCHAIN_LIBRARIES "@QTKEYCHAIN_TARGET_NAME@") get_target_property(QTKEYCHAIN_INCLUDE_DIRS "@QTKEYCHAIN_TARGET_NAME@" INTERFACE_INCLUDE_DIRECTORIES) + +add_library(Qt@QTKEYCHAIN_VERSION_INFIX@Keychain::Qt@QTKEYCHAIN_VERSION_INFIX@Keychain ALIAS qt@QTKEYCHAIN_VERSION_INFIX@keychain) From 28b2e3d5d4460dc8e04f6cf6a5f4db2078f117ed Mon Sep 17 00:00:00 2001 From: Nicolas Fella Date: Wed, 26 May 2021 02:36:11 +0200 Subject: [PATCH 064/168] Don't find QtDBus on Android Android is Unix, but we don't want DBus there --- QtKeychainConfig.cmake.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QtKeychainConfig.cmake.in b/QtKeychainConfig.cmake.in index 73afe7f3..d849dedd 100644 --- a/QtKeychainConfig.cmake.in +++ b/QtKeychainConfig.cmake.in @@ -14,7 +14,7 @@ include(CMakeFindDependencyMacro) find_dependency(Qt@QTKEYCHAIN_VERSION_INFIX@Core) -if(UNIX AND NOT APPLE) +if(UNIX AND NOT APPLE AND NOT ANDROID) find_dependency(Qt@QTKEYCHAIN_VERSION_INFIX@DBus) endif() From f6705f3320eccab324ddf6a00ec71b17d1d5fcfb Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Thu, 24 Jun 2021 16:24:50 +0200 Subject: [PATCH 065/168] Replace NULL and Q_NULLPTR by nullptr --- androidkeystore.cpp | 2 +- androidkeystore_p.h | 4 +-- gnomekeyring.cpp | 2 +- keychain_haiku.cpp | 4 +-- keychain_win.cpp | 14 +++++----- libsecret.cpp | 63 ++++++++++++++++++++++----------------------- 6 files changed, 44 insertions(+), 45 deletions(-) diff --git a/androidkeystore.cpp b/androidkeystore.cpp index 97260bb9..7ef49e76 100644 --- a/androidkeystore.cpp +++ b/androidkeystore.cpp @@ -55,7 +55,7 @@ using JNIObject = QAndroidJniObject; QByteArray fromArray(const jbyteArray array) { QAndroidJniEnvironment env; - jbyte *const bytes = env->GetByteArrayElements(array, Q_NULLPTR); + jbyte *const bytes = env->GetByteArrayElements(array, nullptr); const QByteArray result(reinterpret_cast(bytes), env->GetArrayLength(array)); env->ReleaseByteArrayElements(array, bytes, JNI_ABORT); return result; diff --git a/androidkeystore_p.h b/androidkeystore_p.h index 36d00477..5c490791 100644 --- a/androidkeystore_p.h +++ b/androidkeystore_p.h @@ -236,8 +236,8 @@ class KeyStore : public java::lang::Object bool containsAlias(const QString &alias) const; bool deleteEntry(const QString &alias) const; static KeyStore getInstance(const QString &type); - Entry getEntry(const QString &alias, const ProtectionParameter ¶m = Q_NULLPTR) const; - bool load(const LoadStoreParameter ¶m = Q_NULLPTR) const; + Entry getEntry(const QString &alias, const ProtectionParameter ¶m = nullptr) const; + bool load(const LoadStoreParameter ¶m = nullptr) const; }; namespace interfaces { diff --git a/gnomekeyring.cpp b/gnomekeyring.cpp index dd35670c..6347052d 100644 --- a/gnomekeyring.cpp +++ b/gnomekeyring.cpp @@ -1,6 +1,6 @@ #include "gnomekeyring_p.h" -const char* GnomeKeyring::GNOME_KEYRING_DEFAULT = NULL; +const char* GnomeKeyring::GNOME_KEYRING_DEFAULT = nullptr; bool GnomeKeyring::isAvailable() { diff --git a/keychain_haiku.cpp b/keychain_haiku.cpp index 58b1ab79..82872466 100644 --- a/keychain_haiku.cpp +++ b/keychain_haiku.cpp @@ -29,9 +29,9 @@ class AutoApp { AutoApp::AutoApp() - : app(NULL) + : app(nullptr) { - if (be_app != NULL) + if (be_app) return; // no BApplication object, probably using QCoreApplication diff --git a/keychain_win.cpp b/keychain_win.cpp index 27aaa96d..e888967f 100644 --- a/keychain_win.cpp +++ b/keychain_win.cpp @@ -129,10 +129,10 @@ void ReadPasswordJobPrivate::scheduledStart() { blob_in.cbData = encrypted.size(); const BOOL ret = CryptUnprotectData( &blob_in, - NULL, - NULL, - NULL, - NULL, + nullptr, + nullptr, + nullptr, + nullptr, 0, &blob_out ); if ( !ret ) { @@ -153,9 +153,9 @@ void WritePasswordJobPrivate::scheduledStart() { blob_in.cbData = data.size(); const BOOL res = CryptProtectData( &blob_in, L"QKeychain-encrypted data", - NULL, - NULL, - NULL, + nullptr, + nullptr, + nullptr, 0, &blob_out ); if ( !res ) { diff --git a/libsecret.cpp b/libsecret.cpp index 1d580398..9e22a2dd 100644 --- a/libsecret.cpp +++ b/libsecret.cpp @@ -53,14 +53,14 @@ typedef gboolean (*secret_password_clear_finish_t) (GAsyncResult *result, typedef void (*secret_password_free_t) (gchar *password); typedef GQuark (*secret_error_get_quark_t) (void) G_GNUC_CONST; -static secret_password_lookup_t secret_password_lookup_fn = NULL; -static secret_password_lookup_finish_t secret_password_lookup_finish_fn = NULL; -static secret_password_store_t secret_password_store_fn = NULL; -static secret_password_store_finish_t secret_password_store_finish_fn = NULL; -static secret_password_clear_t secret_password_clear_fn = NULL; -static secret_password_clear_finish_t secret_password_clear_finish_fn = NULL; -static secret_password_free_t secret_password_free_fn = NULL; -static secret_error_get_quark_t secret_error_get_quark_fn = NULL; +static secret_password_lookup_t secret_password_lookup_fn = nullptr; +static secret_password_lookup_finish_t secret_password_lookup_finish_fn = nullptr; +static secret_password_store_t secret_password_store_fn = nullptr; +static secret_password_store_finish_t secret_password_store_finish_fn = nullptr; +static secret_password_clear_t secret_password_clear_fn = nullptr; +static secret_password_clear_finish_t secret_password_clear_finish_fn = nullptr; +static secret_password_free_t secret_password_free_fn = nullptr; +static secret_error_get_quark_t secret_error_get_quark_fn = nullptr; static QKeychain::Error gerrorToCode(const GError *error) { if (error->domain != secret_error_get_quark_fn()) { @@ -82,7 +82,7 @@ on_password_lookup (GObject *source, GAsyncResult *result, gpointer inst) { - GError *error = NULL; + GError *error = nullptr; callbackArg *arg = (callbackArg*)inst; gchar *password = secret_password_lookup_finish_fn (result, &error); @@ -94,7 +94,7 @@ on_password_lookup (GObject *source, arg->self->q->emitFinishedWithError( code, QString::fromUtf8(error->message) ); } else { - if (password != NULL) { + if (password) { QByteArray raw = QByteArray(password); switch(arg->self->mode) { case QKeychain::JobPrivate::Binary: @@ -108,12 +108,12 @@ on_password_lookup (GObject *source, arg->self->q->emitFinished(); } else if (arg->self->mode == QKeychain::JobPrivate::Text) { arg->self->mode = QKeychain::JobPrivate::Binary; - secret_password_lookup_fn (qtkeychainSchema(), NULL, + secret_password_lookup_fn (qtkeychainSchema(), nullptr, on_password_lookup, arg, "user", arg->user.toUtf8().constData(), "server", arg->server.toUtf8().constData(), "type", "base64", - NULL); + nullptr); return; } else { arg->self->q->emitFinishedWithError( QKeychain::EntryNotFound, QObject::tr("Entry not found") ); @@ -138,7 +138,7 @@ on_password_stored (GObject *source, GAsyncResult *result, gpointer inst) { - GError *error = NULL; + GError *error = nullptr; QKeychain::JobPrivate *self = (QKeychain::JobPrivate*)inst; Q_UNUSED(source); @@ -146,14 +146,14 @@ on_password_stored (GObject *source, secret_password_store_finish_fn (result, &error); if (self) { - if (error != NULL) { + if (error) { self->q->emitFinishedWithError( gerrorToCode(error), QString::fromUtf8(error->message) ); } else { self->q->emitFinished(); } } - if (error != NULL) { + if (error) { g_error_free (error); } } @@ -163,7 +163,7 @@ on_password_cleared (GObject *source, GAsyncResult *result, gpointer inst) { - GError *error = NULL; + GError *error = nullptr; QKeychain::JobPrivate *self = (QKeychain::JobPrivate*)inst; gboolean removed = secret_password_clear_finish_fn (result, &error); @@ -177,7 +177,7 @@ on_password_cleared (GObject *source, self->q->emitFinished(); } } - if (error != NULL) { + if (error) { g_error_free (error); } } @@ -197,21 +197,21 @@ bool LibSecretKeyring::isAvailable() { const LibSecretKeyring& keyring = instance(); if (!keyring.isLoaded()) return false; - if (secret_password_lookup_fn == NULL) + if (secret_password_lookup_fn == nullptr) return false; - if (secret_password_lookup_finish_fn == NULL) + if (secret_password_lookup_finish_fn == nullptr) return false; - if (secret_password_store_fn == NULL) + if (secret_password_store_fn == nullptr) return false; - if (secret_password_store_finish_fn == NULL) + if (secret_password_store_finish_fn == nullptr) return false; - if (secret_password_clear_fn == NULL) + if (secret_password_clear_fn == nullptr) return false; - if (secret_password_clear_finish_fn == NULL) + if (secret_password_clear_finish_fn == nullptr) return false; - if (secret_password_free_fn == NULL) + if (secret_password_free_fn == nullptr) return false; - if (secret_error_get_quark_fn == NULL) + if (secret_error_get_quark_fn == nullptr) return false; return true; #else @@ -235,11 +235,11 @@ bool LibSecretKeyring::findPassword(const QString &user, const QString &server, arg->user = user; arg->server = server; - secret_password_lookup_fn (qtkeychainSchema(), NULL, on_password_lookup, arg, + secret_password_lookup_fn (qtkeychainSchema(), nullptr, on_password_lookup, arg, "user", user.toUtf8().constData(), "server", server.toUtf8().constData(), "type", "plaintext", - NULL); + nullptr); return true; #else Q_UNUSED(user) @@ -274,11 +274,11 @@ bool LibSecretKeyring::writePassword(const QString &display_name, secret_password_store_fn (qtkeychainSchema(), SECRET_COLLECTION_DEFAULT, display_name.toUtf8().constData(), - pwd.constData(), NULL, on_password_stored, self, + pwd.constData(), nullptr, on_password_stored, self, "user", user.toUtf8().constData(), "server", server.toUtf8().constData(), "type", type.toUtf8().constData(), - NULL); + nullptr); return true; #else Q_UNUSED(display_name) @@ -299,11 +299,10 @@ bool LibSecretKeyring::deletePassword(const QString &key, const QString &service return false; } - secret_password_clear_fn (qtkeychainSchema(), NULL, on_password_cleared, self, + secret_password_clear_fn (qtkeychainSchema(), nullptr, on_password_cleared, self, "user", key.toUtf8().constData(), "server", service.toUtf8().constData(), - NULL); - return true; + nullptr); #else Q_UNUSED(key) Q_UNUSED(service) From 196482d7f040e6add228674fec1cb3a784e343e8 Mon Sep 17 00:00:00 2001 From: David Faure Date: Sat, 2 Oct 2021 13:50:48 +0200 Subject: [PATCH 066/168] Add missing "return true" like in similar methods, caught by -Werror=return-type --- CMakeLists.txt | 2 +- libsecret.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 31ad8d16..9647ac8c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -139,7 +139,7 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") endif() else() # MSVC's STL / Qt headers are not MSVC -Wall clean, so don't enable it there - add_definitions( -Wall ) + add_definitions( -Wall -Werror=return-type ) endif() if(WIN32) diff --git a/libsecret.cpp b/libsecret.cpp index 9e22a2dd..2289a127 100644 --- a/libsecret.cpp +++ b/libsecret.cpp @@ -303,6 +303,7 @@ bool LibSecretKeyring::deletePassword(const QString &key, const QString &service "user", key.toUtf8().constData(), "server", service.toUtf8().constData(), nullptr); + return true; #else Q_UNUSED(key) Q_UNUSED(service) From d6fcc5cc06f2d8761c1c8b197ada0a13a3515348 Mon Sep 17 00:00:00 2001 From: Christophe Giboudeaux Date: Mon, 4 Oct 2021 22:10:24 +0200 Subject: [PATCH 067/168] Raise the minimum CMake version to match the Qt 6 one Qt 6.3 wants the dependent projects to use the same CMake policies as the ones used to build Qt. To do that, an error is thrown if the minimum CMake version doesn't match the qtbase one on platforms using shared libraries. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9647ac8c..eddcc05b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.16) set(QTKEYCHAIN_VERSION 0.12.90) set(QTKEYCHAIN_SOVERSION 1) From a9143fc5e19208e7740fe4672ac2452094086c00 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Tue, 5 Oct 2021 10:46:12 +0200 Subject: [PATCH 068/168] .gitignore: Ignore build folders Adapting a change from Be-ing (CR #198). --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 8fcb5ea4..3d2a92f8 100644 --- a/.gitignore +++ b/.gitignore @@ -47,4 +47,4 @@ qtkeychain.build *.sw? *~ - +/build-*/ From 450e7fd018e96e974cc436454f33cd0d1c6a0ad9 Mon Sep 17 00:00:00 2001 From: Nadim Asaduzzaman Date: Sun, 9 May 2021 23:54:09 -0600 Subject: [PATCH 069/168] Rename project to remove Qt 5 only suggestion --- qt5keychain.pri => qtkeychain.pri | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename qt5keychain.pri => qtkeychain.pri (100%) diff --git a/qt5keychain.pri b/qtkeychain.pri similarity index 100% rename from qt5keychain.pri rename to qtkeychain.pri From 26006c746030961f177d0bf690f44abd671a5973 Mon Sep 17 00:00:00 2001 From: Armin Novak Date: Wed, 26 May 2021 15:44:24 +0200 Subject: [PATCH 070/168] Fixed policy CMP0042: @rpath setting on mac os --- CMakeLists.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index eddcc05b..2fca5fd2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -245,11 +245,15 @@ generate_export_header(${QTKEYCHAIN_TARGET_NAME} set_target_properties(${QTKEYCHAIN_TARGET_NAME} PROPERTIES VERSION ${QTKEYCHAIN_VERSION} SOVERSION ${QTKEYCHAIN_SOVERSION} - MACOSX_RPATH 1 - INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}" INSTALL_RPATH_USE_LINK_PATH TRUE ) +if (NOT APPLE) + set_target_properties(${QTKEYCHAIN_TARGET_NAME} PROPERTIES + INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}" + ) +endif() + install(FILES keychain.h ${CMAKE_CURRENT_BINARY_DIR}/qkeychain_export.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/qt${QTKEYCHAIN_VERSION_INFIX}keychain/ ) From 7359df94aade097d49733ab9ec727277b8883957 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20H=C3=B6her?= Date: Tue, 6 Apr 2021 13:17:23 +0200 Subject: [PATCH 071/168] Fix: Use CSRelease on Apple Platforms Switch to using CFRelease instead of calling release method on CFTypeRef. The release method led to compilation errors when building for iOS using CMake. Strangely, it works when using qmake or building for macOS. Could be related to compiler flags. --- keychain_apple.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keychain_apple.mm b/keychain_apple.mm index 4662e40e..d2c763af 100644 --- a/keychain_apple.mm +++ b/keychain_apple.mm @@ -89,7 +89,7 @@ static ErrorDescription fromStatus(OSStatus status) } if (dataRef) - [dataRef release]; + CFRelease(dataRef); } void WritePasswordJobPrivate::scheduledStart() From 75aaf2c41981de1b85a7c9d36a61bd7d0cf61802 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Tue, 5 Oct 2021 11:03:11 +0200 Subject: [PATCH 072/168] Fix warnings about missing override --- keychain_p.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/keychain_p.h b/keychain_p.h index d4794a58..a66cb423 100644 --- a/keychain_p.h +++ b/keychain_p.h @@ -102,9 +102,9 @@ private Q_SLOTS: void kwalletFinished( QDBusPendingCallWatcher* watcher ) override; #else //moc's too dumb to respect above macros, so just define empty slot implementations private Q_SLOTS: - void kwalletOpenFinished( QDBusPendingCallWatcher* ) {} + void kwalletOpenFinished( QDBusPendingCallWatcher* ) override {} void kwalletEntryTypeFinished( QDBusPendingCallWatcher* ) {} - void kwalletFinished( QDBusPendingCallWatcher* ) {} + void kwalletFinished( QDBusPendingCallWatcher* ) override {} #endif friend class ReadPasswordJob; From a9ee15f1457fa4bfc8673edeaf6ec20b8f110dd6 Mon Sep 17 00:00:00 2001 From: Be Date: Sat, 14 Aug 2021 22:17:51 -0500 Subject: [PATCH 073/168] use GNUInstallDirs module included in CMake --- cmake/Modules/GNUInstallDirs.cmake | 188 ----------------------------- 1 file changed, 188 deletions(-) delete mode 100644 cmake/Modules/GNUInstallDirs.cmake diff --git a/cmake/Modules/GNUInstallDirs.cmake b/cmake/Modules/GNUInstallDirs.cmake deleted file mode 100644 index 0302e4be..00000000 --- a/cmake/Modules/GNUInstallDirs.cmake +++ /dev/null @@ -1,188 +0,0 @@ -# - Define GNU standard installation directories -# Provides install directory variables as defined for GNU software: -# http://www.gnu.org/prep/standards/html_node/Directory-Variables.html -# Inclusion of this module defines the following variables: -# CMAKE_INSTALL_ - destination for files of a given type -# CMAKE_INSTALL_FULL_ - corresponding absolute path -# where is one of: -# BINDIR - user executables (bin) -# SBINDIR - system admin executables (sbin) -# LIBEXECDIR - program executables (libexec) -# SYSCONFDIR - read-only single-machine data (etc) -# SHAREDSTATEDIR - modifiable architecture-independent data (com) -# LOCALSTATEDIR - modifiable single-machine data (var) -# LIBDIR - object code libraries (lib or lib64 or lib/ on Debian) -# INCLUDEDIR - C header files (include) -# OLDINCLUDEDIR - C header files for non-gcc (/usr/include) -# DATAROOTDIR - read-only architecture-independent data root (share) -# DATADIR - read-only architecture-independent data (DATAROOTDIR) -# INFODIR - info documentation (DATAROOTDIR/info) -# LOCALEDIR - locale-dependent data (DATAROOTDIR/locale) -# MANDIR - man documentation (DATAROOTDIR/man) -# DOCDIR - documentation root (DATAROOTDIR/doc/PROJECT_NAME) -# Each CMAKE_INSTALL_ value may be passed to the DESTINATION options of -# install() commands for the corresponding file type. If the includer does -# not define a value the above-shown default will be used and the value will -# appear in the cache for editing by the user. -# Each CMAKE_INSTALL_FULL_ value contains an absolute path constructed -# from the corresponding destination by prepending (if necessary) the value -# of CMAKE_INSTALL_PREFIX. - -#============================================================================= -# Copyright 2011 Nikita Krupen'ko -# Copyright 2011 Kitware, Inc. -# -# Distributed under the OSI-approved BSD License (the "License"); -# see accompanying file Copyright.txt for details. -# -# This software is distributed WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the License for more information. -#============================================================================= -# (To distribute this file outside of CMake, substitute the full -# License text for the above reference.) - -# Installation directories -# -if(NOT DEFINED CMAKE_INSTALL_BINDIR) - set(CMAKE_INSTALL_BINDIR "bin" CACHE PATH "user executables (bin)") -endif() - -if(NOT DEFINED CMAKE_INSTALL_SBINDIR) - set(CMAKE_INSTALL_SBINDIR "sbin" CACHE PATH "system admin executables (sbin)") -endif() - -if(NOT DEFINED CMAKE_INSTALL_LIBEXECDIR) - set(CMAKE_INSTALL_LIBEXECDIR "libexec" CACHE PATH "program executables (libexec)") -endif() - -if(NOT DEFINED CMAKE_INSTALL_SYSCONFDIR) - set(CMAKE_INSTALL_SYSCONFDIR "etc" CACHE PATH "read-only single-machine data (etc)") -endif() - -if(NOT DEFINED CMAKE_INSTALL_SHAREDSTATEDIR) - set(CMAKE_INSTALL_SHAREDSTATEDIR "com" CACHE PATH "modifiable architecture-independent data (com)") -endif() - -if(NOT DEFINED CMAKE_INSTALL_LOCALSTATEDIR) - set(CMAKE_INSTALL_LOCALSTATEDIR "var" CACHE PATH "modifiable single-machine data (var)") -endif() - -if(NOT DEFINED CMAKE_INSTALL_LIBDIR) - set(_LIBDIR_DEFAULT "lib") - # Override this default 'lib' with 'lib64' iff: - # - we are on Linux system but NOT cross-compiling - # - we are NOT on debian - # - we are on a 64 bits system - # reason is: amd64 ABI: http://www.x86-64.org/documentation/abi.pdf - # For Debian with multiarch, use 'lib/${CMAKE_LIBRARY_ARCHITECTURE}' if - # CMAKE_LIBRARY_ARCHITECTURE is set (which contains e.g. "i386-linux-gnu" - # See http://wiki.debian.org/Multiarch - if(CMAKE_SYSTEM_NAME MATCHES "Linux" - AND NOT CMAKE_CROSSCOMPILING) - if (EXISTS "/etc/debian_version") # is this a debian system ? - if(CMAKE_LIBRARY_ARCHITECTURE) - set(_LIBDIR_DEFAULT "lib/${CMAKE_LIBRARY_ARCHITECTURE}") - endif() - else() # not debian, rely on CMAKE_SIZEOF_VOID_P: - if(NOT DEFINED CMAKE_SIZEOF_VOID_P) - message(AUTHOR_WARNING - "Unable to determine default CMAKE_INSTALL_LIBDIR directory because no target architecture is known. " - "Please enable at least one language before including GNUInstallDirs.") - else() - if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") - set(_LIBDIR_DEFAULT "lib64") - endif() - endif() - endif() - endif() - set(CMAKE_INSTALL_LIBDIR "${_LIBDIR_DEFAULT}" CACHE PATH "object code libraries (${_LIBDIR_DEFAULT})") -endif() - -if(NOT DEFINED CMAKE_INSTALL_INCLUDEDIR) - set(CMAKE_INSTALL_INCLUDEDIR "include" CACHE PATH "C header files (include)") -endif() - -if(NOT DEFINED CMAKE_INSTALL_OLDINCLUDEDIR) - set(CMAKE_INSTALL_OLDINCLUDEDIR "/usr/include" CACHE PATH "C header files for non-gcc (/usr/include)") -endif() - -if(NOT DEFINED CMAKE_INSTALL_DATAROOTDIR) - set(CMAKE_INSTALL_DATAROOTDIR "share" CACHE PATH "read-only architecture-independent data root (share)") -endif() - -#----------------------------------------------------------------------------- -# Values whose defaults are relative to DATAROOTDIR. Store empty values in -# the cache and store the defaults in local variables if the cache values are -# not set explicitly. This auto-updates the defaults as DATAROOTDIR changes. - -if(NOT CMAKE_INSTALL_DATADIR) - set(CMAKE_INSTALL_DATADIR "" CACHE PATH "read-only architecture-independent data (DATAROOTDIR)") - set(CMAKE_INSTALL_DATADIR "${CMAKE_INSTALL_DATAROOTDIR}") -endif() - -if(NOT CMAKE_INSTALL_INFODIR) - set(CMAKE_INSTALL_INFODIR "" CACHE PATH "info documentation (DATAROOTDIR/info)") - set(CMAKE_INSTALL_INFODIR "${CMAKE_INSTALL_DATAROOTDIR}/info") -endif() - -if(NOT CMAKE_INSTALL_LOCALEDIR) - set(CMAKE_INSTALL_LOCALEDIR "" CACHE PATH "locale-dependent data (DATAROOTDIR/locale)") - set(CMAKE_INSTALL_LOCALEDIR "${CMAKE_INSTALL_DATAROOTDIR}/locale") -endif() - -if(NOT CMAKE_INSTALL_MANDIR) - set(CMAKE_INSTALL_MANDIR "" CACHE PATH "man documentation (DATAROOTDIR/man)") - set(CMAKE_INSTALL_MANDIR "${CMAKE_INSTALL_DATAROOTDIR}/man") -endif() - -if(NOT CMAKE_INSTALL_DOCDIR) - set(CMAKE_INSTALL_DOCDIR "" CACHE PATH "documentation root (DATAROOTDIR/doc/PROJECT_NAME)") - set(CMAKE_INSTALL_DOCDIR "${CMAKE_INSTALL_DATAROOTDIR}/doc/${PROJECT_NAME}") -endif() - -#----------------------------------------------------------------------------- - -mark_as_advanced( - CMAKE_INSTALL_BINDIR - CMAKE_INSTALL_SBINDIR - CMAKE_INSTALL_LIBEXECDIR - CMAKE_INSTALL_SYSCONFDIR - CMAKE_INSTALL_SHAREDSTATEDIR - CMAKE_INSTALL_LOCALSTATEDIR - CMAKE_INSTALL_LIBDIR - CMAKE_INSTALL_INCLUDEDIR - CMAKE_INSTALL_OLDINCLUDEDIR - CMAKE_INSTALL_DATAROOTDIR - CMAKE_INSTALL_DATADIR - CMAKE_INSTALL_INFODIR - CMAKE_INSTALL_LOCALEDIR - CMAKE_INSTALL_MANDIR - CMAKE_INSTALL_DOCDIR - ) - -# Result directories -# -foreach(dir - BINDIR - SBINDIR - LIBEXECDIR - SYSCONFDIR - SHAREDSTATEDIR - LOCALSTATEDIR - LIBDIR - INCLUDEDIR - OLDINCLUDEDIR - DATAROOTDIR - DATADIR - INFODIR - LOCALEDIR - MANDIR - DOCDIR - ) - if(NOT IS_ABSOLUTE ${CMAKE_INSTALL_${dir}}) - set(CMAKE_INSTALL_FULL_${dir} "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_${dir}}") - else() - set(CMAKE_INSTALL_FULL_${dir} "${CMAKE_INSTALL_${dir}}") - endif() -endforeach() From 50d5b2686445943ca8be4473de9570338ced29f6 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Thu, 14 Oct 2021 16:13:16 +0200 Subject: [PATCH 074/168] Set version to 0.13.0 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2fca5fd2..ba8059bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16) -set(QTKEYCHAIN_VERSION 0.12.90) +set(QTKEYCHAIN_VERSION 0.13.0) set(QTKEYCHAIN_SOVERSION 1) project(qtkeychain VERSION ${QTKEYCHAIN_VERSION} LANGUAGES CXX) From 5d87cd217f85b13a062fe5924e73c05af6626395 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Sun, 7 Nov 2021 09:06:00 +0100 Subject: [PATCH 075/168] Update changelog for 0.13.0 --- ChangeLog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ChangeLog b/ChangeLog index 932fe2b3..3815cf69 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,12 @@ ChangeLog ========= +version 0.13.0 (release 2021-11-07) + + - Linux: Require libsecret if not explicitly disabled + - Unify implementations for macOS and iOS + - CMake: lots of fixes + version 0.12.0 (release 2020-12-16) * Add Qt 6 support, drop Qt 4 support From 5018e8c475192b0175437278e19670a26ee94b35 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Sun, 7 Nov 2021 09:06:40 +0100 Subject: [PATCH 076/168] Bump version to 0.13.99 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ba8059bb..a5dc2ae1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16) -set(QTKEYCHAIN_VERSION 0.13.0) +set(QTKEYCHAIN_VERSION 0.13.99) set(QTKEYCHAIN_SOVERSION 1) project(qtkeychain VERSION ${QTKEYCHAIN_VERSION} LANGUAGES CXX) From 99425dd1f677ce7c665a24856cf01a8ab34c2eae Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Mon, 8 Nov 2021 09:58:09 +0100 Subject: [PATCH 077/168] KWallet: Fix deleting entries Do not leave empty entries, but actually delete them. Fixes #199 --- keychain_unix.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/keychain_unix.cpp b/keychain_unix.cpp index b9a8e9d1..985e8b4a 100644 --- a/keychain_unix.cpp +++ b/keychain_unix.cpp @@ -512,12 +512,16 @@ void JobPrivate::kwalletOpenFinished( QDBusPendingCallWatcher* watcher ) { QDBusPendingReply nextReply; - if ( mode == Text ) - nextReply = iface->writePassword( handle, q->service(), key, QString::fromUtf8(data), q->service() ); - else if ( mode == Binary ) - nextReply = iface->writeEntry( handle, q->service(), key, data, q->service() ); - else + if ( !data.isNull() ) { + if ( mode == Text ) { + nextReply = iface->writePassword( handle, q->service(), key, QString::fromUtf8(data), q->service() ); + } else { + Q_ASSERT( mode == Binary ); + nextReply = iface->writeEntry( handle, q->service(), key, data, q->service() ); + } + } else { nextReply = iface->removeEntry( handle, q->service(), key, q->service() ); + } QDBusPendingCallWatcher* nextWatcher = new QDBusPendingCallWatcher( nextReply, this ); connect( nextWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletFinished(QDBusPendingCallWatcher*)) ); From bb512c8ec9a875c344ea08487351748d71246f14 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Mon, 8 Nov 2021 09:58:09 +0100 Subject: [PATCH 078/168] KWallet: Fix deleting entries Do not leave empty entries, but actually delete them. Fixes #199 --- keychain_unix.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/keychain_unix.cpp b/keychain_unix.cpp index b9a8e9d1..985e8b4a 100644 --- a/keychain_unix.cpp +++ b/keychain_unix.cpp @@ -512,12 +512,16 @@ void JobPrivate::kwalletOpenFinished( QDBusPendingCallWatcher* watcher ) { QDBusPendingReply nextReply; - if ( mode == Text ) - nextReply = iface->writePassword( handle, q->service(), key, QString::fromUtf8(data), q->service() ); - else if ( mode == Binary ) - nextReply = iface->writeEntry( handle, q->service(), key, data, q->service() ); - else + if ( !data.isNull() ) { + if ( mode == Text ) { + nextReply = iface->writePassword( handle, q->service(), key, QString::fromUtf8(data), q->service() ); + } else { + Q_ASSERT( mode == Binary ); + nextReply = iface->writeEntry( handle, q->service(), key, data, q->service() ); + } + } else { nextReply = iface->removeEntry( handle, q->service(), key, q->service() ); + } QDBusPendingCallWatcher* nextWatcher = new QDBusPendingCallWatcher( nextReply, this ); connect( nextWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletFinished(QDBusPendingCallWatcher*)) ); From f59ac26be709fd2d8d7a062fab1cf1e67a93806c Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Mon, 8 Nov 2021 10:04:46 +0100 Subject: [PATCH 079/168] Prepare 0.13.1 release --- CMakeLists.txt | 2 +- ChangeLog | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ba8059bb..f6dff3c1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16) -set(QTKEYCHAIN_VERSION 0.13.0) +set(QTKEYCHAIN_VERSION 0.13.1) set(QTKEYCHAIN_SOVERSION 1) project(qtkeychain VERSION ${QTKEYCHAIN_VERSION} LANGUAGES CXX) diff --git a/ChangeLog b/ChangeLog index 3815cf69..8b4f4f0d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,10 @@ ChangeLog ========= +version 0.13.1 (release 2021-11-08) + + - KWallet: Fix deletion of entries (Issue #199) + version 0.13.0 (release 2021-11-07) - Linux: Require libsecret if not explicitly disabled From 43be122f1aafa42e1c46682a809b77999e2e0e36 Mon Sep 17 00:00:00 2001 From: Be Date: Wed, 10 Nov 2021 22:48:58 -0600 Subject: [PATCH 080/168] CMake: replace QTKEYCHAIN_STATIC with standard BUILD_SHARED_LIBS BUILD_SHARED_LIBS is the standard CMake variable for controlling whether libraries are built dynamically or statically. Using a nonstandard variable requires extra work for packagers. https://cmake.org/cmake/help/latest/variable/BUILD_SHARED_LIBS.html Fixes https://github.com/frankosterfeld/qtkeychain/issues/180 --- CMakeLists.txt | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a5dc2ae1..616301a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,7 +22,7 @@ include(ECMGeneratePriFile) option(BUILD_WITH_QT6 "Build qtkeychain with Qt 6" OFF) option(BUILD_TEST_APPLICATION "Build test application" OFF) option(BUILD_TRANSLATIONS "Build translations" ON) -option(QTKEYCHAIN_STATIC "Build static library" OFF) +option(BUILD_SHARED_LIBS "Build dynamic library" ON) if(CMAKE_SYSTEM_NAME STREQUAL Android) set(ANDROID 1) @@ -199,11 +199,7 @@ set(qtkeychain_TR_FILES ) set(QTKEYCHAIN_TARGET_NAME qt${QTKEYCHAIN_VERSION_INFIX}keychain) -if(NOT QTKEYCHAIN_STATIC) - add_library(${QTKEYCHAIN_TARGET_NAME} SHARED ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES} ${qtkeychain_QM_FILES}) -else() - add_library(${QTKEYCHAIN_TARGET_NAME} STATIC ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES} ${qtkeychain_QM_FILES}) -endif() +add_library(${QTKEYCHAIN_TARGET_NAME} ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES} ${qtkeychain_QM_FILES}) if(WIN32) set_target_properties( ${QTKEYCHAIN_TARGET_NAME} PROPERTIES DEBUG_POSTFIX "d" ) endif() From 61e51d35671195d4f20ebb1e1c0019d42055c997 Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 12 Nov 2021 14:35:13 -0600 Subject: [PATCH 081/168] CMake: add deprecation warning for QTKEYCHAIN_STATIC The default for the old QTKEYCHAIN_STATIC variable was OFF, which is effectively the same as the new default BUILD_SHARED_LIBS=ON, so there is no need to warn in that case. --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 616301a5..28d20380 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,10 @@ option(BUILD_WITH_QT6 "Build qtkeychain with Qt 6" OFF) option(BUILD_TEST_APPLICATION "Build test application" OFF) option(BUILD_TRANSLATIONS "Build translations" ON) option(BUILD_SHARED_LIBS "Build dynamic library" ON) +if(QTKEYCHAIN_STATIC) + set(BUILD_SHARED_LIBS OFF) + message(WARNING "QTKEYCHAIN_STATIC is deprecated. Use BUILD_SHARED_LIBS=OFF instead.") +endif() if(CMAKE_SYSTEM_NAME STREQUAL Android) set(ANDROID 1) From 84f786c578b7834a49799d2432b3976ea314a260 Mon Sep 17 00:00:00 2001 From: Be Date: Wed, 10 Nov 2021 22:48:58 -0600 Subject: [PATCH 082/168] CMake: replace QTKEYCHAIN_STATIC with standard BUILD_SHARED_LIBS BUILD_SHARED_LIBS is the standard CMake variable for controlling whether libraries are built dynamically or statically. Using a nonstandard variable requires extra work for packagers. https://cmake.org/cmake/help/latest/variable/BUILD_SHARED_LIBS.html Fixes https://github.com/frankosterfeld/qtkeychain/issues/180 --- CMakeLists.txt | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f6dff3c1..c20658ba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,7 +22,7 @@ include(ECMGeneratePriFile) option(BUILD_WITH_QT6 "Build qtkeychain with Qt 6" OFF) option(BUILD_TEST_APPLICATION "Build test application" OFF) option(BUILD_TRANSLATIONS "Build translations" ON) -option(QTKEYCHAIN_STATIC "Build static library" OFF) +option(BUILD_SHARED_LIBS "Build dynamic library" ON) if(CMAKE_SYSTEM_NAME STREQUAL Android) set(ANDROID 1) @@ -199,11 +199,7 @@ set(qtkeychain_TR_FILES ) set(QTKEYCHAIN_TARGET_NAME qt${QTKEYCHAIN_VERSION_INFIX}keychain) -if(NOT QTKEYCHAIN_STATIC) - add_library(${QTKEYCHAIN_TARGET_NAME} SHARED ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES} ${qtkeychain_QM_FILES}) -else() - add_library(${QTKEYCHAIN_TARGET_NAME} STATIC ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES} ${qtkeychain_QM_FILES}) -endif() +add_library(${QTKEYCHAIN_TARGET_NAME} ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES} ${qtkeychain_QM_FILES}) if(WIN32) set_target_properties( ${QTKEYCHAIN_TARGET_NAME} PROPERTIES DEBUG_POSTFIX "d" ) endif() From 301837d71b1b77e88f55c8d78018bf8781f875fa Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 12 Nov 2021 14:35:13 -0600 Subject: [PATCH 083/168] CMake: add deprecation warning for QTKEYCHAIN_STATIC The default for the old QTKEYCHAIN_STATIC variable was OFF, which is effectively the same as the new default BUILD_SHARED_LIBS=ON, so there is no need to warn in that case. --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index c20658ba..023fc050 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,10 @@ option(BUILD_WITH_QT6 "Build qtkeychain with Qt 6" OFF) option(BUILD_TEST_APPLICATION "Build test application" OFF) option(BUILD_TRANSLATIONS "Build translations" ON) option(BUILD_SHARED_LIBS "Build dynamic library" ON) +if(QTKEYCHAIN_STATIC) + set(BUILD_SHARED_LIBS OFF) + message(WARNING "QTKEYCHAIN_STATIC is deprecated. Use BUILD_SHARED_LIBS=OFF instead.") +endif() if(CMAKE_SYSTEM_NAME STREQUAL Android) set(ANDROID 1) From c236f9241e37f1754879cf8726d59351a2b2b24a Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Thu, 18 Nov 2021 16:34:13 +0100 Subject: [PATCH 084/168] Prepare 0.13.2 --- CMakeLists.txt | 2 +- ChangeLog | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 023fc050..3786b63e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16) -set(QTKEYCHAIN_VERSION 0.13.1) +set(QTKEYCHAIN_VERSION 0.13.2) set(QTKEYCHAIN_SOVERSION 1) project(qtkeychain VERSION ${QTKEYCHAIN_VERSION} LANGUAGES CXX) diff --git a/ChangeLog b/ChangeLog index 8b4f4f0d..3c420af7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,10 @@ ChangeLog ========= +version 0.13.2 (release 2021-11-18) + + - CMake: Deprecate QTKEYCHAIN_STATIC in favor of BUILD_SHARED_LIBS (be@mixxx.org) + version 0.13.1 (release 2021-11-08) - KWallet: Fix deletion of entries (Issue #199) From 4dea5e3578dd311c6844d91de3b2c986bafd6bb4 Mon Sep 17 00:00:00 2001 From: akallabeth Date: Thu, 9 Apr 2020 15:15:47 +0200 Subject: [PATCH 085/168] Added option to compile translations into library. --- CMakeLists.txt | 33 ++++++++++++++++++++++---------- translations/translations.qrc.in | 5 +++++ 2 files changed, 28 insertions(+), 10 deletions(-) create mode 100644 translations/translations.qrc.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 28d20380..9555955c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,7 @@ include(GenerateExportHeader) include(CMakePackageConfigHelpers) include(ECMSetupVersion) include(ECMGeneratePriFile) +include(CMakeDependentOption) option(BUILD_WITH_QT6 "Build qtkeychain with Qt 6" OFF) option(BUILD_TEST_APPLICATION "Build test application" OFF) @@ -27,6 +28,8 @@ if(QTKEYCHAIN_STATIC) set(BUILD_SHARED_LIBS OFF) message(WARNING "QTKEYCHAIN_STATIC is deprecated. Use BUILD_SHARED_LIBS=OFF instead.") endif() +CMAKE_DEPENDENT_OPTION(BUILD_TRANSLATIONS_AS_RESOURCES "Bundle translations with the library" OFF + "BUILD_TRANSLATIONS" OFF) if(CMAKE_SYSTEM_NAME STREQUAL Android) set(ANDROID 1) @@ -217,18 +220,28 @@ if ( BUILD_TRANSLATIONS ) # https://github.com/frankosterfeld/qtkeychain/issues/185 add_dependencies(${QTKEYCHAIN_TARGET_NAME} translations) - if(QTKEYCHAIN_VERSION_INFIX EQUAL 5 AND QT_TRANSLATIONS_DIR AND NOT QTKEYCHAIN_TRANSLATIONS_DIR) - # Back compatibility with pre-0.11 versions - message (WARNING "QT_TRANSLATIONS_DIR is deprecated, use QTKEYCHAIN_TRANSLATIONS_DIR instead") - set(QTKEYCHAIN_TRANSLATIONS_DIR ${QT_TRANSLATIONS_DIR} - CACHE PATH "The location of the QtKeychain translations" FORCE) + if (BUILD_TRANSLATIONS_AS_RESOURCES) + set(QM_FILE_LIST "") + foreach(FILE ${qtkeychain_QM_FILES}) + list(APPEND QM_FILE_LIST "${FILE}") + endforeach() + string(REPLACE ";" "" QM_FILE_LIST ${QM_FILE_LIST}) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/translations/translations.qrc.in ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc) + target_sources(${QTKEYCHAIN_TARGET_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc) else() - set(QTKEYCHAIN_TRANSLATIONS_DIR - ${CMAKE_INSTALL_DATADIR}/qt${QTKEYCHAIN_VERSION_INFIX}keychain/translations - CACHE PATH "The location of the QtKeychain translations" ) + if(QTKEYCHAIN_VERSION_INFIX EQUAL 5 AND QT_TRANSLATIONS_DIR AND NOT QTKEYCHAIN_TRANSLATIONS_DIR) + # Back compatibility with pre-0.11 versions + message (WARNING "QT_TRANSLATIONS_DIR is deprecated, use QTKEYCHAIN_TRANSLATIONS_DIR instead") + set(QTKEYCHAIN_TRANSLATIONS_DIR ${QT_TRANSLATIONS_DIR} + CACHE PATH "The location of the QtKeychain translations" FORCE) + else() + set(QTKEYCHAIN_TRANSLATIONS_DIR + ${CMAKE_INSTALL_DATADIR}/qt${QTKEYCHAIN_VERSION_INFIX}keychain/translations + CACHE PATH "The location of the QtKeychain translations" ) + endif() + + install(FILES ${qtkeychain_QM_FILES} DESTINATION ${QTKEYCHAIN_TRANSLATIONS_DIR}) endif() - - install(FILES ${qtkeychain_QM_FILES} DESTINATION ${QTKEYCHAIN_TRANSLATIONS_DIR}) endif( BUILD_TRANSLATIONS ) target_link_libraries(${QTKEYCHAIN_TARGET_NAME} PUBLIC ${qtkeychain_LIBRARIES} PRIVATE ${qtkeychain_LIBRARIES_PRIVATE}) diff --git a/translations/translations.qrc.in b/translations/translations.qrc.in new file mode 100644 index 00000000..f49df661 --- /dev/null +++ b/translations/translations.qrc.in @@ -0,0 +1,5 @@ + + + @QM_FILE_LIST@ + + From e5eeb1763e295f6b05a3f008ee7ae192fd74ed0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Tue, 25 Jan 2022 21:13:43 +0100 Subject: [PATCH 086/168] Define a poper exported CMake target instead of introducing an alias. This fixes building with cmake < version 18 a regression form 70a567abc59d11d2b1c87fcb8b7763365b3a5365 issue #209 --- QtKeychainConfig.cmake.in | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/QtKeychainConfig.cmake.in b/QtKeychainConfig.cmake.in index d849dedd..41abb0ed 100644 --- a/QtKeychainConfig.cmake.in +++ b/QtKeychainConfig.cmake.in @@ -18,7 +18,13 @@ if(UNIX AND NOT APPLE AND NOT ANDROID) find_dependency(Qt@QTKEYCHAIN_VERSION_INFIX@DBus) endif() -set(QTKEYCHAIN_LIBRARIES "@QTKEYCHAIN_TARGET_NAME@") +get_target_property(QTKEYCHAIN_LIBRARIES "@QTKEYCHAIN_TARGET_NAME@" LOCATION) get_target_property(QTKEYCHAIN_INCLUDE_DIRS "@QTKEYCHAIN_TARGET_NAME@" INTERFACE_INCLUDE_DIRECTORIES) -add_library(Qt@QTKEYCHAIN_VERSION_INFIX@Keychain::Qt@QTKEYCHAIN_VERSION_INFIX@Keychain ALIAS qt@QTKEYCHAIN_VERSION_INFIX@keychain) +if(NOT TARGET Qt@QTKEYCHAIN_VERSION_INFIX@Keychain::Qt@QTKEYCHAIN_VERSION_INFIX@Keychain) + add_library(Qt@QTKEYCHAIN_VERSION_INFIX@Keychain::Qt@QTKEYCHAIN_VERSION_INFIX@Keychain UNKNOWN IMPORTED) + set_target_properties(Qt@QTKEYCHAIN_VERSION_INFIX@Keychain::Qt@QTKEYCHAIN_VERSION_INFIX@Keychain PROPERTIES + IMPORTED_LOCATION "${QTKEYCHAIN_LIBRARIES}" + INTERFACE_INCLUDE_DIRECTORIES "${QTKEYCHAIN_INCLUDE_DIRS}" + ) +endif() From 9e39435ce73255017ef7ea6a421ab8396ec2293f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sun, 8 May 2022 08:36:20 +0200 Subject: [PATCH 087/168] Revert "Define a poper exported CMake target instead of introducing an alias." This reverts commit e5eeb1763e295f6b05a3f008ee7ae192fd74ed0c. --- QtKeychainConfig.cmake.in | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/QtKeychainConfig.cmake.in b/QtKeychainConfig.cmake.in index 41abb0ed..d849dedd 100644 --- a/QtKeychainConfig.cmake.in +++ b/QtKeychainConfig.cmake.in @@ -18,13 +18,7 @@ if(UNIX AND NOT APPLE AND NOT ANDROID) find_dependency(Qt@QTKEYCHAIN_VERSION_INFIX@DBus) endif() -get_target_property(QTKEYCHAIN_LIBRARIES "@QTKEYCHAIN_TARGET_NAME@" LOCATION) +set(QTKEYCHAIN_LIBRARIES "@QTKEYCHAIN_TARGET_NAME@") get_target_property(QTKEYCHAIN_INCLUDE_DIRS "@QTKEYCHAIN_TARGET_NAME@" INTERFACE_INCLUDE_DIRECTORIES) -if(NOT TARGET Qt@QTKEYCHAIN_VERSION_INFIX@Keychain::Qt@QTKEYCHAIN_VERSION_INFIX@Keychain) - add_library(Qt@QTKEYCHAIN_VERSION_INFIX@Keychain::Qt@QTKEYCHAIN_VERSION_INFIX@Keychain UNKNOWN IMPORTED) - set_target_properties(Qt@QTKEYCHAIN_VERSION_INFIX@Keychain::Qt@QTKEYCHAIN_VERSION_INFIX@Keychain PROPERTIES - IMPORTED_LOCATION "${QTKEYCHAIN_LIBRARIES}" - INTERFACE_INCLUDE_DIRECTORIES "${QTKEYCHAIN_INCLUDE_DIRS}" - ) -endif() +add_library(Qt@QTKEYCHAIN_VERSION_INFIX@Keychain::Qt@QTKEYCHAIN_VERSION_INFIX@Keychain ALIAS qt@QTKEYCHAIN_VERSION_INFIX@keychain) From ef13befd406539dd7a6c53735a51a0e475b5d455 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sun, 8 May 2022 09:05:10 +0200 Subject: [PATCH 088/168] Only create Qt5Keychain::Qt5Keychain/Qt6Keychain::Qt6Keychain if cmake >= 3.18.0 --- QtKeychainConfig.cmake.in | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/QtKeychainConfig.cmake.in b/QtKeychainConfig.cmake.in index d849dedd..dcf19057 100644 --- a/QtKeychainConfig.cmake.in +++ b/QtKeychainConfig.cmake.in @@ -21,4 +21,7 @@ endif() set(QTKEYCHAIN_LIBRARIES "@QTKEYCHAIN_TARGET_NAME@") get_target_property(QTKEYCHAIN_INCLUDE_DIRS "@QTKEYCHAIN_TARGET_NAME@" INTERFACE_INCLUDE_DIRECTORIES) -add_library(Qt@QTKEYCHAIN_VERSION_INFIX@Keychain::Qt@QTKEYCHAIN_VERSION_INFIX@Keychain ALIAS qt@QTKEYCHAIN_VERSION_INFIX@keychain) +if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.18.0) + add_library(Qt@QTKEYCHAIN_VERSION_INFIX@Keychain::Qt@QTKEYCHAIN_VERSION_INFIX@Keychain ALIAS qt@QTKEYCHAIN_VERSION_INFIX@keychain) +endif() + From cd4d73299b144d11c310f6ca9a6ab1ef50c45431 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sun, 8 May 2022 09:09:49 +0200 Subject: [PATCH 089/168] call check_required_compnents for a proper feedback in cmake logs --- QtKeychainConfig.cmake.in | 1 + 1 file changed, 1 insertion(+) diff --git a/QtKeychainConfig.cmake.in b/QtKeychainConfig.cmake.in index dcf19057..7addba9f 100644 --- a/QtKeychainConfig.cmake.in +++ b/QtKeychainConfig.cmake.in @@ -25,3 +25,4 @@ if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.18.0) add_library(Qt@QTKEYCHAIN_VERSION_INFIX@Keychain::Qt@QTKEYCHAIN_VERSION_INFIX@Keychain ALIAS qt@QTKEYCHAIN_VERSION_INFIX@keychain) endif() +check_required_components(Qt@QTKEYCHAIN_VERSION_INFIX@Keychain) From 67fb08e89194ed3a6cd2b2e4ae24509ea4714fa1 Mon Sep 17 00:00:00 2001 From: Volker Krause Date: Fri, 1 Jul 2022 14:41:34 +0200 Subject: [PATCH 090/168] Update copied ECM files These now have proper Qt6 support and fixes building against Qt6 if there is also a Qt5 available. --- cmake/Modules/ECMGeneratePriFile.cmake | 261 +++++++++++++++---------- cmake/Modules/ECMQueryQmake.cmake | 46 ----- cmake/Modules/ECMQueryQt.cmake | 100 ++++++++++ cmake/Modules/QtVersionOption.cmake | 36 ++++ 4 files changed, 295 insertions(+), 148 deletions(-) delete mode 100644 cmake/Modules/ECMQueryQmake.cmake create mode 100644 cmake/Modules/ECMQueryQt.cmake create mode 100644 cmake/Modules/QtVersionOption.cmake diff --git a/cmake/Modules/ECMGeneratePriFile.cmake b/cmake/Modules/ECMGeneratePriFile.cmake index f63a0ce2..8910cd9e 100644 --- a/cmake/Modules/ECMGeneratePriFile.cmake +++ b/cmake/Modules/ECMGeneratePriFile.cmake @@ -1,104 +1,124 @@ -#.rst: -# ECMGeneratePriFile -# ------------------ -# -# Generate a ``.pri`` file for the benefit of qmake-based projects. -# -# As well as the function below, this module creates the cache variable -# ``ECM_MKSPECS_INSTALL_DIR`` and sets the default value to ``mkspecs/modules``. -# This assumes Qt and the current project are both installed to the same -# non-system prefix. Packagers who use ``-DCMAKE_INSTALL_PREFIX=/usr`` will -# certainly want to set ``ECM_MKSPECS_INSTALL_DIR`` to something like -# ``share/qt5/mkspecs/modules``. -# -# The main thing is that this should be the ``modules`` subdirectory of either -# the default qmake ``mkspecs`` directory or of a directory that will be in the -# ``$QMAKEPATH`` environment variable when qmake is run. -# -# :: -# -# ecm_generate_pri_file(BASE_NAME -# LIB_NAME -# [DEPS " [ [...]]"] -# [FILENAME_VAR ] -# [INCLUDE_INSTALL_DIR ] -# [LIB_INSTALL_DIR ]) -# -# If your CMake project produces a Qt-based library, you may expect there to be -# applications that wish to use it that use a qmake-based build system, rather -# than a CMake-based one. Creating a ``.pri`` file will make use of your -# library convenient for them, in much the same way that CMake config files make -# things convenient for CMake-based applications. -# -# ecm_generate_pri_file() generates just such a file. It requires the -# ``PROJECT_VERSION_STRING`` variable to be set. This is typically set by -# :module:`ECMSetupVersion`, although the project() command in CMake 3.0.0 and -# later can also set this. -# -# BASE_NAME specifies the name qmake project (.pro) files should use to refer to -# the library (eg: KArchive). LIB_NAME is the name of the actual library to -# link to (ie: the first argument to add_library()). DEPS is a space-separated -# list of the base names of other libraries (for Qt libraries, use the same -# names you use with the ``QT`` variable in a qmake project file, such as "core" -# for QtCore). FILENAME_VAR specifies the name of a variable to store the path -# to the generated file in. -# -# INCLUDE_INSTALL_DIR is the path (relative to ``CMAKE_INSTALL_PREFIX``) that -# include files will be installed to. It defaults to -# ``${INCLUDE_INSTALL_DIR}/`` if the ``INCLUDE_INSTALL_DIR`` variable -# is set. If that variable is not set, the ``CMAKE_INSTALL_INCLUDEDIR`` variable -# is used instead, and if neither are set ``include`` is used. LIB_INSTALL_DIR -# operates similarly for the installation location for libraries; it defaults to -# ``${LIB_INSTALL_DIR}``, ``${CMAKE_INSTALL_LIBDIR}`` or ``lib``, in that order. -# -# Example usage: -# -# .. code-block:: cmake -# -# ecm_generate_pri_file( -# BASE_NAME KArchive -# LIB_NAME KF5KArchive -# DEPS "core" -# FILENAME_VAR pri_filename -# ) -# install(FILES ${pri_filename} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) -# -# A qmake-based project that wished to use this would then do:: -# -# QT += KArchive -# -# in their ``.pro`` file. -# -# Since pre-1.0.0. - -#============================================================================= # SPDX-FileCopyrightText: 2014 David Faure # # SPDX-License-Identifier: BSD-3-Clause +#[=======================================================================[.rst: +ECMGeneratePriFile +------------------ + +Generate a ``.pri`` file for the benefit of qmake-based projects. + +As well as the function below, this module creates the cache variable +``ECM_MKSPECS_INSTALL_DIR`` and sets the default value to ``mkspecs/modules``. +This assumes Qt and the current project are both installed to the same +non-system prefix. Packagers who use ``-DCMAKE_INSTALL_PREFIX=/usr`` will +certainly want to set ``ECM_MKSPECS_INSTALL_DIR`` to something like +``share/qt5/mkspecs/modules``. + +The main thing is that this should be the ``modules`` subdirectory of either +the default qmake ``mkspecs`` directory or of a directory that will be in the +``$QMAKEPATH`` environment variable when qmake is run. + +:: + + ecm_generate_pri_file(BASE_NAME + LIB_NAME + [VERSION ] # since 5.83 + [DEPS " [ [...]]"] + [FILENAME_VAR ] + [INCLUDE_INSTALL_DIRS [ [...]]] # since 5.92 + [INCLUDE_INSTALL_DIR ] # deprecated since 5.92 + [LIB_INSTALL_DIR ]) + +If your CMake project produces a Qt-based library, you may expect there to be +applications that wish to use it that use a qmake-based build system, rather +than a CMake-based one. Creating a ``.pri`` file will make use of your +library convenient for them, in much the same way that CMake config files make +things convenient for CMake-based applications. ``ecm_generate_pri_file()`` +generates just such a file. + +``VERSION`` specifies the version of the library the ``.pri`` file describes. If +not set, the value is taken from the context variable ``PROJECT_VERSION``. +This variable is usually set by the ``project(... VERSION ...)`` command or, +if CMake policy CMP0048 is not ``NEW``, by :module:`ECMSetupVersion`. +For backward-compatibility with older ECM versions the +``PROJECT_VERSION_STRING`` variable as set by :module:`ECMSetupVersion` +will be preferred over ``PROJECT_VERSION`` if set, unless the minimum +required version of ECM is 5.83 and newer. Since 5.83. + +``BASE_NAME`` specifies the name qmake project (.pro) files should use to refer to +the library (eg: KArchive). ``LIB_NAME`` is the name of the actual library to +link to (ie: the first argument to add_library()). ``DEPS`` is a space-separated +list of the base names of other libraries (for Qt libraries, use the same +names you use with the ``QT`` variable in a qmake project file, such as "core" +for QtCore). ``FILENAME_VAR`` specifies the name of a variable to store the path +to the generated file in. + +``INCLUDE_INSTALL_DIRS`` are the paths (relative to ``CMAKE_INSTALL_PREFIX``) that +include files will be installed to. It defaults to +``${INCLUDE_INSTALL_DIR}/`` if the ``INCLUDE_INSTALL_DIR`` variable +is set. If that variable is not set, the ``CMAKE_INSTALL_INCLUDEDIR`` variable +is used instead, and if neither are set ``include`` is used. ``LIB_INSTALL_DIR`` +operates similarly for the installation location for libraries; it defaults to +``${LIB_INSTALL_DIR}``, ``${CMAKE_INSTALL_LIBDIR}`` or ``lib``, in that order. + +``INCLUDE_INSTALL_DIR`` is the old variant of ``INCLUDE_INSTALL_DIRS``, taking only one +directory. + +Example usage: + +.. code-block:: cmake + + ecm_generate_pri_file( + BASE_NAME KArchive + LIB_NAME KF5KArchive + DEPS "core" + FILENAME_VAR pri_filename + VERSION 4.2.0 + ) + install(FILES ${pri_filename} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) + +A qmake-based project that wished to use this would then do:: + + QT += KArchive + +in their ``.pro`` file. + +Since pre-1.0.0. +#]=======================================================================] + # Replicate the logic from KDEInstallDirs.cmake as we can't depend on it # Ask qmake if we're using the same prefix as Qt -set(_askqmake OFF) +set(_should_query_qt OFF) if(NOT DEFINED KDE_INSTALL_USE_QT_SYS_PATHS) - include(ECMQueryQmake) - query_qmake(qt_install_prefix_dir QT_INSTALL_PREFIX TRY) + include(ECMQueryQt) + ecm_query_qt(qt_install_prefix_dir QT_INSTALL_PREFIX TRY) if(qt_install_prefix_dir STREQUAL "${CMAKE_INSTALL_PREFIX}") - set(_askqmake ON) + set(_should_query_qt ON) endif() endif() -if(KDE_INSTALL_USE_QT_SYS_PATHS OR _askqmake) - include(ECMQueryQmake) - query_qmake(qt_host_data_dir QT_HOST_DATA) - set(ECM_MKSPECS_INSTALL_DIR ${qt_host_data_dir}/mkspecs/modules CACHE PATH "The directory where mkspecs will be installed to.") +if(KDE_INSTALL_USE_QT_SYS_PATHS OR _should_query_qt) + include(ECMQueryQt) + ecm_query_qt(qt_install_prefix_dir QT_INSTALL_PREFIX) + ecm_query_qt(qt_host_data_dir QT_HOST_DATA) + if(qt_install_prefix_dir STREQUAL "${CMAKE_INSTALL_PREFIX}") + file(RELATIVE_PATH qt_host_data_dir ${qt_install_prefix_dir} ${qt_host_data_dir}) + endif() + if(qt_host_data_dir STREQUAL "") + set(mkspecs_install_dir mkspecs/modules) + else() + set(mkspecs_install_dir ${qt_host_data_dir}/mkspecs/modules) + endif() + set(ECM_MKSPECS_INSTALL_DIR ${mkspecs_install_dir} CACHE PATH "The directory where mkspecs will be installed to.") else() set(ECM_MKSPECS_INSTALL_DIR mkspecs/modules CACHE PATH "The directory where mkspecs will be installed to.") endif() function(ECM_GENERATE_PRI_FILE) set(options ) - set(oneValueArgs BASE_NAME LIB_NAME DEPS FILENAME_VAR INCLUDE_INSTALL_DIR LIB_INSTALL_DIR) - set(multiValueArgs ) + set(oneValueArgs BASE_NAME LIB_NAME DEPS FILENAME_VAR INCLUDE_INSTALL_DIR LIB_INSTALL_DIR VERSION) + set(multiValueArgs INCLUDE_INSTALL_DIRS) cmake_parse_arguments(EGPF "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) @@ -106,22 +126,42 @@ function(ECM_GENERATE_PRI_FILE) message(FATAL_ERROR "Unknown keywords given to ECM_GENERATE_PRI_FILE(): \"${EGPF_UNPARSED_ARGUMENTS}\"") endif() + if(ECM_GLOBAL_FIND_VERSION VERSION_LESS 5.83.0) + set(_support_backward_compat_version_string_var TRUE) + else() + set(_support_backward_compat_version_string_var FALSE) + endif() + if(NOT EGPF_BASE_NAME) message(FATAL_ERROR "Required argument BASE_NAME missing in ECM_GENERATE_PRI_FILE() call") endif() if(NOT EGPF_LIB_NAME) message(FATAL_ERROR "Required argument LIB_NAME missing in ECM_GENERATE_PRI_FILE() call") endif() - if(NOT PROJECT_VERSION_STRING) - message(FATAL_ERROR "Required variable PROJECT_VERSION_STRING not set before ECM_GENERATE_PRI_FILE() call. Did you call ecm_setup_version?") + if(NOT EGPF_VERSION) + if(_support_backward_compat_version_string_var) + if(NOT PROJECT_VERSION_STRING AND NOT PROJECT_VERSION) + message(FATAL_ERROR "Required variable PROJECT_VERSION_STRING or PROJECT_VERSION not set before ECM_GENERATE_PRI_FILE() call. Missing call of ecm_setup_version() or project(VERSION)?") + endif() + else() + if(NOT PROJECT_VERSION) + message(FATAL_ERROR "Required variable PROJECT_VERSION not set before ECM_GENERATE_PRI_FILE() call. Missing call of ecm_setup_version() or project(VERSION)?") + endif() + endif() endif() - if(NOT EGPF_INCLUDE_INSTALL_DIR) + if(EGPF_INCLUDE_INSTALL_DIR) + if(EGPF_INCLUDE_INSTALL_DIRS) + message(FATAL_ERROR "Only one argument of INCLUDE_INSTALL_DIR & INCLUDE_INSTALL_DIRS can be used in ECM_GENERATE_PRI_FILE() call") + endif() + set(EGPF_INCLUDE_INSTALL_DIRS ${EGPF_INCLUDE_INSTALL_DIR}) + endif() + if(NOT EGPF_INCLUDE_INSTALL_DIRS) if(INCLUDE_INSTALL_DIR) - set(EGPF_INCLUDE_INSTALL_DIR "${INCLUDE_INSTALL_DIR}/${EGPF_BASE_NAME}") + set(EGPF_INCLUDE_INSTALL_DIRS "${INCLUDE_INSTALL_DIR}/${EGPF_BASE_NAME}") elseif(CMAKE_INSTALL_INCLUDEDIR) - set(EGPF_INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_INCLUDEDIR}/${EGPF_BASE_NAME}") + set(EGPF_INCLUDE_INSTALL_DIRS "${CMAKE_INSTALL_INCLUDEDIR}/${EGPF_BASE_NAME}") else() - set(EGPF_INCLUDE_INSTALL_DIR "include/${EGPF_BASE_NAME}") + set(EGPF_INCLUDE_INSTALL_DIRS "include/${EGPF_BASE_NAME}") endif() endif() if(NOT EGPF_LIB_INSTALL_DIR) @@ -134,9 +174,22 @@ function(ECM_GENERATE_PRI_FILE) endif() endif() - string(REGEX REPLACE "^([0-9]+)\\.[0-9]+\\.[0-9]+.*" "\\1" PROJECT_VERSION_MAJOR "${PROJECT_VERSION_STRING}") - string(REGEX REPLACE "^[0-9]+\\.([0-9]+)\\.[0-9]+.*" "\\1" PROJECT_VERSION_MINOR "${PROJECT_VERSION_STRING}") - string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" PROJECT_VERSION_PATCH "${PROJECT_VERSION_STRING}") + if(EGPF_VERSION) + set(PRI_VERSION "${EGPF_VERSION}") + else() + if(_support_backward_compat_version_string_var AND PROJECT_VERSION_STRING) + set(PRI_VERSION "${PROJECT_VERSION_STRING}") + if(NOT PROJECT_VERSION_STRING STREQUAL PROJECT_VERSION) + message(DEPRECATION "ECM_GENERATE_PRI_FILE() will no longer support PROJECT_VERSION_STRING when the required minimum version of ECM is 5.83 or newer. Set VERSION parameter or use PROJECT_VERSION instead.") + endif() + else() + set(PRI_VERSION "${PROJECT_VERSION}") + endif() + endif() + + string(REGEX REPLACE "^([0-9]+)\\.[0-9]+\\.[0-9]+.*" "\\1" PRI_VERSION_MAJOR "${PRI_VERSION}") + string(REGEX REPLACE "^[0-9]+\\.([0-9]+)\\.[0-9]+.*" "\\1" PRI_VERSION_MINOR "${PRI_VERSION}") + string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" PRI_VERSION_PATCH "${PRI_VERSION}") # Prepare the right number of "../.." to go from ECM_MKSPECS_INSTALL_DIR to the install prefix # This allows to make the generated pri files relocatable (no absolute paths) @@ -150,11 +203,15 @@ function(ECM_GENERATE_PRI_FILE) set(PRI_TARGET_BASENAME ${EGPF_BASE_NAME}) set(PRI_TARGET_LIBNAME ${EGPF_LIB_NAME}) set(PRI_TARGET_QTDEPS ${EGPF_DEPS}) - if(IS_ABSOLUTE "${EGPF_INCLUDE_INSTALL_DIR}") - set(PRI_TARGET_INCLUDES "${EGPF_INCLUDE_INSTALL_DIR}") - else() - set(PRI_TARGET_INCLUDES "${BASEPATH}/${EGPF_INCLUDE_INSTALL_DIR}") - endif() + set(PRI_TARGET_INCLUDES) + foreach(_dir ${EGPF_INCLUDE_INSTALL_DIRS}) + # separate list entries with space + if(IS_ABSOLUTE "${_dir}") + string(APPEND PRI_TARGET_INCLUDES " ${_dir}") + else() + string(APPEND PRI_TARGET_INCLUDES " ${BASEPATH}/${_dir}") + endif() + endforeach() if(IS_ABSOLUTE "${EGPF_LIB_INSTALL_DIR}") set(PRI_TARGET_LIBS "${EGPF_LIB_INSTALL_DIR}") else() @@ -182,10 +239,10 @@ function(ECM_GENERATE_PRI_FILE) file(GENERATE OUTPUT ${PRI_FILENAME} CONTENT - "QT.${PRI_TARGET_BASENAME}.VERSION = ${PROJECT_VERSION_STRING} -QT.${PRI_TARGET_BASENAME}.MAJOR_VERSION = ${PROJECT_VERSION_MAJOR} -QT.${PRI_TARGET_BASENAME}.MINOR_VERSION = ${PROJECT_VERSION_MINOR} -QT.${PRI_TARGET_BASENAME}.PATCH_VERSION = ${PROJECT_VERSION_PATCH} + "QT.${PRI_TARGET_BASENAME}.VERSION = ${PRI_VERSION} +QT.${PRI_TARGET_BASENAME}.MAJOR_VERSION = ${PRI_VERSION_MAJOR} +QT.${PRI_TARGET_BASENAME}.MINOR_VERSION = ${PRI_VERSION_MINOR} +QT.${PRI_TARGET_BASENAME}.PATCH_VERSION = ${PRI_VERSION_PATCH} QT.${PRI_TARGET_BASENAME}.name = ${PRI_TARGET_LIBNAME} QT.${PRI_TARGET_BASENAME}.module = ${PRI_TARGET_LIBNAME} QT.${PRI_TARGET_BASENAME}.defines = ${PRI_TARGET_DEFINES} diff --git a/cmake/Modules/ECMQueryQmake.cmake b/cmake/Modules/ECMQueryQmake.cmake deleted file mode 100644 index 74a6df87..00000000 --- a/cmake/Modules/ECMQueryQmake.cmake +++ /dev/null @@ -1,46 +0,0 @@ -find_package(Qt5Core QUIET) - -if (Qt5Core_FOUND) - set(_qmake_executable_default "qmake-qt5") -endif () -if (TARGET Qt5::qmake) - get_target_property(_qmake_executable_default Qt5::qmake LOCATION) -endif() -set(QMAKE_EXECUTABLE ${_qmake_executable_default} - CACHE FILEPATH "Location of the Qt5 qmake executable") - -# Helper method -# This is not public API (yet)! -# Usage: query_qmake( [TRY]) -# Passing TRY will result in the method not failing fatal if no qmake executable -# has been found, but instead simply returning an empty string -function(query_qmake result_variable qt_variable) - set(options TRY) - set(oneValueArgs ) - set(multiValueArgs ) - - cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - if(NOT QMAKE_EXECUTABLE) - if(ARGS_TRY) - set(${result_variable} "" PARENT_SCOPE) - message(STATUS "No qmake Qt5 binary found. Can't check ${qt_variable}") - return() - else() - message(FATAL_ERROR "No qmake Qt5 binary found. Can't check ${qt_variable} as required") - endif() - endif() - execute_process( - COMMAND ${QMAKE_EXECUTABLE} -query "${qt_variable}" - RESULT_VARIABLE return_code - OUTPUT_VARIABLE output - ) - if(return_code EQUAL 0) - string(STRIP "${output}" output) - file(TO_CMAKE_PATH "${output}" output_path) - set(${result_variable} "${output_path}" PARENT_SCOPE) - else() - message(WARNING "Failed call: ${QMAKE_EXECUTABLE} -query \"${qt_variable}\"") - message(FATAL_ERROR "QMake call failed: ${return_code}") - endif() -endfunction() diff --git a/cmake/Modules/ECMQueryQt.cmake b/cmake/Modules/ECMQueryQt.cmake new file mode 100644 index 00000000..98eb5008 --- /dev/null +++ b/cmake/Modules/ECMQueryQt.cmake @@ -0,0 +1,100 @@ +# SPDX-FileCopyrightText: 2014 Rohan Garg +# SPDX-FileCopyrightText: 2014 Alex Merry +# SPDX-FileCopyrightText: 2014-2016 Aleix Pol +# SPDX-FileCopyrightText: 2017 Friedrich W. H. Kossebau +# SPDX-FileCopyrightText: 2022 Ahmad Samir +# +# SPDX-License-Identifier: BSD-3-Clause +#[=======================================================================[.rst: +ECMQueryQt +--------------- +This module can be used to query the installation paths used by Qt. + +For Qt5 this uses ``qmake``, and for Qt6 this used ``qtpaths`` (the latter has built-in +support to query the paths of a target platform when cross-compiling). + +This module defines the following function: +:: + + ecm_query_qt( [TRY]) + +Passing ``TRY`` will result in the method not making the build fail if the executable +used for querying has not been found, but instead simply print a warning message and +return an empty string. + +Example usage: + +.. code-block:: cmake + + include(ECMQueryQt) + ecm_query_qt(bin_dir QT_INSTALL_BINS) + +If the call succeeds ``${bin_dir}`` will be set to ``/path/to/bin/dir`` (e.g. +``/usr/lib64/qt/bin/``). + +Since: 5.93 +#]=======================================================================] + +include(${CMAKE_CURRENT_LIST_DIR}/QtVersionOption.cmake) +include(CheckLanguage) +check_language(CXX) +if (CMAKE_CXX_COMPILER) + # Enable the CXX language to let CMake look for config files in library dirs. + # See: https://gitlab.kitware.com/cmake/cmake/-/issues/23266 + enable_language(CXX) +endif() + +if (QT_MAJOR_VERSION STREQUAL "5") + # QUIET to accommodate the TRY option + find_package(Qt${QT_MAJOR_VERSION}Core QUIET) + if(TARGET Qt5::qmake) + get_target_property(_qmake_executable_default Qt5::qmake LOCATION) + + set(QUERY_EXECUTABLE ${_qmake_executable_default} + CACHE FILEPATH "Location of the Qt5 qmake executable") + set(_exec_name_text "Qt5 qmake") + set(_cli_option "-query") + endif() +elseif(QT_MAJOR_VERSION STREQUAL "6") + # QUIET to accommodate the TRY option + find_package(Qt6 COMPONENTS CoreTools QUIET CONFIG) + if (TARGET Qt6::qtpaths) + get_target_property(_qtpaths_executable Qt6::qtpaths LOCATION) + + set(QUERY_EXECUTABLE ${_qtpaths_executable} + CACHE FILEPATH "Location of the Qt6 qtpaths executable") + set(_exec_name_text "Qt6 qtpaths") + set(_cli_option "--query") + endif() +endif() + +function(ecm_query_qt result_variable qt_variable) + set(options TRY) + set(oneValueArgs) + set(multiValueArgs) + + cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT QUERY_EXECUTABLE) + if(ARGS_TRY) + set(${result_variable} "" PARENT_SCOPE) + message(STATUS "No ${_exec_name_text} executable found. Can't check ${qt_variable}") + return() + else() + message(FATAL_ERROR "No ${_exec_name_text} executable found. Can't check ${qt_variable} as required") + endif() + endif() + execute_process( + COMMAND ${QUERY_EXECUTABLE} ${_cli_option} "${qt_variable}" + RESULT_VARIABLE return_code + OUTPUT_VARIABLE output + ) + if(return_code EQUAL 0) + string(STRIP "${output}" output) + file(TO_CMAKE_PATH "${output}" output_path) + set(${result_variable} "${output_path}" PARENT_SCOPE) + else() + message(WARNING "Failed call: ${_command} \"${qt_variable}\"") + message(FATAL_ERROR "${_exec_name_text} call failed: ${return_code}") + endif() +endfunction() diff --git a/cmake/Modules/QtVersionOption.cmake b/cmake/Modules/QtVersionOption.cmake new file mode 100644 index 00000000..ea37da22 --- /dev/null +++ b/cmake/Modules/QtVersionOption.cmake @@ -0,0 +1,36 @@ +# SPDX-FileCopyrightText: 2021 Volker Krause +# +# SPDX-License-Identifier: BSD-3-Clause + +#[=======================================================================[.rst: +QtVersionOption +--------------- + +Adds a build option to select the major Qt version if necessary, +that is, if the major Qt version has not yet been determined otherwise +(e.g. by a corresponding ``find_package()`` call). +This module is typically included by other modules requiring knowledge +about the major Qt version. + +``QT_MAJOR_VERSION`` is defined to either be "5" or "6". + +Since 5.82.0. +#]=======================================================================] + +if (DEFINED QT_MAJOR_VERSION) + return() +endif() + +if (TARGET Qt5::Core) + set(QT_MAJOR_VERSION 5) +elseif (TARGET Qt6::Core) + set(QT_MAJOR_VERSION 6) +else() + option(BUILD_WITH_QT6 "Build against Qt 6" OFF) + + if (BUILD_WITH_QT6) + set(QT_MAJOR_VERSION 6) + else() + set(QT_MAJOR_VERSION 5) + endif() +endif() From f197cdb935b0cfd9881fdc6860874cb8379d1238 Mon Sep 17 00:00:00 2001 From: zneix Date: Sun, 26 Jun 2022 19:27:49 +0200 Subject: [PATCH 091/168] Update deprecated annotations --- org.kde.KWallet.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/org.kde.KWallet.xml b/org.kde.KWallet.xml index 548c2f82..ec4bd6e9 100644 --- a/org.kde.KWallet.xml +++ b/org.kde.KWallet.xml @@ -156,7 +156,7 @@ - + @@ -164,7 +164,7 @@ - + @@ -172,7 +172,7 @@ - + From 97e01b47bdef2230dbcba9f4406f72d5fad58047 Mon Sep 17 00:00:00 2001 From: Igor Bugaev Date: Thu, 22 Jul 2021 10:38:59 +0300 Subject: [PATCH 092/168] Add Qt6 android support --- androidkeystore.cpp | 2 ++ androidkeystore_p.h | 11 +++++++++++ keychain_android.cpp | 6 ++++++ qtkeychain.pri | 4 +++- 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/androidkeystore.cpp b/androidkeystore.cpp index 7ef49e76..3f510f8e 100644 --- a/androidkeystore.cpp +++ b/androidkeystore.cpp @@ -4,7 +4,9 @@ #include "private/qjni_p.h" #endif +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) #include +#endif using namespace QKeychain; diff --git a/androidkeystore_p.h b/androidkeystore_p.h index 5c490791..925c741b 100644 --- a/androidkeystore_p.h +++ b/androidkeystore_p.h @@ -10,7 +10,18 @@ #ifndef QTKEYCHAIN_ANDROIDKEYSTORE_P_H #define QTKEYCHAIN_ANDROIDKEYSTORE_P_H +#include + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) #include +#else +#include +#include + +typedef QJniObject QAndroidJniObject; +typedef QJniEnvironment QAndroidJniEnvironment; + +#endif namespace QKeychain { diff --git a/keychain_android.cpp b/keychain_android.cpp index c1769133..0e9a0a4d 100644 --- a/keychain_android.cpp +++ b/keychain_android.cpp @@ -12,7 +12,9 @@ #include "androidkeystore_p.h" #include "plaintextstore_p.h" +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) #include +#endif using namespace QKeychain; @@ -101,7 +103,11 @@ void WritePasswordJobPrivate::scheduledStart() end.add(Calendar::YEAR, 99); const KeyPairGeneratorSpec spec = + #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) KeyPairGeneratorSpec::Builder(Context(QtAndroid::androidActivity())). + #else + KeyPairGeneratorSpec::Builder(QNativeInterface::QAndroidApplication::context()). + #endif setAlias(alias). setSubject(X500Principal(QStringLiteral("CN=QtKeychain, O=Android Authority"))). setSerialNumber(java::math::BigInteger::ONE). diff --git a/qtkeychain.pri b/qtkeychain.pri index f406e154..5877641c 100644 --- a/qtkeychain.pri +++ b/qtkeychain.pri @@ -58,7 +58,9 @@ unix:!android:!macx:!ios { } android { - QT += androidextras + lessThan(QT_MAJOR_VERSION, 6) { + QT += androidextras + } HEADERS += \ $$QTKEYCHAIN_PWD/androidkeystore_p.h \ From d174ff8288bfe8d8c99bff6707b78ebc3563a985 Mon Sep 17 00:00:00 2001 From: Igor Bugaev Date: Thu, 22 Jul 2021 10:40:14 +0300 Subject: [PATCH 093/168] Add QtQuick client example --- TestAppExample/.gitignore | 73 +++++++++++++++++ TestAppExample/TestAppExample.pro | 29 +++++++ TestAppExample/keychainclass.cpp | 61 ++++++++++++++ TestAppExample/keychainclass.h | 30 +++++++ TestAppExample/main.cpp | 29 +++++++ TestAppExample/main.qml | 131 ++++++++++++++++++++++++++++++ TestAppExample/qml.qrc | 5 ++ 7 files changed, 358 insertions(+) create mode 100644 TestAppExample/.gitignore create mode 100644 TestAppExample/TestAppExample.pro create mode 100644 TestAppExample/keychainclass.cpp create mode 100644 TestAppExample/keychainclass.h create mode 100644 TestAppExample/main.cpp create mode 100644 TestAppExample/main.qml create mode 100644 TestAppExample/qml.qrc diff --git a/TestAppExample/.gitignore b/TestAppExample/.gitignore new file mode 100644 index 00000000..fab7372d --- /dev/null +++ b/TestAppExample/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/TestAppExample/TestAppExample.pro b/TestAppExample/TestAppExample.pro new file mode 100644 index 00000000..d27f7e0b --- /dev/null +++ b/TestAppExample/TestAppExample.pro @@ -0,0 +1,29 @@ +QT += quick + +CONFIG += c++11 + +# You can make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include(../qt5keychain.pri) + +SOURCES += \ + keychainclass.cpp \ + main.cpp + +RESOURCES += qml.qrc + +# Additional import path used to resolve QML modules in Qt Creator's code model +QML_IMPORT_PATH = + +# Additional import path used to resolve QML modules just for Qt Quick Designer +QML_DESIGNER_IMPORT_PATH = + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +HEADERS += \ + keychainclass.h diff --git a/TestAppExample/keychainclass.cpp b/TestAppExample/keychainclass.cpp new file mode 100644 index 00000000..c087381d --- /dev/null +++ b/TestAppExample/keychainclass.cpp @@ -0,0 +1,61 @@ +#include + +#include "keychainclass.h" + +KeyChainClass::KeyChainClass(QObject* parent) : + QObject(parent), + m_readCredentialJob(QLatin1String("keychain.example.project.app")), + m_writeCredentialJob(QLatin1String("keychain.example.project.app")), + m_deleteCredentialJob(QLatin1String("keychain.example.project.app")) +{ + m_readCredentialJob.setAutoDelete(false); + m_writeCredentialJob.setAutoDelete(false); + m_deleteCredentialJob.setAutoDelete(false); +} + +void KeyChainClass::readKey(const QString &key) +{ + m_readCredentialJob.setKey(key); + + QObject::connect(&m_readCredentialJob, &QKeychain::ReadPasswordJob::finished, [=](){ + if (m_readCredentialJob.error()) { + emit error(QString("Write key failed: %1").arg(qPrintable(m_readCredentialJob.errorString()))); + return; + } + emit keyRestored(key, m_readCredentialJob.textData()); + }); + + m_readCredentialJob.start(); +} + +void KeyChainClass::writeKey(const QString &key, const QString &value) +{ + m_writeCredentialJob.setKey(key); + + QObject::connect(&m_writeCredentialJob, &QKeychain::WritePasswordJob::finished, [=](){ + if (m_writeCredentialJob.error()) { + emit error(QString("Write key failed: %1").arg(qPrintable(m_writeCredentialJob.errorString()))); + return; + } + + emit keyStored(key); + }); + + m_writeCredentialJob.setTextData(value); + m_writeCredentialJob.start(); +} + +void KeyChainClass::deleteKey(const QString &key) +{ + m_deleteCredentialJob.setKey(key); + + QObject::connect(&m_deleteCredentialJob, &QKeychain::DeletePasswordJob::finished, [=](){ + if (m_deleteCredentialJob.error()) { + emit error(QString("Delete key failed: %1").arg(qPrintable(m_deleteCredentialJob.errorString()))); + return; + } + emit keyDeleted(key); + }); + + m_deleteCredentialJob.start(); +} diff --git a/TestAppExample/keychainclass.h b/TestAppExample/keychainclass.h new file mode 100644 index 00000000..ea747d70 --- /dev/null +++ b/TestAppExample/keychainclass.h @@ -0,0 +1,30 @@ +#ifndef KEYCHAINCLASS_H +#define KEYCHAINCLASS_H + +#include + +#include + +class KeyChainClass: public QObject +{ + Q_OBJECT +public: + KeyChainClass(QObject* parent = nullptr); + + Q_INVOKABLE void readKey(const QString& key); + Q_INVOKABLE void writeKey(const QString& key, const QString& value); + Q_INVOKABLE void deleteKey(const QString& key); + +Q_SIGNALS: + void keyStored(const QString& key); + void keyRestored(const QString& key, const QString& value); + void keyDeleted(const QString& key); + void error(const QString& errorText); + +private: + QKeychain::ReadPasswordJob m_readCredentialJob; + QKeychain::WritePasswordJob m_writeCredentialJob; + QKeychain::DeletePasswordJob m_deleteCredentialJob; +}; + +#endif // KEYCHAINCLASS_H diff --git a/TestAppExample/main.cpp b/TestAppExample/main.cpp new file mode 100644 index 00000000..61367848 --- /dev/null +++ b/TestAppExample/main.cpp @@ -0,0 +1,29 @@ +#include +#include +#include + +#include "keychainclass.h" + +int main(int argc, char *argv[]) +{ +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); +#endif + + QGuiApplication app(argc, argv); + + QQmlApplicationEngine engine; + const QUrl url(QStringLiteral("qrc:/main.qml")); + QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, + &app, [url](QObject *obj, const QUrl &objUrl) { + if (!obj && url == objUrl) + QCoreApplication::exit(-1); + }, Qt::QueuedConnection); + + KeyChainClass keyChainClass; + + engine.rootContext()->setContextProperty("KeyChain", &keyChainClass); + engine.load(url); + + return app.exec(); +} diff --git a/TestAppExample/main.qml b/TestAppExample/main.qml new file mode 100644 index 00000000..4023aca6 --- /dev/null +++ b/TestAppExample/main.qml @@ -0,0 +1,131 @@ +import QtQuick 2.13 +import QtQuick.Window 2.13 +import QtQuick.Controls 2.12 + +Window { + id: root + + width: 640 + height: 480 + visible: true + + Column { + anchors { + fill: parent + margins: 50 + } + spacing: 20 + + Label { + text: 'Key name:' + font.pixelSize: 20 + } + + TextField { + id: keyNameTextField + + width: parent.width + height: 50 + + text: 'default key name' + } + + Label { + text: 'Key value:' + font.pixelSize: 20 + } + + TextField { + id: keyValueTextField + + width: parent.width + height: 50 + + text: 'some value' + } + + Label { + id: infoLabel + + width: parent.width + wrapMode: Text.Wrap + visible: false + + onVisibleChanged: if (visible) hideAnimation.start(); + + SequentialAnimation { + id: hideAnimation + + PauseAnimation { + duration: 10000 + } + + ScriptAction { + script: infoLabel.visible = false + } + } + + Component.onCompleted: { + KeyChain.keyStored.connect((key) => { + infoLabel.text = String("Key '%1' successfully stored").arg(key) + infoLabel.color = 'green' + infoLabel.visible = true + }) + + KeyChain.keyRestored.connect((key, value) => { + infoLabel.text = String("Key '%1' successfully restored with data '%2'").arg(key).arg(value) + infoLabel.color = 'green' + infoLabel.visible = true + }) + + KeyChain.keyDeleted.connect((key) => { + infoLabel.text = String("Key '%1' successfully deleted").arg(key) + infoLabel.color = 'green' + infoLabel.visible = true + }) + + KeyChain.error.connect((errorText) => { + infoLabel.text = errorText + infoLabel.color = 'red' + infoLabel.visible = true + }) + } + } + + Row { + width: parent.width + height: 50 + spacing: 20 + + Button { + width: 80 + height: parent.height + text: 'Store' + + onClicked: { + KeyChain.writeKey(keyNameTextField.text.trim(), keyValueTextField.text.trim()) + } + } + + Button { + width: 80 + height: parent.height + text: 'Restore' + + onClicked: { + KeyChain.readKey(keyNameTextField.text.trim()) + } + } + + Button { + width: 80 + height: parent.height + text: 'Delete' + onClicked: { + KeyChain.deleteKey(keyNameTextField.text.trim()) + } + } + } + + } +} diff --git a/TestAppExample/qml.qrc b/TestAppExample/qml.qrc new file mode 100644 index 00000000..5f6483ac --- /dev/null +++ b/TestAppExample/qml.qrc @@ -0,0 +1,5 @@ + + + main.qml + + From 86ef2468d4f16e3a67cd105e5f474ee7700dc867 Mon Sep 17 00:00:00 2001 From: Igor Bugaev Date: Tue, 5 Oct 2021 13:51:23 +0300 Subject: [PATCH 094/168] Update .gitignore, and fix TestApp compilation --- .gitignore | 75 ++++++++++++++++++++++++++++++- TestAppExample/TestAppExample.pro | 2 +- TestAppExample/keychainclass.cpp | 6 +-- 3 files changed, 77 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 3d2a92f8..870810bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,76 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + #CMake files CMakeCache.txt CMakeFiles @@ -37,7 +110,6 @@ testclient install_manifest.txt *.manifest *.lib -*.exe #Mac build files qtkeychain.xcodeproj @@ -45,6 +117,5 @@ qtkeychain.build #Temporary files *.sw? -*~ /build-*/ diff --git a/TestAppExample/TestAppExample.pro b/TestAppExample/TestAppExample.pro index d27f7e0b..3ba33eff 100644 --- a/TestAppExample/TestAppExample.pro +++ b/TestAppExample/TestAppExample.pro @@ -6,7 +6,7 @@ CONFIG += c++11 # In order to do so, uncomment the following line. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 -include(../qt5keychain.pri) +include(../qtkeychain.pri) SOURCES += \ keychainclass.cpp \ diff --git a/TestAppExample/keychainclass.cpp b/TestAppExample/keychainclass.cpp index c087381d..1f09cedb 100644 --- a/TestAppExample/keychainclass.cpp +++ b/TestAppExample/keychainclass.cpp @@ -19,7 +19,7 @@ void KeyChainClass::readKey(const QString &key) QObject::connect(&m_readCredentialJob, &QKeychain::ReadPasswordJob::finished, [=](){ if (m_readCredentialJob.error()) { - emit error(QString("Write key failed: %1").arg(qPrintable(m_readCredentialJob.errorString()))); + emit error(tr("Read key failed: %1").arg(qPrintable(m_readCredentialJob.errorString()))); return; } emit keyRestored(key, m_readCredentialJob.textData()); @@ -34,7 +34,7 @@ void KeyChainClass::writeKey(const QString &key, const QString &value) QObject::connect(&m_writeCredentialJob, &QKeychain::WritePasswordJob::finished, [=](){ if (m_writeCredentialJob.error()) { - emit error(QString("Write key failed: %1").arg(qPrintable(m_writeCredentialJob.errorString()))); + emit error(tr("Write key failed: %1").arg(qPrintable(m_writeCredentialJob.errorString()))); return; } @@ -51,7 +51,7 @@ void KeyChainClass::deleteKey(const QString &key) QObject::connect(&m_deleteCredentialJob, &QKeychain::DeletePasswordJob::finished, [=](){ if (m_deleteCredentialJob.error()) { - emit error(QString("Delete key failed: %1").arg(qPrintable(m_deleteCredentialJob.errorString()))); + emit error(tr("Delete key failed: %1").arg(qPrintable(m_deleteCredentialJob.errorString()))); return; } emit keyDeleted(key); From 8506d57e5df5ae2419a711f93bf93793776e5a11 Mon Sep 17 00:00:00 2001 From: Igor Bugaev Date: Wed, 14 Sep 2022 13:21:31 +0200 Subject: [PATCH 095/168] Remove AndroidExtras CMakeLists.txt --- CMakeLists.txt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9555955c..ea5d69fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,11 +94,6 @@ else() find_package(Qt6 COMPONENTS Core REQUIRED) set(QTKEYCHAIN_VERSION_INFIX 6) - if(ANDROID) - find_package(Qt6 COMPONENTS AndroidExtras REQUIRED) - include_directories(${Qt6AndroidExtras_INCLUDE_DIRS}) - set(QTANDROIDEXTRAS_LIBRARIES ${Qt6AndroidExtras_LIBRARIES}) - endif() if(UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU) find_package(Qt6 COMPONENTS DBus REQUIRED) From c6f0b66318f8da6917fb4681103f7303b1836194 Mon Sep 17 00:00:00 2001 From: Volker Krause Date: Fri, 28 Oct 2022 20:15:21 +0200 Subject: [PATCH 096/168] Compile with Qt 6.4 The return type of QAndroidApplication::context() changed. --- keychain_android.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/keychain_android.cpp b/keychain_android.cpp index 0e9a0a4d..46939353 100644 --- a/keychain_android.cpp +++ b/keychain_android.cpp @@ -105,8 +105,10 @@ void WritePasswordJobPrivate::scheduledStart() const KeyPairGeneratorSpec spec = #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) KeyPairGeneratorSpec::Builder(Context(QtAndroid::androidActivity())). - #else + #elif QT_VERSION < QT_VERSION_CHECK(6, 4, 0) KeyPairGeneratorSpec::Builder(QNativeInterface::QAndroidApplication::context()). + #else + KeyPairGeneratorSpec::Builder((jobject)QNativeInterface::QAndroidApplication::context()). #endif setAlias(alias). setSubject(X500Principal(QStringLiteral("CN=QtKeychain, O=Android Authority"))). From 841f31c7ca177e45647fd705200d7fcbeee056e5 Mon Sep 17 00:00:00 2001 From: Marius P Date: Mon, 30 Jan 2023 18:47:19 +0200 Subject: [PATCH 097/168] .gitignore ignore compile_commands.json The file compile_commands.json is generated by default when building using kdesrc-build. kdesrc-build passes to CMake by default the option "-DCMAKE_EXPORT_COMPILE_COMMANDS:BOOL=ON". If the file ignore compile_commands.json is not ignored, when building using kdesrc-build, there are unstaged changes in the local git clone/working directory. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 870810bd..f3cc1d94 100644 --- a/.gitignore +++ b/.gitignore @@ -119,3 +119,4 @@ qtkeychain.build *.sw? /build-*/ +/compile_commands.json From f6247fff2a97b5036f7a7a53eefc314f41916126 Mon Sep 17 00:00:00 2001 From: Julien Wadel Date: Fri, 16 Dec 2022 12:47:45 +0100 Subject: [PATCH 098/168] Fix the deploy issue from windeployqt by changing target name. --- CMakeLists.txt | 30 ++++++++++++++++-------------- QtKeychainConfig.cmake.in | 6 +++--- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ea5d69fa..84873140 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ option(BUILD_WITH_QT6 "Build qtkeychain with Qt 6" OFF) option(BUILD_TEST_APPLICATION "Build test application" OFF) option(BUILD_TRANSLATIONS "Build translations" ON) option(BUILD_SHARED_LIBS "Build dynamic library" ON) +option(QTKEYCHAIN_TARGET_NAME "Custom output name of QtKeychain" "") if(QTKEYCHAIN_STATIC) set(BUILD_SHARED_LIBS OFF) message(WARNING "QTKEYCHAIN_STATIC is deprecated. Use BUILD_SHARED_LIBS=OFF instead.") @@ -199,8 +200,9 @@ set(qtkeychain_TR_FILES translations/qtkeychain_ru.ts translations/qtkeychain_zh.ts ) - -set(QTKEYCHAIN_TARGET_NAME qt${QTKEYCHAIN_VERSION_INFIX}keychain) +if(NOT QTKEYCHAIN_TARGET_NAME) + set(QTKEYCHAIN_TARGET_NAME Qt${QTKEYCHAIN_VERSION_INFIX}Keychain) +endif() add_library(${QTKEYCHAIN_TARGET_NAME} ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES} ${qtkeychain_QM_FILES}) if(WIN32) set_target_properties( ${QTKEYCHAIN_TARGET_NAME} PROPERTIES DEBUG_POSTFIX "d" ) @@ -231,7 +233,7 @@ if ( BUILD_TRANSLATIONS ) CACHE PATH "The location of the QtKeychain translations" FORCE) else() set(QTKEYCHAIN_TRANSLATIONS_DIR - ${CMAKE_INSTALL_DATADIR}/qt${QTKEYCHAIN_VERSION_INFIX}keychain/translations + ${CMAKE_INSTALL_DATADIR}/${QTKEYCHAIN_TARGET_NAME}/translations CACHE PATH "The location of the QtKeychain translations" ) endif() @@ -263,11 +265,11 @@ if (NOT APPLE) endif() install(FILES keychain.h ${CMAKE_CURRENT_BINARY_DIR}/qkeychain_export.h - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/qt${QTKEYCHAIN_VERSION_INFIX}keychain/ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${QTKEYCHAIN_TARGET_NAME}/ ) install(TARGETS ${QTKEYCHAIN_TARGET_NAME} - EXPORT Qt${QTKEYCHAIN_VERSION_INFIX}KeychainLibraryDepends + EXPORT ${QTKEYCHAIN_TARGET_NAME}LibraryDepends RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} @@ -284,17 +286,17 @@ endif() ### configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/QtKeychainConfig.cmake.in" - "${CMAKE_CURRENT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfig.cmake" - INSTALL_DESTINATION Qt${QTKEYCHAIN_VERSION_INFIX}Keychain) + "${CMAKE_CURRENT_BINARY_DIR}/${QTKEYCHAIN_TARGET_NAME}Config.cmake" + INSTALL_DESTINATION ${QTKEYCHAIN_TARGET_NAME}) ecm_setup_version("${QTKEYCHAIN_VERSION}" VARIABLE_PREFIX SNORE - PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfigVersion.cmake" + PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/${QTKEYCHAIN_TARGET_NAME}ConfigVersion.cmake" SOVERSION ${QTKEYCHAIN_VERSION}) if(UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU) set(PRI_EXTRA_DEPS "dbus") endif() -ecm_generate_pri_file(BASE_NAME Qt${QTKEYCHAIN_VERSION_INFIX}Keychain +ecm_generate_pri_file(BASE_NAME ${QTKEYCHAIN_TARGET_NAME} LIB_NAME ${QTKEYCHAIN_TARGET_NAME} DEPS "core ${PRI_EXTRA_DEPS}" INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR} @@ -303,12 +305,12 @@ ecm_generate_pri_file(BASE_NAME Qt${QTKEYCHAIN_VERSION_INFIX}Keychain install(FILES ${pri_filename} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) -install(EXPORT Qt${QTKEYCHAIN_VERSION_INFIX}KeychainLibraryDepends - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Qt${QTKEYCHAIN_VERSION_INFIX}Keychain" +install(EXPORT ${QTKEYCHAIN_TARGET_NAME}LibraryDepends + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${QTKEYCHAIN_TARGET_NAME}" ) -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfig.cmake - ${CMAKE_CURRENT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfigVersion.cmake - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Qt${QTKEYCHAIN_VERSION_INFIX}Keychain +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${QTKEYCHAIN_TARGET_NAME}Config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/${QTKEYCHAIN_TARGET_NAME}ConfigVersion.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${QTKEYCHAIN_TARGET_NAME} ) diff --git a/QtKeychainConfig.cmake.in b/QtKeychainConfig.cmake.in index 7addba9f..d212634f 100644 --- a/QtKeychainConfig.cmake.in +++ b/QtKeychainConfig.cmake.in @@ -8,7 +8,7 @@ @PACKAGE_INIT@ -include("${CMAKE_CURRENT_LIST_DIR}/Qt@QTKEYCHAIN_VERSION_INFIX@KeychainLibraryDepends.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/@QTKEYCHAIN_TARGET_NAME@LibraryDepends.cmake") include(CMakeFindDependencyMacro) @@ -22,7 +22,7 @@ set(QTKEYCHAIN_LIBRARIES "@QTKEYCHAIN_TARGET_NAME@") get_target_property(QTKEYCHAIN_INCLUDE_DIRS "@QTKEYCHAIN_TARGET_NAME@" INTERFACE_INCLUDE_DIRECTORIES) if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.18.0) - add_library(Qt@QTKEYCHAIN_VERSION_INFIX@Keychain::Qt@QTKEYCHAIN_VERSION_INFIX@Keychain ALIAS qt@QTKEYCHAIN_VERSION_INFIX@keychain) + add_library(@QTKEYCHAIN_TARGET_NAME@::@QTKEYCHAIN_TARGET_NAME@ ALIAS @QTKEYCHAIN_TARGET_NAME@) endif() -check_required_components(Qt@QTKEYCHAIN_VERSION_INFIX@Keychain) +check_required_components(@QTKEYCHAIN_TARGET_NAME@) From ece1f1e8cbfa144917eda299f2a5d9eb27d8225b Mon Sep 17 00:00:00 2001 From: Nicolas Fella Date: Fri, 24 Feb 2023 21:31:21 +0100 Subject: [PATCH 099/168] Revert "Fix the deploy issue from windeployqt by changing target name." This changes the name of header install location by default, breaking consumer builds This reverts commit f6247fff2a97b5036f7a7a53eefc314f41916126. --- CMakeLists.txt | 30 ++++++++++++++---------------- QtKeychainConfig.cmake.in | 6 +++--- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 84873140..ea5d69fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,7 +24,6 @@ option(BUILD_WITH_QT6 "Build qtkeychain with Qt 6" OFF) option(BUILD_TEST_APPLICATION "Build test application" OFF) option(BUILD_TRANSLATIONS "Build translations" ON) option(BUILD_SHARED_LIBS "Build dynamic library" ON) -option(QTKEYCHAIN_TARGET_NAME "Custom output name of QtKeychain" "") if(QTKEYCHAIN_STATIC) set(BUILD_SHARED_LIBS OFF) message(WARNING "QTKEYCHAIN_STATIC is deprecated. Use BUILD_SHARED_LIBS=OFF instead.") @@ -200,9 +199,8 @@ set(qtkeychain_TR_FILES translations/qtkeychain_ru.ts translations/qtkeychain_zh.ts ) -if(NOT QTKEYCHAIN_TARGET_NAME) - set(QTKEYCHAIN_TARGET_NAME Qt${QTKEYCHAIN_VERSION_INFIX}Keychain) -endif() + +set(QTKEYCHAIN_TARGET_NAME qt${QTKEYCHAIN_VERSION_INFIX}keychain) add_library(${QTKEYCHAIN_TARGET_NAME} ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES} ${qtkeychain_QM_FILES}) if(WIN32) set_target_properties( ${QTKEYCHAIN_TARGET_NAME} PROPERTIES DEBUG_POSTFIX "d" ) @@ -233,7 +231,7 @@ if ( BUILD_TRANSLATIONS ) CACHE PATH "The location of the QtKeychain translations" FORCE) else() set(QTKEYCHAIN_TRANSLATIONS_DIR - ${CMAKE_INSTALL_DATADIR}/${QTKEYCHAIN_TARGET_NAME}/translations + ${CMAKE_INSTALL_DATADIR}/qt${QTKEYCHAIN_VERSION_INFIX}keychain/translations CACHE PATH "The location of the QtKeychain translations" ) endif() @@ -265,11 +263,11 @@ if (NOT APPLE) endif() install(FILES keychain.h ${CMAKE_CURRENT_BINARY_DIR}/qkeychain_export.h - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${QTKEYCHAIN_TARGET_NAME}/ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/qt${QTKEYCHAIN_VERSION_INFIX}keychain/ ) install(TARGETS ${QTKEYCHAIN_TARGET_NAME} - EXPORT ${QTKEYCHAIN_TARGET_NAME}LibraryDepends + EXPORT Qt${QTKEYCHAIN_VERSION_INFIX}KeychainLibraryDepends RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} @@ -286,17 +284,17 @@ endif() ### configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/QtKeychainConfig.cmake.in" - "${CMAKE_CURRENT_BINARY_DIR}/${QTKEYCHAIN_TARGET_NAME}Config.cmake" - INSTALL_DESTINATION ${QTKEYCHAIN_TARGET_NAME}) + "${CMAKE_CURRENT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfig.cmake" + INSTALL_DESTINATION Qt${QTKEYCHAIN_VERSION_INFIX}Keychain) ecm_setup_version("${QTKEYCHAIN_VERSION}" VARIABLE_PREFIX SNORE - PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/${QTKEYCHAIN_TARGET_NAME}ConfigVersion.cmake" + PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfigVersion.cmake" SOVERSION ${QTKEYCHAIN_VERSION}) if(UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU) set(PRI_EXTRA_DEPS "dbus") endif() -ecm_generate_pri_file(BASE_NAME ${QTKEYCHAIN_TARGET_NAME} +ecm_generate_pri_file(BASE_NAME Qt${QTKEYCHAIN_VERSION_INFIX}Keychain LIB_NAME ${QTKEYCHAIN_TARGET_NAME} DEPS "core ${PRI_EXTRA_DEPS}" INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR} @@ -305,12 +303,12 @@ ecm_generate_pri_file(BASE_NAME ${QTKEYCHAIN_TARGET_NAME} install(FILES ${pri_filename} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) -install(EXPORT ${QTKEYCHAIN_TARGET_NAME}LibraryDepends - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${QTKEYCHAIN_TARGET_NAME}" +install(EXPORT Qt${QTKEYCHAIN_VERSION_INFIX}KeychainLibraryDepends + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Qt${QTKEYCHAIN_VERSION_INFIX}Keychain" ) -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${QTKEYCHAIN_TARGET_NAME}Config.cmake - ${CMAKE_CURRENT_BINARY_DIR}/${QTKEYCHAIN_TARGET_NAME}ConfigVersion.cmake - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${QTKEYCHAIN_TARGET_NAME} +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfig.cmake + ${CMAKE_CURRENT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfigVersion.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Qt${QTKEYCHAIN_VERSION_INFIX}Keychain ) diff --git a/QtKeychainConfig.cmake.in b/QtKeychainConfig.cmake.in index d212634f..7addba9f 100644 --- a/QtKeychainConfig.cmake.in +++ b/QtKeychainConfig.cmake.in @@ -8,7 +8,7 @@ @PACKAGE_INIT@ -include("${CMAKE_CURRENT_LIST_DIR}/@QTKEYCHAIN_TARGET_NAME@LibraryDepends.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/Qt@QTKEYCHAIN_VERSION_INFIX@KeychainLibraryDepends.cmake") include(CMakeFindDependencyMacro) @@ -22,7 +22,7 @@ set(QTKEYCHAIN_LIBRARIES "@QTKEYCHAIN_TARGET_NAME@") get_target_property(QTKEYCHAIN_INCLUDE_DIRS "@QTKEYCHAIN_TARGET_NAME@" INTERFACE_INCLUDE_DIRECTORIES) if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.18.0) - add_library(@QTKEYCHAIN_TARGET_NAME@::@QTKEYCHAIN_TARGET_NAME@ ALIAS @QTKEYCHAIN_TARGET_NAME@) + add_library(Qt@QTKEYCHAIN_VERSION_INFIX@Keychain::Qt@QTKEYCHAIN_VERSION_INFIX@Keychain ALIAS qt@QTKEYCHAIN_VERSION_INFIX@keychain) endif() -check_required_components(@QTKEYCHAIN_TARGET_NAME@) +check_required_components(Qt@QTKEYCHAIN_VERSION_INFIX@Keychain) From d9a724c0e8411e42bc88bb46b75ab0ee03016cdc Mon Sep 17 00:00:00 2001 From: Heimen Stoffels Date: Mon, 6 Mar 2023 14:03:43 +0100 Subject: [PATCH 100/168] Added Dutch translation --- translations/qtkeychain_nl.ts | 320 ++++++++++++++++++++++++++++++++++ 1 file changed, 320 insertions(+) create mode 100644 translations/qtkeychain_nl.ts diff --git a/translations/qtkeychain_nl.ts b/translations/qtkeychain_nl.ts new file mode 100644 index 00000000..7033a984 --- /dev/null +++ b/translations/qtkeychain_nl.ts @@ -0,0 +1,320 @@ + + + + + KeyChainClass + + + Read key failed: %1 + De sleutel kan niet worden ingelezen: %1 + + + + Write key failed: %1 + De sleutel kan niet worden weggeschreven: %1 + + + + Delete key failed: %1 + De sleutel kan niet worden verwijderd: %1 + + + + QKeychain::DeletePasswordJobPrivate + + + Could not open keystore + De sleutelbos kan niet worden geopend + + + + Could not remove private key from keystore + De privésleutel kan niet worden verwijderd uit de sleutelbos + + + + Password not found + Het wachtwoord is niet gevonden + + + + + Unknown error + Onbekende foutmelding + + + + Could not open wallet: %1; %2 + De sleutelbos kan niet worden geopend: %1; %2 + + + + Password entry not found + Het wachtwoord is niet gevonden + + + + Could not decrypt data + De gegevens kunnen niet worden ongrendeld + + + + QKeychain::JobPrivate + + + Unknown error + Onbekende foutmelding + + + + Access to keychain denied + U heeft geen toegang tot de sleutelbos + + + + QKeychain::PlainTextStore + + + Could not store data in settings: access error + De gegevens kunnen niet worden opgeslagen in de instellingen: toegangsfout + + + + Could not store data in settings: format error + De gegevens kunnen niet worden opgeslagen in de instellingen: opmaakfout + + + + Could not delete data from settings: access error + De gegevens kunnen niet worden verwijderd uit de instellingen: toegangsfout + + + + Could not delete data from settings: format error + De gegevens kunnen niet worden verwijderd uit de instellingen: opmaakfout + + + + Entry not found + Het item is niet gevonden + + + + QKeychain::ReadPasswordJobPrivate + + + + Entry not found + Het item is niet gevonden + + + + Could not open keystore + De sleutelbos kan niet worden geopend + + + + Could not retrieve private key from keystore + De privésleutel kan niet worden verwijderd uit de sleutelbos + + + + Could not create decryption cipher + De ontgrendelcode kan niet worden aangemaakt + + + + Password not found + Het wachtwoord is niet gevonden + + + + D-Bus is not running + D-Bus is niet actief + + + + + Unknown error + Onbekende foutmelding + + + + No keychain service available + De sleutelbosdienst is niet beschikbaar + + + + Could not open wallet: %1; %2 + De sleutelbos kan niet worden geopend: %1; %2 + + + + Access to keychain denied + U heeft geen toegang tot de sleutelbos + + + + Could not determine data type: %1; %2 + De gegevensdrager kan niet worden bepaald: %1; %2 + + + + Unsupported entry type 'Map' + Niet-ondersteund itemtype ‘Map’ + + + + Unknown kwallet entry type '%1' + Onbekend KWallet-item van type ‘%1’ + + + + Password entry not found + Het wachtwoord is niet gevonden + + + + + Could not decrypt data + De gegevens kunnen niet worden ongrendeld + + + + QKeychain::WritePasswordJobPrivate + + + Could not open keystore + De sleutelbos kan niet worden geopend + + + + Could not create private key generator + De privésleutelgenerator kan niet worden gestart + + + + Could not generate new private key + Er kan geen nieuwe privésleutel worden gegenereerd + + + + Could not retrieve private key from keystore + De privésleutel kan niet worden opgehaald uit de sleutelbos + + + + Could not create encryption cipher + De vergrendelcode kan niet worden aangemaakt + + + + Could not encrypt data + De gegevens kunnen niet worden ontgrendeld + + + + Password not found + Het wachtwoord is niet gevonden + + + + D-Bus is not running + D-Bus is niet actief + + + + + Unknown error + Onbekende foutmelding + + + + Could not open wallet: %1; %2 + De sleutelbos kan niet worden geopend: %1; %2 + + + + Credential size exceeds maximum size of %1 + De omvang overschrijdt de maximumomvang van %1 + + + + Credential key exceeds maximum size of %1 + De sleutel overschrijdt de maximumomvang van %1 + + + + Writing credentials failed: Win32 error code %1 + De gegevens kunnen niet worden weggeschreven: Win32-foutcode %1 + + + + Encryption failed + Het ontgrendelen is mislukt + + + + QObject + + + error 0x%1: %2 + foutmelding 0x%1: %2 + + + + Access to keychain denied + U heeft geen toegang tot de sleutelbos + + + + No keyring daemon + De sleutelbosdienst is niet actief + + + + Already unlocked + De sleutelbos is reeds ontgrendeld + + + + No such keyring + De sleutelbos bestaat niet + + + + Bad arguments + Onjuiste opties + + + + I/O error + I/O-fout + + + + Cancelled + Geannuleerd + + + + Keyring already exists + De sleutelbos bestaat reeds + + + + No match + Er zijn geen overeenkomsten + + + + Unknown error + Onbekende foutmelding + + + + Entry not found + Het item is niet gevonden + + + From 06195acf9abd052109b3c29039d3b2c4523a2a80 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Thu, 9 Feb 2023 20:00:21 +0100 Subject: [PATCH 101/168] Fix potential freezing with Apple keychain SecItemCopyMatching, SecItemUpdate, and SecItemDelete all block the thread they are run on. This means that on Apple devices there is a possibility for QtKeychain to cause a freeze on the main thread. Per Apple's documentation, this PR wraps the work done on keychain retrieval with `dispatch_async` on `DISPATCH_QUEUE_PRIORITY_BACKGROUND` Documentation links: https://developer.apple.com/documentation/security/1398306-secitemcopymatching?language=objc https://developer.apple.com/documentation/security/1393617-secitemupdate?language=objc https://developer.apple.com/documentation/security/1395547-secitemdelete?language=objc --- keychain_apple.mm | 90 +++++++++++++++++++++++++---------------------- 1 file changed, 48 insertions(+), 42 deletions(-) diff --git a/keychain_apple.mm b/keychain_apple.mm index d2c763af..001cde2c 100644 --- a/keychain_apple.mm +++ b/keychain_apple.mm @@ -72,24 +72,26 @@ static ErrorDescription fromStatus(OSStatus status) (__bridge id) kSecReturnData: @YES, }; - CFTypeRef dataRef = nil; - const OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef) query, &dataRef); + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ + CFTypeRef dataRef = nil; + const OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef) query, &dataRef); - data.clear(); - mode = Binary; + data.clear(); + mode = Binary; - if (status == errSecSuccess) { - if (dataRef) - data = QByteArray::fromCFData((CFDataRef) dataRef); + if (status == errSecSuccess) { + if (dataRef) + data = QByteArray::fromCFData((CFDataRef) dataRef); - q->emitFinished(); - } else { - const ErrorDescription error = ErrorDescription::fromStatus(status); - q->emitFinishedWithError(error.code, Job::tr("Could not retrieve private key from keystore: %1").arg(error.message)); - } + q->emitFinished(); + } else { + const ErrorDescription error = ErrorDescription::fromStatus(status); + q->emitFinishedWithError(error.code, Job::tr("Could not retrieve private key from keystore: %1").arg(error.message)); + } - if (dataRef) - CFRelease(dataRef); + if (dataRef) + CFRelease(dataRef); + }); } void WritePasswordJobPrivate::scheduledStart() @@ -100,31 +102,33 @@ static ErrorDescription fromStatus(OSStatus status) (__bridge id) kSecAttrAccount: (__bridge NSString *) key.toCFString(), }; - OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef) query, nil); + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ + OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef) query, nil); - if (status == errSecSuccess) { - NSDictionary *const update = @{ - (__bridge id) kSecValueData: (__bridge NSData *) data.toCFData(), - }; + if (status == errSecSuccess) { + NSDictionary *const update = @{ + (__bridge id) kSecValueData: (__bridge NSData *) data.toCFData(), + }; - status = SecItemUpdate((__bridge CFDictionaryRef) query, (__bridge CFDictionaryRef) update); - } else { - NSDictionary *const insert = @{ - (__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword, - (__bridge id) kSecAttrService: (__bridge NSString *) service.toCFString(), - (__bridge id) kSecAttrAccount: (__bridge NSString *) key.toCFString(), - (__bridge id) kSecValueData: (__bridge NSData *) data.toCFData(), - }; + status = SecItemUpdate((__bridge CFDictionaryRef) query, (__bridge CFDictionaryRef) update); + } else { + NSDictionary *const insert = @{ + (__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword, + (__bridge id) kSecAttrService: (__bridge NSString *) service.toCFString(), + (__bridge id) kSecAttrAccount: (__bridge NSString *) key.toCFString(), + (__bridge id) kSecValueData: (__bridge NSData *) data.toCFData(), + }; - status = SecItemAdd((__bridge CFDictionaryRef) insert, nil); - } + status = SecItemAdd((__bridge CFDictionaryRef) insert, nil); + } - if (status == errSecSuccess) { - q->emitFinished(); - } else { - const ErrorDescription error = ErrorDescription::fromStatus(status); - q->emitFinishedWithError(error.code, tr("Could not store data in settings: %1").arg(error.message)); - } + if (status == errSecSuccess) { + q->emitFinished(); + } else { + const ErrorDescription error = ErrorDescription::fromStatus(status); + q->emitFinishedWithError(error.code, tr("Could not store data in settings: %1").arg(error.message)); + } + }); } void DeletePasswordJobPrivate::scheduledStart() @@ -135,12 +139,14 @@ static ErrorDescription fromStatus(OSStatus status) (__bridge id) kSecAttrAccount: (__bridge NSString *) key.toCFString(), }; - const OSStatus status = SecItemDelete((__bridge CFDictionaryRef) query); + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ + const OSStatus status = SecItemDelete((__bridge CFDictionaryRef) query); - if (status == errSecSuccess) { - q->emitFinished(); - } else { - const ErrorDescription error = ErrorDescription::fromStatus(status); - q->emitFinishedWithError(error.code, Job::tr("Could not remove private key from keystore: %1").arg(error.message)); - } + if (status == errSecSuccess) { + q->emitFinished(); + } else { + const ErrorDescription error = ErrorDescription::fromStatus(status); + q->emitFinishedWithError(error.code, Job::tr("Could not remove private key from keystore: %1").arg(error.message)); + } + }); } From bc2ff12023e42d074da8ded599852a87f8f5eeb1 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Thu, 23 Feb 2023 17:33:01 +0100 Subject: [PATCH 102/168] Add apple keychain interface between obj-c and qt Signed-off-by: Claudio Cambra --- keychain_apple.mm | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/keychain_apple.mm b/keychain_apple.mm index 001cde2c..5f22ea38 100644 --- a/keychain_apple.mm +++ b/keychain_apple.mm @@ -14,6 +14,28 @@ using namespace QKeychain; +@interface AppleKeychainInterface : NSObject + +@property (readonly) JobPrivate *ownerJob; + +- (instancetype)initWithOwner:(JobPrivate *)ownerJob; + +@end + +@implementation AppleKeychainInterface + +- (instancetype)initWithOwner:(JobPrivate *)ownerJob +{ + self = [super init]; + if (self) { + _ownerJob = ownerJob; + } + return self; +} + +@end + + struct ErrorDescription { QKeychain::Error code; From bccf136c262e6a5593b68f6a24d2de85c2951301 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Thu, 23 Feb 2023 18:24:09 +0100 Subject: [PATCH 103/168] Add ability for AppleKeychainInterface to handle task error notifications Signed-off-by: Claudio Cambra --- keychain_apple.mm | 77 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 57 insertions(+), 20 deletions(-) diff --git a/keychain_apple.mm b/keychain_apple.mm index 5f22ea38..d4719f7c 100644 --- a/keychain_apple.mm +++ b/keychain_apple.mm @@ -14,27 +14,11 @@ using namespace QKeychain; -@interface AppleKeychainInterface : NSObject - -@property (readonly) JobPrivate *ownerJob; - -- (instancetype)initWithOwner:(JobPrivate *)ownerJob; - -@end - -@implementation AppleKeychainInterface - -- (instancetype)initWithOwner:(JobPrivate *)ownerJob -{ - self = [super init]; - if (self) { - _ownerJob = ownerJob; - } - return self; -} - -@end +NSString *AppleKeychainTaskFinishedWithError = @"AppleKeychainTaskFinishedWithError"; +NSString *KeychainNotificationUserInfoStatusKey = @"status"; +NSString *KeychainNotificationUserInfoDataKey = @"data"; +NSString *KeychainNotificationUserInfoDescriptiveErrorKey = @"descriptiveError"; struct ErrorDescription { @@ -85,6 +69,59 @@ static ErrorDescription fromStatus(OSStatus status) } }; +@interface AppleKeychainInterface : NSObject + +@property (readonly) Job *job; + +- (instancetype)initWithOwner:(Job *)job; + +@end + +@implementation AppleKeychainInterface + +- (instancetype)initWithOwner:(Job *)job +{ + self = [super init]; + if (self) { + _job = job; + + NSNotificationCenter * const notificationCenter = NSNotificationCenter.defaultCenter; + [notificationCenter addObserver:self + selector:@selector(keychainTaskFinishedWithError:) + name:AppleKeychainTaskFinishedWithError + object:nil]; + } + return self; +} + +- (void)dealloc +{ + [NSNotificationCenter.defaultCenter removeObserver:self]; + [super dealloc]; +} + +- (void)keychainTaskFinishedWithError:(NSNotification *)notification +{ + NSParameterAssert(notification); + NSDictionary * const userInfo = notification.userInfo; + NSAssert(userInfo, @"Keychain task finished with error notification should contain nonnull user info dictionary"); + + NSNumber * const statusNumber = (NSNumber *)[userInfo objectForKey:KeychainNotificationUserInfoStatusKey]; + NSAssert(statusNumber, @"Keychain task notification user info dict should contain valid status number"); + const OSStatus status = statusNumber.intValue; + + NSString * const descriptiveMessage = (NSString *)[userInfo objectForKey:KeychainNotificationUserInfoDescriptiveErrorKey]; + const auto localisedDescriptiveMessage = Job::tr([descriptiveMessage UTF8String]); + + const ErrorDescription error = ErrorDescription::fromStatus(status); + const auto fullMessage = localisedDescriptiveMessage.isEmpty() ? error.message : QStringLiteral("%1: %2").arg(localisedDescriptiveMessage, error.message); + + _job->emitFinishedWithError(error.code, fullMessage); +} + +@end + + void ReadPasswordJobPrivate::scheduledStart() { NSDictionary *const query = @{ From ad753ec780d21da8e1bae1b10c54806bfcd94cd7 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Thu, 23 Feb 2023 18:43:29 +0100 Subject: [PATCH 104/168] Handle read task finished notifications in AppleKeychainInterface Signed-off-by: Claudio Cambra --- keychain_apple.mm | 46 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/keychain_apple.mm b/keychain_apple.mm index d4719f7c..0f0900d0 100644 --- a/keychain_apple.mm +++ b/keychain_apple.mm @@ -14,11 +14,13 @@ using namespace QKeychain; -NSString *AppleKeychainTaskFinishedWithError = @"AppleKeychainTaskFinishedWithError"; +NSString * const AppleKeychainTaskFinished = @"AppleKeychainTaskFinished"; +NSString * const AppleKeychainReadTaskFinished = @"AppleKeychainReadTaskFinished"; +NSString * const AppleKeychainTaskFinishedWithError = @"AppleKeychainTaskFinishedWithError"; -NSString *KeychainNotificationUserInfoStatusKey = @"status"; -NSString *KeychainNotificationUserInfoDataKey = @"data"; -NSString *KeychainNotificationUserInfoDescriptiveErrorKey = @"descriptiveError"; +NSString * const KeychainNotificationUserInfoStatusKey = @"status"; +NSString * const KeychainNotificationUserInfoDataKey = @"data"; +NSString * const KeychainNotificationUserInfoDescriptiveErrorKey = @"descriptiveError"; struct ErrorDescription { @@ -72,20 +74,30 @@ static ErrorDescription fromStatus(OSStatus status) @interface AppleKeychainInterface : NSObject @property (readonly) Job *job; +@property (readonly) JobPrivate *privateJob; -- (instancetype)initWithOwner:(Job *)job; +- (instancetype)initWithJob:(Job *)job andPrivateJob:(JobPrivate *)privateJob; @end @implementation AppleKeychainInterface -- (instancetype)initWithOwner:(Job *)job +- (instancetype)initWithJob:(Job *)job andPrivateJob:(JobPrivate *)privateJob { self = [super init]; if (self) { _job = job; + _privateJob = privateJob; NSNotificationCenter * const notificationCenter = NSNotificationCenter.defaultCenter; + [notificationCenter addObserver:self + selector:@selector(keychainTaskFinished:) + name:AppleKeychainTaskFinished + object:nil]; + [notificationCenter addObserver:self + selector:@selector(keychainReadTaskFinished:) + name:AppleKeychainReadTaskFinished + object:nil]; [notificationCenter addObserver:self selector:@selector(keychainTaskFinishedWithError:) name:AppleKeychainTaskFinishedWithError @@ -100,6 +112,28 @@ - (void)dealloc [super dealloc]; } +- (void)keychainTaskFinished:(NSNotification *)notification +{ + _job->emitFinished(); +} + +- (void)keychainReadTaskFinished:(NSNotification *)notification +{ + NSParameterAssert(notification); + NSDictionary * const userInfo = notification.userInfo; + NSAssert(userInfo, @"Keychain task finished with error notification should contain nonnull user info dictionary"); + + _privateJob->data.clear(); + _privateJob->mode = JobPrivate::Binary; + + NSData * const retrievedData = (NSData *)[userInfo objectForKey:KeychainNotificationUserInfoDataKey]; + if (retrievedData != nil) { + _privateJob->data = QByteArray::fromNSData(retrievedData); + } + + _job->emitFinished(); +} + - (void)keychainTaskFinishedWithError:(NSNotification *)notification { NSParameterAssert(notification); From 67478a5cf57484a1a131eaf8005bd3f46d91bd70 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Thu, 23 Feb 2023 19:18:28 +0100 Subject: [PATCH 105/168] Make interface object self release when notification is received Signed-off-by: Claudio Cambra --- keychain_apple.mm | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/keychain_apple.mm b/keychain_apple.mm index 0f0900d0..f9d2d71c 100644 --- a/keychain_apple.mm +++ b/keychain_apple.mm @@ -115,6 +115,7 @@ - (void)dealloc - (void)keychainTaskFinished:(NSNotification *)notification { _job->emitFinished(); + [self release]; } - (void)keychainReadTaskFinished:(NSNotification *)notification @@ -129,9 +130,15 @@ - (void)keychainReadTaskFinished:(NSNotification *)notification NSData * const retrievedData = (NSData *)[userInfo objectForKey:KeychainNotificationUserInfoDataKey]; if (retrievedData != nil) { _privateJob->data = QByteArray::fromNSData(retrievedData); + + const CFDataRef dataRef = (__bridge CFDataRef)retrievedData; + if (dataRef) { + CFRelease(dataRef); + } } _job->emitFinished(); + [self release]; } - (void)keychainTaskFinishedWithError:(NSNotification *)notification @@ -151,6 +158,7 @@ - (void)keychainTaskFinishedWithError:(NSNotification *)notification const auto fullMessage = localisedDescriptiveMessage.isEmpty() ? error.message : QStringLiteral("%1: %2").arg(localisedDescriptiveMessage, error.message); _job->emitFinishedWithError(error.code, fullMessage); + [self release]; } @end From e2fb2fee10b11adaeb90bb21eed944b4f4681c9d Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Thu, 23 Feb 2023 20:23:45 +0100 Subject: [PATCH 106/168] Add StartReadPassword function that uses new notification system Signed-off-by: Claudio Cambra --- keychain_apple.mm | 49 ++++++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/keychain_apple.mm b/keychain_apple.mm index f9d2d71c..c90fe537 100644 --- a/keychain_apple.mm +++ b/keychain_apple.mm @@ -164,37 +164,46 @@ - (void)keychainTaskFinishedWithError:(NSNotification *)notification @end -void ReadPasswordJobPrivate::scheduledStart() +static void StartReadPassword(const QString &service, const QString &key) { - NSDictionary *const query = @{ - (__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword, - (__bridge id) kSecAttrService: (__bridge NSString *) service.toCFString(), - (__bridge id) kSecAttrAccount: (__bridge NSString *) key.toCFString(), - (__bridge id) kSecReturnData: @YES, - }; - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ + + NSDictionary * const query = @{ + (__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassGenericPassword, + (__bridge NSString *)kSecAttrService: service.toNSString(), + (__bridge NSString *)kSecAttrAccount: key.toNSString(), + (__bridge NSString *)kSecReturnData: @YES, + }; + CFTypeRef dataRef = nil; const OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef) query, &dataRef); - data.clear(); - mode = Binary; - if (status == errSecSuccess) { - if (dataRef) - data = QByteArray::fromCFData((CFDataRef) dataRef); - - q->emitFinished(); + const CFDataRef castedDataRef = (CFDataRef)dataRef; + NSData * const data = (__bridge NSData *)castedDataRef; + [NSNotificationCenter.defaultCenter postNotificationName:AppleKeychainReadTaskFinished + object:nil + userInfo:@{ KeychainNotificationUserInfoDataKey: data }]; } else { - const ErrorDescription error = ErrorDescription::fromStatus(status); - q->emitFinishedWithError(error.code, Job::tr("Could not retrieve private key from keystore: %1").arg(error.message)); + NSNumber * const statusNumber = [NSNumber numberWithInt:status]; + NSString * const descriptiveErrorString = @"Could not retrieve private key from keystore"; + [NSNotificationCenter.defaultCenter postNotificationName:AppleKeychainTaskFinishedWithError + object:nil + userInfo:@{ KeychainNotificationUserInfoStatusKey: statusNumber, + KeychainNotificationUserInfoDescriptiveErrorKey: descriptiveErrorString}]; + if (dataRef) { + CFRelease(dataRef); + } } - - if (dataRef) - CFRelease(dataRef); }); } +void ReadPasswordJobPrivate::scheduledStart() +{ + [[AppleKeychainInterface alloc] initWithJob:q andPrivateJob:this]; + StartReadPassword(service, key); +} + void WritePasswordJobPrivate::scheduledStart() { NSDictionary *const query = @{ From 837ad768559b56e18b95237f8aaeab0b091567cd Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Thu, 23 Feb 2023 19:31:41 +0100 Subject: [PATCH 107/168] Add StartWritePassword function that uses new notification system Signed-off-by: Claudio Cambra --- keychain_apple.mm | 62 +++++++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 26 deletions(-) diff --git a/keychain_apple.mm b/keychain_apple.mm index c90fe537..a8cbe6f7 100644 --- a/keychain_apple.mm +++ b/keychain_apple.mm @@ -198,49 +198,59 @@ static void StartReadPassword(const QString &service, const QString &key) }); } -void ReadPasswordJobPrivate::scheduledStart() -{ - [[AppleKeychainInterface alloc] initWithJob:q andPrivateJob:this]; - StartReadPassword(service, key); -} - -void WritePasswordJobPrivate::scheduledStart() +static void StartWritePassword(const QString &service, const QString &key, const QByteArray &data) { - NSDictionary *const query = @{ - (__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword, - (__bridge id) kSecAttrService: (__bridge NSString *) service.toCFString(), - (__bridge id) kSecAttrAccount: (__bridge NSString *) key.toCFString(), - }; - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ - OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef) query, nil); + NSDictionary * const query = @{ + (__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassGenericPassword, + (__bridge NSString *)kSecAttrService: service.toNSString(), + (__bridge NSString *)kSecAttrAccount: key.toNSString(), + }; + + OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, nil); if (status == errSecSuccess) { - NSDictionary *const update = @{ - (__bridge id) kSecValueData: (__bridge NSData *) data.toCFData(), + NSDictionary * const update = @{ + (__bridge NSString *)kSecValueData: data.toNSData(), }; - status = SecItemUpdate((__bridge CFDictionaryRef) query, (__bridge CFDictionaryRef) update); + status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)update); } else { - NSDictionary *const insert = @{ - (__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword, - (__bridge id) kSecAttrService: (__bridge NSString *) service.toCFString(), - (__bridge id) kSecAttrAccount: (__bridge NSString *) key.toCFString(), - (__bridge id) kSecValueData: (__bridge NSData *) data.toCFData(), + NSDictionary * const insert = @{ + (__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassGenericPassword, + (__bridge NSString *)kSecAttrService: service.toNSString(), + (__bridge NSString *)kSecAttrAccount: key.toNSString(), + (__bridge NSString *)kSecValueData: data.toNSData(), }; - status = SecItemAdd((__bridge CFDictionaryRef) insert, nil); + status = SecItemAdd((__bridge const CFDictionaryRef)insert, nil); } if (status == errSecSuccess) { - q->emitFinished(); + [NSNotificationCenter.defaultCenter postNotificationName:AppleKeychainTaskFinished object:nil]; } else { - const ErrorDescription error = ErrorDescription::fromStatus(status); - q->emitFinishedWithError(error.code, tr("Could not store data in settings: %1").arg(error.message)); + NSNumber * const statusNumber = [NSNumber numberWithInt:status]; + NSString * const descriptiveErrorString = @"Could not store data in settings"; + [NSNotificationCenter.defaultCenter postNotificationName:AppleKeychainTaskFinishedWithError + object:nil + userInfo:@{ KeychainNotificationUserInfoStatusKey: statusNumber, + KeychainNotificationUserInfoDescriptiveErrorKey: descriptiveErrorString }]; } }); } +void ReadPasswordJobPrivate::scheduledStart() +{ + [[AppleKeychainInterface alloc] initWithJob:q andPrivateJob:this]; + StartReadPassword(service, key); +} + +void WritePasswordJobPrivate::scheduledStart() +{ + [[AppleKeychainInterface alloc] initWithJob:q andPrivateJob:this]; + StartWritePassword(service, key, data); +} + void DeletePasswordJobPrivate::scheduledStart() { const NSDictionary *const query = @{ From 284f75b5a2e4fa7d88d0a6e842ef91ed7f475219 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Thu, 23 Feb 2023 20:17:02 +0100 Subject: [PATCH 108/168] Add StartDeletePassword function that uses new notification system Signed-off-by: Claudio Cambra --- keychain_apple.mm | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/keychain_apple.mm b/keychain_apple.mm index a8cbe6f7..9c1e692c 100644 --- a/keychain_apple.mm +++ b/keychain_apple.mm @@ -239,6 +239,30 @@ static void StartWritePassword(const QString &service, const QString &key, const }); } +static void StartDeletePassword(const QString &service, const QString &key) +{ + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ + NSDictionary * const query = @{ + (__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassGenericPassword, + (__bridge NSString *)kSecAttrService: service.toNSString(), + (__bridge NSString *)kSecAttrAccount: key.toNSString(), + }; + + const OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query); + + if (status == errSecSuccess) { + [NSNotificationCenter.defaultCenter postNotificationName:AppleKeychainTaskFinished object:nil]; + } else { + NSNumber * const statusNumber = [NSNumber numberWithInt:status]; + NSString * const descriptiveErrorString = @"Could not remove private key from keystore"; + [NSNotificationCenter.defaultCenter postNotificationName:AppleKeychainTaskFinishedWithError + object:nil + userInfo:@{ KeychainNotificationUserInfoStatusKey: statusNumber, + KeychainNotificationUserInfoDescriptiveErrorKey: descriptiveErrorString }]; + } + }); +} + void ReadPasswordJobPrivate::scheduledStart() { [[AppleKeychainInterface alloc] initWithJob:q andPrivateJob:this]; @@ -253,20 +277,6 @@ static void StartWritePassword(const QString &service, const QString &key, const void DeletePasswordJobPrivate::scheduledStart() { - const NSDictionary *const query = @{ - (__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword, - (__bridge id) kSecAttrService: (__bridge NSString *) service.toCFString(), - (__bridge id) kSecAttrAccount: (__bridge NSString *) key.toCFString(), - }; - - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ - const OSStatus status = SecItemDelete((__bridge CFDictionaryRef) query); - - if (status == errSecSuccess) { - q->emitFinished(); - } else { - const ErrorDescription error = ErrorDescription::fromStatus(status); - q->emitFinishedWithError(error.code, Job::tr("Could not remove private key from keystore: %1").arg(error.message)); - } - }); + [[AppleKeychainInterface alloc] initWithJob:q andPrivateJob:this]; + StartDeletePassword(service, key); } From 1ba0cfdca7e55218c15270b8fc02d0e6c3638ed0 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Thu, 23 Feb 2023 20:37:07 +0100 Subject: [PATCH 109/168] Guard against null job pointers Signed-off-by: Claudio Cambra --- keychain_apple.mm | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/keychain_apple.mm b/keychain_apple.mm index 9c1e692c..ebaa4d1b 100644 --- a/keychain_apple.mm +++ b/keychain_apple.mm @@ -73,13 +73,17 @@ static ErrorDescription fromStatus(OSStatus status) @interface AppleKeychainInterface : NSObject -@property (readonly) Job *job; -@property (readonly) JobPrivate *privateJob; - - (instancetype)initWithJob:(Job *)job andPrivateJob:(JobPrivate *)privateJob; @end +@interface AppleKeychainInterface() +{ + QPointer _job; + QPointer _privateJob; +} +@end + @implementation AppleKeychainInterface - (instancetype)initWithJob:(Job *)job andPrivateJob:(JobPrivate *)privateJob @@ -114,7 +118,10 @@ - (void)dealloc - (void)keychainTaskFinished:(NSNotification *)notification { - _job->emitFinished(); + if (_job) { + _job->emitFinished(); + } + [self release]; } @@ -129,7 +136,9 @@ - (void)keychainReadTaskFinished:(NSNotification *)notification NSData * const retrievedData = (NSData *)[userInfo objectForKey:KeychainNotificationUserInfoDataKey]; if (retrievedData != nil) { - _privateJob->data = QByteArray::fromNSData(retrievedData); + if (_privateJob) { + _privateJob->data = QByteArray::fromNSData(retrievedData); + } const CFDataRef dataRef = (__bridge CFDataRef)retrievedData; if (dataRef) { @@ -137,7 +146,10 @@ - (void)keychainReadTaskFinished:(NSNotification *)notification } } - _job->emitFinished(); + if (_job) { + _job->emitFinished(); + } + [self release]; } @@ -157,7 +169,10 @@ - (void)keychainTaskFinishedWithError:(NSNotification *)notification const ErrorDescription error = ErrorDescription::fromStatus(status); const auto fullMessage = localisedDescriptiveMessage.isEmpty() ? error.message : QStringLiteral("%1: %2").arg(localisedDescriptiveMessage, error.message); - _job->emitFinishedWithError(error.code, fullMessage); + if (_job) { + _job->emitFinishedWithError(error.code, fullMessage); + } + [self release]; } From 2ee4cdb79df6ea872d0b837eb29a4174c713711a Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Thu, 23 Feb 2023 21:17:17 +0100 Subject: [PATCH 110/168] Ensure that an AppleKeychainInterface does not respond to all keychain notifications Signed-off-by: Claudio Cambra --- keychain_apple.mm | 76 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 59 insertions(+), 17 deletions(-) diff --git a/keychain_apple.mm b/keychain_apple.mm index ebaa4d1b..b9924a8d 100644 --- a/keychain_apple.mm +++ b/keychain_apple.mm @@ -21,6 +21,11 @@ NSString * const KeychainNotificationUserInfoStatusKey = @"status"; NSString * const KeychainNotificationUserInfoDataKey = @"data"; NSString * const KeychainNotificationUserInfoDescriptiveErrorKey = @"descriptiveError"; +NSString * const KeychainNotificationUserInfoNotificationId = @"notificationId"; + +namespace { + static unsigned int currentNotificationId = 0; +} struct ErrorDescription { @@ -73,7 +78,7 @@ static ErrorDescription fromStatus(OSStatus status) @interface AppleKeychainInterface : NSObject -- (instancetype)initWithJob:(Job *)job andPrivateJob:(JobPrivate *)privateJob; +- (instancetype)initWithJob:(Job *)job andPrivateJob:(JobPrivate *)privateJob andNotificationId:(unsigned int)notificationId; @end @@ -81,17 +86,19 @@ @interface AppleKeychainInterface() { QPointer _job; QPointer _privateJob; + unsigned int _notificationId; } @end @implementation AppleKeychainInterface -- (instancetype)initWithJob:(Job *)job andPrivateJob:(JobPrivate *)privateJob +- (instancetype)initWithJob:(Job *)job andPrivateJob:(JobPrivate *)privateJob andNotificationId:(unsigned int)notificationId { self = [super init]; if (self) { _job = job; _privateJob = privateJob; + _notificationId = notificationId; NSNotificationCenter * const notificationCenter = NSNotificationCenter.defaultCenter; [notificationCenter addObserver:self @@ -118,6 +125,15 @@ - (void)dealloc - (void)keychainTaskFinished:(NSNotification *)notification { + NSParameterAssert(notification); + NSDictionary * const userInfo = notification.userInfo; + NSAssert(userInfo, @"Keychain task finished with error notification should contain nonnull user info dictionary"); + + NSNumber * const retrievedNotificationId = (NSNumber *)[userInfo objectForKey:KeychainNotificationUserInfoNotificationId]; + if (retrievedNotificationId.unsignedIntValue != _notificationId) { + return; + } + if (_job) { _job->emitFinished(); } @@ -131,6 +147,11 @@ - (void)keychainReadTaskFinished:(NSNotification *)notification NSDictionary * const userInfo = notification.userInfo; NSAssert(userInfo, @"Keychain task finished with error notification should contain nonnull user info dictionary"); + NSNumber * const retrievedNotificationId = (NSNumber *)[userInfo objectForKey:KeychainNotificationUserInfoNotificationId]; + if (retrievedNotificationId.unsignedIntValue != _notificationId) { + return; + } + _privateJob->data.clear(); _privateJob->mode = JobPrivate::Binary; @@ -159,6 +180,11 @@ - (void)keychainTaskFinishedWithError:(NSNotification *)notification NSDictionary * const userInfo = notification.userInfo; NSAssert(userInfo, @"Keychain task finished with error notification should contain nonnull user info dictionary"); + NSNumber * const retrievedNotificationId = (NSNumber *)[userInfo objectForKey:KeychainNotificationUserInfoNotificationId]; + if (retrievedNotificationId.unsignedIntValue != _notificationId) { + return; + } + NSNumber * const statusNumber = (NSNumber *)[userInfo objectForKey:KeychainNotificationUserInfoStatusKey]; NSAssert(statusNumber, @"Keychain task notification user info dict should contain valid status number"); const OSStatus status = statusNumber.intValue; @@ -179,9 +205,10 @@ - (void)keychainTaskFinishedWithError:(NSNotification *)notification @end -static void StartReadPassword(const QString &service, const QString &key) +static void StartReadPassword(const QString &service, const QString &key, const unsigned int notificationId) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ + NSNumber * const notificationIdNumber = [NSNumber numberWithUnsignedInt:notificationId]; NSDictionary * const query = @{ (__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassGenericPassword, @@ -198,14 +225,16 @@ static void StartReadPassword(const QString &service, const QString &key) NSData * const data = (__bridge NSData *)castedDataRef; [NSNotificationCenter.defaultCenter postNotificationName:AppleKeychainReadTaskFinished object:nil - userInfo:@{ KeychainNotificationUserInfoDataKey: data }]; + userInfo:@{ KeychainNotificationUserInfoDataKey: data, + KeychainNotificationUserInfoNotificationId: notificationIdNumber }]; } else { NSNumber * const statusNumber = [NSNumber numberWithInt:status]; NSString * const descriptiveErrorString = @"Could not retrieve private key from keystore"; [NSNotificationCenter.defaultCenter postNotificationName:AppleKeychainTaskFinishedWithError object:nil userInfo:@{ KeychainNotificationUserInfoStatusKey: statusNumber, - KeychainNotificationUserInfoDescriptiveErrorKey: descriptiveErrorString}]; + KeychainNotificationUserInfoDescriptiveErrorKey: descriptiveErrorString, + KeychainNotificationUserInfoNotificationId: notificationIdNumber }]; if (dataRef) { CFRelease(dataRef); } @@ -213,9 +242,11 @@ static void StartReadPassword(const QString &service, const QString &key) }); } -static void StartWritePassword(const QString &service, const QString &key, const QByteArray &data) +static void StartWritePassword(const QString &service, const QString &key, const QByteArray &data, const unsigned int notificationId) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ + NSNumber * const notificationIdNumber = [NSNumber numberWithUnsignedInt:notificationId]; + NSDictionary * const query = @{ (__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassGenericPassword, (__bridge NSString *)kSecAttrService: service.toNSString(), @@ -242,21 +273,26 @@ static void StartWritePassword(const QString &service, const QString &key, const } if (status == errSecSuccess) { - [NSNotificationCenter.defaultCenter postNotificationName:AppleKeychainTaskFinished object:nil]; + [NSNotificationCenter.defaultCenter postNotificationName:AppleKeychainTaskFinished + object:nil + userInfo:@{ KeychainNotificationUserInfoNotificationId: notificationIdNumber }]; } else { NSNumber * const statusNumber = [NSNumber numberWithInt:status]; NSString * const descriptiveErrorString = @"Could not store data in settings"; [NSNotificationCenter.defaultCenter postNotificationName:AppleKeychainTaskFinishedWithError object:nil userInfo:@{ KeychainNotificationUserInfoStatusKey: statusNumber, - KeychainNotificationUserInfoDescriptiveErrorKey: descriptiveErrorString }]; + KeychainNotificationUserInfoDescriptiveErrorKey: descriptiveErrorString, + KeychainNotificationUserInfoNotificationId: notificationIdNumber }]; } }); } -static void StartDeletePassword(const QString &service, const QString &key) +static void StartDeletePassword(const QString &service, const QString &key, const unsigned int notificationId) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ + NSNumber * const notificationIdNumber = [NSNumber numberWithUnsignedInt:notificationId]; + NSDictionary * const query = @{ (__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassGenericPassword, (__bridge NSString *)kSecAttrService: service.toNSString(), @@ -266,32 +302,38 @@ static void StartDeletePassword(const QString &service, const QString &key) const OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query); if (status == errSecSuccess) { - [NSNotificationCenter.defaultCenter postNotificationName:AppleKeychainTaskFinished object:nil]; + [NSNotificationCenter.defaultCenter postNotificationName:AppleKeychainTaskFinished + object:nil + userInfo:@{ KeychainNotificationUserInfoNotificationId: notificationIdNumber }]; } else { NSNumber * const statusNumber = [NSNumber numberWithInt:status]; NSString * const descriptiveErrorString = @"Could not remove private key from keystore"; [NSNotificationCenter.defaultCenter postNotificationName:AppleKeychainTaskFinishedWithError object:nil userInfo:@{ KeychainNotificationUserInfoStatusKey: statusNumber, - KeychainNotificationUserInfoDescriptiveErrorKey: descriptiveErrorString }]; + KeychainNotificationUserInfoDescriptiveErrorKey: descriptiveErrorString, + KeychainNotificationUserInfoNotificationId: notificationIdNumber }]; } }); } void ReadPasswordJobPrivate::scheduledStart() { - [[AppleKeychainInterface alloc] initWithJob:q andPrivateJob:this]; - StartReadPassword(service, key); + const auto notificationId = ++currentNotificationId; + [[AppleKeychainInterface alloc] initWithJob:q andPrivateJob:this andNotificationId:notificationId]; + StartReadPassword(service, key, currentNotificationId); } void WritePasswordJobPrivate::scheduledStart() { - [[AppleKeychainInterface alloc] initWithJob:q andPrivateJob:this]; - StartWritePassword(service, key, data); + const auto notificationId = ++currentNotificationId; + [[AppleKeychainInterface alloc] initWithJob:q andPrivateJob:this andNotificationId:notificationId]; + StartWritePassword(service, key, data, currentNotificationId); } void DeletePasswordJobPrivate::scheduledStart() { - [[AppleKeychainInterface alloc] initWithJob:q andPrivateJob:this]; - StartDeletePassword(service, key); + const auto notificationId = ++currentNotificationId; + [[AppleKeychainInterface alloc] initWithJob:q andPrivateJob:this andNotificationId:notificationId]; + StartDeletePassword(service, key, currentNotificationId); } From 3fd06f232672770b447dc2f622617cfb6e695bf9 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Fri, 24 Feb 2023 00:32:16 +0100 Subject: [PATCH 111/168] Emit all apple keychain task complete notifications on main queue Signed-off-by: Claudio Cambra --- keychain_apple.mm | 64 +++++++++++++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/keychain_apple.mm b/keychain_apple.mm index b9924a8d..3809c2ea 100644 --- a/keychain_apple.mm +++ b/keychain_apple.mm @@ -223,18 +223,23 @@ static void StartReadPassword(const QString &service, const QString &key, const if (status == errSecSuccess) { const CFDataRef castedDataRef = (CFDataRef)dataRef; NSData * const data = (__bridge NSData *)castedDataRef; - [NSNotificationCenter.defaultCenter postNotificationName:AppleKeychainReadTaskFinished - object:nil - userInfo:@{ KeychainNotificationUserInfoDataKey: data, - KeychainNotificationUserInfoNotificationId: notificationIdNumber }]; + dispatch_async(dispatch_get_main_queue(), ^{ + [NSNotificationCenter.defaultCenter postNotificationName:AppleKeychainReadTaskFinished + object:nil + userInfo:@{ KeychainNotificationUserInfoDataKey: data, + KeychainNotificationUserInfoNotificationId: notificationIdNumber }]; + }); } else { NSNumber * const statusNumber = [NSNumber numberWithInt:status]; NSString * const descriptiveErrorString = @"Could not retrieve private key from keystore"; - [NSNotificationCenter.defaultCenter postNotificationName:AppleKeychainTaskFinishedWithError - object:nil - userInfo:@{ KeychainNotificationUserInfoStatusKey: statusNumber, - KeychainNotificationUserInfoDescriptiveErrorKey: descriptiveErrorString, - KeychainNotificationUserInfoNotificationId: notificationIdNumber }]; + dispatch_async(dispatch_get_main_queue(), ^{ + [NSNotificationCenter.defaultCenter postNotificationName:AppleKeychainTaskFinishedWithError + object:nil + userInfo:@{ KeychainNotificationUserInfoStatusKey: statusNumber, + KeychainNotificationUserInfoDescriptiveErrorKey: descriptiveErrorString, + KeychainNotificationUserInfoNotificationId: notificationIdNumber }]; + }); + if (dataRef) { CFRelease(dataRef); } @@ -273,17 +278,22 @@ static void StartWritePassword(const QString &service, const QString &key, const } if (status == errSecSuccess) { - [NSNotificationCenter.defaultCenter postNotificationName:AppleKeychainTaskFinished - object:nil - userInfo:@{ KeychainNotificationUserInfoNotificationId: notificationIdNumber }]; + dispatch_async(dispatch_get_main_queue(), ^{ + [NSNotificationCenter.defaultCenter postNotificationName:AppleKeychainTaskFinished + object:nil + userInfo:@{ KeychainNotificationUserInfoNotificationId: notificationIdNumber }]; + }); } else { NSNumber * const statusNumber = [NSNumber numberWithInt:status]; NSString * const descriptiveErrorString = @"Could not store data in settings"; - [NSNotificationCenter.defaultCenter postNotificationName:AppleKeychainTaskFinishedWithError - object:nil - userInfo:@{ KeychainNotificationUserInfoStatusKey: statusNumber, - KeychainNotificationUserInfoDescriptiveErrorKey: descriptiveErrorString, - KeychainNotificationUserInfoNotificationId: notificationIdNumber }]; + + dispatch_async(dispatch_get_main_queue(), ^{ + [NSNotificationCenter.defaultCenter postNotificationName:AppleKeychainTaskFinishedWithError + object:nil + userInfo:@{ KeychainNotificationUserInfoStatusKey: statusNumber, + KeychainNotificationUserInfoDescriptiveErrorKey: descriptiveErrorString, + KeychainNotificationUserInfoNotificationId: notificationIdNumber }]; + }); } }); } @@ -302,17 +312,21 @@ static void StartDeletePassword(const QString &service, const QString &key, cons const OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query); if (status == errSecSuccess) { - [NSNotificationCenter.defaultCenter postNotificationName:AppleKeychainTaskFinished - object:nil - userInfo:@{ KeychainNotificationUserInfoNotificationId: notificationIdNumber }]; + dispatch_async(dispatch_get_main_queue(), ^{ + [NSNotificationCenter.defaultCenter postNotificationName:AppleKeychainTaskFinished + object:nil + userInfo:@{ KeychainNotificationUserInfoNotificationId: notificationIdNumber }]; + }); } else { NSNumber * const statusNumber = [NSNumber numberWithInt:status]; NSString * const descriptiveErrorString = @"Could not remove private key from keystore"; - [NSNotificationCenter.defaultCenter postNotificationName:AppleKeychainTaskFinishedWithError - object:nil - userInfo:@{ KeychainNotificationUserInfoStatusKey: statusNumber, - KeychainNotificationUserInfoDescriptiveErrorKey: descriptiveErrorString, - KeychainNotificationUserInfoNotificationId: notificationIdNumber }]; + dispatch_async(dispatch_get_main_queue(), ^{ + [NSNotificationCenter.defaultCenter postNotificationName:AppleKeychainTaskFinishedWithError + object:nil + userInfo:@{ KeychainNotificationUserInfoStatusKey: statusNumber, + KeychainNotificationUserInfoDescriptiveErrorKey: descriptiveErrorString, + KeychainNotificationUserInfoNotificationId: notificationIdNumber }]; + }); } }); } From 41f195e4d6d65b3bb200d13b079969470128b448 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 28 Feb 2023 17:25:13 +0100 Subject: [PATCH 112/168] Fix testclient on Apple devices Signed-off-by: Claudio Cambra --- CMakeLists.txt | 16 +++++++++++++++- testclient.cpp | 14 ++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ea5d69fa..f106ad20 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -274,8 +274,22 @@ install(TARGETS ${QTKEYCHAIN_TARGET_NAME} ) if(BUILD_TEST_APPLICATION) + set( testclient_LIBRARIES ${QTKEYCHAIN_TARGET_NAME} ) + + if(APPLE) + list(APPEND testclient_LIBRARIES "-framework Cocoa") + + if (BUILD_WITH_QT6) + find_package(Qt6 COMPONENTS Gui REQUIRED) + list(APPEND testclient_LIBRARIES Qt6::Gui) + else() + find_package(Qt5 COMPONENTS Gui REQUIRED) + list(APPEND testclient_LIBRARIES Qt5::Gui) + endif() + + endif() add_executable( testclient testclient.cpp ) - target_link_libraries( testclient ${QTKEYCHAIN_TARGET_NAME}) + target_link_libraries( testclient ${testclient_LIBRARIES}) endif() diff --git a/testclient.cpp b/testclient.cpp index 94189cf1..f745bd37 100644 --- a/testclient.cpp +++ b/testclient.cpp @@ -6,7 +6,14 @@ * or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution * * details, check the accompanying file 'COPYING'. * *****************************************************************************/ + +#include +#ifdef Q_OS_DARWIN +#include +#else #include +#endif + #include #include "keychain.h" @@ -22,7 +29,14 @@ static int printUsage() { } int main( int argc, char** argv ) { +#ifdef Q_OS_DARWIN + // Since we use NSNotificationCenter under the hood in keychain_apple, + // we use QGuiApplication to automatically configure the platform + // integration stuff done in this class and not in QCoreApplication + QGuiApplication app( argc, argv ); +#else QCoreApplication app( argc, argv ); +#endif const QStringList args = app.arguments(); if ( args.count() < 2 ) return printUsage(); From f5cdf33f050612ce249eee51ea0279f1570eba3e Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 28 Feb 2023 17:42:18 +0100 Subject: [PATCH 113/168] Create NSData with password data from direct byte pointer rather than bridging CFDataRef Signed-off-by: Claudio Cambra --- keychain_apple.mm | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/keychain_apple.mm b/keychain_apple.mm index 3809c2ea..ae5dbd33 100644 --- a/keychain_apple.mm +++ b/keychain_apple.mm @@ -160,11 +160,6 @@ - (void)keychainReadTaskFinished:(NSNotification *)notification if (_privateJob) { _privateJob->data = QByteArray::fromNSData(retrievedData); } - - const CFDataRef dataRef = (__bridge CFDataRef)retrievedData; - if (dataRef) { - CFRelease(dataRef); - } } if (_job) { @@ -222,7 +217,7 @@ static void StartReadPassword(const QString &service, const QString &key, const if (status == errSecSuccess) { const CFDataRef castedDataRef = (CFDataRef)dataRef; - NSData * const data = (__bridge NSData *)castedDataRef; + NSData * const data = [NSData dataWithBytes:CFDataGetBytePtr(castedDataRef) length:CFDataGetLength(castedDataRef)]; dispatch_async(dispatch_get_main_queue(), ^{ [NSNotificationCenter.defaultCenter postNotificationName:AppleKeychainReadTaskFinished object:nil @@ -239,11 +234,11 @@ static void StartReadPassword(const QString &service, const QString &key, const KeychainNotificationUserInfoDescriptiveErrorKey: descriptiveErrorString, KeychainNotificationUserInfoNotificationId: notificationIdNumber }]; }); - - if (dataRef) { - CFRelease(dataRef); - } } + + if (dataRef) { + CFRelease(dataRef); + } }); } From 8485316ffcb06637d7708176a2a191856f4a328c Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 28 Feb 2023 19:52:50 +0100 Subject: [PATCH 114/168] Remove use of notification center in Apple Keychain, use direct interface pointer instead Signed-off-by: Claudio Cambra --- keychain_apple.mm | 150 ++++++++++------------------------------------ 1 file changed, 30 insertions(+), 120 deletions(-) diff --git a/keychain_apple.mm b/keychain_apple.mm index ae5dbd33..20e25b64 100644 --- a/keychain_apple.mm +++ b/keychain_apple.mm @@ -14,19 +14,6 @@ using namespace QKeychain; -NSString * const AppleKeychainTaskFinished = @"AppleKeychainTaskFinished"; -NSString * const AppleKeychainReadTaskFinished = @"AppleKeychainReadTaskFinished"; -NSString * const AppleKeychainTaskFinishedWithError = @"AppleKeychainTaskFinishedWithError"; - -NSString * const KeychainNotificationUserInfoStatusKey = @"status"; -NSString * const KeychainNotificationUserInfoDataKey = @"data"; -NSString * const KeychainNotificationUserInfoDescriptiveErrorKey = @"descriptiveError"; -NSString * const KeychainNotificationUserInfoNotificationId = @"notificationId"; - -namespace { - static unsigned int currentNotificationId = 0; -} - struct ErrorDescription { QKeychain::Error code; @@ -78,7 +65,10 @@ static ErrorDescription fromStatus(OSStatus status) @interface AppleKeychainInterface : NSObject -- (instancetype)initWithJob:(Job *)job andPrivateJob:(JobPrivate *)privateJob andNotificationId:(unsigned int)notificationId; +- (instancetype)initWithJob:(Job *)job andPrivateJob:(JobPrivate *)privateJob; +- (void)keychainTaskFinished; +- (void)keychainReadTaskFinished:(NSData *)retrievedData; +- (void)keychainTaskFinishedWithError:(OSStatus)status descriptiveMessage:(NSString *)descriptiveMessage; @end @@ -86,33 +76,17 @@ @interface AppleKeychainInterface() { QPointer _job; QPointer _privateJob; - unsigned int _notificationId; } @end @implementation AppleKeychainInterface -- (instancetype)initWithJob:(Job *)job andPrivateJob:(JobPrivate *)privateJob andNotificationId:(unsigned int)notificationId +- (instancetype)initWithJob:(Job *)job andPrivateJob:(JobPrivate *)privateJob { self = [super init]; if (self) { _job = job; _privateJob = privateJob; - _notificationId = notificationId; - - NSNotificationCenter * const notificationCenter = NSNotificationCenter.defaultCenter; - [notificationCenter addObserver:self - selector:@selector(keychainTaskFinished:) - name:AppleKeychainTaskFinished - object:nil]; - [notificationCenter addObserver:self - selector:@selector(keychainReadTaskFinished:) - name:AppleKeychainReadTaskFinished - object:nil]; - [notificationCenter addObserver:self - selector:@selector(keychainTaskFinishedWithError:) - name:AppleKeychainTaskFinishedWithError - object:nil]; } return self; } @@ -123,39 +97,18 @@ - (void)dealloc [super dealloc]; } -- (void)keychainTaskFinished:(NSNotification *)notification +- (void)keychainTaskFinished { - NSParameterAssert(notification); - NSDictionary * const userInfo = notification.userInfo; - NSAssert(userInfo, @"Keychain task finished with error notification should contain nonnull user info dictionary"); - - NSNumber * const retrievedNotificationId = (NSNumber *)[userInfo objectForKey:KeychainNotificationUserInfoNotificationId]; - if (retrievedNotificationId.unsignedIntValue != _notificationId) { - return; - } - if (_job) { _job->emitFinished(); } - - [self release]; } -- (void)keychainReadTaskFinished:(NSNotification *)notification +- (void)keychainReadTaskFinished:(NSData *)retrievedData { - NSParameterAssert(notification); - NSDictionary * const userInfo = notification.userInfo; - NSAssert(userInfo, @"Keychain task finished with error notification should contain nonnull user info dictionary"); - - NSNumber * const retrievedNotificationId = (NSNumber *)[userInfo objectForKey:KeychainNotificationUserInfoNotificationId]; - if (retrievedNotificationId.unsignedIntValue != _notificationId) { - return; - } - _privateJob->data.clear(); _privateJob->mode = JobPrivate::Binary; - NSData * const retrievedData = (NSData *)[userInfo objectForKey:KeychainNotificationUserInfoDataKey]; if (retrievedData != nil) { if (_privateJob) { _privateJob->data = QByteArray::fromNSData(retrievedData); @@ -165,26 +118,10 @@ - (void)keychainReadTaskFinished:(NSNotification *)notification if (_job) { _job->emitFinished(); } - - [self release]; } -- (void)keychainTaskFinishedWithError:(NSNotification *)notification +- (void)keychainTaskFinishedWithError:(OSStatus)status descriptiveMessage:(NSString *)descriptiveMessage { - NSParameterAssert(notification); - NSDictionary * const userInfo = notification.userInfo; - NSAssert(userInfo, @"Keychain task finished with error notification should contain nonnull user info dictionary"); - - NSNumber * const retrievedNotificationId = (NSNumber *)[userInfo objectForKey:KeychainNotificationUserInfoNotificationId]; - if (retrievedNotificationId.unsignedIntValue != _notificationId) { - return; - } - - NSNumber * const statusNumber = (NSNumber *)[userInfo objectForKey:KeychainNotificationUserInfoStatusKey]; - NSAssert(statusNumber, @"Keychain task notification user info dict should contain valid status number"); - const OSStatus status = statusNumber.intValue; - - NSString * const descriptiveMessage = (NSString *)[userInfo objectForKey:KeychainNotificationUserInfoDescriptiveErrorKey]; const auto localisedDescriptiveMessage = Job::tr([descriptiveMessage UTF8String]); const ErrorDescription error = ErrorDescription::fromStatus(status); @@ -193,18 +130,14 @@ - (void)keychainTaskFinishedWithError:(NSNotification *)notification if (_job) { _job->emitFinishedWithError(error.code, fullMessage); } - - [self release]; } @end -static void StartReadPassword(const QString &service, const QString &key, const unsigned int notificationId) +static void StartReadPassword(const QString &service, const QString &key, AppleKeychainInterface * const interface) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ - NSNumber * const notificationIdNumber = [NSNumber numberWithUnsignedInt:notificationId]; - NSDictionary * const query = @{ (__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassGenericPassword, (__bridge NSString *)kSecAttrService: service.toNSString(), @@ -217,22 +150,16 @@ static void StartReadPassword(const QString &service, const QString &key, const if (status == errSecSuccess) { const CFDataRef castedDataRef = (CFDataRef)dataRef; - NSData * const data = [NSData dataWithBytes:CFDataGetBytePtr(castedDataRef) length:CFDataGetLength(castedDataRef)]; + NSData * const data = (__bridge NSData *)castedDataRef; dispatch_async(dispatch_get_main_queue(), ^{ - [NSNotificationCenter.defaultCenter postNotificationName:AppleKeychainReadTaskFinished - object:nil - userInfo:@{ KeychainNotificationUserInfoDataKey: data, - KeychainNotificationUserInfoNotificationId: notificationIdNumber }]; + [interface keychainReadTaskFinished:data]; + [interface release]; }); } else { - NSNumber * const statusNumber = [NSNumber numberWithInt:status]; NSString * const descriptiveErrorString = @"Could not retrieve private key from keystore"; dispatch_async(dispatch_get_main_queue(), ^{ - [NSNotificationCenter.defaultCenter postNotificationName:AppleKeychainTaskFinishedWithError - object:nil - userInfo:@{ KeychainNotificationUserInfoStatusKey: statusNumber, - KeychainNotificationUserInfoDescriptiveErrorKey: descriptiveErrorString, - KeychainNotificationUserInfoNotificationId: notificationIdNumber }]; + [interface keychainTaskFinishedWithError:status descriptiveMessage:descriptiveErrorString]; + [interface release]; }); } @@ -242,11 +169,9 @@ static void StartReadPassword(const QString &service, const QString &key, const }); } -static void StartWritePassword(const QString &service, const QString &key, const QByteArray &data, const unsigned int notificationId) +static void StartWritePassword(const QString &service, const QString &key, const QByteArray &data, AppleKeychainInterface * const interface) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ - NSNumber * const notificationIdNumber = [NSNumber numberWithUnsignedInt:notificationId]; - NSDictionary * const query = @{ (__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassGenericPassword, (__bridge NSString *)kSecAttrService: service.toNSString(), @@ -274,30 +199,23 @@ static void StartWritePassword(const QString &service, const QString &key, const if (status == errSecSuccess) { dispatch_async(dispatch_get_main_queue(), ^{ - [NSNotificationCenter.defaultCenter postNotificationName:AppleKeychainTaskFinished - object:nil - userInfo:@{ KeychainNotificationUserInfoNotificationId: notificationIdNumber }]; + [interface keychainTaskFinished]; + [interface release]; }); } else { - NSNumber * const statusNumber = [NSNumber numberWithInt:status]; NSString * const descriptiveErrorString = @"Could not store data in settings"; dispatch_async(dispatch_get_main_queue(), ^{ - [NSNotificationCenter.defaultCenter postNotificationName:AppleKeychainTaskFinishedWithError - object:nil - userInfo:@{ KeychainNotificationUserInfoStatusKey: statusNumber, - KeychainNotificationUserInfoDescriptiveErrorKey: descriptiveErrorString, - KeychainNotificationUserInfoNotificationId: notificationIdNumber }]; + [interface keychainTaskFinishedWithError:status descriptiveMessage:descriptiveErrorString]; + [interface release]; }); } }); } -static void StartDeletePassword(const QString &service, const QString &key, const unsigned int notificationId) +static void StartDeletePassword(const QString &service, const QString &key, AppleKeychainInterface * const interface) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ - NSNumber * const notificationIdNumber = [NSNumber numberWithUnsignedInt:notificationId]; - NSDictionary * const query = @{ (__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassGenericPassword, (__bridge NSString *)kSecAttrService: service.toNSString(), @@ -308,19 +226,14 @@ static void StartDeletePassword(const QString &service, const QString &key, cons if (status == errSecSuccess) { dispatch_async(dispatch_get_main_queue(), ^{ - [NSNotificationCenter.defaultCenter postNotificationName:AppleKeychainTaskFinished - object:nil - userInfo:@{ KeychainNotificationUserInfoNotificationId: notificationIdNumber }]; + [interface keychainTaskFinished]; + [interface release]; }); } else { - NSNumber * const statusNumber = [NSNumber numberWithInt:status]; NSString * const descriptiveErrorString = @"Could not remove private key from keystore"; dispatch_async(dispatch_get_main_queue(), ^{ - [NSNotificationCenter.defaultCenter postNotificationName:AppleKeychainTaskFinishedWithError - object:nil - userInfo:@{ KeychainNotificationUserInfoStatusKey: statusNumber, - KeychainNotificationUserInfoDescriptiveErrorKey: descriptiveErrorString, - KeychainNotificationUserInfoNotificationId: notificationIdNumber }]; + [interface keychainTaskFinishedWithError:status descriptiveMessage:descriptiveErrorString]; + [interface release]; }); } }); @@ -328,21 +241,18 @@ static void StartDeletePassword(const QString &service, const QString &key, cons void ReadPasswordJobPrivate::scheduledStart() { - const auto notificationId = ++currentNotificationId; - [[AppleKeychainInterface alloc] initWithJob:q andPrivateJob:this andNotificationId:notificationId]; - StartReadPassword(service, key, currentNotificationId); + AppleKeychainInterface * const interface = [[AppleKeychainInterface alloc] initWithJob:q andPrivateJob:this]; + StartReadPassword(service, key, interface); } void WritePasswordJobPrivate::scheduledStart() { - const auto notificationId = ++currentNotificationId; - [[AppleKeychainInterface alloc] initWithJob:q andPrivateJob:this andNotificationId:notificationId]; - StartWritePassword(service, key, data, currentNotificationId); + AppleKeychainInterface * const interface = [[AppleKeychainInterface alloc] initWithJob:q andPrivateJob:this]; + StartWritePassword(service, key, data, interface); } void DeletePasswordJobPrivate::scheduledStart() { - const auto notificationId = ++currentNotificationId; - [[AppleKeychainInterface alloc] initWithJob:q andPrivateJob:this andNotificationId:notificationId]; - StartDeletePassword(service, key, currentNotificationId); + AppleKeychainInterface * const interface = [[AppleKeychainInterface alloc] initWithJob:q andPrivateJob:this]; + StartDeletePassword(service, key, interface); } From 8bbaa6d8302cf0747d9786ace4dd13c7fb746502 Mon Sep 17 00:00:00 2001 From: Volker Krause Date: Sat, 1 Apr 2023 17:46:23 +0200 Subject: [PATCH 115/168] Add API to check whether a secure backend is available at all Particularly important on Unix, where there are multiple options and neither of them is guaranteed. Fixes #224. --- keychain.h | 11 +++++++++++ keychain_android.cpp | 5 +++++ keychain_apple.mm | 5 +++++ keychain_haiku.cpp | 5 +++++ keychain_unix.cpp | 5 +++++ keychain_win.cpp | 5 +++++ 6 files changed, 36 insertions(+) diff --git a/keychain.h b/keychain.h index 45337c6c..47dbecef 100644 --- a/keychain.h +++ b/keychain.h @@ -266,6 +266,17 @@ class QKEYCHAIN_EXPORT DeletePasswordJob : public Job { friend class QKeychain::DeletePasswordJobPrivate; }; +/** + * Checks whether there is a viable secure backend available. + * This particularly matters on UNIX platforms where multiple different backends + * exist and none might be available. + * + * Note that using the insecure fallback will work even if no secure backend is available. + * + * @since 0.14.0 + */ +bool isAvailable(); + } // namespace QtKeychain #endif diff --git a/keychain_android.cpp b/keychain_android.cpp index 46939353..1009188e 100644 --- a/keychain_android.cpp +++ b/keychain_android.cpp @@ -188,3 +188,8 @@ void DeletePasswordJobPrivate::scheduledStart() else q->emitFinished(); } + +bool isAvailable() +{ + return true; +} diff --git a/keychain_apple.mm b/keychain_apple.mm index 20e25b64..48301104 100644 --- a/keychain_apple.mm +++ b/keychain_apple.mm @@ -256,3 +256,8 @@ static void StartDeletePassword(const QString &service, const QString &key, Appl AppleKeychainInterface * const interface = [[AppleKeychainInterface alloc] initWithJob:q andPrivateJob:this]; StartDeletePassword(service, key, interface); } + +bool isAvailable() +{ + return true; +} diff --git a/keychain_haiku.cpp b/keychain_haiku.cpp index 82872466..58f15e61 100644 --- a/keychain_haiku.cpp +++ b/keychain_haiku.cpp @@ -185,3 +185,8 @@ void DeletePasswordJobPrivate::scheduledStart() q->emitFinishedWithError( error, errorString ); } + +bool isAvailable() +{ + return true; +} diff --git a/keychain_unix.cpp b/keychain_unix.cpp index 985e8b4a..0a9a8ad4 100644 --- a/keychain_unix.cpp +++ b/keychain_unix.cpp @@ -588,3 +588,8 @@ void DeletePasswordJobPrivate::fallbackOnError(const QDBusError &err) { q->emitFinished(); } + +bool isAvailable() +{ + return LibSecretKeyring::isAvailable() || GnomeKeyring::isAvailable() || isKwallet5Available(); +} diff --git a/keychain_win.cpp b/keychain_win.cpp index e888967f..b967c089 100644 --- a/keychain_win.cpp +++ b/keychain_win.cpp @@ -186,3 +186,8 @@ void DeletePasswordJobPrivate::scheduledStart() { } } #endif + +bool isAvailable() +{ + return true; +} From 3503f6a00187459a93dfda0540193af2d1acd826 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Thu, 11 May 2023 16:43:21 +0200 Subject: [PATCH 116/168] Android: Do not crash when storing new password (Qt 6) Passing jobject here instead of Context calls Object(jobject) and not the intended KeyPairGeneratorSpec::Builder ctor, which then leads to a reference to the QtActivity. Trying to call "setAlias" on that threw. Fixes #233 --- keychain_android.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/keychain_android.cpp b/keychain_android.cpp index 1009188e..903c73ee 100644 --- a/keychain_android.cpp +++ b/keychain_android.cpp @@ -106,9 +106,9 @@ void WritePasswordJobPrivate::scheduledStart() #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) KeyPairGeneratorSpec::Builder(Context(QtAndroid::androidActivity())). #elif QT_VERSION < QT_VERSION_CHECK(6, 4, 0) - KeyPairGeneratorSpec::Builder(QNativeInterface::QAndroidApplication::context()). + KeyPairGeneratorSpec::Builder(Context(QNativeInterface::QAndroidApplication::context())). #else - KeyPairGeneratorSpec::Builder((jobject)QNativeInterface::QAndroidApplication::context()). + KeyPairGeneratorSpec::Builder(Context((jobject)QNativeInterface::QAndroidApplication::context())). #endif setAlias(alias). setSubject(X500Principal(QStringLiteral("CN=QtKeychain, O=Android Authority"))). From 25d6d2c0a1e9e3e375ca138c7c9192ea085f211e Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Fri, 12 May 2023 10:04:35 +0200 Subject: [PATCH 117/168] Android: Fix JNI signature for deletion Fixes #217 --- androidkeystore.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/androidkeystore.cpp b/androidkeystore.cpp index 3f510f8e..da58084e 100644 --- a/androidkeystore.cpp +++ b/androidkeystore.cpp @@ -115,7 +115,7 @@ bool KeyStore::containsAlias(const QString &alias) const bool KeyStore::deleteEntry(const QString &alias) const { - callMethod("deleteEntry", "(Ljava/lang/String;)Z", fromString(alias).object()); + callMethod("deleteEntry", "(Ljava/lang/String;)V", fromString(alias).object()); return handleExceptions(); } From 30571f8e9927177651a1f17ab6691aefb615e061 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Fri, 12 May 2023 12:19:46 +0200 Subject: [PATCH 118/168] Update Changelog for 0.14.0 --- ChangeLog | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ChangeLog b/ChangeLog index 3c420af7..7852f2a3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,14 @@ ChangeLog ========= +version 0.14.0 (release 2023-05-12) + + - Add Qt 6 Android support (Igor Bugaev ) + - Add QtQuick client example ((Igor Bugaev ) + - Added Dutch translation (Heimen Stoffels ) + - Fix potential freezing with Apple keychain (Claudio Cambra ) + - Add API to check whether a secure backend is available at all (Volker Krause ) + version 0.13.2 (release 2021-11-18) - CMake: Deprecate QTKEYCHAIN_STATIC in favor of BUILD_SHARED_LIBS (be@mixxx.org) From e63da2868465db18eb35a312b2635c26fdc46923 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Fri, 12 May 2023 12:22:02 +0200 Subject: [PATCH 119/168] Update version to 0.14.0 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f106ad20..c25a9688 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16) -set(QTKEYCHAIN_VERSION 0.13.99) +set(QTKEYCHAIN_VERSION 0.14.0) set(QTKEYCHAIN_SOVERSION 1) project(qtkeychain VERSION ${QTKEYCHAIN_VERSION} LANGUAGES CXX) From 96d2f84944880b63c78b28274f113795d88a3235 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Fri, 12 May 2023 12:22:57 +0200 Subject: [PATCH 120/168] Bump version to 0.14.99 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c25a9688..51523fb4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16) -set(QTKEYCHAIN_VERSION 0.14.0) +set(QTKEYCHAIN_VERSION 0.14.99) set(QTKEYCHAIN_SOVERSION 1) project(qtkeychain VERSION ${QTKEYCHAIN_VERSION} LANGUAGES CXX) From 20952955496ae9154e86307979263cc37e9114f0 Mon Sep 17 00:00:00 2001 From: Volker Krause Date: Fri, 26 May 2023 16:23:19 +0200 Subject: [PATCH 121/168] Protect against creating the QtKeychain::QtKeychain alias target twice This can happen if a dependency CMake config module is itself also looking for QtKeychain, which then results in a fatal CMake error. Observed between NeoChat and libQuotient. --- QtKeychainConfig.cmake.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QtKeychainConfig.cmake.in b/QtKeychainConfig.cmake.in index 7addba9f..ead0005a 100644 --- a/QtKeychainConfig.cmake.in +++ b/QtKeychainConfig.cmake.in @@ -21,7 +21,7 @@ endif() set(QTKEYCHAIN_LIBRARIES "@QTKEYCHAIN_TARGET_NAME@") get_target_property(QTKEYCHAIN_INCLUDE_DIRS "@QTKEYCHAIN_TARGET_NAME@" INTERFACE_INCLUDE_DIRECTORIES) -if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.18.0) +if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.18.0 AND NOT TARGET Qt@QTKEYCHAIN_VERSION_INFIX@Keychain::Qt@QTKEYCHAIN_VERSION_INFIX@Keychain) add_library(Qt@QTKEYCHAIN_VERSION_INFIX@Keychain::Qt@QTKEYCHAIN_VERSION_INFIX@Keychain ALIAS qt@QTKEYCHAIN_VERSION_INFIX@keychain) endif() From 269eacf1074d13eb2a4124beab8d754938e7db02 Mon Sep 17 00:00:00 2001 From: Volker Krause Date: Fri, 26 May 2023 17:13:06 +0200 Subject: [PATCH 122/168] Export QKeychain::isAvailable() to make it usable in a shared build Also, the definitions needs to be fully qualified, otherwise this is producing symbols in the top-level namespace instead. --- keychain.h | 2 +- keychain_android.cpp | 2 +- keychain_apple.mm | 2 +- keychain_haiku.cpp | 2 +- keychain_unix.cpp | 2 +- keychain_win.cpp | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/keychain.h b/keychain.h index 47dbecef..8c006cbc 100644 --- a/keychain.h +++ b/keychain.h @@ -275,7 +275,7 @@ class QKEYCHAIN_EXPORT DeletePasswordJob : public Job { * * @since 0.14.0 */ -bool isAvailable(); +QKEYCHAIN_EXPORT bool isAvailable(); } // namespace QtKeychain diff --git a/keychain_android.cpp b/keychain_android.cpp index 903c73ee..8cea4847 100644 --- a/keychain_android.cpp +++ b/keychain_android.cpp @@ -189,7 +189,7 @@ void DeletePasswordJobPrivate::scheduledStart() q->emitFinished(); } -bool isAvailable() +bool QKeychain::isAvailable() { return true; } diff --git a/keychain_apple.mm b/keychain_apple.mm index 48301104..f9a8266b 100644 --- a/keychain_apple.mm +++ b/keychain_apple.mm @@ -257,7 +257,7 @@ static void StartDeletePassword(const QString &service, const QString &key, Appl StartDeletePassword(service, key, interface); } -bool isAvailable() +bool QKeychain::isAvailable() { return true; } diff --git a/keychain_haiku.cpp b/keychain_haiku.cpp index 58f15e61..16abe1c4 100644 --- a/keychain_haiku.cpp +++ b/keychain_haiku.cpp @@ -186,7 +186,7 @@ void DeletePasswordJobPrivate::scheduledStart() q->emitFinishedWithError( error, errorString ); } -bool isAvailable() +bool QKeychain::isAvailable() { return true; } diff --git a/keychain_unix.cpp b/keychain_unix.cpp index 0a9a8ad4..d60cb773 100644 --- a/keychain_unix.cpp +++ b/keychain_unix.cpp @@ -589,7 +589,7 @@ void DeletePasswordJobPrivate::fallbackOnError(const QDBusError &err) { q->emitFinished(); } -bool isAvailable() +bool QKeychain::isAvailable() { return LibSecretKeyring::isAvailable() || GnomeKeyring::isAvailable() || isKwallet5Available(); } diff --git a/keychain_win.cpp b/keychain_win.cpp index b967c089..98dd34cb 100644 --- a/keychain_win.cpp +++ b/keychain_win.cpp @@ -187,7 +187,7 @@ void DeletePasswordJobPrivate::scheduledStart() { } #endif -bool isAvailable() +bool QKeychain::isAvailable() { return true; } From 69f993c47efed7e557d79a30a367014d9a27d809 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Thu, 1 Jun 2023 13:38:35 +0200 Subject: [PATCH 123/168] Prepare 0.14.1 --- CMakeLists.txt | 2 +- ChangeLog | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c25a9688..1ab9f886 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16) -set(QTKEYCHAIN_VERSION 0.14.0) +set(QTKEYCHAIN_VERSION 0.14.1) set(QTKEYCHAIN_SOVERSION 1) project(qtkeychain VERSION ${QTKEYCHAIN_VERSION} LANGUAGES CXX) diff --git a/ChangeLog b/ChangeLog index 7852f2a3..cb383c52 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,11 @@ ChangeLog ========= +version 0.14.1 (release 2023-06-01) + + - Export QKeychain::isAvailable() to make it usable in a shared build (Volker Krause ) + - Protect against creating the QtKeychain::QtKeychain alias target twice (Volker Krause ) + version 0.14.0 (release 2023-05-12) - Add Qt 6 Android support (Igor Bugaev ) From c3ce62c142700d02432414c2452f62c986867b9e Mon Sep 17 00:00:00 2001 From: Volker Krause Date: Thu, 29 Jun 2023 17:39:40 +0200 Subject: [PATCH 124/168] Add support for KWallet 6 See also https://invent.kde.org/frameworks/kwallet/-/merge_requests/58. --- keychain_unix.cpp | 66 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 14 deletions(-) diff --git a/keychain_unix.cpp b/keychain_unix.cpp index d60cb773..a3e83a96 100644 --- a/keychain_unix.cpp +++ b/keychain_unix.cpp @@ -19,24 +19,35 @@ enum KeyringBackend { Backend_LibSecretKeyring, Backend_GnomeKeyring, Backend_Kwallet4, - Backend_Kwallet5 + Backend_Kwallet5, + Backend_Kwallet6, }; enum DesktopEnvironment { DesktopEnv_Gnome, DesktopEnv_Kde4, DesktopEnv_Plasma5, + DesktopEnv_Plasma6, DesktopEnv_Unity, DesktopEnv_Xfce, DesktopEnv_Other }; +static constexpr const char KWALLET6_DBUS_IFACE[] = "org.kde.kwalletd6"; +static constexpr const char KWALLET6_DBUS_PATH[] = "/modules/kwalletd6"; +static constexpr const char KWALLET5_DBUS_IFACE[] = "org.kde.kwalletd5"; +static constexpr const char KWALLET5_DBUS_PATH[] = "/modules/kwalletd5"; +static constexpr const char KWALLET4_DBUS_IFACE[] = "org.kde.kwalletd"; +static constexpr const char KWALLET4_DBUS_PATH[] = "/modules/kwalletd"; + // the following detection algorithm is derived from chromium, // licensed under BSD, see base/nix/xdg_util.cc static DesktopEnvironment getKdeVersion() { QByteArray value = qgetenv("KDE_SESSION_VERSION"); - if ( value == "5" ) { + if ( value == "6" ) { + return DesktopEnv_Plasma6; + } else if ( value == "5" ) { return DesktopEnv_Plasma5; } else if (value == "4" ) { return DesktopEnv_Kde4; @@ -78,14 +89,14 @@ static DesktopEnvironment detectDesktopEnvironment() { return DesktopEnv_Other; } -static bool isKwallet5Available() +static bool isKwalletAvailable(const char *dbusIface, const char *dbusPath) { if (!QDBusConnection::sessionBus().isConnected()) return false; org::kde::KWallet iface( - QLatin1String("org.kde.kwalletd5"), - QLatin1String("/modules/kwalletd5"), + QLatin1String(dbusIface), + QLatin1String(dbusPath), QDBusConnection::sessionBus()); // At this point iface.isValid() can return false even though the @@ -118,7 +129,7 @@ static KeyringBackend detectKeyringBackend() return Backend_Kwallet4; case DesktopEnv_Plasma5: - if (isKwallet5Available()) { + if (isKwalletAvailable(KWALLET5_DBUS_IFACE, KWALLET5_DBUS_PATH)) { return Backend_Kwallet5; } if (LibSecretKeyring::isAvailable()) { @@ -130,6 +141,19 @@ static KeyringBackend detectKeyringBackend() // During startup the keychain backend might just not have started yet return Backend_Kwallet5; + case DesktopEnv_Plasma6: + if (isKwalletAvailable(KWALLET6_DBUS_IFACE, KWALLET6_DBUS_PATH)) { + return Backend_Kwallet6; + } + if (LibSecretKeyring::isAvailable()) { + return Backend_LibSecretKeyring; + } + if (GnomeKeyring::isAvailable()) { + return Backend_GnomeKeyring; + } + // During startup the keychain backend might just not have started yet + return Backend_Kwallet6; + case DesktopEnv_Gnome: case DesktopEnv_Unity: case DesktopEnv_Xfce: @@ -141,7 +165,10 @@ static KeyringBackend detectKeyringBackend() if (GnomeKeyring::isAvailable()) { return Backend_GnomeKeyring; } - if (isKwallet5Available()) { + if (isKwalletAvailable(KWALLET6_DBUS_IFACE, KWALLET6_DBUS_PATH)) { + return Backend_Kwallet6; + } + if (isKwalletAvailable(KWALLET5_DBUS_IFACE, KWALLET5_DBUS_PATH)) { return Backend_Kwallet5; } // During startup the keychain backend might just not have started yet @@ -198,10 +225,13 @@ void ReadPasswordJobPrivate::scheduledStart() { break; case Backend_Kwallet4: - kwalletReadPasswordScheduledStartImpl("org.kde.kwalletd", "/modules/kwalletd", this); + kwalletReadPasswordScheduledStartImpl(KWALLET4_DBUS_IFACE, KWALLET4_DBUS_PATH, this); break; case Backend_Kwallet5: - kwalletReadPasswordScheduledStartImpl("org.kde.kwalletd5", "/modules/kwalletd5", this); + kwalletReadPasswordScheduledStartImpl(KWALLET5_DBUS_IFACE, KWALLET5_DBUS_PATH, this); + break; + case Backend_Kwallet6: + kwalletReadPasswordScheduledStartImpl(KWALLET6_DBUS_IFACE, KWALLET6_DBUS_PATH, this); break; } } @@ -454,10 +484,13 @@ void WritePasswordJobPrivate::scheduledStart() { break; case Backend_Kwallet4: - kwalletWritePasswordScheduledStart("org.kde.kwalletd", "/modules/kwalletd", this); + kwalletWritePasswordScheduledStart(KWALLET4_DBUS_IFACE, KWALLET4_DBUS_PATH, this); break; case Backend_Kwallet5: - kwalletWritePasswordScheduledStart("org.kde.kwalletd5", "/modules/kwalletd5", this); + kwalletWritePasswordScheduledStart(KWALLET5_DBUS_IFACE, KWALLET5_DBUS_PATH, this); + break; + case Backend_Kwallet6: + kwalletWritePasswordScheduledStart(KWALLET6_DBUS_IFACE, KWALLET6_DBUS_PATH, this); break; } } @@ -562,10 +595,13 @@ void DeletePasswordJobPrivate::scheduledStart() { break; case Backend_Kwallet4: - kwalletWritePasswordScheduledStart("org.kde.kwalletd", "/modules/kwalletd", this); + kwalletWritePasswordScheduledStart(KWALLET4_DBUS_IFACE, KWALLET4_DBUS_PATH, this); break; case Backend_Kwallet5: - kwalletWritePasswordScheduledStart("org.kde.kwalletd5", "/modules/kwalletd5", this); + kwalletWritePasswordScheduledStart(KWALLET5_DBUS_IFACE, KWALLET5_DBUS_PATH, this); + break; + case Backend_Kwallet6: + kwalletWritePasswordScheduledStart(KWALLET6_DBUS_IFACE, KWALLET6_DBUS_PATH, this); break; } } @@ -591,5 +627,7 @@ void DeletePasswordJobPrivate::fallbackOnError(const QDBusError &err) { bool QKeychain::isAvailable() { - return LibSecretKeyring::isAvailable() || GnomeKeyring::isAvailable() || isKwallet5Available(); + return LibSecretKeyring::isAvailable() || GnomeKeyring::isAvailable() + || isKwalletAvailable(KWALLET6_DBUS_IFACE, KWALLET6_DBUS_PATH) + || isKwalletAvailable(KWALLET5_DBUS_IFACE, KWALLET5_DBUS_PATH); } From 22bac4191f1977f64ee6c7116e5d807407ee2003 Mon Sep 17 00:00:00 2001 From: David Faure Date: Wed, 13 Dec 2023 23:33:11 +0100 Subject: [PATCH 125/168] Move headers (and cpp files) to a qtkeychain subdir ... and use target_include_directories() so that one can include rather than when using this repository as a git submodule. --- CMakeLists.txt | 126 ++---------------- qtkeychain.pri | 4 +- qtkeychain/CMakeLists.txt | 118 ++++++++++++++++ .../androidkeystore.cpp | 0 .../androidkeystore_p.h | 0 .../gnomekeyring.cpp | 0 .../gnomekeyring_p.h | 0 keychain.cpp => qtkeychain/keychain.cpp | 0 keychain.h => qtkeychain/keychain.h | 0 .../keychain_android.cpp | 0 .../keychain_apple.mm | 0 .../keychain_haiku.cpp | 0 keychain_p.h => qtkeychain/keychain_p.h | 0 .../keychain_unix.cpp | 0 .../keychain_win.cpp | 0 libsecret.cpp => qtkeychain/libsecret.cpp | 0 libsecret_p.h => qtkeychain/libsecret_p.h | 0 .../org.kde.KWallet.xml | 0 .../plaintextstore.cpp | 0 .../plaintextstore_p.h | 0 testclient.cpp | 2 +- 21 files changed, 131 insertions(+), 119 deletions(-) create mode 100644 qtkeychain/CMakeLists.txt rename androidkeystore.cpp => qtkeychain/androidkeystore.cpp (100%) rename androidkeystore_p.h => qtkeychain/androidkeystore_p.h (100%) rename gnomekeyring.cpp => qtkeychain/gnomekeyring.cpp (100%) rename gnomekeyring_p.h => qtkeychain/gnomekeyring_p.h (100%) rename keychain.cpp => qtkeychain/keychain.cpp (100%) rename keychain.h => qtkeychain/keychain.h (100%) rename keychain_android.cpp => qtkeychain/keychain_android.cpp (100%) rename keychain_apple.mm => qtkeychain/keychain_apple.mm (100%) rename keychain_haiku.cpp => qtkeychain/keychain_haiku.cpp (100%) rename keychain_p.h => qtkeychain/keychain_p.h (100%) rename keychain_unix.cpp => qtkeychain/keychain_unix.cpp (100%) rename keychain_win.cpp => qtkeychain/keychain_win.cpp (100%) rename libsecret.cpp => qtkeychain/libsecret.cpp (100%) rename libsecret_p.h => qtkeychain/libsecret_p.h (100%) rename org.kde.KWallet.xml => qtkeychain/org.kde.KWallet.xml (100%) rename plaintextstore.cpp => qtkeychain/plaintextstore.cpp (100%) rename plaintextstore_p.h => qtkeychain/plaintextstore_p.h (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 51523fb4..6e18b2bf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ include(FindPkgConfig) ### -set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${PROJECT_SOURCE_DIR}/cmake/Modules") +list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/Modules") include(GNUInstallDirs) include(GenerateExportHeader) include(CMakePackageConfigHelpers) @@ -121,92 +121,15 @@ else() set(QTCORE_LIBRARIES ${Qt6Core_LIBRARIES}) endif() +set(QTKEYCHAIN_TARGET_NAME qt${QTKEYCHAIN_VERSION_INFIX}keychain) -include_directories(${CMAKE_CURRENT_BINARY_DIR}) - -list(APPEND qtkeychain_LIBRARIES ${QTCORE_LIBRARIES}) -set(qtkeychain_SOURCES - keychain.cpp - qkeychain_export.h - keychain.h -) - -if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") - # CMake < 3.15 sneaks in /W# flags for us, so we need a replacement, - # or we'll get a warning (cf. CMP0092) - if (CMAKE_CXX_FLAGS MATCHES "/W[0-4]") - string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") - else() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") - endif() -else() - # MSVC's STL / Qt headers are not MSVC -Wall clean, so don't enable it there - add_definitions( -Wall -Werror=return-type ) -endif() - -if(WIN32) - list(APPEND qtkeychain_SOURCES keychain_win.cpp) - if (NOT USE_CREDENTIAL_STORE) - list(APPEND qtkeychain_LIBRARIES crypt32) - list(APPEND qtkeychain_SOURCES plaintextstore.cpp) - endif() - #FIXME: mingw bug; otherwise getting undefined refs to RtlSecureZeroMemory there - if(MINGW) - add_definitions( -O2 ) - endif() -endif() - -if(APPLE) - list(APPEND qtkeychain_SOURCES keychain_apple.mm) - list(APPEND qtkeychain_LIBRARIES "-framework Foundation" "-framework Security") -endif() - -if(HAIKU) - list(APPEND qtkeychain_SOURCES keychain_haiku.cpp) - - find_library(BE_LIBRARY be REQUIRED) - list(APPEND qtkeychain_LIBRARIES ${BE_LIBRARY}) -endif() - -if(UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU) - option(LIBSECRET_SUPPORT "Build with libsecret support" ON) - - if(LIBSECRET_SUPPORT) - pkg_check_modules(LIBSECRET REQUIRED libsecret-1) - add_definitions(-DHAVE_LIBSECRET=1) - INCLUDE_DIRECTORIES(${LIBSECRET_INCLUDE_DIRS}) - LINK_DIRECTORIES(${LIBSECRET_LIBRARY_DIRS}) - list(APPEND qtkeychain_LIBRARIES_PRIVATE ${LIBSECRET_LIBRARIES}) - endif() - - add_definitions(-DKEYCHAIN_DBUS=1) - list(APPEND qtkeychain_SOURCES keychain_unix.cpp gnomekeyring.cpp libsecret.cpp plaintextstore.cpp) - qt_add_dbus_interface(qtkeychain_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/org.kde.KWallet.xml kwallet_interface KWalletInterface) - list(APPEND qtkeychain_LIBRARIES ${QTDBUS_LIBRARIES} ) -endif() - -if(ANDROID) - list(APPEND qtkeychain_SOURCES keychain_android.cpp androidkeystore.cpp plaintextstore.cpp) - list(APPEND qtkeychain_LIBRARIES_PRIVATE ${QTANDROIDEXTRAS_LIBRARIES} ) -endif() - -QT_WRAP_CPP(qtkeychain_MOC_OUTFILES keychain.h keychain_p.h gnomekeyring_p.h) - -set(qtkeychain_TR_FILES - translations/qtkeychain_de.ts - translations/qtkeychain_fr.ts - translations/qtkeychain_ro.ts - translations/qtkeychain_ru.ts - translations/qtkeychain_zh.ts -) +add_subdirectory(qtkeychain) -set(QTKEYCHAIN_TARGET_NAME qt${QTKEYCHAIN_VERSION_INFIX}keychain) -add_library(${QTKEYCHAIN_TARGET_NAME} ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES} ${qtkeychain_QM_FILES}) -if(WIN32) - set_target_properties( ${QTKEYCHAIN_TARGET_NAME} PROPERTIES DEBUG_POSTFIX "d" ) -endif() +### +### Translations +### -file(GLOB qtkeychain_TR_SOURCES *.cpp *.h *.ui) +file(GLOB qtkeychain_TR_SOURCES qtkeychain/*.cpp qtkeychain/*.h qtkeychain/*.ui) if ( BUILD_TRANSLATIONS ) qt_create_translation(qtkeychain_MESSAGES ${qtkeychain_TR_SOURCES} ${qtkeychain_TR_FILES}) qt_add_translation(qtkeychain_QM_FILES ${qtkeychain_TR_FILES}) @@ -239,39 +162,10 @@ if ( BUILD_TRANSLATIONS ) endif() endif( BUILD_TRANSLATIONS ) -target_link_libraries(${QTKEYCHAIN_TARGET_NAME} PUBLIC ${qtkeychain_LIBRARIES} PRIVATE ${qtkeychain_LIBRARIES_PRIVATE}) -if(NOT INTERFACE_INCLUDE_SUFFIX) - set(INTERFACE_INCLUDE_SUFFIX include) -endif() -target_include_directories(${QTKEYCHAIN_TARGET_NAME} PUBLIC $) -generate_export_header(${QTKEYCHAIN_TARGET_NAME} - EXPORT_FILE_NAME qkeychain_export.h - EXPORT_MACRO_NAME QKEYCHAIN_EXPORT -) - -set_target_properties(${QTKEYCHAIN_TARGET_NAME} PROPERTIES - VERSION ${QTKEYCHAIN_VERSION} - SOVERSION ${QTKEYCHAIN_SOVERSION} - INSTALL_RPATH_USE_LINK_PATH TRUE -) - -if (NOT APPLE) - set_target_properties(${QTKEYCHAIN_TARGET_NAME} PROPERTIES - INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}" - ) -endif() - -install(FILES keychain.h ${CMAKE_CURRENT_BINARY_DIR}/qkeychain_export.h - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/qt${QTKEYCHAIN_VERSION_INFIX}keychain/ -) - -install(TARGETS ${QTKEYCHAIN_TARGET_NAME} - EXPORT Qt${QTKEYCHAIN_VERSION_INFIX}KeychainLibraryDepends - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} -) +### +### Test application ("testclient") +### if(BUILD_TEST_APPLICATION) set( testclient_LIBRARIES ${QTKEYCHAIN_TARGET_NAME} ) diff --git a/qtkeychain.pri b/qtkeychain.pri index 5877641c..4dc56d1e 100644 --- a/qtkeychain.pri +++ b/qtkeychain.pri @@ -6,7 +6,7 @@ lessThan(QT_MAJOR_VERSION, 5) { error("qtkeychain requires Qt 5 or later") } -QTKEYCHAIN_PWD = $$PWD +QTKEYCHAIN_PWD = $$PWD/qtkeychain CONFIG += depend_includepath DEFINES += QTKEYCHAIN_NO_EXPORT @@ -43,7 +43,7 @@ unix:!android:!macx:!ios { # Generate D-Bus interface: DEFINES += KEYCHAIN_DBUS QT += dbus - kwallet_interface.files = $$PWD/org.kde.KWallet.xml + kwallet_interface.files = $$QTKEYCHAIN_PWD/org.kde.KWallet.xml DBUS_INTERFACES += kwallet_interface HEADERS += \ diff --git a/qtkeychain/CMakeLists.txt b/qtkeychain/CMakeLists.txt new file mode 100644 index 00000000..f9e28ce3 --- /dev/null +++ b/qtkeychain/CMakeLists.txt @@ -0,0 +1,118 @@ + +list(APPEND qtkeychain_LIBRARIES ${QTCORE_LIBRARIES}) +set(qtkeychain_SOURCES + keychain.cpp + qkeychain_export.h + keychain.h +) + +if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + # CMake < 3.15 sneaks in /W# flags for us, so we need a replacement, + # or we'll get a warning (cf. CMP0092) + if (CMAKE_CXX_FLAGS MATCHES "/W[0-4]") + string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") + endif() +else() + # MSVC's STL / Qt headers are not MSVC -Wall clean, so don't enable it there + add_definitions( -Wall -Werror=return-type ) +endif() + +if(WIN32) + list(APPEND qtkeychain_SOURCES keychain_win.cpp) + if (NOT USE_CREDENTIAL_STORE) + list(APPEND qtkeychain_LIBRARIES crypt32) + list(APPEND qtkeychain_SOURCES plaintextstore.cpp) + endif() + #FIXME: mingw bug; otherwise getting undefined refs to RtlSecureZeroMemory there + if(MINGW) + add_definitions( -O2 ) + endif() +endif() + +if(APPLE) + list(APPEND qtkeychain_SOURCES keychain_apple.mm) + list(APPEND qtkeychain_LIBRARIES "-framework Foundation" "-framework Security") +endif() + +if(HAIKU) + list(APPEND qtkeychain_SOURCES keychain_haiku.cpp) + + find_library(BE_LIBRARY be REQUIRED) + list(APPEND qtkeychain_LIBRARIES ${BE_LIBRARY}) +endif() + +if(UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU) + option(LIBSECRET_SUPPORT "Build with libsecret support" ON) + + if(LIBSECRET_SUPPORT) + pkg_check_modules(LIBSECRET REQUIRED libsecret-1) + add_definitions(-DHAVE_LIBSECRET=1) + INCLUDE_DIRECTORIES(${LIBSECRET_INCLUDE_DIRS}) + LINK_DIRECTORIES(${LIBSECRET_LIBRARY_DIRS}) + list(APPEND qtkeychain_LIBRARIES_PRIVATE ${LIBSECRET_LIBRARIES}) + endif() + + add_definitions(-DKEYCHAIN_DBUS=1) + list(APPEND qtkeychain_SOURCES keychain_unix.cpp gnomekeyring.cpp libsecret.cpp plaintextstore.cpp) + qt_add_dbus_interface(qtkeychain_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/org.kde.KWallet.xml kwallet_interface KWalletInterface) + list(APPEND qtkeychain_LIBRARIES ${QTDBUS_LIBRARIES} ) +endif() + +if(ANDROID) + list(APPEND qtkeychain_SOURCES keychain_android.cpp androidkeystore.cpp plaintextstore.cpp) + list(APPEND qtkeychain_LIBRARIES_PRIVATE ${QTANDROIDEXTRAS_LIBRARIES} ) +endif() + +QT_WRAP_CPP(qtkeychain_MOC_OUTFILES keychain.h keychain_p.h gnomekeyring_p.h) + +set(qtkeychain_TR_FILES + translations/qtkeychain_de.ts + translations/qtkeychain_fr.ts + translations/qtkeychain_ro.ts + translations/qtkeychain_ru.ts + translations/qtkeychain_zh.ts +) + +add_library(${QTKEYCHAIN_TARGET_NAME} ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES} ${qtkeychain_QM_FILES}) +if(WIN32) + set_target_properties( ${QTKEYCHAIN_TARGET_NAME} PROPERTIES DEBUG_POSTFIX "d" ) +endif() + +target_link_libraries(${QTKEYCHAIN_TARGET_NAME} PUBLIC ${qtkeychain_LIBRARIES} PRIVATE ${qtkeychain_LIBRARIES_PRIVATE}) +if(NOT INTERFACE_INCLUDE_SUFFIX) + set(INTERFACE_INCLUDE_SUFFIX include) +endif() +# Where to find includes when building the library and using it uninstalled: in the parent dir, so that the code has to use +target_include_directories(${QTKEYCHAIN_TARGET_NAME} PUBLIC $ $) +# Where to find includes when using the installed qtkeychain +target_include_directories(${QTKEYCHAIN_TARGET_NAME} INTERFACE $) + +generate_export_header(${QTKEYCHAIN_TARGET_NAME} + EXPORT_FILE_NAME qkeychain_export.h + EXPORT_MACRO_NAME QKEYCHAIN_EXPORT +) + +set_target_properties(${QTKEYCHAIN_TARGET_NAME} PROPERTIES + VERSION ${QTKEYCHAIN_VERSION} + SOVERSION ${QTKEYCHAIN_SOVERSION} + INSTALL_RPATH_USE_LINK_PATH TRUE +) + +if (NOT APPLE) + set_target_properties(${QTKEYCHAIN_TARGET_NAME} PROPERTIES + INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}" + ) +endif() + +install(FILES keychain.h ${CMAKE_CURRENT_BINARY_DIR}/qkeychain_export.h + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/qt${QTKEYCHAIN_VERSION_INFIX}keychain/ +) + +install(TARGETS ${QTKEYCHAIN_TARGET_NAME} + EXPORT Qt${QTKEYCHAIN_VERSION_INFIX}KeychainLibraryDepends + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} +) diff --git a/androidkeystore.cpp b/qtkeychain/androidkeystore.cpp similarity index 100% rename from androidkeystore.cpp rename to qtkeychain/androidkeystore.cpp diff --git a/androidkeystore_p.h b/qtkeychain/androidkeystore_p.h similarity index 100% rename from androidkeystore_p.h rename to qtkeychain/androidkeystore_p.h diff --git a/gnomekeyring.cpp b/qtkeychain/gnomekeyring.cpp similarity index 100% rename from gnomekeyring.cpp rename to qtkeychain/gnomekeyring.cpp diff --git a/gnomekeyring_p.h b/qtkeychain/gnomekeyring_p.h similarity index 100% rename from gnomekeyring_p.h rename to qtkeychain/gnomekeyring_p.h diff --git a/keychain.cpp b/qtkeychain/keychain.cpp similarity index 100% rename from keychain.cpp rename to qtkeychain/keychain.cpp diff --git a/keychain.h b/qtkeychain/keychain.h similarity index 100% rename from keychain.h rename to qtkeychain/keychain.h diff --git a/keychain_android.cpp b/qtkeychain/keychain_android.cpp similarity index 100% rename from keychain_android.cpp rename to qtkeychain/keychain_android.cpp diff --git a/keychain_apple.mm b/qtkeychain/keychain_apple.mm similarity index 100% rename from keychain_apple.mm rename to qtkeychain/keychain_apple.mm diff --git a/keychain_haiku.cpp b/qtkeychain/keychain_haiku.cpp similarity index 100% rename from keychain_haiku.cpp rename to qtkeychain/keychain_haiku.cpp diff --git a/keychain_p.h b/qtkeychain/keychain_p.h similarity index 100% rename from keychain_p.h rename to qtkeychain/keychain_p.h diff --git a/keychain_unix.cpp b/qtkeychain/keychain_unix.cpp similarity index 100% rename from keychain_unix.cpp rename to qtkeychain/keychain_unix.cpp diff --git a/keychain_win.cpp b/qtkeychain/keychain_win.cpp similarity index 100% rename from keychain_win.cpp rename to qtkeychain/keychain_win.cpp diff --git a/libsecret.cpp b/qtkeychain/libsecret.cpp similarity index 100% rename from libsecret.cpp rename to qtkeychain/libsecret.cpp diff --git a/libsecret_p.h b/qtkeychain/libsecret_p.h similarity index 100% rename from libsecret_p.h rename to qtkeychain/libsecret_p.h diff --git a/org.kde.KWallet.xml b/qtkeychain/org.kde.KWallet.xml similarity index 100% rename from org.kde.KWallet.xml rename to qtkeychain/org.kde.KWallet.xml diff --git a/plaintextstore.cpp b/qtkeychain/plaintextstore.cpp similarity index 100% rename from plaintextstore.cpp rename to qtkeychain/plaintextstore.cpp diff --git a/plaintextstore_p.h b/qtkeychain/plaintextstore_p.h similarity index 100% rename from plaintextstore_p.h rename to qtkeychain/plaintextstore_p.h diff --git a/testclient.cpp b/testclient.cpp index f745bd37..9aed138d 100644 --- a/testclient.cpp +++ b/testclient.cpp @@ -16,7 +16,7 @@ #include -#include "keychain.h" +#include "qtkeychain/keychain.h" #include using namespace QKeychain; From 8eac9889775ca45c106d077bb0d3744754a1570a Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Sun, 17 Dec 2023 08:52:04 +0100 Subject: [PATCH 126/168] Prepare 0.14.2 --- CMakeLists.txt | 2 +- ChangeLog | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ab9f886..0a386c16 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16) -set(QTKEYCHAIN_VERSION 0.14.1) +set(QTKEYCHAIN_VERSION 0.14.2) set(QTKEYCHAIN_SOVERSION 1) project(qtkeychain VERSION ${QTKEYCHAIN_VERSION} LANGUAGES CXX) diff --git a/ChangeLog b/ChangeLog index cb383c52..6d8b6ebc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,10 @@ ChangeLog ========= +version 0.14.2 (release 2023-12-17) + + - Add support for KWallet 6 (Volker Krause ) + version 0.14.1 (release 2023-06-01) - Export QKeychain::isAvailable() to make it usable in a shared build (Volker Krause ) From ef6dec2731b4e19c593837ba6625a280e4237bf8 Mon Sep 17 00:00:00 2001 From: Volker Krause Date: Fri, 3 May 2024 18:32:19 +0200 Subject: [PATCH 127/168] Fix build with Qt 6.7 on Android QNativeInterface::QAndroidApplication::context() changed its return type. --- keychain_android.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/keychain_android.cpp b/keychain_android.cpp index 8cea4847..8efecf97 100644 --- a/keychain_android.cpp +++ b/keychain_android.cpp @@ -107,8 +107,10 @@ void WritePasswordJobPrivate::scheduledStart() KeyPairGeneratorSpec::Builder(Context(QtAndroid::androidActivity())). #elif QT_VERSION < QT_VERSION_CHECK(6, 4, 0) KeyPairGeneratorSpec::Builder(Context(QNativeInterface::QAndroidApplication::context())). - #else + #elif QT_VERSION < QT_VERSION_CHECK(6, 7, 0) KeyPairGeneratorSpec::Builder(Context((jobject)QNativeInterface::QAndroidApplication::context())). + #else + KeyPairGeneratorSpec::Builder(Context(QNativeInterface::QAndroidApplication::context().object())). #endif setAlias(alias). setSubject(X500Principal(QStringLiteral("CN=QtKeychain, O=Android Authority"))). From 38ff50766c7e4ec490fecb2d9939b7cdc4b74b8e Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Fri, 3 May 2024 21:10:25 +0200 Subject: [PATCH 128/168] Prepare 0.14.3 --- CMakeLists.txt | 2 +- ChangeLog | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0a386c16..47a2d324 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16) -set(QTKEYCHAIN_VERSION 0.14.2) +set(QTKEYCHAIN_VERSION 0.14.3) set(QTKEYCHAIN_SOVERSION 1) project(qtkeychain VERSION ${QTKEYCHAIN_VERSION} LANGUAGES CXX) diff --git a/ChangeLog b/ChangeLog index 6d8b6ebc..cf294c34 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,10 @@ ChangeLog ========= +version 0.14.3 (release 2024-05-03) + + - Fix Android build for Qt 6.7 (Volker Krause ) + version 0.14.2 (release 2023-12-17) - Add support for KWallet 6 (Volker Krause ) From bc4b560fe7e3a8be58b96bd7d2a0b680628db2a2 Mon Sep 17 00:00:00 2001 From: Hannah von Reth Date: Fri, 19 Jul 2024 10:25:18 +0200 Subject: [PATCH 129/168] Modernise casts in keychain_win.cpp --- qtkeychain/keychain_win.cpp | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/qtkeychain/keychain_win.cpp b/qtkeychain/keychain_win.cpp index 98dd34cb..509d2a43 100644 --- a/qtkeychain/keychain_win.cpp +++ b/qtkeychain/keychain_win.cpp @@ -20,10 +20,9 @@ using namespace QKeychain; #include void ReadPasswordJobPrivate::scheduledStart() { - LPCWSTR name = (LPCWSTR)key.utf16(); PCREDENTIALW cred; - if (!CredReadW(name, CRED_TYPE_GENERIC, 0, &cred)) { + if (!CredReadW(reinterpret_cast(key.utf16()), CRED_TYPE_GENERIC, 0, &cred)) { Error err; QString msg; switch(GetLastError()) { @@ -41,23 +40,19 @@ void ReadPasswordJobPrivate::scheduledStart() { return; } - data = QByteArray((char*)cred->CredentialBlob, cred->CredentialBlobSize); + data = QByteArray(reinterpret_cast(cred->CredentialBlob), cred->CredentialBlobSize); CredFree(cred); q->emitFinished(); } void WritePasswordJobPrivate::scheduledStart() { - CREDENTIALW cred; - char *pwd = data.data(); - LPWSTR name = (LPWSTR)key.utf16(); - - memset(&cred, 0, sizeof(cred)); + CREDENTIALW cred = {}; cred.Comment = const_cast(L"QtKeychain"); cred.Type = CRED_TYPE_GENERIC; - cred.TargetName = name; + cred.TargetName = const_cast(reinterpret_cast(key.utf16())); cred.CredentialBlobSize = data.size(); - cred.CredentialBlob = (LPBYTE)pwd; + cred.CredentialBlob = reinterpret_cast(data.data()); cred.Persist = CRED_PERSIST_ENTERPRISE; if (CredWriteW(&cred, 0)) { @@ -65,7 +60,7 @@ void WritePasswordJobPrivate::scheduledStart() { return; } - DWORD err = GetLastError(); + const DWORD err = GetLastError(); // Detect size-exceeded errors and provide nicer messages. // Unfortunately these error codes aren't documented. @@ -93,9 +88,7 @@ void WritePasswordJobPrivate::scheduledStart() { } void DeletePasswordJobPrivate::scheduledStart() { - LPCWSTR name = (LPCWSTR)key.utf16(); - - if (!CredDeleteW(name, CRED_TYPE_GENERIC, 0)) { + if (!CredDeleteW(reinterpret_cast(key.utf16()), CRED_TYPE_GENERIC, 0)) { Error err; QString msg; switch(GetLastError()) { From 903572d8ce0eb8225e8b71e5e5dd8a3bf65d28d3 Mon Sep 17 00:00:00 2001 From: Hannah von Reth Date: Tue, 23 Jul 2024 10:44:03 +0200 Subject: [PATCH 130/168] Simplify execution of tests on Windows --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e18b2bf..240b6eb5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,11 @@ SET(CMAKE_CXX_STANDARD 11) include(FindPkgConfig) ### +# write binaries and libraries into a shared folder, this simplifies the execution of tests on Windows +# see https://github.com/KDE/extra-cmake-modules/blob/b3a13868f7f54ab0b7ac19fd26b6dfead8907d25/kde-modules/KDECMakeSettings.cmake#L256C1-L258C69 +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/Modules") include(GNUInstallDirs) From 538e6da4e62d1539cc620bb94b08175591922382 Mon Sep 17 00:00:00 2001 From: Hannah von Reth Date: Tue, 23 Jul 2024 10:40:32 +0200 Subject: [PATCH 131/168] Add simple test case --- CMakeLists.txt | 6 + autotest/CMakeLists.txt | 6 + autotest/basic.cpp | 70 ++++++++ cmake/Modules/ECMAddTests.cmake | 176 ++++++++++++++++++++ cmake/Modules/ECMMarkAsTest.cmake | 41 +++++ cmake/Modules/ECMMarkNonGuiExecutable.cmake | 30 ++++ 6 files changed, 329 insertions(+) create mode 100644 autotest/CMakeLists.txt create mode 100644 autotest/basic.cpp create mode 100644 cmake/Modules/ECMAddTests.cmake create mode 100644 cmake/Modules/ECMMarkAsTest.cmake create mode 100644 cmake/Modules/ECMMarkNonGuiExecutable.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 240b6eb5..43289a36 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -191,6 +191,12 @@ if(BUILD_TEST_APPLICATION) target_link_libraries( testclient ${testclient_LIBRARIES}) endif() +include(CTest) +if(BUILD_TESTING) + add_subdirectory(autotest) +endif() + + ### ### CMake config file diff --git a/autotest/CMakeLists.txt b/autotest/CMakeLists.txt new file mode 100644 index 00000000..2f4b94c3 --- /dev/null +++ b/autotest/CMakeLists.txt @@ -0,0 +1,6 @@ +include(ECMAddTests) + +find_package(Qt${QT_MAJOR_VERSION} COMPONENTS Test REQUIRED) + +ecm_add_tests(basic.cpp LINK_LIBRARIES ${QTKEYCHAIN_TARGET_NAME} Qt${QT_MAJOR_VERSION}::Test) +set_property(TARGET basic PROPERTY AUTOMOC ON) diff --git a/autotest/basic.cpp b/autotest/basic.cpp new file mode 100644 index 00000000..584eeebe --- /dev/null +++ b/autotest/basic.cpp @@ -0,0 +1,70 @@ +#include + +#include "qtkeychain/keychain.h" + +namespace +{ + QByteArray generateRandomString(qsizetype size) + { + std::vector buffer(size, 0); + QRandomGenerator::global()->fillRange(buffer.data(), size); + return QByteArray(reinterpret_cast(buffer.data()), static_cast(size * sizeof(quint32))).toBase64(QByteArray::Base64UrlEncoding).mid(0, size); + } + +} +class BasicTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void test_data() + { + QTest::addColumn("password"); + QTest::newRow("normal password") << QByteArrayLiteral("this is a password"); + QTest::newRow("1000") << generateRandomString(1000); + QTest::newRow("2000") << generateRandomString(2000); + QTest::newRow("3000") << generateRandomString(3000); + + } + + void test() + { +#ifdef Q_OS_MACOS + QSKIP("This test case has no access to the keychain") +#endif + const QString serviceKey = QStringLiteral("QtKeychainTest-%1").arg(QTest::currentDataTag()); + QFETCH(QByteArray, password); + { + QKeychain::WritePasswordJob writeJob(serviceKey); + writeJob.setKey(serviceKey); + writeJob.setBinaryData(password); + QSignalSpy writeSpy(&writeJob, &QKeychain::WritePasswordJob::finished); + writeJob.start(); + writeSpy.wait(); +#ifdef Q_OS_WIN + QEXPECT_FAIL("3000", "Maximum for Windows is 2560", Abort); +#endif + QCOMPARE(writeJob.error(), QKeychain::NoError); + } + { + QKeychain::ReadPasswordJob readJob(serviceKey); + readJob.setKey(serviceKey); + QSignalSpy readSpy(&readJob, &QKeychain::ReadPasswordJob::finished); + readJob.start(); + readSpy.wait(); + QCOMPARE(readJob.error(), QKeychain::NoError); + QCOMPARE(readJob.binaryData(), password); + } + { + QKeychain::DeletePasswordJob deleteJob(serviceKey); + deleteJob.setKey(serviceKey); + QSignalSpy deleteSpy(&deleteJob, &QKeychain::DeletePasswordJob::finished); + deleteJob.start(); + deleteSpy.wait(); + QCOMPARE(deleteJob.error(), QKeychain::NoError); + } + } +}; + +QTEST_MAIN(BasicTest) +#include "basic.moc" diff --git a/cmake/Modules/ECMAddTests.cmake b/cmake/Modules/ECMAddTests.cmake new file mode 100644 index 00000000..47a4d5c1 --- /dev/null +++ b/cmake/Modules/ECMAddTests.cmake @@ -0,0 +1,176 @@ +# SPDX-FileCopyrightText: 2013 Alexander Richardson +# SPDX-FileCopyrightText: 2015 Alex Merry +# +# SPDX-License-Identifier: BSD-3-Clause + +#[=======================================================================[.rst: +ECMAddTests +----------- + +Convenience functions for adding tests. + +:: + + ecm_add_tests( + LINK_LIBRARIES [ [...]] + [NAME_PREFIX ] + [GUI] + [TARGET_NAMES_VAR ] + [TEST_NAMES_VAR ] + [WORKING_DIRECTORY ] # Since 5.111 + ) + +A convenience function for adding multiple tests, each consisting of a +single source file. For each file in , an executable target will be +created (the name of which will be the basename of the source file). This +will be linked against the libraries given with ``LINK_LIBRARIES``. Each +executable will be added as a test with the same name. + +If ``NAME_PREFIX`` is given, this prefix will be prepended to the test names, but +not the target names. As a result, it will not prevent clashes between tests +with the same name in different parts of the project, but it can be used to +give an indication of where to look for a failing test. + +If the flag ``GUI`` is passed the test binaries will be GUI executables, otherwise +the resulting binaries will be console applications (regardless of the value +of ``CMAKE_WIN32_EXECUTABLE`` or ``CMAKE_MACOSX_BUNDLE``). Be aware that this changes +the executable entry point on Windows (although some frameworks, such as Qt, +abstract this difference away). + +The tests will be build with ``-DQT_FORCE_ASSERTS`` to enable assertions in the +test executable even for release builds. + +The ``TARGET_NAMES_VAR`` and ``TEST_NAMES_VAR`` arguments, if given, should specify a +variable name to receive the list of generated target and test names, +respectively. This makes it convenient to apply properties to them as a +whole, for example, using ``set_target_properties()`` or ``set_tests_properties()``. + +The generated target executables will have the effects of ``ecm_mark_as_test()`` +(from the :module:`ECMMarkAsTest` module) applied to it. + +``WORKING_DIRECTORY`` sets the test property `WORKING_DIRECTORY +`_ +in which to execute the test. By default the test will be run in +``${CMAKE_CURRENT_BINARY_DIR}``. The working directory can be specified using +generator expressions. Since 5.111. + +:: + + ecm_add_test( + + LINK_LIBRARIES [ [...]] + [TEST_NAME ] + [NAME_PREFIX ] + [GUI] + [WORKING_DIRECTORY ] # Since 5.111 + ) + +This is a single-test form of ``ecm_add_tests`` that allows multiple source files +to be used for a single test. If using multiple source files, ``TEST_NAME`` must +be given; this will be used for both the target and test names (and, as with +``ecm_add_tests()``, the ``NAME_PREFIX`` argument will be prepended to the test name). + +``WORKING_DIRECTORY`` sets the test property `WORKING_DIRECTORY +`_ +in which to execute the test. By default the test will be run in +``${CMAKE_CURRENT_BINARY_DIR}``. The working directory can be specified using +generator expressions. Since 5.111. + +Since pre-1.0.0. +#]=======================================================================] + +include(ECMMarkAsTest) +include(ECMMarkNonGuiExecutable) + +function(ecm_add_test) + set(options GUI) + # TARGET_NAME_VAR and TEST_NAME_VAR are undocumented args used by + # ecm_add_tests + set(oneValueArgs TEST_NAME NAME_PREFIX TARGET_NAME_VAR TEST_NAME_VAR WORKING_DIRECTORY) + set(multiValueArgs LINK_LIBRARIES) + cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + set(_sources ${ARG_UNPARSED_ARGUMENTS}) + list(LENGTH _sources _sourceCount) + if(ARG_TEST_NAME) + set(_targetname ${ARG_TEST_NAME}) + elseif(${_sourceCount} EQUAL "1") + #use the source file name without extension as the testname + get_filename_component(_targetname ${_sources} NAME_WE) + else() + #more than one source file passed, but no test name given -> error + message(FATAL_ERROR "ecm_add_test() called with multiple source files but without setting \"TEST_NAME\"") + endif() + + set(_testname ${ARG_NAME_PREFIX}${_targetname}) + set(gui_args) + if(ARG_GUI) + set(gui_args WIN32 MACOSX_BUNDLE) + endif() + add_executable(${_targetname} ${gui_args} ${_sources}) + if(NOT ARG_GUI) + ecm_mark_nongui_executable(${_targetname}) + endif() + set(test_args) + if(DEFINED ARG_WORKING_DIRECTORY) + list(APPEND test_args WORKING_DIRECTORY ${ARG_WORKING_DIRECTORY}) + endif() + add_test(NAME ${_testname} COMMAND ${_targetname} ${test_args}) + target_link_libraries(${_targetname} ${ARG_LINK_LIBRARIES}) + target_compile_definitions(${_targetname} PRIVATE -DQT_FORCE_ASSERTS) + ecm_mark_as_test(${_targetname}) + if (CMAKE_LIBRARY_OUTPUT_DIRECTORY) + set(_plugin_path ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) + if (DEFINED ENV{QT_PLUGIN_PATH}) + if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + # https://stackoverflow.com/questions/59862894/how-do-i-make-a-list-in-cmake-with-the-semicolon-value + set(PATHSEP "\\\;") # Don't want cmake to treat it like a list + else() # e.g. Linux + set(PATHSEP ":") + endif() + set(_plugin_path "${_plugin_path}${PATHSEP}$ENV{QT_PLUGIN_PATH}") + endif() + set_property(TEST ${_testname} PROPERTY ENVIRONMENT "QT_PLUGIN_PATH=${_plugin_path}") + endif() + if (ARG_TARGET_NAME_VAR) + set(${ARG_TARGET_NAME_VAR} "${_targetname}" PARENT_SCOPE) + endif() + if (ARG_TEST_NAME_VAR) + set(${ARG_TEST_NAME_VAR} "${_testname}" PARENT_SCOPE) + endif() +endfunction() + +function(ecm_add_tests) + set(options GUI) + set(oneValueArgs NAME_PREFIX TARGET_NAMES_VAR TEST_NAMES_VAR WORKING_DIRECTORY) + set(multiValueArgs LINK_LIBRARIES) + cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + if(ARG_GUI) + set(_exe_type GUI) + else() + set(_exe_type "") + endif() + set(test_args) + if(DEFINED ARG_WORKING_DIRECTORY) + list(APPEND test_args WORKING_DIRECTORY ${ARG_WORKING_DIRECTORY}) + endif() + set(test_names) + set(target_names) + foreach(_test_source ${ARG_UNPARSED_ARGUMENTS}) + ecm_add_test(${_test_source} + NAME_PREFIX ${ARG_NAME_PREFIX} + LINK_LIBRARIES ${ARG_LINK_LIBRARIES} + TARGET_NAME_VAR target_name + TEST_NAME_VAR test_name + ${_exe_type} + ${test_args} + ) + list(APPEND _test_names "${test_name}") + list(APPEND _target_names "${target_name}") + endforeach() + if (ARG_TARGET_NAMES_VAR) + set(${ARG_TARGET_NAMES_VAR} "${_target_names}" PARENT_SCOPE) + endif() + if (ARG_TEST_NAMES_VAR) + set(${ARG_TEST_NAMES_VAR} "${_test_names}" PARENT_SCOPE) + endif() +endfunction() diff --git a/cmake/Modules/ECMMarkAsTest.cmake b/cmake/Modules/ECMMarkAsTest.cmake new file mode 100644 index 00000000..6a5373d5 --- /dev/null +++ b/cmake/Modules/ECMMarkAsTest.cmake @@ -0,0 +1,41 @@ +# SPDX-FileCopyrightText: 2012 Stephen Kelly +# SPDX-FileCopyrightText: 2012 Alex Neundorf +# +# SPDX-License-Identifier: BSD-3-Clause + +#[=======================================================================[.rst: +ECMMarkAsTest +------------- + +Marks a target as only being required for tests. + +:: + + ecm_mark_as_test( [ [...]]) + +This will cause the specified targets to not be built unless either +``BUILD_TESTING`` is set to ``ON`` or the user invokes the ``buildtests`` target. + +``BUILD_TESTING`` is created as a cache variable by the CTest module and by the +:kde-module:`KDECMakeSettings` module. + +Since pre-1.0.0. +#]=======================================================================] + +if (NOT BUILD_TESTING) + if(NOT TARGET buildtests) + add_custom_target(buildtests) + endif() +endif() + +function(ecm_mark_as_test) + if (NOT BUILD_TESTING) + foreach(_target ${ARGN}) + set_target_properties(${_target} + PROPERTIES + EXCLUDE_FROM_ALL TRUE + ) + add_dependencies(buildtests ${_target}) + endforeach() + endif() +endfunction() diff --git a/cmake/Modules/ECMMarkNonGuiExecutable.cmake b/cmake/Modules/ECMMarkNonGuiExecutable.cmake new file mode 100644 index 00000000..48b4feaa --- /dev/null +++ b/cmake/Modules/ECMMarkNonGuiExecutable.cmake @@ -0,0 +1,30 @@ +# SPDX-FileCopyrightText: 2012 Stephen Kelly +# +# SPDX-License-Identifier: BSD-3-Clause + +#[=======================================================================[.rst: +ECMMarkNonGuiExecutable +----------------------- + +Marks an executable target as not being a GUI application. + +:: + + ecm_mark_nongui_executable( [ [...]]) + +This will indicate to CMake that the specified targets should not be included +in a MACOSX_BUNDLE and should not be WIN32_EXECUTABLEs. On platforms other +than MacOS X or Windows, this will have no effect. + +Since pre-1.0.0. +#]=======================================================================] + +function(ecm_mark_nongui_executable) + foreach(_target ${ARGN}) + set_target_properties(${_target} + PROPERTIES + WIN32_EXECUTABLE FALSE + MACOSX_BUNDLE FALSE + ) + endforeach() +endfunction() From 0fd77e46e36df1f9c8333f3d1bacb4fc7f3b28e9 Mon Sep 17 00:00:00 2001 From: David Faure Date: Tue, 13 Aug 2024 11:11:30 +0200 Subject: [PATCH 132/168] Increase timeout for KWallet DBus call This allows to wait for user to unlock wallet, e.g. at Plasma startup. The exact same code has been in kwallet.cpp for 8 years. --- qtkeychain/keychain_unix.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/qtkeychain/keychain_unix.cpp b/qtkeychain/keychain_unix.cpp index a3e83a96..501f1a35 100644 --- a/qtkeychain/keychain_unix.cpp +++ b/qtkeychain/keychain_unix.cpp @@ -240,6 +240,10 @@ void JobPrivate::kwalletWalletFound(QDBusPendingCallWatcher *watcher) { watcher->deleteLater(); const QDBusPendingReply reply = *watcher; + // Don't timeout after 25s, but 24 days + // This allows to wait for user to unlock wallet, e.g. at Plasma startup + iface->setTimeout(0x7FFFFFFF); + const QDBusPendingReply pendingReply = iface->open( reply.value(), 0, q->service() ); QDBusPendingCallWatcher* pendingWatcher = new QDBusPendingCallWatcher( pendingReply, this ); connect( pendingWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), From 8e943a6768885642fb2a0817cd85347daef81d54 Mon Sep 17 00:00:00 2001 From: Hannah von Reth Date: Fri, 19 Jul 2024 15:50:45 +0200 Subject: [PATCH 133/168] Move calls to CryptProtectData and CryptUnprotectData to functions --- qtkeychain/keychain_win.cpp | 101 +++++++++++++++++++++++------------- 1 file changed, 66 insertions(+), 35 deletions(-) diff --git a/qtkeychain/keychain_win.cpp b/qtkeychain/keychain_win.cpp index 509d2a43..6e92d3a3 100644 --- a/qtkeychain/keychain_win.cpp +++ b/qtkeychain/keychain_win.cpp @@ -9,6 +9,7 @@ #include "keychain_p.h" #include "plaintextstore_p.h" +#include #include #include @@ -16,6 +17,61 @@ using namespace QKeychain; +namespace { + QString formatWinError(unsigned long errorCode) + { + return QStringLiteral("WindowsError: %1: %2").arg(QString::number(errorCode, 16), QString::fromWCharArray(_com_error(errorCode).ErrorMessage())); + } + + // decrpyted data, error + std::pair unprotectData(const QByteArray &encrypted) + { + DATA_BLOB blob_in, blob_out; + + blob_in.pbData = const_cast(reinterpret_cast(encrypted.data())); + blob_in.cbData = encrypted.size(); + + if ( !CryptUnprotectData( &blob_in, + nullptr, + nullptr, + nullptr, + nullptr, + 0, + &blob_out ) ) { + return {{}, formatWinError(GetLastError())}; + } + + QByteArray decrypted( reinterpret_cast( blob_out.pbData ), blob_out.cbData ); + SecureZeroMemory( blob_out.pbData, blob_out.cbData ); + LocalFree( blob_out.pbData ); + return {decrypted, {}}; + } + + // encrypted data, error + std::pair protectData(const QByteArray &data) + { + DATA_BLOB blob_in, blob_out; + blob_in.pbData = const_cast(reinterpret_cast(data.data())); + blob_in.cbData = data.size(); + if(!CryptProtectData( &blob_in, + L"QKeychain-encrypted data", + nullptr, + nullptr, + nullptr, + 0, + &blob_out )) { + + return {{}, formatWinError(GetLastError())}; + } + + QByteArray encrypted( reinterpret_cast( blob_out.pbData ), blob_out.cbData ); + LocalFree( blob_out.pbData ); + return {encrypted, {}}; + } + + +} + #if defined(USE_CREDENTIAL_STORE) #include @@ -116,51 +172,26 @@ void ReadPasswordJobPrivate::scheduledStart() { return; } - DATA_BLOB blob_in, blob_out; - - blob_in.pbData = reinterpret_cast( encrypted.data() ); - blob_in.cbData = encrypted.size(); - - const BOOL ret = CryptUnprotectData( &blob_in, - nullptr, - nullptr, - nullptr, - nullptr, - 0, - &blob_out ); - if ( !ret ) { - q->emitFinishedWithError( OtherError, tr("Could not decrypt data") ); + const auto result = unprotectData(encrypted); + if (!result.second.isEmpty()) + { + q->emitFinishedWithError( OtherError, tr("Could not decrypt data: %1").arg(result.second) ); return; } - - data = QByteArray( reinterpret_cast( blob_out.pbData ), blob_out.cbData ); - SecureZeroMemory( blob_out.pbData, blob_out.cbData ); - LocalFree( blob_out.pbData ); - + data = result.first; q->emitFinished(); } void WritePasswordJobPrivate::scheduledStart() { - DATA_BLOB blob_in, blob_out; - blob_in.pbData = reinterpret_cast( data.data() ); - blob_in.cbData = data.size(); - const BOOL res = CryptProtectData( &blob_in, - L"QKeychain-encrypted data", - nullptr, - nullptr, - nullptr, - 0, - &blob_out ); - if ( !res ) { - q->emitFinishedWithError( OtherError, tr("Encryption failed") ); //TODO more details available? + const auto result = protectData(data); + if(!result.second.isEmpty()) + { + q->emitFinishedWithError( OtherError, tr("Encryption failed: %1").arg(result.second)); return; } - const QByteArray encrypted( reinterpret_cast( blob_out.pbData ), blob_out.cbData ); - LocalFree( blob_out.pbData ); - PlainTextStore plainTextStore( q->service(), q->settings() ); - plainTextStore.write( key, encrypted, Binary ); + plainTextStore.write( key, result.first, Binary ); if ( plainTextStore.error() != NoError ) { q->emitFinishedWithError( plainTextStore.error(), plainTextStore.errorString() ); return; From 2e29af3c57264ab464682c54cb3beada21ec4072 Mon Sep 17 00:00:00 2001 From: Hannah von Reth Date: Tue, 13 Aug 2024 11:48:46 +0200 Subject: [PATCH 134/168] Windows: Allow to store payloads up to 18944 bytes instead of 2560 --- autotest/basic.cpp | 5 +- qtkeychain/CMakeLists.txt | 2 +- qtkeychain/keychain_win.cpp | 111 ++++++++++++++++++++++++++++++------ 3 files changed, 98 insertions(+), 20 deletions(-) diff --git a/autotest/basic.cpp b/autotest/basic.cpp index 584eeebe..dc100510 100644 --- a/autotest/basic.cpp +++ b/autotest/basic.cpp @@ -24,6 +24,8 @@ private Q_SLOTS: QTest::newRow("1000") << generateRandomString(1000); QTest::newRow("2000") << generateRandomString(2000); QTest::newRow("3000") << generateRandomString(3000); + QTest::newRow("10000") << generateRandomString(10000); + QTest::newRow("18944") << generateRandomString(18944); } @@ -42,8 +44,9 @@ private Q_SLOTS: writeJob.start(); writeSpy.wait(); #ifdef Q_OS_WIN - QEXPECT_FAIL("3000", "Maximum for Windows is 2560", Abort); + QEXPECT_FAIL("18944", "Maximum for Windows is exceeded", Abort); #endif + qDebug() << writeJob.errorString(); QCOMPARE(writeJob.error(), QKeychain::NoError); } { diff --git a/qtkeychain/CMakeLists.txt b/qtkeychain/CMakeLists.txt index f9e28ce3..969090b1 100644 --- a/qtkeychain/CMakeLists.txt +++ b/qtkeychain/CMakeLists.txt @@ -21,8 +21,8 @@ endif() if(WIN32) list(APPEND qtkeychain_SOURCES keychain_win.cpp) + list(APPEND qtkeychain_LIBRARIES crypt32) if (NOT USE_CREDENTIAL_STORE) - list(APPEND qtkeychain_LIBRARIES crypt32) list(APPEND qtkeychain_SOURCES plaintextstore.cpp) endif() #FIXME: mingw bug; otherwise getting undefined refs to RtlSecureZeroMemory there diff --git a/qtkeychain/keychain_win.cpp b/qtkeychain/keychain_win.cpp index 6e92d3a3..5514c367 100644 --- a/qtkeychain/keychain_win.cpp +++ b/qtkeychain/keychain_win.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -18,7 +19,15 @@ using namespace QKeychain; namespace { - QString formatWinError(unsigned long errorCode) + const std::wstring PRODUCT_NAME = L"QtKeychain"; + const std::wstring ENCRYPTED_DATA_KEY = L"QtKeychain-encrypted data"; + const std::wstring ATTRIBUTE_KEY = L"QtKeychain Attrib"; + + constexpr quint64 MAX_ATTRIBUTE_SIZE = 256; + constexpr quint64 MAX_ATTRIBUTE_COUNT = 64; + constexpr quint64 MAX_BLOB_SIZE = CRED_MAX_CREDENTIAL_BLOB_SIZE + MAX_ATTRIBUTE_SIZE * MAX_ATTRIBUTE_COUNT; + + QString formatWinError(ulong errorCode) { return QStringLiteral("WindowsError: %1: %2").arg(QString::number(errorCode, 16), QString::fromWCharArray(_com_error(errorCode).ErrorMessage())); } @@ -54,7 +63,7 @@ namespace { blob_in.pbData = const_cast(reinterpret_cast(data.data())); blob_in.cbData = data.size(); if(!CryptProtectData( &blob_in, - L"QKeychain-encrypted data", + ENCRYPTED_DATA_KEY.data(), nullptr, nullptr, nullptr, @@ -73,10 +82,18 @@ namespace { } #if defined(USE_CREDENTIAL_STORE) -#include +/*** + * The credentials store has a limit of CRED_MAX_CREDENTIAL_BLOB_SIZE (5* 512) + * As this might not be enough in some scenarios, for bigger payloads we use CryptProtectData which offers similar protection as CredWrite + * in combination with CredWrite. + * We distribute the protected payload to the PCREDENTIALW->CredentialBlob as well as PCREDENTIALW->AttributeCount. + * This increases the max payload size to CRED_MAX_CREDENTIAL_BLOB_SIZE + 64 * 256 = 18944. + * As the protected data requires more space than the original payload, the effective max payload is smaller than that. + * As we continue to use PCREDENTIALW as storage medium, the credentials are still roaming. +*/ void ReadPasswordJobPrivate::scheduledStart() { - PCREDENTIALW cred; + PCREDENTIALW cred = {}; if (!CredReadW(reinterpret_cast(key.utf16()), CRED_TYPE_GENERIC, 0, &cred)) { Error err; @@ -96,7 +113,28 @@ void ReadPasswordJobPrivate::scheduledStart() { return; } - data = QByteArray(reinterpret_cast(cred->CredentialBlob), cred->CredentialBlobSize); + if(cred->AttributeCount == 0) + { + data = QByteArray(reinterpret_cast(cred->CredentialBlob), cred->CredentialBlobSize); + } + else + { + QByteArray encrypted; + encrypted.reserve(CRED_MAX_CREDENTIAL_BLOB_SIZE + cred->AttributeCount * MAX_ATTRIBUTE_SIZE); + encrypted.append(reinterpret_cast(cred->CredentialBlob), cred->CredentialBlobSize); + for (ulong i = 0; i < cred->AttributeCount; ++i) + { + encrypted.append(reinterpret_cast(cred->Attributes[i].Value), cred->Attributes[i].ValueSize); + } + const auto result = unprotectData(encrypted); + if (!result.second.isEmpty()) + { + q->emitFinishedWithError( OtherError, tr("Could not decrypt data: %1").arg(result.second) ); + return; + } + data = result.first; + } + CredFree(cred); q->emitFinished(); @@ -104,13 +142,59 @@ void ReadPasswordJobPrivate::scheduledStart() { void WritePasswordJobPrivate::scheduledStart() { CREDENTIALW cred = {}; - cred.Comment = const_cast(L"QtKeychain"); + cred.Comment = const_cast(PRODUCT_NAME.data()); cred.Type = CRED_TYPE_GENERIC; cred.TargetName = const_cast(reinterpret_cast(key.utf16())); - cred.CredentialBlobSize = data.size(); - cred.CredentialBlob = reinterpret_cast(data.data()); cred.Persist = CRED_PERSIST_ENTERPRISE; + QByteArray buffer; + std::vector attributes; + + if (data.size() < CRED_MAX_CREDENTIAL_BLOB_SIZE) + { + cred.CredentialBlob = reinterpret_cast(data.data()); + cred.CredentialBlobSize = data.size(); + } + else + { + // data is too big for CredentialBlob + // we encrpyt it instead with CryptProtectData which also encrpyt the data with the users credentials + // The data is also protected with the roaming profile + { + auto result = protectData(data); + if(!result.second.isEmpty()) + { + q->emitFinishedWithError( OtherError, tr("Encryption failed: %1").arg(result.second)); + return; + } + if (result.first.size() > MAX_BLOB_SIZE) + { + q->emitFinishedWithError( + OtherError, + tr("Credential size exceeds maximum size of %1: %2").arg(QString::number(MAX_BLOB_SIZE), QString::number(result.first.size()))); + return; + } + // the data must be valid outside of the scope of result + buffer = std::move(result.first); + } + + quint64 pos = 0; + auto read = [&buffer, &pos](const quint64 size, auto &dest, auto &sizeOut) + { + dest = reinterpret_cast::type>(buffer.data()) + pos; + sizeOut = std::min(size, buffer.size() - pos); + pos += sizeOut; + }; + read(CRED_MAX_CREDENTIAL_BLOB_SIZE, cred.CredentialBlob, cred.CredentialBlobSize); + + cred.AttributeCount = std::ceil((buffer.size() - pos) / static_cast(MAX_ATTRIBUTE_SIZE)); + attributes.resize(cred.AttributeCount, {}); + cred.Attributes = attributes.data(); + for(ulong i = 0; i < cred.AttributeCount; ++i) { + attributes[i].Keyword = const_cast(ATTRIBUTE_KEY.data()); + read(MAX_ATTRIBUTE_SIZE, attributes[i].Value, attributes[i].ValueSize); + } + } if (CredWriteW(&cred, 0)) { q->emitFinished(); return; @@ -121,15 +205,6 @@ void WritePasswordJobPrivate::scheduledStart() { // Detect size-exceeded errors and provide nicer messages. // Unfortunately these error codes aren't documented. // Found empirically on Win10 1803 build 17134.523. - if (err == RPC_X_BAD_STUB_DATA) { - const size_t maxBlob = CRED_MAX_CREDENTIAL_BLOB_SIZE; - if (cred.CredentialBlobSize > maxBlob) { - q->emitFinishedWithError( - OtherError, - tr("Credential size exceeds maximum size of %1").arg(maxBlob)); - return; - } - } if (err == RPC_S_INVALID_BOUND) { const size_t maxTargetName = CRED_MAX_GENERIC_TARGET_NAME_LENGTH; if (key.size() > maxTargetName) { @@ -140,7 +215,7 @@ void WritePasswordJobPrivate::scheduledStart() { } } - q->emitFinishedWithError( OtherError, tr("Writing credentials failed: Win32 error code %1").arg(err) ); + q->emitFinishedWithError( OtherError, tr("Writing credentials failed: %1").arg(formatWinError(err))); } void DeletePasswordJobPrivate::scheduledStart() { From 100c8dbd30a02199efcde7a8069c262bb285a984 Mon Sep 17 00:00:00 2001 From: David Faure Date: Fri, 23 Aug 2024 14:12:39 +0200 Subject: [PATCH 135/168] Use nullptr to avoid compiler warning warning: zero as null pointer constant [-Wzero-as-null-pointer-constant] --- qtkeychain/keychain_unix.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/qtkeychain/keychain_unix.cpp b/qtkeychain/keychain_unix.cpp index 501f1a35..766c5254 100644 --- a/qtkeychain/keychain_unix.cpp +++ b/qtkeychain/keychain_unix.cpp @@ -220,7 +220,7 @@ void ReadPasswordJobPrivate::scheduledStart() { q->service().toUtf8().constData(), "plaintext", reinterpret_cast( &JobPrivate::gnomeKeyring_readCb ), - this, 0 ) ) + this, nullptr ) ) q->emitFinishedWithError( OtherError, tr("Unknown error") ); break; @@ -295,7 +295,7 @@ void JobPrivate::gnomeKeyring_readCb( int result, const char* string, JobPrivate self->q->service().toUtf8().constData(), "base64", reinterpret_cast( &JobPrivate::gnomeKeyring_readCb ), - self, 0 ) ) + self, nullptr ) ) self->q->emitFinishedWithError( OtherError, tr("Unknown error") ); } else { const QPair errorResult = mapGnomeKeyringError( result ); @@ -345,7 +345,7 @@ void ReadPasswordJobPrivate::kwalletOpenFinished( QDBusPendingCallWatcher* watch q->emitFinished(); - WritePasswordJob* j = new WritePasswordJob( q->service(), 0 ); + WritePasswordJob* j = new WritePasswordJob( q->service(), nullptr ); j->setSettings( q->settings() ); j->setKey( key ); j->setAutoDelete( true ); @@ -482,7 +482,7 @@ void WritePasswordJobPrivate::scheduledStart() { type.toUtf8().constData(), password.constData(), reinterpret_cast( &JobPrivate::gnomeKeyring_writeCb ), - this, 0 ) ) + this, nullptr ) ) q->emitFinishedWithError( OtherError, tr("Unknown error") ); } break; @@ -593,7 +593,7 @@ void DeletePasswordJobPrivate::scheduledStart() { if ( !GnomeKeyring::delete_network_password( key.toUtf8().constData(), q->service().toUtf8().constData(), reinterpret_cast( &JobPrivate::gnomeKeyring_writeCb ), - this, 0 ) ) + this, nullptr ) ) q->emitFinishedWithError( OtherError, tr("Unknown error") ); } break; @@ -611,7 +611,7 @@ void DeletePasswordJobPrivate::scheduledStart() { } void DeletePasswordJobPrivate::fallbackOnError(const QDBusError &err) { - QScopedPointer local( !q->settings() ? new QSettings( q->service() ) : 0 ); + QScopedPointer local( !q->settings() ? new QSettings( q->service() ) : nullptr ); QSettings* actual = q->settings() ? q->settings() : local.data(); if ( !q->insecureFallback() ) { From f4cb9dcb0235a6faf1230e41d643fdc91bad8da4 Mon Sep 17 00:00:00 2001 From: Christophe Marin Date: Fri, 30 Aug 2024 09:36:43 +0200 Subject: [PATCH 136/168] Fix translation creation 22bac419 inadvertandly moved the qtkeychain_TR_FILES variable creation to the qtkeychain subfolder. It has to be created before qt_create_translation gets used. --- CMakeLists.txt | 8 ++++++++ qtkeychain/CMakeLists.txt | 8 -------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 43289a36..43ee7d36 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -134,6 +134,14 @@ add_subdirectory(qtkeychain) ### Translations ### +set(qtkeychain_TR_FILES + translations/qtkeychain_de.ts + translations/qtkeychain_fr.ts + translations/qtkeychain_ro.ts + translations/qtkeychain_ru.ts + translations/qtkeychain_zh.ts +) + file(GLOB qtkeychain_TR_SOURCES qtkeychain/*.cpp qtkeychain/*.h qtkeychain/*.ui) if ( BUILD_TRANSLATIONS ) qt_create_translation(qtkeychain_MESSAGES ${qtkeychain_TR_SOURCES} ${qtkeychain_TR_FILES}) diff --git a/qtkeychain/CMakeLists.txt b/qtkeychain/CMakeLists.txt index 969090b1..b3133e2b 100644 --- a/qtkeychain/CMakeLists.txt +++ b/qtkeychain/CMakeLists.txt @@ -67,14 +67,6 @@ endif() QT_WRAP_CPP(qtkeychain_MOC_OUTFILES keychain.h keychain_p.h gnomekeyring_p.h) -set(qtkeychain_TR_FILES - translations/qtkeychain_de.ts - translations/qtkeychain_fr.ts - translations/qtkeychain_ro.ts - translations/qtkeychain_ru.ts - translations/qtkeychain_zh.ts -) - add_library(${QTKEYCHAIN_TARGET_NAME} ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES} ${qtkeychain_QM_FILES}) if(WIN32) set_target_properties( ${QTKEYCHAIN_TARGET_NAME} PROPERTIES DEBUG_POSTFIX "d" ) From 73c3772d6432280df83e1ab3299c2a8f1e4ea47f Mon Sep 17 00:00:00 2001 From: Kondrashina Valentina Date: Tue, 8 Oct 2024 10:05:43 +0300 Subject: [PATCH 137/168] Fix MacOS error --- autotest/basic.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autotest/basic.cpp b/autotest/basic.cpp index dc100510..3a7c935f 100644 --- a/autotest/basic.cpp +++ b/autotest/basic.cpp @@ -32,7 +32,7 @@ private Q_SLOTS: void test() { #ifdef Q_OS_MACOS - QSKIP("This test case has no access to the keychain") + QSKIP("This test case has no access to the keychain"); #endif const QString serviceKey = QStringLiteral("QtKeychainTest-%1").arg(QTest::currentDataTag()); QFETCH(QByteArray, password); From 0fa1a42b32d4c8a96028f35cff20d78cd06ec02b Mon Sep 17 00:00:00 2001 From: Nerixyz Date: Tue, 5 Nov 2024 19:44:59 +0100 Subject: [PATCH 138/168] fix: use `/W4` for `clang-cl` instead of `-Wall` --- qtkeychain/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtkeychain/CMakeLists.txt b/qtkeychain/CMakeLists.txt index b3133e2b..5c7c5d0e 100644 --- a/qtkeychain/CMakeLists.txt +++ b/qtkeychain/CMakeLists.txt @@ -6,7 +6,7 @@ set(qtkeychain_SOURCES keychain.h ) -if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") +if(MSVC) # CMake < 3.15 sneaks in /W# flags for us, so we need a replacement, # or we'll get a warning (cf. CMP0092) if (CMAKE_CXX_FLAGS MATCHES "/W[0-4]") From 0ed3dba5f044f9a0c0f5aafab6dc23b94bf6e7a8 Mon Sep 17 00:00:00 2001 From: Nerixyz Date: Tue, 5 Nov 2024 19:45:38 +0100 Subject: [PATCH 139/168] fix: clang-cl comparison and C++ 20 compat warnings --- qtkeychain/keychain_win.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qtkeychain/keychain_win.cpp b/qtkeychain/keychain_win.cpp index 5514c367..673cb755 100644 --- a/qtkeychain/keychain_win.cpp +++ b/qtkeychain/keychain_win.cpp @@ -25,7 +25,7 @@ namespace { constexpr quint64 MAX_ATTRIBUTE_SIZE = 256; constexpr quint64 MAX_ATTRIBUTE_COUNT = 64; - constexpr quint64 MAX_BLOB_SIZE = CRED_MAX_CREDENTIAL_BLOB_SIZE + MAX_ATTRIBUTE_SIZE * MAX_ATTRIBUTE_COUNT; + constexpr qsizetype MAX_BLOB_SIZE = CRED_MAX_CREDENTIAL_BLOB_SIZE + MAX_ATTRIBUTE_SIZE * MAX_ATTRIBUTE_COUNT; QString formatWinError(ulong errorCode) { @@ -181,7 +181,7 @@ void WritePasswordJobPrivate::scheduledStart() { quint64 pos = 0; auto read = [&buffer, &pos](const quint64 size, auto &dest, auto &sizeOut) { - dest = reinterpret_cast::type>(buffer.data()) + pos; + dest = reinterpret_cast::type>(buffer.data()) + pos; sizeOut = std::min(size, buffer.size() - pos); pos += sizeOut; }; @@ -206,7 +206,7 @@ void WritePasswordJobPrivate::scheduledStart() { // Unfortunately these error codes aren't documented. // Found empirically on Win10 1803 build 17134.523. if (err == RPC_S_INVALID_BOUND) { - const size_t maxTargetName = CRED_MAX_GENERIC_TARGET_NAME_LENGTH; + const QString::size_type maxTargetName = CRED_MAX_GENERIC_TARGET_NAME_LENGTH; if (key.size() > maxTargetName) { q->emitFinishedWithError( OtherError, From 5bd32bf8f94c4b2bdc9ff791ad9aa8bc83a840a1 Mon Sep 17 00:00:00 2001 From: Nerixyz Date: Tue, 5 Nov 2024 19:46:04 +0100 Subject: [PATCH 140/168] fix: Qt5 windows build --- qtkeychain/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/qtkeychain/CMakeLists.txt b/qtkeychain/CMakeLists.txt index 5c7c5d0e..bd5e08d4 100644 --- a/qtkeychain/CMakeLists.txt +++ b/qtkeychain/CMakeLists.txt @@ -29,6 +29,9 @@ if(WIN32) if(MINGW) add_definitions( -O2 ) endif() + + set(CMAKE_CXX_STANDARD 17) + add_definitions( /utf-8 -DUNICODE ) endif() if(APPLE) From 181103549bcda0e4c38988255af64930cfa64ed7 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Fri, 15 Nov 2024 17:17:14 -0500 Subject: [PATCH 141/168] Fix read password job on Haiku There seems to be a breaking change in how QString::fromUtf8 works in Qt6, and this affects QtKeychain handling on Haiku. If we use QByteArray with an explicit size, it messes with the null termination handling and the application is given a QString with a null terminator, which fails. But this actually isn't needed at all, letting QByteArray figure out where the password data ends is good enough and lets Qt6 applications work again on Haiku. --- qtkeychain/keychain_haiku.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtkeychain/keychain_haiku.cpp b/qtkeychain/keychain_haiku.cpp index 16abe1c4..153ebe7e 100644 --- a/qtkeychain/keychain_haiku.cpp +++ b/qtkeychain/keychain_haiku.cpp @@ -86,7 +86,7 @@ void ReadPasswordJobPrivate::scheduledStart() q->key().toUtf8().constData(), false, password); - data = QByteArray(reinterpret_cast(password.Data()), password.DataLength()); + data = QByteArray(reinterpret_cast(password.Data())); switch ( result ) { case B_OK: From 89285f11b72264b4396eb5995d140e672e5aa593 Mon Sep 17 00:00:00 2001 From: David Faure Date: Thu, 21 Nov 2024 11:29:57 +0100 Subject: [PATCH 142/168] More porting to nullptr --- qtkeychain/gnomekeyring.cpp | 10 +++++----- qtkeychain/keychain.cpp | 4 ++-- qtkeychain/plaintextstore.cpp | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/qtkeychain/gnomekeyring.cpp b/qtkeychain/gnomekeyring.cpp index 6347052d..e160b20c 100644 --- a/qtkeychain/gnomekeyring.cpp +++ b/qtkeychain/gnomekeyring.cpp @@ -26,14 +26,14 @@ GnomeKeyring::gpointer GnomeKeyring::store_network_password( GDestroyNotify destroy_data ) { if ( !isAvailable() ) - return 0; + return nullptr; return instance().store_password( instance().NETWORK_PASSWORD, keyring, display_name, password, callback, data, destroy_data, "user", user, "server", server, "type", type, - static_cast(0) ); + static_cast(nullptr) ); } GnomeKeyring::gpointer GnomeKeyring::find_network_password( @@ -41,7 +41,7 @@ GnomeKeyring::gpointer GnomeKeyring::find_network_password( OperationGetStringCallback callback, gpointer data, GDestroyNotify destroy_data ) { if ( !isAvailable() ) - return 0; + return nullptr; return instance().find_password( instance().NETWORK_PASSWORD, callback, data, destroy_data, @@ -56,7 +56,7 @@ GnomeKeyring::gpointer GnomeKeyring::delete_network_password( const gchar* user, GDestroyNotify destroy_data ) { if ( !isAvailable() ) - return 0; + return nullptr; return instance().delete_password( instance().NETWORK_PASSWORD, callback, data, destroy_data, "user", user, "server", server, static_cast(0) ); @@ -70,7 +70,7 @@ GnomeKeyring::GnomeKeyring() {{ "user", ATTRIBUTE_TYPE_STRING }, { "server", ATTRIBUTE_TYPE_STRING }, { "type", ATTRIBUTE_TYPE_STRING }, - { 0, static_cast( 0 ) }} + { nullptr, static_cast( 0 ) }} }; NETWORK_PASSWORD = &schema; diff --git a/qtkeychain/keychain.cpp b/qtkeychain/keychain.cpp index 90ee4eb5..272285ee 100644 --- a/qtkeychain/keychain.cpp +++ b/qtkeychain/keychain.cpp @@ -142,7 +142,7 @@ DeletePasswordJobPrivate::DeletePasswordJobPrivate(const QString &service_, Dele } JobExecutor::JobExecutor() - : QObject( 0 ) + : QObject( nullptr ) , m_jobRunning( false ) { } @@ -181,7 +181,7 @@ void JobExecutor::jobFinished( Job* job ) { startNextIfNoneRunning(); } -JobExecutor* JobExecutor::s_instance = 0; +JobExecutor* JobExecutor::s_instance = nullptr; JobExecutor* JobExecutor::instance() { if ( !s_instance ) diff --git a/qtkeychain/plaintextstore.cpp b/qtkeychain/plaintextstore.cpp index b9d02726..f9479f04 100644 --- a/qtkeychain/plaintextstore.cpp +++ b/qtkeychain/plaintextstore.cpp @@ -23,7 +23,7 @@ inline QString typeKey(const QString &key) { return key + QLatin1String("/type") PlainTextStore::PlainTextStore(const QString &service, QSettings *settings) - : m_localSettings(settings ? 0 : new QSettings(service)) + : m_localSettings(settings ? nullptr : new QSettings(service)) , m_actualSettings(settings ? settings : m_localSettings.data()) , m_error(NoError) { From ad7344c45a86a4f66cbafc4b081b5f7b876cb0b7 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Thu, 16 Jan 2025 11:49:55 +0000 Subject: [PATCH 143/168] Prepare 0.15.0 release --- CMakeLists.txt | 2 +- ChangeLog | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 43ee7d36..109cace6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16) -set(QTKEYCHAIN_VERSION 0.14.99) +set(QTKEYCHAIN_VERSION 0.15.0) set(QTKEYCHAIN_SOVERSION 1) project(qtkeychain VERSION ${QTKEYCHAIN_VERSION} LANGUAGES CXX) diff --git a/ChangeLog b/ChangeLog index cf294c34..04b4bdd4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,15 @@ ChangeLog ========= +version 0.15.0 (release 2025-01-16) + + - Windows: Increase size of possible payloads (Hannah von Reth ) + - KWallet: Increase timeout when opening wallet (David Faure ) + - Haiku: Fix reading passwords (Joshua Goins ) + - Add unit tests (Hannah von Reth ) + - Windows build fixes (Nerixyz ) + - Fix translation creation (Christophe Marin ) + version 0.14.3 (release 2024-05-03) - Fix Android build for Qt 6.7 (Volker Krause ) From 23674ecc2ecde93129dd2d989024e28eef670abd Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Thu, 16 Jan 2025 11:53:52 +0000 Subject: [PATCH 144/168] Bump version for development to 0.15.99 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 109cace6..53457eac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16) -set(QTKEYCHAIN_VERSION 0.15.0) +set(QTKEYCHAIN_VERSION 0.15.99) set(QTKEYCHAIN_SOVERSION 1) project(qtkeychain VERSION ${QTKEYCHAIN_VERSION} LANGUAGES CXX) From d3ce7acc98d3c5c7da1cd11ffc8b0b615af54acc Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Tue, 4 Feb 2025 22:39:15 +0100 Subject: [PATCH 145/168] Use clang-format Reuse Qt's clang-format rules and apply them to the codebase. --- .clang-format | 114 +++++++++ TestAppExample/keychainclass.cpp | 25 +- TestAppExample/keychainclass.h | 22 +- TestAppExample/main.cpp | 12 +- autotest/basic.cpp | 19 +- qtkeychain/androidkeystore.cpp | 96 +++---- qtkeychain/androidkeystore_p.h | 40 +-- qtkeychain/gnomekeyring.cpp | 101 ++++---- qtkeychain/gnomekeyring_p.h | 89 +++---- qtkeychain/keychain.cpp | 188 ++++++++------ qtkeychain/keychain.h | 72 +++--- qtkeychain/keychain_android.cpp | 66 +++-- qtkeychain/keychain_apple.mm | 147 ++++++----- qtkeychain/keychain_haiku.cpp | 65 +++-- qtkeychain/keychain_p.h | 101 ++++---- qtkeychain/keychain_unix.cpp | 417 ++++++++++++++++--------------- qtkeychain/keychain_win.cpp | 253 +++++++++---------- qtkeychain/libsecret.cpp | 195 ++++++--------- qtkeychain/libsecret_p.h | 20 +- qtkeychain/plaintextstore.cpp | 24 +- qtkeychain/plaintextstore_p.h | 5 +- testclient.cpp | 96 +++---- 22 files changed, 1164 insertions(+), 1003 deletions(-) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..a770e55e --- /dev/null +++ b/.clang-format @@ -0,0 +1,114 @@ +# Copyright (C) 2016 Olivier Goffart +# +# You may use this file under the terms of the 3-clause BSD license. +# See the file LICENSE from this package for details. + +# This is the clang-format configuration style to be used by Qt, +# based on the rules from https://wiki.qt.io/Qt_Coding_Style and +# https://wiki.qt.io/Coding_Conventions + +--- +# Webkit style was loosely based on the Qt style +BasedOnStyle: WebKit + +Standard: c++11 + +# Column width is limited to 100 in accordance with Qt Coding Style. +# https://wiki.qt.io/Qt_Coding_Style +# Note that this may be changed at some point in the future. +ColumnLimit: 100 +# How much weight do extra characters after the line length limit have. +# PenaltyExcessCharacter: 4 + +# Disable reflow of some specific comments +# qdoc comments: indentation rules are different. +# Translation comments and SPDX license identifiers are also excluded. +CommentPragmas: "^!|^:|^ SPDX-License-Identifier:" + +# We want a space between the type and the star for pointer types. +PointerBindsToType: false + +# We generally use "template <" with space. +SpaceAfterTemplateKeyword: true + +# We want to break before the operators, but not before a '='. +BreakBeforeBinaryOperators: NonAssignment + +# Braces are usually attached, but not after functions or class declarations. +BreakBeforeBraces: Custom +BraceWrapping: + AfterClass: true + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: true + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + +# When constructor initializers do not fit on one line, put them each on a new line. +ConstructorInitializerAllOnOneLineOrOnePerLine: true +# Indent initializers by 4 spaces +ConstructorInitializerIndentWidth: 4 + +# Indent width for line continuations. +ContinuationIndentWidth: 8 + +# No indentation for namespaces. +NamespaceIndentation: None + +# Allow indentation for preprocessing directives (if/ifdef/endif). https://reviews.llvm.org/rL312125 +IndentPPDirectives: AfterHash +# We only indent with 2 spaces for preprocessor directives +PPIndentWidth: 2 + +# Horizontally align arguments after an open bracket. +# The coding style does not specify the following, but this is what gives +# results closest to the existing code. +AlignAfterOpenBracket: true +AlwaysBreakTemplateDeclarations: true + +# Ideally we should also allow less short function in a single line, but +# clang-format does not handle that. +AllowShortFunctionsOnASingleLine: Inline + +# The coding style specifies some include order categories, but also tells to +# separate categories with an empty line. It does not specify the order within +# the categories. Since the SortInclude feature of clang-format does not +# re-order includes separated by empty lines, the feature is not used. +SortIncludes: false + +# macros for which the opening brace stays attached. +ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH, forever, Q_FOREVER, QBENCHMARK, QBENCHMARK_ONCE ] + +# Break constructor initializers before the colon and after the commas. +BreakConstructorInitializers: BeforeColon + +# Add "// namespace " comments on closing brace for a namespace +# Ignored for namespaces that qualify as a short namespace, +# see 'ShortNamespaceLines' +FixNamespaceComments: true + +# Definition of how short a short namespace is, default 1 +ShortNamespaceLines: 1 + +# When escaping newlines in a macro attach the '\' as far left as possible, e.g. +##define a \ +# something; \ +# other; \ +# thelastlineislong; +AlignEscapedNewlines: Left + +# Avoids the addition of a space between an identifier and the +# initializer list in list-initialization. +SpaceBeforeCpp11BracedList: false + +--- +# Use the Google-based style for .proto files. +Language: Proto +BasedOnStyle: Google +IndentWidth: 4 +ColumnLimit: 100 diff --git a/TestAppExample/keychainclass.cpp b/TestAppExample/keychainclass.cpp index 1f09cedb..3e26592a 100644 --- a/TestAppExample/keychainclass.cpp +++ b/TestAppExample/keychainclass.cpp @@ -2,11 +2,11 @@ #include "keychainclass.h" -KeyChainClass::KeyChainClass(QObject* parent) : - QObject(parent), - m_readCredentialJob(QLatin1String("keychain.example.project.app")), - m_writeCredentialJob(QLatin1String("keychain.example.project.app")), - m_deleteCredentialJob(QLatin1String("keychain.example.project.app")) +KeyChainClass::KeyChainClass(QObject *parent) + : QObject(parent), + m_readCredentialJob(QLatin1String("keychain.example.project.app")), + m_writeCredentialJob(QLatin1String("keychain.example.project.app")), + m_deleteCredentialJob(QLatin1String("keychain.example.project.app")) { m_readCredentialJob.setAutoDelete(false); m_writeCredentialJob.setAutoDelete(false); @@ -17,9 +17,10 @@ void KeyChainClass::readKey(const QString &key) { m_readCredentialJob.setKey(key); - QObject::connect(&m_readCredentialJob, &QKeychain::ReadPasswordJob::finished, [=](){ + QObject::connect(&m_readCredentialJob, &QKeychain::ReadPasswordJob::finished, [=]() { if (m_readCredentialJob.error()) { - emit error(tr("Read key failed: %1").arg(qPrintable(m_readCredentialJob.errorString()))); + emit error( + tr("Read key failed: %1").arg(qPrintable(m_readCredentialJob.errorString()))); return; } emit keyRestored(key, m_readCredentialJob.textData()); @@ -32,9 +33,10 @@ void KeyChainClass::writeKey(const QString &key, const QString &value) { m_writeCredentialJob.setKey(key); - QObject::connect(&m_writeCredentialJob, &QKeychain::WritePasswordJob::finished, [=](){ + QObject::connect(&m_writeCredentialJob, &QKeychain::WritePasswordJob::finished, [=]() { if (m_writeCredentialJob.error()) { - emit error(tr("Write key failed: %1").arg(qPrintable(m_writeCredentialJob.errorString()))); + emit error( + tr("Write key failed: %1").arg(qPrintable(m_writeCredentialJob.errorString()))); return; } @@ -49,9 +51,10 @@ void KeyChainClass::deleteKey(const QString &key) { m_deleteCredentialJob.setKey(key); - QObject::connect(&m_deleteCredentialJob, &QKeychain::DeletePasswordJob::finished, [=](){ + QObject::connect(&m_deleteCredentialJob, &QKeychain::DeletePasswordJob::finished, [=]() { if (m_deleteCredentialJob.error()) { - emit error(tr("Delete key failed: %1").arg(qPrintable(m_deleteCredentialJob.errorString()))); + emit error(tr("Delete key failed: %1") + .arg(qPrintable(m_deleteCredentialJob.errorString()))); return; } emit keyDeleted(key); diff --git a/TestAppExample/keychainclass.h b/TestAppExample/keychainclass.h index ea747d70..c8aedf18 100644 --- a/TestAppExample/keychainclass.h +++ b/TestAppExample/keychainclass.h @@ -5,25 +5,25 @@ #include -class KeyChainClass: public QObject +class KeyChainClass : public QObject { Q_OBJECT public: - KeyChainClass(QObject* parent = nullptr); + KeyChainClass(QObject *parent = nullptr); - Q_INVOKABLE void readKey(const QString& key); - Q_INVOKABLE void writeKey(const QString& key, const QString& value); - Q_INVOKABLE void deleteKey(const QString& key); + Q_INVOKABLE void readKey(const QString &key); + Q_INVOKABLE void writeKey(const QString &key, const QString &value); + Q_INVOKABLE void deleteKey(const QString &key); Q_SIGNALS: - void keyStored(const QString& key); - void keyRestored(const QString& key, const QString& value); - void keyDeleted(const QString& key); - void error(const QString& errorText); + void keyStored(const QString &key); + void keyRestored(const QString &key, const QString &value); + void keyDeleted(const QString &key); + void error(const QString &errorText); private: - QKeychain::ReadPasswordJob m_readCredentialJob; - QKeychain::WritePasswordJob m_writeCredentialJob; + QKeychain::ReadPasswordJob m_readCredentialJob; + QKeychain::WritePasswordJob m_writeCredentialJob; QKeychain::DeletePasswordJob m_deleteCredentialJob; }; diff --git a/TestAppExample/main.cpp b/TestAppExample/main.cpp index 61367848..3d9f1bd5 100644 --- a/TestAppExample/main.cpp +++ b/TestAppExample/main.cpp @@ -14,11 +14,13 @@ int main(int argc, char *argv[]) QQmlApplicationEngine engine; const QUrl url(QStringLiteral("qrc:/main.qml")); - QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, - &app, [url](QObject *obj, const QUrl &objUrl) { - if (!obj && url == objUrl) - QCoreApplication::exit(-1); - }, Qt::QueuedConnection); + QObject::connect( + &engine, &QQmlApplicationEngine::objectCreated, &app, + [url](QObject *obj, const QUrl &objUrl) { + if (!obj && url == objUrl) + QCoreApplication::exit(-1); + }, + Qt::QueuedConnection); KeyChainClass keyChainClass; diff --git a/autotest/basic.cpp b/autotest/basic.cpp index 3a7c935f..f9f93235 100644 --- a/autotest/basic.cpp +++ b/autotest/basic.cpp @@ -2,16 +2,18 @@ #include "qtkeychain/keychain.h" -namespace +namespace { +QByteArray generateRandomString(qsizetype size) { - QByteArray generateRandomString(qsizetype size) - { - std::vector buffer(size, 0); - QRandomGenerator::global()->fillRange(buffer.data(), size); - return QByteArray(reinterpret_cast(buffer.data()), static_cast(size * sizeof(quint32))).toBase64(QByteArray::Base64UrlEncoding).mid(0, size); - } - + std::vector buffer(size, 0); + QRandomGenerator::global()->fillRange(buffer.data(), size); + return QByteArray(reinterpret_cast(buffer.data()), + static_cast(size * sizeof(quint32))) + .toBase64(QByteArray::Base64UrlEncoding) + .mid(0, size); } + +} // namespace class BasicTest : public QObject { Q_OBJECT @@ -26,7 +28,6 @@ private Q_SLOTS: QTest::newRow("3000") << generateRandomString(3000); QTest::newRow("10000") << generateRandomString(10000); QTest::newRow("18944") << generateRandomString(18944); - } void test() diff --git a/qtkeychain/androidkeystore.cpp b/qtkeychain/androidkeystore.cpp index da58084e..c831b3bc 100644 --- a/qtkeychain/androidkeystore.cpp +++ b/qtkeychain/androidkeystore.cpp @@ -1,11 +1,11 @@ #include "androidkeystore_p.h" #if QT_VERSION < QT_VERSION_CHECK(5, 7, 0) -#include "private/qjni_p.h" +# include "private/qjni_p.h" #endif #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) -#include +# include #endif using namespace QKeychain; @@ -24,12 +24,15 @@ using namespace javax::crypto; using namespace javax::security::auth::x500; using namespace javax::security::cert; -const BigInteger BigInteger::ONE = BigInteger::getStaticObjectField("java/math/BigInteger", "ONE", "Ljava/math/BigInteger;"); +const BigInteger BigInteger::ONE = + BigInteger::getStaticObjectField("java/math/BigInteger", "ONE", "Ljava/math/BigInteger;"); const int Calendar::YEAR = Calendar::getStaticField("java/util/Calendar", "YEAR"); -const int Cipher::DECRYPT_MODE = Cipher::getStaticField("javax/crypto/Cipher", "DECRYPT_MODE"); -const int Cipher::ENCRYPT_MODE = Cipher::getStaticField("javax/crypto/Cipher", "ENCRYPT_MODE"); +const int Cipher::DECRYPT_MODE = + Cipher::getStaticField("javax/crypto/Cipher", "DECRYPT_MODE"); +const int Cipher::ENCRYPT_MODE = + Cipher::getStaticField("javax/crypto/Cipher", "ENCRYPT_MODE"); namespace { @@ -37,11 +40,12 @@ namespace { struct JNIObject { - JNIObject(QSharedPointer d): d(d) {} + JNIObject(QSharedPointer d) : d(d) { } static JNIObject fromLocalRef(jobject o) { - return JNIObject(QSharedPointer::create(QJNIObjectPrivate::fromLocalRef(o))); + return JNIObject( + QSharedPointer::create(QJNIObjectPrivate::fromLocalRef(o))); } jobject object() const { return d->object(); } @@ -68,12 +72,12 @@ JNIObject toArray(const QByteArray &bytes) QAndroidJniEnvironment env; const int length = bytes.length(); JNIObject array = JNIObject::fromLocalRef(env->NewByteArray(length)); - env->SetByteArrayRegion(static_cast(array.object()), - 0, length, reinterpret_cast(bytes.constData())); + env->SetByteArrayRegion(static_cast(array.object()), 0, length, + reinterpret_cast(bytes.constData())); return array; } -} +} // namespace bool Object::handleExceptions() { @@ -88,12 +92,12 @@ bool Object::handleExceptions() return true; } - KeyPairGenerator KeyPairGenerator::getInstance(const QString &algorithm, const QString &provider) { - return handleExceptions(callStaticObjectMethod("java/security/KeyPairGenerator", "getInstance", - "(Ljava/lang/String;Ljava/lang/String;)Ljava/security/KeyPairGenerator;", - fromString(algorithm).object(), fromString(provider).object())); + return handleExceptions(callStaticObjectMethod( + "java/security/KeyPairGenerator", "getInstance", + "(Ljava/lang/String;Ljava/lang/String;)Ljava/security/KeyPairGenerator;", + fromString(algorithm).object(), fromString(provider).object())); } KeyPair KeyPairGenerator::generateKeyPair() const @@ -126,11 +130,14 @@ KeyStore KeyStore::getInstance(const QString &type) fromString(type).object())); } -KeyStore::Entry KeyStore::getEntry(const QString &alias, const KeyStore::ProtectionParameter ¶m) const +KeyStore::Entry KeyStore::getEntry(const QString &alias, + const KeyStore::ProtectionParameter ¶m) const { - return handleExceptions(callObjectMethod("getEntry", - "(Ljava/lang/String;Ljava/security/KeyStore$ProtectionParameter;)Ljava/security/KeyStore$Entry;", - fromString(alias).object(), param.object())); + return handleExceptions( + callObjectMethod("getEntry", + "(Ljava/lang/String;Ljava/security/" + "KeyStore$ProtectionParameter;)Ljava/security/KeyStore$Entry;", + fromString(alias).object(), param.object())); } bool KeyStore::load(const KeyStore::LoadStoreParameter ¶m) const @@ -139,12 +146,10 @@ bool KeyStore::load(const KeyStore::LoadStoreParameter ¶m) const return handleExceptions(); } - Calendar Calendar::getInstance() { - return handleExceptions(callStaticObjectMethod("java/util/Calendar", "getInstance", - "()Ljava/util/Calendar;")); - + return handleExceptions( + callStaticObjectMethod("java/util/Calendar", "getInstance", "()Ljava/util/Calendar;")); } bool Calendar::add(int field, int amount) const @@ -160,45 +165,48 @@ Date Calendar::getTime() const KeyPairGeneratorSpec::Builder::Builder(const Context &context) : Object(QAndroidJniObject("android/security/KeyPairGeneratorSpec$Builder", - "(Landroid/content/Context;)V", - context.object())) + "(Landroid/content/Context;)V", context.object())) { handleExceptions(); } KeyPairGeneratorSpec::Builder KeyPairGeneratorSpec::Builder::setAlias(const QString &alias) const { - return handleExceptions(callObjectMethod("setAlias", - "(Ljava/lang/String;)Landroid/security/KeyPairGeneratorSpec$Builder;", - fromString(alias).object())); + return handleExceptions(callObjectMethod( + "setAlias", "(Ljava/lang/String;)Landroid/security/KeyPairGeneratorSpec$Builder;", + fromString(alias).object())); } -KeyPairGeneratorSpec::Builder KeyPairGeneratorSpec::Builder::setSubject(const X500Principal &subject) const +KeyPairGeneratorSpec::Builder +KeyPairGeneratorSpec::Builder::setSubject(const X500Principal &subject) const { return handleExceptions(callObjectMethod("setSubject", - "(Ljavax/security/auth/x500/X500Principal;)Landroid/security/KeyPairGeneratorSpec$Builder;", + "(Ljavax/security/auth/x500/X500Principal;)Landroid/" + "security/KeyPairGeneratorSpec$Builder;", subject.object())); } -KeyPairGeneratorSpec::Builder KeyPairGeneratorSpec::Builder::setSerialNumber(const BigInteger &serial) const +KeyPairGeneratorSpec::Builder +KeyPairGeneratorSpec::Builder::setSerialNumber(const BigInteger &serial) const { - return handleExceptions(callObjectMethod("setSerialNumber", - "(Ljava/math/BigInteger;)Landroid/security/KeyPairGeneratorSpec$Builder;", - serial.object())); + return handleExceptions(callObjectMethod( + "setSerialNumber", + "(Ljava/math/BigInteger;)Landroid/security/KeyPairGeneratorSpec$Builder;", + serial.object())); } KeyPairGeneratorSpec::Builder KeyPairGeneratorSpec::Builder::setStartDate(const Date &date) const { - return handleExceptions(callObjectMethod("setStartDate", - "(Ljava/util/Date;)Landroid/security/KeyPairGeneratorSpec$Builder;", - date.object())); + return handleExceptions(callObjectMethod( + "setStartDate", "(Ljava/util/Date;)Landroid/security/KeyPairGeneratorSpec$Builder;", + date.object())); } KeyPairGeneratorSpec::Builder KeyPairGeneratorSpec::Builder::setEndDate(const Date &date) const { - return handleExceptions(callObjectMethod("setEndDate", - "(Ljava/util/Date;)Landroid/security/KeyPairGeneratorSpec$Builder;", - date.object())); + return handleExceptions(callObjectMethod( + "setEndDate", "(Ljava/util/Date;)Landroid/security/KeyPairGeneratorSpec$Builder;", + date.object())); } KeyPairGeneratorSpec KeyPairGeneratorSpec::Builder::build() const @@ -207,8 +215,7 @@ KeyPairGeneratorSpec KeyPairGeneratorSpec::Builder::build() const } X500Principal::X500Principal(const QString &name) - : Object(QAndroidJniObject("javax/security/auth/x500/X500Principal", - "(Ljava/lang/String;)V", + : Object(QAndroidJniObject("javax/security/auth/x500/X500Principal", "(Ljava/lang/String;)V", fromString(name).object())) { handleExceptions(); @@ -216,7 +223,8 @@ X500Principal::X500Principal(const QString &name) Certificate KeyStore::PrivateKeyEntry::getCertificate() const { - return handleExceptions(callObjectMethod("getCertificate", "()Ljava/security/cert/Certificate;")); + return handleExceptions( + callObjectMethod("getCertificate", "()Ljava/security/cert/Certificate;")); } PrivateKey KeyStore::PrivateKeyEntry::getPrivateKey() const @@ -230,7 +238,8 @@ PublicKey Certificate::getPublicKey() const } ByteArrayInputStream::ByteArrayInputStream(const QByteArray &bytes) - : InputStream(QAndroidJniObject("java/io/ByteArrayInputStream", "([B)V", toArray(bytes).object())) + : InputStream( + QAndroidJniObject("java/io/ByteArrayInputStream", "([B)V", toArray(bytes).object())) { } @@ -286,7 +295,6 @@ bool Cipher::init(int opMode, const Key &key) const return handleExceptions(); } - CipherOutputStream::CipherOutputStream(const OutputStream &stream, const Cipher &cipher) : FilterOutputStream(QAndroidJniObject("javax/crypto/CipherOutputStream", "(Ljava/io/OutputStream;Ljavax/crypto/Cipher;)V", diff --git a/qtkeychain/androidkeystore_p.h b/qtkeychain/androidkeystore_p.h index 925c741b..91eec253 100644 --- a/qtkeychain/androidkeystore_p.h +++ b/qtkeychain/androidkeystore_p.h @@ -13,10 +13,10 @@ #include #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) -#include +# include #else -#include -#include +# include +# include typedef QJniObject QAndroidJniObject; typedef QJniEnvironment QAndroidJniEnvironment; @@ -28,20 +28,26 @@ namespace QKeychain { namespace javax { namespace security { -namespace auth { namespace x500 { class X500Principal; } } -namespace cert { class Certificate; } - +namespace auth { +namespace x500 { +class X500Principal; } +} // namespace auth +namespace cert { +class Certificate; } +} // namespace security +} // namespace javax + namespace java { namespace lang { class Object : protected QAndroidJniObject { public: - inline Object(jobject object) : QAndroidJniObject(object) {} - inline Object(const QAndroidJniObject &object) : QAndroidJniObject(object) {} + inline Object(jobject object) : QAndroidJniObject(object) { } + inline Object(const QAndroidJniObject &object) : QAndroidJniObject(object) { } inline operator bool() const { return isValid(); } using QAndroidJniObject::object; @@ -50,11 +56,11 @@ class Object : protected QAndroidJniObject protected: static bool handleExceptions(); - template + template static T handleExceptions(const T &result, const T &resultOnError = T()); }; -template +template inline T Object::handleExceptions(const T &result, const T &resultOnError) { if (!handleExceptions()) @@ -182,7 +188,7 @@ class PrivateKey : public Key public: using Key::Key; - PrivateKey(const Key &init): Key(init) {} + PrivateKey(const Key &init) : Key(init) { } }; class PublicKey : public Key @@ -190,7 +196,7 @@ class PublicKey : public Key public: using Key::Key; - PublicKey(const Key &init): Key(init) {} + PublicKey(const Key &init) : Key(init) { } }; class KeyPair : public java::lang::Object @@ -207,7 +213,6 @@ class KeyPairGenerator : public java::lang::Object static KeyPairGenerator getInstance(const QString &algorithm, const QString &provider); KeyPair generateKeyPair() const; bool initialize(const spec::AlgorithmParameterSpec &spec) const; - }; class KeyStore : public java::lang::Object @@ -224,7 +229,7 @@ class KeyStore : public java::lang::Object public: using Entry::Entry; - inline PrivateKeyEntry(const Entry &init): Entry(init) {} + inline PrivateKeyEntry(const Entry &init) : Entry(init) { } javax::security::cert::Certificate getCertificate() const; java::security::PrivateKey getPrivateKey() const; @@ -258,7 +263,7 @@ class RSAPrivateKey : public PrivateKey public: using PrivateKey::PrivateKey; - RSAPrivateKey(const PrivateKey &init): PrivateKey(init) {} + RSAPrivateKey(const PrivateKey &init) : PrivateKey(init) { } }; class RSAPublicKey : public PublicKey @@ -266,7 +271,7 @@ class RSAPublicKey : public PublicKey public: using PublicKey::PublicKey; - RSAPublicKey(const PublicKey &init): PublicKey(init) {} + RSAPublicKey(const PublicKey &init) : PublicKey(init) { } }; } // namespace interfaces @@ -303,7 +308,6 @@ class KeyPairGeneratorSpec : public java::security::spec::AlgorithmParameterSpec Builder setStartDate(const java::util::Date &date) const; Builder setEndDate(const java::util::Date &date) const; KeyPairGeneratorSpec build() const; - }; using AlgorithmParameterSpec::AlgorithmParameterSpec; @@ -343,7 +347,7 @@ class CipherOutputStream : public java::io::FilterOutputStream explicit CipherOutputStream(const OutputStream &stream, const Cipher &cipher); }; -} +} // namespace crypto namespace security { namespace auth { diff --git a/qtkeychain/gnomekeyring.cpp b/qtkeychain/gnomekeyring.cpp index e160b20c..663d6d11 100644 --- a/qtkeychain/gnomekeyring.cpp +++ b/qtkeychain/gnomekeyring.cpp @@ -1,86 +1,71 @@ #include "gnomekeyring_p.h" -const char* GnomeKeyring::GNOME_KEYRING_DEFAULT = nullptr; +const char *GnomeKeyring::GNOME_KEYRING_DEFAULT = nullptr; bool GnomeKeyring::isAvailable() { - const GnomeKeyring& keyring = instance(); - return keyring.isLoaded() && - keyring.NETWORK_PASSWORD && - keyring.is_available && - keyring.find_password && - keyring.store_password && - keyring.delete_password && - keyring.is_available(); + const GnomeKeyring &keyring = instance(); + return keyring.isLoaded() && keyring.NETWORK_PASSWORD && keyring.is_available + && keyring.find_password && keyring.store_password && keyring.delete_password + && keyring.is_available(); } -GnomeKeyring::gpointer GnomeKeyring::store_network_password( - const gchar* keyring, - const gchar* display_name, - const gchar* user, - const gchar* server, - const gchar* type, - const gchar* password, - OperationDoneCallback callback, - gpointer data, - GDestroyNotify destroy_data ) +GnomeKeyring::gpointer +GnomeKeyring::store_network_password(const gchar *keyring, const gchar *display_name, + const gchar *user, const gchar *server, const gchar *type, + const gchar *password, OperationDoneCallback callback, + gpointer data, GDestroyNotify destroy_data) { - if ( !isAvailable() ) + if (!isAvailable()) return nullptr; - return instance().store_password( instance().NETWORK_PASSWORD, - keyring, display_name, password, callback, - data, destroy_data, - "user", user, - "server", server, - "type", type, - static_cast(nullptr) ); + return instance().store_password(instance().NETWORK_PASSWORD, keyring, display_name, password, + callback, data, destroy_data, "user", user, "server", server, + "type", type, static_cast(nullptr)); } -GnomeKeyring::gpointer GnomeKeyring::find_network_password( - const gchar* user, const gchar* server, const gchar* type, - OperationGetStringCallback callback, gpointer data, GDestroyNotify destroy_data ) +GnomeKeyring::gpointer GnomeKeyring::find_network_password(const gchar *user, const gchar *server, + const gchar *type, + OperationGetStringCallback callback, + gpointer data, + GDestroyNotify destroy_data) { - if ( !isAvailable() ) + if (!isAvailable()) return nullptr; - return instance().find_password( instance().NETWORK_PASSWORD, - callback, data, destroy_data, - "user", user, "server", server, "type", type, - static_cast(0) ); + return instance().find_password(instance().NETWORK_PASSWORD, callback, data, destroy_data, + "user", user, "server", server, "type", type, + static_cast(0)); } -GnomeKeyring::gpointer GnomeKeyring::delete_network_password( const gchar* user, - const gchar* server, - OperationDoneCallback callback, - gpointer data, - GDestroyNotify destroy_data ) +GnomeKeyring::gpointer GnomeKeyring::delete_network_password(const gchar *user, const gchar *server, + OperationDoneCallback callback, + gpointer data, + GDestroyNotify destroy_data) { - if ( !isAvailable() ) + if (!isAvailable()) return nullptr; - return instance().delete_password( instance().NETWORK_PASSWORD, - callback, data, destroy_data, - "user", user, "server", server, static_cast(0) ); + return instance().delete_password(instance().NETWORK_PASSWORD, callback, data, destroy_data, + "user", user, "server", server, static_cast(0)); } -GnomeKeyring::GnomeKeyring() - : QLibrary(QLatin1String("gnome-keyring"), 0) +GnomeKeyring::GnomeKeyring() : QLibrary(QLatin1String("gnome-keyring"), 0) { - static const PasswordSchema schema = { - ITEM_NETWORK_PASSWORD, - {{ "user", ATTRIBUTE_TYPE_STRING }, - { "server", ATTRIBUTE_TYPE_STRING }, - { "type", ATTRIBUTE_TYPE_STRING }, - { nullptr, static_cast( 0 ) }} - }; + static const PasswordSchema schema = { ITEM_NETWORK_PASSWORD, + { { "user", ATTRIBUTE_TYPE_STRING }, + { "server", ATTRIBUTE_TYPE_STRING }, + { "type", ATTRIBUTE_TYPE_STRING }, + { nullptr, static_cast(0) } } }; NETWORK_PASSWORD = &schema; - is_available = reinterpret_cast( resolve( "gnome_keyring_is_available" ) ); - find_password = reinterpret_cast( resolve( "gnome_keyring_find_password" ) ); - store_password = reinterpret_cast( resolve( "gnome_keyring_store_password" ) ); - delete_password = reinterpret_cast( resolve( "gnome_keyring_delete_password" ) ); + is_available = reinterpret_cast(resolve("gnome_keyring_is_available")); + find_password = reinterpret_cast(resolve("gnome_keyring_find_password")); + store_password = reinterpret_cast(resolve("gnome_keyring_store_password")); + delete_password = + reinterpret_cast(resolve("gnome_keyring_delete_password")); } -GnomeKeyring& GnomeKeyring::instance() { +GnomeKeyring &GnomeKeyring::instance() +{ static GnomeKeyring keyring; return keyring; } diff --git a/qtkeychain/gnomekeyring_p.h b/qtkeychain/gnomekeyring_p.h index 87c062c3..57fcdc5a 100644 --- a/qtkeychain/gnomekeyring_p.h +++ b/qtkeychain/gnomekeyring_p.h @@ -3,7 +3,8 @@ #include -class GnomeKeyring : private QLibrary { +class GnomeKeyring : private QLibrary +{ Q_OBJECT public: @@ -29,66 +30,66 @@ class GnomeKeyring : private QLibrary { ITEM_PK_STORAGE = 0x100 }; - enum AttributeType { - ATTRIBUTE_TYPE_STRING, - ATTRIBUTE_TYPE_UINT32 - }; + enum AttributeType { ATTRIBUTE_TYPE_STRING, ATTRIBUTE_TYPE_UINT32 }; typedef char gchar; - typedef void* gpointer; + typedef void *gpointer; typedef bool gboolean; - typedef struct { + typedef struct + { ItemType item_type; - struct { - const gchar* name; + struct + { + const gchar *name; AttributeType type; } attributes[32]; } PasswordSchema; - typedef void ( *OperationGetStringCallback )( Result result, bool binary, - const char* string, gpointer data ); - typedef void ( *OperationDoneCallback )( Result result, gpointer data ); - typedef void ( *GDestroyNotify )( gpointer data ); + typedef void (*OperationGetStringCallback)(Result result, bool binary, const char *string, + gpointer data); + typedef void (*OperationDoneCallback)(Result result, gpointer data); + typedef void (*GDestroyNotify)(gpointer data); - static const char* GNOME_KEYRING_DEFAULT; + static const char *GNOME_KEYRING_DEFAULT; static bool isAvailable(); - static gpointer store_network_password( const gchar* keyring, const gchar* display_name, - const gchar* user, const gchar* server, - const gchar* type, const gchar* password, - OperationDoneCallback callback, gpointer data, GDestroyNotify destroy_data ); + static gpointer store_network_password(const gchar *keyring, const gchar *display_name, + const gchar *user, const gchar *server, + const gchar *type, const gchar *password, + OperationDoneCallback callback, gpointer data, + GDestroyNotify destroy_data); + + static gpointer find_network_password(const gchar *user, const gchar *server, const gchar *type, + OperationGetStringCallback callback, gpointer data, + GDestroyNotify destroy_data); - static gpointer find_network_password( const gchar* user, const gchar* server, - const gchar* type, - OperationGetStringCallback callback, - gpointer data, GDestroyNotify destroy_data ); + static gpointer delete_network_password(const gchar *user, const gchar *server, + OperationDoneCallback callback, gpointer data, + GDestroyNotify destroy_data); - static gpointer delete_network_password( const gchar* user, const gchar* server, - OperationDoneCallback callback, gpointer data, GDestroyNotify destroy_data ); private: GnomeKeyring(); - static GnomeKeyring& instance(); - - const PasswordSchema* NETWORK_PASSWORD; - typedef gboolean ( is_available_fn )( void ); - typedef gpointer ( store_password_fn )( const PasswordSchema* schema, const gchar* keyring, - const gchar* display_name, const gchar* password, - OperationDoneCallback callback, gpointer data, GDestroyNotify destroy_data, - ... ); - typedef gpointer ( find_password_fn )( const PasswordSchema* schema, - OperationGetStringCallback callback, gpointer data, GDestroyNotify destroy_data, - ... ); - typedef gpointer ( delete_password_fn )( const PasswordSchema* schema, - OperationDoneCallback callback, gpointer data, GDestroyNotify destroy_data, - ... ); - - is_available_fn* is_available; - find_password_fn* find_password; - store_password_fn* store_password; - delete_password_fn* delete_password; + static GnomeKeyring &instance(); + + const PasswordSchema *NETWORK_PASSWORD; + typedef gboolean(is_available_fn)(void); + typedef gpointer(store_password_fn)(const PasswordSchema *schema, const gchar *keyring, + const gchar *display_name, const gchar *password, + OperationDoneCallback callback, gpointer data, + GDestroyNotify destroy_data, ...); + typedef gpointer(find_password_fn)(const PasswordSchema *schema, + OperationGetStringCallback callback, gpointer data, + GDestroyNotify destroy_data, ...); + typedef gpointer(delete_password_fn)(const PasswordSchema *schema, + OperationDoneCallback callback, gpointer data, + GDestroyNotify destroy_data, ...); + + is_available_fn *is_available; + find_password_fn *find_password; + store_password_fn *store_password; + delete_password_fn *delete_password; }; - #endif diff --git a/qtkeychain/keychain.cpp b/qtkeychain/keychain.cpp index 272285ee..afa9f61b 100644 --- a/qtkeychain/keychain.cpp +++ b/qtkeychain/keychain.cpp @@ -11,196 +11,218 @@ using namespace QKeychain; -Job::Job( JobPrivate *q, QObject *parent ) - : QObject( parent ) - , d ( q ) { -} +Job::Job(JobPrivate *q, QObject *parent) : QObject(parent), d(q) { } -Job::~Job() { +Job::~Job() +{ delete d; } -QString Job::service() const { +QString Job::service() const +{ return d->service; } -QSettings* Job::settings() const { +QSettings *Job::settings() const +{ return d->settings; } -void Job::setSettings( QSettings* settings ) { +void Job::setSettings(QSettings *settings) +{ d->settings = settings; } -void Job::start() { - QMetaObject::invokeMethod( this, "doStart", Qt::QueuedConnection ); +void Job::start() +{ + QMetaObject::invokeMethod(this, "doStart", Qt::QueuedConnection); } -bool Job::autoDelete() const { +bool Job::autoDelete() const +{ return d->autoDelete; } -void Job::setAutoDelete( bool autoDelete ) { +void Job::setAutoDelete(bool autoDelete) +{ d->autoDelete = autoDelete; } -bool Job::insecureFallback() const { +bool Job::insecureFallback() const +{ return d->insecureFallback; } -void Job::setInsecureFallback( bool insecureFallback ) { +void Job::setInsecureFallback(bool insecureFallback) +{ d->insecureFallback = insecureFallback; } -void Job::doStart() { - JobExecutor::instance()->enqueue( this ); +void Job::doStart() +{ + JobExecutor::instance()->enqueue(this); } -void Job::emitFinished() { - emit finished( this ); - if ( d->autoDelete ) +void Job::emitFinished() +{ + emit finished(this); + if (d->autoDelete) deleteLater(); } -void Job::emitFinishedWithError( Error error, const QString& errorString ) { +void Job::emitFinishedWithError(Error error, const QString &errorString) +{ d->error = error; d->errorString = errorString; emitFinished(); } -void Job::scheduledStart() { +void Job::scheduledStart() +{ d->scheduledStart(); } -Error Job::error() const { +Error Job::error() const +{ return d->error; } -QString Job::errorString() const { +QString Job::errorString() const +{ return d->errorString; } -void Job::setError( Error error ) { +void Job::setError(Error error) +{ d->error = error; } -void Job::setErrorString( const QString& errorString ) { +void Job::setErrorString(const QString &errorString) +{ d->errorString = errorString; } -ReadPasswordJob::ReadPasswordJob( const QString& service, QObject* parent ) - : Job( new ReadPasswordJobPrivate( service, this ), parent ) { - +ReadPasswordJob::ReadPasswordJob(const QString &service, QObject *parent) + : Job(new ReadPasswordJobPrivate(service, this), parent) +{ } -ReadPasswordJob::~ReadPasswordJob() { -} +ReadPasswordJob::~ReadPasswordJob() { } -QString ReadPasswordJob::textData() const { - return QString::fromUtf8( d->data ); +QString ReadPasswordJob::textData() const +{ + return QString::fromUtf8(d->data); } -QByteArray ReadPasswordJob::binaryData() const { +QByteArray ReadPasswordJob::binaryData() const +{ return d->data; } -QString Job::key() const { +QString Job::key() const +{ return d->key; } -void Job::setKey( const QString& key_ ) { +void Job::setKey(const QString &key_) +{ d->key = key_; } -WritePasswordJob::WritePasswordJob( const QString& service, QObject* parent ) - : Job( new WritePasswordJobPrivate( service, this ), parent ) { +WritePasswordJob::WritePasswordJob(const QString &service, QObject *parent) + : Job(new WritePasswordJobPrivate(service, this), parent) +{ } -WritePasswordJob::~WritePasswordJob() { -} +WritePasswordJob::~WritePasswordJob() { } -void WritePasswordJob::setBinaryData( const QByteArray& data ) { +void WritePasswordJob::setBinaryData(const QByteArray &data) +{ d->data = data; d->mode = JobPrivate::Binary; } -void WritePasswordJob::setTextData( const QString& data ) { +void WritePasswordJob::setTextData(const QString &data) +{ d->data = data.toUtf8(); d->mode = JobPrivate::Text; } -DeletePasswordJob::DeletePasswordJob( const QString& service, QObject* parent ) - : Job( new DeletePasswordJobPrivate( service, this ), parent ) { -} - -DeletePasswordJob::~DeletePasswordJob() { +DeletePasswordJob::DeletePasswordJob(const QString &service, QObject *parent) + : Job(new DeletePasswordJobPrivate(service, this), parent) +{ } -DeletePasswordJobPrivate::DeletePasswordJobPrivate(const QString &service_, DeletePasswordJob *qq) : - JobPrivate(service_, qq) { +DeletePasswordJob::~DeletePasswordJob() { } +DeletePasswordJobPrivate::DeletePasswordJobPrivate(const QString &service_, DeletePasswordJob *qq) + : JobPrivate(service_, qq) +{ } -JobExecutor::JobExecutor() - : QObject( nullptr ) - , m_jobRunning( false ) { -} +JobExecutor::JobExecutor() : QObject(nullptr), m_jobRunning(false) { } -void JobExecutor::enqueue( Job* job ) { - m_queue.enqueue( job ); +void JobExecutor::enqueue(Job *job) +{ + m_queue.enqueue(job); startNextIfNoneRunning(); } -void JobExecutor::startNextIfNoneRunning() { - if ( m_queue.isEmpty() || m_jobRunning ) +void JobExecutor::startNextIfNoneRunning() +{ + if (m_queue.isEmpty() || m_jobRunning) return; QPointer next; - while ( !next && !m_queue.isEmpty() ) { + while (!next && !m_queue.isEmpty()) { next = m_queue.dequeue(); } - if ( next ) { - connect( next, SIGNAL(finished(QKeychain::Job*)), this, SLOT(jobFinished(QKeychain::Job*)) ); - connect( next, SIGNAL(destroyed(QObject*)), this, SLOT(jobDestroyed(QObject*)) ); + if (next) { + connect(next, SIGNAL(finished(QKeychain::Job *)), this, + SLOT(jobFinished(QKeychain::Job *))); + connect(next, SIGNAL(destroyed(QObject *)), this, SLOT(jobDestroyed(QObject *))); m_jobRunning = true; next->scheduledStart(); } } -void JobExecutor::jobDestroyed( QObject* object ) { - Job* job = static_cast(object); - Q_UNUSED( object ) // for release mode - job->disconnect( this ); +void JobExecutor::jobDestroyed(QObject *object) +{ + Job *job = static_cast(object); + Q_UNUSED(object) // for release mode + job->disconnect(this); m_jobRunning = false; startNextIfNoneRunning(); } -void JobExecutor::jobFinished( Job* job ) { - Q_UNUSED( job ) // for release mode - job->disconnect( this ); +void JobExecutor::jobFinished(Job *job) +{ + Q_UNUSED(job) // for release mode + job->disconnect(this); m_jobRunning = false; startNextIfNoneRunning(); } -JobExecutor* JobExecutor::s_instance = nullptr; +JobExecutor *JobExecutor::s_instance = nullptr; -JobExecutor* JobExecutor::instance() { - if ( !s_instance ) +JobExecutor *JobExecutor::instance() +{ + if (!s_instance) s_instance = new JobExecutor; return s_instance; } -ReadPasswordJobPrivate::ReadPasswordJobPrivate(const QString &service_, ReadPasswordJob *qq) : - JobPrivate(service_, qq) { - +ReadPasswordJobPrivate::ReadPasswordJobPrivate(const QString &service_, ReadPasswordJob *qq) + : JobPrivate(service_, qq) +{ } JobPrivate::JobPrivate(const QString &service_, Job *qq) - : q(qq) - , mode( Text ) - , error( NoError ) - , service( service_ ) - , autoDelete( true ) - , insecureFallback( false ) + : q(qq), + mode(Text), + error(NoError), + service(service_), + autoDelete(true), + insecureFallback(false) { } @@ -217,7 +239,7 @@ QString JobPrivate::modeToString(Mode m) return QString(); } -JobPrivate::Mode JobPrivate::stringToMode(const QString& s) +JobPrivate::Mode JobPrivate::stringToMode(const QString &s) { if (s == QLatin1String("Text") || s == QLatin1String("1")) return Text; @@ -229,7 +251,7 @@ JobPrivate::Mode JobPrivate::stringToMode(const QString& s) return Text; } -WritePasswordJobPrivate::WritePasswordJobPrivate(const QString &service_, WritePasswordJob *qq) : - JobPrivate(service_, qq) { - +WritePasswordJobPrivate::WritePasswordJobPrivate(const QString &service_, WritePasswordJob *qq) + : JobPrivate(service_, qq) +{ } diff --git a/qtkeychain/keychain.h b/qtkeychain/keychain.h index 8c006cbc..3c6f4810 100644 --- a/qtkeychain/keychain.h +++ b/qtkeychain/keychain.h @@ -10,9 +10,9 @@ #define KEYCHAIN_H #if !defined(QTKEYCHAIN_NO_EXPORT) -#include "qkeychain_export.h" +# include "qkeychain_export.h" #else -#define QKEYCHAIN_EXPORT +# define QKEYCHAIN_EXPORT #endif #include @@ -28,7 +28,7 @@ namespace QKeychain { * Error codes */ enum Error { - NoError=0, /**< No error occurred, operation was successful */ + NoError = 0, /**< No error occurred, operation was successful */ EntryNotFound, /**< For the given key no data was found */ CouldNotDeleteEntry, /**< Could not delete existing secret data */ AccessDeniedByUser, /**< User denied access to keychain */ @@ -44,7 +44,8 @@ class JobPrivate; /** * @brief Abstract base class for all QKeychain jobs. */ -class QKEYCHAIN_EXPORT Job : public QObject { +class QKEYCHAIN_EXPORT Job : public QObject +{ Q_OBJECT public: ~Job() override; @@ -54,14 +55,15 @@ class QKEYCHAIN_EXPORT Job : public QObject { * @see setSettings() * @see insecureFallback() */ - QSettings* settings() const; + QSettings *settings() const; /** - * @return Set the QSettings instance that will be used as plaintext storage if insecureFallback() is true. + * @return Set the QSettings instance that will be used as plaintext storage if + * insecureFallback() is true. * @see settings() * @see insecureFallback() */ - void setSettings( QSettings* settings ); + void setSettings(QSettings *settings); /** * Call this method to start the job. @@ -102,7 +104,8 @@ class QKEYCHAIN_EXPORT Job : public QObject { QString errorString() const; /** - * @return Whether this job autodeletes itself once finished() has been emitted. Default is true. + * @return Whether this job autodeletes itself once finished() has been emitted. Default is + * true. * @see setAutoDelete() */ bool autoDelete() const; @@ -111,10 +114,11 @@ class QKEYCHAIN_EXPORT Job : public QObject { * Set whether this job should autodelete itself once finished() has been emitted. * @see autoDelete() */ - void setAutoDelete( bool autoDelete ); + void setAutoDelete(bool autoDelete); /** - * @return Whether this job will use plaintext storage on unsupported platforms. Default is false. + * @return Whether this job will use plaintext storage on unsupported platforms. Default is + * false. * @see setInsecureFallback() */ bool insecureFallback() const; @@ -123,7 +127,7 @@ class QKEYCHAIN_EXPORT Job : public QObject { * Set whether this job should use plaintext storage on unsupported platforms. * @see insecureFallback() */ - void setInsecureFallback( bool insecureFallback ); + void setInsecureFallback(bool insecureFallback); /** * @return The string used as key by this job. @@ -136,10 +140,10 @@ class QKEYCHAIN_EXPORT Job : public QObject { * The key can be an empty string. * @see key() */ - void setKey( const QString& key ); + void setKey(const QString &key); void emitFinished(); - void emitFinishedWithError(Error, const QString& errorString); + void emitFinishedWithError(Error, const QString &errorString); Q_SIGNALS: /** @@ -147,26 +151,26 @@ class QKEYCHAIN_EXPORT Job : public QObject { * You can connect to this signal to be notified about the job's completion. * @see start() */ - void finished( QKeychain::Job* ); + void finished(QKeychain::Job *); protected: - explicit Job( JobPrivate *q, QObject* parent=nullptr ); + explicit Job(JobPrivate *q, QObject *parent = nullptr); Q_INVOKABLE void doStart(); private: - void setError( Error error ); - void setErrorString( const QString& errorString ); + void setError(Error error); + void setErrorString(const QString &errorString); void scheduledStart(); protected: - JobPrivate* const d; + JobPrivate *const d; -friend class JobExecutor; -friend class JobPrivate; -friend class ReadPasswordJobPrivate; -friend class WritePasswordJobPrivate; -friend class DeletePasswordJobPrivate; + friend class JobExecutor; + friend class JobPrivate; + friend class ReadPasswordJobPrivate; + friend class WritePasswordJobPrivate; + friend class DeletePasswordJobPrivate; }; class ReadPasswordJobPrivate; @@ -177,7 +181,8 @@ class ReadPasswordJobPrivate; * This job requires a "service" string, which is basically a namespace of keys within the keychain. * This means that you can read all the pairs stored in the same service string. */ -class QKEYCHAIN_EXPORT ReadPasswordJob : public Job { +class QKEYCHAIN_EXPORT ReadPasswordJob : public Job +{ Q_OBJECT public: /** @@ -185,7 +190,7 @@ class QKEYCHAIN_EXPORT ReadPasswordJob : public Job { * @param service The service string used by this job (can be empty). * @param parent The parent of this job. */ - explicit ReadPasswordJob( const QString& service, QObject* parent=nullptr ); + explicit ReadPasswordJob(const QString &service, QObject *parent = nullptr); ~ReadPasswordJob() override; /** @@ -214,7 +219,8 @@ class WritePasswordJobPrivate; * This job requires a "service" string, which is basically a namespace of keys within the keychain. * This means that you can store different pairs under the same service string. */ -class QKEYCHAIN_EXPORT WritePasswordJob : public Job { +class QKEYCHAIN_EXPORT WritePasswordJob : public Job +{ Q_OBJECT public: /** @@ -222,24 +228,23 @@ class QKEYCHAIN_EXPORT WritePasswordJob : public Job { * @param service The service string used by this job (can be empty). * @param parent The parent of this job. */ - explicit WritePasswordJob( const QString& service, QObject* parent=nullptr ); + explicit WritePasswordJob(const QString &service, QObject *parent = nullptr); ~WritePasswordJob() override; /** * Set the @p data that the job will store in the keychain as binary data. * @warning setBinaryData() and setTextData() are mutually exclusive. */ - void setBinaryData( const QByteArray& data ); + void setBinaryData(const QByteArray &data); /** * Set the @p data that the job will store in the keychain as string. * Typically @p data is a password. * @warning setBinaryData() and setTextData() are mutually exclusive. */ - void setTextData( const QString& data ); + void setTextData(const QString &data); private: - friend class QKeychain::WritePasswordJobPrivate; }; @@ -251,7 +256,8 @@ class DeletePasswordJobPrivate; * This job requires a "service" string, which is basically a namespace of keys within the keychain. * This means that you can delete all the pairs stored in the same service string. */ -class QKEYCHAIN_EXPORT DeletePasswordJob : public Job { +class QKEYCHAIN_EXPORT DeletePasswordJob : public Job +{ Q_OBJECT public: /** @@ -259,7 +265,7 @@ class QKEYCHAIN_EXPORT DeletePasswordJob : public Job { * @param service The service string used by this job (can be empty). * @param parent The parent of this job. */ - explicit DeletePasswordJob( const QString& service, QObject* parent=nullptr ); + explicit DeletePasswordJob(const QString &service, QObject *parent = nullptr); ~DeletePasswordJob() override; private: @@ -277,6 +283,6 @@ class QKEYCHAIN_EXPORT DeletePasswordJob : public Job { */ QKEYCHAIN_EXPORT bool isAvailable(); -} // namespace QtKeychain +} // namespace QKeychain #endif diff --git a/qtkeychain/keychain_android.cpp b/qtkeychain/keychain_android.cpp index 8efecf97..b759a73d 100644 --- a/qtkeychain/keychain_android.cpp +++ b/qtkeychain/keychain_android.cpp @@ -13,7 +13,7 @@ #include "plaintextstore_p.h" #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) -#include +# include #endif using namespace QKeychain; @@ -23,11 +23,11 @@ using android::security::KeyPairGeneratorSpec; using java::io::ByteArrayInputStream; using java::io::ByteArrayOutputStream; -using java::security::interfaces::RSAPrivateKey; -using java::security::interfaces::RSAPublicKey; using java::security::KeyPair; using java::security::KeyPairGenerator; using java::security::KeyStore; +using java::security::interfaces::RSAPrivateKey; +using java::security::interfaces::RSAPublicKey; using java::util::Calendar; using javax::crypto::Cipher; @@ -42,7 +42,7 @@ inline QString makeAlias(const QString &service, const QString &key) return service + QLatin1Char('/') + key; } -} +} // namespace void ReadPasswordJobPrivate::scheduledStart() { @@ -65,7 +65,8 @@ void ReadPasswordJobPrivate::scheduledStart() const KeyStore::PrivateKeyEntry entry = keyStore.getEntry(alias); if (!entry) { - q->emitFinishedWithError(Error::AccessDenied, tr("Could not retrieve private key from keystore")); + q->emitFinishedWithError(Error::AccessDenied, + tr("Could not retrieve private key from keystore")); return; } @@ -79,7 +80,7 @@ void ReadPasswordJobPrivate::scheduledStart() QByteArray plainData; const CipherInputStream inputStream(ByteArrayInputStream(encryptedData), cipher); - for (int nextByte; (nextByte = inputStream.read()) != -1; ) + for (int nextByte; (nextByte = inputStream.read()) != -1;) plainData.append(nextByte); mode = plainTextStore.readMode(q->key()); @@ -103,27 +104,36 @@ void WritePasswordJobPrivate::scheduledStart() end.add(Calendar::YEAR, 99); const KeyPairGeneratorSpec spec = - #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - KeyPairGeneratorSpec::Builder(Context(QtAndroid::androidActivity())). - #elif QT_VERSION < QT_VERSION_CHECK(6, 4, 0) - KeyPairGeneratorSpec::Builder(Context(QNativeInterface::QAndroidApplication::context())). - #elif QT_VERSION < QT_VERSION_CHECK(6, 7, 0) - KeyPairGeneratorSpec::Builder(Context((jobject)QNativeInterface::QAndroidApplication::context())). - #else - KeyPairGeneratorSpec::Builder(Context(QNativeInterface::QAndroidApplication::context().object())). - #endif - setAlias(alias). - setSubject(X500Principal(QStringLiteral("CN=QtKeychain, O=Android Authority"))). - setSerialNumber(java::math::BigInteger::ONE). - setStartDate(start.getTime()). - setEndDate(end.getTime()). - build(); - - const KeyPairGenerator generator = KeyPairGenerator::getInstance(QStringLiteral("RSA"), - QStringLiteral("AndroidKeyStore")); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + KeyPairGeneratorSpec::Builder(Context(QtAndroid::androidActivity())) + . +#elif QT_VERSION < QT_VERSION_CHECK(6, 4, 0) + KeyPairGeneratorSpec::Builder( + Context(QNativeInterface::QAndroidApplication::context())) + . +#elif QT_VERSION < QT_VERSION_CHECK(6, 7, 0) + KeyPairGeneratorSpec::Builder( + Context((jobject)QNativeInterface::QAndroidApplication::context())) + . +#else + KeyPairGeneratorSpec::Builder( + Context(QNativeInterface::QAndroidApplication::context().object())) + . +#endif + setAlias(alias) + .setSubject( + X500Principal(QStringLiteral("CN=QtKeychain, O=Android Authority"))) + .setSerialNumber(java::math::BigInteger::ONE) + .setStartDate(start.getTime()) + .setEndDate(end.getTime()) + .build(); + + const KeyPairGenerator generator = KeyPairGenerator::getInstance( + QStringLiteral("RSA"), QStringLiteral("AndroidKeyStore")); if (!generator) { - q->emitFinishedWithError(Error::OtherError, tr("Could not create private key generator")); + q->emitFinishedWithError(Error::OtherError, + tr("Could not create private key generator")); return; } @@ -138,7 +148,8 @@ void WritePasswordJobPrivate::scheduledStart() const KeyStore::PrivateKeyEntry entry = keyStore.getEntry(alias); if (!entry) { - q->emitFinishedWithError(Error::AccessDenied, tr("Could not retrieve private key from keystore")); + q->emitFinishedWithError(Error::AccessDenied, + tr("Could not retrieve private key from keystore")); return; } @@ -178,7 +189,8 @@ void DeletePasswordJobPrivate::scheduledStart() const auto &alias = makeAlias(q->service(), q->key()); if (!keyStore.deleteEntry(alias)) { - q->emitFinishedWithError(Error::OtherError, tr("Could not remove private key from keystore")); + q->emitFinishedWithError(Error::OtherError, + tr("Could not remove private key from keystore")); return; } diff --git a/qtkeychain/keychain_apple.mm b/qtkeychain/keychain_apple.mm index f9a8266b..c9c00a49 100644 --- a/qtkeychain/keychain_apple.mm +++ b/qtkeychain/keychain_apple.mm @@ -19,44 +19,60 @@ QKeychain::Error code; QString message; - ErrorDescription(QKeychain::Error code, const QString &message) - : code(code), message(message) {} + ErrorDescription(QKeychain::Error code, const QString &message) : code(code), message(message) + { + } static ErrorDescription fromStatus(OSStatus status) { - switch(status) { + switch (status) { case errSecSuccess: return ErrorDescription(QKeychain::NoError, Job::tr("No error")); case errSecItemNotFound: - return ErrorDescription(QKeychain::EntryNotFound, Job::tr("The specified item could not be found in the keychain")); + return ErrorDescription( + QKeychain::EntryNotFound, + Job::tr("The specified item could not be found in the keychain")); case errSecUserCanceled: - return ErrorDescription(QKeychain::AccessDeniedByUser, Job::tr("User canceled the operation")); + return ErrorDescription(QKeychain::AccessDeniedByUser, + Job::tr("User canceled the operation")); case errSecInteractionNotAllowed: - return ErrorDescription(QKeychain::AccessDenied, Job::tr("User interaction is not allowed")); + return ErrorDescription(QKeychain::AccessDenied, + Job::tr("User interaction is not allowed")); case errSecNotAvailable: - return ErrorDescription(QKeychain::AccessDenied, Job::tr("No keychain is available. You may need to restart your computer")); + return ErrorDescription( + QKeychain::AccessDenied, + Job::tr("No keychain is available. You may need to restart your computer")); case errSecAuthFailed: - return ErrorDescription(QKeychain::AccessDenied, Job::tr("The user name or passphrase you entered is not correct")); + return ErrorDescription( + QKeychain::AccessDenied, + Job::tr("The user name or passphrase you entered is not correct")); case errSecVerifyFailed: - return ErrorDescription(QKeychain::AccessDenied, Job::tr("A cryptographic verification failure has occurred")); + return ErrorDescription(QKeychain::AccessDenied, + Job::tr("A cryptographic verification failure has occurred")); case errSecUnimplemented: - return ErrorDescription(QKeychain::NotImplemented, Job::tr("Function or operation not implemented")); + return ErrorDescription(QKeychain::NotImplemented, + Job::tr("Function or operation not implemented")); case errSecIO: return ErrorDescription(QKeychain::OtherError, Job::tr("I/O error")); case errSecOpWr: - return ErrorDescription(QKeychain::OtherError, Job::tr("Already open with with write permission")); + return ErrorDescription(QKeychain::OtherError, + Job::tr("Already open with with write permission")); case errSecParam: - return ErrorDescription(QKeychain::OtherError, Job::tr("Invalid parameters passed to a function")); + return ErrorDescription(QKeychain::OtherError, + Job::tr("Invalid parameters passed to a function")); case errSecAllocate: return ErrorDescription(QKeychain::OtherError, Job::tr("Failed to allocate memory")); case errSecBadReq: - return ErrorDescription(QKeychain::OtherError, Job::tr("Bad parameter or invalid state for operation")); + return ErrorDescription(QKeychain::OtherError, + Job::tr("Bad parameter or invalid state for operation")); case errSecInternalComponent: return ErrorDescription(QKeychain::OtherError, Job::tr("An internal component failed")); case errSecDuplicateItem: - return ErrorDescription(QKeychain::OtherError, Job::tr("The specified item already exists in the keychain")); + return ErrorDescription(QKeychain::OtherError, + Job::tr("The specified item already exists in the keychain")); case errSecDecode: - return ErrorDescription(QKeychain::OtherError, Job::tr("Unable to decode the provided data")); + return ErrorDescription(QKeychain::OtherError, + Job::tr("Unable to decode the provided data")); } return ErrorDescription(QKeychain::OtherError, Job::tr("Unknown error")); @@ -68,12 +84,12 @@ @interface AppleKeychainInterface : NSObject - (instancetype)initWithJob:(Job *)job andPrivateJob:(JobPrivate *)privateJob; - (void)keychainTaskFinished; - (void)keychainReadTaskFinished:(NSData *)retrievedData; -- (void)keychainTaskFinishedWithError:(OSStatus)status descriptiveMessage:(NSString *)descriptiveMessage; +- (void)keychainTaskFinishedWithError:(OSStatus)status + descriptiveMessage:(NSString *)descriptiveMessage; @end -@interface AppleKeychainInterface() -{ +@interface AppleKeychainInterface () { QPointer _job; QPointer _privateJob; } @@ -120,12 +136,15 @@ - (void)keychainReadTaskFinished:(NSData *)retrievedData } } -- (void)keychainTaskFinishedWithError:(OSStatus)status descriptiveMessage:(NSString *)descriptiveMessage +- (void)keychainTaskFinishedWithError:(OSStatus)status + descriptiveMessage:(NSString *)descriptiveMessage { const auto localisedDescriptiveMessage = Job::tr([descriptiveMessage UTF8String]); const ErrorDescription error = ErrorDescription::fromStatus(status); - const auto fullMessage = localisedDescriptiveMessage.isEmpty() ? error.message : QStringLiteral("%1: %2").arg(localisedDescriptiveMessage, error.message); + const auto fullMessage = localisedDescriptiveMessage.isEmpty() + ? error.message + : QStringLiteral("%1: %2").arg(localisedDescriptiveMessage, error.message); if (_job) { _job->emitFinishedWithError(error.code, fullMessage); @@ -134,64 +153,68 @@ - (void)keychainTaskFinishedWithError:(OSStatus)status descriptiveMessage:(NSStr @end - -static void StartReadPassword(const QString &service, const QString &key, AppleKeychainInterface * const interface) +static void StartReadPassword(const QString &service, const QString &key, + AppleKeychainInterface *const interface) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ - NSDictionary * const query = @{ - (__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassGenericPassword, - (__bridge NSString *)kSecAttrService: service.toNSString(), - (__bridge NSString *)kSecAttrAccount: key.toNSString(), - (__bridge NSString *)kSecReturnData: @YES, + NSDictionary *const query = @{ + (__bridge NSString *)kSecClass : (__bridge NSString *)kSecClassGenericPassword, + (__bridge NSString *)kSecAttrService : service.toNSString(), + (__bridge NSString *)kSecAttrAccount : key.toNSString(), + (__bridge NSString *)kSecReturnData : @YES, }; CFTypeRef dataRef = nil; - const OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef) query, &dataRef); + const OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &dataRef); if (status == errSecSuccess) { const CFDataRef castedDataRef = (CFDataRef)dataRef; - NSData * const data = (__bridge NSData *)castedDataRef; + NSData *const data = (__bridge NSData *)castedDataRef; dispatch_async(dispatch_get_main_queue(), ^{ [interface keychainReadTaskFinished:data]; [interface release]; }); } else { - NSString * const descriptiveErrorString = @"Could not retrieve private key from keystore"; + NSString *const descriptiveErrorString = + @"Could not retrieve private key from keystore"; dispatch_async(dispatch_get_main_queue(), ^{ - [interface keychainTaskFinishedWithError:status descriptiveMessage:descriptiveErrorString]; + [interface keychainTaskFinishedWithError:status + descriptiveMessage:descriptiveErrorString]; [interface release]; }); } - if (dataRef) { - CFRelease(dataRef); - } + if (dataRef) { + CFRelease(dataRef); + } }); } -static void StartWritePassword(const QString &service, const QString &key, const QByteArray &data, AppleKeychainInterface * const interface) +static void StartWritePassword(const QString &service, const QString &key, const QByteArray &data, + AppleKeychainInterface *const interface) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ - NSDictionary * const query = @{ - (__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassGenericPassword, - (__bridge NSString *)kSecAttrService: service.toNSString(), - (__bridge NSString *)kSecAttrAccount: key.toNSString(), + NSDictionary *const query = @{ + (__bridge NSString *)kSecClass : (__bridge NSString *)kSecClassGenericPassword, + (__bridge NSString *)kSecAttrService : service.toNSString(), + (__bridge NSString *)kSecAttrAccount : key.toNSString(), }; OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, nil); if (status == errSecSuccess) { - NSDictionary * const update = @{ - (__bridge NSString *)kSecValueData: data.toNSData(), + NSDictionary *const update = @{ + (__bridge NSString *)kSecValueData : data.toNSData(), }; - status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)update); + status = SecItemUpdate((__bridge CFDictionaryRef)query, + (__bridge CFDictionaryRef)update); } else { - NSDictionary * const insert = @{ - (__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassGenericPassword, - (__bridge NSString *)kSecAttrService: service.toNSString(), - (__bridge NSString *)kSecAttrAccount: key.toNSString(), - (__bridge NSString *)kSecValueData: data.toNSData(), + NSDictionary *const insert = @{ + (__bridge NSString *)kSecClass : (__bridge NSString *)kSecClassGenericPassword, + (__bridge NSString *)kSecAttrService : service.toNSString(), + (__bridge NSString *)kSecAttrAccount : key.toNSString(), + (__bridge NSString *)kSecValueData : data.toNSData(), }; status = SecItemAdd((__bridge const CFDictionaryRef)insert, nil); @@ -203,23 +226,25 @@ static void StartWritePassword(const QString &service, const QString &key, const [interface release]; }); } else { - NSString * const descriptiveErrorString = @"Could not store data in settings"; + NSString *const descriptiveErrorString = @"Could not store data in settings"; dispatch_async(dispatch_get_main_queue(), ^{ - [interface keychainTaskFinishedWithError:status descriptiveMessage:descriptiveErrorString]; + [interface keychainTaskFinishedWithError:status + descriptiveMessage:descriptiveErrorString]; [interface release]; }); } }); } -static void StartDeletePassword(const QString &service, const QString &key, AppleKeychainInterface * const interface) +static void StartDeletePassword(const QString &service, const QString &key, + AppleKeychainInterface *const interface) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ - NSDictionary * const query = @{ - (__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassGenericPassword, - (__bridge NSString *)kSecAttrService: service.toNSString(), - (__bridge NSString *)kSecAttrAccount: key.toNSString(), + NSDictionary *const query = @{ + (__bridge NSString *)kSecClass : (__bridge NSString *)kSecClassGenericPassword, + (__bridge NSString *)kSecAttrService : service.toNSString(), + (__bridge NSString *)kSecAttrAccount : key.toNSString(), }; const OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query); @@ -230,9 +255,10 @@ static void StartDeletePassword(const QString &service, const QString &key, Appl [interface release]; }); } else { - NSString * const descriptiveErrorString = @"Could not remove private key from keystore"; + NSString *const descriptiveErrorString = @"Could not remove private key from keystore"; dispatch_async(dispatch_get_main_queue(), ^{ - [interface keychainTaskFinishedWithError:status descriptiveMessage:descriptiveErrorString]; + [interface keychainTaskFinishedWithError:status + descriptiveMessage:descriptiveErrorString]; [interface release]; }); } @@ -241,19 +267,22 @@ static void StartDeletePassword(const QString &service, const QString &key, Appl void ReadPasswordJobPrivate::scheduledStart() { - AppleKeychainInterface * const interface = [[AppleKeychainInterface alloc] initWithJob:q andPrivateJob:this]; + AppleKeychainInterface *const interface = [[AppleKeychainInterface alloc] initWithJob:q + andPrivateJob:this]; StartReadPassword(service, key, interface); } void WritePasswordJobPrivate::scheduledStart() { - AppleKeychainInterface * const interface = [[AppleKeychainInterface alloc] initWithJob:q andPrivateJob:this]; + AppleKeychainInterface *const interface = [[AppleKeychainInterface alloc] initWithJob:q + andPrivateJob:this]; StartWritePassword(service, key, data, interface); } void DeletePasswordJobPrivate::scheduledStart() { - AppleKeychainInterface * const interface = [[AppleKeychainInterface alloc] initWithJob:q andPrivateJob:this]; + AppleKeychainInterface *const interface = [[AppleKeychainInterface alloc] initWithJob:q + andPrivateJob:this]; StartDeletePassword(service, key, interface); } diff --git a/qtkeychain/keychain_haiku.cpp b/qtkeychain/keychain_haiku.cpp index 153ebe7e..94374c8e 100644 --- a/qtkeychain/keychain_haiku.cpp +++ b/qtkeychain/keychain_haiku.cpp @@ -20,16 +20,15 @@ using namespace QKeychain; -class AutoApp { +class AutoApp +{ public: AutoApp(); ~AutoApp(); BApplication *app; }; - -AutoApp::AutoApp() - : app(nullptr) +AutoApp::AutoApp() : app(nullptr) { if (be_app) return; @@ -56,8 +55,8 @@ AutoApp::AutoApp() if (signature[0] != '\0') appSignature = QLatin1String(signature); else - appSignature = QLatin1String("application/x-vnd.qtkeychain-") + - QCoreApplication::applicationName().remove("_x86"); + appSignature = QLatin1String("application/x-vnd.qtkeychain-") + + QCoreApplication::applicationName().remove("_x86"); app = new BApplication(appSignature.toUtf8().constData()); } @@ -67,10 +66,10 @@ AutoApp::~AutoApp() delete app; } -static QString strForStatus( status_t os ) { - const char * const buf = strerror(os) ; - return QObject::tr( "error 0x%1: %2" ) - .arg( os, 8, 16 ).arg( QString::fromUtf8( buf, strlen( buf ) ) ); +static QString strForStatus(status_t os) +{ + const char *const buf = strerror(os); + return QObject::tr("error 0x%1: %2").arg(os, 8, 16).arg(QString::fromUtf8(buf, strlen(buf))); } void ReadPasswordJobPrivate::scheduledStart() @@ -81,14 +80,12 @@ void ReadPasswordJobPrivate::scheduledStart() BKeyStore keyStore; BPasswordKey password; - status_t result = keyStore.GetKey(B_KEY_TYPE_PASSWORD, - q->service().toUtf8().constData(), - q->key().toUtf8().constData(), - false, password); + status_t result = keyStore.GetKey(B_KEY_TYPE_PASSWORD, q->service().toUtf8().constData(), + q->key().toUtf8().constData(), false, password); - data = QByteArray(reinterpret_cast(password.Data())); + data = QByteArray(reinterpret_cast(password.Data())); - switch ( result ) { + switch (result) { case B_OK: q->emitFinished(); return; @@ -97,12 +94,12 @@ void ReadPasswordJobPrivate::scheduledStart() error = EntryNotFound; break; default: - errorString = strForStatus( result ); + errorString = strForStatus(result); error = OtherError; break; } - q->emitFinishedWithError( error, errorString ); + q->emitFinishedWithError(error, errorString); } void WritePasswordJobPrivate::scheduledStart() @@ -111,32 +108,28 @@ void WritePasswordJobPrivate::scheduledStart() QString errorString; Error error = NoError; BKeyStore keyStore; - BPasswordKey password(data.constData(), - B_KEY_PURPOSE_GENERIC, - q->service().toUtf8().constData(), - q->key().toUtf8().constData()); + BPasswordKey password(data.constData(), B_KEY_PURPOSE_GENERIC, + q->service().toUtf8().constData(), q->key().toUtf8().constData()); status_t result = B_OK; // re-add as binary if it's not text if (mode == Binary) - result = password.SetData(reinterpret_cast(data.constData()), data.size()); + result = password.SetData(reinterpret_cast(data.constData()), data.size()); if (result == B_OK) result = keyStore.AddKey(password); if (result == B_NAME_IN_USE) { BPasswordKey old_password; - result = keyStore.GetKey(B_KEY_TYPE_PASSWORD, - q->service().toUtf8().constData(), - q->key().toUtf8().constData(), - false, old_password); + result = keyStore.GetKey(B_KEY_TYPE_PASSWORD, q->service().toUtf8().constData(), + q->key().toUtf8().constData(), false, old_password); if (result == B_OK) result = keyStore.RemoveKey(old_password); if (result == B_OK) result = keyStore.AddKey(password); } - switch ( result ) { + switch (result) { case B_OK: q->emitFinished(); return; @@ -145,12 +138,12 @@ void WritePasswordJobPrivate::scheduledStart() error = EntryNotFound; break; default: - errorString = strForStatus( result ); + errorString = strForStatus(result); error = OtherError; break; } - q->emitFinishedWithError( error, errorString ); + q->emitFinishedWithError(error, errorString); } void DeletePasswordJobPrivate::scheduledStart() @@ -161,15 +154,13 @@ void DeletePasswordJobPrivate::scheduledStart() BKeyStore keyStore; BPasswordKey password; - status_t result = keyStore.GetKey(B_KEY_TYPE_PASSWORD, - q->service().toUtf8().constData(), - q->key().toUtf8().constData(), - false, password); + status_t result = keyStore.GetKey(B_KEY_TYPE_PASSWORD, q->service().toUtf8().constData(), + q->key().toUtf8().constData(), false, password); if (result == B_OK) result = keyStore.RemoveKey(password); - switch ( result ) { + switch (result) { case B_OK: q->emitFinished(); return; @@ -178,12 +169,12 @@ void DeletePasswordJobPrivate::scheduledStart() error = EntryNotFound; break; default: - errorString = strForStatus( result ); + errorString = strForStatus(result); error = CouldNotDeleteEntry; break; } - q->emitFinishedWithError( error, errorString ); + q->emitFinishedWithError(error, errorString); } bool QKeychain::isAvailable() diff --git a/qtkeychain/keychain_p.h b/qtkeychain/keychain_p.h index a66cb423..6ae89d60 100644 --- a/qtkeychain/keychain_p.h +++ b/qtkeychain/keychain_p.h @@ -17,9 +17,9 @@ #if defined(KEYCHAIN_DBUS) -#include +# include -#include "kwallet_interface.h" +# include "kwallet_interface.h" #else class QDBusPendingCallWatcher; @@ -32,44 +32,42 @@ namespace QKeychain { class JobExecutor; -class JobPrivate : public QObject { +class JobPrivate : public QObject +{ Q_OBJECT public: - enum Mode { - Text, - Binary - }; + enum Mode { Text, Binary }; virtual void scheduledStart() = 0; static QString modeToString(Mode m); - static Mode stringToMode(const QString& s); + static Mode stringToMode(const QString &s); - Job* const q; + Job *const q; Mode mode; QByteArray data; #if defined(KEYCHAIN_DBUS) - org::kde::KWallet* iface; + org::kde::KWallet *iface; int walletHandle; - static void gnomeKeyring_readCb( int result, const char* string, JobPrivate* data ); - static void gnomeKeyring_writeCb( int result, JobPrivate* self ); + static void gnomeKeyring_readCb(int result, const char *string, JobPrivate *data); + static void gnomeKeyring_writeCb(int result, JobPrivate *self); - virtual void fallbackOnError(const QDBusError& err) = 0; + virtual void fallbackOnError(const QDBusError &err) = 0; protected Q_SLOTS: - void kwalletWalletFound( QDBusPendingCallWatcher* watcher ); - virtual void kwalletFinished( QDBusPendingCallWatcher* watcher ); - virtual void kwalletOpenFinished( QDBusPendingCallWatcher* watcher ); + void kwalletWalletFound(QDBusPendingCallWatcher *watcher); + virtual void kwalletFinished(QDBusPendingCallWatcher *watcher); + virtual void kwalletOpenFinished(QDBusPendingCallWatcher *watcher); #else - void kwalletWalletFound( QDBusPendingCallWatcher* ) {} - virtual void kwalletFinished( QDBusPendingCallWatcher* ) {} - virtual void kwalletOpenFinished( QDBusPendingCallWatcher* ) {} + void kwalletWalletFound(QDBusPendingCallWatcher *) { } + virtual void kwalletFinished(QDBusPendingCallWatcher *) { } + virtual void kwalletOpenFinished(QDBusPendingCallWatcher *) { } #endif protected: - JobPrivate( const QString& service_, Job *q ); + JobPrivate(const QString &service_, Job *q); protected: QKeychain::Error error; @@ -80,58 +78,61 @@ protected Q_SLOTS: QPointer settings; QString key; -friend class Job; -friend class JobExecutor; -friend class ReadPasswordJob; -friend class WritePasswordJob; -friend class PlainTextStore; + friend class Job; + friend class JobExecutor; + friend class ReadPasswordJob; + friend class WritePasswordJob; + friend class PlainTextStore; }; -class ReadPasswordJobPrivate : public JobPrivate { +class ReadPasswordJobPrivate : public JobPrivate +{ Q_OBJECT public: - explicit ReadPasswordJobPrivate( const QString &service_, ReadPasswordJob* qq ); + explicit ReadPasswordJobPrivate(const QString &service_, ReadPasswordJob *qq); void scheduledStart() override; #if defined(KEYCHAIN_DBUS) - void fallbackOnError(const QDBusError& err) override; + void fallbackOnError(const QDBusError &err) override; private Q_SLOTS: - void kwalletOpenFinished( QDBusPendingCallWatcher* watcher ) override; - void kwalletEntryTypeFinished( QDBusPendingCallWatcher* watcher ); - void kwalletFinished( QDBusPendingCallWatcher* watcher ) override; -#else //moc's too dumb to respect above macros, so just define empty slot implementations + void kwalletOpenFinished(QDBusPendingCallWatcher *watcher) override; + void kwalletEntryTypeFinished(QDBusPendingCallWatcher *watcher); + void kwalletFinished(QDBusPendingCallWatcher *watcher) override; +#else // moc's too dumb to respect above macros, so just define empty slot implementations private Q_SLOTS: - void kwalletOpenFinished( QDBusPendingCallWatcher* ) override {} - void kwalletEntryTypeFinished( QDBusPendingCallWatcher* ) {} - void kwalletFinished( QDBusPendingCallWatcher* ) override {} + void kwalletOpenFinished(QDBusPendingCallWatcher *) override { } + void kwalletEntryTypeFinished(QDBusPendingCallWatcher *) { } + void kwalletFinished(QDBusPendingCallWatcher *) override { } #endif friend class ReadPasswordJob; }; -class WritePasswordJobPrivate : public JobPrivate { +class WritePasswordJobPrivate : public JobPrivate +{ Q_OBJECT public: - explicit WritePasswordJobPrivate( const QString &service_, WritePasswordJob* qq ); + explicit WritePasswordJobPrivate(const QString &service_, WritePasswordJob *qq); void scheduledStart() override; #if defined(KEYCHAIN_DBUS) - void fallbackOnError(const QDBusError& err) override; + void fallbackOnError(const QDBusError &err) override; #endif friend class WritePasswordJob; }; -class DeletePasswordJobPrivate : public JobPrivate { +class DeletePasswordJobPrivate : public JobPrivate +{ Q_OBJECT public: - explicit DeletePasswordJobPrivate( const QString &service_, DeletePasswordJob* qq ); + explicit DeletePasswordJobPrivate(const QString &service_, DeletePasswordJob *qq); void scheduledStart() override; #if defined(KEYCHAIN_DBUS) - void fallbackOnError(const QDBusError& err) override; + void fallbackOnError(const QDBusError &err) override; #endif protected: @@ -140,28 +141,28 @@ class DeletePasswordJobPrivate : public JobPrivate { friend class DeletePasswordJob; }; -class JobExecutor : public QObject { +class JobExecutor : public QObject +{ Q_OBJECT public: + static JobExecutor *instance(); - static JobExecutor* instance(); - - void enqueue( Job* job ); + void enqueue(Job *job); private: explicit JobExecutor(); void startNextIfNoneRunning(); private Q_SLOTS: - void jobFinished( QKeychain::Job* ); - void jobDestroyed( QObject* object ); + void jobFinished(QKeychain::Job *); + void jobDestroyed(QObject *object); private: - static JobExecutor* s_instance; - QQueue > m_queue; + static JobExecutor *s_instance; + QQueue> m_queue; bool m_jobRunning; }; -} +} // namespace QKeychain #endif // KEYCHAIN_P_H diff --git a/qtkeychain/keychain_unix.cpp b/qtkeychain/keychain_unix.cpp index 766c5254..da7cc7f2 100644 --- a/qtkeychain/keychain_unix.cpp +++ b/qtkeychain/keychain_unix.cpp @@ -43,13 +43,14 @@ static constexpr const char KWALLET4_DBUS_PATH[] = "/modules/kwalletd"; // the following detection algorithm is derived from chromium, // licensed under BSD, see base/nix/xdg_util.cc -static DesktopEnvironment getKdeVersion() { +static DesktopEnvironment getKdeVersion() +{ QByteArray value = qgetenv("KDE_SESSION_VERSION"); - if ( value == "6" ) { + if (value == "6") { return DesktopEnv_Plasma6; - } else if ( value == "5" ) { + } else if (value == "5") { return DesktopEnv_Plasma5; - } else if (value == "4" ) { + } else if (value == "4") { return DesktopEnv_Kde4; } else { // most likely KDE3 @@ -57,32 +58,33 @@ static DesktopEnvironment getKdeVersion() { } } -static DesktopEnvironment detectDesktopEnvironment() { +static DesktopEnvironment detectDesktopEnvironment() +{ QByteArray xdgCurrentDesktop = qgetenv("XDG_CURRENT_DESKTOP"); - if ( xdgCurrentDesktop == "GNOME" ) { + if (xdgCurrentDesktop == "GNOME") { return DesktopEnv_Gnome; - } else if ( xdgCurrentDesktop == "Unity" ) { + } else if (xdgCurrentDesktop == "Unity") { return DesktopEnv_Unity; - } else if ( xdgCurrentDesktop == "KDE" ) { + } else if (xdgCurrentDesktop == "KDE") { return getKdeVersion(); - } else if ( xdgCurrentDesktop == "XFCE" ) { + } else if (xdgCurrentDesktop == "XFCE") { return DesktopEnv_Xfce; } QByteArray desktopSession = qgetenv("DESKTOP_SESSION"); - if ( desktopSession == "gnome" ) { + if (desktopSession == "gnome") { return DesktopEnv_Gnome; - } else if ( desktopSession == "kde" ) { + } else if (desktopSession == "kde") { return getKdeVersion(); - } else if ( desktopSession == "kde4" ) { + } else if (desktopSession == "kde4") { return DesktopEnv_Kde4; - } else if ( desktopSession.contains("xfce") || desktopSession == "xubuntu" ) { + } else if (desktopSession.contains("xfce") || desktopSession == "xubuntu") { return DesktopEnv_Xfce; } - if ( !qgetenv("GNOME_DESKTOP_SESSION_ID").isEmpty() ) { + if (!qgetenv("GNOME_DESKTOP_SESSION_ID").isEmpty()) { return DesktopEnv_Gnome; - } else if ( !qgetenv("KDE_FULL_SESSION").isEmpty() ) { + } else if (!qgetenv("KDE_FULL_SESSION").isEmpty()) { return getKdeVersion(); } @@ -94,10 +96,8 @@ static bool isKwalletAvailable(const char *dbusIface, const char *dbusPath) if (!QDBusConnection::sessionBus().isConnected()) return false; - org::kde::KWallet iface( - QLatin1String(dbusIface), - QLatin1String(dbusPath), - QDBusConnection::sessionBus()); + org::kde::KWallet iface(QLatin1String(dbusIface), QLatin1String(dbusPath), + QDBusConnection::sessionBus()); // At this point iface.isValid() can return false even though the // interface is activatable by making a call. Hence we check whether @@ -182,7 +182,6 @@ static KeyringBackend detectKeyringBackend() // "keychain available". return Backend_GnomeKeyring; } - } static KeyringBackend getKeyringBackend() @@ -191,37 +190,39 @@ static KeyringBackend getKeyringBackend() return backend; } -static void kwalletReadPasswordScheduledStartImpl(const char * service, const char * path, ReadPasswordJobPrivate * priv) { - if ( QDBusConnection::sessionBus().isConnected() ) - { - priv->iface = new org::kde::KWallet( QLatin1String(service), QLatin1String(path), QDBusConnection::sessionBus(), priv ); +static void kwalletReadPasswordScheduledStartImpl(const char *service, const char *path, + ReadPasswordJobPrivate *priv) +{ + if (QDBusConnection::sessionBus().isConnected()) { + priv->iface = new org::kde::KWallet(QLatin1String(service), QLatin1String(path), + QDBusConnection::sessionBus(), priv); const QDBusPendingReply reply = priv->iface->networkWallet(); - QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher( reply, priv ); - priv->connect( watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), priv, SLOT(kwalletWalletFound(QDBusPendingCallWatcher*)) ); - } - else - { + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, priv); + priv->connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher *)), priv, + SLOT(kwalletWalletFound(QDBusPendingCallWatcher *))); + } else { // D-Bus is not reachable so none can tell us something about KWalletd - QDBusError err( QDBusError::NoServer, ReadPasswordJobPrivate::tr("D-Bus is not running") ); - priv->fallbackOnError( err ); + QDBusError err(QDBusError::NoServer, ReadPasswordJobPrivate::tr("D-Bus is not running")); + priv->fallbackOnError(err); } } -void ReadPasswordJobPrivate::scheduledStart() { - switch ( getKeyringBackend() ) { +void ReadPasswordJobPrivate::scheduledStart() +{ + switch (getKeyringBackend()) { case Backend_LibSecretKeyring: { - if ( !LibSecretKeyring::findPassword(key, q->service(), this) ) { - q->emitFinishedWithError( OtherError, tr("Unknown error") ); + if (!LibSecretKeyring::findPassword(key, q->service(), this)) { + q->emitFinishedWithError(OtherError, tr("Unknown error")); } } break; case Backend_GnomeKeyring: this->mode = JobPrivate::Text; - if ( !GnomeKeyring::find_network_password( key.toUtf8().constData(), - q->service().toUtf8().constData(), - "plaintext", - reinterpret_cast( &JobPrivate::gnomeKeyring_readCb ), - this, nullptr ) ) - q->emitFinishedWithError( OtherError, tr("Unknown error") ); + if (!GnomeKeyring::find_network_password( + key.toUtf8().constData(), q->service().toUtf8().constData(), "plaintext", + reinterpret_cast( + &JobPrivate::gnomeKeyring_readCb), + this, nullptr)) + q->emitFinishedWithError(OtherError, tr("Unknown error")); break; case Backend_Kwallet4: @@ -244,45 +245,45 @@ void JobPrivate::kwalletWalletFound(QDBusPendingCallWatcher *watcher) // This allows to wait for user to unlock wallet, e.g. at Plasma startup iface->setTimeout(0x7FFFFFFF); - const QDBusPendingReply pendingReply = iface->open( reply.value(), 0, q->service() ); - QDBusPendingCallWatcher* pendingWatcher = new QDBusPendingCallWatcher( pendingReply, this ); - connect( pendingWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), - this, SLOT(kwalletOpenFinished(QDBusPendingCallWatcher*)) ); + const QDBusPendingReply pendingReply = iface->open(reply.value(), 0, q->service()); + QDBusPendingCallWatcher *pendingWatcher = new QDBusPendingCallWatcher(pendingReply, this); + connect(pendingWatcher, SIGNAL(finished(QDBusPendingCallWatcher *)), this, + SLOT(kwalletOpenFinished(QDBusPendingCallWatcher *))); } -static QPair mapGnomeKeyringError( int result ) +static QPair mapGnomeKeyringError(int result) { - Q_ASSERT( result != GnomeKeyring::RESULT_OK ); + Q_ASSERT(result != GnomeKeyring::RESULT_OK); - switch ( result ) { + switch (result) { case GnomeKeyring::RESULT_DENIED: - return qMakePair( AccessDenied, QObject::tr("Access to keychain denied") ); + return qMakePair(AccessDenied, QObject::tr("Access to keychain denied")); case GnomeKeyring::RESULT_NO_KEYRING_DAEMON: - return qMakePair( NoBackendAvailable, QObject::tr("No keyring daemon") ); + return qMakePair(NoBackendAvailable, QObject::tr("No keyring daemon")); case GnomeKeyring::RESULT_ALREADY_UNLOCKED: - return qMakePair( OtherError, QObject::tr("Already unlocked") ); + return qMakePair(OtherError, QObject::tr("Already unlocked")); case GnomeKeyring::RESULT_NO_SUCH_KEYRING: - return qMakePair( OtherError, QObject::tr("No such keyring") ); + return qMakePair(OtherError, QObject::tr("No such keyring")); case GnomeKeyring::RESULT_BAD_ARGUMENTS: - return qMakePair( OtherError, QObject::tr("Bad arguments") ); + return qMakePair(OtherError, QObject::tr("Bad arguments")); case GnomeKeyring::RESULT_IO_ERROR: - return qMakePair( OtherError, QObject::tr("I/O error") ); + return qMakePair(OtherError, QObject::tr("I/O error")); case GnomeKeyring::RESULT_CANCELLED: - return qMakePair( OtherError, QObject::tr("Cancelled") ); + return qMakePair(OtherError, QObject::tr("Cancelled")); case GnomeKeyring::RESULT_KEYRING_ALREADY_EXISTS: - return qMakePair( OtherError, QObject::tr("Keyring already exists") ); + return qMakePair(OtherError, QObject::tr("Keyring already exists")); case GnomeKeyring::RESULT_NO_MATCH: - return qMakePair( EntryNotFound, QObject::tr("No match") ); + return qMakePair(EntryNotFound, QObject::tr("No match")); default: break; } - return qMakePair( OtherError, QObject::tr("Unknown error") ); + return qMakePair(OtherError, QObject::tr("Unknown error")); } -void JobPrivate::gnomeKeyring_readCb( int result, const char* string, JobPrivate* self ) +void JobPrivate::gnomeKeyring_readCb(int result, const char *string, JobPrivate *self) { - if ( result == GnomeKeyring::RESULT_OK ) { + if (result == GnomeKeyring::RESULT_OK) { if (self->mode == JobPrivate::Text) self->data = QByteArray(string); else @@ -291,70 +292,74 @@ void JobPrivate::gnomeKeyring_readCb( int result, const char* string, JobPrivate self->q->emitFinished(); } else if (self->mode == JobPrivate::Text) { self->mode = JobPrivate::Binary; - if ( !GnomeKeyring::find_network_password( self->key.toUtf8().constData(), - self->q->service().toUtf8().constData(), - "base64", - reinterpret_cast( &JobPrivate::gnomeKeyring_readCb ), - self, nullptr ) ) - self->q->emitFinishedWithError( OtherError, tr("Unknown error") ); + if (!GnomeKeyring::find_network_password( + self->key.toUtf8().constData(), self->q->service().toUtf8().constData(), + "base64", + reinterpret_cast( + &JobPrivate::gnomeKeyring_readCb), + self, nullptr)) + self->q->emitFinishedWithError(OtherError, tr("Unknown error")); } else { - const QPair errorResult = mapGnomeKeyringError( result ); - self->q->emitFinishedWithError( errorResult.first, errorResult.second ); + const QPair errorResult = mapGnomeKeyringError(result); + self->q->emitFinishedWithError(errorResult.first, errorResult.second); } } -void ReadPasswordJobPrivate::fallbackOnError(const QDBusError& err ) +void ReadPasswordJobPrivate::fallbackOnError(const QDBusError &err) { - PlainTextStore plainTextStore( q->service(), q->settings() ); + PlainTextStore plainTextStore(q->service(), q->settings()); - if ( q->insecureFallback() && plainTextStore.contains( key ) ) { - mode = plainTextStore.readMode( key ); - data = plainTextStore.readData( key ); + if (q->insecureFallback() && plainTextStore.contains(key)) { + mode = plainTextStore.readMode(key); + data = plainTextStore.readData(key); - if ( plainTextStore.error() != NoError ) - q->emitFinishedWithError( plainTextStore.error(), plainTextStore.errorString() ); + if (plainTextStore.error() != NoError) + q->emitFinishedWithError(plainTextStore.error(), plainTextStore.errorString()); else q->emitFinished(); } else { - if ( err.type() == QDBusError::ServiceUnknown ) //KWalletd not running - q->emitFinishedWithError( NoBackendAvailable, tr("No keychain service available") ); + if (err.type() == QDBusError::ServiceUnknown) // KWalletd not running + q->emitFinishedWithError(NoBackendAvailable, tr("No keychain service available")); else - q->emitFinishedWithError( OtherError, tr("Could not open wallet: %1; %2").arg( QDBusError::errorString( err.type() ), err.message() ) ); + q->emitFinishedWithError( + OtherError, + tr("Could not open wallet: %1; %2") + .arg(QDBusError::errorString(err.type()), err.message())); } } -void ReadPasswordJobPrivate::kwalletOpenFinished( QDBusPendingCallWatcher* watcher ) { +void ReadPasswordJobPrivate::kwalletOpenFinished(QDBusPendingCallWatcher *watcher) +{ watcher->deleteLater(); const QDBusPendingReply reply = *watcher; - if ( reply.isError() ) { - fallbackOnError( reply.error() ); + if (reply.isError()) { + fallbackOnError(reply.error()); return; } - PlainTextStore plainTextStore( q->service(), q->settings() ); + PlainTextStore plainTextStore(q->service(), q->settings()); - if ( plainTextStore.contains( key ) ) { + if (plainTextStore.contains(key)) { // We previously stored data in the insecure QSettings, but now have KWallet available. // Do the migration - data = plainTextStore.readData( key ); - const WritePasswordJobPrivate::Mode mode = plainTextStore.readMode( key ); - plainTextStore.remove( key ); + data = plainTextStore.readData(key); + const WritePasswordJobPrivate::Mode mode = plainTextStore.readMode(key); + plainTextStore.remove(key); q->emitFinished(); - - WritePasswordJob* j = new WritePasswordJob( q->service(), nullptr ); - j->setSettings( q->settings() ); - j->setKey( key ); - j->setAutoDelete( true ); - if ( mode == WritePasswordJobPrivate::Binary ) - j->setBinaryData( data ); - else if ( mode == WritePasswordJobPrivate::Text ) - j->setTextData( QString::fromUtf8( data ) ); + WritePasswordJob *j = new WritePasswordJob(q->service(), nullptr); + j->setSettings(q->settings()); + j->setKey(key); + j->setAutoDelete(true); + if (mode == WritePasswordJobPrivate::Binary) + j->setBinaryData(data); + else if (mode == WritePasswordJobPrivate::Text) + j->setTextData(QString::fromUtf8(data)); else - Q_ASSERT( false ); + Q_ASSERT(false); j->start(); @@ -363,38 +368,38 @@ void ReadPasswordJobPrivate::kwalletOpenFinished( QDBusPendingCallWatcher* watch walletHandle = reply.value(); - if ( walletHandle < 0 ) { - q->emitFinishedWithError( AccessDenied, tr("Access to keychain denied") ); + if (walletHandle < 0) { + q->emitFinishedWithError(AccessDenied, tr("Access to keychain denied")); return; } - const QDBusPendingReply nextReply = iface->entryType( walletHandle, q->service(), key, q->service() ); - QDBusPendingCallWatcher* nextWatcher = new QDBusPendingCallWatcher( nextReply, this ); - connect( nextWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletEntryTypeFinished(QDBusPendingCallWatcher*)) ); + const QDBusPendingReply nextReply = + iface->entryType(walletHandle, q->service(), key, q->service()); + QDBusPendingCallWatcher *nextWatcher = new QDBusPendingCallWatcher(nextReply, this); + connect(nextWatcher, SIGNAL(finished(QDBusPendingCallWatcher *)), this, + SLOT(kwalletEntryTypeFinished(QDBusPendingCallWatcher *))); } -//Must be in sync with KWallet::EntryType (kwallet.h) -enum KWalletEntryType { - Unknown=0, - Password, - Stream, - Map -}; +// Must be in sync with KWallet::EntryType (kwallet.h) +enum KWalletEntryType { Unknown = 0, Password, Stream, Map }; -void ReadPasswordJobPrivate::kwalletEntryTypeFinished( QDBusPendingCallWatcher* watcher ) { +void ReadPasswordJobPrivate::kwalletEntryTypeFinished(QDBusPendingCallWatcher *watcher) +{ watcher->deleteLater(); - if ( watcher->isError() ) { + if (watcher->isError()) { const QDBusError err = watcher->error(); - q->emitFinishedWithError( OtherError, tr("Could not determine data type: %1; %2").arg( QDBusError::errorString( err.type() ), err.message() ) ); + q->emitFinishedWithError(OtherError, + tr("Could not determine data type: %1; %2") + .arg(QDBusError::errorString(err.type()), err.message())); return; } const QDBusPendingReply reply = *watcher; const int value = reply.value(); - switch ( value ) { + switch (value) { case Unknown: - q->emitFinishedWithError( EntryNotFound, tr("Entry not found") ); + q->emitFinishedWithError(EntryNotFound, tr("Entry not found")); return; case Password: mode = Text; @@ -403,23 +408,25 @@ void ReadPasswordJobPrivate::kwalletEntryTypeFinished( QDBusPendingCallWatcher* mode = Binary; break; case Map: - q->emitFinishedWithError( EntryNotFound, tr("Unsupported entry type 'Map'") ); + q->emitFinishedWithError(EntryNotFound, tr("Unsupported entry type 'Map'")); return; default: - q->emitFinishedWithError( OtherError, tr("Unknown kwallet entry type '%1'").arg( value ) ); + q->emitFinishedWithError(OtherError, tr("Unknown kwallet entry type '%1'").arg(value)); return; } const QDBusPendingCall nextReply = (mode == Text) - ? QDBusPendingCall( iface->readPassword( walletHandle, q->service(), key, q->service() ) ) - : QDBusPendingCall( iface->readEntry( walletHandle, q->service(), key, q->service() ) ); - QDBusPendingCallWatcher* nextWatcher = new QDBusPendingCallWatcher( nextReply, this ); - connect( nextWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletFinished(QDBusPendingCallWatcher*)) ); + ? QDBusPendingCall(iface->readPassword(walletHandle, q->service(), key, q->service())) + : QDBusPendingCall(iface->readEntry(walletHandle, q->service(), key, q->service())); + QDBusPendingCallWatcher *nextWatcher = new QDBusPendingCallWatcher(nextReply, this); + connect(nextWatcher, SIGNAL(finished(QDBusPendingCallWatcher *)), this, + SLOT(kwalletFinished(QDBusPendingCallWatcher *))); } -void ReadPasswordJobPrivate::kwalletFinished( QDBusPendingCallWatcher* watcher ) { - if ( !watcher->isError() ) { - if ( mode == Binary ) { +void ReadPasswordJobPrivate::kwalletFinished(QDBusPendingCallWatcher *watcher) +{ + if (!watcher->isError()) { + if (mode == Binary) { QDBusPendingReply reply = *watcher; if (reply.isValid()) { data = reply.value(); @@ -435,35 +442,36 @@ void ReadPasswordJobPrivate::kwalletFinished( QDBusPendingCallWatcher* watcher ) JobPrivate::kwalletFinished(watcher); } -static void kwalletWritePasswordScheduledStart( const char * service, const char * path, JobPrivate * priv ) { - if ( QDBusConnection::sessionBus().isConnected() ) - { - priv->iface = new org::kde::KWallet( QLatin1String(service), QLatin1String(path), QDBusConnection::sessionBus(), priv ); +static void kwalletWritePasswordScheduledStart(const char *service, const char *path, + JobPrivate *priv) +{ + if (QDBusConnection::sessionBus().isConnected()) { + priv->iface = new org::kde::KWallet(QLatin1String(service), QLatin1String(path), + QDBusConnection::sessionBus(), priv); const QDBusPendingReply reply = priv->iface->networkWallet(); - QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher( reply, priv ); - priv->connect( watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), priv, SLOT(kwalletWalletFound(QDBusPendingCallWatcher*)) ); - } - else - { + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, priv); + priv->connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher *)), priv, + SLOT(kwalletWalletFound(QDBusPendingCallWatcher *))); + } else { // D-Bus is not reachable so none can tell us something about KWalletd - QDBusError err( QDBusError::NoServer, WritePasswordJobPrivate::tr("D-Bus is not running") ); - priv->fallbackOnError( err ); + QDBusError err(QDBusError::NoServer, WritePasswordJobPrivate::tr("D-Bus is not running")); + priv->fallbackOnError(err); } } -void WritePasswordJobPrivate::scheduledStart() { - switch ( getKeyringBackend() ) { +void WritePasswordJobPrivate::scheduledStart() +{ + switch (getKeyringBackend()) { case Backend_LibSecretKeyring: { - if ( !LibSecretKeyring::writePassword(service, key, service, mode, - data, this) ) { - q->emitFinishedWithError( OtherError, tr("Unknown error") ); + if (!LibSecretKeyring::writePassword(service, key, service, mode, data, this)) { + q->emitFinishedWithError(OtherError, tr("Unknown error")); } } break; case Backend_GnomeKeyring: { QString type; QByteArray password; - switch(mode) { + switch (mode) { case JobPrivate::Text: type = QLatin1String("plaintext"); password = data; @@ -475,17 +483,15 @@ void WritePasswordJobPrivate::scheduledStart() { } QByteArray service = q->service().toUtf8(); - if ( !GnomeKeyring::store_network_password( GnomeKeyring::GNOME_KEYRING_DEFAULT, - service.constData(), - key.toUtf8().constData(), - service.constData(), - type.toUtf8().constData(), - password.constData(), - reinterpret_cast( &JobPrivate::gnomeKeyring_writeCb ), - this, nullptr ) ) - q->emitFinishedWithError( OtherError, tr("Unknown error") ); - } - break; + if (!GnomeKeyring::store_network_password( + GnomeKeyring::GNOME_KEYRING_DEFAULT, service.constData(), + key.toUtf8().constData(), service.constData(), type.toUtf8().constData(), + password.constData(), + reinterpret_cast( + &JobPrivate::gnomeKeyring_writeCb), + this, nullptr)) + q->emitFinishedWithError(OtherError, tr("Unknown error")); + } break; case Backend_Kwallet4: kwalletWritePasswordScheduledStart(KWALLET4_DBUS_IFACE, KWALLET4_DBUS_PATH, this); @@ -501,72 +507,79 @@ void WritePasswordJobPrivate::scheduledStart() { void WritePasswordJobPrivate::fallbackOnError(const QDBusError &err) { - if ( !q->insecureFallback() ) { - q->emitFinishedWithError( OtherError, tr("Could not open wallet: %1; %2").arg( QDBusError::errorString( err.type() ), err.message() ) ); + if (!q->insecureFallback()) { + q->emitFinishedWithError(OtherError, + tr("Could not open wallet: %1; %2") + .arg(QDBusError::errorString(err.type()), err.message())); return; } - PlainTextStore plainTextStore( q->service(), q->settings() ); - plainTextStore.write( key, data, mode ); + PlainTextStore plainTextStore(q->service(), q->settings()); + plainTextStore.write(key, data, mode); - if ( plainTextStore.error() != NoError ) - q->emitFinishedWithError( plainTextStore.error(), plainTextStore.errorString() ); + if (plainTextStore.error() != NoError) + q->emitFinishedWithError(plainTextStore.error(), plainTextStore.errorString()); else q->emitFinished(); } -void JobPrivate::gnomeKeyring_writeCb(int result, JobPrivate* self ) +void JobPrivate::gnomeKeyring_writeCb(int result, JobPrivate *self) { - if ( result == GnomeKeyring::RESULT_OK ) { + if (result == GnomeKeyring::RESULT_OK) { self->q->emitFinished(); } else { - const QPair errorResult = mapGnomeKeyringError( result ); - self->q->emitFinishedWithError( errorResult.first, errorResult.second ); + const QPair errorResult = mapGnomeKeyringError(result); + self->q->emitFinishedWithError(errorResult.first, errorResult.second); } } -void JobPrivate::kwalletOpenFinished( QDBusPendingCallWatcher* watcher ) { +void JobPrivate::kwalletOpenFinished(QDBusPendingCallWatcher *watcher) +{ watcher->deleteLater(); QDBusPendingReply reply = *watcher; - if ( reply.isError() ) { - fallbackOnError( reply.error() ); + if (reply.isError()) { + fallbackOnError(reply.error()); return; } - PlainTextStore plainTextStore( q->service(), q->settings() ); - if ( plainTextStore.contains( key ) ) { - // If we had previously written to QSettings, but we now have a kwallet available, migrate and delete old insecure data - plainTextStore.remove( key ); + PlainTextStore plainTextStore(q->service(), q->settings()); + if (plainTextStore.contains(key)) { + // If we had previously written to QSettings, but we now have a kwallet available, migrate + // and delete old insecure data + plainTextStore.remove(key); } const int handle = reply.value(); - if ( handle < 0 ) { - q->emitFinishedWithError( AccessDenied, tr("Access to keychain denied") ); + if (handle < 0) { + q->emitFinishedWithError(AccessDenied, tr("Access to keychain denied")); return; } QDBusPendingReply nextReply; - if ( !data.isNull() ) { - if ( mode == Text ) { - nextReply = iface->writePassword( handle, q->service(), key, QString::fromUtf8(data), q->service() ); + if (!data.isNull()) { + if (mode == Text) { + nextReply = iface->writePassword(handle, q->service(), key, QString::fromUtf8(data), + q->service()); } else { - Q_ASSERT( mode == Binary ); - nextReply = iface->writeEntry( handle, q->service(), key, data, q->service() ); + Q_ASSERT(mode == Binary); + nextReply = iface->writeEntry(handle, q->service(), key, data, q->service()); } } else { - nextReply = iface->removeEntry( handle, q->service(), key, q->service() ); + nextReply = iface->removeEntry(handle, q->service(), key, q->service()); } - QDBusPendingCallWatcher* nextWatcher = new QDBusPendingCallWatcher( nextReply, this ); - connect( nextWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletFinished(QDBusPendingCallWatcher*)) ); + QDBusPendingCallWatcher *nextWatcher = new QDBusPendingCallWatcher(nextReply, this); + connect(nextWatcher, SIGNAL(finished(QDBusPendingCallWatcher *)), this, + SLOT(kwalletFinished(QDBusPendingCallWatcher *))); } -void JobPrivate::kwalletFinished( QDBusPendingCallWatcher* watcher ) { - if ( !watcher->isError() ) { - if ( mode == Binary ) { +void JobPrivate::kwalletFinished(QDBusPendingCallWatcher *watcher) +{ + if (!watcher->isError()) { + if (mode == Binary) { QDBusPendingReply reply = *watcher; if (reply.isValid()) { data = reply.value(); @@ -582,21 +595,22 @@ void JobPrivate::kwalletFinished( QDBusPendingCallWatcher* watcher ) { q->emitFinished(); } -void DeletePasswordJobPrivate::scheduledStart() { - switch ( getKeyringBackend() ) { +void DeletePasswordJobPrivate::scheduledStart() +{ + switch (getKeyringBackend()) { case Backend_LibSecretKeyring: { - if ( !LibSecretKeyring::deletePassword(key, q->service(), this) ) { - q->emitFinishedWithError( OtherError, tr("Unknown error") ); + if (!LibSecretKeyring::deletePassword(key, q->service(), this)) { + q->emitFinishedWithError(OtherError, tr("Unknown error")); } } break; case Backend_GnomeKeyring: { - if ( !GnomeKeyring::delete_network_password( - key.toUtf8().constData(), q->service().toUtf8().constData(), - reinterpret_cast( &JobPrivate::gnomeKeyring_writeCb ), - this, nullptr ) ) - q->emitFinishedWithError( OtherError, tr("Unknown error") ); - } - break; + if (!GnomeKeyring::delete_network_password( + key.toUtf8().constData(), q->service().toUtf8().constData(), + reinterpret_cast( + &JobPrivate::gnomeKeyring_writeCb), + this, nullptr)) + q->emitFinishedWithError(OtherError, tr("Unknown error")); + } break; case Backend_Kwallet4: kwalletWritePasswordScheduledStart(KWALLET4_DBUS_IFACE, KWALLET4_DBUS_PATH, this); @@ -610,28 +624,29 @@ void DeletePasswordJobPrivate::scheduledStart() { } } -void DeletePasswordJobPrivate::fallbackOnError(const QDBusError &err) { - QScopedPointer local( !q->settings() ? new QSettings( q->service() ) : nullptr ); - QSettings* actual = q->settings() ? q->settings() : local.data(); +void DeletePasswordJobPrivate::fallbackOnError(const QDBusError &err) +{ + QScopedPointer local(!q->settings() ? new QSettings(q->service()) : nullptr); + QSettings *actual = q->settings() ? q->settings() : local.data(); - if ( !q->insecureFallback() ) { - q->emitFinishedWithError( OtherError, tr("Could not open wallet: %1; %2") - .arg( QDBusError::errorString( err.type() ), err.message() ) ); + if (!q->insecureFallback()) { + q->emitFinishedWithError(OtherError, + tr("Could not open wallet: %1; %2") + .arg(QDBusError::errorString(err.type()), err.message())); return; } - actual->remove( key ); + actual->remove(key); actual->sync(); q->emitFinished(); - q->emitFinished(); } bool QKeychain::isAvailable() { return LibSecretKeyring::isAvailable() || GnomeKeyring::isAvailable() - || isKwalletAvailable(KWALLET6_DBUS_IFACE, KWALLET6_DBUS_PATH) - || isKwalletAvailable(KWALLET5_DBUS_IFACE, KWALLET5_DBUS_PATH); + || isKwalletAvailable(KWALLET6_DBUS_IFACE, KWALLET6_DBUS_PATH) + || isKwalletAvailable(KWALLET5_DBUS_IFACE, KWALLET5_DBUS_PATH); } diff --git a/qtkeychain/keychain_win.cpp b/qtkeychain/keychain_win.cpp index 673cb755..22b73299 100644 --- a/qtkeychain/keychain_win.cpp +++ b/qtkeychain/keychain_win.cpp @@ -19,86 +19,81 @@ using namespace QKeychain; namespace { - const std::wstring PRODUCT_NAME = L"QtKeychain"; - const std::wstring ENCRYPTED_DATA_KEY = L"QtKeychain-encrypted data"; - const std::wstring ATTRIBUTE_KEY = L"QtKeychain Attrib"; +const std::wstring PRODUCT_NAME = L"QtKeychain"; +const std::wstring ENCRYPTED_DATA_KEY = L"QtKeychain-encrypted data"; +const std::wstring ATTRIBUTE_KEY = L"QtKeychain Attrib"; - constexpr quint64 MAX_ATTRIBUTE_SIZE = 256; - constexpr quint64 MAX_ATTRIBUTE_COUNT = 64; - constexpr qsizetype MAX_BLOB_SIZE = CRED_MAX_CREDENTIAL_BLOB_SIZE + MAX_ATTRIBUTE_SIZE * MAX_ATTRIBUTE_COUNT; +constexpr quint64 MAX_ATTRIBUTE_SIZE = 256; +constexpr quint64 MAX_ATTRIBUTE_COUNT = 64; +constexpr qsizetype MAX_BLOB_SIZE = + CRED_MAX_CREDENTIAL_BLOB_SIZE + MAX_ATTRIBUTE_SIZE * MAX_ATTRIBUTE_COUNT; - QString formatWinError(ulong errorCode) - { - return QStringLiteral("WindowsError: %1: %2").arg(QString::number(errorCode, 16), QString::fromWCharArray(_com_error(errorCode).ErrorMessage())); - } +QString formatWinError(ulong errorCode) +{ + return QStringLiteral("WindowsError: %1: %2") + .arg(QString::number(errorCode, 16), + QString::fromWCharArray(_com_error(errorCode).ErrorMessage())); +} - // decrpyted data, error - std::pair unprotectData(const QByteArray &encrypted) - { - DATA_BLOB blob_in, blob_out; - - blob_in.pbData = const_cast(reinterpret_cast(encrypted.data())); - blob_in.cbData = encrypted.size(); - - if ( !CryptUnprotectData( &blob_in, - nullptr, - nullptr, - nullptr, - nullptr, - 0, - &blob_out ) ) { - return {{}, formatWinError(GetLastError())}; - } +// decrpyted data, error +std::pair unprotectData(const QByteArray &encrypted) +{ + DATA_BLOB blob_in, blob_out; + + blob_in.pbData = + const_cast(reinterpret_cast(encrypted.data())); + blob_in.cbData = encrypted.size(); - QByteArray decrypted( reinterpret_cast( blob_out.pbData ), blob_out.cbData ); - SecureZeroMemory( blob_out.pbData, blob_out.cbData ); - LocalFree( blob_out.pbData ); - return {decrypted, {}}; + if (!CryptUnprotectData(&blob_in, nullptr, nullptr, nullptr, nullptr, 0, &blob_out)) { + return { {}, formatWinError(GetLastError()) }; } - // encrypted data, error - std::pair protectData(const QByteArray &data) - { - DATA_BLOB blob_in, blob_out; - blob_in.pbData = const_cast(reinterpret_cast(data.data())); - blob_in.cbData = data.size(); - if(!CryptProtectData( &blob_in, - ENCRYPTED_DATA_KEY.data(), - nullptr, - nullptr, - nullptr, - 0, - &blob_out )) { - - return {{}, formatWinError(GetLastError())}; - } + QByteArray decrypted(reinterpret_cast(blob_out.pbData), blob_out.cbData); + SecureZeroMemory(blob_out.pbData, blob_out.cbData); + LocalFree(blob_out.pbData); + return { decrypted, {} }; +} - QByteArray encrypted( reinterpret_cast( blob_out.pbData ), blob_out.cbData ); - LocalFree( blob_out.pbData ); - return {encrypted, {}}; +// encrypted data, error +std::pair protectData(const QByteArray &data) +{ + DATA_BLOB blob_in, blob_out; + blob_in.pbData = + const_cast(reinterpret_cast(data.data())); + blob_in.cbData = data.size(); + if (!CryptProtectData(&blob_in, ENCRYPTED_DATA_KEY.data(), nullptr, nullptr, nullptr, 0, + &blob_out)) { + + return { {}, formatWinError(GetLastError()) }; } - + QByteArray encrypted(reinterpret_cast(blob_out.pbData), blob_out.cbData); + LocalFree(blob_out.pbData); + return { encrypted, {} }; } +} // namespace + #if defined(USE_CREDENTIAL_STORE) /*** * The credentials store has a limit of CRED_MAX_CREDENTIAL_BLOB_SIZE (5* 512) - * As this might not be enough in some scenarios, for bigger payloads we use CryptProtectData which offers similar protection as CredWrite - * in combination with CredWrite. - * We distribute the protected payload to the PCREDENTIALW->CredentialBlob as well as PCREDENTIALW->AttributeCount. - * This increases the max payload size to CRED_MAX_CREDENTIAL_BLOB_SIZE + 64 * 256 = 18944. - * As the protected data requires more space than the original payload, the effective max payload is smaller than that. - * As we continue to use PCREDENTIALW as storage medium, the credentials are still roaming. -*/ -void ReadPasswordJobPrivate::scheduledStart() { + * As this might not be enough in some scenarios, for bigger payloads we use CryptProtectData which + * offers similar protection as CredWrite in combination with CredWrite. We distribute the protected + * payload to the PCREDENTIALW->CredentialBlob as well as PCREDENTIALW->AttributeCount. This + * increases the max payload size to CRED_MAX_CREDENTIAL_BLOB_SIZE + 64 * 256 = 18944. As the + * protected data requires more space than the original payload, the effective max payload is + * smaller than that. As we continue to use PCREDENTIALW as storage medium, the credentials are + * still roaming. + */ +void ReadPasswordJobPrivate::scheduledStart() +{ PCREDENTIALW cred = {}; - if (!CredReadW(reinterpret_cast(key.utf16()), CRED_TYPE_GENERIC, 0, &cred)) { + if (!CredReadW(reinterpret_cast(key.utf16()), CRED_TYPE_GENERIC, 0, &cred)) { Error err; QString msg; - switch(GetLastError()) { + switch (GetLastError()) { case ERROR_NOT_FOUND: err = EntryNotFound; msg = tr("Password entry not found"); @@ -109,27 +104,25 @@ void ReadPasswordJobPrivate::scheduledStart() { break; } - q->emitFinishedWithError( err, msg ); + q->emitFinishedWithError(err, msg); return; } - if(cred->AttributeCount == 0) - { - data = QByteArray(reinterpret_cast(cred->CredentialBlob), cred->CredentialBlobSize); - } - else - { + if (cred->AttributeCount == 0) { + data = QByteArray(reinterpret_cast(cred->CredentialBlob), cred->CredentialBlobSize); + } else { QByteArray encrypted; - encrypted.reserve(CRED_MAX_CREDENTIAL_BLOB_SIZE + cred->AttributeCount * MAX_ATTRIBUTE_SIZE); - encrypted.append(reinterpret_cast(cred->CredentialBlob), cred->CredentialBlobSize); - for (ulong i = 0; i < cred->AttributeCount; ++i) - { - encrypted.append(reinterpret_cast(cred->Attributes[i].Value), cred->Attributes[i].ValueSize); + encrypted.reserve(CRED_MAX_CREDENTIAL_BLOB_SIZE + + cred->AttributeCount * MAX_ATTRIBUTE_SIZE); + encrypted.append(reinterpret_cast(cred->CredentialBlob), cred->CredentialBlobSize); + for (ulong i = 0; i < cred->AttributeCount; ++i) { + encrypted.append(reinterpret_cast(cred->Attributes[i].Value), + cred->Attributes[i].ValueSize); } const auto result = unprotectData(encrypted); - if (!result.second.isEmpty()) - { - q->emitFinishedWithError( OtherError, tr("Could not decrypt data: %1").arg(result.second) ); + if (!result.second.isEmpty()) { + q->emitFinishedWithError(OtherError, + tr("Could not decrypt data: %1").arg(result.second)); return; } data = result.first; @@ -140,38 +133,36 @@ void ReadPasswordJobPrivate::scheduledStart() { q->emitFinished(); } -void WritePasswordJobPrivate::scheduledStart() { +void WritePasswordJobPrivate::scheduledStart() +{ CREDENTIALW cred = {}; - cred.Comment = const_cast(PRODUCT_NAME.data()); + cred.Comment = const_cast(PRODUCT_NAME.data()); cred.Type = CRED_TYPE_GENERIC; - cred.TargetName = const_cast(reinterpret_cast(key.utf16())); + cred.TargetName = const_cast(reinterpret_cast(key.utf16())); cred.Persist = CRED_PERSIST_ENTERPRISE; QByteArray buffer; std::vector attributes; - if (data.size() < CRED_MAX_CREDENTIAL_BLOB_SIZE) - { - cred.CredentialBlob = reinterpret_cast(data.data()); + if (data.size() < CRED_MAX_CREDENTIAL_BLOB_SIZE) { + cred.CredentialBlob = reinterpret_cast(data.data()); cred.CredentialBlobSize = data.size(); - } - else - { + } else { // data is too big for CredentialBlob - // we encrpyt it instead with CryptProtectData which also encrpyt the data with the users credentials - // The data is also protected with the roaming profile + // we encrpyt it instead with CryptProtectData which also encrpyt the data with the users + // credentials The data is also protected with the roaming profile { auto result = protectData(data); - if(!result.second.isEmpty()) - { - q->emitFinishedWithError( OtherError, tr("Encryption failed: %1").arg(result.second)); + if (!result.second.isEmpty()) { + q->emitFinishedWithError(OtherError, + tr("Encryption failed: %1").arg(result.second)); return; } - if (result.first.size() > MAX_BLOB_SIZE) - { - q->emitFinishedWithError( - OtherError, - tr("Credential size exceeds maximum size of %1: %2").arg(QString::number(MAX_BLOB_SIZE), QString::number(result.first.size()))); + if (result.first.size() > MAX_BLOB_SIZE) { + q->emitFinishedWithError(OtherError, + tr("Credential size exceeds maximum size of %1: %2") + .arg(QString::number(MAX_BLOB_SIZE), + QString::number(result.first.size()))); return; } // the data must be valid outside of the scope of result @@ -179,19 +170,21 @@ void WritePasswordJobPrivate::scheduledStart() { } quint64 pos = 0; - auto read = [&buffer, &pos](const quint64 size, auto &dest, auto &sizeOut) - { - dest = reinterpret_cast::type>(buffer.data()) + pos; + auto read = [&buffer, &pos](const quint64 size, auto &dest, auto &sizeOut) { + dest = reinterpret_cast::type>( + buffer.data()) + + pos; sizeOut = std::min(size, buffer.size() - pos); pos += sizeOut; }; read(CRED_MAX_CREDENTIAL_BLOB_SIZE, cred.CredentialBlob, cred.CredentialBlobSize); - cred.AttributeCount = std::ceil((buffer.size() - pos) / static_cast(MAX_ATTRIBUTE_SIZE)); + cred.AttributeCount = + std::ceil((buffer.size() - pos) / static_cast(MAX_ATTRIBUTE_SIZE)); attributes.resize(cred.AttributeCount, {}); cred.Attributes = attributes.data(); - for(ulong i = 0; i < cred.AttributeCount; ++i) { - attributes[i].Keyword = const_cast(ATTRIBUTE_KEY.data()); + for (ulong i = 0; i < cred.AttributeCount; ++i) { + attributes[i].Keyword = const_cast(ATTRIBUTE_KEY.data()); read(MAX_ATTRIBUTE_SIZE, attributes[i].Value, attributes[i].ValueSize); } } @@ -209,20 +202,21 @@ void WritePasswordJobPrivate::scheduledStart() { const QString::size_type maxTargetName = CRED_MAX_GENERIC_TARGET_NAME_LENGTH; if (key.size() > maxTargetName) { q->emitFinishedWithError( - OtherError, - tr("Credential key exceeds maximum size of %1").arg(maxTargetName)); + OtherError, tr("Credential key exceeds maximum size of %1").arg(maxTargetName)); return; } } - q->emitFinishedWithError( OtherError, tr("Writing credentials failed: %1").arg(formatWinError(err))); + q->emitFinishedWithError(OtherError, + tr("Writing credentials failed: %1").arg(formatWinError(err))); } -void DeletePasswordJobPrivate::scheduledStart() { - if (!CredDeleteW(reinterpret_cast(key.utf16()), CRED_TYPE_GENERIC, 0)) { +void DeletePasswordJobPrivate::scheduledStart() +{ + if (!CredDeleteW(reinterpret_cast(key.utf16()), CRED_TYPE_GENERIC, 0)) { Error err; QString msg; - switch(GetLastError()) { + switch (GetLastError()) { case ERROR_NOT_FOUND: err = EntryNotFound; msg = tr("Password entry not found"); @@ -233,53 +227,54 @@ void DeletePasswordJobPrivate::scheduledStart() { break; } - q->emitFinishedWithError( err, msg ); + q->emitFinishedWithError(err, msg); } else { q->emitFinished(); } } #else -void ReadPasswordJobPrivate::scheduledStart() { - PlainTextStore plainTextStore( q->service(), q->settings() ); - QByteArray encrypted = plainTextStore.readData( key ); - if ( plainTextStore.error() != NoError ) { - q->emitFinishedWithError( plainTextStore.error(), plainTextStore.errorString() ); +void ReadPasswordJobPrivate::scheduledStart() +{ + PlainTextStore plainTextStore(q->service(), q->settings()); + QByteArray encrypted = plainTextStore.readData(key); + if (plainTextStore.error() != NoError) { + q->emitFinishedWithError(plainTextStore.error(), plainTextStore.errorString()); return; } const auto result = unprotectData(encrypted); - if (!result.second.isEmpty()) - { - q->emitFinishedWithError( OtherError, tr("Could not decrypt data: %1").arg(result.second) ); + if (!result.second.isEmpty()) { + q->emitFinishedWithError(OtherError, tr("Could not decrypt data: %1").arg(result.second)); return; } data = result.first; q->emitFinished(); } -void WritePasswordJobPrivate::scheduledStart() { +void WritePasswordJobPrivate::scheduledStart() +{ const auto result = protectData(data); - if(!result.second.isEmpty()) - { - q->emitFinishedWithError( OtherError, tr("Encryption failed: %1").arg(result.second)); + if (!result.second.isEmpty()) { + q->emitFinishedWithError(OtherError, tr("Encryption failed: %1").arg(result.second)); return; } - PlainTextStore plainTextStore( q->service(), q->settings() ); - plainTextStore.write( key, result.first, Binary ); - if ( plainTextStore.error() != NoError ) { - q->emitFinishedWithError( plainTextStore.error(), plainTextStore.errorString() ); + PlainTextStore plainTextStore(q->service(), q->settings()); + plainTextStore.write(key, result.first, Binary); + if (plainTextStore.error() != NoError) { + q->emitFinishedWithError(plainTextStore.error(), plainTextStore.errorString()); return; } q->emitFinished(); } -void DeletePasswordJobPrivate::scheduledStart() { - PlainTextStore plainTextStore( q->service(), q->settings() ); - plainTextStore.remove( key ); - if ( plainTextStore.error() != NoError ) { - q->emitFinishedWithError( plainTextStore.error(), plainTextStore.errorString() ); +void DeletePasswordJobPrivate::scheduledStart() +{ + PlainTextStore plainTextStore(q->service(), q->settings()); + plainTextStore.remove(key); + if (plainTextStore.error() != NoError) { + q->emitFinishedWithError(plainTextStore.error(), plainTextStore.errorString()); } else { q->emitFinished(); } diff --git a/qtkeychain/libsecret.cpp b/qtkeychain/libsecret.cpp index 2289a127..e8006e75 100644 --- a/qtkeychain/libsecret.cpp +++ b/qtkeychain/libsecret.cpp @@ -1,5 +1,5 @@ #if defined(HAVE_LIBSECRET) -#include +# include #endif #include "libsecret_p.h" @@ -7,51 +7,39 @@ #include #if defined(HAVE_LIBSECRET) -const SecretSchema* qtkeychainSchema(void) { - static const SecretSchema schema = { - "org.qt.keychain", SECRET_SCHEMA_DONT_MATCH_NAME, - { - { "user", SECRET_SCHEMA_ATTRIBUTE_STRING }, - { "server", SECRET_SCHEMA_ATTRIBUTE_STRING }, - { "type", SECRET_SCHEMA_ATTRIBUTE_STRING } - } - }; +const SecretSchema *qtkeychainSchema(void) +{ + static const SecretSchema schema = { "org.qt.keychain", + SECRET_SCHEMA_DONT_MATCH_NAME, + { { "user", SECRET_SCHEMA_ATTRIBUTE_STRING }, + { "server", SECRET_SCHEMA_ATTRIBUTE_STRING }, + { "type", SECRET_SCHEMA_ATTRIBUTE_STRING } } }; return &schema; } -typedef struct { +typedef struct +{ QKeychain::JobPrivate *self; QString user; QString server; } callbackArg; -typedef void (*secret_password_lookup_t) (const SecretSchema *schema, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data, - ...) G_GNUC_NULL_TERMINATED; -typedef gchar *(*secret_password_lookup_finish_t) (GAsyncResult *result, - GError **error); -typedef void (*secret_password_store_t) (const SecretSchema *schema, - const gchar *collection, - const gchar *label, - const gchar *password, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data, +typedef void (*secret_password_lookup_t)(const SecretSchema *schema, GCancellable *cancellable, + GAsyncReadyCallback callback, gpointer user_data, ...) G_GNUC_NULL_TERMINATED; -typedef gboolean (*secret_password_store_finish_t) (GAsyncResult *result, - GError **error); -typedef void (*secret_password_clear_t) (const SecretSchema *schema, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data, - ...) G_GNUC_NULL_TERMINATED; -typedef gboolean (*secret_password_clear_finish_t) (GAsyncResult *result, - GError **error); -typedef void (*secret_password_free_t) (gchar *password); -typedef GQuark (*secret_error_get_quark_t) (void) G_GNUC_CONST; +typedef gchar *(*secret_password_lookup_finish_t)(GAsyncResult *result, GError **error); +typedef void (*secret_password_store_t)(const SecretSchema *schema, const gchar *collection, + const gchar *label, const gchar *password, + GCancellable *cancellable, GAsyncReadyCallback callback, + gpointer user_data, ...) G_GNUC_NULL_TERMINATED; +typedef gboolean (*secret_password_store_finish_t)(GAsyncResult *result, GError **error); +typedef void (*secret_password_clear_t)(const SecretSchema *schema, GCancellable *cancellable, + GAsyncReadyCallback callback, gpointer user_data, + ...) G_GNUC_NULL_TERMINATED; +typedef gboolean (*secret_password_clear_finish_t)(GAsyncResult *result, GError **error); +typedef void (*secret_password_free_t)(gchar *password); +typedef GQuark (*secret_error_get_quark_t)(void) G_GNUC_CONST; static secret_password_lookup_t secret_password_lookup_fn = nullptr; static secret_password_lookup_finish_t secret_password_lookup_finish_fn = nullptr; @@ -62,12 +50,13 @@ static secret_password_clear_finish_t secret_password_clear_finish_fn = nullptr; static secret_password_free_t secret_password_free_fn = nullptr; static secret_error_get_quark_t secret_error_get_quark_fn = nullptr; -static QKeychain::Error gerrorToCode(const GError *error) { +static QKeychain::Error gerrorToCode(const GError *error) +{ if (error->domain != secret_error_get_quark_fn()) { return QKeychain::OtherError; } - switch(error->code) { + switch (error->code) { case SECRET_ERROR_NO_SUCH_OBJECT: return QKeychain::EntryNotFound; case SECRET_ERROR_IS_LOCKED: @@ -77,14 +66,11 @@ static QKeychain::Error gerrorToCode(const GError *error) { } } -static void -on_password_lookup (GObject *source, - GAsyncResult *result, - gpointer inst) +static void on_password_lookup(GObject *source, GAsyncResult *result, gpointer inst) { GError *error = nullptr; - callbackArg *arg = (callbackArg*)inst; - gchar *password = secret_password_lookup_finish_fn (result, &error); + callbackArg *arg = (callbackArg *)inst; + gchar *password = secret_password_lookup_finish_fn(result, &error); Q_UNUSED(source); @@ -92,11 +78,11 @@ on_password_lookup (GObject *source, if (error) { QKeychain::Error code = gerrorToCode(error); - arg->self->q->emitFinishedWithError( code, QString::fromUtf8(error->message) ); + arg->self->q->emitFinishedWithError(code, QString::fromUtf8(error->message)); } else { if (password) { QByteArray raw = QByteArray(password); - switch(arg->self->mode) { + switch (arg->self->mode) { case QKeychain::JobPrivate::Binary: arg->self->data = QByteArray::fromBase64(raw); break; @@ -108,24 +94,23 @@ on_password_lookup (GObject *source, arg->self->q->emitFinished(); } else if (arg->self->mode == QKeychain::JobPrivate::Text) { arg->self->mode = QKeychain::JobPrivate::Binary; - secret_password_lookup_fn (qtkeychainSchema(), nullptr, - on_password_lookup, arg, - "user", arg->user.toUtf8().constData(), - "server", arg->server.toUtf8().constData(), - "type", "base64", - nullptr); + secret_password_lookup_fn(qtkeychainSchema(), nullptr, on_password_lookup, arg, + "user", arg->user.toUtf8().constData(), "server", + arg->server.toUtf8().constData(), "type", "base64", + nullptr); return; } else { - arg->self->q->emitFinishedWithError( QKeychain::EntryNotFound, QObject::tr("Entry not found") ); + arg->self->q->emitFinishedWithError(QKeychain::EntryNotFound, + QObject::tr("Entry not found")); } } } if (error) { - g_error_free (error); + g_error_free(error); } if (password) { - secret_password_free_fn (password); + secret_password_free_fn(password); } if (arg) { @@ -133,57 +118,50 @@ on_password_lookup (GObject *source, } } -static void -on_password_stored (GObject *source, - GAsyncResult *result, - gpointer inst) +static void on_password_stored(GObject *source, GAsyncResult *result, gpointer inst) { GError *error = nullptr; - QKeychain::JobPrivate *self = (QKeychain::JobPrivate*)inst; + QKeychain::JobPrivate *self = (QKeychain::JobPrivate *)inst; Q_UNUSED(source); - secret_password_store_finish_fn (result, &error); + secret_password_store_finish_fn(result, &error); if (self) { if (error) { - self->q->emitFinishedWithError( gerrorToCode(error), - QString::fromUtf8(error->message) ); + self->q->emitFinishedWithError(gerrorToCode(error), QString::fromUtf8(error->message)); } else { self->q->emitFinished(); } } if (error) { - g_error_free (error); + g_error_free(error); } } -static void -on_password_cleared (GObject *source, - GAsyncResult *result, - gpointer inst) +static void on_password_cleared(GObject *source, GAsyncResult *result, gpointer inst) { GError *error = nullptr; - QKeychain::JobPrivate *self = (QKeychain::JobPrivate*)inst; - gboolean removed = secret_password_clear_finish_fn (result, &error); + QKeychain::JobPrivate *self = (QKeychain::JobPrivate *)inst; + gboolean removed = secret_password_clear_finish_fn(result, &error); Q_UNUSED(source); if (self) { - if ( error ) { - self->q->emitFinishedWithError( gerrorToCode(error), - QString::fromUtf8(error->message) ); + if (error) { + self->q->emitFinishedWithError(gerrorToCode(error), QString::fromUtf8(error->message)); } else { Q_UNUSED(removed); self->q->emitFinished(); } } if (error) { - g_error_free (error); + g_error_free(error); } } -static QString modeToString(QKeychain::JobPrivate::Mode mode) { - switch(mode) { +static QString modeToString(QKeychain::JobPrivate::Mode mode) +{ + switch (mode) { case QKeychain::JobPrivate::Binary: return "base64"; default: @@ -192,9 +170,10 @@ static QString modeToString(QKeychain::JobPrivate::Mode mode) { } #endif -bool LibSecretKeyring::isAvailable() { +bool LibSecretKeyring::isAvailable() +{ #if defined(HAVE_LIBSECRET) - const LibSecretKeyring& keyring = instance(); + const LibSecretKeyring &keyring = instance(); if (!keyring.isLoaded()) return false; if (secret_password_lookup_fn == nullptr) @@ -235,11 +214,9 @@ bool LibSecretKeyring::findPassword(const QString &user, const QString &server, arg->user = user; arg->server = server; - secret_password_lookup_fn (qtkeychainSchema(), nullptr, on_password_lookup, arg, - "user", user.toUtf8().constData(), - "server", server.toUtf8().constData(), - "type", "plaintext", - nullptr); + secret_password_lookup_fn(qtkeychainSchema(), nullptr, on_password_lookup, arg, "user", + user.toUtf8().constData(), "server", server.toUtf8().constData(), + "type", "plaintext", nullptr); return true; #else Q_UNUSED(user) @@ -249,12 +226,9 @@ bool LibSecretKeyring::findPassword(const QString &user, const QString &server, #endif } -bool LibSecretKeyring::writePassword(const QString &display_name, - const QString &user, - const QString &server, - const QKeychain::JobPrivate::Mode mode, - const QByteArray &password, - QKeychain::JobPrivate *self) +bool LibSecretKeyring::writePassword(const QString &display_name, const QString &user, + const QString &server, const QKeychain::JobPrivate::Mode mode, + const QByteArray &password, QKeychain::JobPrivate *self) { #if defined(HAVE_LIBSECRET) if (!isAvailable()) { @@ -263,7 +237,7 @@ bool LibSecretKeyring::writePassword(const QString &display_name, QString type = modeToString(mode); QByteArray pwd; - switch(mode) { + switch (mode) { case QKeychain::JobPrivate::Binary: pwd = password.toBase64(); break; @@ -272,13 +246,10 @@ bool LibSecretKeyring::writePassword(const QString &display_name, break; } - secret_password_store_fn (qtkeychainSchema(), SECRET_COLLECTION_DEFAULT, - display_name.toUtf8().constData(), - pwd.constData(), nullptr, on_password_stored, self, - "user", user.toUtf8().constData(), - "server", server.toUtf8().constData(), - "type", type.toUtf8().constData(), - nullptr); + secret_password_store_fn( + qtkeychainSchema(), SECRET_COLLECTION_DEFAULT, display_name.toUtf8().constData(), + pwd.constData(), nullptr, on_password_stored, self, "user", user.toUtf8().constData(), + "server", server.toUtf8().constData(), "type", type.toUtf8().constData(), nullptr); return true; #else Q_UNUSED(display_name) @@ -292,17 +263,16 @@ bool LibSecretKeyring::writePassword(const QString &display_name, } bool LibSecretKeyring::deletePassword(const QString &key, const QString &service, - QKeychain::JobPrivate* self) + QKeychain::JobPrivate *self) { #if defined(HAVE_LIBSECRET) if (!isAvailable()) { return false; } - secret_password_clear_fn (qtkeychainSchema(), nullptr, on_password_cleared, self, - "user", key.toUtf8().constData(), - "server", service.toUtf8().constData(), - nullptr); + secret_password_clear_fn(qtkeychainSchema(), nullptr, on_password_cleared, self, "user", + key.toUtf8().constData(), "server", service.toUtf8().constData(), + nullptr); return true; #else Q_UNUSED(key) @@ -312,32 +282,27 @@ bool LibSecretKeyring::deletePassword(const QString &key, const QString &service #endif } -LibSecretKeyring::LibSecretKeyring() - : QLibrary(QLatin1String("secret-1"), 0) +LibSecretKeyring::LibSecretKeyring() : QLibrary(QLatin1String("secret-1"), 0) { #ifdef HAVE_LIBSECRET if (load()) { - secret_password_lookup_fn = - (secret_password_lookup_t)resolve("secret_password_lookup"); + secret_password_lookup_fn = (secret_password_lookup_t)resolve("secret_password_lookup"); secret_password_lookup_finish_fn = (secret_password_lookup_finish_t)resolve("secret_password_lookup_finish"); - secret_password_store_fn = - (secret_password_store_t)resolve("secret_password_store"); + secret_password_store_fn = (secret_password_store_t)resolve("secret_password_store"); secret_password_store_finish_fn = (secret_password_store_finish_t)resolve("secret_password_store_finish"); - secret_password_clear_fn = - (secret_password_clear_t)resolve("secret_password_clear"); + secret_password_clear_fn = (secret_password_clear_t)resolve("secret_password_clear"); secret_password_clear_finish_fn = (secret_password_clear_finish_t)resolve("secret_password_clear_finish"); - secret_password_free_fn = - (secret_password_free_t)resolve("secret_password_free"); - secret_error_get_quark_fn = - (secret_error_get_quark_t)resolve("secret_error_get_quark"); + secret_password_free_fn = (secret_password_free_t)resolve("secret_password_free"); + secret_error_get_quark_fn = (secret_error_get_quark_t)resolve("secret_error_get_quark"); } #endif } -LibSecretKeyring &LibSecretKeyring::instance() { +LibSecretKeyring &LibSecretKeyring::instance() +{ static LibSecretKeyring instance; return instance; diff --git a/qtkeychain/libsecret_p.h b/qtkeychain/libsecret_p.h index bd966fe0..d5f26005 100644 --- a/qtkeychain/libsecret_p.h +++ b/qtkeychain/libsecret_p.h @@ -5,23 +5,20 @@ #include "keychain_p.h" -class LibSecretKeyring : public QLibrary { +class LibSecretKeyring : public QLibrary +{ public: static bool isAvailable(); - static bool findPassword(const QString& user, - const QString& server, - QKeychain::JobPrivate* self); + static bool findPassword(const QString &user, const QString &server, + QKeychain::JobPrivate *self); - static bool writePassword(const QString& display_name, - const QString& user, - const QString& server, - const QKeychain::JobPrivate::Mode type, - const QByteArray& password, - QKeychain::JobPrivate* self); + static bool writePassword(const QString &display_name, const QString &user, + const QString &server, const QKeychain::JobPrivate::Mode type, + const QByteArray &password, QKeychain::JobPrivate *self); static bool deletePassword(const QString &key, const QString &service, - QKeychain::JobPrivate* self); + QKeychain::JobPrivate *self); private: LibSecretKeyring(); @@ -29,5 +26,4 @@ class LibSecretKeyring : public QLibrary { static LibSecretKeyring &instance(); }; - #endif diff --git a/qtkeychain/plaintextstore.cpp b/qtkeychain/plaintextstore.cpp index f9479f04..14470e52 100644 --- a/qtkeychain/plaintextstore.cpp +++ b/qtkeychain/plaintextstore.cpp @@ -14,18 +14,26 @@ using namespace QKeychain; namespace { #ifdef Q_OS_WIN -inline QString dataKey(const QString &key) { return key; } +inline QString dataKey(const QString &key) +{ + return key; +} #else // Q_OS_WIN -inline QString dataKey(const QString &key) { return key + QLatin1String("/data"); } -inline QString typeKey(const QString &key) { return key + QLatin1String("/type"); } -#endif // Q_OS_WIN +inline QString dataKey(const QString &key) +{ + return key + QLatin1String("/data"); } - +inline QString typeKey(const QString &key) +{ + return key + QLatin1String("/type"); +} +#endif // Q_OS_WIN +} // namespace PlainTextStore::PlainTextStore(const QString &service, QSettings *settings) - : m_localSettings(settings ? nullptr : new QSettings(service)) - , m_actualSettings(settings ? settings : m_localSettings.data()) - , m_error(NoError) + : m_localSettings(settings ? nullptr : new QSettings(service)), + m_actualSettings(settings ? settings : m_localSettings.data()), + m_error(NoError) { } diff --git a/qtkeychain/plaintextstore_p.h b/qtkeychain/plaintextstore_p.h index 7ec05aac..974318e0 100644 --- a/qtkeychain/plaintextstore_p.h +++ b/qtkeychain/plaintextstore_p.h @@ -15,7 +15,8 @@ namespace QKeychain { -class PlainTextStore { +class PlainTextStore +{ Q_DECLARE_TR_FUNCTIONS(QKeychain::PlainTextStore) public: @@ -42,6 +43,6 @@ class PlainTextStore { Error m_error; }; -} +} // namespace QKeychain #endif // QTKEYCHAIN_PLAINTEXTSTORE_P_H diff --git a/testclient.cpp b/testclient.cpp index 9aed138d..3ab8fcb0 100644 --- a/testclient.cpp +++ b/testclient.cpp @@ -9,9 +9,9 @@ #include #ifdef Q_OS_DARWIN -#include +# include #else -#include +# include #endif #include @@ -21,109 +21,112 @@ using namespace QKeychain; -static int printUsage() { +static int printUsage() +{ std::cerr << "testclient store " << std::endl; std::cerr << "testclient restore " << std::endl; std::cerr << "testclient delete " << std::endl; return 1; } -int main( int argc, char** argv ) { +int main(int argc, char **argv) +{ #ifdef Q_OS_DARWIN // Since we use NSNotificationCenter under the hood in keychain_apple, // we use QGuiApplication to automatically configure the platform // integration stuff done in this class and not in QCoreApplication - QGuiApplication app( argc, argv ); + QGuiApplication app(argc, argv); #else - QCoreApplication app( argc, argv ); + QCoreApplication app(argc, argv); #endif const QStringList args = app.arguments(); - if ( args.count() < 2 ) + if (args.count() < 2) return printUsage(); QStringList::ConstIterator it = args.constBegin(); ++it; - if ( *it == QLatin1String("store") ) { - if ( ++it == args.constEnd() ) + if (*it == QLatin1String("store")) { + if (++it == args.constEnd()) return printUsage(); const QString acc = *it; - if ( ++it == args.constEnd() ) + if (++it == args.constEnd()) return printUsage(); const QString pass = *it; - if ( ++it != args.constEnd() ) + if (++it != args.constEnd()) return printUsage(); - WritePasswordJob job( QLatin1String("qtkeychain-testclient") ); - job.setAutoDelete( false ); - job.setKey( acc ); - job.setTextData( pass ); + WritePasswordJob job(QLatin1String("qtkeychain-testclient")); + job.setAutoDelete(false); + job.setKey(acc); + job.setTextData(pass); QEventLoop loop; - job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()) ); + job.connect(&job, SIGNAL(finished(QKeychain::Job *)), &loop, SLOT(quit())); job.start(); loop.exec(); - if ( job.error() ) { + if (job.error()) { std::cerr << "Storing password failed: " << qPrintable(job.errorString()) << std::endl; return 1; } std::cout << "Password stored successfully" << std::endl; - } else if ( *it == QLatin1String("bstore") ) { - if ( ++it == args.constEnd() ) + } else if (*it == QLatin1String("bstore")) { + if (++it == args.constEnd()) return printUsage(); const QString acc = *it; - if ( ++it == args.constEnd() ) + if (++it == args.constEnd()) return printUsage(); const QString pass = *it; - if ( ++it != args.constEnd() ) + if (++it != args.constEnd()) return printUsage(); - WritePasswordJob job( QLatin1String("qtkeychain-testclient") ); - job.setAutoDelete( false ); - job.setKey( acc ); - job.setBinaryData( pass.toUtf8() ); + WritePasswordJob job(QLatin1String("qtkeychain-testclient")); + job.setAutoDelete(false); + job.setKey(acc); + job.setBinaryData(pass.toUtf8()); QEventLoop loop; - job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()) ); + job.connect(&job, SIGNAL(finished(QKeychain::Job *)), &loop, SLOT(quit())); job.start(); loop.exec(); - if ( job.error() ) { - std::cerr << "Storing binary password failed: " - << qPrintable(job.errorString()) << std::endl; + if (job.error()) { + std::cerr << "Storing binary password failed: " << qPrintable(job.errorString()) + << std::endl; return 1; } std::cout << "Password stored successfully" << std::endl; - } else if ( *it == QLatin1String("restore") ) { - if ( ++it == args.constEnd() ) + } else if (*it == QLatin1String("restore")) { + if (++it == args.constEnd()) return printUsage(); const QString acc = *it; - if ( ++it != args.constEnd() ) + if (++it != args.constEnd()) return printUsage(); - ReadPasswordJob job( QLatin1String("qtkeychain-testclient") ); - job.setAutoDelete( false ); - job.setKey( acc ); + ReadPasswordJob job(QLatin1String("qtkeychain-testclient")); + job.setAutoDelete(false); + job.setKey(acc); QEventLoop loop; - job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()) ); + job.connect(&job, SIGNAL(finished(QKeychain::Job *)), &loop, SLOT(quit())); job.start(); loop.exec(); const QString pw = job.textData(); - if ( job.error() ) { - std::cerr << "Restoring password failed: " << qPrintable(job.errorString()) << std::endl; + if (job.error()) { + std::cerr << "Restoring password failed: " << qPrintable(job.errorString()) + << std::endl; return 1; } std::cout << qPrintable(pw) << std::endl; - } else if ( *it == QLatin1String("delete") ) { - if ( ++it == args.constEnd() ) + } else if (*it == QLatin1String("delete")) { + if (++it == args.constEnd()) return printUsage(); const QString acc = *it; - if ( ++it != args.constEnd() ) + if (++it != args.constEnd()) return printUsage(); - DeletePasswordJob job( QLatin1String("qtkeychain-testclient") ); - job.setAutoDelete( false ); - job.setKey( acc ); + DeletePasswordJob job(QLatin1String("qtkeychain-testclient")); + job.setAutoDelete(false); + job.setKey(acc); QEventLoop loop; - job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()) ); + job.connect(&job, SIGNAL(finished(QKeychain::Job *)), &loop, SLOT(quit())); job.start(); loop.exec(); - if ( job.error() ) { + if (job.error()) { std::cerr << "Deleting password failed: " << qPrintable(job.errorString()) << std::endl; return 1; } @@ -132,4 +135,3 @@ int main( int argc, char** argv ) { return printUsage(); } } - From ad19e689a7ac171b7fb83fc78a991def9737216d Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Tue, 4 Feb 2025 22:53:33 +0100 Subject: [PATCH 146/168] Modernize connects --- qtkeychain/keychain.cpp | 5 ++--- qtkeychain/keychain_p.h | 2 +- qtkeychain/keychain_unix.cpp | 23 +++++++++++------------ testclient.cpp | 8 ++++---- 4 files changed, 18 insertions(+), 20 deletions(-) diff --git a/qtkeychain/keychain.cpp b/qtkeychain/keychain.cpp index afa9f61b..93f338ca 100644 --- a/qtkeychain/keychain.cpp +++ b/qtkeychain/keychain.cpp @@ -177,9 +177,8 @@ void JobExecutor::startNextIfNoneRunning() next = m_queue.dequeue(); } if (next) { - connect(next, SIGNAL(finished(QKeychain::Job *)), this, - SLOT(jobFinished(QKeychain::Job *))); - connect(next, SIGNAL(destroyed(QObject *)), this, SLOT(jobDestroyed(QObject *))); + connect(next, &Job::finished, this, &JobExecutor::jobFinished); + connect(next, &Job::destroyed, this, &JobExecutor::jobDestroyed); m_jobRunning = true; next->scheduledStart(); } diff --git a/qtkeychain/keychain_p.h b/qtkeychain/keychain_p.h index 6ae89d60..5696e678 100644 --- a/qtkeychain/keychain_p.h +++ b/qtkeychain/keychain_p.h @@ -56,7 +56,7 @@ class JobPrivate : public QObject virtual void fallbackOnError(const QDBusError &err) = 0; -protected Q_SLOTS: +public Q_SLOTS: void kwalletWalletFound(QDBusPendingCallWatcher *watcher); virtual void kwalletFinished(QDBusPendingCallWatcher *watcher); virtual void kwalletOpenFinished(QDBusPendingCallWatcher *watcher); diff --git a/qtkeychain/keychain_unix.cpp b/qtkeychain/keychain_unix.cpp index da7cc7f2..4b5f1789 100644 --- a/qtkeychain/keychain_unix.cpp +++ b/qtkeychain/keychain_unix.cpp @@ -198,8 +198,8 @@ static void kwalletReadPasswordScheduledStartImpl(const char *service, const cha QDBusConnection::sessionBus(), priv); const QDBusPendingReply reply = priv->iface->networkWallet(); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, priv); - priv->connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher *)), priv, - SLOT(kwalletWalletFound(QDBusPendingCallWatcher *))); + priv->connect(watcher, &QDBusPendingCallWatcher::finished, priv, + &ReadPasswordJobPrivate::kwalletWalletFound); } else { // D-Bus is not reachable so none can tell us something about KWalletd QDBusError err(QDBusError::NoServer, ReadPasswordJobPrivate::tr("D-Bus is not running")); @@ -247,8 +247,8 @@ void JobPrivate::kwalletWalletFound(QDBusPendingCallWatcher *watcher) const QDBusPendingReply pendingReply = iface->open(reply.value(), 0, q->service()); QDBusPendingCallWatcher *pendingWatcher = new QDBusPendingCallWatcher(pendingReply, this); - connect(pendingWatcher, SIGNAL(finished(QDBusPendingCallWatcher *)), this, - SLOT(kwalletOpenFinished(QDBusPendingCallWatcher *))); + connect(pendingWatcher, &QDBusPendingCallWatcher::finished, this, + &JobPrivate::kwalletOpenFinished); } static QPair mapGnomeKeyringError(int result) @@ -376,8 +376,8 @@ void ReadPasswordJobPrivate::kwalletOpenFinished(QDBusPendingCallWatcher *watche const QDBusPendingReply nextReply = iface->entryType(walletHandle, q->service(), key, q->service()); QDBusPendingCallWatcher *nextWatcher = new QDBusPendingCallWatcher(nextReply, this); - connect(nextWatcher, SIGNAL(finished(QDBusPendingCallWatcher *)), this, - SLOT(kwalletEntryTypeFinished(QDBusPendingCallWatcher *))); + connect(nextWatcher, &QDBusPendingCallWatcher::finished, this, + &ReadPasswordJobPrivate::kwalletEntryTypeFinished); } // Must be in sync with KWallet::EntryType (kwallet.h) @@ -419,8 +419,8 @@ void ReadPasswordJobPrivate::kwalletEntryTypeFinished(QDBusPendingCallWatcher *w ? QDBusPendingCall(iface->readPassword(walletHandle, q->service(), key, q->service())) : QDBusPendingCall(iface->readEntry(walletHandle, q->service(), key, q->service())); QDBusPendingCallWatcher *nextWatcher = new QDBusPendingCallWatcher(nextReply, this); - connect(nextWatcher, SIGNAL(finished(QDBusPendingCallWatcher *)), this, - SLOT(kwalletFinished(QDBusPendingCallWatcher *))); + connect(nextWatcher, &QDBusPendingCallWatcher::finished, this, + &ReadPasswordJobPrivate::kwalletFinished); } void ReadPasswordJobPrivate::kwalletFinished(QDBusPendingCallWatcher *watcher) @@ -450,8 +450,8 @@ static void kwalletWritePasswordScheduledStart(const char *service, const char * QDBusConnection::sessionBus(), priv); const QDBusPendingReply reply = priv->iface->networkWallet(); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, priv); - priv->connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher *)), priv, - SLOT(kwalletWalletFound(QDBusPendingCallWatcher *))); + priv->connect(watcher, &QDBusPendingCallWatcher::finished, priv, + &JobPrivate::kwalletWalletFound); } else { // D-Bus is not reachable so none can tell us something about KWalletd QDBusError err(QDBusError::NoServer, WritePasswordJobPrivate::tr("D-Bus is not running")); @@ -572,8 +572,7 @@ void JobPrivate::kwalletOpenFinished(QDBusPendingCallWatcher *watcher) } QDBusPendingCallWatcher *nextWatcher = new QDBusPendingCallWatcher(nextReply, this); - connect(nextWatcher, SIGNAL(finished(QDBusPendingCallWatcher *)), this, - SLOT(kwalletFinished(QDBusPendingCallWatcher *))); + connect(nextWatcher, &QDBusPendingCallWatcher::finished, this, &JobPrivate::kwalletFinished); } void JobPrivate::kwalletFinished(QDBusPendingCallWatcher *watcher) diff --git a/testclient.cpp b/testclient.cpp index 3ab8fcb0..9d4be2f8 100644 --- a/testclient.cpp +++ b/testclient.cpp @@ -60,7 +60,7 @@ int main(int argc, char **argv) job.setKey(acc); job.setTextData(pass); QEventLoop loop; - job.connect(&job, SIGNAL(finished(QKeychain::Job *)), &loop, SLOT(quit())); + job.connect(&job, &Job::finished, &loop, &QEventLoop::quit); job.start(); loop.exec(); if (job.error()) { @@ -82,7 +82,7 @@ int main(int argc, char **argv) job.setKey(acc); job.setBinaryData(pass.toUtf8()); QEventLoop loop; - job.connect(&job, SIGNAL(finished(QKeychain::Job *)), &loop, SLOT(quit())); + job.connect(&job, &Job::finished, &loop, &QEventLoop::quit); job.start(); loop.exec(); if (job.error()) { @@ -101,7 +101,7 @@ int main(int argc, char **argv) job.setAutoDelete(false); job.setKey(acc); QEventLoop loop; - job.connect(&job, SIGNAL(finished(QKeychain::Job *)), &loop, SLOT(quit())); + job.connect(&job, &Job::finished, &loop, &QEventLoop::quit); job.start(); loop.exec(); @@ -122,7 +122,7 @@ int main(int argc, char **argv) job.setAutoDelete(false); job.setKey(acc); QEventLoop loop; - job.connect(&job, SIGNAL(finished(QKeychain::Job *)), &loop, SLOT(quit())); + job.connect(&job, &Job::finished, &loop, &QEventLoop::quit); job.start(); loop.exec(); From 3364c1cd2fc11e35aac61335932f3f3176175ef7 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Tue, 4 Feb 2025 22:59:25 +0100 Subject: [PATCH 147/168] Use some auto --- qtkeychain/keychain_android.cpp | 16 ++++++++-------- qtkeychain/keychain_unix.cpp | 18 +++++++++--------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/qtkeychain/keychain_android.cpp b/qtkeychain/keychain_android.cpp index b759a73d..317d55f8 100644 --- a/qtkeychain/keychain_android.cpp +++ b/qtkeychain/keychain_android.cpp @@ -54,7 +54,7 @@ void ReadPasswordJobPrivate::scheduledStart() } const QByteArray &encryptedData = plainTextStore.readData(q->key()); - const KeyStore keyStore = KeyStore::getInstance(QStringLiteral("AndroidKeyStore")); + const auto keyStore = KeyStore::getInstance(QStringLiteral("AndroidKeyStore")); if (!keyStore || !keyStore.load()) { q->emitFinishedWithError(Error::AccessDenied, tr("Could not open keystore")); @@ -70,7 +70,7 @@ void ReadPasswordJobPrivate::scheduledStart() return; } - const Cipher cipher = Cipher::getInstance(QStringLiteral("RSA/ECB/PKCS1Padding")); + const auto cipher = Cipher::getInstance(QStringLiteral("RSA/ECB/PKCS1Padding")); if (!cipher || !cipher.init(Cipher::DECRYPT_MODE, entry.getPrivateKey())) { q->emitFinishedWithError(Error::OtherError, tr("Could not create decryption cipher")); @@ -99,8 +99,8 @@ void WritePasswordJobPrivate::scheduledStart() const auto &alias = makeAlias(q->service(), q->key()); if (!keyStore.containsAlias(alias)) { - const Calendar start = Calendar::getInstance(); - const Calendar end = Calendar::getInstance(); + const auto start = Calendar::getInstance(); + const auto end = Calendar::getInstance(); end.add(Calendar::YEAR, 99); const KeyPairGeneratorSpec spec = @@ -128,8 +128,8 @@ void WritePasswordJobPrivate::scheduledStart() .setEndDate(end.getTime()) .build(); - const KeyPairGenerator generator = KeyPairGenerator::getInstance( - QStringLiteral("RSA"), QStringLiteral("AndroidKeyStore")); + const auto generator = KeyPairGenerator::getInstance(QStringLiteral("RSA"), + QStringLiteral("AndroidKeyStore")); if (!generator) { q->emitFinishedWithError(Error::OtherError, @@ -154,7 +154,7 @@ void WritePasswordJobPrivate::scheduledStart() } const RSAPublicKey publicKey = entry.getCertificate().getPublicKey(); - const Cipher cipher = Cipher::getInstance(QStringLiteral("RSA/ECB/PKCS1Padding")); + const auto cipher = Cipher::getInstance(QStringLiteral("RSA/ECB/PKCS1Padding")); if (!cipher || !cipher.init(Cipher::ENCRYPT_MODE, publicKey)) { q->emitFinishedWithError(Error::OtherError, tr("Could not create encryption cipher")); @@ -180,7 +180,7 @@ void WritePasswordJobPrivate::scheduledStart() void DeletePasswordJobPrivate::scheduledStart() { - const KeyStore keyStore = KeyStore::getInstance(QStringLiteral("AndroidKeyStore")); + const auto keyStore = KeyStore::getInstance(QStringLiteral("AndroidKeyStore")); if (!keyStore || !keyStore.load()) { q->emitFinishedWithError(Error::AccessDenied, tr("Could not open keystore")); diff --git a/qtkeychain/keychain_unix.cpp b/qtkeychain/keychain_unix.cpp index 4b5f1789..1e57ae45 100644 --- a/qtkeychain/keychain_unix.cpp +++ b/qtkeychain/keychain_unix.cpp @@ -197,7 +197,7 @@ static void kwalletReadPasswordScheduledStartImpl(const char *service, const cha priv->iface = new org::kde::KWallet(QLatin1String(service), QLatin1String(path), QDBusConnection::sessionBus(), priv); const QDBusPendingReply reply = priv->iface->networkWallet(); - QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, priv); + auto watcher = new QDBusPendingCallWatcher(reply, priv); priv->connect(watcher, &QDBusPendingCallWatcher::finished, priv, &ReadPasswordJobPrivate::kwalletWalletFound); } else { @@ -246,7 +246,7 @@ void JobPrivate::kwalletWalletFound(QDBusPendingCallWatcher *watcher) iface->setTimeout(0x7FFFFFFF); const QDBusPendingReply pendingReply = iface->open(reply.value(), 0, q->service()); - QDBusPendingCallWatcher *pendingWatcher = new QDBusPendingCallWatcher(pendingReply, this); + auto pendingWatcher = new QDBusPendingCallWatcher(pendingReply, this); connect(pendingWatcher, &QDBusPendingCallWatcher::finished, this, &JobPrivate::kwalletOpenFinished); } @@ -350,7 +350,7 @@ void ReadPasswordJobPrivate::kwalletOpenFinished(QDBusPendingCallWatcher *watche q->emitFinished(); - WritePasswordJob *j = new WritePasswordJob(q->service(), nullptr); + auto j = new WritePasswordJob(q->service(), nullptr); j->setSettings(q->settings()); j->setKey(key); j->setAutoDelete(true); @@ -375,7 +375,7 @@ void ReadPasswordJobPrivate::kwalletOpenFinished(QDBusPendingCallWatcher *watche const QDBusPendingReply nextReply = iface->entryType(walletHandle, q->service(), key, q->service()); - QDBusPendingCallWatcher *nextWatcher = new QDBusPendingCallWatcher(nextReply, this); + auto nextWatcher = new QDBusPendingCallWatcher(nextReply, this); connect(nextWatcher, &QDBusPendingCallWatcher::finished, this, &ReadPasswordJobPrivate::kwalletEntryTypeFinished); } @@ -387,7 +387,7 @@ void ReadPasswordJobPrivate::kwalletEntryTypeFinished(QDBusPendingCallWatcher *w { watcher->deleteLater(); if (watcher->isError()) { - const QDBusError err = watcher->error(); + const auto err = watcher->error(); q->emitFinishedWithError(OtherError, tr("Could not determine data type: %1; %2") .arg(QDBusError::errorString(err.type()), err.message())); @@ -415,10 +415,10 @@ void ReadPasswordJobPrivate::kwalletEntryTypeFinished(QDBusPendingCallWatcher *w return; } - const QDBusPendingCall nextReply = (mode == Text) + const auto nextReply = (mode == Text) ? QDBusPendingCall(iface->readPassword(walletHandle, q->service(), key, q->service())) : QDBusPendingCall(iface->readEntry(walletHandle, q->service(), key, q->service())); - QDBusPendingCallWatcher *nextWatcher = new QDBusPendingCallWatcher(nextReply, this); + auto nextWatcher = new QDBusPendingCallWatcher(nextReply, this); connect(nextWatcher, &QDBusPendingCallWatcher::finished, this, &ReadPasswordJobPrivate::kwalletFinished); } @@ -449,7 +449,7 @@ static void kwalletWritePasswordScheduledStart(const char *service, const char * priv->iface = new org::kde::KWallet(QLatin1String(service), QLatin1String(path), QDBusConnection::sessionBus(), priv); const QDBusPendingReply reply = priv->iface->networkWallet(); - QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, priv); + auto watcher = new QDBusPendingCallWatcher(reply, priv); priv->connect(watcher, &QDBusPendingCallWatcher::finished, priv, &JobPrivate::kwalletWalletFound); } else { @@ -571,7 +571,7 @@ void JobPrivate::kwalletOpenFinished(QDBusPendingCallWatcher *watcher) nextReply = iface->removeEntry(handle, q->service(), key, q->service()); } - QDBusPendingCallWatcher *nextWatcher = new QDBusPendingCallWatcher(nextReply, this); + auto nextWatcher = new QDBusPendingCallWatcher(nextReply, this); connect(nextWatcher, &QDBusPendingCallWatcher::finished, this, &JobPrivate::kwalletFinished); } From e45e73aa9377b553a5b20f892dcae21348dd7c44 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Tue, 4 Feb 2025 23:01:14 +0100 Subject: [PATCH 148/168] DeleteJob: Do not emit finished() twice --- qtkeychain/keychain_unix.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/qtkeychain/keychain_unix.cpp b/qtkeychain/keychain_unix.cpp index 1e57ae45..271daabb 100644 --- a/qtkeychain/keychain_unix.cpp +++ b/qtkeychain/keychain_unix.cpp @@ -639,8 +639,6 @@ void DeletePasswordJobPrivate::fallbackOnError(const QDBusError &err) actual->sync(); q->emitFinished(); - - q->emitFinished(); } bool QKeychain::isAvailable() From 4d535dae937226008544f0d211e3127f4080f7a7 Mon Sep 17 00:00:00 2001 From: David Faure Date: Fri, 4 Apr 2025 21:03:56 +0200 Subject: [PATCH 149/168] Fix build with BUILD_TESTING=ON, don't rely on QT_MAJOR_VERSION CMake Error at 3rdparty/qtkeychain/autotest/CMakeLists.txt:3 (find_package): By not providing "FindQt.cmake" in CMAKE_MODULE_PATH this project has asked [...] Two problems there: - QT_MAJOR_VERSION isn't set, so don't rely on it, use BUILD_WITH_QT6 like the parent CMakeLists.txt - There's no FindQt, FindQt5 or FindQt6, so add CONFIG so that cmake only warns about Qt5Config.cmake/Qt6Config.cmake not found --- autotest/CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/autotest/CMakeLists.txt b/autotest/CMakeLists.txt index 2f4b94c3..ffd82dee 100644 --- a/autotest/CMakeLists.txt +++ b/autotest/CMakeLists.txt @@ -1,6 +1,10 @@ include(ECMAddTests) -find_package(Qt${QT_MAJOR_VERSION} COMPONENTS Test REQUIRED) +if (BUILD_WITH_QT6) + find_package(Qt6 CONFIG COMPONENTS Test REQUIRED) +else() + find_package(Qt5 CONFIG COMPONENTS Test REQUIRED) +endif() ecm_add_tests(basic.cpp LINK_LIBRARIES ${QTKEYCHAIN_TARGET_NAME} Qt${QT_MAJOR_VERSION}::Test) set_property(TARGET basic PROPERTY AUTOMOC ON) From 77063bd429af4bb00b66cabaa189facdb465d5cb Mon Sep 17 00:00:00 2001 From: Biswapriyo Nath Date: Fri, 17 Jan 2025 05:53:50 +0000 Subject: [PATCH 150/168] Include cmath for std::ceil This fixes the following compiler error with mingw g++ keychain_win.cpp:190:36: error: 'ceil' is not a member of 'std' --- qtkeychain/keychain_win.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/qtkeychain/keychain_win.cpp b/qtkeychain/keychain_win.cpp index 22b73299..c470b908 100644 --- a/qtkeychain/keychain_win.cpp +++ b/qtkeychain/keychain_win.cpp @@ -14,6 +14,7 @@ #include #include +#include #include using namespace QKeychain; From b33e98f56b3b1cabb2e2d61c61a2ed9e165e6e0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ball=C3=B3=20Gy=C3=B6rgy?= Date: Thu, 27 Feb 2025 20:30:08 +0100 Subject: [PATCH 151/168] Add support for selecting backend Check the environment variable QTKEYCHAIN_BACKEND for a manually specified backend before trying to automatically guess which backend to use. --- qtkeychain/keychain_unix.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/qtkeychain/keychain_unix.cpp b/qtkeychain/keychain_unix.cpp index 271daabb..c87daf23 100644 --- a/qtkeychain/keychain_unix.cpp +++ b/qtkeychain/keychain_unix.cpp @@ -124,6 +124,20 @@ static KeyringBackend detectKeyringBackend() * available. */ + // Check if user wants to override detection logic + QByteArray backendOverride = qgetenv("QTKEYCHAIN_BACKEND"); + if (backendOverride == "libsecret") { + return Backend_LibSecretKeyring; + } else if (backendOverride == "gnome") { + return Backend_GnomeKeyring; + } else if (backendOverride == "kwallet4") { + return Backend_Kwallet4; + } else if (backendOverride == "kwallet5") { + return Backend_Kwallet5; + } else if (backendOverride == "kwallet6") { + return Backend_Kwallet6; + } + switch (detectDesktopEnvironment()) { case DesktopEnv_Kde4: return Backend_Kwallet4; From 9676912f907f190f5ef92e65c73c140a4076b9ae Mon Sep 17 00:00:00 2001 From: Nils Schimmelmann Date: Thu, 13 Mar 2025 10:30:18 -0500 Subject: [PATCH 152/168] fix CMake build on MinGW --- qtkeychain/CMakeLists.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/qtkeychain/CMakeLists.txt b/qtkeychain/CMakeLists.txt index bd5e08d4..5ba7dc7c 100644 --- a/qtkeychain/CMakeLists.txt +++ b/qtkeychain/CMakeLists.txt @@ -31,7 +31,12 @@ if(WIN32) endif() set(CMAKE_CXX_STANDARD 17) - add_definitions( /utf-8 -DUNICODE ) + add_definitions(-DUNICODE) + if (MSVC) + add_definitions(/utf-8) + elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") + add_definitions(-finput-charset=UTF-8 -fexec-charset=UTF-8) + endif() endif() if(APPLE) From 35c4f40186a6ac3260982256e846bb0774ea91de Mon Sep 17 00:00:00 2001 From: Nils Schimmelmann Date: Thu, 13 Mar 2025 10:57:31 -0500 Subject: [PATCH 153/168] add Github actions --- .github/workflows/build.yml | 131 ++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..592a20dd --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,131 @@ +name: Build + +on: + push: + branches: + - main + pull_request: + workflow_dispatch: + +jobs: + build: + strategy: + fail-fast: false + matrix: + os: [windows-latest, ubuntu-latest, macos-latest] + compiler: [msvc, gcc, clang] + qt_version: [qt5, qt6] + exclude: + - os: ubuntu-latest + compiler: msvc + - os: macos-latest + compiler: gcc + - os: macos-latest + compiler: msvc + - os: windows-latest + compiler: clang + + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Setup CMake and Ninja + uses: lukka/get-cmake@latest + + - if: matrix.compiler == 'msvc' + uses: ilammy/msvc-dev-cmd@v1 + + - name: Install Dependencies (Windows only) + if: runner.os == 'Windows' + uses: crazy-max/ghaction-chocolatey@v3 + with: + args: install pkgconfiglite + + - name: Install Qt (Mac/Linux only) + if: runner.os != 'Windows' + run: | + if [ '${{ runner.os }}' == 'macOS' ]; then + if [ '${{ matrix.qt_version }}' == 'qt6' ]; then + brew install qt6 pkg-config + brew link qt6 --force + echo "$(brew --prefix qt6)/bin" >> $GITHUB_PATH + else + brew install qt5 pkg-config + brew link qt5 --force + echo "$(brew --prefix qt5)/bin" >> $GITHUB_PATH + fi + elif [ '${{ runner.os }}' == 'Linux' ]; then + sudo apt-get update + if [ '${{ matrix.qt_version }}' == 'qt6' ]; then + sudo apt-get install -y qt6-base-dev qtchooser qmake6 qt6-base-dev-tools qt6-tools-dev pkg-config libsecret-1-dev + else + sudo apt-get install -y qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools qttools5-dev pkg-config libsecret-1-dev + fi + fi + + - name: Set AQT Version and Arch (Windows only) + if: runner.os == 'Windows' + id: set_qt_arch + shell: pwsh + run: | + $qtVer = "${{ matrix.qt_version == 'qt5' && '5.15.2' || '6.5.0' }}" + $qtArch = "${{ matrix.compiler == 'msvc' && 'msvc2019_64' || + matrix.qt_version == 'qt5' && matrix.compiler == 'gcc' && 'mingw81' || + matrix.qt_version == 'qt6' && matrix.compiler == 'gcc' && 'mingw' }}" + echo "::set-output name=QT_VER::$qtVer" + echo "::set-output name=QT_ARCH::$qtArch" + + - name: Install Qt (Windows only) + if: runner.os == 'Windows' + uses: jurplel/install-qt-action@v4 + with: + version: ${{ steps.set_qt_arch.outputs.QT_VER }} + arch: win64_${{ steps.set_qt_arch.outputs.QT_ARCH }} + dir: C:\ + cache: true + + - name: Setup Build Directory + run: | + mkdir -p ${{ github.workspace }}/work/build/${{ github.event.repository.name }} + + - name: Run CMake (Windows only) + if: runner.os == 'Windows' + shell: pwsh + run: | + $qtPath = "C:\\Qt\\${{ steps.set_qt_arch.outputs.QT_VER }}\\win64_${{ steps.set_qt_arch.outputs.QT_ARCH }}" + if ( '${{ matrix.compiler }}' -eq 'msvc' ) { + $generator = "NMake Makefiles" + } else { + $generator = "MinGW Makefiles" + } + echo "C:\\Qt\\Tools\\${{ steps.set_qt_arch.outputs.QT_ARCH }}\\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + cd "${{ github.workspace }}/work/build/${{ github.event.repository.name }}" + cmake -G $generator "${{ github.workspace }}" ` + -DCMAKE_BUILD_TYPE=Release ` + -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" ` + -DCMAKE_PREFIX_PATH=$qtPath ` + ${{ matrix.qt_version == 'qt6' && '-DBUILD_WITH_QT6=true' || '' }} + + - name: Run CMake (Mac/Linux Only) + if: runner.os != 'Windows' + run: | + cd ${{ github.workspace }}/work/build/${{ github.event.repository.name }} + cmake -G Ninja ${{ github.workspace }} \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" \ + ${{ matrix.qt_version == 'qt6' && '-DBUILD_WITH_QT6=true' || '' }} + + - name: Build and Install + run: | + cd ${{ github.workspace }}/work/build/${{ github.event.repository.name }} + cmake --build . + cmake --install . --prefix "${{ github.workspace }}/install" + + - name: Upload Artifact + uses: actions/upload-artifact@v4 + with: + name: build-artifact ${{ matrix.os }} ${{ matrix.compiler }} ${{ matrix.qt_version }} + path: ${{ github.workspace }}/work/build/${{ github.event.repository.name }} + From fbfe86733e8bf4512980b6edf8ff55ba4e00790c Mon Sep 17 00:00:00 2001 From: Daniel Nylander Date: Fri, 30 May 2025 10:45:45 +0200 Subject: [PATCH 154/168] Adding Swedish translation --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 53457eac..8d835e98 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -139,6 +139,7 @@ set(qtkeychain_TR_FILES translations/qtkeychain_fr.ts translations/qtkeychain_ro.ts translations/qtkeychain_ru.ts + translations/qtkeychain_sv.ts translations/qtkeychain_zh.ts ) From d97209933e4a4ebc2c30d3b460d7b56671bf5da4 Mon Sep 17 00:00:00 2001 From: Daniel Nylander Date: Fri, 30 May 2025 16:46:02 +0800 Subject: [PATCH 155/168] Adding Swedish translation --- translations/qtkeychain_sv.ts | 334 ++++++++++++++++++++++++++++++++++ 1 file changed, 334 insertions(+) create mode 100644 translations/qtkeychain_sv.ts diff --git a/translations/qtkeychain_sv.ts b/translations/qtkeychain_sv.ts new file mode 100644 index 00000000..bfa5bc30 --- /dev/null +++ b/translations/qtkeychain_sv.ts @@ -0,0 +1,334 @@ + + + + sv_SE + + Daniel Nylander <github@danielnylander.se> + + + + Poedit 3.6 + Project-Id-Version,POT-Creation-Date,PO-Revision-Date,Last-Translator,Language-Team,Language,MIME-Version,Content-Type,Content-Transfer-Encoding,Plural-Forms,X-Language,X-Qt-Contexts,X-Generator + + KeyChainClass + + + Read key failed: %1 + Läsning av nyckel misslyckades: %1 + + + + Write key failed: %1 + Skrivning av nyckel misslyckades: %1 + + + + Delete key failed: %1 + Borttagning av nyckel misslyckades: %1 + + + + QKeychain::DeletePasswordJobPrivate + + + Could not open keystore + Det gick inte att öppna nyckellagret + + + + Could not remove private key from keystore + Det gick inte att ta bort den privata nyckeln från nyckelagret + + + + Password not found + Lösenordet hittades inte + + + + + Unknown error + Okänt fel + + + + Could not open wallet: %1; %2 + Det gick inte att öppna plånboken: %1; %2 + + + + Password entry not found + Lösenordet hittades inte + + + + Could not decrypt data + Kunde inte avkryptera data + + + + QKeychain::JobPrivate + + + Unknown error + Okänt fel + + + + Access to keychain denied + Åtkomst till nyckelring nekades + + + + QKeychain::PlainTextStore + + + Could not store data in settings: access error + Det gick inte att lagra data i inställningarna: åtkomstfel + + + + Could not store data in settings: format error + Det gick inte att lagra data i inställningarna: formatfel + + + + Could not delete data from settings: access error + Det gick inte att ta bort data från inställningarna: åtkomstfel + + + + Could not delete data from settings: format error + Det gick inte att ta bort data från inställningarna: formatfel + + + + Entry not found + Posten hittades inte + + + + QKeychain::ReadPasswordJobPrivate + + + + Entry not found + Posten hittades inte + + + + Could not open keystore + Det gick inte att öppna nyckellagret + + + + Could not retrieve private key from keystore + Det gick inte att hämta den privata nyckeln från nyckellagret + + + + Could not create decryption cipher + Kunde inte skapa avkrypteringsskiffer + + + + Password not found + Lösenordet hittades inte + + + + D-Bus is not running + D-Bus är inte igång + + + + + Unknown error + Okänt fel + + + + No keychain service available + Ingen nyckelringstjänst tillgänglig + + + + Could not open wallet: %1; %2 + Det gick inte att öppna plånboken: %1; %2 + + + + Access to keychain denied + Åtkomst till nyckelring nekades + + + + Could not determine data type: %1; %2 + Datatypen kunde inte fastställas: %1; %2 + + + + Unsupported entry type 'Map' + Posttypen 'Map' stöds inte + + + + Unknown kwallet entry type '%1' + Okänd typ av kwallet-post '%1' + + + + Password entry not found + Lösenordsposten hittades inte + + + + Could not decrypt data + Kunde inte avkryptera data + + + + + Could not decrypt data: %1 + Det gick inte att avkryptera data: %1 + + + + QKeychain::WritePasswordJobPrivate + + + Could not open keystore + Det gick inte att öppna nyckellagret + + + + Could not create private key generator + Det gick inte att skapa en generator för privata nycklar + + + + Could not generate new private key + Det gick inte att generera en ny privat nyckel + + + + Could not retrieve private key from keystore + Det gick inte att hämta den privata nyckeln från nyckellagret + + + + Could not create encryption cipher + Det gick inte att skapa krypteringschiffer + + + + Could not encrypt data + Kunde inte kryptera data + + + + Password not found + Lösenordet hittades inte + + + + D-Bus is not running + D-Bus är inte igång + + + + + Unknown error + Okänt fel + + + + Could not open wallet: %1; %2 + Det gick inte att öppna plånboken: %1; %2 + + + + + Encryption failed: %1 + Krypteringen misslyckades: %1 + + + + Credential size exceeds maximum size of %1: %2 + Autentiseringsstorleken överskrider maxstorleken för %1: %2 + + + + Credential key exceeds maximum size of %1 + Autentiseringsnyckeln överskrider den maximala storleken på %1 + + + + Writing credentials failed: %1 + Skrivning av autentiseringsuppgifter misslyckades: %1 + + + + QObject + + + error 0x%1: %2 + fel 0x%1: %2 + + + + Access to keychain denied + Åtkomst till nyckelring nekades + + + + No keyring daemon + Ingen keyring-daemon + + + + Already unlocked + Redan upplåst + + + + No such keyring + Ingen sådan nyckelring + + + + Bad arguments + Felaktiga argument + + + + I/O error + In/ut-fel + + + + Cancelled + Avbruten + + + + Keyring already exists + Nyckelringen finns redan + + + + No match + Ingen matchning + + + + Unknown error + Okänt fel + + + + Entry not found + Posten hittades inte + + + From 7668a63a3669400223c5a563928ae042f499dbf4 Mon Sep 17 00:00:00 2001 From: yaoxp Date: Fri, 12 Sep 2025 18:00:02 +0800 Subject: [PATCH 156/168] Fix the crash caused by timeout when reading or writing keychain on macOS Reproduction method: Enter the password when reading or writing the keychain for the first time. If the input is slow and causes the read or write timeout, the application will crash. --- qtkeychain/keychain_apple.mm | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/qtkeychain/keychain_apple.mm b/qtkeychain/keychain_apple.mm index c9c00a49..2b0abeee 100644 --- a/qtkeychain/keychain_apple.mm +++ b/qtkeychain/keychain_apple.mm @@ -122,11 +122,10 @@ - (void)keychainTaskFinished - (void)keychainReadTaskFinished:(NSData *)retrievedData { - _privateJob->data.clear(); - _privateJob->mode = JobPrivate::Binary; - - if (retrievedData != nil) { - if (_privateJob) { + if (_privateJob) { + _privateJob->data.clear(); + _privateJob->mode = JobPrivate::Binary; + if (retrievedData != nil) { _privateJob->data = QByteArray::fromNSData(retrievedData); } } From f73a3410160e85089ea3a1e671d6a59717b06a84 Mon Sep 17 00:00:00 2001 From: Nils Schimmelmann Date: Fri, 19 Dec 2025 19:32:58 -0600 Subject: [PATCH 157/168] fix Windows CI job --- .github/workflows/build.yml | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 592a20dd..5f0ac95b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -70,12 +70,12 @@ jobs: id: set_qt_arch shell: pwsh run: | - $qtVer = "${{ matrix.qt_version == 'qt5' && '5.15.2' || '6.5.0' }}" + $qtVer = "${{ matrix.qt_version == 'qt5' && '5.15.2' || '6.5.3' }}" $qtArch = "${{ matrix.compiler == 'msvc' && 'msvc2019_64' || matrix.qt_version == 'qt5' && matrix.compiler == 'gcc' && 'mingw81' || matrix.qt_version == 'qt6' && matrix.compiler == 'gcc' && 'mingw' }}" - echo "::set-output name=QT_VER::$qtVer" - echo "::set-output name=QT_ARCH::$qtArch" + echo "QT_VER=$qtVer" >> $env:GITHUB_OUTPUT + echo "QT_ARCH=$qtArch" >> $env:GITHUB_OUTPUT - name: Install Qt (Windows only) if: runner.os == 'Windows' @@ -83,8 +83,9 @@ jobs: with: version: ${{ steps.set_qt_arch.outputs.QT_VER }} arch: win64_${{ steps.set_qt_arch.outputs.QT_ARCH }} - dir: C:\ + dir: 'C:\' cache: true + tools: 'tools_mingw1310' - name: Setup Build Directory run: | @@ -94,18 +95,20 @@ jobs: if: runner.os == 'Windows' shell: pwsh run: | - $qtPath = "C:\\Qt\\${{ steps.set_qt_arch.outputs.QT_VER }}\\win64_${{ steps.set_qt_arch.outputs.QT_ARCH }}" if ( '${{ matrix.compiler }}' -eq 'msvc' ) { $generator = "NMake Makefiles" } else { $generator = "MinGW Makefiles" } - echo "C:\\Qt\\Tools\\${{ steps.set_qt_arch.outputs.QT_ARCH }}\\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + if ( '${{ matrix.compiler }}' -eq 'gcc' ) { + echo "C:/Qt/Tools/mingw1310_64/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + $env:PATH = "C:\Qt\Tools\mingw1310_64\bin;$env:PATH" + } cd "${{ github.workspace }}/work/build/${{ github.event.repository.name }}" cmake -G $generator "${{ github.workspace }}" ` -DCMAKE_BUILD_TYPE=Release ` -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" ` - -DCMAKE_PREFIX_PATH=$qtPath ` + -DCMAKE_PREFIX_PATH=$env:QT_ROOT_DIR ` ${{ matrix.qt_version == 'qt6' && '-DBUILD_WITH_QT6=true' || '' }} - name: Run CMake (Mac/Linux Only) From 17c3ca04a7f1ed42554b4ea12fbf032711f0f286 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 22 Dec 2025 22:10:32 +0100 Subject: [PATCH 158/168] Fix build with emscripten --- CMakeLists.txt | 4 ++-- QtKeychainConfig.cmake.in | 2 +- qtkeychain/CMakeLists.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8d835e98..87a231de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,7 +70,7 @@ if (Qt5Core_FOUND AND NOT BUILD_WITH_QT6) set(QTANDROIDEXTRAS_LIBRARIES ${Qt5AndroidExtras_LIBRARIES}) endif() - if(UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU) + if(UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU AND NOT EMSCRIPTEN) find_package(Qt5 COMPONENTS DBus REQUIRED) include_directories(${Qt5DBus_INCLUDE_DIRS}) set(QTDBUS_LIBRARIES ${Qt5DBus_LIBRARIES}) @@ -100,7 +100,7 @@ else() set(QTKEYCHAIN_VERSION_INFIX 6) - if(UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU) + if(UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU AND NOT EMSCRIPTEN) find_package(Qt6 COMPONENTS DBus REQUIRED) include_directories(${Qt6DBus_INCLUDE_DIRS}) set(QTDBUS_LIBRARIES ${Qt6DBus_LIBRARIES}) diff --git a/QtKeychainConfig.cmake.in b/QtKeychainConfig.cmake.in index ead0005a..9395e362 100644 --- a/QtKeychainConfig.cmake.in +++ b/QtKeychainConfig.cmake.in @@ -14,7 +14,7 @@ include(CMakeFindDependencyMacro) find_dependency(Qt@QTKEYCHAIN_VERSION_INFIX@Core) -if(UNIX AND NOT APPLE AND NOT ANDROID) +if(UNIX AND NOT APPLE AND NOT ANDROID AND NOT EMSCRIPTEN) find_dependency(Qt@QTKEYCHAIN_VERSION_INFIX@DBus) endif() diff --git a/qtkeychain/CMakeLists.txt b/qtkeychain/CMakeLists.txt index 5ba7dc7c..8534b6c1 100644 --- a/qtkeychain/CMakeLists.txt +++ b/qtkeychain/CMakeLists.txt @@ -51,7 +51,7 @@ if(HAIKU) list(APPEND qtkeychain_LIBRARIES ${BE_LIBRARY}) endif() -if(UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU) +if(UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU AND NOT EMSCRIPTEN) option(LIBSECRET_SUPPORT "Build with libsecret support" ON) if(LIBSECRET_SUPPORT) From c7e10630cedc60257665be370e8ac97abe04ff32 Mon Sep 17 00:00:00 2001 From: Nicolas Fella Date: Tue, 11 Nov 2025 19:56:41 +0100 Subject: [PATCH 159/168] Use default DBus timeout for KWallet check When just starting up, on a slow system, 500ms may be too little time for KWallet to respond When this happens we fall back to libsecret, which can result in the right secrets not being available Instead use the default Qt DBus timeout (25s) If KWallet is not available the call will fail fast Fixes https://github.com/frankosterfeld/qtkeychain/issues/242 --- qtkeychain/keychain_unix.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/qtkeychain/keychain_unix.cpp b/qtkeychain/keychain_unix.cpp index c87daf23..a586ff8c 100644 --- a/qtkeychain/keychain_unix.cpp +++ b/qtkeychain/keychain_unix.cpp @@ -103,7 +103,6 @@ static bool isKwalletAvailable(const char *dbusIface, const char *dbusPath) // interface is activatable by making a call. Hence we check whether // a wallet can be opened. - iface.setTimeout(500); QDBusMessage reply = iface.call(QLatin1String("networkWallet")); return reply.type() == QDBusMessage::ReplyMessage; } From a147596acd51974273c45a6eef16fe3c8f1d2992 Mon Sep 17 00:00:00 2001 From: akallabeth Date: Fri, 10 Oct 2025 08:49:44 +0200 Subject: [PATCH 160/168] add cmake build for test --- TestAppExample/CMakeLists.txt | 38 +++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 TestAppExample/CMakeLists.txt diff --git a/TestAppExample/CMakeLists.txt b/TestAppExample/CMakeLists.txt new file mode 100644 index 00000000..5e3f924c --- /dev/null +++ b/TestAppExample/CMakeLists.txt @@ -0,0 +1,38 @@ +cmake_minimum_required(VERSION 3.16) +project(TestAppExample VERSION ${QTKEYCHAIN_VERSION} LANGUAGES CXX) + +include(GNUInstallDirs) + +# Enable C++11 +SET(CMAKE_CXX_STANDARD 11) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_AUTOUIC ON) + +option(BUILD_WITH_QT6 "Build qtkeychain with Qt 6" OFF) +if (BUILD_WITH_QT6) + set(QTx Qt6) +else() + set(QTx Qt5) +endif() + +find_package(${QTx}Keychain REQUIRED) +find_package(${QTx} COMPONENTS Core Network Quick Qml REQUIRED) + +if (BUILD_WITH_QT6) + include_directories(${QTKEYCHAIN_INCLUDE_DIRS}/qt6keychain) +else() + include_directories(${QTKEYCHAIN_INCLUDE_DIRS}/qt5keychain) +endif() + +qt_add_resources(QT_RESOURCES qml.qrc) +add_executable(${PROJECT_NAME} + keychainclass.h + keychainclass.cpp + main.cpp + ${QT_RESOURCES} +) + +target_link_libraries(${PROJECT_NAME} PRIVATE ${QTx}::Core ${QTx}::Network ${QTx}::Quick ${QTx}::Qml ${QTx}Keychain::${QTx}Keychain) +install(TARGETS ${PROJECT_NAME}) From d1c89cea112b3a6ec78559ee01b2c678df4fb79a Mon Sep 17 00:00:00 2001 From: akallabeth Date: Fri, 10 Oct 2025 02:53:53 +0200 Subject: [PATCH 161/168] improve display name for libsecret and gnome keyring display user@domain if a non empty user is given. This makes it easier to find an entry in e.g. seahorse as the display name is the one that is listed first. --- qtkeychain/keychain_unix.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/qtkeychain/keychain_unix.cpp b/qtkeychain/keychain_unix.cpp index a586ff8c..97b188cb 100644 --- a/qtkeychain/keychain_unix.cpp +++ b/qtkeychain/keychain_unix.cpp @@ -474,9 +474,16 @@ static void kwalletWritePasswordScheduledStart(const char *service, const char * void WritePasswordJobPrivate::scheduledStart() { + auto descr = service; + if (service.isEmpty()) { + descr = key; + } else if (!key.isEmpty()) { + descr = key + "@" + service; + } + switch (getKeyringBackend()) { case Backend_LibSecretKeyring: { - if (!LibSecretKeyring::writePassword(service, key, service, mode, data, this)) { + if (!LibSecretKeyring::writePassword(descr, key, service, mode, data, this)) { q->emitFinishedWithError(OtherError, tr("Unknown error")); } } break; @@ -497,7 +504,7 @@ void WritePasswordJobPrivate::scheduledStart() QByteArray service = q->service().toUtf8(); if (!GnomeKeyring::store_network_password( - GnomeKeyring::GNOME_KEYRING_DEFAULT, service.constData(), + GnomeKeyring::GNOME_KEYRING_DEFAULT, descr.toUtf8().constData(), key.toUtf8().constData(), service.constData(), type.toUtf8().constData(), password.constData(), reinterpret_cast( From 7ad9b0905ba1b958798b371d394ab324a1526d39 Mon Sep 17 00:00:00 2001 From: akallabeth Date: Fri, 10 Oct 2025 03:40:25 +0200 Subject: [PATCH 162/168] unit tests: add multiple usernames To improve test coverage write multiple keys for a service key. This way it can be tested if multiple keys are actually working without overwriting each other. --- autotest/basic.cpp | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/autotest/basic.cpp b/autotest/basic.cpp index f9f93235..0efe3f40 100644 --- a/autotest/basic.cpp +++ b/autotest/basic.cpp @@ -22,12 +22,14 @@ private Q_SLOTS: void test_data() { QTest::addColumn("password"); - QTest::newRow("normal password") << QByteArrayLiteral("this is a password"); - QTest::newRow("1000") << generateRandomString(1000); - QTest::newRow("2000") << generateRandomString(2000); - QTest::newRow("3000") << generateRandomString(3000); - QTest::newRow("10000") << generateRandomString(10000); - QTest::newRow("18944") << generateRandomString(18944); + QTest::addColumn("usernames"); + + QTest::newRow("normal password") << QByteArrayLiteral("this is a password") << QStringList{"", "user1", "user2"}; + QTest::newRow("1000") << generateRandomString(1000) << QStringList{"", "user1", "user2"}; + QTest::newRow("2000") << generateRandomString(2000)<< QStringList{"", "user1", "user2"}; + QTest::newRow("3000") << generateRandomString(3000)<< QStringList{"", "user1", "user2"}; + QTest::newRow("10000") << generateRandomString(10000)<< QStringList{"", "user1", "user2"}; + QTest::newRow("18944") << generateRandomString(18944)<< QStringList{"", "user1", "user2"}; } void test() @@ -37,9 +39,12 @@ private Q_SLOTS: #endif const QString serviceKey = QStringLiteral("QtKeychainTest-%1").arg(QTest::currentDataTag()); QFETCH(QByteArray, password); + QFETCH(QStringList, usernames); + + for (const auto& username : usernames) { QKeychain::WritePasswordJob writeJob(serviceKey); - writeJob.setKey(serviceKey); + writeJob.setKey(username); writeJob.setBinaryData(password); QSignalSpy writeSpy(&writeJob, &QKeychain::WritePasswordJob::finished); writeJob.start(); @@ -50,18 +55,22 @@ private Q_SLOTS: qDebug() << writeJob.errorString(); QCOMPARE(writeJob.error(), QKeychain::NoError); } + + for (const auto& username : usernames) { QKeychain::ReadPasswordJob readJob(serviceKey); - readJob.setKey(serviceKey); + readJob.setKey(username); QSignalSpy readSpy(&readJob, &QKeychain::ReadPasswordJob::finished); readJob.start(); readSpy.wait(); QCOMPARE(readJob.error(), QKeychain::NoError); QCOMPARE(readJob.binaryData(), password); } + + for (const auto& username : usernames) { QKeychain::DeletePasswordJob deleteJob(serviceKey); - deleteJob.setKey(serviceKey); + deleteJob.setKey(username); QSignalSpy deleteSpy(&deleteJob, &QKeychain::DeletePasswordJob::finished); deleteJob.start(); deleteSpy.wait(); From e2f045965bc22c30f5797514cb2212cae4b25cd3 Mon Sep 17 00:00:00 2001 From: akallabeth Date: Fri, 10 Oct 2025 04:29:33 +0200 Subject: [PATCH 163/168] job: abort if both service and key are empty From the job description both, service name and key can be empty strings. Since in that case there is no way to identify more than one entry assume this is invalid and return an error --- qtkeychain/keychain.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/qtkeychain/keychain.cpp b/qtkeychain/keychain.cpp index 93f338ca..6179a62f 100644 --- a/qtkeychain/keychain.cpp +++ b/qtkeychain/keychain.cpp @@ -79,6 +79,10 @@ void Job::emitFinishedWithError(Error error, const QString &errorString) void Job::scheduledStart() { + if (d->service.isEmpty() && d->key.isEmpty()) { + emitFinishedWithError(EntryNotFound, tr("Both service name and key are empty")); + return; + } d->scheduledStart(); } From 4cd1baa1892599f5757dba856f0466f4bc989bd1 Mon Sep 17 00:00:00 2001 From: akallabeth Date: Fri, 10 Oct 2025 04:12:15 +0200 Subject: [PATCH 164/168] unit tests: test empty service key Add tests that utilize an empty service key as is allowed by the API description --- autotest/basic.cpp | 68 ++++++++++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 26 deletions(-) diff --git a/autotest/basic.cpp b/autotest/basic.cpp index 0efe3f40..6feeea05 100644 --- a/autotest/basic.cpp +++ b/autotest/basic.cpp @@ -37,44 +37,60 @@ private Q_SLOTS: #ifdef Q_OS_MACOS QSKIP("This test case has no access to the keychain"); #endif - const QString serviceKey = QStringLiteral("QtKeychainTest-%1").arg(QTest::currentDataTag()); + const QStringList serviceKeys ={"", QStringLiteral("QtKeychainTest-%1").arg(QTest::currentDataTag())}; QFETCH(QByteArray, password); QFETCH(QStringList, usernames); - for (const auto& username : usernames) + for (const auto& serviceKey: serviceKeys) { - QKeychain::WritePasswordJob writeJob(serviceKey); - writeJob.setKey(username); - writeJob.setBinaryData(password); - QSignalSpy writeSpy(&writeJob, &QKeychain::WritePasswordJob::finished); - writeJob.start(); - writeSpy.wait(); + for (const auto& username : usernames) + { + QKeychain::WritePasswordJob writeJob(serviceKey); + writeJob.setKey(username); + writeJob.setBinaryData(username.toUtf8()+password); + QSignalSpy writeSpy(&writeJob, &QKeychain::WritePasswordJob::finished); + writeJob.start(); + writeSpy.wait(); #ifdef Q_OS_WIN - QEXPECT_FAIL("18944", "Maximum for Windows is exceeded", Abort); + QEXPECT_FAIL("18944", "Maximum for Windows is exceeded", Abort); #endif - qDebug() << writeJob.errorString(); - QCOMPARE(writeJob.error(), QKeychain::NoError); + qDebug() << "[write]" << writeJob.error() << ": " << writeJob.errorString(); + const auto expected = (serviceKey.isEmpty() && username.isEmpty()) ? QKeychain::EntryNotFound : QKeychain::NoError; + QCOMPARE(writeJob.error(), expected); + } } - for (const auto& username : usernames) + for (const auto& serviceKey: serviceKeys) { - QKeychain::ReadPasswordJob readJob(serviceKey); - readJob.setKey(username); - QSignalSpy readSpy(&readJob, &QKeychain::ReadPasswordJob::finished); - readJob.start(); - readSpy.wait(); - QCOMPARE(readJob.error(), QKeychain::NoError); - QCOMPARE(readJob.binaryData(), password); + for (const auto& username : usernames) + { + QKeychain::ReadPasswordJob readJob(serviceKey); + readJob.setKey(username); + QSignalSpy readSpy(&readJob, &QKeychain::ReadPasswordJob::finished); + readJob.start(); + readSpy.wait(); + qDebug() << "[read]" << readJob.error() << ": " << readJob.errorString(); + const auto expected = (serviceKey.isEmpty() && username.isEmpty()) ? QKeychain::EntryNotFound : QKeychain::NoError; + QCOMPARE(readJob.error(), expected); + if (expected == QKeychain::NoError) { + QCOMPARE(readJob.binaryData(), username.toUtf8()+password); + } + } } - for (const auto& username : usernames) + for (const auto& serviceKey: serviceKeys) { - QKeychain::DeletePasswordJob deleteJob(serviceKey); - deleteJob.setKey(username); - QSignalSpy deleteSpy(&deleteJob, &QKeychain::DeletePasswordJob::finished); - deleteJob.start(); - deleteSpy.wait(); - QCOMPARE(deleteJob.error(), QKeychain::NoError); + for (const auto& username : usernames) + { + QKeychain::DeletePasswordJob deleteJob(serviceKey); + deleteJob.setKey(username); + QSignalSpy deleteSpy(&deleteJob, &QKeychain::DeletePasswordJob::finished); + deleteJob.start(); + deleteSpy.wait(); + qDebug() << "[delete]" << deleteJob.error() << ": " << deleteJob.errorString(); + const auto expected = (serviceKey.isEmpty() && username.isEmpty()) ? QKeychain::EntryNotFound : QKeychain::NoError; + QCOMPARE(deleteJob.error(), expected); + } } } }; From 844beb4772d18b36f848c22df584d4fee4be40f1 Mon Sep 17 00:00:00 2001 From: Kang Lin Date: Fri, 10 Oct 2025 13:43:20 +0800 Subject: [PATCH 165/168] qmake: fix build error in windows --- qtkeychain.pri | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtkeychain.pri b/qtkeychain.pri index 4dc56d1e..caf434e2 100644 --- a/qtkeychain.pri +++ b/qtkeychain.pri @@ -78,7 +78,7 @@ win32 { DEFINES += USE_CREDENTIAL_STORE contains(DEFINES, USE_CREDENTIAL_STORE) { !build_pass:message("Windows Credential Store support: on") - LIBS += -ladvapi32 + LIBS += -ladvapi32 -lcrypt32 } else { !build_pass:message("Windows Credential Store support: off") LIBS += -lcrypt32 From 047a68cebd257a7aa60ff932200167fbbdf2395d Mon Sep 17 00:00:00 2001 From: Ekaterine Papava Date: Mon, 16 Feb 2026 08:39:11 +0400 Subject: [PATCH 166/168] i18n: Add Georgian translation --- translations/qtkeychain_ka.ts | 326 ++++++++++++++++++++++++++++++++++ 1 file changed, 326 insertions(+) create mode 100644 translations/qtkeychain_ka.ts diff --git a/translations/qtkeychain_ka.ts b/translations/qtkeychain_ka.ts new file mode 100644 index 00000000..8fee67be --- /dev/null +++ b/translations/qtkeychain_ka.ts @@ -0,0 +1,326 @@ + + + + + KeyChainClass + + + Read key failed: %1 + გასაღების წაკითხვა ჩავარდა: %1 + + + + Write key failed: %1 + გასაღების ჩაწერის შეცდომა: %1 + + + + Delete key failed: %1 + გასაღების წაშლის შეცდომა: %1 + + + + QKeychain::DeletePasswordJobPrivate + + + Could not open keystore + გასაღებების საცავის გახსნა შეუძლებელია + + + + Could not remove private key from keystore + გასაღებების საცავიდან პირადი გასაღების წაშლა შეუძლებელია + + + + Password not found + პაროლი აღმოჩენილი არაა + + + + + Unknown error + უცნობი შეცდომა + + + + Could not open wallet: %1; %2 + გახსნის შეცდომა საფულისთვის: %1; %2 + + + + Password entry not found + პაროლის ჩანაწერი აღმოჩენილი არაა + + + + Could not decrypt data + მონაცემების გაშიფვრის შეცდომა + + + + QKeychain::JobPrivate + + + Unknown error + უცნობი შეცდომა + + + + Access to keychain denied + ბრელოკთან წვდომა აკრძალულია + + + + QKeychain::PlainTextStore + + + Could not store data in settings: access error + მონაცემების პარამეტრებში შენახვა შეუძლებელია: წვდომის შეცდომა + + + + Could not store data in settings: format error + მონაცემების პარამეტრებში შენახვა შეუძლებელია: ფორმატის შეცდომა + + + + Could not delete data from settings: access error + პარამეტრებიდან მონაცემების წაშლის შეცდომა: წვდომის შეცდომა + + + + Could not delete data from settings: format error + პარამეტრებიდან მონაცემების წაშლის შეცდომა: ფორმატის შეცდომა + + + + Entry not found + ჩანაწერი ვერ ვიპოვე + + + + QKeychain::ReadPasswordJobPrivate + + + + Entry not found + ჩანაწერი ვერ ვიპოვე + + + + Could not open keystore + გასაღებების საცავის გახსნა შეუძლებელია + + + + Could not retrieve private key from keystore + გასაღებების საცავიდან პირადი გასაღების მიღება შეუძლებელია + + + + Could not create decryption cipher + გაშიფვრის შიფრის შექმნა შეუძლებელია + + + + Password not found + პაროლი აღმოჩენილი არაა + + + + D-Bus is not running + D-Bus is გაშვებული არაა + + + + + Unknown error + უცნობი შეცდომა + + + + No keychain service available + ბრელოკი სერვისი ხელმისაწვდომი არაა + + + + Could not open wallet: %1; %2 + გახსნის შეცდომა საფულისთვის: %1; %2 + + + + Access to keychain denied + ბრელოკთან წვდომა აკრძალულია + + + + Could not determine data type: %1; %2 + მონაცემთა ტიპის დადგენა შეუძლებელია: %1; %2 + + + + Unsupported entry type 'Map' + მხარდაუჭერელი ჩანაწერის ტიპი 'Map' + + + + Unknown kwallet entry type '%1' + უცნობი kwallet-ის ჩანაწერის ტიპი '%1' + + + + Password entry not found + პაროლის ჩანაწერი აღმოჩენილი არაა + + + + Could not decrypt data + მონაცემების გაშიფვრის შეცდომა + + + + + Could not decrypt data: %1 + მონაცემების გაშიფვრის შეცდომა: %1 + + + + QKeychain::WritePasswordJobPrivate + + + Could not open keystore + გასაღებების საცავის გახსნა შეუძლებელია + + + + Could not create private key generator + პირადი გასაღების გენერატორის შექმნა შეუძლებელია + + + + Could not generate new private key + ახალი პირადი გასაღების გენერაცია შეუძლებელია + + + + Could not retrieve private key from keystore + გასაღებების საცავიდან პირადი გასაღების მიღება შეუძლებელია + + + + Could not create encryption cipher + დაშიფვრის შიფრის შექმნა შეუძლებელია + + + + Could not encrypt data + მონაცემების დაშიფვრა შეუძლებელია + + + + Password not found + პაროლი აღმოჩენილი არაა + + + + D-Bus is not running + D-Bus is გაშვებული არაა + + + + + Unknown error + უცნობი შეცდომა + + + + Could not open wallet: %1; %2 + გახსნის შეცდომა საფულისთვის: %1; %2 + + + + + Encryption failed: %1 + დაშიფვრის შეცდომა: %1 + + + + Credential size exceeds maximum size of %1: %2 + ავტორიზაციის დეტალების ზომა %1-ზე მეტია: %2 + + + + Credential key exceeds maximum size of %1 + ავტორიზაციის დეტალების ზომა %1-ზე მეტია + + + + Writing credentials failed: %1 + ავტორიზაციის დეტალების ჩაწერა ჩავარდა: %1 + + + + QObject + + + error 0x%1: %2 + შეცდომა 0x%1: %2 + + + + Access to keychain denied + ბრელოკთან წვდომა აკრძალულია + + + + No keyring daemon + ბრელოკის დემონის გარეშე + + + + Already unlocked + უკვე განბლოკილია + + + + No such keyring + ასეთი ბრელოკი არ არსებობს + + + + Bad arguments + არასწორი არგუმენტები + + + + I/O error + I/O შეცდომა + + + + Cancelled + გაუქმებულია + + + + Keyring already exists + ბრელოკი უნდა არსებობს + + + + No match + დამთხვევის გარეშე + + + + Unknown error + უცნობი შეცდომა + + + + Entry not found + ჩანაწერი ვერ ვიპოვე + + + From 783320e73acbe9a6f6d119488fa41506e74bb070 Mon Sep 17 00:00:00 2001 From: Ekaterine Papava Date: Mon, 16 Feb 2026 05:41:37 +0100 Subject: [PATCH 167/168] i18n: Add Georgian translation --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 87a231de..f07d8b5c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -137,6 +137,7 @@ add_subdirectory(qtkeychain) set(qtkeychain_TR_FILES translations/qtkeychain_de.ts translations/qtkeychain_fr.ts + translations/qtkeychain_ka.ts translations/qtkeychain_ro.ts translations/qtkeychain_ru.ts translations/qtkeychain_sv.ts From 6e90eda69bd8c5e2f7dd64cd9a5d2392382b8525 Mon Sep 17 00:00:00 2001 From: Saif Khalifa <90653226+Saif-k93@users.noreply.github.com> Date: Fri, 20 Feb 2026 14:10:58 +0200 Subject: [PATCH 168/168] Fix restore-after-deletion issue by creating QKeychain jobs dynamically (#289) * Refactor keychainclass.h to use dynamic job creation Commented out persistent member variables for credential jobs. * Refactor KeyChainClass to use local credential jobs Refactor KeyChainClass to use local job instances for reading, writing, and deleting keys. Removed unused member jobs and added auto-delete functionality. --- TestAppExample/keychainclass.cpp | 86 +++++++++++++++++--------------- TestAppExample/keychainclass.h | 3 -- 2 files changed, 47 insertions(+), 42 deletions(-) diff --git a/TestAppExample/keychainclass.cpp b/TestAppExample/keychainclass.cpp index 3e26592a..696d5e84 100644 --- a/TestAppExample/keychainclass.cpp +++ b/TestAppExample/keychainclass.cpp @@ -1,64 +1,72 @@ +// fix for https://github.com/frankosterfeld/qtkeychain/issues/288 + #include #include "keychainclass.h" +static const QString service = "keychain.example.project.app"; + KeyChainClass::KeyChainClass(QObject *parent) - : QObject(parent), - m_readCredentialJob(QLatin1String("keychain.example.project.app")), - m_writeCredentialJob(QLatin1String("keychain.example.project.app")), - m_deleteCredentialJob(QLatin1String("keychain.example.project.app")) + : QObject(parent) { - m_readCredentialJob.setAutoDelete(false); - m_writeCredentialJob.setAutoDelete(false); - m_deleteCredentialJob.setAutoDelete(false); + } void KeyChainClass::readKey(const QString &key) { - m_readCredentialJob.setKey(key); + auto readCredentialJob = new QKeychain::ReadPasswordJob(service); + readCredentialJob->setAutoDelete(true); + readCredentialJob->setKey(key); - QObject::connect(&m_readCredentialJob, &QKeychain::ReadPasswordJob::finished, [=]() { - if (m_readCredentialJob.error()) { - emit error( - tr("Read key failed: %1").arg(qPrintable(m_readCredentialJob.errorString()))); - return; - } - emit keyRestored(key, m_readCredentialJob.textData()); - }); + QObject::connect(readCredentialJob, &QKeychain::ReadPasswordJob::finished, this, + [this, key](QKeychain::Job *job) { + auto j = static_cast(job); + if (j->error() == QKeychain::NoError) { + emit keyRestored(key, j->textData()); + } else { + emit error(tr("Read key failed: %1").arg(qPrintable(j->errorString()))); + } + // no delete needed, autoDelete takes care of it + }); - m_readCredentialJob.start(); + readCredentialJob->start(); } void KeyChainClass::writeKey(const QString &key, const QString &value) { - m_writeCredentialJob.setKey(key); - - QObject::connect(&m_writeCredentialJob, &QKeychain::WritePasswordJob::finished, [=]() { - if (m_writeCredentialJob.error()) { - emit error( - tr("Write key failed: %1").arg(qPrintable(m_writeCredentialJob.errorString()))); - return; - } + auto writeCredentialJob = new QKeychain::WritePasswordJob(service); + writeCredentialJob->setAutoDelete(true); + writeCredentialJob->setKey(key); + writeCredentialJob->setTextData(value); - emit keyStored(key); - }); + QObject::connect(writeCredentialJob, &QKeychain::WritePasswordJob::finished, this, + [this, key](QKeychain::Job *job) { + auto j = static_cast(job); + if (j->error() == QKeychain::NoError) { + emit keyStored(key); + } else { + emit error(tr("Write key failed: %1").arg(qPrintable(j->errorString()))); + } + }); - m_writeCredentialJob.setTextData(value); - m_writeCredentialJob.start(); + writeCredentialJob->start(); } void KeyChainClass::deleteKey(const QString &key) { - m_deleteCredentialJob.setKey(key); + auto deleteCredentialJob = new QKeychain::DeletePasswordJob(service); + deleteCredentialJob->setAutoDelete(true); + deleteCredentialJob->setKey(key); - QObject::connect(&m_deleteCredentialJob, &QKeychain::DeletePasswordJob::finished, [=]() { - if (m_deleteCredentialJob.error()) { - emit error(tr("Delete key failed: %1") - .arg(qPrintable(m_deleteCredentialJob.errorString()))); - return; - } - emit keyDeleted(key); - }); + QObject::connect(deleteCredentialJob, &QKeychain::DeletePasswordJob::finished, this, + [this, key](QKeychain::Job *job) { + auto j = static_cast(job); + if (j->error() == QKeychain::NoError) { + emit keyDeleted(key); + } else { + emit error(tr("Delete key failed: %1").arg(qPrintable(j->errorString()))); + } + }); - m_deleteCredentialJob.start(); + deleteCredentialJob->start(); } diff --git a/TestAppExample/keychainclass.h b/TestAppExample/keychainclass.h index c8aedf18..100e0ea4 100644 --- a/TestAppExample/keychainclass.h +++ b/TestAppExample/keychainclass.h @@ -22,9 +22,6 @@ class KeyChainClass : public QObject void error(const QString &errorText); private: - QKeychain::ReadPasswordJob m_readCredentialJob; - QKeychain::WritePasswordJob m_writeCredentialJob; - QKeychain::DeletePasswordJob m_deleteCredentialJob; }; #endif // KEYCHAINCLASS_H