From 078164838e656b97c38fa8bc2ce91426b301a648 Mon Sep 17 00:00:00 2001 From: Edgar Date: Sun, 30 May 2021 13:37:40 +0200 Subject: [PATCH] :arrow_up: Update qtkeychain --- .github/dependabot.yml | 8 + .github/workflows/build.yml | 77 ++++ .gitignore | 2 + .travis.yml | 10 - CMakeLists.txt | 154 +++++++ appveyor.yml | 35 -- ext/qtkeychain.pri | 9 + ext/qtkeychain/.gitignore | 50 +++ ext/qtkeychain/CMakeLists.txt | 378 +++++++++++------ ext/qtkeychain/COPYING | 3 + ext/qtkeychain/ChangeLog | 59 +++ .../QtKeychainBuildTreeSettings.cmake.in | 4 - ext/qtkeychain/QtKeychainConfig.cmake.in | 24 +- .../QtKeychainConfigVersion.cmake.in | 11 - ext/qtkeychain/ReadMe.markdown | 15 - ext/qtkeychain/ReadMe.md | 35 ++ ext/qtkeychain/ReadMe.txt | 15 - ext/qtkeychain/androidkeystore.cpp | 302 ++++++++++++++ ext/qtkeychain/androidkeystore_p.h | 371 +++++++++++++++++ ext/qtkeychain/appveyor.yml | 31 ++ ext/qtkeychain/appveyorHelp.psm1 | 362 ++++++++++++++++ .../cmake/Modules/ECMGeneratePriFile.cmake | 199 +++++++++ .../Modules/ECMPackageConfigHelpers.cmake | 202 +++++++++ .../cmake/Modules/ECMQueryQmake.cmake | 46 +++ .../cmake/Modules/ECMSetupVersion.cmake | 202 +++++++++ ext/qtkeychain/gnomekeyring.cpp | 33 +- ext/qtkeychain/gnomekeyring_p.h | 12 +- ext/qtkeychain/keychain.cpp | 154 +++---- ext/qtkeychain/keychain.h | 202 +++++++-- ext/qtkeychain/keychain_android.cpp | 182 ++++++++ ext/qtkeychain/keychain_apple.mm | 146 +++++++ ext/qtkeychain/keychain_haiku.cpp | 187 +++++++++ ext/qtkeychain/keychain_mac.cpp | 161 -------- ext/qtkeychain/keychain_p.h | 146 +++---- ext/qtkeychain/keychain_unix.cpp | 390 +++++++++++------- ext/qtkeychain/keychain_win.cpp | 157 +++++-- ext/qtkeychain/libsecret.cpp | 350 ++++++++++++++++ ext/qtkeychain/libsecret_p.h | 33 ++ ext/qtkeychain/plaintextstore.cpp | 110 +++++ ext/qtkeychain/plaintextstore_p.h | 47 +++ ext/qtkeychain/qkeychain_export.h | 17 - ext/qtkeychain/qt5keychain.pri | 100 +++++ ext/qtkeychain/testclient.cpp | 23 ++ ext/qtkeychain/translations/qtkeychain_de.ts | 235 +++++++++-- ext/qtkeychain/translations/qtkeychain_fr.ts | 334 +++++++++++++++ ext/qtkeychain/translations/qtkeychain_ro.ts | 239 +++++++++-- ext/qtkeychain/translations/qtkeychain_ru.ts | 310 ++++++++++++++ ext/qtkeychain/translations/qtkeychain_zh.ts | 330 +++++++++++++++ fuel.pro | 35 +- intl/convert.bat | 20 - intl/convert.sh | 36 -- intl/de_DE.ts | 336 ++++----------- intl/el_GR.ts | 314 ++++---------- intl/en_US.ts | 314 ++++---------- intl/es_ES.ts | 336 ++++----------- intl/fr_FR.ts | 314 ++++---------- intl/it_IT.ts | 314 ++++---------- intl/ko_KR.ts | 314 ++++---------- intl/nl_NL.ts | 314 ++++---------- intl/pt_PT.ts | 314 ++++---------- intl/ru_RU.ts | 314 ++++---------- intl/update.sh | 5 - rsrc/resources.qrc | 38 +- 63 files changed, 6375 insertions(+), 3445 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/build.yml delete mode 100644 .travis.yml create mode 100644 CMakeLists.txt delete mode 100644 appveyor.yml create mode 100644 ext/qtkeychain.pri create mode 100644 ext/qtkeychain/.gitignore delete mode 100644 ext/qtkeychain/QtKeychainBuildTreeSettings.cmake.in delete mode 100644 ext/qtkeychain/QtKeychainConfigVersion.cmake.in delete mode 100644 ext/qtkeychain/ReadMe.markdown create mode 100644 ext/qtkeychain/ReadMe.md delete mode 100644 ext/qtkeychain/ReadMe.txt create mode 100644 ext/qtkeychain/androidkeystore.cpp create mode 100644 ext/qtkeychain/androidkeystore_p.h create mode 100644 ext/qtkeychain/appveyor.yml create mode 100644 ext/qtkeychain/appveyorHelp.psm1 create mode 100644 ext/qtkeychain/cmake/Modules/ECMGeneratePriFile.cmake create mode 100644 ext/qtkeychain/cmake/Modules/ECMPackageConfigHelpers.cmake create mode 100644 ext/qtkeychain/cmake/Modules/ECMQueryQmake.cmake create mode 100644 ext/qtkeychain/cmake/Modules/ECMSetupVersion.cmake create mode 100644 ext/qtkeychain/keychain_android.cpp create mode 100644 ext/qtkeychain/keychain_apple.mm create mode 100644 ext/qtkeychain/keychain_haiku.cpp delete mode 100644 ext/qtkeychain/keychain_mac.cpp create mode 100644 ext/qtkeychain/libsecret.cpp create mode 100644 ext/qtkeychain/libsecret_p.h create mode 100644 ext/qtkeychain/plaintextstore.cpp create mode 100644 ext/qtkeychain/plaintextstore_p.h delete mode 100644 ext/qtkeychain/qkeychain_export.h create mode 100644 ext/qtkeychain/qt5keychain.pri create mode 100644 ext/qtkeychain/translations/qtkeychain_fr.ts create mode 100644 ext/qtkeychain/translations/qtkeychain_ru.ts create mode 100644 ext/qtkeychain/translations/qtkeychain_zh.ts delete mode 100755 intl/convert.bat delete mode 100755 intl/convert.sh delete mode 100755 intl/update.sh diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..0ef7bb7 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,8 @@ +# Set update schedule for GitHub Actions +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + # Check for updates to GitHub Actions every weekday + interval: "weekly" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..5527c49 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,77 @@ +--- +name: Build + +on: [push, pull_request] + +jobs: + build-msvc: + name: Build Windows + runs-on: windows-latest + env: + BUILD_TOOLS_PATH: C:\apps\build-tools\ + steps: + - run: echo $env:BUILD_TOOLS_PATH | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + + - uses: actions/checkout@v2.3.4 + with: + submodules: true + + - name: Install JOM + shell: cmake -P {0} + run: | + file(MAKE_DIRECTORY $ENV{BUILD_TOOLS_PATH}) + file(DOWNLOAD http://prdownloads.sourceforge.net/rigs-of-rods/build-tools.zip "$ENV{TMP}/build-tools.zip" SHOW_PROGRESS) + execute_process(COMMAND ${CMAKE_COMMAND} -E tar xzf "$ENV{TMP}/build-tools.zip" WORKING_DIRECTORY "$ENV{BUILD_TOOLS_PATH}") + + - name: Enable Developer Command Prompt + uses: ilammy/msvc-dev-cmd@v1.8.0 + + - name: Cache Qt + id: cache-qt + uses: actions/cache@v1 + with: + path: ../Qt + key: ${{ runner.os }}-QtCache + + - name: Install Qt + uses: jurplel/install-qt-action@v2 + with: + cached: ${{ steps.cache-qt.outputs.cache-hit }} + modules: qtwebengine + + - name: Build + run: | + mkdir build + cd build + cmake -G"NMake Makefiles JOM" .. + jom + shell: cmd + + build-gcc: + name: Build Linux + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2.3.4 + with: + submodules: true + + - name: Cache Qt + id: cache-qt + uses: actions/cache@v1 + with: + path: ../Qt + key: ${{ runner.os }}-QtCache + + - name: Install Qt + uses: jurplel/install-qt-action@v2 + with: + cached: ${{ steps.cache-qt.outputs.cache-hit }} + modules: qtwebengine + + - name: Build + run: | + mkdir build + cd build + cmake -DCMAKE_BUILD_TYPE=Release .. + make -j8 + shell: bash \ No newline at end of file diff --git a/.gitignore b/.gitignore index cad9482..423bea4 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,5 @@ target_wrapper.* CMakeLists.txt.user* *.qm +/build* +.idea/ \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index d44f482..0000000 --- a/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -dist: bionic -language: cpp - -install: - - sudo apt-get update - - sudo apt-get install qt5-default qtwebengine5-dev qttools5-dev-tools -y - -script: - - qmake fuel.pro - - make diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..ceb6baf --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,154 @@ +cmake_minimum_required(VERSION 3.10) +cmake_policy(SET CMP0087 NEW) +include(FeatureSummary) + +# Setup paths +SET(RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/") +SET(LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/") +SET(ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/") +SET(EXECUTABLE_OUTPUT_PATH ${RUNTIME_OUTPUT_DIRECTORY}) +SET(LIBRARY_OUTPUT_PATH ${RUNTIME_OUTPUT_DIRECTORY}) +# Fix executable paths for windows +SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${RUNTIME_OUTPUT_DIRECTORY}) +SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${RUNTIME_OUTPUT_DIRECTORY}) +SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${RUNTIME_OUTPUT_DIRECTORY}) +SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${RUNTIME_OUTPUT_DIRECTORY}) + +project(Fuel) + +option(UPDATE_TRANSLATIONS "Update translations" OFF) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOUIC_SEARCH_PATHS "${CMAKE_SOURCE_DIR}/ui") + +find_package(Qt5 COMPONENTS Core Gui WebEngineWidgets REQUIRED) + +set(FORMS + ui/MainWindow.ui + ui/CommitDialog.ui + ui/FileActionDialog.ui + ui/SettingsDialog.ui + ui/FslSettingsDialog.ui + ui/CloneDialog.ui + ui/BrowserWidget.ui + ui/RevisionDialog.ui + ui/RemoteDialog.ui + ui/AboutDialog.ui) + +set(SOURCES + src/main.cpp + src/MainWindow.cpp + src/CommitDialog.cpp + src/FileActionDialog.cpp + src/SettingsDialog.cpp + src/FslSettingsDialog.cpp + src/CloneDialog.cpp + src/RevisionDialog.cpp + src/Utils.cpp + src/FileTableView.cpp + src/LoggedProcess.cpp + src/BrowserWidget.cpp + src/CustomWebView.cpp + src/Fossil.cpp + src/Workspace.cpp + src/SearchBox.cpp + src/AppSettings.cpp + src/RemoteDialog.cpp + src/AboutDialog.cpp) + +set(HEADERS + src/MainWindow.h + src/CommitDialog.h + src/FileActionDialog.h + src/SettingsDialog.h + src/FslSettingsDialog.h + src/CloneDialog.h + src/RevisionDialog.h + src/Utils.h + src/FileTableView.h + src/LoggedProcess.h + src/BrowserWidget.h + src/CustomWebView.h + src/Fossil.h + src/Workspace.h + src/SearchBox.h + src/AppSettings.h + src/RemoteDialog.h + src/AboutDialog.h + src/WorkspaceCommon.h) + + +set(RESOURCES + rsrc/resources.qrc) + +set(TRANSLATIONS + intl/en_US.ts + intl/el_GR.ts + intl/de_DE.ts + intl/es_ES.ts + intl/fr_FR.ts + intl/ru_RU.ts + intl/pt_PT.ts + intl/it_IT.ts + intl/nl_NL.ts + intl/ko_KR.ts) + +set_source_files_properties(${TRANSLATIONS} PROPERTIES OUTPUT_LOCATION "${RUNTIME_OUTPUT_DIRECTORY}translations") + +find_package(Qt5LinguistTools) +if (Qt5LinguistTools_FOUND) + if (UPDATE_TRANSLATIONS) + qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR}/ui ${CMAKE_SOURCE_DIR}/src ${TRANSLATIONS}) + else () + qt5_add_translation(QM_FILES ${TRANSLATIONS}) + endif () + install(FILES ${QM_FILES} DESTINATION ./translations) +else () + message("Qt5LinguistTools not found") +endif () + +if (WIN32) + # clang-cl doesn't support resource files + if (NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang") + list(APPEND SOURCES "${CMAKE_SOURCE_DIR}/rsrc/fuel.rc") + endif () +elseif (APPLE) + list(APPEND SOURCES "${CMAKE_SOURCE_DIR}/rsrc/icons/fuel.icns") +endif () + +add_subdirectory("${CMAKE_SOURCE_DIR}/ext/qtkeychain" EXCLUDE_FROM_ALL) + +add_executable(${PROJECT_NAME} + ${FORMS} + ${HEADERS} + ${SOURCES} + ${QM_FILES} + ${RESOURCES}) + +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/ui/ + ${CMAKE_SOURCE_DIR}/src/) +target_link_libraries(${PROJECT_NAME} Qt5::Widgets Qt5::WebEngineCore Qt5::WebEngineWidgets qt5keychain) + +if (WIN32) + set_target_properties(${PROJECT_NAME} PROPERTIES WIN32_EXECUTABLE TRUE) + + get_target_property(Qt5_Core_Location Qt5::Core LOCATION) + get_filename_component(QT_BIN_DIR ${Qt5_Core_Location} DIRECTORY) + + add_custom_target(copy_dll + COMMAND ${QT_BIN_DIR}/windeployqt.exe $ --release --no-compiler-runtime --no-translations --no-opengl-sw + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + ) + + install(TARGETS ${PROJECT_NAME} + RUNTIME DESTINATION . + ) + + install(CODE "execute_process(COMMAND ${QT_BIN_DIR}/windeployqt.exe $ --release --no-compiler-runtime --no-translations --no-opengl-sw --dir \${CMAKE_INSTALL_PREFIX})") +endif () + + +feature_summary(WHAT ALL) \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index f887ec6..0000000 --- a/appveyor.yml +++ /dev/null @@ -1,35 +0,0 @@ -version: "{build}" -image: Visual Studio 2017 -pull_requests: - do_not_increment_build_number: true -shallow_clone: true - -install: -- cmd: >- - set QTDIR=C:\Qt\5.13\msvc2017_64 - - set PATH=%PATH%;%QTDIR%\bin - - call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 -build_script: -- cmd: >- - dir - - mkdir build - - cd build - - qmake ../fuel.pro - - set cl=/MP - - nmake /S /NOLOGO - - windeployqt release/fuel.exe --release --no-compiler-runtime --no-opengl-sw --dir fuel-scm/ - - cp release/fuel.exe fuel-scm/ - - -artifacts: -- path: build/fuel-scm - name: fuel-scm diff --git a/ext/qtkeychain.pri b/ext/qtkeychain.pri new file mode 100644 index 0000000..0cda579 --- /dev/null +++ b/ext/qtkeychain.pri @@ -0,0 +1,9 @@ +freebsd { + INCLUDEPATH += /usr/local/include/qt5keychain + LIBS += -lqt5keychain +} else { + unix:!android:!macx:!ios { + DEFINES += KEYCHAIN_DBUS + } + include(qtkeychain/qt5keychain.pri) +} diff --git a/ext/qtkeychain/.gitignore b/ext/qtkeychain/.gitignore new file mode 100644 index 0000000..8fcb5ea --- /dev/null +++ b/ext/qtkeychain/.gitignore @@ -0,0 +1,50 @@ +#CMake files +CMakeCache.txt +CMakeFiles +CMakeScripts +cmake_install.cmake + +#Keychain temporary files +Qt5KeychainBuildTreeSettings.cmake +Qt5KeychainConfig.cmake +Qt5KeychainConfigVersion.cmake +QtKeychainBuildTreeSettings.cmake +QtKeychainConfig.cmake +QtKeychainConfigVersion.cmake +kwallet_interface.cpp +kwallet_interface.h +kwallet_interface.moc +moc_keychain.* +moc_keychain_p.* +moc_gnomekeyring_p.* +qkeychain_export.h +qt_Qt5Keychain.pri + +#Qt files +*_parameters +*.qm + +#General build files +Debug +Release +Makefile + +#Linux build files +libqt5keychain.* +testclient + +#Windows build files +install_manifest.txt +*.manifest +*.lib +*.exe + +#Mac build files +qtkeychain.xcodeproj +qtkeychain.build + +#Temporary files +*.sw? +*~ + + diff --git a/ext/qtkeychain/CMakeLists.txt b/ext/qtkeychain/CMakeLists.txt index 7736c60..3de8afb 100644 --- a/ext/qtkeychain/CMakeLists.txt +++ b/ext/qtkeychain/CMakeLists.txt @@ -1,195 +1,307 @@ -cmake_minimum_required(VERSION 2.8) -project(qtkeychain) +cmake_minimum_required(VERSION 3.3) -### +set(QTKEYCHAIN_VERSION 0.12.90) +set(QTKEYCHAIN_SOVERSION 1) -set(QTKEYCHAIN_VERSION 0.5.90) -set(QTKEYCHAIN_SOVERSION 0) +project(qtkeychain VERSION ${QTKEYCHAIN_VERSION} LANGUAGES CXX) + +# Enable C++11 +SET(CMAKE_CXX_STANDARD 11) + +include(FindPkgConfig) ### set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${PROJECT_SOURCE_DIR}/cmake/Modules") include(GNUInstallDirs) +include(GenerateExportHeader) +include(CMakePackageConfigHelpers) +include(ECMSetupVersion) +include(ECMGeneratePriFile) -option(BUILD_WITH_QT4 "Build qtkeychain with Qt4 no matter if Qt5 was found" OFF) +option(BUILD_WITH_QT6 "Build qtkeychain with Qt 6" OFF) +option(BUILD_TEST_APPLICATION "Build test application" ON) +option(BUILD_TRANSLATIONS "Build translations" ON) +option(QTKEYCHAIN_STATIC "Build static library" OFF) +if (CMAKE_SYSTEM_NAME STREQUAL Android) + set(ANDROID 1) +endif () -if( NOT BUILD_WITH_QT4 ) - # try Qt5 first, and prefer that if found - find_package(Qt5Core QUIET) -endif() +if (CMAKE_SYSTEM_NAME STREQUAL Haiku) + set(HAIKU 1) +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}) +if (WIN32) + option(USE_CREDENTIAL_STORE "Build with windows CredentialStore support" ON) + + if (USE_CREDENTIAL_STORE) + add_definitions(-DUSE_CREDENTIAL_STORE=1) + endif () +endif () + +if (NOT BUILD_WITH_QT6) + find_package(Qt5 COMPONENTS Core REQUIRED) +endif () + +if (Qt5Core_FOUND AND NOT BUILD_WITH_QT6) + set(QTKEYCHAIN_VERSION_INFIX 5) + + if (ANDROID) + if (Qt5Core_VERSION VERSION_LESS 5.7) + find_package(Qt5 COMPONENTS Core REQUIRED Private) + include_directories(${Qt5Core_PRIVATE_INCLUDE_DIRS}) + endif () + + find_package(Qt5 COMPONENTS AndroidExtras REQUIRED) + include_directories(${Qt5AndroidExtras_INCLUDE_DIRS}) + set(QTANDROIDEXTRAS_LIBRARIES ${Qt5AndroidExtras_LIBRARIES}) + endif () + + if (UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU) + find_package(Qt5 COMPONENTS DBus REQUIRED) + include_directories(${Qt5DBus_INCLUDE_DIRS}) + set(QTDBUS_LIBRARIES ${Qt5DBus_LIBRARIES}) + macro(qt_add_dbus_interface) + qt5_add_dbus_interface(${ARGN}) + endmacro() + endif () + + if (BUILD_TRANSLATIONS) + find_package(Qt5 COMPONENTS LinguistTools 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) + endif () + + macro(qt_wrap_cpp) + qt5_wrap_cpp(${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}) + set(QTCORE_LIBRARIES ${Qt5Core_LIBRARIES}) + include_directories(${Qt5Core_INCLUDE_DIRS}) +else () + find_package(Qt6 COMPONENTS Core REQUIRED) + set(QTKEYCHAIN_VERSION_INFIX 6) - 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}) + if (ANDROID) + find_package(Qt6 COMPONENTS AndroidExtras REQUIRED) + include_directories(${Qt6AndroidExtras_INCLUDE_DIRS}) + set(QTANDROIDEXTRAS_LIBRARIES ${Qt6AndroidExtras_LIBRARIES}) + endif () + + if (UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU) + find_package(Qt6 COMPONENTS DBus REQUIRED) + include_directories(${Qt6DBus_INCLUDE_DIRS}) + set(QTDBUS_LIBRARIES ${Qt6DBus_LIBRARIES}) + macro(qt_add_dbus_interface) + qt6_add_dbus_interface(${ARGN}) + endmacro() + endif () + + if (BUILD_TRANSLATIONS) + find_package(Qt6 COMPONENTS LinguistTools REQUIRED) + macro(qt_add_translation) + qt6_add_translation(${ARGN}) + endmacro(qt_add_translation) + macro(qt_create_translation) + qt6_create_translation(${ARGN}) + endmacro(qt_create_translation) + endif () + + macro(qt_wrap_cpp) + qt6_wrap_cpp(${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() + + set(QTCORE_LIBRARIES ${Qt6Core_LIBRARIES}) +endif () include_directories(${CMAKE_CURRENT_BINARY_DIR}) list(APPEND qtkeychain_LIBRARIES ${QTCORE_LIBRARIES}) set(qtkeychain_SOURCES - keychain.cpp -) + keychain.cpp + qkeychain_export.h + keychain.h + ) -ADD_DEFINITIONS( -Wall ) +if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + # CMake < 3.15 sneaks in /W# flags for us, so we need a replacement, + # or we'll get a warning (cf. CMP0092) + if (CMAKE_CXX_FLAGS MATCHES "/W[0-4]") + string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + else () + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") + endif () +else () + # MSVC's STL / Qt headers are not MSVC -Wall clean, so don't enable it there + add_definitions(-Wall) +endif () -if(WIN32) +if (WIN32) list(APPEND qtkeychain_SOURCES keychain_win.cpp) - list(APPEND qtkeychain_LIBRARIES crypt32) + if (NOT USE_CREDENTIAL_STORE) + list(APPEND qtkeychain_LIBRARIES crypt32) + list(APPEND qtkeychain_SOURCES plaintextstore.cpp) + endif () #FIXME: mingw bug; otherwise getting undefined refs to RtlSecureZeroMemory there - if(MINGW) - add_definitions( -O2 ) - endif() -endif() + if (MINGW) + add_definitions(-O2) + endif () +endif () -if(APPLE) - list(APPEND qtkeychain_SOURCES keychain_mac.cpp) +if (APPLE) + list(APPEND qtkeychain_SOURCES keychain_apple.mm) + list(APPEND qtkeychain_LIBRARIES "-framework Foundation" "-framework Security") +endif () - find_library(COREFOUNDATION_LIBRARY CoreFoundation) - list(APPEND qtkeychain_LIBRARIES ${COREFOUNDATION_LIBRARY}) +if (HAIKU) + list(APPEND qtkeychain_SOURCES keychain_haiku.cpp) - find_library(SECURITY_LIBRARY Security) - list(APPEND qtkeychain_LIBRARIES ${SECURITY_LIBRARY}) -endif() + find_library(BE_LIBRARY be REQUIRED) + list(APPEND qtkeychain_LIBRARIES ${BE_LIBRARY}) +endif () -if(UNIX AND NOT APPLE) - list(APPEND qtkeychain_SOURCES keychain_unix.cpp gnomekeyring.cpp) +if (UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU) + option(LIBSECRET_SUPPORT "Build with libsecret support" ON) + + if (LIBSECRET_SUPPORT) + pkg_check_modules(LIBSECRET libsecret-1) + + if (LIBSECRET_FOUND) + add_definitions(-DHAVE_LIBSECRET=1) + endif () + INCLUDE_DIRECTORIES(${LIBSECRET_INCLUDE_DIRS}) + LINK_DIRECTORIES(${LIBSECRET_LIBRARY_DIRS}) + list(APPEND qtkeychain_LIBRARIES_PRIVATE ${LIBSECRET_LIBRARIES}) + endif () + + add_definitions(-DKEYCHAIN_DBUS=1) + list(APPEND qtkeychain_SOURCES keychain_unix.cpp gnomekeyring.cpp libsecret.cpp plaintextstore.cpp) qt_add_dbus_interface(qtkeychain_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/org.kde.KWallet.xml kwallet_interface KWalletInterface) list(APPEND qtkeychain_LIBRARIES ${QTDBUS_LIBRARIES}) -endif() +endif () -QT_WRAP_CPP(qtkeychain_MOC_OUTFILES keychain.h keychain_p.h) +if (ANDROID) + list(APPEND qtkeychain_SOURCES keychain_android.cpp androidkeystore.cpp plaintextstore.cpp) + list(APPEND qtkeychain_LIBRARIES ${QTANDROIDEXTRAS_LIBRARIES}) +endif () + +QT_WRAP_CPP(qtkeychain_MOC_OUTFILES keychain.h keychain_p.h gnomekeyring_p.h) set(qtkeychain_TR_FILES - translations/qtkeychain_de.ts - translations/qtkeychain_ro.ts -) + translations/qtkeychain_de.ts + translations/qtkeychain_fr.ts + translations/qtkeychain_ro.ts + translations/qtkeychain_ru.ts + translations/qtkeychain_zh.ts + ) file(GLOB qtkeychain_TR_SOURCES *.cpp *.h *.ui) -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 (BUILD_TRANSLATIONS) + qt_create_translation(qtkeychain_MESSAGES ${qtkeychain_TR_SOURCES} ${qtkeychain_TR_FILES}) + qt_add_translation(qtkeychain_QM_FILES ${qtkeychain_TR_FILES}) + add_custom_target(messages DEPENDS ${qtkeychain_MESSAGES}) + add_custom_target(translations DEPENDS ${qtkeychain_QM_FILES}) -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() + if (QTKEYCHAIN_VERSION_INFIX EQUAL 5 AND QT_TRANSLATIONS_DIR AND NOT QTKEYCHAIN_TRANSLATIONS_DIR) + # Back compatibility with pre-0.11 versions + message(WARNING "QT_TRANSLATIONS_DIR is deprecated, use QTKEYCHAIN_TRANSLATIONS_DIR instead") + set(QTKEYCHAIN_TRANSLATIONS_DIR ${QT_TRANSLATIONS_DIR} + CACHE PATH "The location of the QtKeychain translations" FORCE) + else () + set(QTKEYCHAIN_TRANSLATIONS_DIR + ${CMAKE_INSTALL_DATADIR}/qt${QTKEYCHAIN_VERSION_INFIX}keychain/translations + CACHE PATH "The location of the QtKeychain translations") + endif () -install(FILES ${qtkeychain_QM_FILES} - DESTINATION ${QT_TRANSLATIONS_DIR}) + install(FILES ${qtkeychain_QM_FILES} + DESTINATION ${QTKEYCHAIN_TRANSLATIONS_DIR}) +endif (BUILD_TRANSLATIONS) set(QTKEYCHAIN_TARGET_NAME qt${QTKEYCHAIN_VERSION_INFIX}keychain) -if(NOT QTKEYCHAIN_STATIC) +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() +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() +endif () +if (WIN32) + set_target_properties(${QTKEYCHAIN_TARGET_NAME} PROPERTIES DEBUG_POSTFIX "d") +endif () + +target_link_libraries(${QTKEYCHAIN_TARGET_NAME} PUBLIC ${qtkeychain_LIBRARIES} PRIVATE ${qtkeychain_LIBRARIES_PRIVATE}) +if (NOT INTERFACE_INCLUDE_SUFFIX) + set(INTERFACE_INCLUDE_SUFFIX include) +endif () +target_include_directories(${QTKEYCHAIN_TARGET_NAME} PUBLIC + "$" + $ + ) + +generate_export_header(${QTKEYCHAIN_TARGET_NAME} + EXPORT_FILE_NAME qkeychain_export.h + EXPORT_MACRO_NAME QKEYCHAIN_EXPORT + ) set_target_properties(${QTKEYCHAIN_TARGET_NAME} PROPERTIES - VERSION ${QTKEYCHAIN_VERSION} - SOVERSION ${QTKEYCHAIN_SOVERSION} - MACOSX_RPATH 1 - INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}" -) + VERSION ${QTKEYCHAIN_VERSION} + SOVERSION ${QTKEYCHAIN_SOVERSION} + MACOSX_RPATH 1 + INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}" + INSTALL_RPATH_USE_LINK_PATH TRUE + ) -install(FILES keychain.h qkeychain_export.h +install(FILES keychain.h ${CMAKE_CURRENT_BINARY_DIR}/qkeychain_export.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/qt${QTKEYCHAIN_VERSION_INFIX}keychain/ -) + ) install(TARGETS ${QTKEYCHAIN_TARGET_NAME} - EXPORT Qt${QTKEYCHAIN_VERSION_INFIX}KeychainLibraryDepends - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} -) + 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}) +if (BUILD_TEST_APPLICATION) + add_executable(testclient testclient.cpp) + target_link_libraries(testclient ${QTKEYCHAIN_TARGET_NAME}) +endif () ### ### 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_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/QtKeychainConfig.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfig.cmake" + INSTALL_DESTINATION Qt${QTKEYCHAIN_VERSION_INFIX}Keychain) + +ecm_setup_version("${QTKEYCHAIN_VERSION}" VARIABLE_PREFIX SNORE + PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfigVersion.cmake" + SOVERSION ${QTKEYCHAIN_VERSION}) + +if (UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU) + set(PRI_EXTRA_DEPS "dbus") +endif () +ecm_generate_pri_file(BASE_NAME Qt${QTKEYCHAIN_VERSION_INFIX}Keychain + LIB_NAME ${QTKEYCHAIN_TARGET_NAME} + DEPS "core ${PRI_EXTRA_DEPS}" + INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR} + FILENAME_VAR pri_filename) + +install(FILES ${pri_filename} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) -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" -) + 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 + ${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 index cca2a5c..69f70ff 100644 --- a/ext/qtkeychain/COPYING +++ b/ext/qtkeychain/COPYING @@ -7,6 +7,9 @@ are met: 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to + endorse or promote products derived from this software without + specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES diff --git a/ext/qtkeychain/ChangeLog b/ext/qtkeychain/ChangeLog index f89dc19..932fe2b 100644 --- a/ext/qtkeychain/ChangeLog +++ b/ext/qtkeychain/ChangeLog @@ -1,6 +1,65 @@ ChangeLog ========= +version 0.12.0 (release 2020-12-16) + + * Add Qt 6 support, drop Qt 4 support + * Require C++11 + * Add Android support (Mathias Hasselmann) + +version 0.11.1 (release 2020-09-08) + + * Build system fixes + +version 0.11.0 (release 2020-09-08) + + * Important: Debug builds on Windows now get the "d" suffix + * Various build system fixes + * Add Haiku support (François Revol ) + * Translation: Russian (Alexander Gorishnyak ) + * Translation: Update French (David Geiger ) + +version 0.10.0 (release 2019-12-17) + + * Detect XFCE desktop correctly. (Sandro Knauß ) + * Windows Use CRED_PERSIST_ENTERPRISE (Olivier Goffart ) + * Windows: Improve CredWrite() error handling (Christian Kamm ) + * Fix build with Qt 5.12.x (Sergey Ilinykh ) + * Fix Qt 4 build (Robert-André Mauchin ) + * Translation: Mandarin (Taiwan) (Poren Chiang ) + * Translation: French (François Revol ) + +version 0.9.1 (release 2018-08-20) + * Windows Credential Store: Use CRED_PERSIST_ENTERPRISE (Olivier Goffart ) + * Secret: Don't match the schema name #114 (Christian Kamm ) + * Fix qmake build on Windows (Alexander Gorishnyak ) + +version 0.9.0 (release 2018-07-13) + * Fall back on libsecret if kwallet is not available (Christian Kamm ) + * Only require QtLinguist if building translations (Victor Kropp ) + * Fix building on Windows without credential store (Dmitry Ivanov ) + * Fix Qt 4 build (Sandro Knauß ) + * Make build of test application optional (Boris Pek ) + +version 0.8.0 (release 2017-04-19) + * Buildsystem improvements (Kristofer Tingdahl , Hannah von Reth , Giuseppe D'Angelo ) + * Enable C++11 support for Qt >= 5.7 (Dmitry Ivanov ) + * Doxygen documentation ( Elvis Angelaccio ) + * Libsecret support (Armin Novak ) + * iOS support (Mathias Hasselmann ) + +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) diff --git a/ext/qtkeychain/QtKeychainBuildTreeSettings.cmake.in b/ext/qtkeychain/QtKeychainBuildTreeSettings.cmake.in deleted file mode 100644 index 3f9e9f6..0000000 --- a/ext/qtkeychain/QtKeychainBuildTreeSettings.cmake.in +++ /dev/null @@ -1,4 +0,0 @@ -set(QTKEYCHAIN_INCLUDE_DIRS - "@PROJECT_SOURCE_DIR@" - "@PROJECT_BINARY_DIR@" -) diff --git a/ext/qtkeychain/QtKeychainConfig.cmake.in b/ext/qtkeychain/QtKeychainConfig.cmake.in index 74c1356..0c7779d 100644 --- a/ext/qtkeychain/QtKeychainConfig.cmake.in +++ b/ext/qtkeychain/QtKeychainConfig.cmake.in @@ -3,19 +3,17 @@ # 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@") +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/Qt@QTKEYCHAIN_VERSION_INFIX@KeychainLibraryDepends.cmake") + +include(CMakeFindDependencyMacro) + +find_dependency(Qt@QTKEYCHAIN_VERSION_INFIX@Core) + +if(UNIX AND NOT APPLE) + find_dependency(Qt@QTKEYCHAIN_VERSION_INFIX@DBus) 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) +get_target_property(QTKEYCHAIN_INCLUDE_DIRS "@QTKEYCHAIN_TARGET_NAME@" INTERFACE_INCLUDE_DIRECTORIES) diff --git a/ext/qtkeychain/QtKeychainConfigVersion.cmake.in b/ext/qtkeychain/QtKeychainConfigVersion.cmake.in deleted file mode 100644 index fba821a..0000000 --- a/ext/qtkeychain/QtKeychainConfigVersion.cmake.in +++ /dev/null @@ -1,11 +0,0 @@ -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 deleted file mode 100644 index e89adb7..0000000 --- a/ext/qtkeychain/ReadMe.markdown +++ /dev/null @@ -1,15 +0,0 @@ -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.md b/ext/qtkeychain/ReadMe.md new file mode 100644 index 0000000..4702bca --- /dev/null +++ b/ext/qtkeychain/ReadMe.md @@ -0,0 +1,35 @@ +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. + +Changes made in this fork (Chatterino/qtkeychain) +------- + +Updated the .pri file to point to the right mac/apple-specific file +Added `#undef singals` in libsecret.cpp to fix compilation. diff --git a/ext/qtkeychain/ReadMe.txt b/ext/qtkeychain/ReadMe.txt deleted file mode 100644 index e89adb7..0000000 --- a/ext/qtkeychain/ReadMe.txt +++ /dev/null @@ -1,15 +0,0 @@ -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/androidkeystore.cpp b/ext/qtkeychain/androidkeystore.cpp new file mode 100644 index 0000000..97260bb --- /dev/null +++ b/ext/qtkeychain/androidkeystore.cpp @@ -0,0 +1,302 @@ +#include "androidkeystore_p.h" + +#if QT_VERSION < QT_VERSION_CHECK(5, 7, 0) +#include "private/qjni_p.h" +#endif + +#include + +using namespace QKeychain; + +using namespace android::content; +using namespace android::security; + +using namespace java::io; +using namespace java::lang; +using namespace java::math; +using namespace java::util; +using namespace java::security; +using namespace java::security::spec; + +using namespace javax::crypto; +using namespace javax::security::auth::x500; +using namespace javax::security::cert; + +const BigInteger BigInteger::ONE = BigInteger::getStaticObjectField("java/math/BigInteger", "ONE", "Ljava/math/BigInteger;"); + +const int Calendar::YEAR = Calendar::getStaticField("java/util/Calendar", "YEAR"); + +const int Cipher::DECRYPT_MODE = Cipher::getStaticField("javax/crypto/Cipher", "DECRYPT_MODE"); +const int Cipher::ENCRYPT_MODE = Cipher::getStaticField("javax/crypto/Cipher", "ENCRYPT_MODE"); + +namespace { + +#if QT_VERSION < QT_VERSION_CHECK(5, 7, 0) + +struct JNIObject +{ + JNIObject(QSharedPointer d): d(d) {} + + static JNIObject fromLocalRef(jobject o) + { + return JNIObject(QSharedPointer::create(QJNIObjectPrivate::fromLocalRef(o))); + } + + jobject object() const { return d->object(); } + QSharedPointer d; +}; + +#else + +using JNIObject = QAndroidJniObject; + +#endif + +QByteArray fromArray(const jbyteArray array) +{ + QAndroidJniEnvironment env; + jbyte *const bytes = env->GetByteArrayElements(array, Q_NULLPTR); + const QByteArray result(reinterpret_cast(bytes), env->GetArrayLength(array)); + env->ReleaseByteArrayElements(array, bytes, JNI_ABORT); + return result; +} + +JNIObject toArray(const QByteArray &bytes) +{ + QAndroidJniEnvironment env; + const int length = bytes.length(); + JNIObject array = JNIObject::fromLocalRef(env->NewByteArray(length)); + env->SetByteArrayRegion(static_cast(array.object()), + 0, length, reinterpret_cast(bytes.constData())); + return array; +} + +} + +bool Object::handleExceptions() +{ + QAndroidJniEnvironment env; + + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + return false; + } + + return true; +} + + +KeyPairGenerator KeyPairGenerator::getInstance(const QString &algorithm, const QString &provider) +{ + return handleExceptions(callStaticObjectMethod("java/security/KeyPairGenerator", "getInstance", + "(Ljava/lang/String;Ljava/lang/String;)Ljava/security/KeyPairGenerator;", + fromString(algorithm).object(), fromString(provider).object())); +} + +KeyPair KeyPairGenerator::generateKeyPair() const +{ + return handleExceptions(callObjectMethod("generateKeyPair", "()Ljava/security/KeyPair;")); +} + +bool KeyPairGenerator::initialize(const AlgorithmParameterSpec &spec) const +{ + callMethod("initialize", "(Ljava/security/spec/AlgorithmParameterSpec;)V", spec.object()); + return handleExceptions(); +} + +bool KeyStore::containsAlias(const QString &alias) const +{ + return handleExceptions(callMethod("containsAlias", "(Ljava/lang/String;)Z", + fromString(alias).object())); +} + +bool KeyStore::deleteEntry(const QString &alias) const +{ + callMethod("deleteEntry", "(Ljava/lang/String;)Z", fromString(alias).object()); + return handleExceptions(); +} + +KeyStore KeyStore::getInstance(const QString &type) +{ + return handleExceptions(callStaticObjectMethod("java/security/KeyStore", "getInstance", + "(Ljava/lang/String;)Ljava/security/KeyStore;", + fromString(type).object())); +} + +KeyStore::Entry KeyStore::getEntry(const QString &alias, const KeyStore::ProtectionParameter ¶m) const +{ + return handleExceptions(callObjectMethod("getEntry", + "(Ljava/lang/String;Ljava/security/KeyStore$ProtectionParameter;)Ljava/security/KeyStore$Entry;", + fromString(alias).object(), param.object())); +} + +bool KeyStore::load(const KeyStore::LoadStoreParameter ¶m) const +{ + callMethod("load", "(Ljava/security/KeyStore$LoadStoreParameter;)V", param.object()); + return handleExceptions(); +} + + +Calendar Calendar::getInstance() +{ + return handleExceptions(callStaticObjectMethod("java/util/Calendar", "getInstance", + "()Ljava/util/Calendar;")); + +} + +bool Calendar::add(int field, int amount) const +{ + callMethod("add", "(II)V", field, amount); + return handleExceptions(); +} + +Date Calendar::getTime() const +{ + return handleExceptions(callObjectMethod("getTime", "()Ljava/util/Date;")); +} + +KeyPairGeneratorSpec::Builder::Builder(const Context &context) + : Object(QAndroidJniObject("android/security/KeyPairGeneratorSpec$Builder", + "(Landroid/content/Context;)V", + context.object())) +{ + handleExceptions(); +} + +KeyPairGeneratorSpec::Builder KeyPairGeneratorSpec::Builder::setAlias(const QString &alias) const +{ + return handleExceptions(callObjectMethod("setAlias", + "(Ljava/lang/String;)Landroid/security/KeyPairGeneratorSpec$Builder;", + fromString(alias).object())); +} + +KeyPairGeneratorSpec::Builder KeyPairGeneratorSpec::Builder::setSubject(const X500Principal &subject) const +{ + return handleExceptions(callObjectMethod("setSubject", + "(Ljavax/security/auth/x500/X500Principal;)Landroid/security/KeyPairGeneratorSpec$Builder;", + subject.object())); +} + +KeyPairGeneratorSpec::Builder KeyPairGeneratorSpec::Builder::setSerialNumber(const BigInteger &serial) const +{ + return handleExceptions(callObjectMethod("setSerialNumber", + "(Ljava/math/BigInteger;)Landroid/security/KeyPairGeneratorSpec$Builder;", + serial.object())); +} + +KeyPairGeneratorSpec::Builder KeyPairGeneratorSpec::Builder::setStartDate(const Date &date) const +{ + return handleExceptions(callObjectMethod("setStartDate", + "(Ljava/util/Date;)Landroid/security/KeyPairGeneratorSpec$Builder;", + date.object())); +} + +KeyPairGeneratorSpec::Builder KeyPairGeneratorSpec::Builder::setEndDate(const Date &date) const +{ + return handleExceptions(callObjectMethod("setEndDate", + "(Ljava/util/Date;)Landroid/security/KeyPairGeneratorSpec$Builder;", + date.object())); +} + +KeyPairGeneratorSpec KeyPairGeneratorSpec::Builder::build() const +{ + return handleExceptions(callObjectMethod("build", "()Landroid/security/KeyPairGeneratorSpec;")); +} + +X500Principal::X500Principal(const QString &name) + : Object(QAndroidJniObject("javax/security/auth/x500/X500Principal", + "(Ljava/lang/String;)V", + fromString(name).object())) +{ + handleExceptions(); +} + +Certificate KeyStore::PrivateKeyEntry::getCertificate() const +{ + return handleExceptions(callObjectMethod("getCertificate", "()Ljava/security/cert/Certificate;")); +} + +PrivateKey KeyStore::PrivateKeyEntry::getPrivateKey() const +{ + return handleExceptions(callObjectMethod("getPrivateKey", "()Ljava/security/PrivateKey;")); +} + +PublicKey Certificate::getPublicKey() const +{ + return handleExceptions(callObjectMethod("getPublicKey", "()Ljava/security/PublicKey;")); +} + +ByteArrayInputStream::ByteArrayInputStream(const QByteArray &bytes) + : InputStream(QAndroidJniObject("java/io/ByteArrayInputStream", "([B)V", toArray(bytes).object())) +{ +} + +ByteArrayOutputStream::ByteArrayOutputStream() + : OutputStream(QAndroidJniObject("java/io/ByteArrayOutputStream")) +{ + handleExceptions(); +} + +QByteArray ByteArrayOutputStream::toByteArray() const +{ + const QAndroidJniObject wrapper = callObjectMethod("toByteArray"); + + if (!handleExceptions()) + return QByteArray(); + + return fromArray(static_cast(wrapper.object())); +} + +int InputStream::read() const +{ + return handleExceptions(callMethod("read"), -1); +} + +bool OutputStream::write(const QByteArray &bytes) const +{ + callMethod("write", "([B)V", toArray(bytes).object()); + return handleExceptions(); +} + +bool OutputStream::close() const +{ + callMethod("close"); + return handleExceptions(); +} + +bool OutputStream::flush() const +{ + callMethod("flush"); + return handleExceptions(); +} + +Cipher Cipher::getInstance(const QString &transformation) +{ + return handleExceptions(callStaticObjectMethod("javax/crypto/Cipher", "getInstance", + "(Ljava/lang/String;)Ljavax/crypto/Cipher;", + fromString(transformation).object())); +} + +bool Cipher::init(int opMode, const Key &key) const +{ + callMethod("init", "(ILjava/security/Key;)V", opMode, key.object()); + return handleExceptions(); +} + + +CipherOutputStream::CipherOutputStream(const OutputStream &stream, const Cipher &cipher) + : FilterOutputStream(QAndroidJniObject("javax/crypto/CipherOutputStream", + "(Ljava/io/OutputStream;Ljavax/crypto/Cipher;)V", + stream.object(), cipher.object())) +{ + handleExceptions(); +} + +CipherInputStream::CipherInputStream(const InputStream &stream, const Cipher &cipher) + : FilterInputStream(QAndroidJniObject("javax/crypto/CipherInputStream", + "(Ljava/io/InputStream;Ljavax/crypto/Cipher;)V", + stream.object(), cipher.object())) +{ + handleExceptions(); +} diff --git a/ext/qtkeychain/androidkeystore_p.h b/ext/qtkeychain/androidkeystore_p.h new file mode 100644 index 0000000..36d0047 --- /dev/null +++ b/ext/qtkeychain/androidkeystore_p.h @@ -0,0 +1,371 @@ +/****************************************************************************** + * Copyright (C) 2016 Mathias Hasselmann * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution * + * details, check the accompanying file 'COPYING'. * + *****************************************************************************/ + +#ifndef QTKEYCHAIN_ANDROIDKEYSTORE_P_H +#define QTKEYCHAIN_ANDROIDKEYSTORE_P_H + +#include + +namespace QKeychain { + +namespace javax { +namespace security { + +namespace auth { namespace x500 { class X500Principal; } } +namespace cert { class Certificate; } + +} +} + +namespace java { +namespace lang { + +class Object : protected QAndroidJniObject +{ +public: + inline Object(jobject object) : QAndroidJniObject(object) {} + inline Object(const QAndroidJniObject &object) : QAndroidJniObject(object) {} + inline operator bool() const { return isValid(); } + + using QAndroidJniObject::object; + using QAndroidJniObject::toString; + +protected: + static bool handleExceptions(); + + template + static T handleExceptions(const T &result, const T &resultOnError = T()); +}; + +template +inline T Object::handleExceptions(const T &result, const T &resultOnError) +{ + if (!handleExceptions()) + return resultOnError; + + return result; +} + +} // namespace lang + +namespace io { + +class InputStream : public java::lang::Object +{ +public: + using Object::Object; + + int read() const; +}; + +class ByteArrayInputStream : public InputStream +{ +public: + using InputStream::InputStream; + + explicit ByteArrayInputStream(const QByteArray &bytes); +}; + +class FilterInputStream : public InputStream +{ +public: + using InputStream::InputStream; +}; + +class OutputStream : public java::lang::Object +{ +public: + using Object::Object; + + bool write(const QByteArray &bytes) const; + bool flush() const; + bool close() const; +}; + +class ByteArrayOutputStream : public OutputStream +{ +public: + using OutputStream::OutputStream; + + ByteArrayOutputStream(); + + QByteArray toByteArray() const; +}; + +class FilterOutputStream : public OutputStream +{ +public: + using OutputStream::OutputStream; +}; + +} // namespace io + +namespace math { + +class BigInteger : public java::lang::Object +{ +public: + using Object::Object; + + static const BigInteger ZERO; + static const BigInteger ONE; + static const BigInteger TEN; +}; + +} // namespace math + +namespace util { + +class Date : public java::lang::Object +{ +public: + using Object::Object; +}; + +class Calendar : public java::lang::Object +{ +public: + using Object::Object; + + static const int YEAR; + static const int MONTH; + static const int DAY; + static const int HOUR; + static const int MINUTE; + static const int SECOND; + static const int MILLISECOND; + + static Calendar getInstance(); + + bool add(int field, int amount) const; + Date getTime() const; +}; + +} // namespace util + +namespace security { +namespace spec { + +class AlgorithmParameterSpec : public java::lang::Object +{ +public: + using Object::Object; +}; + +} // namespace spec + +class Key : public java::lang::Object +{ +public: + using Object::Object; +}; + +class PrivateKey : public Key +{ +public: + using Key::Key; + + PrivateKey(const Key &init): Key(init) {} +}; + +class PublicKey : public Key +{ +public: + using Key::Key; + + PublicKey(const Key &init): Key(init) {} +}; + +class KeyPair : public java::lang::Object +{ +public: + using Object::Object; +}; + +class KeyPairGenerator : public java::lang::Object +{ +public: + using Object::Object; + + static KeyPairGenerator getInstance(const QString &algorithm, const QString &provider); + KeyPair generateKeyPair() const; + bool initialize(const spec::AlgorithmParameterSpec &spec) const; + +}; + +class KeyStore : public java::lang::Object +{ +public: + class Entry : public java::lang::Object + { + public: + using Object::Object; + }; + + class PrivateKeyEntry : public Entry + { + public: + using Entry::Entry; + + inline PrivateKeyEntry(const Entry &init): Entry(init) {} + + javax::security::cert::Certificate getCertificate() const; + java::security::PrivateKey getPrivateKey() const; + }; + + class LoadStoreParameter : public java::lang::Object + { + public: + using Object::Object; + }; + + class ProtectionParameter : public java::lang::Object + { + public: + using Object::Object; + }; + + using Object::Object; + + bool containsAlias(const QString &alias) const; + bool deleteEntry(const QString &alias) const; + static KeyStore getInstance(const QString &type); + Entry getEntry(const QString &alias, const ProtectionParameter ¶m = Q_NULLPTR) const; + bool load(const LoadStoreParameter ¶m = Q_NULLPTR) const; +}; + +namespace interfaces { + +class RSAPrivateKey : public PrivateKey +{ +public: + using PrivateKey::PrivateKey; + + RSAPrivateKey(const PrivateKey &init): PrivateKey(init) {} +}; + +class RSAPublicKey : public PublicKey +{ +public: + using PublicKey::PublicKey; + + RSAPublicKey(const PublicKey &init): PublicKey(init) {} +}; + +} // namespace interfaces + +} // namespace security +} // namespace java + +namespace android { +namespace content { + +class Context : public java::lang::Object +{ +public: + using Object::Object; +}; + +} // namespace content + +namespace security { + +class KeyPairGeneratorSpec : public java::security::spec::AlgorithmParameterSpec +{ +public: + class Builder : public java::lang::Object + { + public: + using Object::Object; + + explicit Builder(const android::content::Context &context); + + Builder setAlias(const QString &alias) const; + Builder setSubject(const javax::security::auth::x500::X500Principal &subject) const; + Builder setSerialNumber(const java::math::BigInteger &serial) const; + Builder setStartDate(const java::util::Date &date) const; + Builder setEndDate(const java::util::Date &date) const; + KeyPairGeneratorSpec build() const; + + }; + + using AlgorithmParameterSpec::AlgorithmParameterSpec; +}; + +} // namespace security +} // namespace android + +namespace javax { +namespace crypto { + +class Cipher : public java::lang::Object +{ +public: + static const int DECRYPT_MODE; + static const int ENCRYPT_MODE; + + using Object::Object; + + static Cipher getInstance(const QString &transformation); + bool init(int opMode, const java::security::Key &key) const; +}; + +class CipherInputStream : public java::io::FilterInputStream +{ +public: + using FilterInputStream::FilterInputStream; + + explicit CipherInputStream(const InputStream &stream, const Cipher &cipher); +}; + +class CipherOutputStream : public java::io::FilterOutputStream +{ +public: + using FilterOutputStream::FilterOutputStream; + + explicit CipherOutputStream(const OutputStream &stream, const Cipher &cipher); +}; + +} + +namespace security { +namespace auth { +namespace x500 { + +class X500Principal; + +class X500Principal : public java::lang::Object +{ +public: + using Object::Object; + + explicit X500Principal(const QString &name); +}; + +} // namespace x500 +} // namespace auth + +namespace cert { + +class Certificate : public java::lang::Object +{ +public: + using Object::Object; + + java::security::PublicKey getPublicKey() const; +}; + +} // namespace cert + +} // namespace security +} // namespace javax + +} // namespace QKeychain + +#endif // QTKEYCHAIN_ANDROIDKEYSTORE_P_H diff --git a/ext/qtkeychain/appveyor.yml b/ext/qtkeychain/appveyor.yml new file mode 100644 index 0000000..2e2804a --- /dev/null +++ b/ext/qtkeychain/appveyor.yml @@ -0,0 +1,31 @@ +version: '{build}' + +build_script: + - ps: | + $ErrorActionPreference="Stop" + Import-Module $env:APPVEYOR_BUILD_FOLDER\appveyorHelp.psm1 + + Init @("ninja") + mkdir -Force $env:APPVEYOR_BUILD_FOLDER\work\build\$env:APPVEYOR_PROJECT_NAME + cd $env:APPVEYOR_BUILD_FOLDER\work\build\$env:APPVEYOR_PROJECT_NAME + LogExec cmake -G"Ninja" $env:APPVEYOR_BUILD_FOLDER -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="$CMAKE_INSTALL_ROOT" + + CmakeImageInstall + +test: off + +cache: + - work\install -> appveyor.yml + - C:\ProgramData\chocolatey\bin -> appveyor.yml + - C:\ProgramData\chocolatey\lib -> appveyor.yml + +environment: + QT_VER: 5.7 + + matrix: + - COMPILER: msvc2015 + - COMPILER: msvc2015_64 + - COMPILER: mingw53_32 + + + diff --git a/ext/qtkeychain/appveyorHelp.psm1 b/ext/qtkeychain/appveyorHelp.psm1 new file mode 100644 index 0000000..b472d0b --- /dev/null +++ b/ext/qtkeychain/appveyorHelp.psm1 @@ -0,0 +1,362 @@ +Write-Host "Appveyor Helper scrips https://github.com/TheOneRing/appVeyorHelp" + +$ErrorActionPreference="Stop" + +$script:INSTALL_DIR="$env:APPVEYOR_BUILD_FOLDER\work\install" +$CMAKE_INSTALL_ROOT="`"$INSTALL_DIR`"" -replace "\\", "/" +$env:PATH="$env:PATH;$script:INSTALL_DIR" + +if(!$env:CI -eq "true") +{ + function Push-AppveyorArtifact() + { + Write-Host "Push-AppveyorArtifact $ARGS" + } + function Start-FileDownload([string] $url, [string] $out) + { + if(!$out) + { + $out = $url.SubString($url.LastIndexOf("/")) + } + wget $url -Outfile $out + } +} + +function LogExec() +{ + $OldErrorActionPreference=$ErrorActionPreference + $ErrorActionPreference="Continue" + $LastExitCode = 0 + Write-Host $Args[0], $Args[1..(($Args.Count)-1)] + & $Args[0] $Args[1..(($Args.Count)-1)] + if(!$LastExitCode -eq 0) + { + exit $LastExitCode + } + $ErrorActionPreference=$OldErrorActionPreference +} + +#Set environment variables for Visual Studio Command Prompt +#http://stackoverflow.com/questions/2124753/how-i-can-use-powershell-with-the-visual-studio-command-prompt +function BAT-CALL([string] $path, [string] $arg) +{ + Write-Host "Calling `"$path`" `"$arg`"" + cmd /c "$path" "$arg" `&`& set `|`| exit 1| + foreach { + if ($_ -match "=") { + $v = $_.split("=") + #Write-Host "ENV:\$($v[0])=$($v[1])" + set-item -force -path "ENV:\$($v[0])" -value "$($v[1])" + } + } + if($LastExitCode -eq 1) { + Write-Error "$path not found." + } +} + +function Get-QtDir() +{ + $ver = 5.5 + if($env:QT_VER) + { + $ver = $env:QT_VER + } + return "C:\Qt\$ver\$env:COMPILER\" +} + +function SETUP-QT() +{ + [string] $compiler=$env:COMPILER + $qtDir = Get-QtDir + $script:QT_BINARY_DIRS = @($qtDir) + + BAT-CALL "$qtDir\bin\qtenv2.bat" + if ($compiler.StartsWith("mingw49")) + { + #remove sh.exe from path + $env:PATH=$env:PATH -replace "C:\\Program Files \(x86\)\\Git\\bin", "" + $script:MAKE="mingw32-make" + $script:CMAKE_GENERATOR="MinGW Makefiles" + $script:STRIP=@("strip", "-s") + $script:QT_BINARY_DIRS += (Resolve-Path "$qtDir\..\..\Tools\mingw492_32\opt\") + } + elseif ($compiler.StartsWith("msvc")) + { + $arch = "x86" + if($compiler.EndsWith("64")) + { + $arch = "amd64" + } + $compilerDirs = @{ + "msvc2010" = "VS100COMNTOOLS"; + "msvc2012" = "VS110COMNTOOLS"; + "msvc2013" = "VS120COMNTOOLS"; + "msvc2015" = "VS140COMNTOOLS" + } + + $compilerVar = $compilerDirs[$compiler.Split("_")[0]] + $compilerDir = (get-item -path "env:\$($compilerVar)").Value + BAT-CALL "$compilerDir\..\..\VC\vcvarsall.bat" $arch + $script:MAKE="nmake" + $script:CMAKE_GENERATOR="NMake Makefiles" + if($arch -eq "x86") + { + $script:QT_BINARY_DIRS += ("C:\OpenSSL-Win32") + } + else + { + $script:QT_BINARY_DIRS += ("C:\OpenSSL-Win64") + } + } +} + +function Install-ChocolatelyModule([string] $module, [string[]] $myargs) +{ + Write-Host "Install chocolately package $module" + LogExec cinst $module @myargs -y +} + +function Install-CmakeGitModule([string] $url, [hashtable] $arguments) +{ + $module = $url.SubString($url.LastIndexOf("/")+1) + $module = $module.Substring(0,$module.Length - 4) + if(!$arguments.Contains("branch")) + { + $arguments["branch"] = "master" + } + if(!$arguments.Contains("buildType")) + { + $arguments["buildType"] = "Release" + } + mkdir -Force $env:APPVEYOR_BUILD_FOLDER\work\build\$module + pushd $env:APPVEYOR_BUILD_FOLDER\work\git + LogExec git clone -q --depth 1 --branch ([string]$arguments["branch"]) $url $module + popd + pushd $env:APPVEYOR_BUILD_FOLDER\work\build\$module + LogExec cmake -G $script:CMAKE_GENERATOR ("-DCMAKE_BUILD_TYPE=`"{0}`"" -f [string]$arguments["buildType"]) $env:APPVEYOR_BUILD_FOLDER\work\git\$module -DCMAKE_INSTALL_PREFIX="$CMAKE_INSTALL_ROOT" $arguments["options"] + LogExec $script:MAKE install + popd +} + +function Init([string[]] $chocoDeps, [System.Collections.Specialized.OrderedDictionary] $cmakeModules) +{ + $script:MAKE="" + $script:CMAKE_GENERATOR="" + $script:STRIP=$null + + mkdir -Force $env:APPVEYOR_BUILD_FOLDER\work\image | Out-Null + mkdir -Force $env:APPVEYOR_BUILD_FOLDER\work\build | Out-Null + + SETUP-QT + + if($chocoDeps -contains "ninja") { + $script:CMAKE_GENERATOR="Ninja" + $script:MAKE="ninja" + } + + if ( !(Test-Path "$env:APPVEYOR_BUILD_FOLDER\work\install" ) ) + { + mkdir -Force $env:APPVEYOR_BUILD_FOLDER\work\install | Out-Null + mkdir -Force $env:APPVEYOR_BUILD_FOLDER\work\git | Out-Null + + foreach($module in $chocoDeps) { + if($module -eq "nsis") + { + Install-ChocolatelyModule "nsis.portable" @("-pre") + continue + } + Install-ChocolatelyModule $module + } + + foreach($key in $cmakeModules.Keys) { + Install-CmakeGitModule $key $cmakeModules[$key] + } + + [string] $compiler=$env:COMPILER + if($compiler.StartsWith("msvc")) + { + Write-Host "Downloading vcredist.exe" + if ($compiler.StartsWith("msvc2015")) + { + if($compiler.EndsWith("64")) + { + Start-FileDownload https://download.microsoft.com/download/9/3/F/93FCF1E7-E6A4-478B-96E7-D4B285925B00/vc_redist.x64.exe $env:APPVEYOR_BUILD_FOLDER\work\install\vcredist.exe + } + else + { + Start-FileDownload https://download.microsoft.com/download/9/3/F/93FCF1E7-E6A4-478B-96E7-D4B285925B00/vc_redist.x86.exe $env:APPVEYOR_BUILD_FOLDER\work\install\vcredist.exe + } + } + else + { + if($compiler.EndsWith("64")) + { + Start-FileDownload http://download.microsoft.com/download/2/E/6/2E61CFA4-993B-4DD4-91DA-3737CD5CD6E3/vcredist_x64.exe $env:APPVEYOR_BUILD_FOLDER\work\install\vcredist.exe + } + else + { + Start-FileDownload http://download.microsoft.com/download/2/E/6/2E61CFA4-993B-4DD4-91DA-3737CD5CD6E3/vcredist_x86.exe $env:APPVEYOR_BUILD_FOLDER\work\install\vcredist.exe + } + } + } + } +} + +function relativePath([string] $root, [string] $path) +{ + pushd $root + $out = Resolve-Path -Relative $path + popd + return $out +} + +function StripFile([string] $name) +{ + if($script:STRIP) { + if( $name.EndsWith(".dll") -or $name.EndsWith(".exe")) + { + Write-Host "strip file $name" + LogExec @script:STRIP $name + } + } +} + +function Get-DeployImageName() +{ + $version = Get-Version + if($env:APPVEYOR_REPO_TAG -eq "true") { + return "$env:APPVEYOR_PROJECT_NAME`_$version`_Qt$env:QT_VER`_$env:COMPILER" + }else{ + return "$env:APPVEYOR_PROJECT_NAME`_$env:APPVEYOR_REPO_BRANCH`_$version`_Qt$env:QT_VER`_$env:COMPILER" + } +} + +function Get-Version() +{ + if($env:APPVEYOR_REPO_TAG -eq "true") { + return $env:APPVEYOR_REPO_TAG_NAME + }else{ + $commit = ([string]$env:APPVEYOR_REPO_COMMIT).SubString(0,6) + return $commit + } +} + +function CmakeImageInstall() +{ + $imageName = Get-DeployImageName + $destDir = "$env:APPVEYOR_BUILD_FOLDER\work\cmakeDeployImage\$imageName" + $env:DESTDIR = $destDir + LogExec $script:MAKE install + $env:DESTDIR = $null + if(!$LastExitCode -eq 0) + { + Write-Error "Build Failed" + } + $env:DESTDIR=$null + $prefix=$script:INSTALL_DIR + if( $prefix.substring(1,1) -eq ":") + { + $prefix=$prefix.substring(3) + } + Write-Host "move $destDir\$prefix to $destDir" + mv -Force "$destDir\$prefix\*" "$destDir" + $rootLeftOver = $prefix.substring(0, $prefix.indexOf("\")) + rm -Recurse "$destDir\$rootLeftOver" +} + +function CreateDeployImage([string[]] $whiteList, [string[]] $blackList) +{ + $imageName = Get-DeployImageName + $deployPath = "$env:APPVEYOR_BUILD_FOLDER\work\deployImage\$imageName" + + function copyWithWhitelist([string] $root) + { + $files = ls $root -Recurse + foreach($fileName in $files.FullName) + { + $relPath = (relativePath $root $fileName).SubString(2) + if($whiteList | Where {$relPath -match $_}) + { + if($blackList | Where {$relPath -match $_}) + { + continue + } + if(!(Test-Path $deployPath\$relPath)) + { + Write-Host "copy $fileName to $deployPath\$relPath" + mkdir -Force (Split-Path -Parent $deployPath\$relPath) | Out-Null + cp -Force $fileName $deployPath\$relPath + StripFile $deployPath\$relPath + } + } + } + } + Write-Host "CreateDeployImage $imageName" + mkdir $deployPath | Out-Null + + copyWithWhitelist "$env:APPVEYOR_BUILD_FOLDER\work\cmakeDeployImage\$imageName" + copyWithWhitelist "$env:APPVEYOR_BUILD_FOLDER\work\install\" + foreach($folder in $script:QT_BINARY_DIRS) + { + copyWithWhitelist $folder + } + Write-Host "Deploy path $deployPath" + return $deployPath +} + +function 7ZipDeployImage() +{ + $imageName = Get-DeployImageName + LogExec 7za a "$env:APPVEYOR_BUILD_FOLDER\work\deployImage\$imageName.7z" "$env:APPVEYOR_BUILD_FOLDER\work\deployImage\$imageName" + Push-AppveyorArtifact "$env:APPVEYOR_BUILD_FOLDER\work\deployImage\$imageName.7z" +} + +function NsisDeployImage([string] $scriptName) +{ + $imageName = Get-DeployImageName + $installerName = "$env:APPVEYOR_BUILD_FOLDER\work\deployImage\$imageName.exe" + $version = Get-Version + if(([string]$env:COMPILER).StartsWith("msvc")) + { + $redist = "$env:APPVEYOR_BUILD_FOLDER\work\install\vcredist.exe" + }else{ + $redist = "none" + } + if($env:COMPILER.EndsWith("64")) + { + $defaultinstdir = "`$PROGRAMFILES64" + }else{ + $defaultinstdir = "`$PROGRAMFILES" + } + LogExec makensis.exe /DgitDir=$env:APPVEYOR_BUILD_FOLDER /Dsetupname=$installerName /Dcaption=$imageName /Dversion=$version /Dcompiler=$env:COMPILER /Dvcredist=$redist /Ddefaultinstdir=$defaultinstdir /Dsrcdir=$env:APPVEYOR_BUILD_FOLDER\work\deployImage\$imageName $scriptName + Push-AppveyorArtifact $installerName +} + +# based on http://thesurlyadmin.com/2013/01/07/remove-empty-directories-recursively/ +function DeleteEmptyFodlers([string] $root) +{ + $Folders = @() + foreach($Folder in (Get-ChildItem -Path $root -Recurse -Directory)) + { + $Folders += New-Object PSObject -Property @{ + Object = $Folder + Depth = ($Folder.FullName.Split("\")).Count + } + } + $Folders = $Folders | Sort Depth -Descending + + foreach($Folder in $Folders) + { + If ($Folder.Object.GetFileSystemInfos().Count -eq 0) + { + Write-Host "Delete empty dir:" $Folder.Object.FullName + Remove-Item -Path $Folder.Object.FullName -Force + } + } + +} + +Write-Host "CMAKE_INSTALL_ROOT: $CMAKE_INSTALL_ROOT" +Write-Host "Image-Name: ", (Get-DeployImageName) + +Export-ModuleMember -Function @("Init","CmakeImageInstall", "CreateDeployImage", "LogExec", "7ZipDeployImage", "NsisDeployImage", "DeleteEmptyFodlers") -Variable @("CMAKE_INSTALL_ROOT") diff --git a/ext/qtkeychain/cmake/Modules/ECMGeneratePriFile.cmake b/ext/qtkeychain/cmake/Modules/ECMGeneratePriFile.cmake new file mode 100644 index 0000000..f63a0ce --- /dev/null +++ b/ext/qtkeychain/cmake/Modules/ECMGeneratePriFile.cmake @@ -0,0 +1,199 @@ +#.rst: +# ECMGeneratePriFile +# ------------------ +# +# Generate a ``.pri`` file for the benefit of qmake-based projects. +# +# As well as the function below, this module creates the cache variable +# ``ECM_MKSPECS_INSTALL_DIR`` and sets the default value to ``mkspecs/modules``. +# This assumes Qt and the current project are both installed to the same +# non-system prefix. Packagers who use ``-DCMAKE_INSTALL_PREFIX=/usr`` will +# certainly want to set ``ECM_MKSPECS_INSTALL_DIR`` to something like +# ``share/qt5/mkspecs/modules``. +# +# The main thing is that this should be the ``modules`` subdirectory of either +# the default qmake ``mkspecs`` directory or of a directory that will be in the +# ``$QMAKEPATH`` environment variable when qmake is run. +# +# :: +# +# ecm_generate_pri_file(BASE_NAME +# LIB_NAME +# [DEPS " [ [...]]"] +# [FILENAME_VAR ] +# [INCLUDE_INSTALL_DIR ] +# [LIB_INSTALL_DIR ]) +# +# If your CMake project produces a Qt-based library, you may expect there to be +# applications that wish to use it that use a qmake-based build system, rather +# than a CMake-based one. Creating a ``.pri`` file will make use of your +# library convenient for them, in much the same way that CMake config files make +# things convenient for CMake-based applications. +# +# ecm_generate_pri_file() generates just such a file. It requires the +# ``PROJECT_VERSION_STRING`` variable to be set. This is typically set by +# :module:`ECMSetupVersion`, although the project() command in CMake 3.0.0 and +# later can also set this. +# +# BASE_NAME specifies the name qmake project (.pro) files should use to refer to +# the library (eg: KArchive). LIB_NAME is the name of the actual library to +# link to (ie: the first argument to add_library()). DEPS is a space-separated +# list of the base names of other libraries (for Qt libraries, use the same +# names you use with the ``QT`` variable in a qmake project file, such as "core" +# for QtCore). FILENAME_VAR specifies the name of a variable to store the path +# to the generated file in. +# +# INCLUDE_INSTALL_DIR is the path (relative to ``CMAKE_INSTALL_PREFIX``) that +# include files will be installed to. It defaults to +# ``${INCLUDE_INSTALL_DIR}/`` if the ``INCLUDE_INSTALL_DIR`` variable +# is set. If that variable is not set, the ``CMAKE_INSTALL_INCLUDEDIR`` variable +# is used instead, and if neither are set ``include`` is used. LIB_INSTALL_DIR +# operates similarly for the installation location for libraries; it defaults to +# ``${LIB_INSTALL_DIR}``, ``${CMAKE_INSTALL_LIBDIR}`` or ``lib``, in that order. +# +# Example usage: +# +# .. code-block:: cmake +# +# ecm_generate_pri_file( +# BASE_NAME KArchive +# LIB_NAME KF5KArchive +# DEPS "core" +# FILENAME_VAR pri_filename +# ) +# install(FILES ${pri_filename} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) +# +# A qmake-based project that wished to use this would then do:: +# +# QT += KArchive +# +# in their ``.pro`` file. +# +# Since pre-1.0.0. + +#============================================================================= +# SPDX-FileCopyrightText: 2014 David Faure +# +# SPDX-License-Identifier: BSD-3-Clause + +# Replicate the logic from KDEInstallDirs.cmake as we can't depend on it +# Ask qmake if we're using the same prefix as Qt +set(_askqmake OFF) +if(NOT DEFINED KDE_INSTALL_USE_QT_SYS_PATHS) + include(ECMQueryQmake) + query_qmake(qt_install_prefix_dir QT_INSTALL_PREFIX TRY) + if(qt_install_prefix_dir STREQUAL "${CMAKE_INSTALL_PREFIX}") + set(_askqmake ON) + endif() +endif() + +if(KDE_INSTALL_USE_QT_SYS_PATHS OR _askqmake) + include(ECMQueryQmake) + query_qmake(qt_host_data_dir QT_HOST_DATA) + set(ECM_MKSPECS_INSTALL_DIR ${qt_host_data_dir}/mkspecs/modules CACHE PATH "The directory where mkspecs will be installed to.") +else() + set(ECM_MKSPECS_INSTALL_DIR mkspecs/modules CACHE PATH "The directory where mkspecs will be installed to.") +endif() + +function(ECM_GENERATE_PRI_FILE) + set(options ) + set(oneValueArgs BASE_NAME LIB_NAME DEPS FILENAME_VAR INCLUDE_INSTALL_DIR LIB_INSTALL_DIR) + set(multiValueArgs ) + + cmake_parse_arguments(EGPF "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(EGPF_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unknown keywords given to ECM_GENERATE_PRI_FILE(): \"${EGPF_UNPARSED_ARGUMENTS}\"") + endif() + + if(NOT EGPF_BASE_NAME) + message(FATAL_ERROR "Required argument BASE_NAME missing in ECM_GENERATE_PRI_FILE() call") + endif() + if(NOT EGPF_LIB_NAME) + message(FATAL_ERROR "Required argument LIB_NAME missing in ECM_GENERATE_PRI_FILE() call") + endif() + if(NOT PROJECT_VERSION_STRING) + message(FATAL_ERROR "Required variable PROJECT_VERSION_STRING not set before ECM_GENERATE_PRI_FILE() call. Did you call ecm_setup_version?") + endif() + if(NOT EGPF_INCLUDE_INSTALL_DIR) + if(INCLUDE_INSTALL_DIR) + set(EGPF_INCLUDE_INSTALL_DIR "${INCLUDE_INSTALL_DIR}/${EGPF_BASE_NAME}") + elseif(CMAKE_INSTALL_INCLUDEDIR) + set(EGPF_INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_INCLUDEDIR}/${EGPF_BASE_NAME}") + else() + set(EGPF_INCLUDE_INSTALL_DIR "include/${EGPF_BASE_NAME}") + endif() + endif() + if(NOT EGPF_LIB_INSTALL_DIR) + if(LIB_INSTALL_DIR) + set(EGPF_LIB_INSTALL_DIR "${LIB_INSTALL_DIR}") + elseif(CMAKE_INSTALL_LIBDIR) + set(EGPF_LIB_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}") + else() + set(EGPF_LIB_INSTALL_DIR "lib") + endif() + endif() + + string(REGEX REPLACE "^([0-9]+)\\.[0-9]+\\.[0-9]+.*" "\\1" PROJECT_VERSION_MAJOR "${PROJECT_VERSION_STRING}") + string(REGEX REPLACE "^[0-9]+\\.([0-9]+)\\.[0-9]+.*" "\\1" PROJECT_VERSION_MINOR "${PROJECT_VERSION_STRING}") + string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" PROJECT_VERSION_PATCH "${PROJECT_VERSION_STRING}") + + # Prepare the right number of "../.." to go from ECM_MKSPECS_INSTALL_DIR to the install prefix + # This allows to make the generated pri files relocatable (no absolute paths) + if (IS_ABSOLUTE ${ECM_MKSPECS_INSTALL_DIR}) + set(BASEPATH ${CMAKE_INSTALL_PREFIX}) + else() + string(REGEX REPLACE "[^/]+" ".." PRI_ROOT_RELATIVE_TO_MKSPECS ${ECM_MKSPECS_INSTALL_DIR}) + set(BASEPATH "$$PWD/${PRI_ROOT_RELATIVE_TO_MKSPECS}") + endif() + + set(PRI_TARGET_BASENAME ${EGPF_BASE_NAME}) + set(PRI_TARGET_LIBNAME ${EGPF_LIB_NAME}) + set(PRI_TARGET_QTDEPS ${EGPF_DEPS}) + if(IS_ABSOLUTE "${EGPF_INCLUDE_INSTALL_DIR}") + set(PRI_TARGET_INCLUDES "${EGPF_INCLUDE_INSTALL_DIR}") + else() + set(PRI_TARGET_INCLUDES "${BASEPATH}/${EGPF_INCLUDE_INSTALL_DIR}") + endif() + if(IS_ABSOLUTE "${EGPF_LIB_INSTALL_DIR}") + set(PRI_TARGET_LIBS "${EGPF_LIB_INSTALL_DIR}") + else() + set(PRI_TARGET_LIBS "${BASEPATH}/${EGPF_LIB_INSTALL_DIR}") + endif() + set(PRI_TARGET_DEFINES "") + + set(PRI_FILENAME ${CMAKE_CURRENT_BINARY_DIR}/qt_${PRI_TARGET_BASENAME}.pri) + if (EGPF_FILENAME_VAR) + set(${EGPF_FILENAME_VAR} ${PRI_FILENAME} PARENT_SCOPE) + endif() + + set(PRI_TARGET_MODULE_CONFIG "") + # backward compat: it was not obvious LIB_NAME needs to be a target name, + # and some projects where the target name was not the actual library output name + # passed the output name for LIB_NAME, so .name & .module prperties are correctly set. + # TODO: improve API dox, allow control over module name if target name != output name + if(TARGET ${EGPF_LIB_NAME}) + get_target_property(target_type ${EGPF_LIB_NAME} TYPE) + if (target_type STREQUAL "STATIC_LIBRARY") + set(PRI_TARGET_MODULE_CONFIG "staticlib") + endif() + endif() + + file(GENERATE + OUTPUT ${PRI_FILENAME} + CONTENT + "QT.${PRI_TARGET_BASENAME}.VERSION = ${PROJECT_VERSION_STRING} +QT.${PRI_TARGET_BASENAME}.MAJOR_VERSION = ${PROJECT_VERSION_MAJOR} +QT.${PRI_TARGET_BASENAME}.MINOR_VERSION = ${PROJECT_VERSION_MINOR} +QT.${PRI_TARGET_BASENAME}.PATCH_VERSION = ${PROJECT_VERSION_PATCH} +QT.${PRI_TARGET_BASENAME}.name = ${PRI_TARGET_LIBNAME} +QT.${PRI_TARGET_BASENAME}.module = ${PRI_TARGET_LIBNAME} +QT.${PRI_TARGET_BASENAME}.defines = ${PRI_TARGET_DEFINES} +QT.${PRI_TARGET_BASENAME}.includes = ${PRI_TARGET_INCLUDES} +QT.${PRI_TARGET_BASENAME}.private_includes = +QT.${PRI_TARGET_BASENAME}.libs = ${PRI_TARGET_LIBS} +QT.${PRI_TARGET_BASENAME}.depends = ${PRI_TARGET_QTDEPS} +QT.${PRI_TARGET_BASENAME}.module_config = ${PRI_TARGET_MODULE_CONFIG} +" + ) +endfunction() diff --git a/ext/qtkeychain/cmake/Modules/ECMPackageConfigHelpers.cmake b/ext/qtkeychain/cmake/Modules/ECMPackageConfigHelpers.cmake new file mode 100644 index 0000000..8d48772 --- /dev/null +++ b/ext/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 +# `_ +# module in CMake; see that module's documentation for +# more information. +# +# :: +# +# ecm_configure_package_config_file( +# INSTALL_DESTINATION +# [PATH_VARS [ [...]] +# [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 +# `_ +# 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( [ [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 +# SPDX-FileCopyrightText: 2013 Stephen Kelly +# +# 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/ext/qtkeychain/cmake/Modules/ECMQueryQmake.cmake b/ext/qtkeychain/cmake/Modules/ECMQueryQmake.cmake new file mode 100644 index 0000000..74a6df8 --- /dev/null +++ b/ext/qtkeychain/cmake/Modules/ECMQueryQmake.cmake @@ -0,0 +1,46 @@ +find_package(Qt5Core QUIET) + +if (Qt5Core_FOUND) + set(_qmake_executable_default "qmake-qt5") +endif () +if (TARGET Qt5::qmake) + get_target_property(_qmake_executable_default Qt5::qmake LOCATION) +endif() +set(QMAKE_EXECUTABLE ${_qmake_executable_default} + CACHE FILEPATH "Location of the Qt5 qmake executable") + +# Helper method +# This is not public API (yet)! +# Usage: query_qmake( [TRY]) +# Passing TRY will result in the method not failing fatal if no qmake executable +# has been found, but instead simply returning an empty string +function(query_qmake result_variable qt_variable) + set(options TRY) + set(oneValueArgs ) + set(multiValueArgs ) + + cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT QMAKE_EXECUTABLE) + if(ARGS_TRY) + set(${result_variable} "" PARENT_SCOPE) + message(STATUS "No qmake Qt5 binary found. Can't check ${qt_variable}") + return() + else() + message(FATAL_ERROR "No qmake Qt5 binary found. Can't check ${qt_variable} as required") + endif() + endif() + execute_process( + COMMAND ${QMAKE_EXECUTABLE} -query "${qt_variable}" + RESULT_VARIABLE return_code + OUTPUT_VARIABLE output + ) + if(return_code EQUAL 0) + string(STRIP "${output}" output) + file(TO_CMAKE_PATH "${output}" output_path) + set(${result_variable} "${output_path}" PARENT_SCOPE) + else() + message(WARNING "Failed call: ${QMAKE_EXECUTABLE} -query \"${qt_variable}\"") + message(FATAL_ERROR "QMake call failed: ${return_code}") + endif() +endfunction() diff --git a/ext/qtkeychain/cmake/Modules/ECMSetupVersion.cmake b/ext/qtkeychain/cmake/Modules/ECMSetupVersion.cmake new file mode 100644 index 0000000..65c1688 --- /dev/null +++ b/ext/qtkeychain/cmake/Modules/ECMSetupVersion.cmake @@ -0,0 +1,202 @@ +#.rst: +# ECMSetupVersion +# --------------- +# +# Handle library version information. +# +# :: +# +# ecm_setup_version( +# VARIABLE_PREFIX +# [SOVERSION ] +# [VERSION_HEADER ] +# [PACKAGE_VERSION_FILE [COMPATIBILITY ]] ) +# +# 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 ```` argument is of the form ``..`` +# (or ``...``), The following CMake variables are +# set:: +# +# _VERSION_MAJOR - +# _VERSION_MINOR - +# _VERSION_PATCH - +# _VERSION - +# _VERSION_STRING - (for compatibility: use _VERSION instead) +# _SOVERSION - , or if SOVERSION was not given +# +# If CMake policy CMP0048 is not NEW, the following CMake variables will also +# be set:: +# +# PROJECT_VERSION_MAJOR - +# PROJECT_VERSION_MINOR - +# PROJECT_VERSION_PATCH - +# PROJECT_VERSION - +# PROJECT_VERSION_STRING - (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:: +# +# _VERSION_MAJOR - as an integer +# _VERSION_MINOR - as an integer +# _VERSION_PATCH - as an integer +# _VERSION_STRING - as a C string +# _VERSION - the version as an integer +# +# ``_VERSION`` has ```` in the bottom 8 bits, ```` in the +# next 8 bits and ```` in the remaining bits. Note that ```` and +# ```` 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 ] +# [SOVERSION ] +# [VERSION_HEADER ] +# [PACKAGE_VERSION_FILE ] ) +# +# 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 +# SPDX-FileCopyrightText: 2012 Alexander Neundorf +# +# 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/ext/qtkeychain/gnomekeyring.cpp b/ext/qtkeychain/gnomekeyring.cpp index 9cef00f..dd35670 100644 --- a/ext/qtkeychain/gnomekeyring.cpp +++ b/ext/qtkeychain/gnomekeyring.cpp @@ -14,25 +14,39 @@ bool GnomeKeyring::isAvailable() 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 ) +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, static_cast(0) ); + keyring, display_name, password, callback, + data, destroy_data, + "user", user, + "server", server, + "type", type, + static_cast(0) ); } -GnomeKeyring::gpointer GnomeKeyring::find_network_password( const gchar* user, const gchar* server, - OperationGetStringCallback callback, gpointer data, GDestroyNotify destroy_data ) +GnomeKeyring::gpointer GnomeKeyring::find_network_password( + const gchar* user, const gchar* server, const gchar* type, + OperationGetStringCallback callback, gpointer data, GDestroyNotify destroy_data ) { if ( !isAvailable() ) return 0; + return instance().find_password( instance().NETWORK_PASSWORD, callback, data, destroy_data, - "user", user, "server", server, static_cast(0) ); + "user", user, "server", server, "type", type, + static_cast(0) ); } GnomeKeyring::gpointer GnomeKeyring::delete_network_password( const gchar* user, @@ -49,12 +63,13 @@ GnomeKeyring::gpointer GnomeKeyring::delete_network_password( const gchar* user, } GnomeKeyring::GnomeKeyring() - : QLibrary("gnome-keyring", 0) + : 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( 0 ) }} }; diff --git a/ext/qtkeychain/gnomekeyring_p.h b/ext/qtkeychain/gnomekeyring_p.h index 6d150ba..87c062c 100644 --- a/ext/qtkeychain/gnomekeyring_p.h +++ b/ext/qtkeychain/gnomekeyring_p.h @@ -4,6 +4,8 @@ #include class GnomeKeyring : private QLibrary { + Q_OBJECT + public: enum Result { RESULT_OK, @@ -43,7 +45,8 @@ public: } attributes[32]; } PasswordSchema; - typedef void ( *OperationGetStringCallback )( Result result, const char* string, gpointer data ); + typedef void ( *OperationGetStringCallback )( Result result, bool binary, + const char* string, gpointer data ); typedef void ( *OperationDoneCallback )( Result result, gpointer data ); typedef void ( *GDestroyNotify )( gpointer data ); @@ -52,11 +55,14 @@ public: static bool isAvailable(); static gpointer store_network_password( const gchar* keyring, const gchar* display_name, - const gchar* user, const gchar* server, const gchar* password, + 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, - OperationGetStringCallback callback, gpointer data, GDestroyNotify destroy_data ); + 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 ); diff --git a/ext/qtkeychain/keychain.cpp b/ext/qtkeychain/keychain.cpp index 7687f9d..90ee4eb 100644 --- a/ext/qtkeychain/keychain.cpp +++ b/ext/qtkeychain/keychain.cpp @@ -11,9 +11,9 @@ using namespace QKeychain; -Job::Job( const QString& service, QObject *parent ) +Job::Job( JobPrivate *q, QObject *parent ) : QObject( parent ) - , d ( new JobPrivate( service ) ) { + , d ( q ) { } Job::~Job() { @@ -52,6 +52,10 @@ void Job::setInsecureFallback( bool insecureFallback ) { d->insecureFallback = insecureFallback; } +void Job::doStart() { + JobExecutor::instance()->enqueue( this ); +} + void Job::emitFinished() { emit finished( this ); if ( d->autoDelete ) @@ -64,6 +68,10 @@ void Job::emitFinishedWithError( Error error, const QString& errorString ) { emitFinished(); } +void Job::scheduledStart() { + d->scheduledStart(); +} + Error Job::error() const { return d->error; } @@ -81,12 +89,11 @@ void Job::setErrorString( const QString& errorString ) { } ReadPasswordJob::ReadPasswordJob( const QString& service, QObject* parent ) - : Job( service, parent ) - , d( new ReadPasswordJobPrivate( this ) ) -{} + : Job( new ReadPasswordJobPrivate( service, this ), parent ) { + +} ReadPasswordJob::~ReadPasswordJob() { - delete d; } QString ReadPasswordJob::textData() const { @@ -97,126 +104,80 @@ QByteArray ReadPasswordJob::binaryData() const { return d->data; } -QString ReadPasswordJob::key() const { +QString Job::key() const { return d->key; } -void ReadPasswordJob::setKey( const QString& key ) { - d->key = key; -} - -void ReadPasswordJob::doStart() { - JobExecutor::instance()->enqueue( this ); +void Job::setKey( const QString& key_ ) { + d->key = key_; } WritePasswordJob::WritePasswordJob( const QString& service, QObject* parent ) - : Job( service, parent ) - , d( new WritePasswordJobPrivate( this ) ) { + : Job( new WritePasswordJobPrivate( service, this ), parent ) { } 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; + d->data = data; + d->mode = JobPrivate::Binary; } void WritePasswordJob::setTextData( const QString& data ) { - d->textData = data; - d->mode = WritePasswordJobPrivate::Text; -} - -void WritePasswordJob::doStart() { - JobExecutor::instance()->enqueue( this ); + d->data = data.toUtf8(); + d->mode = JobPrivate::Text; } DeletePasswordJob::DeletePasswordJob( const QString& service, QObject* parent ) - : Job( service, parent ) - , d( new DeletePasswordJobPrivate( this ) ) { + : Job( new DeletePasswordJobPrivate( service, this ), parent ) { } 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(); -} +DeletePasswordJobPrivate::DeletePasswordJobPrivate(const QString &service_, DeletePasswordJob *qq) : + JobPrivate(service_, qq) { -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 ) -{ + , m_jobRunning( false ) { } void JobExecutor::enqueue( Job* job ) { - m_queue.append( job ); + m_queue.enqueue( job ); startNextIfNoneRunning(); } void JobExecutor::startNextIfNoneRunning() { - if ( m_queue.isEmpty() || m_runningJob ) + if ( m_queue.isEmpty() || m_jobRunning ) return; QPointer next; while ( !next && !m_queue.isEmpty() ) { - next = m_queue.first(); - m_queue.pop_front(); + 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_runningJob = next; - if ( ReadPasswordJob* rpj = qobject_cast( m_runningJob ) ) - rpj->d->scheduledStart(); - else if ( WritePasswordJob* wpj = qobject_cast( m_runningJob) ) - wpj->d->scheduledStart(); + m_jobRunning = true; + next->scheduledStart(); } } void JobExecutor::jobDestroyed( QObject* object ) { + Job* job = static_cast(object); Q_UNUSED( object ) // for release mode - Q_ASSERT( object == m_runningJob ); - m_runningJob->disconnect( this ); - m_runningJob = 0; + job->disconnect( this ); + m_jobRunning = false; startNextIfNoneRunning(); } void JobExecutor::jobFinished( Job* job ) { Q_UNUSED( job ) // for release mode - Q_ASSERT( job == m_runningJob ); - m_runningJob->disconnect( this ); - m_runningJob = 0; + job->disconnect( this ); + m_jobRunning = false; startNextIfNoneRunning(); } @@ -227,3 +188,48 @@ JobExecutor* JobExecutor::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/ext/qtkeychain/keychain.h b/ext/qtkeychain/keychain.h index 6ed5e95..45337c6 100644 --- a/ext/qtkeychain/keychain.h +++ b/ext/qtkeychain/keychain.h @@ -9,7 +9,11 @@ #ifndef KEYCHAIN_H #define KEYCHAIN_H +#if !defined(QTKEYCHAIN_NO_EXPORT) #include "qkeychain_export.h" +#else +#define QKEYCHAIN_EXPORT +#endif #include #include @@ -37,107 +41,229 @@ enum Error { class JobExecutor; class JobPrivate; +/** + * @brief Abstract base class for all QKeychain jobs. + */ class QKEYCHAIN_EXPORT Job : public QObject { Q_OBJECT public: - explicit Job( const QString& service, QObject* parent=0 ); - ~Job(); + ~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 ); -Q_SIGNALS: - void finished( QKeychain::Job* ); + /** + * @return The string used as key by this job. + * @see setKey() + */ + QString key() const; -protected: - Q_INVOKABLE virtual void doStart() = 0; + /** + * 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 setError( Error error ); - void setErrorString( const QString& errorString ); 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 stored in the same service string. + */ 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 ); + /** + * 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; - QString textData() const; -protected: - void doStart(); + /** + * @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; - friend class QKeychain::JobExecutor; - ReadPasswordJobPrivate* const d; }; 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 under the same service string. + */ 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 ); + /** + * 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 ); -protected: - void doStart(); - private: - friend class QKeychain::JobExecutor; + friend class QKeychain::WritePasswordJobPrivate; - friend class DeletePasswordJob; - WritePasswordJobPrivate* const d; }; 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 stored in the same service string. + */ 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(); + /** + * 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; - DeletePasswordJobPrivate* const d; }; } // namespace QtKeychain diff --git a/ext/qtkeychain/keychain_android.cpp b/ext/qtkeychain/keychain_android.cpp new file mode 100644 index 0000000..c176913 --- /dev/null +++ b/ext/qtkeychain/keychain_android.cpp @@ -0,0 +1,182 @@ +/****************************************************************************** + * Copyright (C) 2016 Mathias Hasselmann * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution * + * details, check the accompanying file 'COPYING'. * + *****************************************************************************/ + +#include "keychain_p.h" + +#include "androidkeystore_p.h" +#include "plaintextstore_p.h" + +#include + +using namespace QKeychain; + +using android::content::Context; +using android::security::KeyPairGeneratorSpec; + +using java::io::ByteArrayInputStream; +using java::io::ByteArrayOutputStream; +using java::security::interfaces::RSAPrivateKey; +using java::security::interfaces::RSAPublicKey; +using java::security::KeyPair; +using java::security::KeyPairGenerator; +using java::security::KeyStore; +using java::util::Calendar; + +using javax::crypto::Cipher; +using javax::crypto::CipherInputStream; +using javax::crypto::CipherOutputStream; +using javax::security::auth::x500::X500Principal; + +namespace { + +inline QString makeAlias(const QString &service, const QString &key) +{ + return service + QLatin1Char('/') + key; +} + +} + +void ReadPasswordJobPrivate::scheduledStart() +{ + PlainTextStore plainTextStore(q->service(), q->settings()); + + if (!plainTextStore.contains(q->key())) { + q->emitFinishedWithError(Error::EntryNotFound, tr("Entry not found")); + return; + } + + const QByteArray &encryptedData = plainTextStore.readData(q->key()); + const KeyStore keyStore = KeyStore::getInstance(QStringLiteral("AndroidKeyStore")); + + if (!keyStore || !keyStore.load()) { + q->emitFinishedWithError(Error::AccessDenied, tr("Could not open keystore")); + return; + } + + const auto &alias = makeAlias(q->service(), q->key()); + const KeyStore::PrivateKeyEntry entry = keyStore.getEntry(alias); + + if (!entry) { + q->emitFinishedWithError(Error::AccessDenied, tr("Could not retrieve private key from keystore")); + return; + } + + const Cipher cipher = Cipher::getInstance(QStringLiteral("RSA/ECB/PKCS1Padding")); + + if (!cipher || !cipher.init(Cipher::DECRYPT_MODE, entry.getPrivateKey())) { + q->emitFinishedWithError(Error::OtherError, tr("Could not create decryption cipher")); + return; + } + + QByteArray plainData; + const CipherInputStream inputStream(ByteArrayInputStream(encryptedData), cipher); + + for (int nextByte; (nextByte = inputStream.read()) != -1; ) + plainData.append(nextByte); + + mode = plainTextStore.readMode(q->key()); + data = plainData; + q->emitFinished(); +} + +void WritePasswordJobPrivate::scheduledStart() +{ + const KeyStore keyStore = KeyStore::getInstance(QStringLiteral("AndroidKeyStore")); + + if (!keyStore || !keyStore.load()) { + q->emitFinishedWithError(Error::AccessDenied, tr("Could not open keystore")); + return; + } + + const auto &alias = makeAlias(q->service(), q->key()); + if (!keyStore.containsAlias(alias)) { + const Calendar start = Calendar::getInstance(); + const Calendar end = Calendar::getInstance(); + end.add(Calendar::YEAR, 99); + + const KeyPairGeneratorSpec spec = + KeyPairGeneratorSpec::Builder(Context(QtAndroid::androidActivity())). + setAlias(alias). + setSubject(X500Principal(QStringLiteral("CN=QtKeychain, O=Android Authority"))). + setSerialNumber(java::math::BigInteger::ONE). + setStartDate(start.getTime()). + setEndDate(end.getTime()). + build(); + + const KeyPairGenerator generator = KeyPairGenerator::getInstance(QStringLiteral("RSA"), + QStringLiteral("AndroidKeyStore")); + + if (!generator) { + q->emitFinishedWithError(Error::OtherError, tr("Could not create private key generator")); + return; + } + + generator.initialize(spec); + + if (!generator.generateKeyPair()) { + q->emitFinishedWithError(Error::OtherError, tr("Could not generate new private key")); + return; + } + } + + const KeyStore::PrivateKeyEntry entry = keyStore.getEntry(alias); + + if (!entry) { + q->emitFinishedWithError(Error::AccessDenied, tr("Could not retrieve private key from keystore")); + return; + } + + const RSAPublicKey publicKey = entry.getCertificate().getPublicKey(); + const Cipher cipher = Cipher::getInstance(QStringLiteral("RSA/ECB/PKCS1Padding")); + + if (!cipher || !cipher.init(Cipher::ENCRYPT_MODE, publicKey)) { + q->emitFinishedWithError(Error::OtherError, tr("Could not create encryption cipher")); + return; + } + + ByteArrayOutputStream outputStream; + CipherOutputStream cipherOutputStream(outputStream, cipher); + + if (!cipherOutputStream.write(data) || !cipherOutputStream.close()) { + q->emitFinishedWithError(Error::OtherError, tr("Could not encrypt data")); + return; + } + + PlainTextStore plainTextStore(q->service(), q->settings()); + plainTextStore.write(q->key(), outputStream.toByteArray(), mode); + + if (plainTextStore.error() != NoError) + q->emitFinishedWithError(plainTextStore.error(), plainTextStore.errorString()); + else + q->emitFinished(); +} + +void DeletePasswordJobPrivate::scheduledStart() +{ + const KeyStore keyStore = KeyStore::getInstance(QStringLiteral("AndroidKeyStore")); + + if (!keyStore || !keyStore.load()) { + q->emitFinishedWithError(Error::AccessDenied, tr("Could not open keystore")); + return; + } + + const auto &alias = makeAlias(q->service(), q->key()); + if (!keyStore.deleteEntry(alias)) { + q->emitFinishedWithError(Error::OtherError, tr("Could not remove private key from keystore")); + return; + } + + PlainTextStore plainTextStore(q->service(), q->settings()); + plainTextStore.remove(q->key()); + + if (plainTextStore.error() != NoError) + q->emitFinishedWithError(plainTextStore.error(), plainTextStore.errorString()); + else + q->emitFinished(); +} diff --git a/ext/qtkeychain/keychain_apple.mm b/ext/qtkeychain/keychain_apple.mm new file mode 100644 index 0000000..4662e40 --- /dev/null +++ b/ext/qtkeychain/keychain_apple.mm @@ -0,0 +1,146 @@ +/****************************************************************************** + * Copyright (C) 2016 Mathias Hasselmann * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution * + * details, check the accompanying file 'COPYING'. * + *****************************************************************************/ + +#include "keychain_p.h" + +#import +#import + +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")); + } +}; + +void ReadPasswordJobPrivate::scheduledStart() +{ + NSDictionary *const query = @{ + (__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword, + (__bridge id) kSecAttrService: (__bridge NSString *) service.toCFString(), + (__bridge id) kSecAttrAccount: (__bridge NSString *) key.toCFString(), + (__bridge id) kSecReturnData: @YES, + }; + + CFTypeRef dataRef = nil; + const OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef) query, &dataRef); + + data.clear(); + mode = Binary; + + if (status == errSecSuccess) { + if (dataRef) + data = QByteArray::fromCFData((CFDataRef) dataRef); + + q->emitFinished(); + } else { + const ErrorDescription error = ErrorDescription::fromStatus(status); + q->emitFinishedWithError(error.code, Job::tr("Could not retrieve private key from keystore: %1").arg(error.message)); + } + + if (dataRef) + [dataRef release]; +} + +void WritePasswordJobPrivate::scheduledStart() +{ + NSDictionary *const query = @{ + (__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword, + (__bridge id) kSecAttrService: (__bridge NSString *) service.toCFString(), + (__bridge id) kSecAttrAccount: (__bridge NSString *) key.toCFString(), + }; + + OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef) query, nil); + + if (status == errSecSuccess) { + NSDictionary *const update = @{ + (__bridge id) kSecValueData: (__bridge NSData *) data.toCFData(), + }; + + status = SecItemUpdate((__bridge CFDictionaryRef) query, (__bridge CFDictionaryRef) update); + } else { + NSDictionary *const insert = @{ + (__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword, + (__bridge id) kSecAttrService: (__bridge NSString *) service.toCFString(), + (__bridge id) kSecAttrAccount: (__bridge NSString *) key.toCFString(), + (__bridge id) kSecValueData: (__bridge NSData *) data.toCFData(), + }; + + status = SecItemAdd((__bridge CFDictionaryRef) insert, nil); + } + + if (status == errSecSuccess) { + q->emitFinished(); + } else { + const ErrorDescription error = ErrorDescription::fromStatus(status); + q->emitFinishedWithError(error.code, tr("Could not store data in settings: %1").arg(error.message)); + } +} + +void DeletePasswordJobPrivate::scheduledStart() +{ + const NSDictionary *const query = @{ + (__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword, + (__bridge id) kSecAttrService: (__bridge NSString *) service.toCFString(), + (__bridge id) kSecAttrAccount: (__bridge NSString *) key.toCFString(), + }; + + const OSStatus status = SecItemDelete((__bridge CFDictionaryRef) query); + + if (status == errSecSuccess) { + q->emitFinished(); + } else { + const ErrorDescription error = ErrorDescription::fromStatus(status); + q->emitFinishedWithError(error.code, Job::tr("Could not remove private key from keystore: %1").arg(error.message)); + } +} diff --git a/ext/qtkeychain/keychain_haiku.cpp b/ext/qtkeychain/keychain_haiku.cpp new file mode 100644 index 0000000..58b1ab7 --- /dev/null +++ b/ext/qtkeychain/keychain_haiku.cpp @@ -0,0 +1,187 @@ +/****************************************************************************** + * Copyright (C) 2018 François Revol * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution * + * details, check the accompanying file 'COPYING'. * + *****************************************************************************/ +#include "keychain_p.h" + +#include + +#include +#include +#include + +#include +#include +#include + +using namespace QKeychain; + +class AutoApp { +public: + AutoApp(); + ~AutoApp(); + BApplication *app; +}; + + +AutoApp::AutoApp() + : app(NULL) +{ + if (be_app != NULL) + return; + + // no BApplication object, probably using QCoreApplication + // but we need one around + + QString appSignature; + + char signature[B_MIME_TYPE_LENGTH]; + signature[0] = '\0'; + + QString appPath = QCoreApplication::applicationFilePath(); + + BFile appFile(appPath.toUtf8(), B_READ_ONLY); + if (appFile.InitCheck() == B_OK) { + BAppFileInfo info(&appFile); + if (info.InitCheck() == B_OK) { + if (info.GetSignature(signature) != B_OK) + signature[0] = '\0'; + } + } + + if (signature[0] != '\0') + appSignature = QLatin1String(signature); + else + appSignature = QLatin1String("application/x-vnd.qtkeychain-") + + QCoreApplication::applicationName().remove("_x86"); + + app = new BApplication(appSignature.toUtf8().constData()); +} + +AutoApp::~AutoApp() +{ + delete app; +} + +static QString strForStatus( status_t os ) { + const char * const buf = strerror(os) ; + return QObject::tr( "error 0x%1: %2" ) + .arg( os, 8, 16 ).arg( QString::fromUtf8( buf, strlen( buf ) ) ); +} + +void ReadPasswordJobPrivate::scheduledStart() +{ + AutoApp aa; + QString errorString; + Error error = NoError; + BKeyStore keyStore; + BPasswordKey password; + + status_t result = keyStore.GetKey(B_KEY_TYPE_PASSWORD, + q->service().toUtf8().constData(), + q->key().toUtf8().constData(), + false, password); + + data = QByteArray(reinterpret_cast(password.Data()), password.DataLength()); + + switch ( result ) { + case B_OK: + q->emitFinished(); + return; + case B_ENTRY_NOT_FOUND: + errorString = tr("Password not found"); + error = EntryNotFound; + break; + default: + errorString = strForStatus( result ); + error = OtherError; + break; + } + + q->emitFinishedWithError( error, errorString ); +} + +void WritePasswordJobPrivate::scheduledStart() +{ + AutoApp aa; + QString errorString; + Error error = NoError; + BKeyStore keyStore; + BPasswordKey password(data.constData(), + B_KEY_PURPOSE_GENERIC, + q->service().toUtf8().constData(), + q->key().toUtf8().constData()); + status_t result = B_OK; + + // re-add as binary if it's not text + if (mode == Binary) + result = password.SetData(reinterpret_cast(data.constData()), data.size()); + + if (result == B_OK) + result = keyStore.AddKey(password); + + if (result == B_NAME_IN_USE) { + BPasswordKey old_password; + result = keyStore.GetKey(B_KEY_TYPE_PASSWORD, + q->service().toUtf8().constData(), + q->key().toUtf8().constData(), + false, old_password); + if (result == B_OK) + result = keyStore.RemoveKey(old_password); + if (result == B_OK) + result = keyStore.AddKey(password); + } + + switch ( result ) { + case B_OK: + q->emitFinished(); + return; + case B_ENTRY_NOT_FOUND: + errorString = tr("Password not found"); + error = EntryNotFound; + break; + default: + errorString = strForStatus( result ); + error = OtherError; + break; + } + + q->emitFinishedWithError( error, errorString ); +} + +void DeletePasswordJobPrivate::scheduledStart() +{ + AutoApp aa; + QString errorString; + Error error = NoError; + BKeyStore keyStore; + BPasswordKey password; + + status_t result = keyStore.GetKey(B_KEY_TYPE_PASSWORD, + q->service().toUtf8().constData(), + q->key().toUtf8().constData(), + false, password); + + if (result == B_OK) + result = keyStore.RemoveKey(password); + + switch ( result ) { + case B_OK: + q->emitFinished(); + return; + case B_ENTRY_NOT_FOUND: + errorString = tr("Password not found"); + error = EntryNotFound; + break; + default: + errorString = strForStatus( result ); + error = CouldNotDeleteEntry; + break; + } + + q->emitFinishedWithError( error, errorString ); +} diff --git a/ext/qtkeychain/keychain_mac.cpp b/ext/qtkeychain/keychain_mac.cpp deleted file mode 100644 index 8a058cf..0000000 --- a/ext/qtkeychain/keychain_mac.cpp +++ /dev/null @@ -1,161 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2011-2015 Frank Osterfeld * - * * - * This program is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * - * or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution * - * details, check the accompanying file 'COPYING'. * - *****************************************************************************/ -#include "keychain_p.h" - -#include -#include -#include - -using namespace QKeychain; - -template -struct Releaser { - explicit Releaser( const T& v ) : value( v ) {} - ~Releaser() { - CFRelease( value ); - } - - const T value; -}; - -static QString strForStatus( OSStatus os ) { - const Releaser str( SecCopyErrorMessageString( os, 0 ) ); - const char * const buf = CFStringGetCStringPtr( str.value, kCFStringEncodingUTF8 ); - if ( !buf ) - return QObject::tr( "%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 index 09c9b25..d4794a5 100644 --- a/ext/qtkeychain/keychain_p.h +++ b/ext/qtkeychain/keychain_p.h @@ -13,9 +13,9 @@ #include #include #include -#include +#include -#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) +#if defined(KEYCHAIN_DBUS) #include @@ -35,105 +35,109 @@ class JobExecutor; class JobPrivate : public QObject { Q_OBJECT public: - JobPrivate( const QString& service_ ) - : error( NoError ) - , service( service_ ) - , autoDelete( true ) - , insecureFallback( false ) {} + 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 settings; + QString key; + +friend class Job; +friend class JobExecutor; +friend class ReadPasswordJob; +friend class WritePasswordJob; +friend class PlainTextStore; }; -class ReadPasswordJobPrivate : public QObject { +class ReadPasswordJobPrivate : public JobPrivate { Q_OBJECT public: - explicit ReadPasswordJobPrivate( ReadPasswordJob* qq ) : q( qq ), walletHandle( 0 ), dataType( Text ) {} - void scheduledStart(); + explicit ReadPasswordJobPrivate( const QString &service_, ReadPasswordJob* qq ); + void scheduledStart() override; - 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); +#if defined(KEYCHAIN_DBUS) + void fallbackOnError(const QDBusError& err) override; private Q_SLOTS: - void kwalletWalletFound( QDBusPendingCallWatcher* watcher ); - void kwalletOpenFinished( QDBusPendingCallWatcher* watcher ); + void kwalletOpenFinished( QDBusPendingCallWatcher* watcher ) override; void kwalletEntryTypeFinished( QDBusPendingCallWatcher* watcher ); - void kwalletReadFinished( 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 kwalletWalletFound( QDBusPendingCallWatcher* ) {} void kwalletOpenFinished( QDBusPendingCallWatcher* ) {} void kwalletEntryTypeFinished( QDBusPendingCallWatcher* ) {} - void kwalletReadFinished( QDBusPendingCallWatcher* ) {} + void kwalletFinished( QDBusPendingCallWatcher* ) {} #endif + friend class ReadPasswordJob; }; -class WritePasswordJobPrivate : public QObject { +class WritePasswordJobPrivate : public JobPrivate { Q_OBJECT public: - explicit WritePasswordJobPrivate( WritePasswordJob* qq ) : q( qq ), mode( Delete ) {} - void scheduledStart(); + explicit WritePasswordJobPrivate( const QString &service_, WritePasswordJob* qq ); + void scheduledStart() override; - 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* ) {} +#if defined(KEYCHAIN_DBUS) + void fallbackOnError(const QDBusError& err) override; #endif + + friend class WritePasswordJob; }; -class DeletePasswordJobPrivate : public QObject { +class DeletePasswordJobPrivate : public JobPrivate { Q_OBJECT public: - explicit DeletePasswordJobPrivate( DeletePasswordJob* qq ) : q( qq ) {} + explicit DeletePasswordJobPrivate( const QString &service_, DeletePasswordJob* qq ); + + void scheduledStart() override; + +#if defined(KEYCHAIN_DBUS) + void fallbackOnError(const QDBusError& err) override; +#endif + +protected: void doStart(); - DeletePasswordJob* const q; - QString key; -private Q_SLOTS: - void jobFinished( QKeychain::Job* ); + + friend class DeletePasswordJob; }; class JobExecutor : public QObject { @@ -154,8 +158,8 @@ private Q_SLOTS: private: static JobExecutor* s_instance; - Job* m_runningJob; - QVector > m_queue; + QQueue > m_queue; + bool m_jobRunning; }; } diff --git a/ext/qtkeychain/keychain_unix.cpp b/ext/qtkeychain/keychain_unix.cpp index 48f4240..b9a8e9d 100644 --- a/ext/qtkeychain/keychain_unix.cpp +++ b/ext/qtkeychain/keychain_unix.cpp @@ -8,24 +8,15 @@ *****************************************************************************/ #include "keychain_p.h" #include "gnomekeyring_p.h" - -#include +#include "libsecret_p.h" +#include "plaintextstore_p.h" #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_LibSecretKeyring, Backend_GnomeKeyring, Backend_Kwallet4, Backend_Kwallet5 @@ -44,7 +35,7 @@ enum DesktopEnvironment { // licensed under BSD, see base/nix/xdg_util.cc static DesktopEnvironment getKdeVersion() { - QString value = qgetenv("KDE_SESSION_VERSION"); + QByteArray value = qgetenv("KDE_SESSION_VERSION"); if ( value == "5" ) { return DesktopEnv_Plasma5; } else if (value == "4" ) { @@ -63,6 +54,8 @@ static DesktopEnvironment detectDesktopEnvironment() { return DesktopEnv_Unity; } else if ( xdgCurrentDesktop == "KDE" ) { return getKdeVersion(); + } else if ( xdgCurrentDesktop == "XFCE" ) { + return DesktopEnv_Xfce; } QByteArray desktopSession = qgetenv("DESKTOP_SESSION"); @@ -85,26 +78,82 @@ static DesktopEnvironment detectDesktopEnvironment() { return DesktopEnv_Other; } +static bool isKwallet5Available() +{ + if (!QDBusConnection::sessionBus().isConnected()) + return false; + + org::kde::KWallet iface( + QLatin1String("org.kde.kwalletd5"), + QLatin1String("/modules/kwalletd5"), + 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; - break; + case DesktopEnv_Plasma5: + if (isKwallet5Available()) { + 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; - 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; + if (LibSecretKeyring::isAvailable()) { + return Backend_LibSecretKeyring; } + if (GnomeKeyring::isAvailable()) { + return Backend_GnomeKeyring; + } + if (isKwallet5Available()) { + 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; } } @@ -125,7 +174,7 @@ static void kwalletReadPasswordScheduledStartImpl(const char * service, const ch } else { - // D-Bus is not reachable so none can tell us something about KWalletd + // 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 ); } @@ -133,9 +182,17 @@ static void kwalletReadPasswordScheduledStartImpl(const char * service, const ch 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: - if ( !GnomeKeyring::find_network_password( key.toUtf8().constData(), q->service().toUtf8().constData(), - reinterpret_cast( &ReadPasswordJobPrivate::gnomeKeyring_cb ), + this->mode = JobPrivate::Text; + if ( !GnomeKeyring::find_network_password( key.toUtf8().constData(), + q->service().toUtf8().constData(), + "plaintext", + reinterpret_cast( &JobPrivate::gnomeKeyring_readCb ), this, 0 ) ) q->emitFinishedWithError( OtherError, tr("Unknown error") ); break; @@ -149,13 +206,14 @@ void ReadPasswordJobPrivate::scheduledStart() { } } -void ReadPasswordJobPrivate::kwalletWalletFound(QDBusPendingCallWatcher *watcher) +void JobPrivate::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*)) ); + connect( pendingWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), + this, SLOT(kwalletOpenFinished(QDBusPendingCallWatcher*)) ); } static QPair mapGnomeKeyringError( int result ) @@ -188,14 +246,23 @@ static QPair mapGnomeKeyringError( int result ) return qMakePair( OtherError, QObject::tr("Unknown error") ); } -void ReadPasswordJobPrivate::gnomeKeyring_cb( int result, const char* string, ReadPasswordJobPrivate* self ) +void JobPrivate::gnomeKeyring_readCb( int result, const char* string, JobPrivate* self ) { if ( result == GnomeKeyring::RESULT_OK ) { - if ( self->dataType == ReadPasswordJobPrivate::Text ) - self->data = string; + if (self->mode == JobPrivate::Text) + self->data = QByteArray(string); else - self->data = QByteArray::fromBase64( string ); + 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( &JobPrivate::gnomeKeyring_readCb ), + self, 0 ) ) + self->q->emitFinishedWithError( OtherError, tr("Unknown error") ); } else { const QPair errorResult = mapGnomeKeyringError( result ); self->q->emitFinishedWithError( errorResult.first, errorResult.second ); @@ -204,19 +271,16 @@ void ReadPasswordJobPrivate::gnomeKeyring_cb( int result, const char* string, Re void ReadPasswordJobPrivate::fallbackOnError(const QDBusError& err ) { - QScopedPointer local( !q->settings() ? new QSettings( q->service() ) : 0 ); - QSettings* actual = q->settings() ? q->settings() : local.data(); + PlainTextStore plainTextStore( q->service(), q->settings() ); - if ( q->insecureFallback() && actual->contains( dataKey( key ) ) ) { + if ( q->insecureFallback() && plainTextStore.contains( key ) ) { + mode = plainTextStore.readMode( key ); + data = plainTextStore.readData( key ); - const WritePasswordJobPrivate::Mode mode = WritePasswordJobPrivate::stringToMode( actual->value( typeKey( key ) ).toString() ); - if (mode == WritePasswordJobPrivate::Binary) - dataType = Binary; + if ( plainTextStore.error() != NoError ) + q->emitFinishedWithError( plainTextStore.error(), plainTextStore.errorString() ); else - dataType = Text; - data = actual->value( dataKey( key ) ).toByteArray(); - - q->emitFinished(); + q->emitFinished(); } else { if ( err.type() == QDBusError::ServiceUnknown ) //KWalletd not running q->emitFinishedWithError( NoBackendAvailable, tr("No keychain service available") ); @@ -229,21 +293,20 @@ void ReadPasswordJobPrivate::kwalletOpenFinished( QDBusPendingCallWatcher* watch 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 ) ) ) { + 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 = actual->value( dataKey( key ) ).toByteArray(); - const WritePasswordJobPrivate::Mode mode = WritePasswordJobPrivate::stringToMode( actual->value( typeKey( key ) ).toString() ); - actual->remove( key ); + data = plainTextStore.readData( key ); + const WritePasswordJobPrivate::Mode mode = plainTextStore.readMode( key ); + plainTextStore.remove( key ); q->emitFinished(); @@ -300,10 +363,10 @@ void ReadPasswordJobPrivate::kwalletEntryTypeFinished( QDBusPendingCallWatcher* q->emitFinishedWithError( EntryNotFound, tr("Entry not found") ); return; case Password: - dataType = Text; + mode = Text; break; case Stream: - dataType = Binary; + mode = Binary; break; case Map: q->emitFinishedWithError( EntryNotFound, tr("Unsupported entry type 'Map'") ); @@ -313,32 +376,32 @@ void ReadPasswordJobPrivate::kwalletEntryTypeFinished( QDBusPendingCallWatcher* return; } - const QDBusPendingCall nextReply = dataType == Text - ? QDBusPendingCall( iface->readPassword( walletHandle, q->service(), key, q->service() ) ) - : QDBusPendingCall( iface->readEntry( walletHandle, q->service(), key, q->service() ) ); + 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(kwalletReadFinished(QDBusPendingCallWatcher*)) ); + connect( nextWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletFinished(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; +void ReadPasswordJobPrivate::kwalletFinished( QDBusPendingCallWatcher* watcher ) { + if ( !watcher->isError() ) { + if ( mode == Binary ) { + QDBusPendingReply reply = *watcher; + if (reply.isValid()) { + data = reply.value(); + } + } else { + QDBusPendingReply reply = *watcher; + if (reply.isValid()) { + data = reply.value().toUtf8(); + } + } } - if ( dataType == Binary ) { - QDBusPendingReply reply = *watcher; - data = reply.value(); - } else { - QDBusPendingReply reply = *watcher; - data = reply.value().toUtf8(); - } - q->emitFinished(); + JobPrivate::kwalletFinished(watcher); } -static void kwalletWritePasswordScheduledStart( const char * service, const char * path, WritePasswordJobPrivate * priv ) { +static void kwalletWritePasswordScheduledStart( const char * service, const char * path, JobPrivate * priv ) { if ( QDBusConnection::sessionBus().isConnected() ) { priv->iface = new org::kde::KWallet( QLatin1String(service), QLatin1String(path), QDBusConnection::sessionBus(), priv ); @@ -356,21 +419,38 @@ static void kwalletWritePasswordScheduledStart( const char * service, const char 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") ); + 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( &JobPrivate::gnomeKeyring_writeCb ), + this, 0 ) ) + q->emitFinishedWithError( OtherError, tr("Unknown error") ); + } break; case Backend_Kwallet4: @@ -382,64 +462,23 @@ void WritePasswordJobPrivate::scheduledStart() { } } -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(); + PlainTextStore plainTextStore( q->service(), q->settings() ); + plainTextStore.write( key, data, mode ); + if ( plainTextStore.error() != NoError ) + q->emitFinishedWithError( plainTextStore.error(), plainTextStore.errorString() ); + else 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 ) +void JobPrivate::gnomeKeyring_writeCb(int result, JobPrivate* self ) { if ( result == GnomeKeyring::RESULT_OK ) { self->q->emitFinished(); @@ -449,32 +488,19 @@ void WritePasswordJobPrivate::gnomeKeyring_cb( int result, WritePasswordJobPriva } } -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 ) { +void JobPrivate::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 ) ) - { + 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 - actual->remove( key ); - actual->sync(); + plainTextStore.remove( key ); } const int handle = reply.value(); @@ -486,25 +512,75 @@ void WritePasswordJobPrivate::kwalletOpenFinished( QDBusPendingCallWatcher* watc 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() ); + if ( mode == Text ) + nextReply = iface->writePassword( handle, q->service(), key, QString::fromUtf8(data), q->service() ); + else if ( mode == Binary ) + nextReply = iface->writeEntry( handle, q->service(), key, data, q->service() ); else nextReply = iface->removeEntry( handle, q->service(), key, q->service() ); QDBusPendingCallWatcher* nextWatcher = new QDBusPendingCallWatcher( nextReply, this ); - connect( nextWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletWriteFinished(QDBusPendingCallWatcher*)) ); + connect( nextWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletFinished(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; +void JobPrivate::kwalletFinished( QDBusPendingCallWatcher* watcher ) { + if ( !watcher->isError() ) { + if ( mode == Binary ) { + QDBusPendingReply reply = *watcher; + if (reply.isValid()) { + data = reply.value(); + } + } else { + QDBusPendingReply 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( &JobPrivate::gnomeKeyring_writeCb ), + 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; + } +} + +void DeletePasswordJobPrivate::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; + } + + actual->remove( key ); + actual->sync(); + + q->emitFinished(); + + q->emitFinished(); } diff --git a/ext/qtkeychain/keychain_win.cpp b/ext/qtkeychain/keychain_win.cpp index e2df4b8..27aaa96 100644 --- a/ext/qtkeychain/keychain_win.cpp +++ b/ext/qtkeychain/keychain_win.cpp @@ -7,8 +7,7 @@ * details, check the accompanying file 'COPYING'. * *****************************************************************************/ #include "keychain_p.h" - -#include +#include "plaintextstore_p.h" #include #include @@ -17,14 +16,110 @@ 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(); +#if defined(USE_CREDENTIAL_STORE) +#include - QByteArray encrypted = actual->value( key ).toByteArray(); - if ( encrypted.isNull() ) { - q->emitFinishedWithError( EntryNotFound, tr("Entry not found") ); +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(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; } @@ -53,24 +148,6 @@ void ReadPasswordJobPrivate::scheduledStart() { } 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(); @@ -89,19 +166,23 @@ void WritePasswordJobPrivate::scheduledStart() { 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 ); + 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 diff --git a/ext/qtkeychain/libsecret.cpp b/ext/qtkeychain/libsecret.cpp new file mode 100644 index 0000000..61d0083 --- /dev/null +++ b/ext/qtkeychain/libsecret.cpp @@ -0,0 +1,350 @@ +#undef signals + +#if defined(HAVE_LIBSECRET) +#include +#endif + +#include "libsecret_p.h" + +#include +#include + +#if defined(HAVE_LIBSECRET) +const SecretSchema* qtkeychainSchema(void) { + static const SecretSchema schema = { + "org.qt.keychain", SECRET_SCHEMA_DONT_MATCH_NAME, + { + { "user", SECRET_SCHEMA_ATTRIBUTE_STRING }, + { "server", SECRET_SCHEMA_ATTRIBUTE_STRING }, + { "type", SECRET_SCHEMA_ATTRIBUTE_STRING } + } + }; + + 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 = NULL; +static secret_password_lookup_finish_t secret_password_lookup_finish_fn = NULL; +static secret_password_store_t secret_password_store_fn = NULL; +static secret_password_store_finish_t secret_password_store_finish_fn = NULL; +static secret_password_clear_t secret_password_clear_fn = NULL; +static secret_password_clear_finish_t secret_password_clear_finish_fn = NULL; +static secret_password_free_t secret_password_free_fn = NULL; +static secret_error_get_quark_t secret_error_get_quark_fn = NULL; + +static 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 = NULL; + 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 != NULL) { + 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(), NULL, + on_password_lookup, arg, + "user", arg->user.toUtf8().constData(), + "server", arg->server.toUtf8().constData(), + "type", "base64", + NULL); + 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 = NULL; + QKeychain::JobPrivate *self = (QKeychain::JobPrivate*)inst; + + Q_UNUSED(source); + + secret_password_store_finish_fn (result, &error); + + if (self) { + if (error != NULL) { + self->q->emitFinishedWithError( gerrorToCode(error), + QString::fromUtf8(error->message) ); + } else { + self->q->emitFinished(); + } + } + if (error != NULL) { + g_error_free (error); + } +} + +static void +on_password_cleared (GObject *source, + GAsyncResult *result, + gpointer inst) +{ + GError *error = NULL; + 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 != NULL) { + 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 == NULL) + return false; + if (secret_password_lookup_finish_fn == NULL) + return false; + if (secret_password_store_fn == NULL) + return false; + if (secret_password_store_finish_fn == NULL) + return false; + if (secret_password_clear_fn == NULL) + return false; + if (secret_password_clear_finish_fn == NULL) + return false; + if (secret_password_free_fn == NULL) + return false; + if (secret_error_get_quark_fn == NULL) + 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; + + qDebug() << Q_FUNC_INFO; + secret_password_lookup_fn (qtkeychainSchema(), NULL, on_password_lookup, arg, + "user", user.toUtf8().constData(), + "server", server.toUtf8().constData(), + "type", "plaintext", + NULL); + 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; + } + + qDebug() << Q_FUNC_INFO; + secret_password_store_fn (qtkeychainSchema(), SECRET_COLLECTION_DEFAULT, + display_name.toUtf8().constData(), + pwd.constData(), NULL, on_password_stored, self, + "user", user.toUtf8().constData(), + "server", server.toUtf8().constData(), + "type", type.toUtf8().constData(), + NULL); + return true; +#else + Q_UNUSED(display_name) + Q_UNUSED(user) + Q_UNUSED(server) + Q_UNUSED(mode) + Q_UNUSED(password) + Q_UNUSED(self) + return false; +#endif +} + +bool LibSecretKeyring::deletePassword(const QString &key, const QString &service, + QKeychain::JobPrivate* self) +{ +#if defined(HAVE_LIBSECRET) + if (!isAvailable()) { + return false; + } + + qDebug() << Q_FUNC_INFO; + secret_password_clear_fn (qtkeychainSchema(), NULL, on_password_cleared, self, + "user", key.toUtf8().constData(), + "server", service.toUtf8().constData(), + NULL); + 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/ext/qtkeychain/libsecret_p.h b/ext/qtkeychain/libsecret_p.h new file mode 100644 index 0000000..bd966fe --- /dev/null +++ b/ext/qtkeychain/libsecret_p.h @@ -0,0 +1,33 @@ +#ifndef QTKEYCHAIN_LIBSECRET_P_H +#define QTKEYCHAIN_LIBSECRET_P_H + +#include + +#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/ext/qtkeychain/plaintextstore.cpp b/ext/qtkeychain/plaintextstore.cpp new file mode 100644 index 0000000..b9d0272 --- /dev/null +++ b/ext/qtkeychain/plaintextstore.cpp @@ -0,0 +1,110 @@ +/****************************************************************************** + * Copyright (C) 2011-2015 Frank Osterfeld * + * Copyright (C) 2016 Mathias Hasselmann * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution * + * details, check the accompanying file 'COPYING'. * + *****************************************************************************/ + +#include "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/ext/qtkeychain/plaintextstore_p.h b/ext/qtkeychain/plaintextstore_p.h new file mode 100644 index 0000000..7ec05aa --- /dev/null +++ b/ext/qtkeychain/plaintextstore_p.h @@ -0,0 +1,47 @@ +/****************************************************************************** + * Copyright (C) 2011-2015 Frank Osterfeld * + * Copyright (C) 2016 Mathias Hasselmann * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution * + * details, check the accompanying file 'COPYING'. * + *****************************************************************************/ + +#ifndef QTKEYCHAIN_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 m_localSettings; + QSettings *const m_actualSettings; + QString m_errorString; + Error m_error; +}; + +} + +#endif // QTKEYCHAIN_PLAINTEXTSTORE_P_H diff --git a/ext/qtkeychain/qkeychain_export.h b/ext/qtkeychain/qkeychain_export.h deleted file mode 100644 index 1bb1669..0000000 --- a/ext/qtkeychain/qkeychain_export.h +++ /dev/null @@ -1,17 +0,0 @@ -#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/qt5keychain.pri b/ext/qtkeychain/qt5keychain.pri new file mode 100644 index 0000000..812ded2 --- /dev/null +++ b/ext/qtkeychain/qt5keychain.pri @@ -0,0 +1,100 @@ +# Minimal qmake support. +# This file is provided as is without any warranty. +# It can break at anytime or be removed without notice. + +lessThan(QT_MAJOR_VERSION, 5) { + error("qtkeychain requires Qt 5 or later") +} + +QTKEYCHAIN_PWD = $$PWD + +CONFIG += depend_includepath +DEFINES += QTKEYCHAIN_NO_EXPORT + +INCLUDEPATH += \ + $$PWD/.. \ + $$QTKEYCHAIN_PWD + +HEADERS += \ + $$QTKEYCHAIN_PWD/keychain_p.h \ + $$QTKEYCHAIN_PWD/keychain.h + +SOURCES += \ + $$QTKEYCHAIN_PWD/keychain.cpp + +unix:!android:!macx:!ios { + # Remove the following LIBSECRET_SUPPORT line + # to build without libsecret support. + DEFINES += LIBSECRET_SUPPORT + contains(DEFINES, LIBSECRET_SUPPORT) { + packagesExist(libsecret-1) { + !build_pass:message("Libsecret support: on") + CONFIG += link_pkgconfig + PKGCONFIG += libsecret-1 + DEFINES += HAVE_LIBSECRET + } else { + !build_pass:warning("Libsecret not found.") + !build_pass:message("Libsecret support: off") + } + } else { + !build_pass:message("Libsecret support: off") + } + + # Generate D-Bus interface: + DEFINES += KEYCHAIN_DBUS + QT += dbus + kwallet_interface.files = $$PWD/org.kde.KWallet.xml + DBUS_INTERFACES += kwallet_interface + + HEADERS += \ + $$QTKEYCHAIN_PWD/gnomekeyring_p.h \ + $$QTKEYCHAIN_PWD/plaintextstore_p.h \ + $$QTKEYCHAIN_PWD/libsecret_p.h + SOURCES += \ + $$QTKEYCHAIN_PWD/keychain_unix.cpp \ + $$QTKEYCHAIN_PWD/plaintextstore.cpp \ + $$QTKEYCHAIN_PWD/gnomekeyring.cpp \ + $$QTKEYCHAIN_PWD/libsecret.cpp +} + +android { + QT += androidextras + + HEADERS += \ + $$QTKEYCHAIN_PWD/androidkeystore_p.h \ + $$QTKEYCHAIN_PWD/plaintextstore_p.h + SOURCES += \ + $$QTKEYCHAIN_PWD/androidkeystore.cpp \ + $$QTKEYCHAIN_PWD/keychain_android.cpp \ + $$QTKEYCHAIN_PWD/plaintextstore.cpp +} + +win32 { + # Remove the following USE_CREDENTIAL_STORE line + # to use the CryptProtectData Windows API function + # instead of the Windows Credential Store. + DEFINES += USE_CREDENTIAL_STORE + contains(DEFINES, USE_CREDENTIAL_STORE) { + !build_pass:message("Windows Credential Store support: on") + LIBS += -ladvapi32 + } else { + !build_pass:message("Windows Credential Store support: off") + LIBS += -lcrypt32 + HEADERS += $$QTKEYCHAIN_PWD/plaintextstore_p.h + SOURCES += $$QTKEYCHAIN_PWD/plaintextstore.cpp + } + HEADERS += $$QTKEYCHAIN_PWD/libsecret_p.h + SOURCES += \ + $$QTKEYCHAIN_PWD/keychain_win.cpp \ + $$QTKEYCHAIN_PWD/libsecret.cpp +} + +macx:!ios { + LIBS += -framework Security -framework Foundation + SOURCES += $$QTKEYCHAIN_PWD/keychain_apple.mm +} + +ios { + LIBS += -framework Security -framework Foundation + OBJECTIVE_SOURCES += $$QTKEYCHAIN_PWD/keychain_ios.mm +} diff --git a/ext/qtkeychain/testclient.cpp b/ext/qtkeychain/testclient.cpp index a9d9bad..94189cf 100644 --- a/ext/qtkeychain/testclient.cpp +++ b/ext/qtkeychain/testclient.cpp @@ -52,6 +52,29 @@ int main( int argc, char** argv ) { return 1; } std::cout << "Password stored successfully" << std::endl; + } else if ( *it == QLatin1String("bstore") ) { + 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.setBinaryData( pass.toUtf8() ); + QEventLoop loop; + job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()) ); + job.start(); + loop.exec(); + if ( job.error() ) { + std::cerr << "Storing binary password failed: " + << qPrintable(job.errorString()) << std::endl; + return 1; + } + std::cout << "Password stored successfully" << std::endl; } else if ( *it == QLatin1String("restore") ) { if ( ++it == args.constEnd() ) return printUsage(); diff --git a/ext/qtkeychain/translations/qtkeychain_de.ts b/ext/qtkeychain/translations/qtkeychain_de.ts index cd8aad0..58f4451 100644 --- a/ext/qtkeychain/translations/qtkeychain_de.ts +++ b/ext/qtkeychain/translations/qtkeychain_de.ts @@ -1,176 +1,325 @@ + + QKeychain::DeletePasswordJobPrivate + + + Password entry not found + + + + + Could not decrypt data + Kann Daten nicht entschlüsseln + + + + + Unknown error + Unbekannter Fehler + + + + Could not open wallet: %1; %2 + Konnte Brieftasche nicht öffnen: %1; %2 + + + + Password not found + Passwort nicht gefunden + + + + Could not open keystore + + + + + Could not remove private key from keystore + + + + + QKeychain::JobPrivate + + + Unknown error + Unbekannter Fehler + + + + Access to keychain denied + Zugriff auf Schlüsselbund verweigert + + + + QKeychain::PlainTextStore + + + Could not store data in settings: access error + + + + + Could not store data in settings: format error + + + + + Could not delete data from settings: access error + + + + + Could not delete data from settings: format error + + + + + Entry not found + Eintrag nicht gefunden + + 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 + Passwort konnte nicht ausgelesen werden: %1; %2 - + Password not found Passwort nicht gefunden - - + + Entry not found Eintrag nicht gefunden - + + Password entry not found + + + + + Could not decrypt data Kann Daten nicht entschlüsseln + + + Could not open keystore + + + + + Could not retrieve private key from keystore + + + + + Could not create decryption cipher + + 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 + Zugriff auf Schlüsselbund verweigert - Could not delete encrypted data from settings: access error - Kann verschlüsselte Daten nicht aus den Einstellungen entfernen: Zugriffsfehler + 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 + Kann verschlüsselte Daten nicht aus den Einstellungen entfernen: Formatfehler - + + Credential size exceeds maximum size of %1 + + + + + Credential key exceeds maximum size of %1 + + + + + Writing credentials failed: Win32 error code %1 + + + + Encryption failed Verschlüsselung fehlgeschlagen - Could not store encrypted data in settings: access error - Kann verschlüsselte Daten nicht in den Einstellungen speichern: Zugriffsfehler + 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 + Kann verschlüsselte Daten nicht in den Einstellungen speichern: Formatfehler + + + + Password not found + Passwort nicht gefunden + + + + Could not open keystore + + + + + Could not create private key generator + + + + + Could not generate new private key + + + + + Could not retrieve private key from keystore + + + + + Could not create encryption cipher + + + + + Could not encrypt data + 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) + + Entry not found + Eintrag nicht gefunden + + + + error 0x%1: %2 diff --git a/ext/qtkeychain/translations/qtkeychain_fr.ts b/ext/qtkeychain/translations/qtkeychain_fr.ts new file mode 100644 index 0000000..4af8c5a --- /dev/null +++ b/ext/qtkeychain/translations/qtkeychain_fr.ts @@ -0,0 +1,334 @@ + + + + + QKeychain::DeletePasswordJobPrivate + + + Password entry not found + Mot de passe introuvable + + + + Could not decrypt data + Impossible de déchiffrer les données + + + + + Unknown error + Erreur inconnue + + + + Could not open wallet: %1; %2 + Impossible d'ouvrir le portefeuille : %1; %2 + + + + Password not found + Mot de passe introuvable + + + + Could not open keystore + + + + + Could not remove private key from keystore + + + + + QKeychain::JobPrivate + + + Unknown error + Erreur inconnue + + + + Access to keychain denied + Accès au trousseau refusé + + + + QKeychain::PlainTextStore + + + Could not store data in settings: access error + Impossible de stocker les données dans les paramètres : Erreur d'accès + + + + Could not store data in settings: format error + Impossible de stocker les données dans les paramètres : Erreur de format + + + + Could not delete data from settings: access error + Impossible de supprimer les données depuis les paramètres : Erreur d'accès + + + + Could not delete data from settings: format error + Impossible de supprimer les données depuis les paramètres : Erreur de format + + + + Entry not found + Entrée introuvable + + + + QKeychain::ReadPasswordJobPrivate + + + + Unknown error + Erreur inconnue + + + + D-Bus is not running + D-Bus n'est pas en cours d'exécution + + + + No keychain service available + Aucun service de trousseau disponible + + + + Could not open wallet: %1; %2 + Impossible d'ouvrir le trousseau : %1; %2 + + + + Access to keychain denied + Accès au trousseau refusé + + + + Could not determine data type: %1; %2 + Impossible de déterminer le type de données : %1: %2 + + + + Unsupported entry type 'Map' + Type d'entrée non supporté 'Map' + + + + Unknown kwallet entry type '%1' + Type de trousseau inconnu '%1' + + + Could not read password: %1; %2 + Impossible de lire le mot de passe : %1; %2 + + + + Password not found + Mot de passe introuvable + + + + + Entry not found + Entrée introuvable + + + + Password entry not found + Entrée de mot de passe introuvable + + + + + Could not decrypt data + Impossible de déchiffrer les données + + + + Could not open keystore + + + + + Could not retrieve private key from keystore + + + + + Could not create decryption cipher + + + + + QKeychain::WritePasswordJobPrivate + + + + Unknown error + Erreur inconnue + + + + D-Bus is not running + D-Bus n'est pas en cours d'exécution + + + + Could not open wallet: %1; %2 + Impossible d'ouvrir le trousseau : %1; %2 + + + Access to keychain denied + Accès au trousseau refusé + + + Could not delete encrypted data from settings: access error + Impossible de supprimer des données chiffrées dans les paramètres : Erreur d'accès + + + Could not delete encrypted data from settings: format error + Impossible de supprimer des données chiffrées dans les paramètres : Erreur de format + + + + Credential size exceeds maximum size of %1 + + + + + Credential key exceeds maximum size of %1 + + + + + Writing credentials failed: Win32 error code %1 + + + + + Encryption failed + Le chiffrement a échoué + + + Could not store encrypted data in settings: access error + Impossible de stocker des données chiffrées dans les paramètres : Erreur d'accès + + + Could not store encrypted data in settings: format error + Impossible de stocker des données chiffrées dans les paramètres : Erreur de format + + + + Password not found + Mot de passe introuvable + + + + Could not open keystore + + + + + Could not create private key generator + + + + + Could not generate new private key + + + + + Could not retrieve private key from keystore + + + + + Could not create encryption cipher + + + + + Could not encrypt data + + + + + QObject + + + Access to keychain denied + Accès au trousseau refusé + + + + No keyring daemon + Aucun démon de trousseau + + + + Already unlocked + Déjà déverrouillé + + + + No such keyring + Aucun trousseau + + + + Bad arguments + Mauvais arguments + + + + I/O error + Erreur d'E/S + + + + Cancelled + Annulé + + + + Keyring already exists + Trousseau déjà existant + + + + No match + Aucune correspondance + + + + Unknown error + Erreur inconnue + + + OS X Keychain error (OSStatus %1) + OS X Keychain error (OSStatus %1) + + + %1 (OSStatus %2) + %1 (OSStatus %2) + + + + Entry not found + Entrée introuvable + + + + error 0x%1: %2 + Erreur 0x%1 : %2 + + + diff --git a/ext/qtkeychain/translations/qtkeychain_ro.ts b/ext/qtkeychain/translations/qtkeychain_ro.ts index 9fa3494..2c716bb 100644 --- a/ext/qtkeychain/translations/qtkeychain_ro.ts +++ b/ext/qtkeychain/translations/qtkeychain_ro.ts @@ -1,178 +1,331 @@ + + QKeychain::DeletePasswordJobPrivate + + + Password entry not found + + + + + Could not decrypt data + Nu se poate decripta data + + + + + Unknown error + Eroare necunoscută + + + + Could not open wallet: %1; %2 + Nu se poate deschide portofelul: %1; %2 + + + + Password not found + Parola nu a fost găsită + + + + Could not open keystore + + + + + Could not remove private key from keystore + + + + + QKeychain::JobPrivate + + + Unknown error + Eroare necunoscută + + + + Access to keychain denied + Acces interzis la serviciul de chei + + + + QKeychain::PlainTextStore + + + Could not store data in settings: access error + + + + + Could not store data in settings: format error + + + + + Could not delete data from settings: access error + + + + + Could not delete data from settings: format error + + + + + Entry not found + Înregistrarea nu a fost găsită + + 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 + 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ă - + + Password entry not found + + + + + Could not decrypt data Nu se poate decripta data + + + Could not open keystore + + + + + Could not retrieve private key from keystore + + + + + Could not create decryption cipher + + 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 + 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 + 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 + Nu se pot șterge datele criptate din setări: eroare de format - + + Credential size exceeds maximum size of %1 + + + + + Credential key exceeds maximum size of %1 + + + + + Writing credentials failed: Win32 error code %1 + + + + 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 + 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 + Nu se pot stoca datele criptate în setări: eroare de format + + + + Password not found + Parola nu a fost găsită + + + + Could not open keystore + + + + + Could not create private key generator + + + + + Could not generate new private key + + + + + Could not retrieve private key from keystore + + + + + Could not create encryption cipher + + + + + Could not encrypt data + 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) + %1 (OSStatus %2) + + + + Entry not found + Înregistrarea nu a fost găsită + + + + error 0x%1: %2 + diff --git a/ext/qtkeychain/translations/qtkeychain_ru.ts b/ext/qtkeychain/translations/qtkeychain_ru.ts new file mode 100644 index 0000000..dd0f8b1 --- /dev/null +++ b/ext/qtkeychain/translations/qtkeychain_ru.ts @@ -0,0 +1,310 @@ + + + + + QKeychain::DeletePasswordJobPrivate + + + + Unknown error + Неизвестная ошибка + + + + Could not open wallet: %1; %2 + Не удалось открыть бумажник: %1; %2 + + + + Password entry not found + Пароль не найден + + + + Could not decrypt data + Не удалось расшифровать данные + + + + Password not found + Пароль не найден + + + + Could not open keystore + + + + + Could not remove private key from keystore + + + + + QKeychain::JobPrivate + + + Unknown error + Неизвестная ошибка + + + + Access to keychain denied + Доступ к связке ключей запрещён + + + + QKeychain::PlainTextStore + + + Could not store data in settings: access error + Не удалось сохранить данные в настройках: ошибка доступа + + + + Could not store data in settings: format error + Не удалось сохранить данные в настройках: ошибка формата + + + + Could not delete data from settings: access error + Не удалось удалить данные из настроек: ошибка доступа + + + + Could not delete data from settings: format error + Не удалось удалить данные из настроек: ошибка формата + + + + Entry not found + Запись не найдена + + + + QKeychain::ReadPasswordJobPrivate + + + Password not found + Пароль не найден + + + + D-Bus is not running + D-Bus не запущен + + + + + Unknown error + Неизвестная ошибка + + + + No keychain service available + Служба связки ключей недоступна + + + + Could not open wallet: %1; %2 + Не удалось открыть кошелёк: %1; %2 + + + + Access to keychain denied + Доступ к связке ключей запрещён + + + + Could not determine data type: %1; %2 + Не удалось определить тип данных: %1; %2 + + + + + Entry not found + Запись не найдена + + + + Unsupported entry type 'Map' + Неподдерживаемый тип записи 'Map' + + + + Unknown kwallet entry type '%1' + Неизвестный тип записи kwallet '%1' + + + + Password entry not found + Пароль не найден + + + + + Could not decrypt data + Не удалось расшифровать данные + + + + Could not open keystore + + + + + Could not retrieve private key from keystore + + + + + Could not create decryption cipher + + + + + QKeychain::WritePasswordJobPrivate + + + D-Bus is not running + D-Bus не запущен + + + + + Unknown error + Неизвестная ошибка + + + + Could not open wallet: %1; %2 + Не удалось открыть кошелёк: %1; %2 + + + + Credential size exceeds maximum size of %1 + Учётные данные превышают максимальный размер %1 + + + + Credential key exceeds maximum size of %1 + Ключ учётных данных превышает максимальный размер %1 + + + + Writing credentials failed: Win32 error code %1 + Не удалось сохранить учётные данные: код ошибки win32 %1 + + + + Encryption failed + Шифрование не удалось + + + + Password not found + Пароль не найден + + + + Could not open keystore + + + + + Could not create private key generator + + + + + Could not generate new private key + + + + + Could not retrieve private key from keystore + + + + + Could not create encryption cipher + + + + + Could not encrypt data + + + + + QObject + + OS X Keychain error (OSStatus %1) + Ошибка связки ключей OS X (OSStatus %1) + + + %1 (OSStatus %2) + %1 (OSStatus %2) + + + + Access to keychain denied + Доступ к связке ключей запрещён + + + + No keyring daemon + Нет демона связки ключей + + + + Already unlocked + Уже разблокировано + + + + No such keyring + Связка ключей не найдена + + + + Bad arguments + Неверные аргументы + + + + I/O error + Ошибка ввода/вывода + + + + Cancelled + Отменено + + + + Keyring already exists + Связка ключей уже существует + + + + No match + Нет совпадений + + + + Unknown error + Неизвестная ошибка + + + + Entry not found + Запись не найдена + + + + error 0x%1: %2 + + + + diff --git a/ext/qtkeychain/translations/qtkeychain_zh.ts b/ext/qtkeychain/translations/qtkeychain_zh.ts new file mode 100644 index 0000000..e90c774 --- /dev/null +++ b/ext/qtkeychain/translations/qtkeychain_zh.ts @@ -0,0 +1,330 @@ + + + + + QKeychain::DeletePasswordJobPrivate + + + Password entry not found + + + + + Could not decrypt data + 無法解密資料 + + + + + Unknown error + 未知的錯誤 + + + + Could not open wallet: %1; %2 + 無法開啟錢包:%1; %2 + + + + Password not found + 找不到密碼 + + + + Could not open keystore + + + + + Could not remove private key from keystore + + + + + QKeychain::JobPrivate + + + Unknown error + 未知的錯誤 + + + + Access to keychain denied + 鑰匙圈存取被拒絕 + + + + QKeychain::PlainTextStore + + + Could not store data in settings: access error + + + + + Could not store data in settings: format error + + + + + Could not delete data from settings: access error + + + + + Could not delete data from settings: format error + + + + + Entry not found + 找不到項目 + + + + QKeychain::ReadPasswordJobPrivate + + + + Unknown error + 未知的錯誤 + + + + D-Bus is not running + D-Bus 不在執行中 + + + + No keychain service available + 沒有可用的鑰匙圈服務 + + + + Could not open wallet: %1; %2 + 無法開啟錢包:%1; %2 + + + + Access to keychain denied + 鑰匙圈存取被拒絕 + + + + Could not determine data type: %1; %2 + 無法判斷資料型別:%1; %2 + + + + Unsupported entry type 'Map' + 不支援的項目類型 'Map' + + + + Unknown kwallet entry type '%1' + 未知的 kwallet 項目類型 '%1' + + + Could not read password: %1; %2 + 無法讀取密碼:%1; %2 + + + + Password not found + 找不到密碼 + + + + + Entry not found + 找不到項目 + + + + Password entry not found + + + + + + Could not decrypt data + 無法解密資料 + + + + Could not open keystore + + + + + Could not retrieve private key from keystore + + + + + Could not create decryption cipher + + + + + QKeychain::WritePasswordJobPrivate + + + + Unknown error + 未知的錯誤 + + + + D-Bus is not running + D-Bus 不在執行中 + + + + Could not open wallet: %1; %2 + 無法開啟錢包:%1; %2 + + + Access to keychain denied + 鑰匙圈存取被拒絕 + + + Could not delete encrypted data from settings: access error + 無法從設定刪除加密資料:存取錯誤 + + + Could not delete encrypted data from settings: format error + 無法從設定刪除加密資料:格式錯誤 + + + + Credential size exceeds maximum size of %1 + + + + + Credential key exceeds maximum size of %1 + + + + + Writing credentials failed: Win32 error code %1 + + + + + Encryption failed + 加密失敗 + + + Could not store encrypted data in settings: access error + 無法將加密資料儲存至設定:存取錯誤 + + + Could not store encrypted data in settings: format error + 無法將加密資料儲存至設定:格式錯誤 + + + + Password not found + 找不到密碼 + + + + Could not open keystore + + + + + Could not create private key generator + + + + + Could not generate new private key + + + + + Could not retrieve private key from keystore + + + + + Could not create encryption cipher + + + + + Could not encrypt data + + + + + QObject + + + Access to keychain denied + 鑰匙圈存取被拒絕 + + + + No keyring daemon + 沒有可用的鑰匙圈背景程式 + + + + Already unlocked + 已解鎖 + + + + No such keyring + 鑰匙圈不存在 + + + + Bad arguments + 引數錯誤 + + + + I/O error + I/O 錯誤 + + + + Cancelled + 已取消 + + + + Keyring already exists + 鑰匙圈已存在 + + + + No match + 無相符項目 + + + + Unknown error + 未知的錯誤 + + + %1 (OSStatus %2) + %1 (OSStatus %2) + + + + Entry not found + 找不到項目 + + + + error 0x%1: %2 + + + + diff --git a/fuel.pro b/fuel.pro index 7e004f7..2423c5a 100644 --- a/fuel.pro +++ b/fuel.pro @@ -23,9 +23,6 @@ TEMPLATE = app win32 { RC_FILE = rsrc/fuel.rc LIBS += -luser32 -lshell32 -luuid - - # Prevent batch file being called multiple times - !build_pass:system(intl\convert.bat) } macx { @@ -53,7 +50,6 @@ unix:!macx { icon.files += rsrc/icons/fuel.png INSTALLS += target desktop icon - system(intl/convert.sh) } @@ -114,36 +110,7 @@ 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 - -DEFINES += QKEYCHAIN_STATICLIB - -unix:!macx { - QT += dbus - - 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 - LIBS += -framework CoreFoundation -framework Security -} - -win32 { - SOURCES += ext/qtkeychain/keychain_win.cpp - LIBS += -lCrypt32 -} - - - +include(ext/qtkeychain.pri) CODECFORTR = UTF-8 diff --git a/intl/convert.bat b/intl/convert.bat deleted file mode 100755 index 98b5b1d..0000000 --- a/intl/convert.bat +++ /dev/null @@ -1,20 +0,0 @@ -@echo off -setlocal EnableDelayedExpansion -set SCRIPTDIR=%~dp0% -set PRJDIR=%SCRIPTDIR%.. -set QTPATH=C:\Qt\5.5\mingw492_32 - -if NOT "%QTDIR%"=="" set QTPATH=%QTDIR% - -echo Using QT at %QTPATH% -echo Converting localizations -del /q %PRJDIR%\rsrc\intl\* -if not exist %PRJDIR%\rsrc\intl\ mkdir %PRJDIR%\rsrc\intl\ - -REM Convert all except the en_US which is the original text in the code - -for %%i in (de_DE el_GR es_ES fr_FR ru_RU pt_PT it_IT nl_NL ko_KR) do ( - %QTPATH%\bin\lrelease %PRJDIR%\intl\%%i.ts -qm %PRJDIR%\rsrc\intl\%%i.qm -) - -endlocal diff --git a/intl/convert.sh b/intl/convert.sh deleted file mode 100755 index 526dfc9..0000000 --- a/intl/convert.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/sh -SCRIPTDIR="$( cd "$( dirname "$0" )" && pwd )" -PRJDIR=$SCRIPTDIR/.. -INTLDIR=$SCRIPTDIR - -# Detect lrelease tool -if which lrelease-qt5 2>/dev/null; then - LRELEASE="lrelease-qt5" -elif which lrelease4 2>/dev/null; then - LRELEASE="lrelease4" -elif which lrelease 2>/dev/null; then - LRELEASE="lrelease" -else - echo "lrelease not found" - exit 1 -fi - -echo "Using ${LRELEASE}" - -echo "Converting localizations" - -rm -rf $PRJDIR/rsrc/intl -mkdir $PRJDIR/rsrc/intl - -for i in $INTLDIR/*.ts -do - BASE=`basename $i .ts` - - # Convert all except the en_US which is - # the original text in the code - if [ "$BASE" != "en_US" ]; then - echo "$TARGET" - $LRELEASE $i -qm $PRJDIR/rsrc/intl/$BASE.qm - fi -done - diff --git a/intl/de_DE.ts b/intl/de_DE.ts index 8626766..30a8be4 100644 --- a/intl/de_DE.ts +++ b/intl/de_DE.ts @@ -108,6 +108,11 @@ Released under the GNU GPL Local Repository + + + Path to the local repository file + Pfad der lokalen Repositorydatei + The URL of the HTTP proxy. Leave blank if not required @@ -118,11 +123,6 @@ Released under the GNU GPL HTTP Proxy - - - Path to the local repository file - Pfad der lokalen Repositorydatei - @@ -342,6 +342,11 @@ Released under the GNU GPL Main Toolbar Hauptwerkzeugleiste + + + &Refresh + + @@ -353,11 +358,6 @@ Released under the GNU GPL F5 F5 - - - &Refresh - - &Commit @@ -470,6 +470,11 @@ Released under the GNU GPL C&lone... + + + Clone a remote repository + + &Push @@ -490,6 +495,74 @@ Released under the GNU GPL Pull changes from the default remote repository + + + &Push to Remote + + + + + + Push changes to a remote repository + + + + + Pu&ll from Remote + + + + + + Pull changes from a remote repository + + + + + U&ndo + + + + + &Update + + + + + Update the workspace to a revision + + + + + &Modified Files + + + + + &Unchanged Files + + + + + Un&known Files + + + + + &Ignored Files + + + + + + View files as a list + + + + + &Stash Changes + + Create &Tag @@ -617,11 +690,6 @@ Released under the GNU GPL Abort the current operation - - - Clone a remote repository - - Push changes to the remote repository @@ -642,28 +710,6 @@ Released under the GNU GPL Ctrl+L Strg+L - - - &Push to Remote - - - - - - Push changes to a remote repository - - - - - Pu&ll from Remote - - - - - - Pull changes from a remote repository - - @@ -728,47 +774,6 @@ Released under the GNU GPL Open File Öffne Datei - - - U&ndo - - - - - &Update - - - - - Update the workspace to a revision - - - - - &Modified Files - - - - - &Unchanged Files - - - - - Un&known Files - - - - - &Ignored Files - - - - - - View files as a list - - Revert @@ -939,11 +944,6 @@ Released under the GNU GPL Rename the selected folder Ausgewählten Ordner umbenennen - - - &Stash Changes - - Stash changes @@ -1603,178 +1603,8 @@ Die folgenden Dateien werden in das Repository verschoben. Die folgenden Stashes werden entfernt - - QKeychain::ReadPasswordJobPrivate - - - Password not found - - - - - D-Bus is not running - - - - - Unknown error - - - - - No keychain service available - - - - - Could not open wallet: %1; %2 - - - - - Access to keychain denied - - - - - Could not determine data type: %1; %2 - - - - - - Entry not found - - - - - Unsupported entry type 'Map' - - - - - Unknown kwallet entry type '%1' - - - - - Could not read password: %1; %2 - - - - - Could not decrypt data - - - - - QKeychain::WritePasswordJobPrivate - - - D-Bus is not running - - - - - - Unknown error - - - - - - Could not open wallet: %1; %2 - - - - - Access to keychain denied - - - - - Could not delete encrypted data from settings: access error - - - - - Could not delete encrypted data from settings: format error - - - - - Encryption failed - - - - - Could not store encrypted data in settings: access error - - - - - Could not store encrypted data in settings: format error - - - QObject - - - - %1 (OSStatus %2) - - - - - Access to keychain denied - - - - - No keyring daemon - - - - - Already unlocked - - - - - No such keyring - - - - - Bad arguments - - - - - I/O error - - - - - Cancelled - - - - - Keyring already exists - - - - - No match - - - - - Unknown error - - Custom Action %0 diff --git a/intl/el_GR.ts b/intl/el_GR.ts index 2e7464c..f60d67d 100644 --- a/intl/el_GR.ts +++ b/intl/el_GR.ts @@ -108,6 +108,11 @@ Released under the GNU GPL Local Repository + + + Path to the local repository file + Διαδρομή για το αρχείο τοπικού αποθετηρίου + The URL of the HTTP proxy. Leave blank if not required @@ -118,11 +123,6 @@ Released under the GNU GPL HTTP Proxy - - - Path to the local repository file - Διαδρομή για το αρχείο τοπικού αποθετηρίου - @@ -342,6 +342,11 @@ Released under the GNU GPL Main Toolbar Κύρια μπάρα εργαλείων + + + &Refresh + + @@ -353,11 +358,6 @@ Released under the GNU GPL F5 F5 - - - &Refresh - - &Commit @@ -490,6 +490,68 @@ Released under the GNU GPL Pull changes from the default remote repository + + + &Push to Remote + + + + + + Push changes to a remote repository + + + + + Pu&ll from Remote + + + + + + Pull changes from a remote repository + + + + + U&ndo + + + + + &Update + + + + + Update the workspace to a revision + + + + + &Modified Files + + + + + &Unchanged Files + + + + + Un&known Files + + + + + &Ignored Files + + + + + &Stash Changes + + Create &Tag @@ -642,28 +704,6 @@ Released under the GNU GPL Ctrl+L Ctrl+L - - - &Push to Remote - - - - - - Push changes to a remote repository - - - - - Pu&ll from Remote - - - - - - Pull changes from a remote repository - - @@ -728,41 +768,6 @@ Released under the GNU GPL Open File Άνοιγμα Αρχείου - - - U&ndo - - - - - &Update - - - - - Update the workspace to a revision - - - - - &Modified Files - - - - - &Unchanged Files - - - - - Un&known Files - - - - - &Ignored Files - - @@ -939,11 +944,6 @@ Released under the GNU GPL Rename the selected folder Μετονομασία του επιλεγμένου φακέλου - - - &Stash Changes - - Stash changes @@ -1602,178 +1602,8 @@ The following files will be moved in the repository. Οι παρακάτω στοίβες θα διαγραφούν. - - QKeychain::ReadPasswordJobPrivate - - - Password not found - - - - - D-Bus is not running - - - - - Unknown error - - - - - No keychain service available - - - - - Could not open wallet: %1; %2 - - - - - Access to keychain denied - - - - - Could not determine data type: %1; %2 - - - - - - Entry not found - - - - - Unsupported entry type 'Map' - - - - - Unknown kwallet entry type '%1' - - - - - Could not read password: %1; %2 - - - - - Could not decrypt data - - - - - QKeychain::WritePasswordJobPrivate - - - D-Bus is not running - - - - - - Unknown error - - - - - - Could not open wallet: %1; %2 - - - - - Access to keychain denied - - - - - Could not delete encrypted data from settings: access error - - - - - Could not delete encrypted data from settings: format error - - - - - Encryption failed - - - - - Could not store encrypted data in settings: access error - - - - - Could not store encrypted data in settings: format error - - - QObject - - - - %1 (OSStatus %2) - - - - - Access to keychain denied - - - - - No keyring daemon - - - - - Already unlocked - - - - - No such keyring - - - - - Bad arguments - - - - - I/O error - - - - - Cancelled - - - - - Keyring already exists - - - - - No match - - - - - Unknown error - - Custom Action %0 diff --git a/intl/en_US.ts b/intl/en_US.ts index 6c93f53..61f14e2 100644 --- a/intl/en_US.ts +++ b/intl/en_US.ts @@ -108,6 +108,11 @@ Released under the GNU GPL Local Repository + + + Path to the local repository file + Path to the local repository file + The URL of the HTTP proxy. Leave blank if not required @@ -118,11 +123,6 @@ Released under the GNU GPL HTTP Proxy - - - Path to the local repository file - Path to the local repository file - @@ -342,6 +342,11 @@ Released under the GNU GPL Main Toolbar Main Toolbar + + + &Refresh + + @@ -353,11 +358,6 @@ Released under the GNU GPL F5 F5 - - - &Refresh - - &Commit @@ -490,6 +490,68 @@ Released under the GNU GPL Pull changes from the default remote repository + + + &Push to Remote + + + + + + Push changes to a remote repository + + + + + Pu&ll from Remote + + + + + + Pull changes from a remote repository + + + + + U&ndo + + + + + &Update + + + + + Update the workspace to a revision + + + + + &Modified Files + + + + + &Unchanged Files + + + + + Un&known Files + + + + + &Ignored Files + + + + + &Stash Changes + + Create &Tag @@ -642,28 +704,6 @@ Released under the GNU GPL Ctrl+L Ctrl+L - - - &Push to Remote - - - - - - Push changes to a remote repository - - - - - Pu&ll from Remote - - - - - - Pull changes from a remote repository - - @@ -728,41 +768,6 @@ Released under the GNU GPL Open File Open File - - - U&ndo - - - - - &Update - - - - - Update the workspace to a revision - - - - - &Modified Files - - - - - &Unchanged Files - - - - - Un&known Files - - - - - &Ignored Files - - @@ -939,11 +944,6 @@ Released under the GNU GPL Rename the selected folder Rename the selected folder - - - &Stash Changes - - Stash changes @@ -1602,178 +1602,8 @@ The following files will be moved in the repository. The following stashes will be deleted. - - QKeychain::ReadPasswordJobPrivate - - - Password not found - - - - - D-Bus is not running - - - - - Unknown error - - - - - No keychain service available - - - - - Could not open wallet: %1; %2 - - - - - Access to keychain denied - - - - - Could not determine data type: %1; %2 - - - - - - Entry not found - - - - - Unsupported entry type 'Map' - - - - - Unknown kwallet entry type '%1' - - - - - Could not read password: %1; %2 - - - - - Could not decrypt data - - - - - QKeychain::WritePasswordJobPrivate - - - D-Bus is not running - - - - - - Unknown error - - - - - - Could not open wallet: %1; %2 - - - - - Access to keychain denied - - - - - Could not delete encrypted data from settings: access error - - - - - Could not delete encrypted data from settings: format error - - - - - Encryption failed - - - - - Could not store encrypted data in settings: access error - - - - - Could not store encrypted data in settings: format error - - - QObject - - - - %1 (OSStatus %2) - - - - - Access to keychain denied - - - - - No keyring daemon - - - - - Already unlocked - - - - - No such keyring - - - - - Bad arguments - - - - - I/O error - - - - - Cancelled - - - - - Keyring already exists - - - - - No match - - - - - Unknown error - - Custom Action %0 diff --git a/intl/es_ES.ts b/intl/es_ES.ts index 6c9c121..a73ac59 100644 --- a/intl/es_ES.ts +++ b/intl/es_ES.ts @@ -108,6 +108,11 @@ Released under the GNU GPL Local Repository + + + Path to the local repository file + Ruta al archivo del depósito local + The URL of the HTTP proxy. Leave blank if not required @@ -118,11 +123,6 @@ Released under the GNU GPL HTTP Proxy - - - Path to the local repository file - Ruta al archivo del depósito local - @@ -342,6 +342,11 @@ Released under the GNU GPL Main Toolbar Barra principal de herramientas + + + &Refresh + + @@ -353,11 +358,6 @@ Released under the GNU GPL F5 F5 - - - &Refresh - - &Commit @@ -470,6 +470,11 @@ Released under the GNU GPL C&lone... + + + Clone a remote repository + + &Push @@ -490,6 +495,74 @@ Released under the GNU GPL Pull changes from the default remote repository + + + &Push to Remote + + + + + + Push changes to a remote repository + + + + + Pu&ll from Remote + + + + + + Pull changes from a remote repository + + + + + U&ndo + + + + + &Update + + + + + Update the workspace to a revision + + + + + &Modified Files + + + + + &Unchanged Files + + + + + Un&known Files + + + + + &Ignored Files + + + + + + View files as a list + + + + + &Stash Changes + + Create &Tag @@ -617,11 +690,6 @@ Released under the GNU GPL Abort the current operation - - - Clone a remote repository - - Push changes to the remote repository @@ -642,28 +710,6 @@ Released under the GNU GPL Ctrl+L Ctrl+L - - - &Push to Remote - - - - - - Push changes to a remote repository - - - - - Pu&ll from Remote - - - - - - Pull changes from a remote repository - - @@ -728,47 +774,6 @@ Released under the GNU GPL Open File Abrir Archivo - - - U&ndo - - - - - &Update - - - - - Update the workspace to a revision - - - - - &Modified Files - - - - - &Unchanged Files - - - - - Un&known Files - - - - - &Ignored Files - - - - - - View files as a list - - Revert @@ -939,11 +944,6 @@ Released under the GNU GPL Rename the selected folder Renombrar la carpeta seleccionada - - - &Stash Changes - - Stash changes @@ -1602,178 +1602,8 @@ Los siguientes archivos serán movidos al depósito. Serán borrados los siguientes cambios apartados - - QKeychain::ReadPasswordJobPrivate - - - Password not found - - - - - D-Bus is not running - - - - - Unknown error - - - - - No keychain service available - - - - - Could not open wallet: %1; %2 - - - - - Access to keychain denied - - - - - Could not determine data type: %1; %2 - - - - - - Entry not found - - - - - Unsupported entry type 'Map' - - - - - Unknown kwallet entry type '%1' - - - - - Could not read password: %1; %2 - - - - - Could not decrypt data - - - - - QKeychain::WritePasswordJobPrivate - - - D-Bus is not running - - - - - - Unknown error - - - - - - Could not open wallet: %1; %2 - - - - - Access to keychain denied - - - - - Could not delete encrypted data from settings: access error - - - - - Could not delete encrypted data from settings: format error - - - - - Encryption failed - - - - - Could not store encrypted data in settings: access error - - - - - Could not store encrypted data in settings: format error - - - QObject - - - - %1 (OSStatus %2) - - - - - Access to keychain denied - - - - - No keyring daemon - - - - - Already unlocked - - - - - No such keyring - - - - - Bad arguments - - - - - I/O error - - - - - Cancelled - - - - - Keyring already exists - - - - - No match - - - - - Unknown error - - Custom Action %0 diff --git a/intl/fr_FR.ts b/intl/fr_FR.ts index b56bfcd..b0bc67d 100644 --- a/intl/fr_FR.ts +++ b/intl/fr_FR.ts @@ -108,6 +108,11 @@ Released under the GNU GPL Local Repository + + + Path to the local repository file + Chemin vers le fichier du dépôt local + The URL of the HTTP proxy. Leave blank if not required @@ -118,11 +123,6 @@ Released under the GNU GPL HTTP Proxy - - - Path to the local repository file - Chemin vers le fichier du dépôt local - @@ -342,6 +342,11 @@ Released under the GNU GPL Main Toolbar Barre d'outils principale + + + &Refresh + + @@ -353,11 +358,6 @@ Released under the GNU GPL F5 F5 - - - &Refresh - - &Commit @@ -490,6 +490,68 @@ Released under the GNU GPL Pull changes from the default remote repository + + + &Push to Remote + + + + + + Push changes to a remote repository + + + + + Pu&ll from Remote + + + + + + Pull changes from a remote repository + + + + + U&ndo + + + + + &Update + + + + + Update the workspace to a revision + + + + + &Modified Files + + + + + &Unchanged Files + + + + + Un&known Files + + + + + &Ignored Files + + + + + &Stash Changes + + Create &Tag @@ -642,28 +704,6 @@ Released under the GNU GPL Ctrl+L Ctrl+L - - - &Push to Remote - - - - - - Push changes to a remote repository - - - - - Pu&ll from Remote - - - - - - Pull changes from a remote repository - - @@ -728,41 +768,6 @@ Released under the GNU GPL Open File Ouvrir le fichier - - - U&ndo - - - - - &Update - - - - - Update the workspace to a revision - - - - - &Modified Files - - - - - &Unchanged Files - - - - - Un&known Files - - - - - &Ignored Files - - @@ -939,11 +944,6 @@ Released under the GNU GPL Rename the selected folder Renommer le dossier sélectionné - - - &Stash Changes - - Stash changes @@ -1602,178 +1602,8 @@ Les fichiers suivants seront déplacés dans le dépôt. Les réserves suivantes seront supprimées. - - QKeychain::ReadPasswordJobPrivate - - - Password not found - - - - - D-Bus is not running - - - - - Unknown error - - - - - No keychain service available - - - - - Could not open wallet: %1; %2 - - - - - Access to keychain denied - - - - - Could not determine data type: %1; %2 - - - - - - Entry not found - - - - - Unsupported entry type 'Map' - - - - - Unknown kwallet entry type '%1' - - - - - Could not read password: %1; %2 - - - - - Could not decrypt data - - - - - QKeychain::WritePasswordJobPrivate - - - D-Bus is not running - - - - - - Unknown error - - - - - - Could not open wallet: %1; %2 - - - - - Access to keychain denied - - - - - Could not delete encrypted data from settings: access error - - - - - Could not delete encrypted data from settings: format error - - - - - Encryption failed - - - - - Could not store encrypted data in settings: access error - - - - - Could not store encrypted data in settings: format error - - - QObject - - - - %1 (OSStatus %2) - - - - - Access to keychain denied - - - - - No keyring daemon - - - - - Already unlocked - - - - - No such keyring - - - - - Bad arguments - - - - - I/O error - - - - - Cancelled - - - - - Keyring already exists - - - - - No match - - - - - Unknown error - - Custom Action %0 diff --git a/intl/it_IT.ts b/intl/it_IT.ts index a6017b5..f5120b2 100644 --- a/intl/it_IT.ts +++ b/intl/it_IT.ts @@ -108,6 +108,11 @@ Released under the GNU GPL Local Repository + + + Path to the local repository file + Percorso al file dell'archivio locale + The URL of the HTTP proxy. Leave blank if not required @@ -118,11 +123,6 @@ Released under the GNU GPL HTTP Proxy - - - Path to the local repository file - Percorso al file dell'archivio locale - @@ -342,6 +342,11 @@ Released under the GNU GPL Main Toolbar Barra degli strumenti principale + + + &Refresh + + @@ -353,11 +358,6 @@ Released under the GNU GPL F5 F5 - - - &Refresh - - &Commit @@ -490,6 +490,68 @@ Released under the GNU GPL Pull changes from the default remote repository + + + &Push to Remote + + + + + + Push changes to a remote repository + + + + + Pu&ll from Remote + + + + + + Pull changes from a remote repository + + + + + U&ndo + + + + + &Update + + + + + Update the workspace to a revision + + + + + &Modified Files + + + + + &Unchanged Files + + + + + Un&known Files + + + + + &Ignored Files + + + + + &Stash Changes + + Create &Tag @@ -642,28 +704,6 @@ Released under the GNU GPL Ctrl+L Ctrl+L - - - &Push to Remote - - - - - - Push changes to a remote repository - - - - - Pu&ll from Remote - - - - - - Pull changes from a remote repository - - @@ -728,41 +768,6 @@ Released under the GNU GPL Open File Apri file - - - U&ndo - - - - - &Update - - - - - Update the workspace to a revision - - - - - &Modified Files - - - - - &Unchanged Files - - - - - Un&known Files - - - - - &Ignored Files - - @@ -939,11 +944,6 @@ Released under the GNU GPL Rename the selected folder Rinomina la cartella selezionata - - - &Stash Changes - - Stash changes @@ -1602,178 +1602,8 @@ Saranno spostati nell'archivio i file seguenti: Le seguenti modiche riposte verranno rimosse - - QKeychain::ReadPasswordJobPrivate - - - Password not found - - - - - D-Bus is not running - - - - - Unknown error - - - - - No keychain service available - - - - - Could not open wallet: %1; %2 - - - - - Access to keychain denied - - - - - Could not determine data type: %1; %2 - - - - - - Entry not found - - - - - Unsupported entry type 'Map' - - - - - Unknown kwallet entry type '%1' - - - - - Could not read password: %1; %2 - - - - - Could not decrypt data - - - - - QKeychain::WritePasswordJobPrivate - - - D-Bus is not running - - - - - - Unknown error - - - - - - Could not open wallet: %1; %2 - - - - - Access to keychain denied - - - - - Could not delete encrypted data from settings: access error - - - - - Could not delete encrypted data from settings: format error - - - - - Encryption failed - - - - - Could not store encrypted data in settings: access error - - - - - Could not store encrypted data in settings: format error - - - QObject - - - - %1 (OSStatus %2) - - - - - Access to keychain denied - - - - - No keyring daemon - - - - - Already unlocked - - - - - No such keyring - - - - - Bad arguments - - - - - I/O error - - - - - Cancelled - - - - - Keyring already exists - - - - - No match - - - - - Unknown error - - Custom Action %0 diff --git a/intl/ko_KR.ts b/intl/ko_KR.ts index da44276..00abe85 100644 --- a/intl/ko_KR.ts +++ b/intl/ko_KR.ts @@ -108,6 +108,11 @@ Released under the GNU GPL Local Repository + + + Path to the local repository file + 복제해온 저장소 파일을 보관할 위치입니다. + The URL of the HTTP proxy. Leave blank if not required @@ -118,11 +123,6 @@ Released under the GNU GPL HTTP Proxy - - - Path to the local repository file - 복제해온 저장소 파일을 보관할 위치입니다. - @@ -342,6 +342,11 @@ Released under the GNU GPL Main Toolbar 주 메뉴 + + + &Refresh + + @@ -353,11 +358,6 @@ Released under the GNU GPL F5 F5 - - - &Refresh - - &Commit @@ -490,6 +490,68 @@ Released under the GNU GPL Pull changes from the default remote repository + + + &Push to Remote + + + + + + Push changes to a remote repository + + + + + Pu&ll from Remote + + + + + + Pull changes from a remote repository + + + + + U&ndo + + + + + &Update + + + + + Update the workspace to a revision + + + + + &Modified Files + + + + + &Unchanged Files + + + + + Un&known Files + + + + + &Ignored Files + + + + + &Stash Changes + + Create &Tag @@ -642,28 +704,6 @@ Released under the GNU GPL Ctrl+L Ctrl+L - - - &Push to Remote - - - - - - Push changes to a remote repository - - - - - Pu&ll from Remote - - - - - - Pull changes from a remote repository - - @@ -728,41 +768,6 @@ Released under the GNU GPL Open File 파일 열기 - - - U&ndo - - - - - &Update - - - - - Update the workspace to a revision - - - - - &Modified Files - - - - - &Unchanged Files - - - - - Un&known Files - - - - - &Ignored Files - - @@ -939,11 +944,6 @@ Released under the GNU GPL Rename the selected folder 폴더의 이름을 바꿉니다. - - - &Stash Changes - - Stash changes @@ -1602,178 +1602,8 @@ The following files will be moved in the repository. 쟁여둔 다음 변경사항을 지웁니다. - - QKeychain::ReadPasswordJobPrivate - - - Password not found - - - - - D-Bus is not running - - - - - Unknown error - - - - - No keychain service available - - - - - Could not open wallet: %1; %2 - - - - - Access to keychain denied - - - - - Could not determine data type: %1; %2 - - - - - - Entry not found - - - - - Unsupported entry type 'Map' - - - - - Unknown kwallet entry type '%1' - - - - - Could not read password: %1; %2 - - - - - Could not decrypt data - - - - - QKeychain::WritePasswordJobPrivate - - - D-Bus is not running - - - - - - Unknown error - - - - - - Could not open wallet: %1; %2 - - - - - Access to keychain denied - - - - - Could not delete encrypted data from settings: access error - - - - - Could not delete encrypted data from settings: format error - - - - - Encryption failed - - - - - Could not store encrypted data in settings: access error - - - - - Could not store encrypted data in settings: format error - - - QObject - - - - %1 (OSStatus %2) - - - - - Access to keychain denied - - - - - No keyring daemon - - - - - Already unlocked - - - - - No such keyring - - - - - Bad arguments - - - - - I/O error - - - - - Cancelled - - - - - Keyring already exists - - - - - No match - - - - - Unknown error - - Custom Action %0 diff --git a/intl/nl_NL.ts b/intl/nl_NL.ts index 9516315..f54c5f7 100644 --- a/intl/nl_NL.ts +++ b/intl/nl_NL.ts @@ -108,6 +108,11 @@ Released under the GNU GPL Local Repository + + + Path to the local repository file + Pad naar het lokale archief + The URL of the HTTP proxy. Leave blank if not required @@ -118,11 +123,6 @@ Released under the GNU GPL HTTP Proxy - - - Path to the local repository file - Pad naar het lokale archief - @@ -342,6 +342,11 @@ Released under the GNU GPL Main Toolbar Hoofd taakbalk + + + &Refresh + + @@ -353,11 +358,6 @@ Released under the GNU GPL F5 F5 - - - &Refresh - - &Commit @@ -490,6 +490,68 @@ Released under the GNU GPL Pull changes from the default remote repository + + + &Push to Remote + + + + + + Push changes to a remote repository + + + + + Pu&ll from Remote + + + + + + Pull changes from a remote repository + + + + + U&ndo + + + + + &Update + + + + + Update the workspace to a revision + + + + + &Modified Files + + + + + &Unchanged Files + + + + + Un&known Files + + + + + &Ignored Files + + + + + &Stash Changes + + Create &Tag @@ -642,28 +704,6 @@ Released under the GNU GPL Ctrl+L Ctrl+L - - - &Push to Remote - - - - - - Push changes to a remote repository - - - - - Pu&ll from Remote - - - - - - Pull changes from a remote repository - - @@ -728,41 +768,6 @@ Released under the GNU GPL Open File Open Bestand - - - U&ndo - - - - - &Update - - - - - Update the workspace to a revision - - - - - &Modified Files - - - - - &Unchanged Files - - - - - Un&known Files - - - - - &Ignored Files - - @@ -939,11 +944,6 @@ Released under the GNU GPL Rename the selected folder Geselecteerde map hernoemen - - - &Stash Changes - - Stash changes @@ -1602,178 +1602,8 @@ De volgende bestanden worden verplaatst naar het archief. De volgende verborgen veranderingen zullen worden verwijderd. - - QKeychain::ReadPasswordJobPrivate - - - Password not found - - - - - D-Bus is not running - - - - - Unknown error - - - - - No keychain service available - - - - - Could not open wallet: %1; %2 - - - - - Access to keychain denied - - - - - Could not determine data type: %1; %2 - - - - - - Entry not found - - - - - Unsupported entry type 'Map' - - - - - Unknown kwallet entry type '%1' - - - - - Could not read password: %1; %2 - - - - - Could not decrypt data - - - - - QKeychain::WritePasswordJobPrivate - - - D-Bus is not running - - - - - - Unknown error - - - - - - Could not open wallet: %1; %2 - - - - - Access to keychain denied - - - - - Could not delete encrypted data from settings: access error - - - - - Could not delete encrypted data from settings: format error - - - - - Encryption failed - - - - - Could not store encrypted data in settings: access error - - - - - Could not store encrypted data in settings: format error - - - QObject - - - - %1 (OSStatus %2) - - - - - Access to keychain denied - - - - - No keyring daemon - - - - - Already unlocked - - - - - No such keyring - - - - - Bad arguments - - - - - I/O error - - - - - Cancelled - - - - - Keyring already exists - - - - - No match - - - - - Unknown error - - Custom Action %0 diff --git a/intl/pt_PT.ts b/intl/pt_PT.ts index a7879e1..8893c36 100644 --- a/intl/pt_PT.ts +++ b/intl/pt_PT.ts @@ -108,6 +108,11 @@ Released under the GNU GPL Local Repository + + + Path to the local repository file + Caminho para o ficheiro do repositório local + The URL of the HTTP proxy. Leave blank if not required @@ -118,11 +123,6 @@ Released under the GNU GPL HTTP Proxy - - - Path to the local repository file - Caminho para o ficheiro do repositório local - @@ -342,6 +342,11 @@ Released under the GNU GPL Main Toolbar Barra de ferramentas + + + &Refresh + + @@ -353,11 +358,6 @@ Released under the GNU GPL F5 F5 - - - &Refresh - - &Commit @@ -490,6 +490,68 @@ Released under the GNU GPL Pull changes from the default remote repository + + + &Push to Remote + + + + + + Push changes to a remote repository + + + + + Pu&ll from Remote + + + + + + Pull changes from a remote repository + + + + + U&ndo + + + + + &Update + + + + + Update the workspace to a revision + + + + + &Modified Files + + + + + &Unchanged Files + + + + + Un&known Files + + + + + &Ignored Files + + + + + &Stash Changes + + Create &Tag @@ -642,28 +704,6 @@ Released under the GNU GPL Ctrl+L Ctrl+L - - - &Push to Remote - - - - - - Push changes to a remote repository - - - - - Pu&ll from Remote - - - - - - Pull changes from a remote repository - - @@ -728,41 +768,6 @@ Released under the GNU GPL Open File Abrir ficheiro - - - U&ndo - - - - - &Update - - - - - Update the workspace to a revision - - - - - &Modified Files - - - - - &Unchanged Files - - - - - Un&known Files - - - - - &Ignored Files - - @@ -939,11 +944,6 @@ Released under the GNU GPL Rename the selected folder Renomear a pasta seleccionada - - - &Stash Changes - - Stash changes @@ -1602,178 +1602,8 @@ Os ficheiros seguintes irão ser movidos no repositório. Os seguintes armazéns irão ser eliminados. - - QKeychain::ReadPasswordJobPrivate - - - Password not found - - - - - D-Bus is not running - - - - - Unknown error - - - - - No keychain service available - - - - - Could not open wallet: %1; %2 - - - - - Access to keychain denied - - - - - Could not determine data type: %1; %2 - - - - - - Entry not found - - - - - Unsupported entry type 'Map' - - - - - Unknown kwallet entry type '%1' - - - - - Could not read password: %1; %2 - - - - - Could not decrypt data - - - - - QKeychain::WritePasswordJobPrivate - - - D-Bus is not running - - - - - - Unknown error - - - - - - Could not open wallet: %1; %2 - - - - - Access to keychain denied - - - - - Could not delete encrypted data from settings: access error - - - - - Could not delete encrypted data from settings: format error - - - - - Encryption failed - - - - - Could not store encrypted data in settings: access error - - - - - Could not store encrypted data in settings: format error - - - QObject - - - - %1 (OSStatus %2) - - - - - Access to keychain denied - - - - - No keyring daemon - - - - - Already unlocked - - - - - No such keyring - - - - - Bad arguments - - - - - I/O error - - - - - Cancelled - - - - - Keyring already exists - - - - - No match - - - - - Unknown error - - Custom Action %0 diff --git a/intl/ru_RU.ts b/intl/ru_RU.ts index 7c5d9b5..d5cd39c 100644 --- a/intl/ru_RU.ts +++ b/intl/ru_RU.ts @@ -108,6 +108,11 @@ Released under the GNU GPL Local Repository + + + Path to the local repository file + Путь к локальному хранилищу + The URL of the HTTP proxy. Leave blank if not required @@ -118,11 +123,6 @@ Released under the GNU GPL HTTP Proxy - - - Path to the local repository file - Путь к локальному хранилищу - @@ -342,6 +342,11 @@ Released under the GNU GPL Main Toolbar Панель + + + &Refresh + + @@ -353,11 +358,6 @@ Released under the GNU GPL F5 F5 - - - &Refresh - - &Commit @@ -490,6 +490,68 @@ Released under the GNU GPL Pull changes from the default remote repository + + + &Push to Remote + + + + + + Push changes to a remote repository + + + + + Pu&ll from Remote + + + + + + Pull changes from a remote repository + + + + + U&ndo + + + + + &Update + + + + + Update the workspace to a revision + + + + + &Modified Files + + + + + &Unchanged Files + + + + + Un&known Files + + + + + &Ignored Files + + + + + &Stash Changes + + Create &Tag @@ -642,28 +704,6 @@ Released under the GNU GPL Ctrl+L Ctrl+L - - - &Push to Remote - - - - - - Push changes to a remote repository - - - - - Pu&ll from Remote - - - - - - Pull changes from a remote repository - - @@ -728,41 +768,6 @@ Released under the GNU GPL Open File Открыть Файл - - - U&ndo - - - - - &Update - - - - - Update the workspace to a revision - - - - - &Modified Files - - - - - &Unchanged Files - - - - - Un&known Files - - - - - &Ignored Files - - @@ -939,11 +944,6 @@ Released under the GNU GPL Rename the selected folder Переименовать выбранную директорию - - - &Stash Changes - - Stash changes @@ -1602,178 +1602,8 @@ The following files will be moved in the repository. Следующие изменения в Буфере будут удалены - - QKeychain::ReadPasswordJobPrivate - - - Password not found - - - - - D-Bus is not running - - - - - Unknown error - - - - - No keychain service available - - - - - Could not open wallet: %1; %2 - - - - - Access to keychain denied - - - - - Could not determine data type: %1; %2 - - - - - - Entry not found - - - - - Unsupported entry type 'Map' - - - - - Unknown kwallet entry type '%1' - - - - - Could not read password: %1; %2 - - - - - Could not decrypt data - - - - - QKeychain::WritePasswordJobPrivate - - - D-Bus is not running - - - - - - Unknown error - - - - - - Could not open wallet: %1; %2 - - - - - Access to keychain denied - - - - - Could not delete encrypted data from settings: access error - - - - - Could not delete encrypted data from settings: format error - - - - - Encryption failed - - - - - Could not store encrypted data in settings: access error - - - - - Could not store encrypted data in settings: format error - - - QObject - - - - %1 (OSStatus %2) - - - - - Access to keychain denied - - - - - No keyring daemon - - - - - Already unlocked - - - - - No such keyring - - - - - Bad arguments - - - - - I/O error - - - - - Cancelled - - - - - Keyring already exists - - - - - No match - - - - - Unknown error - - Custom Action %0 diff --git a/intl/update.sh b/intl/update.sh deleted file mode 100755 index 6ff1aa7..0000000 --- a/intl/update.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh - -# Update existing translations -lupdate-qt5 ../fuel.pro - diff --git a/rsrc/resources.qrc b/rsrc/resources.qrc index d47787a..cdbbc99 100644 --- a/rsrc/resources.qrc +++ b/rsrc/resources.qrc @@ -156,22 +156,24 @@ icons/Zoom In-01.png icons/Zoom Out-01.png - - intl/el_GR.qm - intl/de_DE.qm - intl/es_ES.qm - intl/fr_FR.qm - intl/ru_RU.qm - intl/pt_PT.qm - intl/it_IT.qm - intl/nl_NL.qm - intl/ko_KR.qm - - - docs/Translators.txt - docs/Licenses.txt - - - ../manifest.uuid - + + + intl/el_GR.qm + intl/de_DE.qm + intl/es_ES.qm + intl/fr_FR.qm + intl/ru_RU.qm + intl/pt_PT.qm + intl/it_IT.qm + intl/nl_NL.qm + intl/ko_KR.qm + + <--> + + docs/Translators.txt + docs/Licenses.txt + + + ../manifest.uuid +