diff --git a/ext/qtkeychain/CMakeLists.txt b/ext/qtkeychain/CMakeLists.txt new file mode 100644 index 0000000..7736c60 --- /dev/null +++ b/ext/qtkeychain/CMakeLists.txt @@ -0,0 +1,195 @@ +cmake_minimum_required(VERSION 2.8) +project(qtkeychain) + +### + +set(QTKEYCHAIN_VERSION 0.5.90) +set(QTKEYCHAIN_SOVERSION 0) + +### + +set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${PROJECT_SOURCE_DIR}/cmake/Modules") +include(GNUInstallDirs) + +option(BUILD_WITH_QT4 "Build qtkeychain with Qt4 no matter if Qt5 was found" OFF) + + +if( NOT BUILD_WITH_QT4 ) + # try Qt5 first, and prefer that if found + find_package(Qt5Core QUIET) +endif() + +if (Qt5Core_FOUND) + set(QTKEYCHAIN_VERSION_INFIX 5) + if(UNIX AND NOT APPLE) + find_package(Qt5DBus REQUIRED) + include_directories(${Qt5DBus_INCLUDE_DIRS}) + set(QTDBUS_LIBRARIES ${Qt5DBus_LIBRARIES}) + macro(qt_add_dbus_interface) + qt5_add_dbus_interface(${ARGN}) + endmacro() + endif() + find_package(Qt5LinguistTools REQUIRED) + macro(qt_add_translation) + qt5_add_translation(${ARGN}) + endmacro(qt_add_translation) + macro(qt_create_translation) + qt5_create_translation(${ARGN}) + endmacro(qt_create_translation) + macro(qt_wrap_cpp) + qt5_wrap_cpp(${ARGN}) + endmacro() + + set(QTCORE_LIBRARIES ${Qt5Core_LIBRARIES}) + include_directories(${Qt5Core_INCLUDE_DIRS}) + + if (Qt5_POSITION_INDEPENDENT_CODE) + if (CMAKE_VERSION VERSION_LESS 2.8.9) # TODO remove once we increase the cmake requirement + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") + else() + set(CMAKE_POSITION_INDEPENDENT_CODE ON) + endif() + endif() +else() + set(QTKEYCHAIN_VERSION_INFIX "") + if(UNIX AND NOT APPLE) + find_package(Qt4 COMPONENTS QtCore QtDBus REQUIRED) + set(QTDBUS_LIBRARIES ${QT_QTDBUS_LIBRARY}) + macro(qt_add_dbus_interface) + qt4_add_dbus_interface(${ARGN}) + endmacro() + else() + find_package(Qt4 COMPONENTS QtCore REQUIRED) + endif() + include_directories(${QT_INCLUDES}) + set(QTCORE_LIBRARIES ${QT_QTCORE_LIBRARY}) + macro(qt_add_translation) + qt4_add_translation(${ARGN}) + endmacro(qt_add_translation) + macro(qt_create_translation) + qt4_create_translation(${ARGN}) + endmacro(qt_create_translation) + macro(qt_wrap_cpp) + qt4_wrap_cpp(${ARGN}) + endmacro() +endif() + + +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +list(APPEND qtkeychain_LIBRARIES ${QTCORE_LIBRARIES}) +set(qtkeychain_SOURCES + keychain.cpp +) + +ADD_DEFINITIONS( -Wall ) + +if(WIN32) + list(APPEND qtkeychain_SOURCES keychain_win.cpp) + list(APPEND qtkeychain_LIBRARIES crypt32) + #FIXME: mingw bug; otherwise getting undefined refs to RtlSecureZeroMemory there + if(MINGW) + add_definitions( -O2 ) + endif() +endif() + +if(APPLE) + list(APPEND qtkeychain_SOURCES keychain_mac.cpp) + + find_library(COREFOUNDATION_LIBRARY CoreFoundation) + list(APPEND qtkeychain_LIBRARIES ${COREFOUNDATION_LIBRARY}) + + find_library(SECURITY_LIBRARY Security) + list(APPEND qtkeychain_LIBRARIES ${SECURITY_LIBRARY}) +endif() + +if(UNIX AND NOT APPLE) + list(APPEND qtkeychain_SOURCES keychain_unix.cpp gnomekeyring.cpp) + qt_add_dbus_interface(qtkeychain_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/org.kde.KWallet.xml kwallet_interface KWalletInterface) + list(APPEND qtkeychain_LIBRARIES ${QTDBUS_LIBRARIES}) +endif() + +QT_WRAP_CPP(qtkeychain_MOC_OUTFILES keychain.h keychain_p.h) + +set(qtkeychain_TR_FILES + translations/qtkeychain_de.ts + translations/qtkeychain_ro.ts +) + +file(GLOB qtkeychain_TR_SOURCES *.cpp *.h *.ui) +qt_create_translation(qtkeychain_MESSAGES ${qtkeychain_TR_SOURCES} ${qtkeychain_TR_FILES}) +qt_add_translation(qtkeychain_QM_FILES ${qtkeychain_TR_FILES}) +add_custom_target(messages DEPENDS ${qtkeychain_MESSAGES}) +add_custom_target(translations DEPENDS ${qtkeychain_QM_FILES}) + +if(NOT QT_TRANSLATIONS_DIR) + # If this directory is missing, we are in a Qt5 environment. + # Extract the qmake executable location + get_target_property(QT5_QMAKE_EXECUTABLE Qt5::qmake IMPORTED_LOCATION) + # Ask Qt5 where to put the translations + execute_process( COMMAND ${QT5_QMAKE_EXECUTABLE} -query QT_INSTALL_TRANSLATIONS + OUTPUT_VARIABLE qt_translations_dir OUTPUT_STRIP_TRAILING_WHITESPACE ) + # make sure we have / and not \ as qmake gives on windows + FILE(TO_CMAKE_PATH "${qt_translations_dir}" qt_translations_dir) + SET(QT_TRANSLATIONS_DIR ${qt_translations_dir} CACHE PATH "The + location of the Qt translations" FORCE) +endif() + +install(FILES ${qtkeychain_QM_FILES} + DESTINATION ${QT_TRANSLATIONS_DIR}) + +set(QTKEYCHAIN_TARGET_NAME qt${QTKEYCHAIN_VERSION_INFIX}keychain) +if(NOT QTKEYCHAIN_STATIC) + add_library(${QTKEYCHAIN_TARGET_NAME} SHARED ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES} ${qtkeychain_QM_FILES}) + set_target_properties(${QTKEYCHAIN_TARGET_NAME} PROPERTIES COMPILE_DEFINITIONS QKEYCHAIN_BUILD_QKEYCHAIN_LIB) + target_link_libraries(${QTKEYCHAIN_TARGET_NAME} ${qtkeychain_LIBRARIES}) +else() + add_library(${QTKEYCHAIN_TARGET_NAME} STATIC ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES} ${qtkeychain_QM_FILES}) + set_target_properties(${QTKEYCHAIN_TARGET_NAME} PROPERTIES COMPILE_DEFINITIONS QKEYCHAIN_STATICLIB) +endif() + +set_target_properties(${QTKEYCHAIN_TARGET_NAME} PROPERTIES + VERSION ${QTKEYCHAIN_VERSION} + SOVERSION ${QTKEYCHAIN_SOVERSION} + MACOSX_RPATH 1 + INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}" +) + +install(FILES keychain.h qkeychain_export.h + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/qt${QTKEYCHAIN_VERSION_INFIX}keychain/ +) + +install(TARGETS ${QTKEYCHAIN_TARGET_NAME} + EXPORT Qt${QTKEYCHAIN_VERSION_INFIX}KeychainLibraryDepends + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +add_executable( testclient testclient.cpp ) +target_link_libraries( testclient ${QTKEYCHAIN_TARGET_NAME}) + + +### +### CMake config file +### + +export(TARGETS ${QTKEYCHAIN_TARGET_NAME} FILE "${PROJECT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainLibraryDepends.cmake") +export(PACKAGE Qt${QTKEYCHAIN_VERSION_INFIX}Keychain) + +configure_file(QtKeychainBuildTreeSettings.cmake.in + "${PROJECT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainBuildTreeSettings.cmake" @ONLY) +configure_file(QtKeychainConfig.cmake.in + "${PROJECT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfig.cmake" @ONLY) +configure_file(QtKeychainConfigVersion.cmake.in + "${PROJECT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfigVersion.cmake" @ONLY) + +install(EXPORT Qt${QTKEYCHAIN_VERSION_INFIX}KeychainLibraryDepends + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Qt${QTKEYCHAIN_VERSION_INFIX}Keychain" +) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfig.cmake + ${CMAKE_CURRENT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfigVersion.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Qt${QTKEYCHAIN_VERSION_INFIX}Keychain +) + diff --git a/ext/qtkeychain/COPYING b/ext/qtkeychain/COPYING new file mode 100644 index 0000000..cca2a5c --- /dev/null +++ b/ext/qtkeychain/COPYING @@ -0,0 +1,20 @@ +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. + +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/ext/qtkeychain/ChangeLog b/ext/qtkeychain/ChangeLog new file mode 100644 index 0000000..f89dc19 --- /dev/null +++ b/ext/qtkeychain/ChangeLog @@ -0,0 +1,23 @@ +ChangeLog +========= + +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 ) + * Improved desktop environment detection at runtime (Daniel Molkentin ) + +version 0.3.0 (release 2014-03-13) + * Gnome Keyring supported added (Francois Ferrand ) + * Improved Qt 5 support + * KWallet: Distinguish empty passwords from non-existing entries + * KWallet: Do not use hardcoded wallet name + * German translation (Daniel Molkentin ) + * Romanian translation (Arthur Țițeică ) + +version 0.2.0: no official release + +version 0.1.0 (release 2013-01-16) + * Initial release + diff --git a/ext/qtkeychain/QtKeychainBuildTreeSettings.cmake.in b/ext/qtkeychain/QtKeychainBuildTreeSettings.cmake.in new file mode 100644 index 0000000..3f9e9f6 --- /dev/null +++ b/ext/qtkeychain/QtKeychainBuildTreeSettings.cmake.in @@ -0,0 +1,4 @@ +set(QTKEYCHAIN_INCLUDE_DIRS + "@PROJECT_SOURCE_DIR@" + "@PROJECT_BINARY_DIR@" +) diff --git a/ext/qtkeychain/QtKeychainConfig.cmake.in b/ext/qtkeychain/QtKeychainConfig.cmake.in new file mode 100644 index 0000000..74c1356 --- /dev/null +++ b/ext/qtkeychain/QtKeychainConfig.cmake.in @@ -0,0 +1,21 @@ +# - Config file for the QtKeychain package +# It defines the following variables +# QTKEYCHAIN_INCLUDE_DIRS - include directories for QtKeychain +# QTKEYCHAIN_LIBRARIES - libraries to link against + +# Compute paths +get_filename_component(QTKEYCHAIN_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) +if(EXISTS "${QTKEYCHAIN_CMAKE_DIR}/CMakeCache.txt") + # In build tree + include("${QTKEYCHAIN_CMAKE_DIR}/Qt@QTKEYCHAIN_VERSION_INFIX@KeychainBuildTreeSettings.cmake") +else() + set(QTKEYCHAIN_INCLUDE_DIRS "@CMAKE_INSTALL_FULL_INCLUDEDIR@") +endif() + +# Our library dependencies (contains definitions for IMPORTED targets) +include("${QTKEYCHAIN_CMAKE_DIR}/Qt@QTKEYCHAIN_VERSION_INFIX@KeychainLibraryDepends.cmake") + +# These are IMPORTED targets created by FooBarLibraryDepends.cmake +set(QTKEYCHAIN_LIBRARIES "@QTKEYCHAIN_TARGET_NAME@") + +set(QTKEYCHAIN_FOUND TRUE) diff --git a/ext/qtkeychain/QtKeychainConfigVersion.cmake.in b/ext/qtkeychain/QtKeychainConfigVersion.cmake.in new file mode 100644 index 0000000..fba821a --- /dev/null +++ b/ext/qtkeychain/QtKeychainConfigVersion.cmake.in @@ -0,0 +1,11 @@ +set(PACKAGE_VERSION "@QTKEYCHAIN_VERSION@") + +# Check whether the requested PACKAGE_FIND_VERSION is compatible +if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}") + set(PACKAGE_VERSION_COMPATIBLE FALSE) +else() + set(PACKAGE_VERSION_COMPATIBLE TRUE) + if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}") + set(PACKAGE_VERSION_EXACT TRUE) + endif() +endif() diff --git a/ext/qtkeychain/ReadMe.markdown b/ext/qtkeychain/ReadMe.markdown new file mode 100644 index 0000000..e89adb7 --- /dev/null +++ b/ext/qtkeychain/ReadMe.markdown @@ -0,0 +1,15 @@ +QtKeychain +========== + +QtKeychain is a Qt API to store passwords and other secret data securely. How the data is stored depends on the platform: + + * **Mac OS X:** Passwords are stored in the OS X Keychain. + + * **Linux/Unix:** If running, GNOME Keyring is used, otherwise +qtkeychain tries to use KWallet (via D-Bus), if available. + + * **Windows:** Windows does not provide a service for secure storage. 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. + +In unsupported environments QtKeychain will report an error. It will not store any data unencrypted unless explicitly requested (setInsecureFallback( true )). + +**License:** QtKeychain is available under the [Modified BSD License](http://www.gnu.org/licenses/license-list.html#ModifiedBSD). See the file COPYING for details. diff --git a/ext/qtkeychain/ReadMe.txt b/ext/qtkeychain/ReadMe.txt new file mode 100644 index 0000000..e89adb7 --- /dev/null +++ b/ext/qtkeychain/ReadMe.txt @@ -0,0 +1,15 @@ +QtKeychain +========== + +QtKeychain is a Qt API to store passwords and other secret data securely. How the data is stored depends on the platform: + + * **Mac OS X:** Passwords are stored in the OS X Keychain. + + * **Linux/Unix:** If running, GNOME Keyring is used, otherwise +qtkeychain tries to use KWallet (via D-Bus), if available. + + * **Windows:** Windows does not provide a service for secure storage. 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. + +In unsupported environments QtKeychain will report an error. It will not store any data unencrypted unless explicitly requested (setInsecureFallback( true )). + +**License:** QtKeychain is available under the [Modified BSD License](http://www.gnu.org/licenses/license-list.html#ModifiedBSD). See the file COPYING for details. diff --git a/ext/qtkeychain/cmake/Modules/GNUInstallDirs.cmake b/ext/qtkeychain/cmake/Modules/GNUInstallDirs.cmake new file mode 100644 index 0000000..0302e4b --- /dev/null +++ b/ext/qtkeychain/cmake/Modules/GNUInstallDirs.cmake @@ -0,0 +1,188 @@ +# - Define GNU standard installation directories +# Provides install directory variables as defined for GNU software: +# http://www.gnu.org/prep/standards/html_node/Directory-Variables.html +# Inclusion of this module defines the following variables: +# CMAKE_INSTALL_ - destination for files of a given type +# CMAKE_INSTALL_FULL_ - corresponding absolute path +# where is one of: +# BINDIR - user executables (bin) +# SBINDIR - system admin executables (sbin) +# LIBEXECDIR - program executables (libexec) +# SYSCONFDIR - read-only single-machine data (etc) +# SHAREDSTATEDIR - modifiable architecture-independent data (com) +# LOCALSTATEDIR - modifiable single-machine data (var) +# LIBDIR - object code libraries (lib or lib64 or lib/ on Debian) +# INCLUDEDIR - C header files (include) +# OLDINCLUDEDIR - C header files for non-gcc (/usr/include) +# DATAROOTDIR - read-only architecture-independent data root (share) +# DATADIR - read-only architecture-independent data (DATAROOTDIR) +# INFODIR - info documentation (DATAROOTDIR/info) +# LOCALEDIR - locale-dependent data (DATAROOTDIR/locale) +# MANDIR - man documentation (DATAROOTDIR/man) +# DOCDIR - documentation root (DATAROOTDIR/doc/PROJECT_NAME) +# Each CMAKE_INSTALL_ value may be passed to the DESTINATION options of +# install() commands for the corresponding file type. If the includer does +# not define a value the above-shown default will be used and the value will +# appear in the cache for editing by the user. +# Each CMAKE_INSTALL_FULL_ value contains an absolute path constructed +# from the corresponding destination by prepending (if necessary) the value +# of CMAKE_INSTALL_PREFIX. + +#============================================================================= +# Copyright 2011 Nikita Krupen'ko +# Copyright 2011 Kitware, Inc. +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +# Installation directories +# +if(NOT DEFINED CMAKE_INSTALL_BINDIR) + set(CMAKE_INSTALL_BINDIR "bin" CACHE PATH "user executables (bin)") +endif() + +if(NOT DEFINED CMAKE_INSTALL_SBINDIR) + set(CMAKE_INSTALL_SBINDIR "sbin" CACHE PATH "system admin executables (sbin)") +endif() + +if(NOT DEFINED CMAKE_INSTALL_LIBEXECDIR) + set(CMAKE_INSTALL_LIBEXECDIR "libexec" CACHE PATH "program executables (libexec)") +endif() + +if(NOT DEFINED CMAKE_INSTALL_SYSCONFDIR) + set(CMAKE_INSTALL_SYSCONFDIR "etc" CACHE PATH "read-only single-machine data (etc)") +endif() + +if(NOT DEFINED CMAKE_INSTALL_SHAREDSTATEDIR) + set(CMAKE_INSTALL_SHAREDSTATEDIR "com" CACHE PATH "modifiable architecture-independent data (com)") +endif() + +if(NOT DEFINED CMAKE_INSTALL_LOCALSTATEDIR) + set(CMAKE_INSTALL_LOCALSTATEDIR "var" CACHE PATH "modifiable single-machine data (var)") +endif() + +if(NOT DEFINED CMAKE_INSTALL_LIBDIR) + set(_LIBDIR_DEFAULT "lib") + # Override this default 'lib' with 'lib64' iff: + # - we are on Linux system but NOT cross-compiling + # - we are NOT on debian + # - we are on a 64 bits system + # reason is: amd64 ABI: http://www.x86-64.org/documentation/abi.pdf + # For Debian with multiarch, use 'lib/${CMAKE_LIBRARY_ARCHITECTURE}' if + # CMAKE_LIBRARY_ARCHITECTURE is set (which contains e.g. "i386-linux-gnu" + # See http://wiki.debian.org/Multiarch + if(CMAKE_SYSTEM_NAME MATCHES "Linux" + AND NOT CMAKE_CROSSCOMPILING) + if (EXISTS "/etc/debian_version") # is this a debian system ? + if(CMAKE_LIBRARY_ARCHITECTURE) + set(_LIBDIR_DEFAULT "lib/${CMAKE_LIBRARY_ARCHITECTURE}") + endif() + else() # not debian, rely on CMAKE_SIZEOF_VOID_P: + if(NOT DEFINED CMAKE_SIZEOF_VOID_P) + message(AUTHOR_WARNING + "Unable to determine default CMAKE_INSTALL_LIBDIR directory because no target architecture is known. " + "Please enable at least one language before including GNUInstallDirs.") + else() + if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") + set(_LIBDIR_DEFAULT "lib64") + endif() + endif() + endif() + endif() + set(CMAKE_INSTALL_LIBDIR "${_LIBDIR_DEFAULT}" CACHE PATH "object code libraries (${_LIBDIR_DEFAULT})") +endif() + +if(NOT DEFINED CMAKE_INSTALL_INCLUDEDIR) + set(CMAKE_INSTALL_INCLUDEDIR "include" CACHE PATH "C header files (include)") +endif() + +if(NOT DEFINED CMAKE_INSTALL_OLDINCLUDEDIR) + set(CMAKE_INSTALL_OLDINCLUDEDIR "/usr/include" CACHE PATH "C header files for non-gcc (/usr/include)") +endif() + +if(NOT DEFINED CMAKE_INSTALL_DATAROOTDIR) + set(CMAKE_INSTALL_DATAROOTDIR "share" CACHE PATH "read-only architecture-independent data root (share)") +endif() + +#----------------------------------------------------------------------------- +# Values whose defaults are relative to DATAROOTDIR. Store empty values in +# the cache and store the defaults in local variables if the cache values are +# not set explicitly. This auto-updates the defaults as DATAROOTDIR changes. + +if(NOT CMAKE_INSTALL_DATADIR) + set(CMAKE_INSTALL_DATADIR "" CACHE PATH "read-only architecture-independent data (DATAROOTDIR)") + set(CMAKE_INSTALL_DATADIR "${CMAKE_INSTALL_DATAROOTDIR}") +endif() + +if(NOT CMAKE_INSTALL_INFODIR) + set(CMAKE_INSTALL_INFODIR "" CACHE PATH "info documentation (DATAROOTDIR/info)") + set(CMAKE_INSTALL_INFODIR "${CMAKE_INSTALL_DATAROOTDIR}/info") +endif() + +if(NOT CMAKE_INSTALL_LOCALEDIR) + set(CMAKE_INSTALL_LOCALEDIR "" CACHE PATH "locale-dependent data (DATAROOTDIR/locale)") + set(CMAKE_INSTALL_LOCALEDIR "${CMAKE_INSTALL_DATAROOTDIR}/locale") +endif() + +if(NOT CMAKE_INSTALL_MANDIR) + set(CMAKE_INSTALL_MANDIR "" CACHE PATH "man documentation (DATAROOTDIR/man)") + set(CMAKE_INSTALL_MANDIR "${CMAKE_INSTALL_DATAROOTDIR}/man") +endif() + +if(NOT CMAKE_INSTALL_DOCDIR) + set(CMAKE_INSTALL_DOCDIR "" CACHE PATH "documentation root (DATAROOTDIR/doc/PROJECT_NAME)") + set(CMAKE_INSTALL_DOCDIR "${CMAKE_INSTALL_DATAROOTDIR}/doc/${PROJECT_NAME}") +endif() + +#----------------------------------------------------------------------------- + +mark_as_advanced( + CMAKE_INSTALL_BINDIR + CMAKE_INSTALL_SBINDIR + CMAKE_INSTALL_LIBEXECDIR + CMAKE_INSTALL_SYSCONFDIR + CMAKE_INSTALL_SHAREDSTATEDIR + CMAKE_INSTALL_LOCALSTATEDIR + CMAKE_INSTALL_LIBDIR + CMAKE_INSTALL_INCLUDEDIR + CMAKE_INSTALL_OLDINCLUDEDIR + CMAKE_INSTALL_DATAROOTDIR + CMAKE_INSTALL_DATADIR + CMAKE_INSTALL_INFODIR + CMAKE_INSTALL_LOCALEDIR + CMAKE_INSTALL_MANDIR + CMAKE_INSTALL_DOCDIR + ) + +# Result directories +# +foreach(dir + BINDIR + SBINDIR + LIBEXECDIR + SYSCONFDIR + SHAREDSTATEDIR + LOCALSTATEDIR + LIBDIR + INCLUDEDIR + OLDINCLUDEDIR + DATAROOTDIR + DATADIR + INFODIR + LOCALEDIR + MANDIR + DOCDIR + ) + if(NOT IS_ABSOLUTE ${CMAKE_INSTALL_${dir}}) + set(CMAKE_INSTALL_FULL_${dir} "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_${dir}}") + else() + set(CMAKE_INSTALL_FULL_${dir} "${CMAKE_INSTALL_${dir}}") + endif() +endforeach() diff --git a/ext/qtkeychain/gnomekeyring.cpp b/ext/qtkeychain/gnomekeyring.cpp new file mode 100644 index 0000000..9cef00f --- /dev/null +++ b/ext/qtkeychain/gnomekeyring.cpp @@ -0,0 +1,71 @@ +#include "gnomekeyring_p.h" + +const char* GnomeKeyring::GNOME_KEYRING_DEFAULT = NULL; + +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* 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, static_cast(0) ); +} + +GnomeKeyring::gpointer GnomeKeyring::find_network_password( const gchar* user, const gchar* server, + 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, static_cast(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(0) ); +} + +GnomeKeyring::GnomeKeyring() + : QLibrary("gnome-keyring", 0) +{ + static const PasswordSchema schema = { + ITEM_NETWORK_PASSWORD, + {{ "user", ATTRIBUTE_TYPE_STRING }, + { "server", ATTRIBUTE_TYPE_STRING }, + { 0, static_cast( 0 ) }} + }; + + NETWORK_PASSWORD = &schema; + is_available = reinterpret_cast( resolve( "gnome_keyring_is_available" ) ); + find_password = reinterpret_cast( resolve( "gnome_keyring_find_password" ) ); + store_password = reinterpret_cast( resolve( "gnome_keyring_store_password" ) ); + delete_password = reinterpret_cast( resolve( "gnome_keyring_delete_password" ) ); +} + +GnomeKeyring& GnomeKeyring::instance() { + static GnomeKeyring keyring; + return keyring; +} diff --git a/ext/qtkeychain/gnomekeyring_p.h b/ext/qtkeychain/gnomekeyring_p.h new file mode 100644 index 0000000..6d150ba --- /dev/null +++ b/ext/qtkeychain/gnomekeyring_p.h @@ -0,0 +1,88 @@ +#ifndef QTKEYCHAIN_GNOME_P_H +#define QTKEYCHAIN_GNOME_P_H + +#include + +class GnomeKeyring : private QLibrary { +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, 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* password, + OperationDoneCallback callback, gpointer data, GDestroyNotify destroy_data ); + + static gpointer find_network_password( const gchar* user, const gchar* server, + 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/ext/qtkeychain/keychain.cpp b/ext/qtkeychain/keychain.cpp new file mode 100644 index 0000000..7687f9d --- /dev/null +++ b/ext/qtkeychain/keychain.cpp @@ -0,0 +1,229 @@ +/****************************************************************************** + * Copyright (C) 2011-2015 Frank Osterfeld * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution * + * details, check the accompanying file 'COPYING'. * + *****************************************************************************/ +#include "keychain.h" +#include "keychain_p.h" + +using namespace QKeychain; + +Job::Job( const QString& service, QObject *parent ) + : QObject( parent ) + , d ( new JobPrivate( service ) ) { +} + +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::emitFinished() { + emit finished( this ); + if ( d->autoDelete ) + deleteLater(); +} + +void Job::emitFinishedWithError( Error error, const QString& errorString ) { + d->error = error; + d->errorString = errorString; + emitFinished(); +} + +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( service, parent ) + , d( new ReadPasswordJobPrivate( this ) ) +{} + +ReadPasswordJob::~ReadPasswordJob() { + delete d; +} + +QString ReadPasswordJob::textData() const { + return QString::fromUtf8( d->data ); +} + +QByteArray ReadPasswordJob::binaryData() const { + return d->data; +} + +QString ReadPasswordJob::key() const { + return d->key; +} + +void ReadPasswordJob::setKey( const QString& key ) { + d->key = key; +} + +void ReadPasswordJob::doStart() { + JobExecutor::instance()->enqueue( this ); +} + +WritePasswordJob::WritePasswordJob( const QString& service, QObject* parent ) + : Job( service, parent ) + , d( new WritePasswordJobPrivate( this ) ) { +} + +WritePasswordJob::~WritePasswordJob() { + delete d; +} + +QString WritePasswordJob::key() const { + return d->key; +} + +void WritePasswordJob::setKey( const QString& key ) { + d->key = key; +} + +void WritePasswordJob::setBinaryData( const QByteArray& data ) { + d->binaryData = data; + d->mode = WritePasswordJobPrivate::Binary; +} + +void WritePasswordJob::setTextData( const QString& data ) { + d->textData = data; + d->mode = WritePasswordJobPrivate::Text; +} + +void WritePasswordJob::doStart() { + JobExecutor::instance()->enqueue( this ); +} + +DeletePasswordJob::DeletePasswordJob( const QString& service, QObject* parent ) + : Job( service, parent ) + , d( new DeletePasswordJobPrivate( this ) ) { +} + +DeletePasswordJob::~DeletePasswordJob() { + delete d; +} + +void DeletePasswordJob::doStart() { + //Internally, to delete a password we just execute a write job with no data set (null byte array). + //In all current implementations, this deletes the entry so this is sufficient + WritePasswordJob* job = new WritePasswordJob( service(), this ); + connect( job, SIGNAL(finished(QKeychain::Job*)), d, SLOT(jobFinished(QKeychain::Job*)) ); + job->setInsecureFallback(true); + job->setSettings(settings()); + job->setKey( d->key ); + job->doStart(); +} + +QString DeletePasswordJob::key() const { + return d->key; +} + +void DeletePasswordJob::setKey( const QString& key ) { + d->key = key; +} + +void DeletePasswordJobPrivate::jobFinished( Job* job ) { + q->setError( job->error() ); + q->setErrorString( job->errorString() ); + q->emitFinished(); +} + +JobExecutor::JobExecutor() + : QObject( 0 ) + , m_runningJob( 0 ) +{ +} + +void JobExecutor::enqueue( Job* job ) { + m_queue.append( job ); + startNextIfNoneRunning(); +} + +void JobExecutor::startNextIfNoneRunning() { + if ( m_queue.isEmpty() || m_runningJob ) + return; + QPointer next; + while ( !next && !m_queue.isEmpty() ) { + next = m_queue.first(); + m_queue.pop_front(); + } + if ( next ) { + connect( next, SIGNAL(finished(QKeychain::Job*)), this, SLOT(jobFinished(QKeychain::Job*)) ); + connect( next, SIGNAL(destroyed(QObject*)), this, SLOT(jobDestroyed(QObject*)) ); + m_runningJob = next; + if ( ReadPasswordJob* rpj = qobject_cast( m_runningJob ) ) + rpj->d->scheduledStart(); + else if ( WritePasswordJob* wpj = qobject_cast( m_runningJob) ) + wpj->d->scheduledStart(); + } +} + +void JobExecutor::jobDestroyed( QObject* object ) { + Q_UNUSED( object ) // for release mode + Q_ASSERT( object == m_runningJob ); + m_runningJob->disconnect( this ); + m_runningJob = 0; + startNextIfNoneRunning(); +} + +void JobExecutor::jobFinished( Job* job ) { + Q_UNUSED( job ) // for release mode + Q_ASSERT( job == m_runningJob ); + m_runningJob->disconnect( this ); + m_runningJob = 0; + startNextIfNoneRunning(); +} + +JobExecutor* JobExecutor::s_instance = 0; + +JobExecutor* JobExecutor::instance() { + if ( !s_instance ) + s_instance = new JobExecutor; + return s_instance; +} diff --git a/ext/qtkeychain/keychain.h b/ext/qtkeychain/keychain.h new file mode 100644 index 0000000..6ed5e95 --- /dev/null +++ b/ext/qtkeychain/keychain.h @@ -0,0 +1,145 @@ +/****************************************************************************** + * Copyright (C) 2011-2015 Frank Osterfeld * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution * + * details, check the accompanying file 'COPYING'. * + *****************************************************************************/ +#ifndef KEYCHAIN_H +#define KEYCHAIN_H + +#include "qkeychain_export.h" + +#include +#include + +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; + +class QKEYCHAIN_EXPORT Job : public QObject { + Q_OBJECT +public: + explicit Job( const QString& service, QObject* parent=0 ); + ~Job(); + + QSettings* settings() const; + void setSettings( QSettings* settings ); + + void start(); + + QString service() const; + + Error error() const; + QString errorString() const; + + bool autoDelete() const; + void setAutoDelete( bool autoDelete ); + + bool insecureFallback() const; + void setInsecureFallback( bool insecureFallback ); + +Q_SIGNALS: + void finished( QKeychain::Job* ); + +protected: + Q_INVOKABLE virtual void doStart() = 0; + + void setError( Error error ); + void setErrorString( const QString& errorString ); + void emitFinished(); + void emitFinishedWithError(Error, const QString& errorString); + +private: + JobPrivate* const d; +}; + +class ReadPasswordJobPrivate; + +class QKEYCHAIN_EXPORT ReadPasswordJob : public Job { + Q_OBJECT +public: + explicit ReadPasswordJob( const QString& service, QObject* parent=0 ); + ~ReadPasswordJob(); + + QString key() const; + void setKey( const QString& key ); + + QByteArray binaryData() const; + QString textData() const; + +protected: + void doStart(); + +private: + friend class QKeychain::ReadPasswordJobPrivate; + friend class QKeychain::JobExecutor; + ReadPasswordJobPrivate* const d; +}; + +class WritePasswordJobPrivate; + +class QKEYCHAIN_EXPORT WritePasswordJob : public Job { + Q_OBJECT +public: + explicit WritePasswordJob( const QString& service, QObject* parent=0 ); + ~WritePasswordJob(); + + QString key() const; + void setKey( const QString& key ); + + void setBinaryData( const QByteArray& data ); + void setTextData( const QString& data ); + +protected: + void doStart(); + +private: + friend class QKeychain::JobExecutor; + friend class QKeychain::WritePasswordJobPrivate; + friend class DeletePasswordJob; + WritePasswordJobPrivate* const d; +}; + +class DeletePasswordJobPrivate; + +class QKEYCHAIN_EXPORT DeletePasswordJob : public Job { + Q_OBJECT +public: + explicit DeletePasswordJob( const QString& service, QObject* parent=0 ); + ~DeletePasswordJob(); + + QString key() const; + void setKey( const QString& key ); + +protected: + void doStart(); + +private: + friend class QKeychain::DeletePasswordJobPrivate; + DeletePasswordJobPrivate* const d; +}; + +} // namespace QtKeychain + +#endif diff --git a/ext/qtkeychain/keychain_mac.cpp b/ext/qtkeychain/keychain_mac.cpp new file mode 100644 index 0000000..8a058cf --- /dev/null +++ b/ext/qtkeychain/keychain_mac.cpp @@ -0,0 +1,161 @@ +/****************************************************************************** + * Copyright (C) 2011-2015 Frank Osterfeld * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution * + * details, check the accompanying file 'COPYING'. * + *****************************************************************************/ +#include "keychain_p.h" + +#include +#include +#include + +using namespace QKeychain; + +template +struct Releaser { + explicit Releaser( const T& v ) : value( v ) {} + ~Releaser() { + CFRelease( value ); + } + + const T value; +}; + +static QString strForStatus( OSStatus os ) { + const Releaser str( SecCopyErrorMessageString( os, 0 ) ); + const char * const buf = CFStringGetCStringPtr( str.value, kCFStringEncodingUTF8 ); + if ( !buf ) + return QObject::tr( "%1 (OSStatus %2)" ) + .arg( "OSX Keychain Error" ).arg( os ); + return QObject::tr( "%1 (OSStatus %2)" ) + .arg( QString::fromUtf8( buf, strlen( buf ) ) ).arg( os ); +} + +static OSStatus readPw( QByteArray* pw, + const QString& service, + const QString& account, + SecKeychainItemRef* ref ) { + Q_ASSERT( pw ); + pw->clear(); + const QByteArray serviceData = service.toUtf8(); + const QByteArray accountData = account.toUtf8(); + + void* data = 0; + UInt32 len = 0; + + const OSStatus ret = SecKeychainFindGenericPassword( NULL, // default keychain + serviceData.size(), + serviceData.constData(), + accountData.size(), + accountData.constData(), + &len, + &data, + ref ); + if ( ret == noErr ) { + *pw = QByteArray( reinterpret_cast( data ), len ); + const OSStatus ret2 = SecKeychainItemFreeContent ( 0, data ); + if ( ret2 != noErr ) + qWarning() << "Could not free item content: " << strForStatus( ret2 ); + } + return ret; +} + +void ReadPasswordJobPrivate::scheduledStart() +{ + QString errorString; + Error error = NoError; + const OSStatus ret = readPw( &data, q->service(), q->key(), 0 ); + + switch ( ret ) { + case noErr: + break; + case errSecItemNotFound: + errorString = tr("Password not found"); + error = EntryNotFound; + break; + default: + errorString = strForStatus( ret ); + error = OtherError; + break; + } + q->emitFinishedWithError( error, errorString ); +} + + +static QKeychain::Error deleteEntryImpl( const QString& service, const QString& account, QString* err ) { + SecKeychainItemRef ref; + QByteArray pw; + const OSStatus ret1 = readPw( &pw, service, account, &ref ); + if ( ret1 == errSecItemNotFound ) + return NoError; // No item stored, we're done + if ( ret1 != noErr ) { + *err = strForStatus( ret1 ); + //TODO map error code, set errstr + return OtherError; + } + const Releaser releaser( ref ); + + const OSStatus ret2 = SecKeychainItemDelete( ref ); + + if ( ret2 == noErr ) + return NoError; + //TODO map error code + *err = strForStatus( ret2 ); + return CouldNotDeleteEntry; +} + +static QKeychain::Error writeEntryImpl( const QString& service, + const QString& account, + const QByteArray& data, + QString* err ) { + Q_ASSERT( err ); + err->clear(); + const QByteArray serviceData = service.toUtf8(); + const QByteArray accountData = account.toUtf8(); + const OSStatus ret = SecKeychainAddGenericPassword( NULL, //default keychain + serviceData.size(), + serviceData.constData(), + accountData.size(), + accountData.constData(), + data.size(), + data.constData(), + NULL //item reference + ); + if ( ret != noErr ) { + switch ( ret ) { + case errSecDuplicateItem: + { + Error derr = deleteEntryImpl( service, account, err ); + if ( derr != NoError ) + return CouldNotDeleteEntry; + else + return writeEntryImpl( service, account, data, err ); + } + default: + *err = strForStatus( ret ); + return OtherError; + } + } + + return NoError; +} + +void WritePasswordJobPrivate::scheduledStart() +{ + QString errorString; + Error error = NoError; + + if ( mode == Delete ) { + const Error derr = deleteEntryImpl( q->service(), key, &errorString ); + if ( derr != NoError ) + error = CouldNotDeleteEntry; + q->emitFinishedWithError( error, errorString ); + return; + } + const QByteArray data = mode == Text ? textData.toUtf8() : binaryData; + error = writeEntryImpl( q->service(), key, data, &errorString ); + q->emitFinishedWithError( error, errorString ); +} diff --git a/ext/qtkeychain/keychain_p.h b/ext/qtkeychain/keychain_p.h new file mode 100644 index 0000000..09c9b25 --- /dev/null +++ b/ext/qtkeychain/keychain_p.h @@ -0,0 +1,163 @@ +/****************************************************************************** + * Copyright (C) 2011-2015 Frank Osterfeld * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution * + * details, check the accompanying file 'COPYING'. * + *****************************************************************************/ +#ifndef KEYCHAIN_P_H +#define KEYCHAIN_P_H + +#include +#include +#include +#include +#include + +#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) + +#include + +#include "kwallet_interface.h" +#else + +class QDBusPendingCallWatcher; + +#endif + +#include "keychain.h" + +namespace QKeychain { + +class JobExecutor; + +class JobPrivate : public QObject { + Q_OBJECT +public: + JobPrivate( const QString& service_ ) + : error( NoError ) + , service( service_ ) + , autoDelete( true ) + , insecureFallback( false ) {} + + QKeychain::Error error; + QString errorString; + QString service; + bool autoDelete; + bool insecureFallback; + QPointer settings; +}; + +class ReadPasswordJobPrivate : public QObject { + Q_OBJECT +public: + explicit ReadPasswordJobPrivate( ReadPasswordJob* qq ) : q( qq ), walletHandle( 0 ), dataType( Text ) {} + void scheduledStart(); + + ReadPasswordJob* const q; + QByteArray data; + QString key; + int walletHandle; + enum DataType { + Binary, + Text + }; + DataType dataType; + +#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) + org::kde::KWallet* iface; + static void gnomeKeyring_cb( int result, const char* string, ReadPasswordJobPrivate* data ); + friend class QKeychain::JobExecutor; + void fallbackOnError(const QDBusError& err); + +private Q_SLOTS: + void kwalletWalletFound( QDBusPendingCallWatcher* watcher ); + void kwalletOpenFinished( QDBusPendingCallWatcher* watcher ); + void kwalletEntryTypeFinished( QDBusPendingCallWatcher* watcher ); + void kwalletReadFinished( QDBusPendingCallWatcher* watcher ); +#else //moc's too dumb to respect above macros, so just define empty slot implementations +private Q_SLOTS: + void kwalletWalletFound( QDBusPendingCallWatcher* ) {} + void kwalletOpenFinished( QDBusPendingCallWatcher* ) {} + void kwalletEntryTypeFinished( QDBusPendingCallWatcher* ) {} + void kwalletReadFinished( QDBusPendingCallWatcher* ) {} +#endif + +}; + +class WritePasswordJobPrivate : public QObject { + Q_OBJECT +public: + explicit WritePasswordJobPrivate( WritePasswordJob* qq ) : q( qq ), mode( Delete ) {} + void scheduledStart(); + + enum Mode { + Delete, + Text, + Binary + }; + + static QString modeToString(Mode m); + static Mode stringToMode(const QString& s); + + WritePasswordJob* const q; + Mode mode; + QString key; + QByteArray binaryData; + QString textData; + +#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) + org::kde::KWallet* iface; + static void gnomeKeyring_cb( int result, WritePasswordJobPrivate* self ); + friend class QKeychain::JobExecutor; + void fallbackOnError(const QDBusError& err); + +private Q_SLOTS: + void kwalletWalletFound( QDBusPendingCallWatcher* watcher ); + void kwalletOpenFinished( QDBusPendingCallWatcher* watcher ); + void kwalletWriteFinished( QDBusPendingCallWatcher* watcher ); +#else +private Q_SLOTS: + void kwalletWalletFound( QDBusPendingCallWatcher* ) {} + void kwalletOpenFinished( QDBusPendingCallWatcher* ) {} + void kwalletWriteFinished( QDBusPendingCallWatcher* ) {} +#endif +}; + +class DeletePasswordJobPrivate : public QObject { + Q_OBJECT +public: + explicit DeletePasswordJobPrivate( DeletePasswordJob* qq ) : q( qq ) {} + void doStart(); + DeletePasswordJob* const q; + QString key; +private Q_SLOTS: + void jobFinished( QKeychain::Job* ); +}; + +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; + Job* m_runningJob; + QVector > m_queue; +}; + +} + +#endif // KEYCHAIN_P_H diff --git a/ext/qtkeychain/keychain_unix.cpp b/ext/qtkeychain/keychain_unix.cpp new file mode 100644 index 0000000..48f4240 --- /dev/null +++ b/ext/qtkeychain/keychain_unix.cpp @@ -0,0 +1,510 @@ +/****************************************************************************** + * Copyright (C) 2011-2015 Frank Osterfeld * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution * + * details, check the accompanying file 'COPYING'. * + *****************************************************************************/ +#include "keychain_p.h" +#include "gnomekeyring_p.h" + +#include + +#include + +using namespace QKeychain; + +static QString typeKey( const QString& key ) +{ + return QString::fromLatin1( "%1/type" ).arg( key ); +} + +static QString dataKey( const QString& key ) +{ + return QString::fromLatin1( "%1/data" ).arg( key ); +} + +enum KeyringBackend { + Backend_GnomeKeyring, + Backend_Kwallet4, + Backend_Kwallet5 +}; + +enum DesktopEnvironment { + DesktopEnv_Gnome, + DesktopEnv_Kde4, + DesktopEnv_Plasma5, + DesktopEnv_Unity, + DesktopEnv_Xfce, + DesktopEnv_Other +}; + +// the following detection algorithm is derived from chromium, +// licensed under BSD, see base/nix/xdg_util.cc + +static DesktopEnvironment getKdeVersion() { + QString value = qgetenv("KDE_SESSION_VERSION"); + 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(); + } + + 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 KeyringBackend detectKeyringBackend() +{ + switch (detectDesktopEnvironment()) { + case DesktopEnv_Kde4: + return Backend_Kwallet4; + break; + case DesktopEnv_Plasma5: + return Backend_Kwallet5; + break; + // fall through + case DesktopEnv_Gnome: + case DesktopEnv_Unity: + case DesktopEnv_Xfce: + case DesktopEnv_Other: + default: + if ( GnomeKeyring::isAvailable() ) { + return Backend_GnomeKeyring; + } else { + return Backend_Kwallet4; + } + } + +} + +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 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_GnomeKeyring: + if ( !GnomeKeyring::find_network_password( key.toUtf8().constData(), q->service().toUtf8().constData(), + reinterpret_cast( &ReadPasswordJobPrivate::gnomeKeyring_cb ), + this, 0 ) ) + q->emitFinishedWithError( OtherError, tr("Unknown error") ); + break; + + case Backend_Kwallet4: + kwalletReadPasswordScheduledStartImpl("org.kde.kwalletd", "/modules/kwalletd", this); + break; + case Backend_Kwallet5: + kwalletReadPasswordScheduledStartImpl("org.kde.kwalletd5", "/modules/kwalletd5", this); + break; + } +} + +void ReadPasswordJobPrivate::kwalletWalletFound(QDBusPendingCallWatcher *watcher) +{ + watcher->deleteLater(); + const QDBusPendingReply reply = *watcher; + const QDBusPendingReply pendingReply = iface->open( reply.value(), 0, q->service() ); + QDBusPendingCallWatcher* pendingWatcher = new QDBusPendingCallWatcher( pendingReply, this ); + connect( pendingWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletOpenFinished(QDBusPendingCallWatcher*)) ); +} + +static QPair 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 ReadPasswordJobPrivate::gnomeKeyring_cb( int result, const char* string, ReadPasswordJobPrivate* self ) +{ + if ( result == GnomeKeyring::RESULT_OK ) { + if ( self->dataType == ReadPasswordJobPrivate::Text ) + self->data = string; + else + self->data = QByteArray::fromBase64( string ); + self->q->emitFinished(); + } else { + const QPair errorResult = mapGnomeKeyringError( result ); + self->q->emitFinishedWithError( errorResult.first, errorResult.second ); + } +} + +void ReadPasswordJobPrivate::fallbackOnError(const QDBusError& err ) +{ + QScopedPointer local( !q->settings() ? new QSettings( q->service() ) : 0 ); + QSettings* actual = q->settings() ? q->settings() : local.data(); + + if ( q->insecureFallback() && actual->contains( dataKey( key ) ) ) { + + const WritePasswordJobPrivate::Mode mode = WritePasswordJobPrivate::stringToMode( actual->value( typeKey( key ) ).toString() ); + if (mode == WritePasswordJobPrivate::Binary) + dataType = Binary; + else + dataType = Text; + data = actual->value( dataKey( key ) ).toByteArray(); + + 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 reply = *watcher; + + QScopedPointer local( !q->settings() ? new QSettings( q->service() ) : 0 ); + QSettings* actual = q->settings() ? q->settings() : local.data(); + + if ( reply.isError() ) { + fallbackOnError( reply.error() ); + return; + } + + if ( actual->contains( dataKey( key ) ) ) { + // We previously stored data in the insecure QSettings, but now have KWallet available. + // Do the migration + + data = actual->value( dataKey( key ) ).toByteArray(); + const WritePasswordJobPrivate::Mode mode = WritePasswordJobPrivate::stringToMode( actual->value( typeKey( key ) ).toString() ); + actual->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 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 reply = *watcher; + const int value = reply.value(); + + switch ( value ) { + case Unknown: + q->emitFinishedWithError( EntryNotFound, tr("Entry not found") ); + return; + case Password: + dataType = Text; + break; + case Stream: + dataType = 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 = dataType == 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(kwalletReadFinished(QDBusPendingCallWatcher*)) ); +} + +void ReadPasswordJobPrivate::kwalletReadFinished( QDBusPendingCallWatcher* watcher ) { + watcher->deleteLater(); + if ( watcher->isError() ) { + const QDBusError err = watcher->error(); + q->emitFinishedWithError( OtherError, tr("Could not read password: %1; %2").arg( QDBusError::errorString( err.type() ), err.message() ) ); + return; + } + + if ( dataType == Binary ) { + QDBusPendingReply reply = *watcher; + data = reply.value(); + } else { + QDBusPendingReply reply = *watcher; + data = reply.value().toUtf8(); + } + q->emitFinished(); +} + +static void kwalletWritePasswordScheduledStart( const char * service, const char * path, WritePasswordJobPrivate * priv ) { + if ( QDBusConnection::sessionBus().isConnected() ) + { + priv->iface = new org::kde::KWallet( QLatin1String(service), QLatin1String(path), QDBusConnection::sessionBus(), priv ); + const QDBusPendingReply reply = priv->iface->networkWallet(); + QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher( reply, priv ); + priv->connect( watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), priv, SLOT(kwalletWalletFound(QDBusPendingCallWatcher*)) ); + } + else + { + // 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_GnomeKeyring: + if ( mode == WritePasswordJobPrivate::Delete ) { + if ( !GnomeKeyring::delete_network_password( key.toUtf8().constData(), q->service().toUtf8().constData(), + reinterpret_cast( &WritePasswordJobPrivate::gnomeKeyring_cb ), + this, 0 ) ) + q->emitFinishedWithError( OtherError, tr("Unknown error") ); + } else { + QByteArray password = mode == WritePasswordJobPrivate::Text ? textData.toUtf8() : binaryData.toBase64(); + QByteArray service = q->service().toUtf8(); + if ( !GnomeKeyring::store_network_password( GnomeKeyring::GNOME_KEYRING_DEFAULT, service.constData(), + key.toUtf8().constData(), service.constData(), password.constData(), + reinterpret_cast( &WritePasswordJobPrivate::gnomeKeyring_cb ), + this, 0 ) ) + q->emitFinishedWithError( OtherError, tr("Unknown error") ); + } + break; + + case Backend_Kwallet4: + kwalletWritePasswordScheduledStart("org.kde.kwalletd", "/modules/kwalletd", this); + break; + case Backend_Kwallet5: + kwalletWritePasswordScheduledStart("org.kde.kwalletd5", "/modules/kwalletd5", this); + break; + } +} + +QString WritePasswordJobPrivate::modeToString(Mode m) +{ + switch (m) { + case Delete: + return QLatin1String("Delete"); + case Text: + return QLatin1String("Text"); + case Binary: + return QLatin1String("Binary"); + } + + Q_ASSERT_X(false, Q_FUNC_INFO, "Unhandled Mode value"); + return QString(); +} + +WritePasswordJobPrivate::Mode WritePasswordJobPrivate::stringToMode(const QString& s) +{ + if (s == QLatin1String("Delete") || s == QLatin1String("0")) + return Delete; + 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; +} + +void WritePasswordJobPrivate::fallbackOnError(const QDBusError &err) +{ + QScopedPointer 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; + } + + if ( mode == Delete ) { + actual->remove( key ); + actual->sync(); + + q->emitFinished(); + return; + } + + actual->setValue( QString::fromLatin1( "%1/type" ).arg( key ), mode ); + if ( mode == Text ) + actual->setValue( QString::fromLatin1( "%1/data" ).arg( key ), textData.toUtf8() ); + else if ( mode == Binary ) + actual->setValue( QString::fromLatin1( "%1/data" ).arg( key ), binaryData ); + actual->sync(); + + q->emitFinished(); +} + +void WritePasswordJobPrivate::gnomeKeyring_cb( int result, WritePasswordJobPrivate* self ) +{ + if ( result == GnomeKeyring::RESULT_OK ) { + self->q->emitFinished(); + } else { + const QPair errorResult = mapGnomeKeyringError( result ); + self->q->emitFinishedWithError( errorResult.first, errorResult.second ); + } +} + +void WritePasswordJobPrivate::kwalletWalletFound(QDBusPendingCallWatcher *watcher) +{ + watcher->deleteLater(); + const QDBusPendingReply reply = *watcher; + const QDBusPendingReply pendingReply = iface->open( reply.value(), 0, q->service() ); + QDBusPendingCallWatcher* pendingWatcher = new QDBusPendingCallWatcher( pendingReply, this ); + connect( pendingWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletOpenFinished(QDBusPendingCallWatcher*)) ); +} + +void WritePasswordJobPrivate::kwalletOpenFinished( QDBusPendingCallWatcher* watcher ) { + watcher->deleteLater(); + QDBusPendingReply reply = *watcher; + + QScopedPointer local( !q->settings() ? new QSettings( q->service() ) : 0 ); + QSettings* actual = q->settings() ? q->settings() : local.data(); + + if ( reply.isError() ) { + fallbackOnError( reply.error() ); + return; + } + + if ( actual->contains( key ) ) + { + // If we had previously written to QSettings, but we now have a kwallet available, migrate and delete old insecure data + actual->remove( key ); + actual->sync(); + } + + const int handle = reply.value(); + + if ( handle < 0 ) { + q->emitFinishedWithError( AccessDenied, tr("Access to keychain denied") ); + return; + } + + QDBusPendingReply nextReply; + + if ( !textData.isEmpty() ) + nextReply = iface->writePassword( handle, q->service(), key, textData, q->service() ); + else if ( !binaryData.isEmpty() ) + nextReply = iface->writeEntry( handle, q->service(), key, binaryData, 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(kwalletWriteFinished(QDBusPendingCallWatcher*)) ); +} + +void WritePasswordJobPrivate::kwalletWriteFinished( QDBusPendingCallWatcher* watcher ) { + watcher->deleteLater(); + QDBusPendingReply reply = *watcher; + if ( reply.isError() ) { + const QDBusError err = reply.error(); + q->emitFinishedWithError( OtherError, tr("Could not open wallet: %1; %2").arg( QDBusError::errorString( err.type() ), err.message() ) ); + return; + } + + q->emitFinished(); +} diff --git a/ext/qtkeychain/keychain_win.cpp b/ext/qtkeychain/keychain_win.cpp new file mode 100644 index 0000000..e2df4b8 --- /dev/null +++ b/ext/qtkeychain/keychain_win.cpp @@ -0,0 +1,107 @@ +/****************************************************************************** + * Copyright (C) 2011-2015 Frank Osterfeld * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution * + * details, check the accompanying file 'COPYING'. * + *****************************************************************************/ +#include "keychain_p.h" + +#include + +#include +#include + +#include + +using namespace QKeychain; + +void ReadPasswordJobPrivate::scheduledStart() { + //Use settings member if there, create local settings object if not + std::auto_ptr local( !q->settings() ? new QSettings( q->service() ) : 0 ); + QSettings* actual = q->settings() ? q->settings() : local.get(); + + QByteArray encrypted = actual->value( key ).toByteArray(); + if ( encrypted.isNull() ) { + q->emitFinishedWithError( EntryNotFound, tr("Entry not found") ); + return; + } + + DATA_BLOB blob_in, blob_out; + + blob_in.pbData = reinterpret_cast( encrypted.data() ); + blob_in.cbData = encrypted.size(); + + const BOOL ret = CryptUnprotectData( &blob_in, + NULL, + NULL, + NULL, + NULL, + 0, + &blob_out ); + if ( !ret ) { + q->emitFinishedWithError( OtherError, tr("Could not decrypt data") ); + return; + } + + data = QByteArray( reinterpret_cast( blob_out.pbData ), blob_out.cbData ); + SecureZeroMemory( blob_out.pbData, blob_out.cbData ); + LocalFree( blob_out.pbData ); + + q->emitFinished(); +} + +void WritePasswordJobPrivate::scheduledStart() { + if ( mode == Delete ) { + //Use settings member if there, create local settings object if not + std::auto_ptr local( !q->settings() ? new QSettings( q->service() ) : 0 ); + QSettings* actual = q->settings() ? q->settings() : local.get(); + actual->remove( key ); + actual->sync(); + if ( actual->status() != QSettings::NoError ) { + const QString err = actual->status() == QSettings::AccessError + ? tr("Could not delete encrypted data from settings: access error") + : tr("Could not delete encrypted data from settings: format error"); + q->emitFinishedWithError( OtherError, err ); + } else { + q->emitFinished(); + } + return; + } + + QByteArray data = mode == Binary ? binaryData : textData.toUtf8(); + DATA_BLOB blob_in, blob_out; + blob_in.pbData = reinterpret_cast( data.data() ); + blob_in.cbData = data.size(); + const BOOL res = CryptProtectData( &blob_in, + L"QKeychain-encrypted data", + NULL, + NULL, + NULL, + 0, + &blob_out ); + if ( !res ) { + q->emitFinishedWithError( OtherError, tr("Encryption failed") ); //TODO more details available? + return; + } + + const QByteArray encrypted( reinterpret_cast( blob_out.pbData ), blob_out.cbData ); + LocalFree( blob_out.pbData ); + + //Use settings member if there, create local settings object if not + std::auto_ptr local( !q->settings() ? new QSettings( q->service() ) : 0 ); + QSettings* actual = q->settings() ? q->settings() : local.get(); + actual->setValue( key, encrypted ); + actual->sync(); + if ( actual->status() != QSettings::NoError ) { + + const QString errorString = actual->status() == QSettings::AccessError + ? tr("Could not store encrypted data in settings: access error") + : tr("Could not store encrypted data in settings: format error"); + q->emitFinishedWithError( OtherError, errorString ); + return; + } + + q->emitFinished(); +} diff --git a/ext/qtkeychain/org.kde.KWallet.xml b/ext/qtkeychain/org.kde.KWallet.xml new file mode 100644 index 0000000..548c2f8 --- /dev/null +++ b/ext/qtkeychain/org.kde.KWallet.xml @@ -0,0 +1,276 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ext/qtkeychain/qkeychain_export.h b/ext/qtkeychain/qkeychain_export.h new file mode 100644 index 0000000..1bb1669 --- /dev/null +++ b/ext/qtkeychain/qkeychain_export.h @@ -0,0 +1,17 @@ +#ifndef QKEYCHAIN_EXPORT_H +#define QKEYCHAIN_EXPORT_H + +#include + +# ifdef QKEYCHAIN_STATICLIB +# undef QKEYCHAIN_SHAREDLIB +# define QKEYCHAIN_EXPORT +# else +# ifdef QKEYCHAIN_BUILD_QKEYCHAIN_LIB +# define QKEYCHAIN_EXPORT Q_DECL_EXPORT +# else +# define QKEYCHAIN_EXPORT Q_DECL_IMPORT +# endif +# endif + +#endif diff --git a/ext/qtkeychain/testclient.cpp b/ext/qtkeychain/testclient.cpp new file mode 100644 index 0000000..a9d9bad --- /dev/null +++ b/ext/qtkeychain/testclient.cpp @@ -0,0 +1,98 @@ +/****************************************************************************** + * Copyright (C) 2011-2015 Frank Osterfeld * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution * + * details, check the accompanying file 'COPYING'. * + *****************************************************************************/ +#include +#include + +#include "keychain.h" +#include + +using namespace QKeychain; + +static int printUsage() { + std::cerr << "testclient store " << std::endl; + std::cerr << "testclient restore " << std::endl; + std::cerr << "testclient delete " << std::endl; + return 1; +} + +int main( int argc, char** argv ) { + QCoreApplication app( argc, argv ); + const QStringList args = app.arguments(); + if ( args.count() < 2 ) + return printUsage(); + + QStringList::ConstIterator it = args.constBegin(); + ++it; + + if ( *it == QLatin1String("store") ) { + if ( ++it == args.constEnd() ) + return printUsage(); + const QString acc = *it; + if ( ++it == args.constEnd() ) + return printUsage(); + const QString pass = *it; + if ( ++it != args.constEnd() ) + return printUsage(); + WritePasswordJob job( QLatin1String("qtkeychain-testclient") ); + job.setAutoDelete( false ); + job.setKey( acc ); + job.setTextData( pass ); + QEventLoop loop; + job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()) ); + job.start(); + loop.exec(); + if ( job.error() ) { + std::cerr << "Storing password failed: " << qPrintable(job.errorString()) << std::endl; + return 1; + } + std::cout << "Password stored successfully" << std::endl; + } else if ( *it == QLatin1String("restore") ) { + if ( ++it == args.constEnd() ) + return printUsage(); + const QString acc = *it; + if ( ++it != args.constEnd() ) + return printUsage(); + ReadPasswordJob job( QLatin1String("qtkeychain-testclient") ); + job.setAutoDelete( false ); + job.setKey( acc ); + QEventLoop loop; + job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()) ); + job.start(); + loop.exec(); + + const QString pw = job.textData(); + if ( job.error() ) { + std::cerr << "Restoring password failed: " << qPrintable(job.errorString()) << std::endl; + return 1; + } + std::cout << qPrintable(pw) << std::endl; + } else if ( *it == QLatin1String("delete") ) { + if ( ++it == args.constEnd() ) + return printUsage(); + const QString acc = *it; + if ( ++it != args.constEnd() ) + return printUsage(); + DeletePasswordJob job( QLatin1String("qtkeychain-testclient") ); + job.setAutoDelete( false ); + job.setKey( acc ); + QEventLoop loop; + job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()) ); + job.start(); + loop.exec(); + + if ( job.error() ) { + std::cerr << "Deleting password failed: " << qPrintable(job.errorString()) << std::endl; + return 1; + } + std::cout << "Password deleted successfully" << std::endl; + } else { + return printUsage(); + } +} + diff --git a/ext/qtkeychain/translations/qtkeychain_de.ts b/ext/qtkeychain/translations/qtkeychain_de.ts new file mode 100644 index 0000000..cd8aad0 --- /dev/null +++ b/ext/qtkeychain/translations/qtkeychain_de.ts @@ -0,0 +1,177 @@ + + + + + QKeychain::ReadPasswordJobPrivate + + + Unknown error + Unbekannter Fehler + + + + D-Bus is not running + + + + + No keychain service available + Kein Schlüsselbund-Dienst verfügbar + + + + Could not open wallet: %1; %2 + Konnte Brieftasche nicht öffnen: %1; %2 + + + + Access to keychain denied + Zugriff auf Schlüsselbund verweigert + + + + Could not determine data type: %1; %2 + Datentyp kann nicht ermittelt werden: %1: %2 + + + + Unsupported entry type 'Map' + + + + + Unknown kwallet entry type '%1' + + + + + Could not read password: %1; %2 + Passwort konnte nicht ausgelesen werden: %1; %2 + + + + Password not found + Passwort nicht gefunden + + + + + Entry not found + Eintrag nicht gefunden + + + + Could not decrypt data + Kann Daten nicht entschlüsseln + + + + QKeychain::WritePasswordJobPrivate + + + + Unknown error + Unbekannter Fehler + + + + D-Bus is not running + + + + + + Could not open wallet: %1; %2 + Konnte Brieftasche nicht öffnen: %1; %2 + + + + Access to keychain denied + Zugriff auf Schlüsselbund verweigert + + + + Could not delete encrypted data from settings: access error + Kann verschlüsselte Daten nicht aus den Einstellungen entfernen: Zugriffsfehler + + + + Could not delete encrypted data from settings: format error + Kann verschlüsselte Daten nicht aus den Einstellungen entfernen: Formatfehler + + + + Encryption failed + Verschlüsselung fehlgeschlagen + + + + Could not store encrypted data in settings: access error + Kann verschlüsselte Daten nicht in den Einstellungen speichern: Zugriffsfehler + + + + Could not store encrypted data in settings: format error + Kann verschlüsselte Daten nicht in den Einstellungen speichern: Formatfehler + + + + QObject + + + Access to keychain denied + Zugriff auf Schlüsselbund verweigert + + + + No keyring daemon + Kein Schlüsselbund-Dienst + + + + Already unlocked + Bereits entsperrt + + + + No such keyring + Kein solcher Schlüsselbund + + + + Bad arguments + Ungültige Argumente + + + + I/O error + Ein-/Ausgabe-Fehler + + + + Cancelled + Abgebrochen + + + + Keyring already exists + Schlüsselbund existiert bereits + + + + No match + Kein Treffer + + + + Unknown error + Unbekannter Fehler + + + + + %1 (OSStatus %2) + + + + diff --git a/ext/qtkeychain/translations/qtkeychain_ro.ts b/ext/qtkeychain/translations/qtkeychain_ro.ts new file mode 100644 index 0000000..9fa3494 --- /dev/null +++ b/ext/qtkeychain/translations/qtkeychain_ro.ts @@ -0,0 +1,178 @@ + + + + + QKeychain::ReadPasswordJobPrivate + + + Unknown error + Eroare necunoscută + + + + D-Bus is not running + D-Bus nu rulează + + + + No keychain service available + Nu există niciun serviciu de chei disponibil + Kein Schlüsselbund-Dienst verfügbar + + + + Could not open wallet: %1; %2 + Nu se poate deschide portofelul: %1; %2 + + + + Access to keychain denied + Acces interzis la serviciul de chei + + + + Could not determine data type: %1; %2 + Nu se poate stabili tipul de date: %1: %2 + + + + Unsupported entry type 'Map' + Tip de înregistrare nesuportat 'Map' + + + + Unknown kwallet entry type '%1' + Tip de înregistrare kwallet necunoscut '%1' + + + + Could not read password: %1; %2 + Nu se poate citi parola: %1; %2 + + + + Password not found + Parola nu a fost găsită + + + + + Entry not found + Înregistrarea nu a fost găsită + + + + Could not decrypt data + Nu se poate decripta data + + + + QKeychain::WritePasswordJobPrivate + + + + Unknown error + Eroare necunoscută + + + + D-Bus is not running + D-Bus nu rulează + + + + + Could not open wallet: %1; %2 + Nu se poate deschide portofelul: %1; %2 + + + + Access to keychain denied + Acces interzis la serviciul de chei + + + + Could not delete encrypted data from settings: access error + Nu se pot șterge datele criptate din setări: eroare de acces + + + + Could not delete encrypted data from settings: format error + Nu se pot șterge datele criptate din setări: eroare de format + + + + Encryption failed + Criptarea a eșuat + + + + Could not store encrypted data in settings: access error + Nu se pot stoca datele criptate în setări: eroare de acces + + + + Could not store encrypted data in settings: format error + Nu se pot stoca datele criptate în setări: eroare de format + + + + QObject + + + Access to keychain denied + Acces interzis la serviciul de chei + + + + No keyring daemon + Niciun demon pentru inelul de chei + + + + Already unlocked + Deja deblocat + + + + No such keyring + Nu există astfel de inel de chei + + + + Bad arguments + Argumente greșite + + + + I/O error + Eroare de I/E + + + + Cancelled + Anulat + + + + Keyring already exists + Inelul de chei deja există + + + + No match + Nicio potrivire + + + + Unknown error + Eroare necunoscută + + + + + %1 (OSStatus %2) + %1 (OSStatus %2) + + + diff --git a/fuel.pro b/fuel.pro index 1c3ab8f..36383a8 100644 --- a/fuel.pro +++ b/fuel.pro @@ -7,6 +7,10 @@ QT = core gui webkit contains(QT_VERSION, ^5\\..*) { QT += widgets webkitwidgets QT -= quick multimediawidgets opengl printsupport qml multimedia positioning sensors + + unix:!macx { + QT += dbus + } } TARGET = Fuel @@ -92,6 +96,33 @@ FORMS += ui/MainWindow.ui \ RESOURCES += \ rsrc/resources.qrc +# QtKeychain +SOURCES += ext/qtkeychain/keychain.cpp + +HEADERS += ext/qtkeychain/keychain.h \ + ext/qtkeychain/keychain_p.h \ + ext/qtkeychain/qkeychain_export.h + +unix:!macx { + SOURCES += ext/qtkeychain/keychain_unix.cpp \ + ext/qtkeychain/gnomekeyring.cpp + + HEADERS += ext/qtkeychain/gnomekeyring_p.h + + DBUS_INTERFACES += ext/qtkeychain/org.kde.KWallet.xml +} + +macx { + SOURCES += ext/qtkeychain/keychain_mac.cpp +} + +win32 { + SOURCES += ext/qtkeychain/keychain_win.cpp +} + + + + CODECFORTR = UTF-8 TRANSLATIONS += \ diff --git a/manifest b/manifest index 2d16cb4..e64a918 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Initial\swork\son\sremote\smanagement -D 2015-05-25T12:27:27.983 +C Implemented\sremote\scredential\sstorage\svia\sqtkeychain\nImported\sqtkeychain +D 2015-05-25T14:03:38.817 F .travis.yml 77966888a81c4ceee1fcc79bce842c9667ad8a35 F debian/changelog eb4304dfcb6bb66850ec740838090eb50ce1249b F debian/compat b6abd567fa79cbe0196d093a067271361dc6ca8b @@ -15,7 +15,29 @@ F dist/win/fuel.iss ef3558dbba409eb194938b930377fc9ee27d319e F doc/Building.txt 17b43fa23da764b5d1b828cc48c5a95e612bbd8f F doc/Changes.txt b03302545e4a6c0b16a30d623a7627f8aef65ef6 F doc/License.txt 4cc77b90af91e615a64ae04893fdffa7939db84c -F fuel.pro 641f62716fc90c9430f5c4cbc415c9d63f833a52 +F ext/qtkeychain/CMakeLists.txt fc1afa05034f2765ba243ce758a7e9d6b6efe2d6 +F ext/qtkeychain/COPYING d0f83c8198fdd5464d2373015b7b64ce7cae607e +F ext/qtkeychain/ChangeLog 1703279e17036995806ba1719033d14840a8a7e2 +F ext/qtkeychain/QtKeychainBuildTreeSettings.cmake.in a50c3b646181124f15b946c3297f13012e959341 +F ext/qtkeychain/QtKeychainConfig.cmake.in ac7c87e54854a06c51e00f833f21f8323d1e6884 +F ext/qtkeychain/QtKeychainConfigVersion.cmake.in 3b650037d5775f28802c0471afe2cf6dbe51084e +F ext/qtkeychain/ReadMe.markdown 65fe7f400600aa98a9a7fa5c3fc842ad8699cc43 +F ext/qtkeychain/ReadMe.txt 65fe7f400600aa98a9a7fa5c3fc842ad8699cc43 +F ext/qtkeychain/cmake/Modules/GNUInstallDirs.cmake 7a2ccf81f25546e93a6f48c792cdebaae51857e9 +F ext/qtkeychain/gnomekeyring.cpp 7fa97bd4ffb7c9df00d25e56cd9173d34109afe6 +F ext/qtkeychain/gnomekeyring_p.h 7f6acaf6d00b36bd08f5f31ba9136efa969e9875 +F ext/qtkeychain/keychain.cpp 427cbda7c6a76de995b1f1b4caa700cd06a9d19a +F ext/qtkeychain/keychain.h f084c671b481af6ac7ce00bf641055a3cfc9cf9b +F ext/qtkeychain/keychain_mac.cpp a028f6fc5e40f9ab88c94ebe30b8b0ae417f2f34 +F ext/qtkeychain/keychain_p.h 36f4caee2cbdbde971a1105ab388681ad2924665 +F ext/qtkeychain/keychain_unix.cpp 8e657da9acd9e86b2fdec19dc40f1afa4a1c5191 +F ext/qtkeychain/keychain_win.cpp e52877828703650219c1c674e618c7211f588d0d +F ext/qtkeychain/org.kde.KWallet.xml f3729fda9f8fa8031a6f69415dcc29455c3c9ae6 +F ext/qtkeychain/qkeychain_export.h d756528188ef9bf3c4461ecc80048f06c362b54b +F ext/qtkeychain/testclient.cpp cb1290a9584b627306a7bfdf1c02a8bbae503f5f +F ext/qtkeychain/translations/qtkeychain_de.ts 0a70c8205c066c30ed8172f0670942de1fc5eede +F ext/qtkeychain/translations/qtkeychain_ro.ts f16939382fd1a047b0692426bc82847347f14b32 +F fuel.pro 0522a3f57fa34122d47ccd0a62bf14aeb1caeb03 F intl/convert.bat 4222ae403418381452b843929d15259ea9850ab1 x F intl/convert.sh 2ca2179ff53e727f241925b75e19182607883c45 x F intl/de_DE.ts e2faceab920ac60c97bbc6fba038e261d51fc741 @@ -202,9 +224,9 @@ F src/FslSettingsDialog.cpp f5a34a70ecb0560d2b6eea6bf27e42048548aedd F src/FslSettingsDialog.h dfe2a61884a55a74cbb9206b6f6b482b979725e7 F src/LoggedProcess.cpp 2a1e5c94bc1e57c8984563e66c210e43a14dc60c F src/LoggedProcess.h 85df7c635c807a5a0e8c4763f17a0752aaff7261 -F src/MainWindow.cpp 717ccd4c91127a08d8cc71d46d8df1a40db62146 +F src/MainWindow.cpp 2bfafff682c13a9176b4f4200c54c2c5c55b8ae8 F src/MainWindow.h 6cb82a4fc337793a339ffef4728e6d30b7b74305 -F src/RemoteDialog.cpp f0ee2d05e6d6c079f2a00bb551431407eb9d783d +F src/RemoteDialog.cpp 03a6cf213d6dde68ee0ceb8d122e05da2d491724 F src/RemoteDialog.h 364e867210ec55d8d6d23d0c8078f2517b2389ec F src/RevisionDialog.cpp 51065c65a07c118dd1a7363da4a55a135d1c6c9c F src/RevisionDialog.h b718c3009342eaabad39c8a11a253a4e4fef7a73 @@ -214,8 +236,8 @@ F src/Settings.cpp 6ab826273b9693bfcd65f0f59b550ae2aa3577f1 F src/Settings.h 1ff8bb71e19949150e8caa4f5e5f13f8810e496b F src/SettingsDialog.cpp 25be4c351dd21ea9132321944f42dc0bc22fb128 F src/SettingsDialog.h b324dfd77ca3ad24fd83588aaf79a7e4c291e716 -F src/Utils.cpp c48e3316f3a0a5d735e8d4953710500358985e32 -F src/Utils.h c2a28611bd77fb35ea3dcf65fb60ed585f68aa3c +F src/Utils.cpp abfd679b8a4a320207abe385b8f3a4af8f646a55 +F src/Utils.h c293175ea08801aa4f0513bf2820213f8958f704 F src/Workspace.cpp f68a4ca05d1b7c5c345fbd89527691813593c663 F src/Workspace.h d6649a3ae1cd0fbad55237030313e85530417271 F src/main.cpp d8c65ea5e54102e4989fef9fd8cfd4f13ef8a8f0 @@ -230,10 +252,7 @@ F ui/MainWindow.ui 72d363caef1a9ce0484509cdf3e5f924af363381 F ui/RemoteDialog.ui aa738503e4f6625ed8d7cc46169a319a39b0e575 F ui/RevisionDialog.ui 27c3b98c665fec014a50cbf3352c0627f75e68cd F ui/SettingsDialog.ui 4c480cd595a32664d01c85bf74845c4282fc0068 -P 7ebac37b3255e6721889c070b7327a96c63b6c97 -R 70bbd6d1fd63555631752f47198d7051 -T *branch * remote-management -T *sym-remote-management * -T -sym-new-workspace * +P 13d3a5222dfe24a33a72efffff0ecb0b5bfefe73 +R 9e773ad6f2945f8bd613b0b9f6e23acf U kostas -Z a9dba47682969d430cd680d884c75123 +Z 96c30d4c2ad3ef32f51d03d388cfc133 diff --git a/manifest.uuid b/manifest.uuid index 853493f..310e153 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -13d3a5222dfe24a33a72efffff0ecb0b5bfefe73 \ No newline at end of file +7c068aa8acdae1b86dee004d2d4cd7fb42904186 \ No newline at end of file diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 1e6b716..3ad952a 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -2505,12 +2505,18 @@ void MainWindow::on_actionEditRemote_triggered() { QStringList remotes; getSelectionRemotes(remotes); - if(remotes.empty()) return; QUrl url(remotes.first()); + bool exists = KeychainGet(this, url); if(!RemoteDialog::run(this, url)) return; + + if(exists) + KeychainDelete(this, url); + + if(!KeychainSet(this, url)) + QMessageBox::critical(this, tr("Error"), tr("Could not store information to keychain."), QMessageBox::Ok ); } diff --git a/src/RemoteDialog.cpp b/src/RemoteDialog.cpp index ed308c9..1b25ad3 100644 --- a/src/RemoteDialog.cpp +++ b/src/RemoteDialog.cpp @@ -29,8 +29,8 @@ bool RemoteDialog::run(QWidget *parent, QUrl &url) // Set URL components if(!url.isEmpty()) { - QString qq = url.toString(QUrl::PrettyDecoded|QUrl::RemoveUserInfo); - dlg.ui->lineURL->setText(qq); + QString url_no_credentials = url.toString(QUrl::PrettyDecoded|QUrl::RemoveUserInfo); + dlg.ui->lineURL->setText(url_no_credentials); dlg.ui->lineUserName->setText(url.userName()); dlg.ui->linePassword->setText(url.password()); } diff --git a/src/Utils.cpp b/src/Utils.cpp index 4dedf39..990a4f5 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -2,6 +2,9 @@ #include #include #include +#include +#include "ext/qtkeychain/keychain.h" +#define KEYCHAIN_ROOT "Fuel-SCM" /////////////////////////////////////////////////////////////////////////////// QMessageBox::StandardButton DialogQuery(QWidget *parent, const QString &title, const QString &query, QMessageBox::StandardButtons buttons) @@ -388,4 +391,56 @@ void BuildNameToModelIndex(name_modelindex_map_t &map, const QStandardItemModel Q_ASSERT(item); BuildNameToModelIndex(map, *item); } -} \ No newline at end of file +} + +//------------------------------------------------------------------------------ +bool KeychainSet(QObject *parent, const QUrl &url) +{ + QEventLoop loop(parent); + QKeychain::WritePasswordJob job(url.toString(QUrl::PrettyDecoded|QUrl::RemoveUserInfo)); + job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()) ); + job.setAutoDelete( false ); + job.setInsecureFallback(false); + job.setKey(url.userName()); + job.setTextData(url.password()); + job.start(); + loop.exec(); + return job.error() == QKeychain::NoError; +} + +//------------------------------------------------------------------------------ +bool KeychainGet(QObject *parent, QUrl &url) +{ + QEventLoop loop(parent); + QKeychain::ReadPasswordJob job(url.toString(QUrl::PrettyDecoded|QUrl::RemoveUserInfo)); + job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit())); + job.setAutoDelete( false ); + job.setInsecureFallback(false); + job.setAutoDelete( false ); + job.setKey(url.userName()); + job.start(); + loop.exec(); + + if(job.error() != QKeychain::NoError) + return false; + + url.setUserName(job.key()); + url.setPassword(job.textData()); + return true; +} + +//------------------------------------------------------------------------------ +bool KeychainDelete(QObject* parent, const QUrl& url) +{ + QEventLoop loop(parent); + QKeychain::DeletePasswordJob job(url.toString(QUrl::PrettyDecoded|QUrl::RemoveUserInfo)); + job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit())); + job.setAutoDelete( false ); + job.setInsecureFallback(false); + job.setAutoDelete( false ); + job.setKey(url.userName()); + job.start(); + loop.exec(); + + return job.error() == QKeychain::NoError; +} diff --git a/src/Utils.h b/src/Utils.h index 0e86eae..dc0322e 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -22,6 +22,9 @@ typedef QMap name_modelindex_map_t; void GetStandardItemTextRecursive(QString &name, const QStandardItem &item, const QChar &separator='/'); void BuildNameToModelIndex(name_modelindex_map_t &map, const QStandardItem &item); void BuildNameToModelIndex(name_modelindex_map_t &map, const QStandardItemModel &model); +bool KeychainSet(QObject* parent, const QUrl& url); +bool KeychainGet(QObject* parent, QUrl& url); +bool KeychainDelete(QObject* parent, const QUrl& url); typedef QMap QStringMap;