aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md33
-rw-r--r--doc/qtcreator/src/overview/creator-acknowledgements.qdoc14
-rw-r--r--doc/qtcreator/src/overview/license-bsd-3-clause.qdocinc29
-rw-r--r--src/libs/3rdparty/CMakeLists.txt1
-rw-r--r--src/libs/3rdparty/qtkeychain/CMakeLists.txt66
-rw-r--r--src/libs/3rdparty/qtkeychain/CMakeLists.txt.upstream198
-rw-r--r--src/libs/3rdparty/qtkeychain/COPYING23
-rw-r--r--src/libs/3rdparty/qtkeychain/ChangeLog109
-rw-r--r--src/libs/3rdparty/qtkeychain/QtKeychainConfig.cmake.in28
-rw-r--r--src/libs/3rdparty/qtkeychain/ReadMe.md29
-rw-r--r--src/libs/3rdparty/qtkeychain/cmake/Modules/ECMPackageConfigHelpers.cmake202
-rw-r--r--src/libs/3rdparty/qtkeychain/cmake/Modules/ECMQueryQt.cmake100
-rw-r--r--src/libs/3rdparty/qtkeychain/cmake/Modules/ECMSetupVersion.cmake202
-rw-r--r--src/libs/3rdparty/qtkeychain/cmake/Modules/QtVersionOption.cmake36
-rw-r--r--src/libs/3rdparty/qtkeychain/gnomekeyring.cpp86
-rw-r--r--src/libs/3rdparty/qtkeychain/gnomekeyring_p.h94
-rw-r--r--src/libs/3rdparty/qtkeychain/keychain.cpp235
-rw-r--r--src/libs/3rdparty/qtkeychain/keychain.h282
-rw-r--r--src/libs/3rdparty/qtkeychain/keychain_apple.mm263
-rw-r--r--src/libs/3rdparty/qtkeychain/keychain_p.h167
-rw-r--r--src/libs/3rdparty/qtkeychain/keychain_unix.cpp633
-rw-r--r--src/libs/3rdparty/qtkeychain/keychain_win.cpp193
-rw-r--r--src/libs/3rdparty/qtkeychain/libsecret.cpp349
-rw-r--r--src/libs/3rdparty/qtkeychain/libsecret_p.h33
-rw-r--r--src/libs/3rdparty/qtkeychain/org.kde.KWallet.xml276
-rw-r--r--src/libs/3rdparty/qtkeychain/plaintextstore.cpp110
-rw-r--r--src/libs/3rdparty/qtkeychain/plaintextstore_p.h47
-rw-r--r--src/libs/3rdparty/qtkeychain/qkeychain_export.h11
-rw-r--r--src/libs/3rdparty/qtkeychain/translations/qtkeychain_de.ts234
-rw-r--r--src/libs/3rdparty/qtkeychain/translations/qtkeychain_fr.ts226
-rw-r--r--src/libs/3rdparty/qtkeychain/translations/qtkeychain_nl.ts320
-rw-r--r--src/libs/3rdparty/qtkeychain/translations/qtkeychain_ro.ts235
-rw-r--r--src/libs/3rdparty/qtkeychain/translations/qtkeychain_ru.ts234
-rw-r--r--src/libs/3rdparty/qtkeychain/translations/qtkeychain_zh.ts234
-rw-r--r--src/libs/3rdparty/qtkeychain/translations/translations.qrc.in5
35 files changed, 5337 insertions, 0 deletions
diff --git a/README.md b/README.md
index 70500da736d..98504b7c751 100644
--- a/README.md
+++ b/README.md
@@ -1003,3 +1003,36 @@ SQLite (https://www.sqlite.org) is in the Public Domain.
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.
+
+### QtKeychain
+
+ QtKeychain provides the Axivion plugin with the means to securely store
+ and retrieve dashboard credentials.
+
+ https://github.com/frankosterfeld/qtkeychain
+
+ Distributed under the Modified BSD License
+
+ 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 above copyright
+ notice, this list of conditions and the following disclaimer.
+ 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
+ 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.
diff --git a/doc/qtcreator/src/overview/creator-acknowledgements.qdoc b/doc/qtcreator/src/overview/creator-acknowledgements.qdoc
index a9773753b67..5c7a8371e05 100644
--- a/doc/qtcreator/src/overview/creator-acknowledgements.qdoc
+++ b/doc/qtcreator/src/overview/creator-acknowledgements.qdoc
@@ -976,5 +976,19 @@
\endcode
+ \li \b QtKeychain
+
+ QtKeychain is a platform-independent Qt API to store passwords and
+ other secret data securely.
+
+
+ \list
+ \li \l https://github.com/frankosterfeld/qtkeychain
+ \endlist
+
+ Distributed under the Modified BSD License.
+
+ \include license-bsd-3-clause.qdocinc
+
\endlist
*/
diff --git a/doc/qtcreator/src/overview/license-bsd-3-clause.qdocinc b/doc/qtcreator/src/overview/license-bsd-3-clause.qdocinc
new file mode 100644
index 00000000000..3f55574fef0
--- /dev/null
+++ b/doc/qtcreator/src/overview/license-bsd-3-clause.qdocinc
@@ -0,0 +1,29 @@
+ \badcode
+ 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 above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (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 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.
+ \endcode
diff --git a/src/libs/3rdparty/CMakeLists.txt b/src/libs/3rdparty/CMakeLists.txt
index 0cf9818ed54..165c5f5ca02 100644
--- a/src/libs/3rdparty/CMakeLists.txt
+++ b/src/libs/3rdparty/CMakeLists.txt
@@ -2,6 +2,7 @@ add_subdirectory(cplusplus)
add_subdirectory(syntax-highlighting)
add_subdirectory(libvterm)
add_subdirectory(libptyqt)
+add_subdirectory(qtkeychain)
if(WIN32)
add_subdirectory(winpty)
diff --git a/src/libs/3rdparty/qtkeychain/CMakeLists.txt b/src/libs/3rdparty/qtkeychain/CMakeLists.txt
new file mode 100644
index 00000000000..d8ba7e3178b
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/CMakeLists.txt
@@ -0,0 +1,66 @@
+add_qtc_library(qtkeychain
+ DEPENDS Qt::Core
+ SOURCES
+ keychain.cpp keychain.h
+ qkeychain_export.h
+ PROPERTIES
+ QT_COMPILE_OPTIONS_DISABLE_WARNINGS ON
+)
+
+if (WIN32)
+ option(USE_CREDENTIAL_STORE "Build with windows CredentialStore support" ON)
+
+ extend_qtc_library(qtkeychain SOURCES keychain_win.cpp)
+
+ extend_qtc_library(qtkeychain
+ CONDITION USE_CREDENTIAL_STORE
+ FEATURE_INFO "CredentialStore keychain support"
+ DEFINES USE_CREDENTIAL_STORE=1
+ )
+ extend_qtc_library(qtkeychain
+ CONDITION NOT USE_CREDENTIAL_STORE
+ SOURCES plaintextstore.cpp
+ DEPENDS crypt32
+ )
+ endif()
+endif()
+
+extend_qtc_library(qtkeychain
+ CONDITION APPLE
+ SOURCES keychain_apple.mm
+ DEPENDS ${FWFoundation} ${FWSecurity}
+)
+
+if (UNIX AND NOT APPLE)
+ find_package(Qt6 COMPONENTS DBus REQUIRED)
+
+ option(LIBSECRET_SUPPORT "Build with libsecret support if available" ON)
+ if (LIBSECRET_SUPPORT)
+ find_package(PkgConfig REQUIRED)
+
+ include(FindPkgConfig)
+ pkg_check_modules(LIBSECRET libsecret-1)
+
+ extend_qtc_library(qtkeychain
+ CONDITION LIBSECRET_FOUND
+ FEATURE_INFO "libsecret keychain support"
+ DEFINES HAVE_LIBSECRET=1
+ INCLUDES ${LIBSECRET_INCLUDE_DIRS}
+ DEPENDS ${LIBSECRET_LIBRARIES}
+ )
+ endif()
+
+ qt6_add_dbus_interface(dbus_SOURCES
+ ${CMAKE_CURRENT_SOURCE_DIR}/org.kde.KWallet.xml kwallet_interface KWalletInterface)
+
+ extend_qtc_library(qtkeychain
+ DEFINES KEYCHAIN_DBUS=1
+ DEPENDS Qt::DBus
+ SOURCES
+ gnomekeyring.cpp
+ keychain_unix.cpp
+ libsecret.cpp
+ plaintextstore.cpp
+ ${dbus_SOURCES}
+ )
+endif()
diff --git a/src/libs/3rdparty/qtkeychain/CMakeLists.txt.upstream b/src/libs/3rdparty/qtkeychain/CMakeLists.txt.upstream
new file mode 100644
index 00000000000..3b4e30d1295
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/CMakeLists.txt.upstream
@@ -0,0 +1,198 @@
+set(QTKEYCHAIN_VERSION 0.14.99)
+set(QTKEYCHAIN_SOVERSION 1)
+
+project(qtkeychain VERSION ${QTKEYCHAIN_VERSION} LANGUAGES CXX)
+
+include(FindPkgConfig)
+
+###
+
+set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${PROJECT_SOURCE_DIR}/cmake/Modules")
+include(GNUInstallDirs)
+include(GenerateExportHeader)
+include(CMakePackageConfigHelpers)
+include(ECMSetupVersion)
+include(CMakeDependentOption)
+
+option(BUILD_TRANSLATIONS "Build translations" ON)
+option(BUILD_SHARED_LIBS "Build dynamic library" OFF)
+
+CMAKE_DEPENDENT_OPTION(BUILD_TRANSLATIONS_AS_RESOURCES "Bundle translations with the library" OFF
+ "BUILD_TRANSLATIONS" OFF)
+
+if (WIN32)
+ option(USE_CREDENTIAL_STORE "Build with windows CredentialStore support" ON)
+
+ if (USE_CREDENTIAL_STORE)
+ add_definitions(-DUSE_CREDENTIAL_STORE=1)
+ endif()
+endif()
+
+
+find_package(Qt6 COMPONENTS Core REQUIRED)
+
+if(UNIX AND NOT APPLE)
+ find_package(Qt6 COMPONENTS DBus REQUIRED)
+ include_directories(${Qt6DBus_INCLUDE_DIRS})
+ set(QTDBUS_LIBRARIES ${Qt6DBus_LIBRARIES})
+endif()
+
+if(BUILD_TRANSLATIONS)
+ find_package(Qt6 COMPONENTS LinguistTools REQUIRED)
+endif()
+
+set(QTCORE_LIBRARIES ${Qt6Core_LIBRARIES})
+
+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(UNIX AND NOT APPLE)
+ option(LIBSECRET_SUPPORT "Build with libsecret support if available" ON)
+ find_package(PkgConfig REQUIRED)
+ pkg_check_modules(LIBSECRET libsecret-1)
+
+ if(LIBSECRET_SUPPORT AND LIBSECRET_FOUND)
+ 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)
+ qt6_add_dbus_interface(qtkeychain_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/org.kde.KWallet.xml kwallet_interface KWalletInterface)
+ list(APPEND qtkeychain_LIBRARIES ${QTDBUS_LIBRARIES} )
+endif()
+
+qt6_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
+)
+
+set(QTKEYCHAIN_TARGET_NAME qtkeychain)
+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()
+
+file(GLOB qtkeychain_TR_SOURCES *.cpp *.h *.ui)
+if ( BUILD_TRANSLATIONS )
+ qt6_create_translation(qtkeychain_MESSAGES ${qtkeychain_TR_SOURCES} ${qtkeychain_TR_FILES})
+ qt6_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 (BUILD_TRANSLATIONS_AS_RESOURCES)
+ set(QM_FILE_LIST "")
+ foreach(FILE ${qtkeychain_QM_FILES})
+ list(APPEND QM_FILE_LIST "<file>${FILE}</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}/qtkeychain/translations
+ CACHE PATH "The location of the QtKeychain translations" )
+ install(FILES ${qtkeychain_QM_FILES} DESTINATION ${QTKEYCHAIN_TRANSLATIONS_DIR})
+ 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 $<INSTALL_INTERFACE:${INTERFACE_INCLUDE_SUFFIX}/>)
+
+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}/qtkeychain/
+)
+
+install(TARGETS ${QTKEYCHAIN_TARGET_NAME}
+ EXPORT QtKeychainLibraryDepends
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+###
+### CMake config file
+###
+
+configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/QtKeychainConfig.cmake.in"
+ "${CMAKE_CURRENT_BINARY_DIR}/QtKeychainConfig.cmake"
+ INSTALL_DESTINATION QtKeychain)
+
+ecm_setup_version("${QTKEYCHAIN_VERSION}" VARIABLE_PREFIX SNORE
+ PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/QtKeychainConfigVersion.cmake"
+ SOVERSION ${QTKEYCHAIN_VERSION})
+
+install(EXPORT QtKeychainLibraryDepends
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/QtKeychain"
+)
+
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/QtKeychainConfig.cmake
+ ${CMAKE_CURRENT_BINARY_DIR}/QtKeychainConfigVersion.cmake
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/QtKeychain
+)
+
diff --git a/src/libs/3rdparty/qtkeychain/COPYING b/src/libs/3rdparty/qtkeychain/COPYING
new file mode 100644
index 00000000000..69f70fff716
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/COPYING
@@ -0,0 +1,23 @@
+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 above copyright
+ notice, this list of conditions and the following disclaimer.
+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
+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.
diff --git a/src/libs/3rdparty/qtkeychain/ChangeLog b/src/libs/3rdparty/qtkeychain/ChangeLog
new file mode 100644
index 00000000000..cb383c52968
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/ChangeLog
@@ -0,0 +1,109 @@
+ChangeLog
+=========
+
+version 0.14.1 (release 2023-06-01)
+
+ - Export QKeychain::isAvailable() to make it usable in a shared build (Volker Krause <vkrause@kde.org>)
+ - Protect against creating the QtKeychain::QtKeychain alias target twice (Volker Krause <vkrause@kde.org>)
+
+version 0.14.0 (release 2023-05-12)
+
+ - Add Qt 6 Android support (Igor Bugaev <freedbrt@gmail.com>)
+ - Add QtQuick client example ((Igor Bugaev <freedbrt@gmail.com>)
+ - Added Dutch translation (Heimen Stoffels <vistausss@fastmail.com>)
+ - Fix potential freezing with Apple keychain (Claudio Cambra <developer@claudiocambra.com>)
+ - Add API to check whether a secure backend is available at all (Volker Krause <vkrause@kde.org>)
+
+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)
+
+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
+ * Require C++11
+ * Add Android support (Mathias Hasselmann)
+
+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
+ * Various build system fixes
+ * Add Haiku support (François Revol <revol@free.fr>)
+ * Translation: Russian (Alexander Gorishnyak <kefir500@gmail.com>)
+ * Translation: Update French (David Geiger <david.david@mageialinux-online.org>)
+
+version 0.10.0 (release 2019-12-17)
+
+ * Detect XFCE desktop correctly. (Sandro Knauß <hefee@debian.org>)
+ * Windows Use CRED_PERSIST_ENTERPRISE (Olivier Goffart <ogoffart@woboq.com>)
+ * Windows: Improve CredWrite() error handling (Christian Kamm <mail@ckamm.de>)
+ * Fix build with Qt 5.12.x (Sergey Ilinykh <rion4ik@gmail.com>)
+ * Fix Qt 4 build (Robert-André Mauchin <zebob.m@gmail.com>)
+ * Translation: Mandarin (Taiwan) (Poren Chiang <ren.chiang@gmail.com>)
+ * Translation: French (François Revol <revol@free.fr>)
+
+version 0.9.1 (release 2018-08-20)
+ * Windows Credential Store: Use CRED_PERSIST_ENTERPRISE (Olivier Goffart <ogoffart@woboq.com>)
+ * Secret: Don't match the schema name #114 (Christian Kamm <mail@ckamm.de>)
+ * Fix qmake build on Windows (Alexander Gorishnyak <kefir500@gmail.com>)
+
+version 0.9.0 (release 2018-07-13)
+ * Fall back on libsecret if kwallet is not available (Christian Kamm <mail@ckamm.de>)
+ * Only require QtLinguist if building translations (Victor Kropp <victor.kropp@jetbrains.com>)
+ * Fix building on Windows without credential store (Dmitry Ivanov <dm.vl.ivanov@gmail.com>)
+ * Fix Qt 4 build (Sandro Knauß <hefee@debian.org>)
+ * Make build of test application optional (Boris Pek <tehnick-8@yandex.ru>)
+
+version 0.8.0 (release 2017-04-19)
+ * Buildsystem improvements (Kristofer Tingdahl <kristofer.tingdahl@dgbes.com>, Hannah von Reth <hannah.vonreth@kdab.com>, Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>)
+ * Enable C++11 support for Qt >= 5.7 (Dmitry Ivanov <dm.vl.ivanov@gmail.com>)
+ * Doxygen documentation ( Elvis Angelaccio <elvis.angelaccio@kdemail.net>)
+ * Libsecret support (Armin Novak <armin.novak@thincast.com>)
+ * iOS support (Mathias Hasselmann <mathias.hasselmann@kdab.com>)
+
+version 0.7.0 (release 2016-05-23)
+ * Bump SO version due to 0.6 being binary-incompatible to previous releases
+
+version 0.6.2 (release 2016-04-04)
+ * KWallet: Fixes a crash when storing passwords, seen on Debian/KDE4
+
+version 0.6.1 (release 2016-03-31)
+ * Fix KWallet not working (regressions in 0.6.0)
+
+version 0.6.0 (release 2016-03-18)
+ * Added support for the Windows Credential Store
+
+version 0.5.0 (release 2015-05-04)
+ * Added support for KWallet5 (KDE5/KF)
+
+version 0.4.0 (release 2014-09-01)
+ * KWallet: Handle case where no wallet exists yet (Liviu Cristian Mirea Ghiban <contact@liviucmg.com>)
+ * Improved desktop environment detection at runtime (Daniel Molkentin <daniel@molkentin.de>)
+
+version 0.3.0 (release 2014-03-13)
+ * Gnome Keyring supported added (Francois Ferrand <thetypz@gmail.com>)
+ * Improved Qt 5 support
+ * KWallet: Distinguish empty passwords from non-existing entries
+ * KWallet: Do not use hardcoded wallet name
+ * German translation (Daniel Molkentin <daniel@molkentin.de>)
+ * Romanian translation (Arthur Țițeică <arthur@psw.ro>)
+
+version 0.2.0: no official release
+
+version 0.1.0 (release 2013-01-16)
+ * Initial release
+
diff --git a/src/libs/3rdparty/qtkeychain/QtKeychainConfig.cmake.in b/src/libs/3rdparty/qtkeychain/QtKeychainConfig.cmake.in
new file mode 100644
index 00000000000..084d45e57f0
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/QtKeychainConfig.cmake.in
@@ -0,0 +1,28 @@
+# - Config file for the QtKeychain package
+# 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@
+
+include("${CMAKE_CURRENT_LIST_DIR}/QtKeychainLibraryDepends.cmake")
+
+include(CMakeFindDependencyMacro)
+
+find_dependency(Qt6Core)
+
+if(UNIX AND NOT APPLE AND NOT ANDROID)
+ find_dependency(Qt6DBus)
+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 AND NOT TARGET QtKeychain::QtKeychain)
+ add_library(QtKeychain::QtKeychain ALIAS qtkeychain)
+endif()
+
+check_required_components(QtKeychain)
diff --git a/src/libs/3rdparty/qtkeychain/ReadMe.md b/src/libs/3rdparty/qtkeychain/ReadMe.md
new file mode 100644
index 00000000000..cf0a2e1311a
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/ReadMe.md
@@ -0,0 +1,29 @@
+QtKeychain
+==========
+
+QtKeychain is a Qt API to store passwords and other secret data securely. How the data is stored depends on the platform:
+
+ * **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)
+ is also supported.
+
+ * **Windows:** By default, the Windows Credential Store is used (requires Windows 7 or newer).
+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 )`).
+
+
+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.
diff --git a/src/libs/3rdparty/qtkeychain/cmake/Modules/ECMPackageConfigHelpers.cmake b/src/libs/3rdparty/qtkeychain/cmake/Modules/ECMPackageConfigHelpers.cmake
new file mode 100644
index 00000000000..8d48772b70d
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/cmake/Modules/ECMPackageConfigHelpers.cmake
@@ -0,0 +1,202 @@
+#.rst:
+# ECMPackageConfigHelpers
+# -----------------------
+#
+# Helper macros for generating CMake package config files.
+#
+# ``write_basic_package_version_file()`` is the same as the one provided by the
+# `CMakePackageConfigHelpers
+# <https://www.cmake.org/cmake/help/v2.8.12/cmake.html#module:CMakePackageConfigHelpers>`_
+# module in CMake; see that module's documentation for
+# more information.
+#
+# ::
+#
+# ecm_configure_package_config_file(<input> <output>
+# INSTALL_DESTINATION <path>
+# [PATH_VARS <var1> [<var2> [...]]
+# [NO_SET_AND_CHECK_MACRO]
+# [NO_CHECK_REQUIRED_COMPONENTS_MACRO])
+#
+#
+# This behaves in the same way as configure_package_config_file() from CMake
+# 2.8.12, except that it adds an extra helper macro: find_dependency(). It is
+# highly recommended that you read the `documentation for
+# CMakePackageConfigHelpers
+# <https://www.cmake.org/cmake/help/v2.8.12/cmake.html#module: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;
+# if you do not require this macro, you should use
+# ``configure_package_config_file`` from the CMakePackageConfigHelpers module.
+#
+# CMake 3.0 includes a CMakeFindDependencyMacro module that provides the
+# find_dependency() macro (which you can ``include()`` in your package config
+# file), so this file is only useful for projects wishing to provide config
+# files that will work with CMake 2.8.12.
+#
+# Additional Config File Macros
+# =============================
+#
+# ::
+#
+# find_dependency(<dep> [<version> [EXACT]])
+#
+# find_dependency() should be used instead of find_package() to find package
+# dependencies. It forwards the correct parameters for EXACT, QUIET and
+# REQUIRED which were passed to the original find_package() call. It also sets
+# an informative diagnostic message if the dependency could not be found.
+#
+# Since pre-1.0.0.
+
+#=============================================================================
+# SPDX-FileCopyrightText: 2014 Alex Merry <alex.merry@kdemail.net>
+# SPDX-FileCopyrightText: 2013 Stephen Kelly <steveire@gmail.com>
+#
+# SPDX-License-Identifier: BSD-3-Clause
+
+include(${CMAKE_ROOT}/Modules/CMakePackageConfigHelpers.cmake)
+
+set(_ecm_package_config_helpers_included TRUE)
+
+if(NOT CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.13)
+ message(AUTHOR_WARNING "Your project already requires a version of CMake that includes the find_dependency macro via the CMakeFindDependencyMacro module. You should use CMakePackageConfigHelpers instead of ECMPackageConfigHelpers.")
+endif()
+
+function(ECM_CONFIGURE_PACKAGE_CONFIG_FILE _inputFile _outputFile)
+ set(options NO_SET_AND_CHECK_MACRO NO_CHECK_REQUIRED_COMPONENTS_MACRO)
+ set(oneValueArgs INSTALL_DESTINATION )
+ set(multiValueArgs PATH_VARS )
+
+ cmake_parse_arguments(CCF "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ if(CCF_UNPARSED_ARGUMENTS)
+ message(FATAL_ERROR "Unknown keywords given to CONFIGURE_PACKAGE_CONFIG_FILE(): \"${CCF_UNPARSED_ARGUMENTS}\"")
+ endif()
+
+ if(NOT CCF_INSTALL_DESTINATION)
+ message(FATAL_ERROR "No INSTALL_DESTINATION given to CONFIGURE_PACKAGE_CONFIG_FILE()")
+ endif()
+
+ if(IS_ABSOLUTE "${CCF_INSTALL_DESTINATION}")
+ set(absInstallDir "${CCF_INSTALL_DESTINATION}")
+ else()
+ set(absInstallDir "${CMAKE_INSTALL_PREFIX}/${CCF_INSTALL_DESTINATION}")
+ endif()
+
+ file(RELATIVE_PATH PACKAGE_RELATIVE_PATH "${absInstallDir}" "${CMAKE_INSTALL_PREFIX}" )
+
+ foreach(var ${CCF_PATH_VARS})
+ if(NOT DEFINED ${var})
+ message(FATAL_ERROR "Variable ${var} does not exist")
+ else()
+ if(IS_ABSOLUTE "${${var}}")
+ string(REPLACE "${CMAKE_INSTALL_PREFIX}" "\${PACKAGE_PREFIX_DIR}"
+ PACKAGE_${var} "${${var}}")
+ else()
+ set(PACKAGE_${var} "\${PACKAGE_PREFIX_DIR}/${${var}}")
+ endif()
+ endif()
+ endforeach()
+
+ get_filename_component(inputFileName "${_inputFile}" NAME)
+
+ set(PACKAGE_INIT "
+####### Expanded from @PACKAGE_INIT@ by configure_package_config_file() (ECM variant) #######
+####### Any changes to this file will be overwritten by the next CMake run #######
+####### The input file was ${inputFileName} #######
+
+get_filename_component(PACKAGE_PREFIX_DIR \"\${CMAKE_CURRENT_LIST_DIR}/${PACKAGE_RELATIVE_PATH}\" ABSOLUTE)
+")
+
+ if("${absInstallDir}" MATCHES "^(/usr)?/lib(64)?/.+")
+ # Handle "/usr move" symlinks created by some Linux distros.
+ set(PACKAGE_INIT "${PACKAGE_INIT}
+# Use original install prefix when loaded through a \"/usr move\"
+# cross-prefix symbolic link such as /lib -> /usr/lib.
+get_filename_component(_realCurr \"\${CMAKE_CURRENT_LIST_DIR}\" REALPATH)
+get_filename_component(_realOrig \"${absInstallDir}\" REALPATH)
+if(_realCurr STREQUAL _realOrig)
+ set(PACKAGE_PREFIX_DIR \"${CMAKE_INSTALL_PREFIX}\")
+endif()
+unset(_realOrig)
+unset(_realCurr)
+")
+ endif()
+
+ if(NOT CCF_NO_SET_AND_CHECK_MACRO)
+ set(PACKAGE_INIT "${PACKAGE_INIT}
+macro(set_and_check _var _file)
+ set(\${_var} \"\${_file}\")
+ if(NOT EXISTS \"\${_file}\")
+ message(FATAL_ERROR \"File or directory \${_file} referenced by variable \${_var} does not exist !\")
+ endif()
+endmacro()
+
+include(CMakeFindDependencyMacro OPTIONAL RESULT_VARIABLE _CMakeFindDependencyMacro_FOUND)
+
+if (NOT _CMakeFindDependencyMacro_FOUND)
+ macro(find_dependency dep)
+ if (NOT \${dep}_FOUND)
+
+ set(ecm_fd_version)
+ if (\${ARGC} GREATER 1)
+ set(ecm_fd_version \${ARGV1})
+ endif()
+ set(ecm_fd_exact_arg)
+ if(\${CMAKE_FIND_PACKAGE_NAME}_FIND_VERSION_EXACT)
+ set(ecm_fd_exact_arg EXACT)
+ endif()
+ set(ecm_fd_quiet_arg)
+ if(\${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY)
+ set(ecm_fd_quiet_arg QUIET)
+ endif()
+ set(ecm_fd_required_arg)
+ if(\${CMAKE_FIND_PACKAGE_NAME}_FIND_REQUIRED)
+ set(ecm_fd_required_arg REQUIRED)
+ endif()
+
+ find_package(\${dep} \${ecm_fd_version}
+ \${ecm_fd_exact_arg}
+ \${ecm_fd_quiet_arg}
+ \${ecm_fd_required_arg}
+ )
+
+ if (NOT \${dep}_FOUND)
+ set(\${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE \"\${CMAKE_FIND_PACKAGE_NAME} could not be found because dependency \${dep} could not be found.\")
+ set(\${CMAKE_FIND_PACKAGE_NAME}_FOUND False)
+ return()
+ endif()
+
+ set(ecm_fd_version)
+ set(ecm_fd_required_arg)
+ set(ecm_fd_quiet_arg)
+ set(ecm_fd_exact_arg)
+ endif()
+ endmacro()
+endif()
+
+")
+ endif()
+
+
+ if(NOT CCF_NO_CHECK_REQUIRED_COMPONENTS_MACRO)
+ set(PACKAGE_INIT "${PACKAGE_INIT}
+macro(check_required_components _NAME)
+ foreach(comp \${\${_NAME}_FIND_COMPONENTS})
+ if(NOT \${_NAME}_\${comp}_FOUND)
+ if(\${_NAME}_FIND_REQUIRED_\${comp})
+ set(\${_NAME}_FOUND FALSE)
+ endif()
+ endif()
+ endforeach()
+endmacro()
+")
+ endif()
+
+ set(PACKAGE_INIT "${PACKAGE_INIT}
+####################################################################################")
+
+ configure_file("${_inputFile}" "${_outputFile}" @ONLY)
+
+endfunction()
diff --git a/src/libs/3rdparty/qtkeychain/cmake/Modules/ECMQueryQt.cmake b/src/libs/3rdparty/qtkeychain/cmake/Modules/ECMQueryQt.cmake
new file mode 100644
index 00000000000..98eb50089e4
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/cmake/Modules/ECMQueryQt.cmake
@@ -0,0 +1,100 @@
+# SPDX-FileCopyrightText: 2014 Rohan Garg <rohan16garg@gmail.com>
+# SPDX-FileCopyrightText: 2014 Alex Merry <alex.merry@kde.org>
+# SPDX-FileCopyrightText: 2014-2016 Aleix Pol <aleixpol@kde.org>
+# SPDX-FileCopyrightText: 2017 Friedrich W. H. Kossebau <kossebau@kde.org>
+# SPDX-FileCopyrightText: 2022 Ahmad Samir <a.samir78@gmail.com>
+#
+# 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(<result_variable> <qt_variable> [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 ``<prefix>/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/src/libs/3rdparty/qtkeychain/cmake/Modules/ECMSetupVersion.cmake b/src/libs/3rdparty/qtkeychain/cmake/Modules/ECMSetupVersion.cmake
new file mode 100644
index 00000000000..65c1688ab00
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/cmake/Modules/ECMSetupVersion.cmake
@@ -0,0 +1,202 @@
+#.rst:
+# ECMSetupVersion
+# ---------------
+#
+# Handle library version information.
+#
+# ::
+#
+# ecm_setup_version(<version>
+# VARIABLE_PREFIX <prefix>
+# [SOVERSION <soversion>]
+# [VERSION_HEADER <filename>]
+# [PACKAGE_VERSION_FILE <filename> [COMPATIBILITY <compat>]] )
+#
+# This parses a version string and sets up a standard set of version variables.
+# It can optionally also create a C version header file and a CMake package
+# version file to install along with the library.
+#
+# If the ``<version>`` argument is of the form ``<major>.<minor>.<patch>``
+# (or ``<major>.<minor>.<patch>.<tweak>``), The following CMake variables are
+# set::
+#
+# <prefix>_VERSION_MAJOR - <major>
+# <prefix>_VERSION_MINOR - <minor>
+# <prefix>_VERSION_PATCH - <patch>
+# <prefix>_VERSION - <version>
+# <prefix>_VERSION_STRING - <version> (for compatibility: use <prefix>_VERSION instead)
+# <prefix>_SOVERSION - <soversion>, or <major> if SOVERSION was not given
+#
+# If CMake policy CMP0048 is not NEW, the following CMake variables will also
+# be set::
+#
+# PROJECT_VERSION_MAJOR - <major>
+# PROJECT_VERSION_MINOR - <minor>
+# PROJECT_VERSION_PATCH - <patch>
+# PROJECT_VERSION - <version>
+# PROJECT_VERSION_STRING - <version> (for compatibility: use PROJECT_VERSION instead)
+#
+# If the VERSION_HEADER option is used, a simple C header is generated with the
+# given filename. If filename is a relative path, it is interpreted as relative
+# to CMAKE_CURRENT_BINARY_DIR. The generated header contains the following
+# macros::
+#
+# <prefix>_VERSION_MAJOR - <major> as an integer
+# <prefix>_VERSION_MINOR - <minor> as an integer
+# <prefix>_VERSION_PATCH - <patch> as an integer
+# <prefix>_VERSION_STRING - <version> as a C string
+# <prefix>_VERSION - the version as an integer
+#
+# ``<prefix>_VERSION`` has ``<patch>`` in the bottom 8 bits, ``<minor>`` in the
+# next 8 bits and ``<major>`` in the remaining bits. Note that ``<patch>`` and
+# ``<minor>`` must be less than 256.
+#
+# If the PACKAGE_VERSION_FILE option is used, a simple CMake package version
+# file is created using the write_basic_package_version_file() macro provided by
+# CMake. It should be installed in the same location as the Config.cmake file of
+# the library so that it can be found by find_package(). If the filename is a
+# relative path, it is interpreted as relative to CMAKE_CURRENT_BINARY_DIR. The
+# optional COMPATIBILITY option is forwarded to
+# write_basic_package_version_file(), and defaults to AnyNewerVersion.
+#
+# If CMake policy CMP0048 is NEW, an alternative form of the command is
+# available::
+#
+# ecm_setup_version(PROJECT
+# [VARIABLE_PREFIX <prefix>]
+# [SOVERSION <soversion>]
+# [VERSION_HEADER <filename>]
+# [PACKAGE_VERSION_FILE <filename>] )
+#
+# This will use the version information set by the project() command.
+# VARIABLE_PREFIX defaults to the project name. Note that PROJECT must be the
+# first argument. In all other respects, it behaves like the other form of the
+# command.
+#
+# Since pre-1.0.0.
+#
+# COMPATIBILITY option available since 1.6.0.
+
+#=============================================================================
+# SPDX-FileCopyrightText: 2014 Alex Merry <alex.merry@kde.org>
+# SPDX-FileCopyrightText: 2012 Alexander Neundorf <neundorf@kde.org>
+#
+# SPDX-License-Identifier: BSD-3-Clause
+
+include(CMakePackageConfigHelpers)
+
+# save the location of the header template while CMAKE_CURRENT_LIST_DIR
+# has the value we want
+set(_ECM_SETUP_VERSION_HEADER_TEMPLATE "${CMAKE_CURRENT_LIST_DIR}/ECMVersionHeader.h.in")
+
+function(ecm_setup_version _version)
+ set(options )
+ set(oneValueArgs VARIABLE_PREFIX SOVERSION VERSION_HEADER PACKAGE_VERSION_FILE COMPATIBILITY)
+ set(multiValueArgs )
+
+ cmake_parse_arguments(ESV "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ if(ESV_UNPARSED_ARGUMENTS)
+ message(FATAL_ERROR "Unknown keywords given to ECM_SETUP_VERSION(): \"${ESV_UNPARSED_ARGUMENTS}\"")
+ endif()
+
+ set(project_manages_version FALSE)
+ set(use_project_version FALSE)
+ # CMP0048 only exists in CMake 3.0.0 and later
+ if(CMAKE_VERSION VERSION_LESS 3.0.0)
+ set(project_version_policy "OLD")
+ else()
+ cmake_policy(GET CMP0048 project_version_policy)
+ endif()
+ if(project_version_policy STREQUAL "NEW")
+ set(project_manages_version TRUE)
+ if(_version STREQUAL "PROJECT")
+ set(use_project_version TRUE)
+ endif()
+ elseif(_version STREQUAL "PROJECT")
+ message(FATAL_ERROR "ecm_setup_version given PROJECT argument, but CMP0048 is not NEW")
+ endif()
+
+ set(should_set_prefixed_vars TRUE)
+ if(NOT ESV_VARIABLE_PREFIX)
+ if(use_project_version)
+ set(ESV_VARIABLE_PREFIX "${PROJECT_NAME}")
+ set(should_set_prefixed_vars FALSE)
+ else()
+ message(FATAL_ERROR "Required argument PREFIX missing in ECM_SETUP_VERSION() call")
+ endif()
+ endif()
+
+ if(use_project_version)
+ set(_version "${PROJECT_VERSION}")
+ set(_major "${PROJECT_VERSION_MAJOR}")
+ set(_minor "${PROJECT_VERSION_MINOR}")
+ set(_patch "${PROJECT_VERSION_PATCH}")
+ else()
+ 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)
+ set(ESV_SOVERSION ${_major})
+ endif()
+
+ if(should_set_prefixed_vars)
+ set(${ESV_VARIABLE_PREFIX}_VERSION "${_version}")
+ set(${ESV_VARIABLE_PREFIX}_VERSION_MAJOR ${_major})
+ set(${ESV_VARIABLE_PREFIX}_VERSION_MINOR ${_minor})
+ set(${ESV_VARIABLE_PREFIX}_VERSION_PATCH ${_patch})
+ endif()
+
+ set(${ESV_VARIABLE_PREFIX}_SOVERSION ${ESV_SOVERSION})
+
+ if(NOT project_manages_version)
+ set(PROJECT_VERSION "${_version}")
+ set(PROJECT_VERSION_MAJOR "${_major}")
+ set(PROJECT_VERSION_MINOR "${_minor}")
+ set(PROJECT_VERSION_PATCH "${_patch}")
+ endif()
+
+ # compat
+ set(PROJECT_VERSION_STRING "${PROJECT_VERSION}")
+ set(${ESV_VARIABLE_PREFIX}_VERSION_STRING "${${ESV_VARIABLE_PREFIX}_VERSION}")
+
+ if(ESV_VERSION_HEADER)
+ set(HEADER_PREFIX "${ESV_VARIABLE_PREFIX}")
+ set(HEADER_VERSION "${_version}")
+ set(HEADER_VERSION_MAJOR "${_major}")
+ set(HEADER_VERSION_MINOR "${_minor}")
+ set(HEADER_VERSION_PATCH "${_patch}")
+ configure_file("${_ECM_SETUP_VERSION_HEADER_TEMPLATE}" "${ESV_VERSION_HEADER}")
+ endif()
+
+ if(ESV_PACKAGE_VERSION_FILE)
+ if(NOT ESV_COMPATIBILITY)
+ set(ESV_COMPATIBILITY AnyNewerVersion)
+ endif()
+ write_basic_package_version_file("${ESV_PACKAGE_VERSION_FILE}" VERSION ${_version} COMPATIBILITY ${ESV_COMPATIBILITY})
+ endif()
+
+ if(should_set_prefixed_vars)
+ set(${ESV_VARIABLE_PREFIX}_VERSION_MAJOR "${${ESV_VARIABLE_PREFIX}_VERSION_MAJOR}" PARENT_SCOPE)
+ set(${ESV_VARIABLE_PREFIX}_VERSION_MINOR "${${ESV_VARIABLE_PREFIX}_VERSION_MINOR}" PARENT_SCOPE)
+ set(${ESV_VARIABLE_PREFIX}_VERSION_PATCH "${${ESV_VARIABLE_PREFIX}_VERSION_PATCH}" PARENT_SCOPE)
+ set(${ESV_VARIABLE_PREFIX}_VERSION "${${ESV_VARIABLE_PREFIX}_VERSION}" PARENT_SCOPE)
+ endif()
+
+ # always set the soversion
+ set(${ESV_VARIABLE_PREFIX}_SOVERSION "${${ESV_VARIABLE_PREFIX}_SOVERSION}" PARENT_SCOPE)
+
+ if(NOT project_manages_version)
+ set(PROJECT_VERSION "${PROJECT_VERSION}" PARENT_SCOPE)
+ set(PROJECT_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}" PARENT_SCOPE)
+ set(PROJECT_VERSION_MINOR "${PROJECT_VERSION_MINOR}" PARENT_SCOPE)
+ set(PROJECT_VERSION_PATCH "${PROJECT_VERSION_PATCH}" PARENT_SCOPE)
+ endif()
+
+ # always set the compatibility variables
+ set(PROJECT_VERSION_STRING "${PROJECT_VERSION_STRING}" PARENT_SCOPE)
+ set(${ESV_VARIABLE_PREFIX}_VERSION_STRING "${${ESV_VARIABLE_PREFIX}_VERSION}" PARENT_SCOPE)
+
+endfunction()
diff --git a/src/libs/3rdparty/qtkeychain/cmake/Modules/QtVersionOption.cmake b/src/libs/3rdparty/qtkeychain/cmake/Modules/QtVersionOption.cmake
new file mode 100644
index 00000000000..ea37da22de1
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/cmake/Modules/QtVersionOption.cmake
@@ -0,0 +1,36 @@
+# SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
+#
+# 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()
diff --git a/src/libs/3rdparty/qtkeychain/gnomekeyring.cpp b/src/libs/3rdparty/qtkeychain/gnomekeyring.cpp
new file mode 100644
index 00000000000..6347052dcbf
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/gnomekeyring.cpp
@@ -0,0 +1,86 @@
+#include "gnomekeyring_p.h"
+
+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();
+}
+
+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() )
+ return 0;
+ return instance().store_password( instance().NETWORK_PASSWORD,
+ keyring, display_name, password, callback,
+ data, destroy_data,
+ "user", user,
+ "server", server,
+ "type", type,
+ static_cast<char*>(0) );
+}
+
+GnomeKeyring::gpointer GnomeKeyring::find_network_password(
+ const gchar* user, const gchar* server, const gchar* type,
+ OperationGetStringCallback callback, gpointer data, GDestroyNotify destroy_data )
+{
+ if ( !isAvailable() )
+ return 0;
+
+ return instance().find_password( instance().NETWORK_PASSWORD,
+ callback, data, destroy_data,
+ "user", user, "server", server, "type", type,
+ static_cast<char*>(0) );
+}
+
+GnomeKeyring::gpointer GnomeKeyring::delete_network_password( const gchar* user,
+ const gchar* server,
+ OperationDoneCallback callback,
+ gpointer data,
+ GDestroyNotify destroy_data )
+{
+ if ( !isAvailable() )
+ return 0;
+ return instance().delete_password( instance().NETWORK_PASSWORD,
+ callback, data, destroy_data,
+ "user", user, "server", server, static_cast<char*>(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 },
+ { 0, static_cast<AttributeType>( 0 ) }}
+ };
+
+ NETWORK_PASSWORD = &schema;
+ is_available = reinterpret_cast<is_available_fn*>( resolve( "gnome_keyring_is_available" ) );
+ find_password = reinterpret_cast<find_password_fn*>( resolve( "gnome_keyring_find_password" ) );
+ store_password = reinterpret_cast<store_password_fn*>( resolve( "gnome_keyring_store_password" ) );
+ delete_password = reinterpret_cast<delete_password_fn*>( resolve( "gnome_keyring_delete_password" ) );
+}
+
+GnomeKeyring& GnomeKeyring::instance() {
+ static GnomeKeyring keyring;
+ return keyring;
+}
diff --git a/src/libs/3rdparty/qtkeychain/gnomekeyring_p.h b/src/libs/3rdparty/qtkeychain/gnomekeyring_p.h
new file mode 100644
index 00000000000..87c062c375a
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/gnomekeyring_p.h
@@ -0,0 +1,94 @@
+#ifndef QTKEYCHAIN_GNOME_P_H
+#define QTKEYCHAIN_GNOME_P_H
+
+#include <QLibrary>
+
+class GnomeKeyring : private QLibrary {
+ Q_OBJECT
+
+public:
+ enum Result {
+ RESULT_OK,
+ RESULT_DENIED,
+ RESULT_NO_KEYRING_DAEMON,
+ RESULT_ALREADY_UNLOCKED,
+ RESULT_NO_SUCH_KEYRING,
+ RESULT_BAD_ARGUMENTS,
+ RESULT_IO_ERROR,
+ RESULT_CANCELLED,
+ RESULT_KEYRING_ALREADY_EXISTS,
+ RESULT_NO_MATCH
+ };
+
+ enum ItemType {
+ ITEM_GENERIC_SECRET = 0,
+ ITEM_NETWORK_PASSWORD,
+ ITEM_NOTE,
+ ITEM_CHAINED_KEYRING_PASSWORD,
+ ITEM_ENCRYPTION_KEY_PASSWORD,
+ ITEM_PK_STORAGE = 0x100
+ };
+
+ enum AttributeType {
+ ATTRIBUTE_TYPE_STRING,
+ ATTRIBUTE_TYPE_UINT32
+ };
+
+ typedef char gchar;
+ typedef void* gpointer;
+ typedef bool gboolean;
+ typedef struct {
+ ItemType item_type;
+ 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 );
+
+ 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 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 );
+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;
+};
+
+
+#endif
diff --git a/src/libs/3rdparty/qtkeychain/keychain.cpp b/src/libs/3rdparty/qtkeychain/keychain.cpp
new file mode 100644
index 00000000000..90ee4eb502a
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/keychain.cpp
@@ -0,0 +1,235 @@
+/******************************************************************************
+ * Copyright (C) 2011-2015 Frank Osterfeld <frank.osterfeld@gmail.com> *
+ * *
+ * 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.h"
+#include "keychain_p.h"
+
+using namespace QKeychain;
+
+Job::Job( JobPrivate *q, QObject *parent )
+ : QObject( parent )
+ , d ( q ) {
+}
+
+Job::~Job() {
+ delete d;
+}
+
+QString Job::service() const {
+ return d->service;
+}
+
+QSettings* Job::settings() const {
+ return d->settings;
+}
+
+void Job::setSettings( QSettings* settings ) {
+ d->settings = settings;
+}
+
+void Job::start() {
+ QMetaObject::invokeMethod( this, "doStart", Qt::QueuedConnection );
+}
+
+bool Job::autoDelete() const {
+ return d->autoDelete;
+}
+
+void Job::setAutoDelete( bool autoDelete ) {
+ d->autoDelete = autoDelete;
+}
+
+bool Job::insecureFallback() const {
+ return d->insecureFallback;
+}
+
+void Job::setInsecureFallback( bool insecureFallback ) {
+ d->insecureFallback = insecureFallback;
+}
+
+void Job::doStart() {
+ JobExecutor::instance()->enqueue( this );
+}
+
+void Job::emitFinished() {
+ emit finished( this );
+ if ( d->autoDelete )
+ deleteLater();
+}
+
+void Job::emitFinishedWithError( Error error, const QString& errorString ) {
+ d->error = error;
+ d->errorString = errorString;
+ emitFinished();
+}
+
+void Job::scheduledStart() {
+ d->scheduledStart();
+}
+
+Error Job::error() const {
+ return d->error;
+}
+
+QString Job::errorString() const {
+ return d->errorString;
+}
+
+void Job::setError( Error error ) {
+ d->error = error;
+}
+
+void Job::setErrorString( const QString& errorString ) {
+ d->errorString = errorString;
+}
+
+ReadPasswordJob::ReadPasswordJob( const QString& service, QObject* parent )
+ : Job( new ReadPasswordJobPrivate( service, this ), parent ) {
+
+}
+
+ReadPasswordJob::~ReadPasswordJob() {
+}
+
+QString ReadPasswordJob::textData() const {
+ return QString::fromUtf8( d->data );
+}
+
+QByteArray ReadPasswordJob::binaryData() const {
+ return d->data;
+}
+
+QString Job::key() const {
+ return d->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() {
+}
+
+void WritePasswordJob::setBinaryData( const QByteArray& data ) {
+ d->data = data;
+ d->mode = JobPrivate::Binary;
+}
+
+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() {
+}
+
+DeletePasswordJobPrivate::DeletePasswordJobPrivate(const QString &service_, DeletePasswordJob *qq) :
+ JobPrivate(service_, qq) {
+
+}
+
+JobExecutor::JobExecutor()
+ : QObject( 0 )
+ , m_jobRunning( false ) {
+}
+
+void JobExecutor::enqueue( Job* job ) {
+ m_queue.enqueue( job );
+ startNextIfNoneRunning();
+}
+
+void JobExecutor::startNextIfNoneRunning() {
+ if ( m_queue.isEmpty() || m_jobRunning )
+ return;
+ QPointer<Job> next;
+ 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*)) );
+ m_jobRunning = true;
+ next->scheduledStart();
+ }
+}
+
+void JobExecutor::jobDestroyed( QObject* object ) {
+ Job* job = static_cast<Job*>(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 );
+ m_jobRunning = false;
+ startNextIfNoneRunning();
+}
+
+JobExecutor* JobExecutor::s_instance = 0;
+
+JobExecutor* JobExecutor::instance() {
+ if ( !s_instance )
+ s_instance = new JobExecutor;
+ return s_instance;
+}
+
+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 )
+{
+}
+
+QString JobPrivate::modeToString(Mode m)
+{
+ switch (m) {
+ case Text:
+ return QLatin1String("Text");
+ case Binary:
+ return QLatin1String("Binary");
+ }
+
+ Q_ASSERT_X(false, Q_FUNC_INFO, "Unhandled Mode value");
+ return QString();
+}
+
+JobPrivate::Mode JobPrivate::stringToMode(const QString& s)
+{
+ if (s == QLatin1String("Text") || s == QLatin1String("1"))
+ return Text;
+ if (s == QLatin1String("Binary") || s == QLatin1String("2"))
+ return Binary;
+
+ qCritical("Unexpected mode string '%s'", qPrintable(s));
+
+ return Text;
+}
+
+WritePasswordJobPrivate::WritePasswordJobPrivate(const QString &service_, WritePasswordJob *qq) :
+ JobPrivate(service_, qq) {
+
+}
diff --git a/src/libs/3rdparty/qtkeychain/keychain.h b/src/libs/3rdparty/qtkeychain/keychain.h
new file mode 100644
index 00000000000..8c006cbcddb
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/keychain.h
@@ -0,0 +1,282 @@
+/******************************************************************************
+ * Copyright (C) 2011-2015 Frank Osterfeld <frank.osterfeld@gmail.com> *
+ * *
+ * 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 KEYCHAIN_H
+#define KEYCHAIN_H
+
+#if !defined(QTKEYCHAIN_NO_EXPORT)
+#include "qkeychain_export.h"
+#else
+#define QKEYCHAIN_EXPORT
+#endif
+
+#include <QtCore/QObject>
+#include <QtCore/QString>
+
+class QSettings;
+
+#define QTKEYCHAIN_VERSION 0x000100
+
+namespace QKeychain {
+
+/**
+ * Error codes
+ */
+enum Error {
+ 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 */
+ AccessDenied, /**< Access denied for other reasons */
+ NoBackendAvailable, /**< No platform-specific keychain service available */
+ NotImplemented, /**< Not implemented on platform */
+ OtherError /**< Something else went wrong (errorString() might provide details) */
+};
+
+class JobExecutor;
+class JobPrivate;
+
+/**
+ * @brief Abstract base class for all QKeychain jobs.
+ */
+class QKEYCHAIN_EXPORT Job : public QObject {
+ Q_OBJECT
+public:
+ ~Job() override;
+
+ /**
+ * @return The QSettings instance used as plaintext storage if insecureFallback() is true.
+ * @see setSettings()
+ * @see insecureFallback()
+ */
+ QSettings* settings() const;
+
+ /**
+ * @return Set the QSettings instance that will be used as plaintext storage if insecureFallback() is true.
+ * @see settings()
+ * @see insecureFallback()
+ */
+ void setSettings( QSettings* settings );
+
+ /**
+ * Call this method to start the job.
+ * Typically you want to connect some slot to the finished() signal first:
+ *
+ * \code
+ * SomeClass::startJob()
+ * {
+ * connect(job, &Job::finished, this, &SomeClass::slotJobFinished);
+ * job->start();
+ * }
+ *
+ * SomeClass::slotJobFinished(Job *job)
+ * {
+ * if (job->error()) {
+ * // handle error
+ * } else {
+ * // do job-specific stuff
+ * }
+ * }
+ * \endcode
+ *
+ * @see finished()
+ */
+ void start();
+
+ QString service() const;
+
+ /**
+ * @note Call this method only after finished() has been emitted.
+ * @return The error code of the job (0 if no error).
+ */
+ Error error() const;
+
+ /**
+ * @return An error message that might provide details if error() returns OtherError.
+ */
+ QString errorString() const;
+
+ /**
+ * @return Whether this job autodeletes itself once finished() has been emitted. Default is true.
+ * @see setAutoDelete()
+ */
+ bool autoDelete() const;
+
+ /**
+ * Set whether this job should autodelete itself once finished() has been emitted.
+ * @see autoDelete()
+ */
+ void setAutoDelete( bool autoDelete );
+
+ /**
+ * @return Whether this job will use plaintext storage on unsupported platforms. Default is false.
+ * @see setInsecureFallback()
+ */
+ bool insecureFallback() const;
+
+ /**
+ * Set whether this job should use plaintext storage on unsupported platforms.
+ * @see insecureFallback()
+ */
+ void setInsecureFallback( bool insecureFallback );
+
+ /**
+ * @return The string used as key by this job.
+ * @see setKey()
+ */
+ QString key() const;
+
+ /**
+ * Set the @p key that this job will use to read or write data from/to the keychain.
+ * The key can be an empty string.
+ * @see key()
+ */
+ void setKey( const QString& key );
+
+ void emitFinished();
+ void emitFinishedWithError(Error, const QString& errorString);
+
+Q_SIGNALS:
+ /**
+ * Emitted when this job is finished.
+ * You can connect to this signal to be notified about the job's completion.
+ * @see start()
+ */
+ void finished( QKeychain::Job* );
+
+protected:
+ explicit Job( JobPrivate *q, QObject* parent=nullptr );
+ Q_INVOKABLE void doStart();
+
+private:
+ void setError( Error error );
+ void setErrorString( const QString& errorString );
+
+ void scheduledStart();
+
+protected:
+ JobPrivate* const d;
+
+friend class JobExecutor;
+friend class JobPrivate;
+friend class ReadPasswordJobPrivate;
+friend class WritePasswordJobPrivate;
+friend class DeletePasswordJobPrivate;
+};
+
+class ReadPasswordJobPrivate;
+
+/**
+ * @brief Job for reading secrets from the keychain.
+ * You can use a ReadPasswordJob to read passwords or binary data from the keychain.
+ * 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 <key, secret> stored in the same service string.
+ */
+class QKEYCHAIN_EXPORT ReadPasswordJob : public Job {
+ Q_OBJECT
+public:
+ /**
+ * Create a new ReadPasswordJob.
+ * @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 );
+ ~ReadPasswordJob() override;
+
+ /**
+ * @return The binary data stored as value of this job's key().
+ * @see Job::key()
+ */
+ QByteArray binaryData() const;
+
+ /**
+ * @return The string stored as value of this job's key().
+ * @see Job::key()
+ * @warning Returns meaningless data if the data was stored as binary data.
+ * @see WritePasswordJob::setTextData()
+ */
+ QString textData() const;
+
+private:
+ friend class QKeychain::ReadPasswordJobPrivate;
+};
+
+class WritePasswordJobPrivate;
+
+/**
+ * @brief Job for writing secrets to the keychain.
+ * You can use a WritePasswordJob to store passwords or binary data in the keychain.
+ * This job requires a "service" string, which is basically a namespace of keys within the keychain.
+ * This means that you can store different pairs <key, secret> under the same service string.
+ */
+class QKEYCHAIN_EXPORT WritePasswordJob : public Job {
+ Q_OBJECT
+public:
+ /**
+ * Create a new WritePasswordJob.
+ * @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 );
+ ~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 );
+
+ /**
+ * 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 );
+
+private:
+
+ friend class QKeychain::WritePasswordJobPrivate;
+};
+
+class DeletePasswordJobPrivate;
+
+/**
+ * @brief Job for deleting secrets from the keychain.
+ * You can use a DeletePasswordJob to delete passwords or binary data from the keychain.
+ * 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 <key, secret> stored in the same service string.
+ */
+class QKEYCHAIN_EXPORT DeletePasswordJob : public Job {
+ Q_OBJECT
+public:
+ /**
+ * Create a new DeletePasswordJob.
+ * @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 );
+ ~DeletePasswordJob() override;
+
+private:
+ 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
+ */
+QKEYCHAIN_EXPORT bool isAvailable();
+
+} // namespace QtKeychain
+
+#endif
diff --git a/src/libs/3rdparty/qtkeychain/keychain_apple.mm b/src/libs/3rdparty/qtkeychain/keychain_apple.mm
new file mode 100644
index 00000000000..f9a8266be05
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/keychain_apple.mm
@@ -0,0 +1,263 @@
+/******************************************************************************
+ * Copyright (C) 2016 Mathias Hasselmann <mathias.hasselmann@kdab.com> *
+ * *
+ * 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"
+
+#import <Foundation/Foundation.h>
+#import <Security/Security.h>
+
+using namespace QKeychain;
+
+struct ErrorDescription
+{
+ QKeychain::Error code;
+ QString message;
+
+ ErrorDescription(QKeychain::Error code, const QString &message)
+ : code(code), message(message) {}
+
+ static ErrorDescription fromStatus(OSStatus 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"));
+ case errSecUserCanceled:
+ return ErrorDescription(QKeychain::AccessDeniedByUser, Job::tr("User canceled the operation"));
+ case errSecInteractionNotAllowed:
+ 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"));
+ case errSecAuthFailed:
+ 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"));
+ case errSecUnimplemented:
+ 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"));
+ case errSecParam:
+ 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"));
+ 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"));
+ case errSecDecode:
+ return ErrorDescription(QKeychain::OtherError, Job::tr("Unable to decode the provided data"));
+ }
+
+ return ErrorDescription(QKeychain::OtherError, Job::tr("Unknown error"));
+ }
+};
+
+@interface AppleKeychainInterface : NSObject
+
+- (instancetype)initWithJob:(Job *)job andPrivateJob:(JobPrivate *)privateJob;
+- (void)keychainTaskFinished;
+- (void)keychainReadTaskFinished:(NSData *)retrievedData;
+- (void)keychainTaskFinishedWithError:(OSStatus)status descriptiveMessage:(NSString *)descriptiveMessage;
+
+@end
+
+@interface AppleKeychainInterface()
+{
+ QPointer<Job> _job;
+ QPointer<JobPrivate> _privateJob;
+}
+@end
+
+@implementation AppleKeychainInterface
+
+- (instancetype)initWithJob:(Job *)job andPrivateJob:(JobPrivate *)privateJob
+{
+ self = [super init];
+ if (self) {
+ _job = job;
+ _privateJob = privateJob;
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ [NSNotificationCenter.defaultCenter removeObserver:self];
+ [super dealloc];
+}
+
+- (void)keychainTaskFinished
+{
+ if (_job) {
+ _job->emitFinished();
+ }
+}
+
+- (void)keychainReadTaskFinished:(NSData *)retrievedData
+{
+ _privateJob->data.clear();
+ _privateJob->mode = JobPrivate::Binary;
+
+ if (retrievedData != nil) {
+ if (_privateJob) {
+ _privateJob->data = QByteArray::fromNSData(retrievedData);
+ }
+ }
+
+ if (_job) {
+ _job->emitFinished();
+ }
+}
+
+- (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);
+
+ if (_job) {
+ _job->emitFinishedWithError(error.code, fullMessage);
+ }
+}
+
+@end
+
+
+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,
+ };
+
+ CFTypeRef dataRef = nil;
+ const OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef) query, &dataRef);
+
+ if (status == errSecSuccess) {
+ const CFDataRef castedDataRef = (CFDataRef)dataRef;
+ 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";
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [interface keychainTaskFinishedWithError:status descriptiveMessage:descriptiveErrorString];
+ [interface release];
+ });
+ }
+
+ if (dataRef) {
+ CFRelease(dataRef);
+ }
+ });
+}
+
+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(),
+ };
+
+ OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, nil);
+
+ if (status == errSecSuccess) {
+ NSDictionary * const update = @{
+ (__bridge NSString *)kSecValueData: data.toNSData(),
+ };
+
+ 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(),
+ };
+
+ status = SecItemAdd((__bridge const CFDictionaryRef)insert, nil);
+ }
+
+ if (status == errSecSuccess) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [interface keychainTaskFinished];
+ [interface release];
+ });
+ } else {
+ NSString * const descriptiveErrorString = @"Could not store data in settings";
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [interface keychainTaskFinishedWithError:status descriptiveMessage:descriptiveErrorString];
+ [interface release];
+ });
+ }
+ });
+}
+
+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(),
+ };
+
+ const OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query);
+
+ if (status == errSecSuccess) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [interface keychainTaskFinished];
+ [interface release];
+ });
+ } else {
+ NSString * const descriptiveErrorString = @"Could not remove private key from keystore";
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [interface keychainTaskFinishedWithError:status descriptiveMessage:descriptiveErrorString];
+ [interface release];
+ });
+ }
+ });
+}
+
+void ReadPasswordJobPrivate::scheduledStart()
+{
+ 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];
+ StartWritePassword(service, key, data, interface);
+}
+
+void DeletePasswordJobPrivate::scheduledStart()
+{
+ AppleKeychainInterface * const interface = [[AppleKeychainInterface alloc] initWithJob:q andPrivateJob:this];
+ StartDeletePassword(service, key, interface);
+}
+
+bool QKeychain::isAvailable()
+{
+ return true;
+}
diff --git a/src/libs/3rdparty/qtkeychain/keychain_p.h b/src/libs/3rdparty/qtkeychain/keychain_p.h
new file mode 100644
index 00000000000..a66cb423d1c
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/keychain_p.h
@@ -0,0 +1,167 @@
+/******************************************************************************
+ * Copyright (C) 2011-2015 Frank Osterfeld <frank.osterfeld@gmail.com> *
+ * *
+ * 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 KEYCHAIN_P_H
+#define KEYCHAIN_P_H
+
+#include <QCoreApplication>
+#include <QObject>
+#include <QPointer>
+#include <QSettings>
+#include <QQueue>
+
+#if defined(KEYCHAIN_DBUS)
+
+#include <QDBusPendingCallWatcher>
+
+#include "kwallet_interface.h"
+#else
+
+class QDBusPendingCallWatcher;
+
+#endif
+
+#include "keychain.h"
+
+namespace QKeychain {
+
+class JobExecutor;
+
+class JobPrivate : public QObject {
+ Q_OBJECT
+public:
+ enum Mode {
+ Text,
+ Binary
+ };
+
+ virtual void scheduledStart() = 0;
+
+ static QString modeToString(Mode m);
+ static Mode stringToMode(const QString& s);
+
+ Job* const q;
+ Mode mode;
+ QByteArray data;
+
+#if defined(KEYCHAIN_DBUS)
+ 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 );
+
+ virtual void fallbackOnError(const QDBusError& err) = 0;
+
+protected Q_SLOTS:
+ 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* ) {}
+#endif
+
+protected:
+ JobPrivate( const QString& service_, Job *q );
+
+protected:
+ QKeychain::Error error;
+ QString errorString;
+ QString service;
+ bool autoDelete;
+ bool insecureFallback;
+ QPointer<QSettings> settings;
+ QString key;
+
+friend class Job;
+friend class JobExecutor;
+friend class ReadPasswordJob;
+friend class WritePasswordJob;
+friend class PlainTextStore;
+};
+
+class ReadPasswordJobPrivate : public JobPrivate {
+ Q_OBJECT
+public:
+ explicit ReadPasswordJobPrivate( const QString &service_, ReadPasswordJob* qq );
+ void scheduledStart() override;
+
+#if defined(KEYCHAIN_DBUS)
+ 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
+private Q_SLOTS:
+ void kwalletOpenFinished( QDBusPendingCallWatcher* ) override {}
+ void kwalletEntryTypeFinished( QDBusPendingCallWatcher* ) {}
+ void kwalletFinished( QDBusPendingCallWatcher* ) override {}
+#endif
+
+ friend class ReadPasswordJob;
+};
+
+class WritePasswordJobPrivate : public JobPrivate {
+ Q_OBJECT
+public:
+ explicit WritePasswordJobPrivate( const QString &service_, WritePasswordJob* qq );
+ void scheduledStart() override;
+
+#if defined(KEYCHAIN_DBUS)
+ void fallbackOnError(const QDBusError& err) override;
+#endif
+
+ friend class WritePasswordJob;
+};
+
+class DeletePasswordJobPrivate : public JobPrivate {
+ Q_OBJECT
+public:
+ explicit DeletePasswordJobPrivate( const QString &service_, DeletePasswordJob* qq );
+
+ void scheduledStart() override;
+
+#if defined(KEYCHAIN_DBUS)
+ void fallbackOnError(const QDBusError& err) override;
+#endif
+
+protected:
+ void doStart();
+
+ friend class DeletePasswordJob;
+};
+
+class JobExecutor : public QObject {
+ Q_OBJECT
+public:
+
+ static JobExecutor* instance();
+
+ void enqueue( Job* job );
+
+private:
+ explicit JobExecutor();
+ void startNextIfNoneRunning();
+
+private Q_SLOTS:
+ void jobFinished( QKeychain::Job* );
+ void jobDestroyed( QObject* object );
+
+private:
+ static JobExecutor* s_instance;
+ QQueue<QPointer<Job> > m_queue;
+ bool m_jobRunning;
+};
+
+}
+
+#endif // KEYCHAIN_P_H
diff --git a/src/libs/3rdparty/qtkeychain/keychain_unix.cpp b/src/libs/3rdparty/qtkeychain/keychain_unix.cpp
new file mode 100644
index 00000000000..a3e83a96875
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/keychain_unix.cpp
@@ -0,0 +1,633 @@
+/******************************************************************************
+ * Copyright (C) 2011-2015 Frank Osterfeld <frank.osterfeld@gmail.com> *
+ * *
+ * 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 "gnomekeyring_p.h"
+#include "libsecret_p.h"
+#include "plaintextstore_p.h"
+
+#include <QScopedPointer>
+
+using namespace QKeychain;
+
+enum KeyringBackend {
+ Backend_LibSecretKeyring,
+ Backend_GnomeKeyring,
+ Backend_Kwallet4,
+ 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 == "6" ) {
+ return DesktopEnv_Plasma6;
+ } else if ( value == "5" ) {
+ return DesktopEnv_Plasma5;
+ } else if (value == "4" ) {
+ return DesktopEnv_Kde4;
+ } else {
+ // most likely KDE3
+ return DesktopEnv_Other;
+ }
+}
+
+static DesktopEnvironment detectDesktopEnvironment() {
+ QByteArray xdgCurrentDesktop = qgetenv("XDG_CURRENT_DESKTOP");
+ if ( xdgCurrentDesktop == "GNOME" ) {
+ return DesktopEnv_Gnome;
+ } else if ( xdgCurrentDesktop == "Unity" ) {
+ return DesktopEnv_Unity;
+ } else if ( xdgCurrentDesktop == "KDE" ) {
+ return getKdeVersion();
+ } else if ( xdgCurrentDesktop == "XFCE" ) {
+ return DesktopEnv_Xfce;
+ }
+
+ QByteArray desktopSession = qgetenv("DESKTOP_SESSION");
+ if ( desktopSession == "gnome" ) {
+ return DesktopEnv_Gnome;
+ } else if ( desktopSession == "kde" ) {
+ return getKdeVersion();
+ } else if ( desktopSession == "kde4" ) {
+ return DesktopEnv_Kde4;
+ } else if ( desktopSession.contains("xfce") || desktopSession == "xubuntu" ) {
+ return DesktopEnv_Xfce;
+ }
+
+ if ( !qgetenv("GNOME_DESKTOP_SESSION_ID").isEmpty() ) {
+ return DesktopEnv_Gnome;
+ } else if ( !qgetenv("KDE_FULL_SESSION").isEmpty() ) {
+ return getKdeVersion();
+ }
+
+ return DesktopEnv_Other;
+}
+
+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());
+
+ // At this point iface.isValid() can return false even though the
+ // 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;
+}
+
+static KeyringBackend detectKeyringBackend()
+{
+ /* The secret service dbus api, accessible through libsecret, is supposed
+ * to unify password services.
+ *
+ * Unfortunately at the time of Kubuntu 18.04 the secret service backend
+ * in KDE is gnome-keyring-daemon - using it has several complications:
+ * - the default collection isn't opened on session start, so users need
+ * to manually unlock it when the first application uses it
+ * - it's separate from the kwallet5 keyring, so switching to it means the
+ * existing keyring data can't be accessed anymore
+ *
+ * Thus we still prefer kwallet backends on KDE even if libsecret is
+ * available.
+ */
+
+ switch (detectDesktopEnvironment()) {
+ case DesktopEnv_Kde4:
+ return Backend_Kwallet4;
+
+ case DesktopEnv_Plasma5:
+ if (isKwalletAvailable(KWALLET5_DBUS_IFACE, KWALLET5_DBUS_PATH)) {
+ return Backend_Kwallet5;
+ }
+ 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_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:
+ case DesktopEnv_Other:
+ default:
+ if (LibSecretKeyring::isAvailable()) {
+ return Backend_LibSecretKeyring;
+ }
+ if (GnomeKeyring::isAvailable()) {
+ return Backend_GnomeKeyring;
+ }
+ 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
+ //
+ // This doesn't need to be libsecret because LibSecretKeyring::isAvailable()
+ // only fails if the libsecret shared library couldn't be loaded. In contrast
+ // to that GnomeKeyring::isAvailable() can return false if the shared library
+ // *was* loaded but its libgnome_keyring::is_available() returned false.
+ //
+ // In the future there should be a difference between "API available" and
+ // "keychain available".
+ return Backend_GnomeKeyring;
+ }
+
+}
+
+static KeyringBackend getKeyringBackend()
+{
+ static KeyringBackend backend = detectKeyringBackend();
+ 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 );
+ const QDBusPendingReply<QString> reply = priv->iface->networkWallet();
+ 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 );
+ }
+}
+
+void ReadPasswordJobPrivate::scheduledStart() {
+ switch ( getKeyringBackend() ) {
+ case Backend_LibSecretKeyring: {
+ 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<GnomeKeyring::OperationGetStringCallback>( &JobPrivate::gnomeKeyring_readCb ),
+ this, 0 ) )
+ q->emitFinishedWithError( OtherError, tr("Unknown error") );
+ break;
+
+ case Backend_Kwallet4:
+ kwalletReadPasswordScheduledStartImpl(KWALLET4_DBUS_IFACE, KWALLET4_DBUS_PATH, this);
+ break;
+ case Backend_Kwallet5:
+ kwalletReadPasswordScheduledStartImpl(KWALLET5_DBUS_IFACE, KWALLET5_DBUS_PATH, this);
+ break;
+ case Backend_Kwallet6:
+ kwalletReadPasswordScheduledStartImpl(KWALLET6_DBUS_IFACE, KWALLET6_DBUS_PATH, this);
+ break;
+ }
+}
+
+void JobPrivate::kwalletWalletFound(QDBusPendingCallWatcher *watcher)
+{
+ watcher->deleteLater();
+ const QDBusPendingReply<QString> reply = *watcher;
+ const QDBusPendingReply<int> 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<Error, QString> mapGnomeKeyringError( int result )
+{
+ Q_ASSERT( result != GnomeKeyring::RESULT_OK );
+
+ switch ( result ) {
+ case GnomeKeyring::RESULT_DENIED:
+ return qMakePair( AccessDenied, QObject::tr("Access to keychain denied") );
+ case GnomeKeyring::RESULT_NO_KEYRING_DAEMON:
+ return qMakePair( NoBackendAvailable, QObject::tr("No keyring daemon") );
+ case GnomeKeyring::RESULT_ALREADY_UNLOCKED:
+ return qMakePair( OtherError, QObject::tr("Already unlocked") );
+ case GnomeKeyring::RESULT_NO_SUCH_KEYRING:
+ return qMakePair( OtherError, QObject::tr("No such keyring") );
+ case GnomeKeyring::RESULT_BAD_ARGUMENTS:
+ return qMakePair( OtherError, QObject::tr("Bad arguments") );
+ case GnomeKeyring::RESULT_IO_ERROR:
+ return qMakePair( OtherError, QObject::tr("I/O error") );
+ case GnomeKeyring::RESULT_CANCELLED:
+ return qMakePair( OtherError, QObject::tr("Cancelled") );
+ case GnomeKeyring::RESULT_KEYRING_ALREADY_EXISTS:
+ return qMakePair( OtherError, QObject::tr("Keyring already exists") );
+ case GnomeKeyring::RESULT_NO_MATCH:
+ return qMakePair( EntryNotFound, QObject::tr("No match") );
+ default:
+ break;
+ }
+
+ return qMakePair( OtherError, QObject::tr("Unknown error") );
+}
+
+void JobPrivate::gnomeKeyring_readCb( int result, const char* string, JobPrivate* self )
+{
+ if ( result == GnomeKeyring::RESULT_OK ) {
+ if (self->mode == JobPrivate::Text)
+ self->data = QByteArray(string);
+ else
+ self->data = QByteArray::fromBase64(string);
+
+ 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<GnomeKeyring::OperationGetStringCallback>( &JobPrivate::gnomeKeyring_readCb ),
+ self, 0 ) )
+ self->q->emitFinishedWithError( OtherError, tr("Unknown error") );
+ } else {
+ const QPair<Error, QString> errorResult = mapGnomeKeyringError( result );
+ self->q->emitFinishedWithError( errorResult.first, errorResult.second );
+ }
+}
+
+void ReadPasswordJobPrivate::fallbackOnError(const QDBusError& err )
+{
+ PlainTextStore plainTextStore( q->service(), q->settings() );
+
+ if ( q->insecureFallback() && plainTextStore.contains( key ) ) {
+ mode = plainTextStore.readMode( key );
+ data = plainTextStore.readData( key );
+
+ 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") );
+ else
+ q->emitFinishedWithError( OtherError, tr("Could not open wallet: %1; %2").arg( QDBusError::errorString( err.type() ), err.message() ) );
+ }
+}
+
+void ReadPasswordJobPrivate::kwalletOpenFinished( QDBusPendingCallWatcher* watcher ) {
+ watcher->deleteLater();
+ const QDBusPendingReply<int> reply = *watcher;
+
+ if ( reply.isError() ) {
+ fallbackOnError( reply.error() );
+ return;
+ }
+
+ PlainTextStore plainTextStore( q->service(), q->settings() );
+
+ 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 );
+
+ q->emitFinished();
+
+
+ WritePasswordJob* j = new WritePasswordJob( q->service(), 0 );
+ 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 );
+
+ j->start();
+
+ return;
+ }
+
+ walletHandle = reply.value();
+
+ if ( walletHandle < 0 ) {
+ q->emitFinishedWithError( AccessDenied, tr("Access to keychain denied") );
+ return;
+ }
+
+ const QDBusPendingReply<int> 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
+};
+
+void ReadPasswordJobPrivate::kwalletEntryTypeFinished( QDBusPendingCallWatcher* watcher ) {
+ watcher->deleteLater();
+ 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() ) );
+ return;
+ }
+
+ const QDBusPendingReply<int> reply = *watcher;
+ const int value = reply.value();
+
+ switch ( value ) {
+ case Unknown:
+ q->emitFinishedWithError( EntryNotFound, tr("Entry not found") );
+ return;
+ case Password:
+ mode = Text;
+ break;
+ case Stream:
+ mode = Binary;
+ break;
+ case Map:
+ q->emitFinishedWithError( EntryNotFound, tr("Unsupported entry type 'Map'") );
+ return;
+ default:
+ 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*)) );
+}
+
+void ReadPasswordJobPrivate::kwalletFinished( QDBusPendingCallWatcher* watcher ) {
+ if ( !watcher->isError() ) {
+ if ( mode == Binary ) {
+ QDBusPendingReply<QByteArray> reply = *watcher;
+ if (reply.isValid()) {
+ data = reply.value();
+ }
+ } else {
+ QDBusPendingReply<QString> reply = *watcher;
+ if (reply.isValid()) {
+ data = reply.value().toUtf8();
+ }
+ }
+ }
+
+ 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 );
+ const QDBusPendingReply<QString> reply = priv->iface->networkWallet();
+ 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 );
+ }
+}
+
+void WritePasswordJobPrivate::scheduledStart() {
+ switch ( getKeyringBackend() ) {
+ case Backend_LibSecretKeyring: {
+ 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) {
+ case JobPrivate::Text:
+ type = QLatin1String("plaintext");
+ password = data;
+ break;
+ default:
+ type = QLatin1String("base64");
+ password = data.toBase64();
+ break;
+ }
+
+ 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<GnomeKeyring::OperationDoneCallback>( &JobPrivate::gnomeKeyring_writeCb ),
+ this, 0 ) )
+ q->emitFinishedWithError( OtherError, tr("Unknown error") );
+ }
+ break;
+
+ case Backend_Kwallet4:
+ kwalletWritePasswordScheduledStart(KWALLET4_DBUS_IFACE, KWALLET4_DBUS_PATH, this);
+ break;
+ case Backend_Kwallet5:
+ kwalletWritePasswordScheduledStart(KWALLET5_DBUS_IFACE, KWALLET5_DBUS_PATH, this);
+ break;
+ case Backend_Kwallet6:
+ kwalletWritePasswordScheduledStart(KWALLET6_DBUS_IFACE, KWALLET6_DBUS_PATH, this);
+ break;
+ }
+}
+
+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() ) );
+ return;
+ }
+
+ PlainTextStore plainTextStore( q->service(), q->settings() );
+ plainTextStore.write( key, data, mode );
+
+ if ( plainTextStore.error() != NoError )
+ q->emitFinishedWithError( plainTextStore.error(), plainTextStore.errorString() );
+ else
+ q->emitFinished();
+}
+
+void JobPrivate::gnomeKeyring_writeCb(int result, JobPrivate* self )
+{
+ if ( result == GnomeKeyring::RESULT_OK ) {
+ self->q->emitFinished();
+ } else {
+ const QPair<Error, QString> errorResult = mapGnomeKeyringError( result );
+ self->q->emitFinishedWithError( errorResult.first, errorResult.second );
+ }
+}
+
+void JobPrivate::kwalletOpenFinished( QDBusPendingCallWatcher* watcher ) {
+ watcher->deleteLater();
+ QDBusPendingReply<int> reply = *watcher;
+
+ 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 );
+ }
+
+ const int handle = reply.value();
+
+ if ( handle < 0 ) {
+ q->emitFinishedWithError( AccessDenied, tr("Access to keychain denied") );
+ return;
+ }
+
+ QDBusPendingReply<int> nextReply;
+
+ 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*)) );
+}
+
+void JobPrivate::kwalletFinished( QDBusPendingCallWatcher* watcher ) {
+ if ( !watcher->isError() ) {
+ if ( mode == Binary ) {
+ QDBusPendingReply<QByteArray> reply = *watcher;
+ if (reply.isValid()) {
+ data = reply.value();
+ }
+ } else {
+ QDBusPendingReply<QString> reply = *watcher;
+ if (reply.isValid()) {
+ data = reply.value().toUtf8();
+ }
+ }
+ }
+
+ q->emitFinished();
+}
+
+void DeletePasswordJobPrivate::scheduledStart() {
+ switch ( getKeyringBackend() ) {
+ case Backend_LibSecretKeyring: {
+ 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<GnomeKeyring::OperationDoneCallback>( &JobPrivate::gnomeKeyring_writeCb ),
+ this, 0 ) )
+ q->emitFinishedWithError( OtherError, tr("Unknown error") );
+ }
+ break;
+
+ case Backend_Kwallet4:
+ kwalletWritePasswordScheduledStart(KWALLET4_DBUS_IFACE, KWALLET4_DBUS_PATH, this);
+ break;
+ case Backend_Kwallet5:
+ kwalletWritePasswordScheduledStart(KWALLET5_DBUS_IFACE, KWALLET5_DBUS_PATH, this);
+ break;
+ case Backend_Kwallet6:
+ kwalletWritePasswordScheduledStart(KWALLET6_DBUS_IFACE, KWALLET6_DBUS_PATH, this);
+ break;
+ }
+}
+
+void DeletePasswordJobPrivate::fallbackOnError(const QDBusError &err) {
+ QScopedPointer<QSettings> local( !q->settings() ? new QSettings( q->service() ) : 0 );
+ 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() ) );
+ return;
+ }
+
+ 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);
+}
diff --git a/src/libs/3rdparty/qtkeychain/keychain_win.cpp b/src/libs/3rdparty/qtkeychain/keychain_win.cpp
new file mode 100644
index 00000000000..98dd34cbab3
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/keychain_win.cpp
@@ -0,0 +1,193 @@
+/******************************************************************************
+ * Copyright (C) 2011-2015 Frank Osterfeld <frank.osterfeld@gmail.com> *
+ * *
+ * 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 "plaintextstore_p.h"
+
+#include <windows.h>
+#include <wincrypt.h>
+
+#include <memory>
+
+using namespace QKeychain;
+
+#if defined(USE_CREDENTIAL_STORE)
+#include <wincred.h>
+
+void ReadPasswordJobPrivate::scheduledStart() {
+ LPCWSTR name = (LPCWSTR)key.utf16();
+ PCREDENTIALW cred;
+
+ if (!CredReadW(name, CRED_TYPE_GENERIC, 0, &cred)) {
+ Error err;
+ QString msg;
+ switch(GetLastError()) {
+ case ERROR_NOT_FOUND:
+ err = EntryNotFound;
+ msg = tr("Password entry not found");
+ break;
+ default:
+ err = OtherError;
+ msg = tr("Could not decrypt data");
+ break;
+ }
+
+ q->emitFinishedWithError( err, msg );
+ return;
+ }
+
+ data = QByteArray((char*)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));
+ cred.Comment = const_cast<wchar_t*>(L"QtKeychain");
+ cred.Type = CRED_TYPE_GENERIC;
+ cred.TargetName = name;
+ cred.CredentialBlobSize = data.size();
+ cred.CredentialBlob = (LPBYTE)pwd;
+ cred.Persist = CRED_PERSIST_ENTERPRISE;
+
+ if (CredWriteW(&cred, 0)) {
+ q->emitFinished();
+ return;
+ }
+
+ DWORD err = GetLastError();
+
+ // 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) {
+ q->emitFinishedWithError(
+ OtherError,
+ tr("Credential key exceeds maximum size of %1").arg(maxTargetName));
+ return;
+ }
+ }
+
+ q->emitFinishedWithError( OtherError, tr("Writing credentials failed: Win32 error code %1").arg(err) );
+}
+
+void DeletePasswordJobPrivate::scheduledStart() {
+ LPCWSTR name = (LPCWSTR)key.utf16();
+
+ if (!CredDeleteW(name, CRED_TYPE_GENERIC, 0)) {
+ Error err;
+ QString msg;
+ switch(GetLastError()) {
+ case ERROR_NOT_FOUND:
+ err = EntryNotFound;
+ msg = tr("Password entry not found");
+ break;
+ default:
+ err = OtherError;
+ msg = tr("Could not decrypt data");
+ break;
+ }
+
+ 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() );
+ return;
+ }
+
+ DATA_BLOB blob_in, blob_out;
+
+ blob_in.pbData = reinterpret_cast<BYTE*>( 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") );
+ return;
+ }
+
+ data = QByteArray( reinterpret_cast<char*>( blob_out.pbData ), blob_out.cbData );
+ SecureZeroMemory( blob_out.pbData, blob_out.cbData );
+ LocalFree( blob_out.pbData );
+
+ q->emitFinished();
+}
+
+void WritePasswordJobPrivate::scheduledStart() {
+ DATA_BLOB blob_in, blob_out;
+ blob_in.pbData = reinterpret_cast<BYTE*>( 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?
+ return;
+ }
+
+ const QByteArray encrypted( reinterpret_cast<char*>( blob_out.pbData ), blob_out.cbData );
+ LocalFree( blob_out.pbData );
+
+ PlainTextStore plainTextStore( q->service(), q->settings() );
+ plainTextStore.write( key, encrypted, 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() );
+ } else {
+ q->emitFinished();
+ }
+}
+#endif
+
+bool QKeychain::isAvailable()
+{
+ return true;
+}
diff --git a/src/libs/3rdparty/qtkeychain/libsecret.cpp b/src/libs/3rdparty/qtkeychain/libsecret.cpp
new file mode 100644
index 00000000000..6ee024989e8
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/libsecret.cpp
@@ -0,0 +1,349 @@
+#if defined(HAVE_LIBSECRET)
+
+#undef signals
+#include <libsecret/secret.h>
+#define signals
+
+#endif
+
+#include "libsecret_p.h"
+
+
+#include <QLibrary>
+
+#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 }
+ }
+ };
+
+ return &schema;
+}
+
+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,
+ ...) 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;
+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()) {
+ return QKeychain::OtherError;
+ }
+
+ switch(error->code) {
+ case SECRET_ERROR_NO_SUCH_OBJECT:
+ return QKeychain::EntryNotFound;
+ case SECRET_ERROR_IS_LOCKED:
+ return QKeychain::AccessDenied;
+ default:
+ return QKeychain::OtherError;
+ }
+}
+
+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);
+
+ Q_UNUSED(source);
+
+ if (arg) {
+ if (error) {
+ QKeychain::Error code = gerrorToCode(error);
+
+ arg->self->q->emitFinishedWithError( code, QString::fromUtf8(error->message) );
+ } else {
+ if (password) {
+ QByteArray raw = QByteArray(password);
+ switch(arg->self->mode) {
+ case QKeychain::JobPrivate::Binary:
+ arg->self->data = QByteArray::fromBase64(raw);
+ break;
+ case QKeychain::JobPrivate::Text:
+ default:
+ arg->self->data = raw;
+ }
+
+ 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);
+ return;
+ } else {
+ arg->self->q->emitFinishedWithError( QKeychain::EntryNotFound, QObject::tr("Entry not found") );
+ }
+ }
+ }
+ if (error) {
+ g_error_free (error);
+ }
+
+ if (password) {
+ secret_password_free_fn (password);
+ }
+
+ if (arg) {
+ delete arg;
+ }
+}
+
+static void
+on_password_stored (GObject *source,
+ GAsyncResult *result,
+ gpointer inst)
+{
+ GError *error = nullptr;
+ QKeychain::JobPrivate *self = (QKeychain::JobPrivate*)inst;
+
+ Q_UNUSED(source);
+
+ secret_password_store_finish_fn (result, &error);
+
+ if (self) {
+ if (error) {
+ self->q->emitFinishedWithError( gerrorToCode(error),
+ QString::fromUtf8(error->message) );
+ } else {
+ self->q->emitFinished();
+ }
+ }
+ if (error) {
+ g_error_free (error);
+ }
+}
+
+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);
+
+ Q_UNUSED(source);
+ if (self) {
+ if ( error ) {
+ self->q->emitFinishedWithError( gerrorToCode(error),
+ QString::fromUtf8(error->message) );
+ } else {
+ Q_UNUSED(removed);
+ self->q->emitFinished();
+ }
+ }
+ if (error) {
+ g_error_free (error);
+ }
+}
+
+static QString modeToString(QKeychain::JobPrivate::Mode mode) {
+ switch(mode) {
+ case QKeychain::JobPrivate::Binary:
+ return "base64";
+ default:
+ return "plaintext";
+ }
+}
+#endif
+
+bool LibSecretKeyring::isAvailable() {
+#if defined(HAVE_LIBSECRET)
+ const LibSecretKeyring& keyring = instance();
+ if (!keyring.isLoaded())
+ return false;
+ if (secret_password_lookup_fn == nullptr)
+ return false;
+ if (secret_password_lookup_finish_fn == nullptr)
+ return false;
+ if (secret_password_store_fn == nullptr)
+ return false;
+ if (secret_password_store_finish_fn == nullptr)
+ return false;
+ if (secret_password_clear_fn == nullptr)
+ return false;
+ if (secret_password_clear_finish_fn == nullptr)
+ return false;
+ if (secret_password_free_fn == nullptr)
+ return false;
+ if (secret_error_get_quark_fn == nullptr)
+ return false;
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool LibSecretKeyring::findPassword(const QString &user, const QString &server,
+ QKeychain::JobPrivate *self)
+{
+#if defined(HAVE_LIBSECRET)
+ if (!isAvailable()) {
+ return false;
+ }
+
+ self->mode = QKeychain::JobPrivate::Text;
+ self->data = QByteArray();
+
+ callbackArg *arg = new callbackArg;
+ arg->self = self;
+ 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);
+ return true;
+#else
+ Q_UNUSED(user)
+ Q_UNUSED(server)
+ Q_UNUSED(self)
+ return false;
+#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)
+{
+#if defined(HAVE_LIBSECRET)
+ if (!isAvailable()) {
+ return false;
+ }
+
+ QString type = modeToString(mode);
+ QByteArray pwd;
+ switch(mode) {
+ case QKeychain::JobPrivate::Binary:
+ pwd = password.toBase64();
+ break;
+ default:
+ pwd = password;
+ 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);
+ 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
+}
+
+bool LibSecretKeyring::deletePassword(const QString &key, const QString &service,
+ 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);
+ return true;
+#else
+ Q_UNUSED(key)
+ Q_UNUSED(service)
+ Q_UNUSED(self)
+ return false;
+#endif
+}
+
+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_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_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_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");
+ }
+#endif
+}
+
+LibSecretKeyring &LibSecretKeyring::instance() {
+ static LibSecretKeyring instance;
+
+ return instance;
+}
diff --git a/src/libs/3rdparty/qtkeychain/libsecret_p.h b/src/libs/3rdparty/qtkeychain/libsecret_p.h
new file mode 100644
index 00000000000..bd966fe0f7c
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/libsecret_p.h
@@ -0,0 +1,33 @@
+#ifndef QTKEYCHAIN_LIBSECRET_P_H
+#define QTKEYCHAIN_LIBSECRET_P_H
+
+#include <QLibrary>
+
+#include "keychain_p.h"
+
+class LibSecretKeyring : public QLibrary {
+public:
+ static bool isAvailable();
+
+ 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 deletePassword(const QString &key, const QString &service,
+ QKeychain::JobPrivate* self);
+
+private:
+ LibSecretKeyring();
+
+ static LibSecretKeyring &instance();
+};
+
+
+#endif
diff --git a/src/libs/3rdparty/qtkeychain/org.kde.KWallet.xml b/src/libs/3rdparty/qtkeychain/org.kde.KWallet.xml
new file mode 100644
index 00000000000..ec4bd6e9c32
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/org.kde.KWallet.xml
@@ -0,0 +1,276 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node>
+ <interface name="org.kde.KWallet">
+ <signal name="walletListDirty">
+ </signal>
+ <signal name="walletCreated">
+ <arg name="wallet" type="s" direction="out"/>
+ </signal>
+ <signal name="walletOpened">
+ <arg name="wallet" type="s" direction="out"/>
+ </signal>
+ <signal name="walletAsyncOpened">
+ <arg name="tId" type="i" direction="out"/>
+ <arg name="handle" type="i" direction="out"/>
+ </signal>
+ <signal name="walletDeleted">
+ <arg name="wallet" type="s" direction="out"/>
+ </signal>
+ <signal name="walletClosed">
+ <arg name="wallet" type="s" direction="out"/>
+ </signal>
+ <signal name="walletClosed">
+ <arg name="handle" type="i" direction="out"/>
+ </signal>
+ <signal name="allWalletsClosed">
+ </signal>
+ <signal name="folderListUpdated">
+ <arg name="wallet" type="s" direction="out"/>
+ </signal>
+ <signal name="folderUpdated">
+ <arg type="s" direction="out"/>
+ <arg type="s" direction="out"/>
+ </signal>
+ <signal name="applicationDisconnected">
+ <arg name="wallet" type="s" direction="out"/>
+ <arg name="application" type="s" direction="out"/>
+ </signal>
+ <method name="isEnabled">
+ <arg type="b" direction="out"/>
+ </method>
+ <method name="open">
+ <arg type="i" direction="out"/>
+ <arg name="wallet" type="s" direction="in"/>
+ <arg name="wId" type="x" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="openPath">
+ <arg type="i" direction="out"/>
+ <arg name="path" type="s" direction="in"/>
+ <arg name="wId" type="x" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="openAsync">
+ <arg type="i" direction="out"/>
+ <arg name="wallet" type="s" direction="in"/>
+ <arg name="wId" type="x" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ <arg name="handleSession" type="b" direction="in"/>
+ </method>
+ <method name="openPathAsync">
+ <arg type="i" direction="out"/>
+ <arg name="path" type="s" direction="in"/>
+ <arg name="wId" type="x" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ <arg name="handleSession" type="b" direction="in"/>
+ </method>
+ <method name="close">
+ <arg type="i" direction="out"/>
+ <arg name="wallet" type="s" direction="in"/>
+ <arg name="force" type="b" direction="in"/>
+ </method>
+ <method name="close">
+ <arg type="i" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="force" type="b" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="sync">
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ <annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
+ </method>
+ <method name="deleteWallet">
+ <arg type="i" direction="out"/>
+ <arg name="wallet" type="s" direction="in"/>
+ </method>
+ <method name="isOpen">
+ <arg type="b" direction="out"/>
+ <arg name="wallet" type="s" direction="in"/>
+ </method>
+ <method name="isOpen">
+ <arg type="b" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ </method>
+ <method name="users">
+ <arg type="as" direction="out"/>
+ <arg name="wallet" type="s" direction="in"/>
+ </method>
+ <method name="changePassword">
+ <arg name="wallet" type="s" direction="in"/>
+ <arg name="wId" type="x" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="wallets">
+ <arg type="as" direction="out"/>
+ </method>
+ <method name="folderList">
+ <arg type="as" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="hasFolder">
+ <arg type="b" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="createFolder">
+ <arg type="b" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="removeFolder">
+ <arg type="b" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="entryList">
+ <arg type="as" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="readEntry">
+ <arg type="ay" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="key" type="s" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="readMap">
+ <arg type="ay" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="key" type="s" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="readPassword">
+ <arg type="s" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="key" type="s" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="readEntryList">
+ <arg type="a{sv}" direction="out"/>
+ <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="key" type="s" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="readMapList">
+ <arg type="a{sv}" direction="out"/>
+ <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="key" type="s" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="readPasswordList">
+ <arg type="a{sv}" direction="out"/>
+ <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="key" type="s" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="renameEntry">
+ <arg type="i" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="oldName" type="s" direction="in"/>
+ <arg name="newName" type="s" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="writeEntry">
+ <arg type="i" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="key" type="s" direction="in"/>
+ <arg name="value" type="ay" direction="in"/>
+ <arg name="entryType" type="i" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="writeEntry">
+ <arg type="i" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="key" type="s" direction="in"/>
+ <arg name="value" type="ay" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="writeMap">
+ <arg type="i" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="key" type="s" direction="in"/>
+ <arg name="value" type="ay" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="writePassword">
+ <arg type="i" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="key" type="s" direction="in"/>
+ <arg name="value" type="s" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="hasEntry">
+ <arg type="b" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="key" type="s" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="entryType">
+ <arg type="i" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="key" type="s" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="removeEntry">
+ <arg type="i" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="key" type="s" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="disconnectApplication">
+ <arg type="b" direction="out"/>
+ <arg name="wallet" type="s" direction="in"/>
+ <arg name="application" type="s" direction="in"/>
+ </method>
+ <method name="reconfigure">
+ </method>
+ <method name="folderDoesNotExist">
+ <arg type="b" direction="out"/>
+ <arg name="wallet" type="s" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ </method>
+ <method name="keyDoesNotExist">
+ <arg type="b" direction="out"/>
+ <arg name="wallet" type="s" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="key" type="s" direction="in"/>
+ </method>
+ <method name="closeAllWallets">
+ </method>
+ <method name="networkWallet">
+ <arg type="s" direction="out"/>
+ </method>
+ <method name="localWallet">
+ <arg type="s" direction="out"/>
+ </method>
+ <method name="pamOpen">
+ <arg name="wallet" type="s" direction="in"/>
+ <arg name="passwordHash" type="ay" direction="in"/>
+ <arg name="sessionTimeout" type="i" direction="in"/>
+ <annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
+ </method>
+ </interface>
+</node>
diff --git a/src/libs/3rdparty/qtkeychain/plaintextstore.cpp b/src/libs/3rdparty/qtkeychain/plaintextstore.cpp
new file mode 100644
index 00000000000..b9d027264d4
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/plaintextstore.cpp
@@ -0,0 +1,110 @@
+/******************************************************************************
+ * Copyright (C) 2011-2015 Frank Osterfeld <frank.osterfeld@gmail.com> *
+ * Copyright (C) 2016 Mathias Hasselmann <mathias.hasselmann@kdab.com> *
+ * *
+ * 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 "plaintextstore_p.h"
+
+using namespace QKeychain;
+
+namespace {
+#ifdef Q_OS_WIN
+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
+}
+
+
+PlainTextStore::PlainTextStore(const QString &service, QSettings *settings)
+ : m_localSettings(settings ? 0 : new QSettings(service))
+ , m_actualSettings(settings ? settings : m_localSettings.data())
+ , m_error(NoError)
+{
+}
+
+bool PlainTextStore::contains(const QString &key) const
+{
+ return m_actualSettings->contains(dataKey(key));
+}
+
+QByteArray PlainTextStore::readData(const QString &key)
+{
+ return read(dataKey(key)).toByteArray();
+}
+
+#ifndef Q_OS_WIN
+
+JobPrivate::Mode PlainTextStore::readMode(const QString &key)
+{
+ return JobPrivate::stringToMode(read(typeKey(key)).toString());
+}
+
+#endif // Q_OS_WIN
+
+void PlainTextStore::write(const QString &key, const QByteArray &data, JobPrivate::Mode mode)
+{
+ if (m_actualSettings->status() != QSettings::NoError)
+ return;
+
+#ifndef Q_OS_WIN
+ m_actualSettings->setValue(typeKey(key), JobPrivate::modeToString(mode));
+#else // Q_OS_WIN
+ Q_UNUSED(mode);
+#endif // Q_OS_WIN
+ m_actualSettings->setValue(dataKey(key), data);
+ m_actualSettings->sync();
+
+ if (m_actualSettings->status() == QSettings::AccessError) {
+ setError(AccessDenied, tr("Could not store data in settings: access error"));
+ } else if (m_actualSettings->status() != QSettings::NoError) {
+ setError(OtherError, tr("Could not store data in settings: format error"));
+ } else {
+ setError(NoError, QString());
+ }
+}
+
+void PlainTextStore::remove(const QString &key)
+{
+ if (m_actualSettings->status() != QSettings::NoError)
+ return;
+
+#ifndef Q_OS_WIN
+ m_actualSettings->remove(typeKey(key));
+#endif // Q_OS_WIN
+ m_actualSettings->remove(dataKey(key));
+ m_actualSettings->sync();
+
+ if (m_actualSettings->status() == QSettings::AccessError) {
+ setError(AccessDenied, tr("Could not delete data from settings: access error"));
+ } else if (m_actualSettings->status() != QSettings::NoError) {
+ setError(OtherError, tr("Could not delete data from settings: format error"));
+ } else {
+ setError(NoError, QString());
+ }
+}
+
+void PlainTextStore::setError(Error error, const QString &errorString)
+{
+ m_error = error;
+ m_errorString = errorString;
+}
+
+QVariant PlainTextStore::read(const QString &key)
+{
+ const QVariant value = m_actualSettings->value(key);
+
+ if (value.isNull()) {
+ setError(EntryNotFound, tr("Entry not found"));
+ } else {
+ setError(NoError, QString());
+ }
+
+ return value;
+}
diff --git a/src/libs/3rdparty/qtkeychain/plaintextstore_p.h b/src/libs/3rdparty/qtkeychain/plaintextstore_p.h
new file mode 100644
index 00000000000..7ec05aaca17
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/plaintextstore_p.h
@@ -0,0 +1,47 @@
+/******************************************************************************
+ * Copyright (C) 2011-2015 Frank Osterfeld <frank.osterfeld@gmail.com> *
+ * Copyright (C) 2016 Mathias Hasselmann <mathias.hasselmann@kdab.com> *
+ * *
+ * 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_PLAINTEXTSTORE_P_H
+#define QTKEYCHAIN_PLAINTEXTSTORE_P_H
+
+#include "keychain_p.h"
+
+namespace QKeychain {
+
+class PlainTextStore {
+ Q_DECLARE_TR_FUNCTIONS(QKeychain::PlainTextStore)
+
+public:
+ explicit PlainTextStore(const QString &service, QSettings *settings);
+
+ Error error() const { return m_error; }
+ QString errorString() const { return m_errorString; }
+
+ bool contains(const QString &key) const;
+
+ QByteArray readData(const QString &key);
+ JobPrivate::Mode readMode(const QString &key);
+
+ void write(const QString &key, const QByteArray &data, JobPrivate::Mode mode);
+ void remove(const QString &key);
+
+private:
+ void setError(Error error, const QString &errorString);
+ QVariant read(const QString &key);
+
+ const QScopedPointer<QSettings> m_localSettings;
+ QSettings *const m_actualSettings;
+ QString m_errorString;
+ Error m_error;
+};
+
+}
+
+#endif // QTKEYCHAIN_PLAINTEXTSTORE_P_H
diff --git a/src/libs/3rdparty/qtkeychain/qkeychain_export.h b/src/libs/3rdparty/qtkeychain/qkeychain_export.h
new file mode 100644
index 00000000000..14dc72287fb
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/qkeychain_export.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include <qglobal.h>
+
+#if defined(QTKEYCHAIN_LIBRARY)
+# define QKEYCHAIN_EXPORT Q_DECL_EXPORT
+#elif defined(QTKEYCHAIN_STATIC_LIBRARY)
+# define QKEYCHAIN_EXPORT
+#else
+# define QKEYCHAIN_EXPORT Q_DECL_IMPORT
+#endif
diff --git a/src/libs/3rdparty/qtkeychain/translations/qtkeychain_de.ts b/src/libs/3rdparty/qtkeychain/translations/qtkeychain_de.ts
new file mode 100644
index 00000000000..59ed992ed1b
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/translations/qtkeychain_de.ts
@@ -0,0 +1,234 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>QKeychain::DeletePasswordJobPrivate</name>
+ <message>
+ <location filename="../keychain_win.cpp" line="104"/>
+ <source>Password entry not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="108"/>
+ <source>Could not decrypt data</source>
+ <translation type="unfinished">Kann Daten nicht entschlüsseln</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="585"/>
+ <location filename="../keychain_unix.cpp" line="593"/>
+ <source>Unknown error</source>
+ <translation type="unfinished">Unbekannter Fehler</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="614"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation type="unfinished">Konnte Brieftasche nicht öffnen: %1; %2</translation>
+ </message>
+ <message>
+ <source>Password not found</source>
+ <translation type="obsolete">Passwort nicht gefunden</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::JobPrivate</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="295"/>
+ <source>Unknown error</source>
+ <translation type="unfinished">Unbekannter Fehler</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="542"/>
+ <source>Access to keychain denied</source>
+ <translation type="unfinished">Zugriff auf Schlüsselbund verweigert</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::PlainTextStore</name>
+ <message>
+ <location filename="../plaintextstore.cpp" line="65"/>
+ <source>Could not store data in settings: access error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="67"/>
+ <source>Could not store data in settings: format error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="85"/>
+ <source>Could not delete data from settings: access error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="87"/>
+ <source>Could not delete data from settings: format error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="104"/>
+ <source>Entry not found</source>
+ <translation type="unfinished">Eintrag nicht gefunden</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::ReadPasswordJobPrivate</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="214"/>
+ <location filename="../keychain_unix.cpp" line="224"/>
+ <source>Unknown error</source>
+ <translation>Unbekannter Fehler</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="205"/>
+ <source>D-Bus is not running</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="316"/>
+ <source>No keychain service available</source>
+ <translation>Kein Schlüsselbund-Dienst verfügbar</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="318"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation>Konnte Brieftasche nicht öffnen: %1; %2</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="363"/>
+ <source>Access to keychain denied</source>
+ <translation>Zugriff auf Schlüsselbund verweigert</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="384"/>
+ <source>Could not determine data type: %1; %2</source>
+ <translation>Datentyp kann nicht ermittelt werden: %1: %2</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="402"/>
+ <source>Unsupported entry type &apos;Map&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="405"/>
+ <source>Unknown kwallet entry type &apos;%1&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="393"/>
+ <source>Entry not found</source>
+ <translation>Eintrag nicht gefunden</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="32"/>
+ <source>Password entry not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="36"/>
+ <location filename="../keychain_win.cpp" line="139"/>
+ <source>Could not decrypt data</source>
+ <translation>Kann Daten nicht entschlüsseln</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::WritePasswordJobPrivate</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="455"/>
+ <location filename="../keychain_unix.cpp" line="482"/>
+ <source>Unknown error</source>
+ <translation>Unbekannter Fehler</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="445"/>
+ <source>D-Bus is not running</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="501"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation>Konnte Brieftasche nicht öffnen: %1; %2</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="78"/>
+ <source>Credential size exceeds maximum size of %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="87"/>
+ <source>Credential key exceeds maximum size of %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="92"/>
+ <source>Writing credentials failed: Win32 error code %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="162"/>
+ <source>Encryption failed</source>
+ <translation>Verschlüsselung fehlgeschlagen</translation>
+ </message>
+ <message>
+ <source>Password not found</source>
+ <translation type="obsolete">Passwort nicht gefunden</translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="255"/>
+ <source>Access to keychain denied</source>
+ <translation>Zugriff auf Schlüsselbund verweigert</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="257"/>
+ <source>No keyring daemon</source>
+ <translation>Kein Schlüsselbund-Dienst </translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="259"/>
+ <source>Already unlocked</source>
+ <translation>Bereits entsperrt</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="261"/>
+ <source>No such keyring</source>
+ <translation>Kein solcher Schlüsselbund</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="263"/>
+ <source>Bad arguments</source>
+ <translation>Ungültige Argumente</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="265"/>
+ <source>I/O error</source>
+ <translation>Ein-/Ausgabe-Fehler</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="267"/>
+ <source>Cancelled</source>
+ <translation>Abgebrochen</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="269"/>
+ <source>Keyring already exists</source>
+ <translation>Schlüsselbund existiert bereits</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="271"/>
+ <source>No match</source>
+ <translation>Kein Treffer</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="276"/>
+ <source>Unknown error</source>
+ <translation>Unbekannter Fehler</translation>
+ </message>
+ <message>
+ <location filename="../libsecret.cpp" line="119"/>
+ <source>Entry not found</source>
+ <translation type="unfinished">Eintrag nicht gefunden</translation>
+ </message>
+</context>
+</TS>
diff --git a/src/libs/3rdparty/qtkeychain/translations/qtkeychain_fr.ts b/src/libs/3rdparty/qtkeychain/translations/qtkeychain_fr.ts
new file mode 100644
index 00000000000..5eee2e34daa
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/translations/qtkeychain_fr.ts
@@ -0,0 +1,226 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="fr_FR">
+<context>
+ <name>QKeychain::DeletePasswordJobPrivate</name>
+ <message>
+ <location filename="../keychain_win.cpp" line="104"/>
+ <source>Password entry not found</source>
+ <translation>Mot de passe introuvable</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="108"/>
+ <source>Could not decrypt data</source>
+ <translation>Impossible de déchiffrer les données</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="585"/>
+ <location filename="../keychain_unix.cpp" line="593"/>
+ <source>Unknown error</source>
+ <translation>Erreur inconnue</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="614"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation>Impossible d&apos;ouvrir le portefeuille : %1; %2</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::JobPrivate</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="295"/>
+ <source>Unknown error</source>
+ <translation>Erreur inconnue</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="542"/>
+ <source>Access to keychain denied</source>
+ <translation>Accès au trousseau refusé</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::PlainTextStore</name>
+ <message>
+ <location filename="../plaintextstore.cpp" line="65"/>
+ <source>Could not store data in settings: access error</source>
+ <translation>Impossible de stocker les données dans les paramètres : Erreur d&apos;accès</translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="67"/>
+ <source>Could not store data in settings: format error</source>
+ <translation>Impossible de stocker les données dans les paramètres : Erreur de format</translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="85"/>
+ <source>Could not delete data from settings: access error</source>
+ <translation>Impossible de supprimer les données depuis les paramètres : Erreur d&apos;accès</translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="87"/>
+ <source>Could not delete data from settings: format error</source>
+ <translation>Impossible de supprimer les données depuis les paramètres : Erreur de format</translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="104"/>
+ <source>Entry not found</source>
+ <translation>Entrée introuvable</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::ReadPasswordJobPrivate</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="214"/>
+ <location filename="../keychain_unix.cpp" line="224"/>
+ <source>Unknown error</source>
+ <translation>Erreur inconnue</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="205"/>
+ <source>D-Bus is not running</source>
+ <translation>D-Bus n&apos;est pas en cours d&apos;exécution</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="316"/>
+ <source>No keychain service available</source>
+ <translation>Aucun service de trousseau disponible</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="318"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation>Impossible d&apos;ouvrir le trousseau : %1; %2</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="363"/>
+ <source>Access to keychain denied</source>
+ <translation>Accès au trousseau refusé</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="384"/>
+ <source>Could not determine data type: %1; %2</source>
+ <translation>Impossible de déterminer le type de données : %1: %2</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="402"/>
+ <source>Unsupported entry type &apos;Map&apos;</source>
+ <translation>Type d&apos;entrée non supporté &apos;Map&apos;</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="405"/>
+ <source>Unknown kwallet entry type &apos;%1&apos;</source>
+ <translation>Type de trousseau inconnu &apos;%1&apos;</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="393"/>
+ <source>Entry not found</source>
+ <translation>Entrée introuvable</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="32"/>
+ <source>Password entry not found</source>
+ <translation>Entrée de mot de passe introuvable</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="36"/>
+ <location filename="../keychain_win.cpp" line="139"/>
+ <source>Could not decrypt data</source>
+ <translation>Impossible de déchiffrer les données</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::WritePasswordJobPrivate</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="455"/>
+ <location filename="../keychain_unix.cpp" line="482"/>
+ <source>Unknown error</source>
+ <translation>Erreur inconnue</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="445"/>
+ <source>D-Bus is not running</source>
+ <translation>D-Bus n&apos;est pas en cours d&apos;exécution</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="501"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation>Impossible d&apos;ouvrir le trousseau : %1; %2</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="78"/>
+ <source>Credential size exceeds maximum size of %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="87"/>
+ <source>Credential key exceeds maximum size of %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="92"/>
+ <source>Writing credentials failed: Win32 error code %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="162"/>
+ <source>Encryption failed</source>
+ <translation>Le chiffrement a échoué</translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="255"/>
+ <source>Access to keychain denied</source>
+ <translation>Accès au trousseau refusé</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="257"/>
+ <source>No keyring daemon</source>
+ <translation>Aucun démon de trousseau</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="259"/>
+ <source>Already unlocked</source>
+ <translation>Déjà déverrouillé</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="261"/>
+ <source>No such keyring</source>
+ <translation>Aucun trousseau</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="263"/>
+ <source>Bad arguments</source>
+ <translation>Mauvais arguments</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="265"/>
+ <source>I/O error</source>
+ <translation>Erreur d&apos;E/S</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="267"/>
+ <source>Cancelled</source>
+ <translation>Annulé</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="269"/>
+ <source>Keyring already exists</source>
+ <translation>Trousseau déjà existant</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="271"/>
+ <source>No match</source>
+ <translation>Aucune correspondance</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="276"/>
+ <source>Unknown error</source>
+ <translation>Erreur inconnue</translation>
+ </message>
+ <message>
+ <location filename="../libsecret.cpp" line="119"/>
+ <source>Entry not found</source>
+ <translation>Entrée introuvable</translation>
+ </message>
+</context>
+</TS>
diff --git a/src/libs/3rdparty/qtkeychain/translations/qtkeychain_nl.ts b/src/libs/3rdparty/qtkeychain/translations/qtkeychain_nl.ts
new file mode 100644
index 00000000000..7033a98457b
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/translations/qtkeychain_nl.ts
@@ -0,0 +1,320 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="nl_NL">
+<context>
+ <name>KeyChainClass</name>
+ <message>
+ <location filename="TestAppExample/keychainclass.cpp" line="22"/>
+ <source>Read key failed: %1</source>
+ <translation>De sleutel kan niet worden ingelezen: %1</translation>
+ </message>
+ <message>
+ <location filename="TestAppExample/keychainclass.cpp" line="37"/>
+ <source>Write key failed: %1</source>
+ <translation>De sleutel kan niet worden weggeschreven: %1</translation>
+ </message>
+ <message>
+ <location filename="TestAppExample/keychainclass.cpp" line="54"/>
+ <source>Delete key failed: %1</source>
+ <translation>De sleutel kan niet worden verwijderd: %1</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::DeletePasswordJobPrivate</name>
+ <message>
+ <location filename="keychain_android.cpp" line="173"/>
+ <source>Could not open keystore</source>
+ <translation>De sleutelbos kan niet worden geopend</translation>
+ </message>
+ <message>
+ <location filename="keychain_android.cpp" line="179"/>
+ <source>Could not remove private key from keystore</source>
+ <translation>De privésleutel kan niet worden verwijderd uit de sleutelbos</translation>
+ </message>
+ <message>
+ <location filename="keychain_haiku.cpp" line="177"/>
+ <source>Password not found</source>
+ <translation>Het wachtwoord is niet gevonden</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="552"/>
+ <location filename="keychain_unix.cpp" line="560"/>
+ <source>Unknown error</source>
+ <translation>Onbekende foutmelding</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="578"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation>De sleutelbos kan niet worden geopend: %1; %2</translation>
+ </message>
+ <message>
+ <location filename="keychain_win.cpp" line="104"/>
+ <source>Password entry not found</source>
+ <translation>Het wachtwoord is niet gevonden</translation>
+ </message>
+ <message>
+ <location filename="keychain_win.cpp" line="108"/>
+ <source>Could not decrypt data</source>
+ <translation>De gegevens kunnen niet worden ongrendeld</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::JobPrivate</name>
+ <message>
+ <location filename="keychain_unix.cpp" line="265"/>
+ <source>Unknown error</source>
+ <translation>Onbekende foutmelding</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="509"/>
+ <source>Access to keychain denied</source>
+ <translation>U heeft geen toegang tot de sleutelbos</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::PlainTextStore</name>
+ <message>
+ <location filename="plaintextstore.cpp" line="65"/>
+ <source>Could not store data in settings: access error</source>
+ <translation>De gegevens kunnen niet worden opgeslagen in de instellingen: toegangsfout</translation>
+ </message>
+ <message>
+ <location filename="plaintextstore.cpp" line="67"/>
+ <source>Could not store data in settings: format error</source>
+ <translation>De gegevens kunnen niet worden opgeslagen in de instellingen: opmaakfout</translation>
+ </message>
+ <message>
+ <location filename="plaintextstore.cpp" line="85"/>
+ <source>Could not delete data from settings: access error</source>
+ <translation>De gegevens kunnen niet worden verwijderd uit de instellingen: toegangsfout</translation>
+ </message>
+ <message>
+ <location filename="plaintextstore.cpp" line="87"/>
+ <source>Could not delete data from settings: format error</source>
+ <translation>De gegevens kunnen niet worden verwijderd uit de instellingen: opmaakfout</translation>
+ </message>
+ <message>
+ <location filename="plaintextstore.cpp" line="104"/>
+ <source>Entry not found</source>
+ <translation>Het item is niet gevonden</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::ReadPasswordJobPrivate</name>
+ <message>
+ <location filename="keychain_android.cpp" line="52"/>
+ <location filename="keychain_unix.cpp" line="363"/>
+ <source>Entry not found</source>
+ <translation>Het item is niet gevonden</translation>
+ </message>
+ <message>
+ <location filename="keychain_android.cpp" line="60"/>
+ <source>Could not open keystore</source>
+ <translation>De sleutelbos kan niet worden geopend</translation>
+ </message>
+ <message>
+ <location filename="keychain_android.cpp" line="68"/>
+ <source>Could not retrieve private key from keystore</source>
+ <translation>De privésleutel kan niet worden verwijderd uit de sleutelbos</translation>
+ </message>
+ <message>
+ <location filename="keychain_android.cpp" line="75"/>
+ <source>Could not create decryption cipher</source>
+ <translation>De ontgrendelcode kan niet worden aangemaakt</translation>
+ </message>
+ <message>
+ <location filename="keychain_haiku.cpp" line="96"/>
+ <source>Password not found</source>
+ <translation>Het wachtwoord is niet gevonden</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="178"/>
+ <source>D-Bus is not running</source>
+ <translation>D-Bus is niet actief</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="187"/>
+ <location filename="keychain_unix.cpp" line="197"/>
+ <source>Unknown error</source>
+ <translation>Onbekende foutmelding</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="286"/>
+ <source>No keychain service available</source>
+ <translation>De sleutelbosdienst is niet beschikbaar</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="288"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation>De sleutelbos kan niet worden geopend: %1; %2</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="333"/>
+ <source>Access to keychain denied</source>
+ <translation>U heeft geen toegang tot de sleutelbos</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="354"/>
+ <source>Could not determine data type: %1; %2</source>
+ <translation>De gegevensdrager kan niet worden bepaald: %1; %2</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="372"/>
+ <source>Unsupported entry type &apos;Map&apos;</source>
+ <translation>Niet-ondersteund itemtype ‘Map’</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="375"/>
+ <source>Unknown kwallet entry type &apos;%1&apos;</source>
+ <translation>Onbekend KWallet-item van type ‘%1’</translation>
+ </message>
+ <message>
+ <location filename="keychain_win.cpp" line="32"/>
+ <source>Password entry not found</source>
+ <translation>Het wachtwoord is niet gevonden</translation>
+ </message>
+ <message>
+ <location filename="keychain_win.cpp" line="36"/>
+ <location filename="keychain_win.cpp" line="139"/>
+ <source>Could not decrypt data</source>
+ <translation>De gegevens kunnen niet worden ongrendeld</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::WritePasswordJobPrivate</name>
+ <message>
+ <location filename="keychain_android.cpp" line="95"/>
+ <source>Could not open keystore</source>
+ <translation>De sleutelbos kan niet worden geopend</translation>
+ </message>
+ <message>
+ <location filename="keychain_android.cpp" line="124"/>
+ <source>Could not create private key generator</source>
+ <translation>De privésleutelgenerator kan niet worden gestart</translation>
+ </message>
+ <message>
+ <location filename="keychain_android.cpp" line="131"/>
+ <source>Could not generate new private key</source>
+ <translation>Er kan geen nieuwe privésleutel worden gegenereerd</translation>
+ </message>
+ <message>
+ <location filename="keychain_android.cpp" line="139"/>
+ <source>Could not retrieve private key from keystore</source>
+ <translation>De privésleutel kan niet worden opgehaald uit de sleutelbos</translation>
+ </message>
+ <message>
+ <location filename="keychain_android.cpp" line="147"/>
+ <source>Could not create encryption cipher</source>
+ <translation>De vergrendelcode kan niet worden aangemaakt</translation>
+ </message>
+ <message>
+ <location filename="keychain_android.cpp" line="155"/>
+ <source>Could not encrypt data</source>
+ <translation>De gegevens kunnen niet worden ontgrendeld</translation>
+ </message>
+ <message>
+ <location filename="keychain_haiku.cpp" line="144"/>
+ <source>Password not found</source>
+ <translation>Het wachtwoord is niet gevonden</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="415"/>
+ <source>D-Bus is not running</source>
+ <translation>D-Bus is niet actief</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="425"/>
+ <location filename="keychain_unix.cpp" line="452"/>
+ <source>Unknown error</source>
+ <translation>Onbekende foutmelding</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="468"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation>De sleutelbos kan niet worden geopend: %1; %2</translation>
+ </message>
+ <message>
+ <location filename="keychain_win.cpp" line="78"/>
+ <source>Credential size exceeds maximum size of %1</source>
+ <translation>De omvang overschrijdt de maximumomvang van %1</translation>
+ </message>
+ <message>
+ <location filename="keychain_win.cpp" line="87"/>
+ <source>Credential key exceeds maximum size of %1</source>
+ <translation>De sleutel overschrijdt de maximumomvang van %1</translation>
+ </message>
+ <message>
+ <location filename="keychain_win.cpp" line="92"/>
+ <source>Writing credentials failed: Win32 error code %1</source>
+ <translation>De gegevens kunnen niet worden weggeschreven: Win32-foutcode %1</translation>
+ </message>
+ <message>
+ <location filename="keychain_win.cpp" line="162"/>
+ <source>Encryption failed</source>
+ <translation>Het ontgrendelen is mislukt</translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="keychain_haiku.cpp" line="72"/>
+ <source>error 0x%1: %2</source>
+ <translation>foutmelding 0x%1: %2</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="225"/>
+ <source>Access to keychain denied</source>
+ <translation>U heeft geen toegang tot de sleutelbos</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="227"/>
+ <source>No keyring daemon</source>
+ <translation>De sleutelbosdienst is niet actief</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="229"/>
+ <source>Already unlocked</source>
+ <translation>De sleutelbos is reeds ontgrendeld</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="231"/>
+ <source>No such keyring</source>
+ <translation>De sleutelbos bestaat niet</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="233"/>
+ <source>Bad arguments</source>
+ <translation>Onjuiste opties</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="235"/>
+ <source>I/O error</source>
+ <translation>I/O-fout</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="237"/>
+ <source>Cancelled</source>
+ <translation>Geannuleerd</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="239"/>
+ <source>Keyring already exists</source>
+ <translation>De sleutelbos bestaat reeds</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="241"/>
+ <source>No match</source>
+ <translation>Er zijn geen overeenkomsten</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="246"/>
+ <source>Unknown error</source>
+ <translation>Onbekende foutmelding</translation>
+ </message>
+ <message>
+ <location filename="libsecret.cpp" line="119"/>
+ <source>Entry not found</source>
+ <translation>Het item is niet gevonden</translation>
+ </message>
+</context>
+</TS>
diff --git a/src/libs/3rdparty/qtkeychain/translations/qtkeychain_ro.ts b/src/libs/3rdparty/qtkeychain/translations/qtkeychain_ro.ts
new file mode 100644
index 00000000000..fb4e5906764
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/translations/qtkeychain_ro.ts
@@ -0,0 +1,235 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="ro_RO">
+<context>
+ <name>QKeychain::DeletePasswordJobPrivate</name>
+ <message>
+ <location filename="../keychain_win.cpp" line="104"/>
+ <source>Password entry not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="108"/>
+ <source>Could not decrypt data</source>
+ <translation type="unfinished">Nu se poate decripta data</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="585"/>
+ <location filename="../keychain_unix.cpp" line="593"/>
+ <source>Unknown error</source>
+ <translation type="unfinished">Eroare necunoscută</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="614"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation type="unfinished">Nu se poate deschide portofelul: %1; %2</translation>
+ </message>
+ <message>
+ <source>Password not found</source>
+ <translation type="obsolete">Parola nu a fost găsită</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::JobPrivate</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="295"/>
+ <source>Unknown error</source>
+ <translation type="unfinished">Eroare necunoscută</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="542"/>
+ <source>Access to keychain denied</source>
+ <translation type="unfinished">Acces interzis la serviciul de chei</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::PlainTextStore</name>
+ <message>
+ <location filename="../plaintextstore.cpp" line="65"/>
+ <source>Could not store data in settings: access error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="67"/>
+ <source>Could not store data in settings: format error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="85"/>
+ <source>Could not delete data from settings: access error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="87"/>
+ <source>Could not delete data from settings: format error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="104"/>
+ <source>Entry not found</source>
+ <translation type="unfinished">Înregistrarea nu a fost găsită</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::ReadPasswordJobPrivate</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="214"/>
+ <location filename="../keychain_unix.cpp" line="224"/>
+ <source>Unknown error</source>
+ <translation>Eroare necunoscută</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="205"/>
+ <source>D-Bus is not running</source>
+ <translation>D-Bus nu rulează</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="316"/>
+ <source>No keychain service available</source>
+ <translatorcomment>Nu există niciun serviciu de chei disponibil</translatorcomment>
+ <translation>Kein Schlüsselbund-Dienst verfügbar</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="318"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation>Nu se poate deschide portofelul: %1; %2</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="363"/>
+ <source>Access to keychain denied</source>
+ <translation>Acces interzis la serviciul de chei</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="384"/>
+ <source>Could not determine data type: %1; %2</source>
+ <translation>Nu se poate stabili tipul de date: %1: %2</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="402"/>
+ <source>Unsupported entry type &apos;Map&apos;</source>
+ <translation>Tip de înregistrare nesuportat &apos;Map&apos;</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="405"/>
+ <source>Unknown kwallet entry type &apos;%1&apos;</source>
+ <translation>Tip de înregistrare kwallet necunoscut &apos;%1&apos;</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="393"/>
+ <source>Entry not found</source>
+ <translation>Înregistrarea nu a fost găsită</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="32"/>
+ <source>Password entry not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="36"/>
+ <location filename="../keychain_win.cpp" line="139"/>
+ <source>Could not decrypt data</source>
+ <translation>Nu se poate decripta data</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::WritePasswordJobPrivate</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="455"/>
+ <location filename="../keychain_unix.cpp" line="482"/>
+ <source>Unknown error</source>
+ <translation>Eroare necunoscută</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="445"/>
+ <source>D-Bus is not running</source>
+ <translation>D-Bus nu rulează</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="501"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation>Nu se poate deschide portofelul: %1; %2</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="78"/>
+ <source>Credential size exceeds maximum size of %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="87"/>
+ <source>Credential key exceeds maximum size of %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="92"/>
+ <source>Writing credentials failed: Win32 error code %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="162"/>
+ <source>Encryption failed</source>
+ <translation>Criptarea a eșuat</translation>
+ </message>
+ <message>
+ <source>Password not found</source>
+ <translation type="obsolete">Parola nu a fost găsită</translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="255"/>
+ <source>Access to keychain denied</source>
+ <translation>Acces interzis la serviciul de chei</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="257"/>
+ <source>No keyring daemon</source>
+ <translation>Niciun demon pentru inelul de chei</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="259"/>
+ <source>Already unlocked</source>
+ <translation>Deja deblocat</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="261"/>
+ <source>No such keyring</source>
+ <translation>Nu există astfel de inel de chei</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="263"/>
+ <source>Bad arguments</source>
+ <translation>Argumente greșite</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="265"/>
+ <source>I/O error</source>
+ <translation>Eroare de I/E</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="267"/>
+ <source>Cancelled</source>
+ <translation>Anulat</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="269"/>
+ <source>Keyring already exists</source>
+ <translation>Inelul de chei deja există</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="271"/>
+ <source>No match</source>
+ <translation>Nicio potrivire</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="276"/>
+ <source>Unknown error</source>
+ <translation>Eroare necunoscută</translation>
+ </message>
+ <message>
+ <location filename="../libsecret.cpp" line="119"/>
+ <source>Entry not found</source>
+ <translation type="unfinished">Înregistrarea nu a fost găsită</translation>
+ </message>
+</context>
+</TS>
diff --git a/src/libs/3rdparty/qtkeychain/translations/qtkeychain_ru.ts b/src/libs/3rdparty/qtkeychain/translations/qtkeychain_ru.ts
new file mode 100644
index 00000000000..c4cec0bb52f
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/translations/qtkeychain_ru.ts
@@ -0,0 +1,234 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="ru_RU">
+<context>
+ <name>QKeychain::DeletePasswordJobPrivate</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="585"/>
+ <location filename="../keychain_unix.cpp" line="593"/>
+ <source>Unknown error</source>
+ <translation>Неизвестная ошибка</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="614"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation>Не удалось открыть бумажник: %1; %2</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="104"/>
+ <source>Password entry not found</source>
+ <translation>Пароль не найден</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="108"/>
+ <source>Could not decrypt data</source>
+ <translation>Не удалось расшифровать данные</translation>
+ </message>
+ <message>
+ <source>Password not found</source>
+ <translation type="obsolete">Пароль не найден</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::JobPrivate</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="295"/>
+ <source>Unknown error</source>
+ <translation>Неизвестная ошибка</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="542"/>
+ <source>Access to keychain denied</source>
+ <translation>Доступ к связке ключей запрещён</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::PlainTextStore</name>
+ <message>
+ <location filename="../plaintextstore.cpp" line="65"/>
+ <source>Could not store data in settings: access error</source>
+ <translation>Не удалось сохранить данные в настройках: ошибка доступа</translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="67"/>
+ <source>Could not store data in settings: format error</source>
+ <translation>Не удалось сохранить данные в настройках: ошибка формата</translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="85"/>
+ <source>Could not delete data from settings: access error</source>
+ <translation>Не удалось удалить данные из настроек: ошибка доступа</translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="87"/>
+ <source>Could not delete data from settings: format error</source>
+ <translation>Не удалось удалить данные из настроек: ошибка формата</translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="104"/>
+ <source>Entry not found</source>
+ <translation>Запись не найдена</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::ReadPasswordJobPrivate</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="205"/>
+ <source>D-Bus is not running</source>
+ <translation>D-Bus не запущен</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="214"/>
+ <location filename="../keychain_unix.cpp" line="224"/>
+ <source>Unknown error</source>
+ <translation>Неизвестная ошибка</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="316"/>
+ <source>No keychain service available</source>
+ <translation>Служба связки ключей недоступна</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="318"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation>Не удалось открыть кошелёк: %1; %2</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="363"/>
+ <source>Access to keychain denied</source>
+ <translation>Доступ к связке ключей запрещён</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="384"/>
+ <source>Could not determine data type: %1; %2</source>
+ <translation>Не удалось определить тип данных: %1; %2</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="393"/>
+ <source>Entry not found</source>
+ <translation>Запись не найдена</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="402"/>
+ <source>Unsupported entry type &apos;Map&apos;</source>
+ <translation>Неподдерживаемый тип записи &apos;Map&apos;</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="405"/>
+ <source>Unknown kwallet entry type &apos;%1&apos;</source>
+ <translation>Неизвестный тип записи kwallet &apos;%1&apos;</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="32"/>
+ <source>Password entry not found</source>
+ <translation>Пароль не найден</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="36"/>
+ <location filename="../keychain_win.cpp" line="139"/>
+ <source>Could not decrypt data</source>
+ <translation>Не удалось расшифровать данные</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::WritePasswordJobPrivate</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="445"/>
+ <source>D-Bus is not running</source>
+ <translation>D-Bus не запущен</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="455"/>
+ <location filename="../keychain_unix.cpp" line="482"/>
+ <source>Unknown error</source>
+ <translation>Неизвестная ошибка</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="501"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation>Не удалось открыть кошелёк: %1; %2</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="78"/>
+ <source>Credential size exceeds maximum size of %1</source>
+ <translation>Учётные данные превышают максимальный размер %1</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="87"/>
+ <source>Credential key exceeds maximum size of %1</source>
+ <translation>Ключ учётных данных превышает максимальный размер %1</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="92"/>
+ <source>Writing credentials failed: Win32 error code %1</source>
+ <translation>Не удалось сохранить учётные данные: код ошибки win32 %1</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="162"/>
+ <source>Encryption failed</source>
+ <translation>Шифрование не удалось</translation>
+ </message>
+ <message>
+ <source>Password not found</source>
+ <translation type="obsolete">Пароль не найден</translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="255"/>
+ <source>Access to keychain denied</source>
+ <translation>Доступ к связке ключей запрещён</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="257"/>
+ <source>No keyring daemon</source>
+ <translation>Нет демона связки ключей</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="259"/>
+ <source>Already unlocked</source>
+ <translation>Уже разблокировано</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="261"/>
+ <source>No such keyring</source>
+ <translation>Связка ключей не найдена</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="263"/>
+ <source>Bad arguments</source>
+ <translation>Неверные аргументы</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="265"/>
+ <source>I/O error</source>
+ <translation>Ошибка ввода/вывода</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="267"/>
+ <source>Cancelled</source>
+ <translation>Отменено</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="269"/>
+ <source>Keyring already exists</source>
+ <translation>Связка ключей уже существует</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="271"/>
+ <source>No match</source>
+ <translation>Нет совпадений</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="276"/>
+ <source>Unknown error</source>
+ <translation>Неизвестная ошибка</translation>
+ </message>
+ <message>
+ <location filename="../libsecret.cpp" line="119"/>
+ <source>Entry not found</source>
+ <translation>Запись не найдена</translation>
+ </message>
+</context>
+</TS>
diff --git a/src/libs/3rdparty/qtkeychain/translations/qtkeychain_zh.ts b/src/libs/3rdparty/qtkeychain/translations/qtkeychain_zh.ts
new file mode 100644
index 00000000000..5d4d64a4995
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/translations/qtkeychain_zh.ts
@@ -0,0 +1,234 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="zh_TW">
+<context>
+ <name>QKeychain::DeletePasswordJobPrivate</name>
+ <message>
+ <location filename="../keychain_win.cpp" line="104"/>
+ <source>Password entry not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="108"/>
+ <source>Could not decrypt data</source>
+ <translation type="unfinished">無法解密資料</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="585"/>
+ <location filename="../keychain_unix.cpp" line="593"/>
+ <source>Unknown error</source>
+ <translation type="unfinished">未知的錯誤</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="614"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation type="unfinished">無法開啟錢包:%1; %2</translation>
+ </message>
+ <message>
+ <source>Password not found</source>
+ <translation type="obsolete">找不到密碼</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::JobPrivate</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="295"/>
+ <source>Unknown error</source>
+ <translation type="unfinished">未知的錯誤</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="542"/>
+ <source>Access to keychain denied</source>
+ <translation type="unfinished">鑰匙圈存取被拒絕</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::PlainTextStore</name>
+ <message>
+ <location filename="../plaintextstore.cpp" line="65"/>
+ <source>Could not store data in settings: access error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="67"/>
+ <source>Could not store data in settings: format error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="85"/>
+ <source>Could not delete data from settings: access error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="87"/>
+ <source>Could not delete data from settings: format error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="104"/>
+ <source>Entry not found</source>
+ <translation type="unfinished">找不到項目</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::ReadPasswordJobPrivate</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="214"/>
+ <location filename="../keychain_unix.cpp" line="224"/>
+ <source>Unknown error</source>
+ <translation>未知的錯誤</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="205"/>
+ <source>D-Bus is not running</source>
+ <translation>D-Bus 不在執行中</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="316"/>
+ <source>No keychain service available</source>
+ <translation>沒有可用的鑰匙圈服務</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="318"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation>無法開啟錢包:%1; %2</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="363"/>
+ <source>Access to keychain denied</source>
+ <translation>鑰匙圈存取被拒絕</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="384"/>
+ <source>Could not determine data type: %1; %2</source>
+ <translation>無法判斷資料型別:%1; %2</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="402"/>
+ <source>Unsupported entry type &apos;Map&apos;</source>
+ <translation>不支援的項目類型 &apos;Map&apos;</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="405"/>
+ <source>Unknown kwallet entry type &apos;%1&apos;</source>
+ <translation>未知的 kwallet 項目類型 &apos;%1&apos;</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="393"/>
+ <source>Entry not found</source>
+ <translation>找不到項目</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="32"/>
+ <source>Password entry not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="36"/>
+ <location filename="../keychain_win.cpp" line="139"/>
+ <source>Could not decrypt data</source>
+ <translation>無法解密資料</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::WritePasswordJobPrivate</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="455"/>
+ <location filename="../keychain_unix.cpp" line="482"/>
+ <source>Unknown error</source>
+ <translation>未知的錯誤</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="445"/>
+ <source>D-Bus is not running</source>
+ <translation>D-Bus 不在執行中</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="501"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation>無法開啟錢包:%1; %2</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="78"/>
+ <source>Credential size exceeds maximum size of %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="87"/>
+ <source>Credential key exceeds maximum size of %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="92"/>
+ <source>Writing credentials failed: Win32 error code %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="162"/>
+ <source>Encryption failed</source>
+ <translation>加密失敗</translation>
+ </message>
+ <message>
+ <source>Password not found</source>
+ <translation type="obsolete">找不到密碼</translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="255"/>
+ <source>Access to keychain denied</source>
+ <translation>鑰匙圈存取被拒絕</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="257"/>
+ <source>No keyring daemon</source>
+ <translation>沒有可用的鑰匙圈背景程式</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="259"/>
+ <source>Already unlocked</source>
+ <translation>已解鎖</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="261"/>
+ <source>No such keyring</source>
+ <translation>鑰匙圈不存在</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="263"/>
+ <source>Bad arguments</source>
+ <translation>引數錯誤</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="265"/>
+ <source>I/O error</source>
+ <translation>I/O 錯誤</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="267"/>
+ <source>Cancelled</source>
+ <translation>已取消</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="269"/>
+ <source>Keyring already exists</source>
+ <translation>鑰匙圈已存在</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="271"/>
+ <source>No match</source>
+ <translation>無相符項目</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="276"/>
+ <source>Unknown error</source>
+ <translation>未知的錯誤</translation>
+ </message>
+ <message>
+ <location filename="../libsecret.cpp" line="119"/>
+ <source>Entry not found</source>
+ <translation type="unfinished">找不到項目</translation>
+ </message>
+</context>
+</TS>
diff --git a/src/libs/3rdparty/qtkeychain/translations/translations.qrc.in b/src/libs/3rdparty/qtkeychain/translations/translations.qrc.in
new file mode 100644
index 00000000000..f49df661508
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/translations/translations.qrc.in
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/translations">
+ @QM_FILE_LIST@
+ </qresource>
+</RCC>