pax_global_header00006660000000000000000000000064151744265310014522gustar00rootroot0000000000000052 comment=aa6da344e1a20b9194e12bace3665caeea6b6304 frankosterfeld-qtkeychain-8ce634f/000077500000000000000000000000001517442653100173155ustar00rootroot00000000000000frankosterfeld-qtkeychain-8ce634f/.clang-format000066400000000000000000000074241517442653100216770ustar00rootroot00000000000000# Copyright (C) 2016 Olivier Goffart # # You may use this file under the terms of the 3-clause BSD license. # See the file LICENSE from this package for details. # This is the clang-format configuration style to be used by Qt, # based on the rules from https://wiki.qt.io/Qt_Coding_Style and # https://wiki.qt.io/Coding_Conventions --- # Webkit style was loosely based on the Qt style BasedOnStyle: WebKit Standard: c++11 # Column width is limited to 100 in accordance with Qt Coding Style. # https://wiki.qt.io/Qt_Coding_Style # Note that this may be changed at some point in the future. ColumnLimit: 100 # How much weight do extra characters after the line length limit have. # PenaltyExcessCharacter: 4 # Disable reflow of some specific comments # qdoc comments: indentation rules are different. # Translation comments and SPDX license identifiers are also excluded. CommentPragmas: "^!|^:|^ SPDX-License-Identifier:" # We want a space between the type and the star for pointer types. PointerBindsToType: false # We generally use "template <" with space. SpaceAfterTemplateKeyword: true # We want to break before the operators, but not before a '='. BreakBeforeBinaryOperators: NonAssignment # Braces are usually attached, but not after functions or class declarations. BreakBeforeBraces: Custom BraceWrapping: AfterClass: true AfterControlStatement: false AfterEnum: false AfterFunction: true AfterNamespace: false AfterObjCDeclaration: false AfterStruct: true AfterUnion: false BeforeCatch: false BeforeElse: false IndentBraces: false # When constructor initializers do not fit on one line, put them each on a new line. ConstructorInitializerAllOnOneLineOrOnePerLine: true # Indent initializers by 4 spaces ConstructorInitializerIndentWidth: 4 # Indent width for line continuations. ContinuationIndentWidth: 8 # No indentation for namespaces. NamespaceIndentation: None # Allow indentation for preprocessing directives (if/ifdef/endif). https://reviews.llvm.org/rL312125 IndentPPDirectives: AfterHash # We only indent with 2 spaces for preprocessor directives PPIndentWidth: 2 # Horizontally align arguments after an open bracket. # The coding style does not specify the following, but this is what gives # results closest to the existing code. AlignAfterOpenBracket: true AlwaysBreakTemplateDeclarations: true # Ideally we should also allow less short function in a single line, but # clang-format does not handle that. AllowShortFunctionsOnASingleLine: Inline # The coding style specifies some include order categories, but also tells to # separate categories with an empty line. It does not specify the order within # the categories. Since the SortInclude feature of clang-format does not # re-order includes separated by empty lines, the feature is not used. SortIncludes: false # macros for which the opening brace stays attached. ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH, forever, Q_FOREVER, QBENCHMARK, QBENCHMARK_ONCE ] # Break constructor initializers before the colon and after the commas. BreakConstructorInitializers: BeforeColon # Add "// namespace " comments on closing brace for a namespace # Ignored for namespaces that qualify as a short namespace, # see 'ShortNamespaceLines' FixNamespaceComments: true # Definition of how short a short namespace is, default 1 ShortNamespaceLines: 1 # When escaping newlines in a macro attach the '\' as far left as possible, e.g. ##define a \ # something; \ # other; \ # thelastlineislong; AlignEscapedNewlines: Left # Avoids the addition of a space between an identifier and the # initializer list in list-initialization. SpaceBeforeCpp11BracedList: false --- # Use the Google-based style for .proto files. Language: Proto BasedOnStyle: Google IndentWidth: 4 ColumnLimit: 100 frankosterfeld-qtkeychain-8ce634f/.github/000077500000000000000000000000001517442653100206555ustar00rootroot00000000000000frankosterfeld-qtkeychain-8ce634f/.github/workflows/000077500000000000000000000000001517442653100227125ustar00rootroot00000000000000frankosterfeld-qtkeychain-8ce634f/.github/workflows/build.yml000066400000000000000000000171511517442653100245410ustar00rootroot00000000000000name: Build on: push: branches: - main pull_request: workflow_dispatch: jobs: build: strategy: fail-fast: false matrix: os: [windows-latest, ubuntu-latest, macos-latest] compiler: [msvc, gcc, clang] qt_version: [qt5, qt6] exclude: - os: ubuntu-latest compiler: msvc - os: macos-latest compiler: gcc - os: macos-latest compiler: msvc - os: windows-latest compiler: clang runs-on: ${{ matrix.os }} steps: - name: Checkout repository uses: actions/checkout@v2 - name: Setup CMake and Ninja uses: lukka/get-cmake@latest - if: matrix.compiler == 'msvc' uses: ilammy/msvc-dev-cmd@v1 - name: Install Dependencies (Windows only) if: runner.os == 'Windows' uses: crazy-max/ghaction-chocolatey@v3 with: args: install pkgconfiglite - name: Install Qt (Mac/Linux only) if: runner.os != 'Windows' run: | if [ '${{ runner.os }}' == 'macOS' ]; then if [ '${{ matrix.qt_version }}' == 'qt6' ]; then brew install qt6 pkg-config brew link qt6 --force echo "$(brew --prefix qt6)/bin" >> $GITHUB_PATH else brew install qt5 pkg-config brew link qt5 --force echo "$(brew --prefix qt5)/bin" >> $GITHUB_PATH fi elif [ '${{ runner.os }}' == 'Linux' ]; then sudo apt-get update if [ '${{ matrix.qt_version }}' == 'qt6' ]; then sudo apt-get install -y qt6-base-dev qtchooser qmake6 qt6-base-dev-tools qt6-tools-dev pkg-config libsecret-1-dev else sudo apt-get install -y qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools qttools5-dev pkg-config libsecret-1-dev fi fi - name: Set AQT Version and Arch (Windows only) if: runner.os == 'Windows' id: set_qt_arch shell: pwsh run: | $qtVer = "${{ matrix.qt_version == 'qt5' && '5.15.2' || '6.5.3' }}" $qtArch = "${{ matrix.compiler == 'msvc' && 'msvc2019_64' || matrix.qt_version == 'qt5' && matrix.compiler == 'gcc' && 'mingw81' || matrix.qt_version == 'qt6' && matrix.compiler == 'gcc' && 'mingw' }}" echo "QT_VER=$qtVer" >> $env:GITHUB_OUTPUT echo "QT_ARCH=$qtArch" >> $env:GITHUB_OUTPUT - name: Install Qt (Windows only) if: runner.os == 'Windows' uses: jurplel/install-qt-action@v4 with: version: ${{ steps.set_qt_arch.outputs.QT_VER }} arch: win64_${{ steps.set_qt_arch.outputs.QT_ARCH }} dir: 'C:\' cache: true tools: 'tools_mingw1310' - name: Setup Build Directory run: | mkdir -p ${{ github.workspace }}/work/build/${{ github.event.repository.name }} - name: Run CMake (Windows only) if: runner.os == 'Windows' shell: pwsh run: | if ( '${{ matrix.compiler }}' -eq 'msvc' ) { $generator = "NMake Makefiles" } else { $generator = "MinGW Makefiles" } if ( '${{ matrix.compiler }}' -eq 'gcc' ) { echo "C:/Qt/Tools/mingw1310_64/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append $env:PATH = "C:\Qt\Tools\mingw1310_64\bin;$env:PATH" } cd "${{ github.workspace }}/work/build/${{ github.event.repository.name }}" cmake -G $generator "${{ github.workspace }}" ` -DCMAKE_BUILD_TYPE=Release ` -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" ` -DCMAKE_PREFIX_PATH=$env:QT_ROOT_DIR ` ${{ matrix.qt_version == 'qt6' && '-DBUILD_WITH_QT6=true' || '' }} - name: Run CMake (Mac/Linux Only) if: runner.os != 'Windows' run: | cd ${{ github.workspace }}/work/build/${{ github.event.repository.name }} cmake -G Ninja ${{ github.workspace }} \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" \ ${{ matrix.qt_version == 'qt6' && '-DBUILD_WITH_QT6=true' || '' }} - name: Build and Install run: | cd ${{ github.workspace }}/work/build/${{ github.event.repository.name }} cmake --build . cmake --install . --prefix "${{ github.workspace }}/install" - name: Upload Artifact uses: actions/upload-artifact@v4 with: name: build-artifact ${{ matrix.os }} ${{ matrix.compiler }} ${{ matrix.qt_version }} path: ${{ github.workspace }}/work/build/${{ github.event.repository.name }} build-android: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 - name: Setup Java uses: actions/setup-java@v4 with: distribution: temurin java-version: 17 - name: Setup CMake and Ninja uses: lukka/get-cmake@latest - name: Install Qt for Android (arm64-v8a) uses: jurplel/install-qt-action@v4 with: version: '6.8.3' target: android arch: android_arm64_v8a cache: true extra: --autodesktop - name: Build and install qtkeychain (Android) run: | cmake -S ${{ github.workspace }} -B build/qtkeychain \ -DCMAKE_TOOLCHAIN_FILE=$QT_ROOT_DIR/lib/cmake/Qt6/qt.toolchain.cmake \ -DCMAKE_BUILD_TYPE=Release \ -DBUILD_WITH_QT6=ON \ -DBUILD_TRANSLATIONS=OFF cmake --build build/qtkeychain cmake --install build/qtkeychain --prefix ${{ github.workspace }}/install - name: Build TestAppExample (Android) run: | cmake -S ${{ github.workspace }}/TestAppExample -B build/testapp \ -DCMAKE_TOOLCHAIN_FILE=$QT_ROOT_DIR/lib/cmake/Qt6/qt.toolchain.cmake \ -DCMAKE_BUILD_TYPE=Release \ -DQt6Keychain_DIR=${{ github.workspace }}/install/lib/cmake/Qt6Keychain \ -DBUILD_WITH_QT6=ON cmake --build build/testapp build-ios: runs-on: macos-latest steps: - name: Checkout repository uses: actions/checkout@v4 - name: Setup CMake and Ninja uses: lukka/get-cmake@latest - name: Install Qt for iOS uses: jurplel/install-qt-action@v4 with: version: '6.8.3' target: ios cache: true extra: --autodesktop - name: Build and install qtkeychain (iOS) run: | cmake -S ${{ github.workspace }} -B build/qtkeychain \ -G Xcode \ -DCMAKE_TOOLCHAIN_FILE=$QT_ROOT_DIR/lib/cmake/Qt6/qt.toolchain.cmake \ -DBUILD_WITH_QT6=ON \ -DBUILD_TRANSLATIONS=OFF \ -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=NO \ -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED=NO cmake --build build/qtkeychain --config Release cmake --install build/qtkeychain --prefix ${{ github.workspace }}/install --config Release - name: Build TestAppExample (iOS) run: | cmake -S ${{ github.workspace }}/TestAppExample -B build/testapp \ -G Xcode \ -DCMAKE_TOOLCHAIN_FILE=$QT_ROOT_DIR/lib/cmake/Qt6/qt.toolchain.cmake \ -DQt6Keychain_DIR=${{ github.workspace }}/install/lib/cmake/Qt6Keychain \ -DBUILD_WITH_QT6=ON \ -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=NO \ -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED=NO cmake --build build/testapp --config Release frankosterfeld-qtkeychain-8ce634f/.gitignore000066400000000000000000000026741517442653100213160ustar00rootroot00000000000000# This file is used to ignore files which are generated # ---------------------------------------------------------------------------- *~ *.autosave *.a *.core *.moc *.o *.obj *.orig *.rej *.so *.so.* *_pch.h.cpp *_resource.rc *.qm .#* *.*# core !core/ tags .DS_Store .directory *.debug Makefile* *.prl *.app moc_*.cpp ui_*.h qrc_*.cpp Thumbs.db *.res *.rc /.qmake.cache /.qmake.stash # qtcreator generated files *.pro.user* # xemacs temporary files *.flc # Vim temporary files .*.swp # Visual Studio generated files *.ib_pdb_index *.idb *.ilk *.pdb *.sln *.suo *.vcproj *vcproj.*.*.user *.ncb *.sdf *.opensdf *.vcxproj *vcxproj.* # MinGW generated files *.Debug *.Release # Python byte code *.pyc # Binaries # -------- *.dll *.exe #CMake files CMakeCache.txt CMakeFiles 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 #Mac build files qtkeychain.xcodeproj qtkeychain.build #Temporary files *.sw? /build-*/ /compile_commands.json frankosterfeld-qtkeychain-8ce634f/CMakeLists.txt000066400000000000000000000201231517442653100220530ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.16) set(QTKEYCHAIN_VERSION 0.16.0) set(QTKEYCHAIN_SOVERSION 1) project(qtkeychain VERSION ${QTKEYCHAIN_VERSION} LANGUAGES CXX) if(BUILD_WITH_QT6) set(CMAKE_CXX_STANDARD 17) # qt6 requires a c++17 compiler else() set(CMAKE_CXX_STANDARD 11) endif() include(FindPkgConfig) ### # write binaries and libraries into a shared folder, this simplifies the execution of tests on Windows # see https://github.com/KDE/extra-cmake-modules/blob/b3a13868f7f54ab0b7ac19fd26b6dfead8907d25/kde-modules/KDECMakeSettings.cmake#L256C1-L258C69 set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/Modules") include(GNUInstallDirs) include(GenerateExportHeader) include(CMakePackageConfigHelpers) include(ECMSetupVersion) include(ECMGeneratePriFile) include(CMakeDependentOption) if(CMAKE_SYSTEM_NAME STREQUAL Android) set(ANDROID 1) endif() if(CMAKE_SYSTEM_NAME STREQUAL Haiku) set(HAIKU 1) endif() option(BUILD_WITH_QT6 "Build qtkeychain with Qt 6" OFF) option(BUILD_TEST_APPLICATION "Build test application (CLI)" OFF) option(BUILD_QTQUICK_DEMO "Build QtQuick demo application" OFF) option(BUILD_TRANSLATIONS "Build translations" ON) CMAKE_DEPENDENT_OPTION(BUILD_SHARED_LIBS "Build dynamic library" ON "NOT IOS" OFF) if(QTKEYCHAIN_STATIC) set(BUILD_SHARED_LIBS OFF) message(WARNING "QTKEYCHAIN_STATIC is deprecated. Use BUILD_SHARED_LIBS=OFF instead.") endif() CMAKE_DEPENDENT_OPTION(BUILD_TRANSLATIONS_AS_RESOURCES "Bundle translations with the library" OFF "BUILD_TRANSLATIONS" OFF) if (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 AND NOT EMSCRIPTEN) 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() set(QTCORE_LIBRARIES ${Qt5Core_LIBRARIES}) include_directories(${Qt5Core_INCLUDE_DIRS}) else() find_package(Qt6 COMPONENTS Core REQUIRED) set(QTKEYCHAIN_VERSION_INFIX 6) if(UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU AND NOT EMSCRIPTEN) find_package(Qt6 COMPONENTS DBus REQUIRED) set(QTDBUS_LIBRARIES Qt6::DBus) 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() set(QTCORE_LIBRARIES Qt6::Core) endif() set(QTKEYCHAIN_TARGET_NAME qt${QTKEYCHAIN_VERSION_INFIX}keychain) add_subdirectory(qtkeychain) ### ### Translations ### set(qtkeychain_TR_FILES translations/qtkeychain_de.ts translations/qtkeychain_fr.ts translations/qtkeychain_ka.ts translations/qtkeychain_ro.ts translations/qtkeychain_ru.ts translations/qtkeychain_sv.ts translations/qtkeychain_zh.ts ) file(GLOB qtkeychain_TR_SOURCES qtkeychain/*.cpp qtkeychain/*.h qtkeychain/*.ui) if ( BUILD_TRANSLATIONS ) qt_create_translation(qtkeychain_MESSAGES ${qtkeychain_TR_SOURCES} ${qtkeychain_TR_FILES}) qt_add_translation(qtkeychain_QM_FILES ${qtkeychain_TR_FILES}) add_custom_target(messages DEPENDS ${qtkeychain_MESSAGES}) add_custom_target(translations DEPENDS ${qtkeychain_QM_FILES} messages) # https://github.com/frankosterfeld/qtkeychain/issues/185 add_dependencies(${QTKEYCHAIN_TARGET_NAME} translations) if (BUILD_TRANSLATIONS_AS_RESOURCES) set(QM_FILE_LIST "") foreach(FILE ${qtkeychain_QM_FILES}) list(APPEND QM_FILE_LIST "${FILE}") endforeach() string(REPLACE ";" "" QM_FILE_LIST ${QM_FILE_LIST}) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/translations/translations.qrc.in ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc) target_sources(${QTKEYCHAIN_TARGET_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc) else() if(QTKEYCHAIN_VERSION_INFIX EQUAL 5 AND QT_TRANSLATIONS_DIR AND NOT QTKEYCHAIN_TRANSLATIONS_DIR) # Back compatibility with pre-0.11 versions message (WARNING "QT_TRANSLATIONS_DIR is deprecated, use QTKEYCHAIN_TRANSLATIONS_DIR instead") set(QTKEYCHAIN_TRANSLATIONS_DIR ${QT_TRANSLATIONS_DIR} CACHE PATH "The location of the QtKeychain translations" FORCE) else() set(QTKEYCHAIN_TRANSLATIONS_DIR ${CMAKE_INSTALL_DATADIR}/qt${QTKEYCHAIN_VERSION_INFIX}keychain/translations CACHE PATH "The location of the QtKeychain translations" ) endif() install(FILES ${qtkeychain_QM_FILES} DESTINATION ${QTKEYCHAIN_TRANSLATIONS_DIR}) endif() endif( BUILD_TRANSLATIONS ) ### ### Test applications ### if(BUILD_TEST_APPLICATION) set( testclient_LIBRARIES ${QTKEYCHAIN_TARGET_NAME} ) if(APPLE) list(APPEND testclient_LIBRARIES "-framework Cocoa") if (BUILD_WITH_QT6) find_package(Qt6 COMPONENTS Gui REQUIRED) list(APPEND testclient_LIBRARIES Qt6::Gui) else() find_package(Qt5 COMPONENTS Gui REQUIRED) list(APPEND testclient_LIBRARIES Qt5::Gui) endif() endif() add_executable( testclient testclient.cpp ) target_link_libraries( testclient ${testclient_LIBRARIES}) endif() if(BUILD_QTQUICK_DEMO) add_subdirectory(TestAppExample) endif() include(CTest) if(BUILD_TESTING AND NOT ANDROID AND NOT IOS) add_subdirectory(autotest) endif() ### ### CMake config file ### 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}) install(EXPORT Qt${QTKEYCHAIN_VERSION_INFIX}KeychainLibraryDepends DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Qt${QTKEYCHAIN_VERSION_INFIX}Keychain" ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfig.cmake ${CMAKE_CURRENT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfigVersion.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Qt${QTKEYCHAIN_VERSION_INFIX}Keychain ) frankosterfeld-qtkeychain-8ce634f/COPYING000066400000000000000000000024751517442653100203600ustar00rootroot00000000000000Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. frankosterfeld-qtkeychain-8ce634f/ChangeLog000066400000000000000000000131011517442653100210630ustar00rootroot00000000000000ChangeLog ========= version 0.16.0 (release 2026-04-29) - Add support for selecting backend via environment variable (Balló György) - Use default DBus timeout for KWallet check (Nicolas Fella) - Fix the crash caused by timeout when reading or writing keychain on macOS (yaoxp) - Fix restore-after-deletion issue by creating QKeychain jobs dynamically (Saif Khalifa) - Added Swedish translation (Daniel Nylander) - Added Georgian translation (Ekaterine Papava) - Fixes for various build/build system issues version 0.15.0 (release 2025-01-16) - Windows: Increase size of possible payloads (Hannah von Reth ) - KWallet: Increase timeout when opening wallet (David Faure ) - Haiku: Fix reading passwords (Joshua Goins ) - Add unit tests (Hannah von Reth ) - Windows build fixes (Nerixyz ) - Fix translation creation (Christophe Marin ) version 0.14.3 (release 2024-05-03) - Fix Android build for Qt 6.7 (Volker Krause ) version 0.14.2 (release 2023-12-17) - Add support for KWallet 6 (Volker Krause ) version 0.14.1 (release 2023-06-01) - Export QKeychain::isAvailable() to make it usable in a shared build (Volker Krause ) - Protect against creating the QtKeychain::QtKeychain alias target twice (Volker Krause ) version 0.14.0 (release 2023-05-12) - Add Qt 6 Android support (Igor Bugaev ) - Add QtQuick client example ((Igor Bugaev ) - Added Dutch translation (Heimen Stoffels ) - Fix potential freezing with Apple keychain (Claudio Cambra ) - Add API to check whether a secure backend is available at all (Volker Krause ) version 0.13.2 (release 2021-11-18) - CMake: Deprecate QTKEYCHAIN_STATIC in favor of BUILD_SHARED_LIBS (be@mixxx.org) version 0.13.1 (release 2021-11-08) - KWallet: Fix deletion of entries (Issue #199) version 0.13.0 (release 2021-11-07) - Linux: Require libsecret if not explicitly disabled - Unify implementations for macOS and iOS - CMake: lots of fixes version 0.12.0 (release 2020-12-16) * Add Qt 6 support, drop Qt 4 support * Require C++11 * Add Android support (Mathias Hasselmann) version 0.11.1 (release 2020-09-08) * Build system fixes version 0.11.0 (release 2020-09-08) * Important: Debug builds on Windows now get the "d" suffix * Various build system fixes * Add Haiku support (François Revol ) * 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) version 0.4.0 (release 2014-09-01) * KWallet: Handle case where no wallet exists yet (Liviu Cristian Mirea Ghiban ) * Improved desktop environment detection at runtime (Daniel Molkentin ) version 0.3.0 (release 2014-03-13) * Gnome Keyring supported added (Francois Ferrand ) * Improved Qt 5 support * KWallet: Distinguish empty passwords from non-existing entries * KWallet: Do not use hardcoded wallet name * German translation (Daniel Molkentin ) * Romanian translation (Arthur Țițeică ) version 0.2.0: no official release version 0.1.0 (release 2013-01-16) * Initial release frankosterfeld-qtkeychain-8ce634f/QtKeychainConfig.cmake.in000066400000000000000000000021741517442653100241160ustar00rootroot00000000000000# - Config file for the QtKeychain package # It defines the following variables # QTKEYCHAIN_INCLUDE_DIRS - include directories for QtKeychain # QTKEYCHAIN_LIBRARIES - libraries to link against # as well as the following imported targets # qt5keychain / qt6keychain # Qt5Keychain::Qt5Keychain / Qt6Keychain::Qt6Keychain @PACKAGE_INIT@ include("${CMAKE_CURRENT_LIST_DIR}/Qt@QTKEYCHAIN_VERSION_INFIX@KeychainLibraryDepends.cmake") include(CMakeFindDependencyMacro) find_dependency(Qt@QTKEYCHAIN_VERSION_INFIX@Core) if(UNIX AND NOT APPLE AND NOT ANDROID AND NOT EMSCRIPTEN) find_dependency(Qt@QTKEYCHAIN_VERSION_INFIX@DBus) endif() set(QTKEYCHAIN_LIBRARIES "@QTKEYCHAIN_TARGET_NAME@") get_target_property(QTKEYCHAIN_INCLUDE_DIRS "@QTKEYCHAIN_TARGET_NAME@" INTERFACE_INCLUDE_DIRECTORIES) if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.18.0 AND NOT TARGET Qt@QTKEYCHAIN_VERSION_INFIX@Keychain::Qt@QTKEYCHAIN_VERSION_INFIX@Keychain) add_library(Qt@QTKEYCHAIN_VERSION_INFIX@Keychain::Qt@QTKEYCHAIN_VERSION_INFIX@Keychain ALIAS qt@QTKEYCHAIN_VERSION_INFIX@keychain) endif() check_required_components(Qt@QTKEYCHAIN_VERSION_INFIX@Keychain) frankosterfeld-qtkeychain-8ce634f/ReadMe.md000066400000000000000000000026571517442653100210060ustar00rootroot00000000000000QtKeychain ========== 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. frankosterfeld-qtkeychain-8ce634f/TestAppExample/000077500000000000000000000000001517442653100222115ustar00rootroot00000000000000frankosterfeld-qtkeychain-8ce634f/TestAppExample/.gitignore000066400000000000000000000013451517442653100242040ustar00rootroot00000000000000# This file is used to ignore files which are generated # ---------------------------------------------------------------------------- *~ *.autosave *.a *.core *.moc *.o *.obj *.orig *.rej *.so *.so.* *_pch.h.cpp *_resource.rc *.qm .#* *.*# core !core/ tags .DS_Store .directory *.debug Makefile* *.prl *.app moc_*.cpp ui_*.h qrc_*.cpp Thumbs.db *.res *.rc /.qmake.cache /.qmake.stash # qtcreator generated files *.pro.user* # xemacs temporary files *.flc # Vim temporary files .*.swp # Visual Studio generated files *.ib_pdb_index *.idb *.ilk *.pdb *.sln *.suo *.vcproj *vcproj.*.*.user *.ncb *.sdf *.opensdf *.vcxproj *vcxproj.* # MinGW generated files *.Debug *.Release # Python byte code *.pyc # Binaries # -------- *.dll *.exe frankosterfeld-qtkeychain-8ce634f/TestAppExample/CMakeLists.txt000066400000000000000000000042641517442653100247570ustar00rootroot00000000000000if(NOT DEFINED QTKEYCHAIN_TARGET_NAME) cmake_minimum_required(VERSION 3.16) project(TestAppExample LANGUAGES CXX) endif() include(GNUInstallDirs) # Enable C++11 SET(CMAKE_CXX_STANDARD 11) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_AUTOUIC ON) set(BUNDLE_IDENTIFIER "org.qtkeychain.TestAppExample") option(BUILD_WITH_QT6 "Build qtkeychain with Qt 6" OFF) if (BUILD_WITH_QT6) set(QTx Qt6) else() set(QTx Qt5) endif() if(DEFINED QTKEYCHAIN_TARGET_NAME) # Included as subdirectory from the top-level build (BUILD_QTQUICK_DEMO=ON) set(KEYCHAIN_TARGET ${QTKEYCHAIN_TARGET_NAME}) else() # Standalone build: pull in the library from source (one level up) get_filename_component(_qtkeychain_source_dir "${CMAKE_CURRENT_SOURCE_DIR}/.." ABSOLUTE) if(EXISTS "${_qtkeychain_source_dir}/qtkeychain/keychain.h") add_subdirectory("${_qtkeychain_source_dir}" "${CMAKE_CURRENT_BINARY_DIR}/qtkeychain_build") # QTKEYCHAIN_TARGET_NAME is set inside the subdirectory scope, derive it here if(BUILD_WITH_QT6) set(KEYCHAIN_TARGET qt6keychain) else() set(KEYCHAIN_TARGET qt5keychain) endif() else() # Fallback: find an already-installed QtKeychain find_package(${QTx}Keychain REQUIRED) set(KEYCHAIN_TARGET ${QTx}Keychain::${QTx}Keychain) endif() endif() find_package(${QTx} COMPONENTS Core Network Quick Qml REQUIRED) qt_add_resources(QT_RESOURCES qml.qrc) if(BUILD_WITH_QT6) qt_add_executable(TestAppExample MANUAL_FINALIZATION keychainclass.h keychainclass.cpp main.cpp ${QT_RESOURCES} ) else() add_executable(TestAppExample keychainclass.h keychainclass.cpp main.cpp ${QT_RESOURCES} ) endif() set_target_properties(TestAppExample PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER ${BUNDLE_IDENTIFIER} MACOSX_BUNDLE_IDENTIFIER ${BUNDLE_IDENTIFIER} MACOSX_BUNDLE_BUNDLE_NAME TestAppExample MACOSX_BUNDLE TRUE ) target_link_libraries(TestAppExample PRIVATE ${QTx}::Core ${QTx}::Network ${QTx}::Quick ${QTx}::Qml ${KEYCHAIN_TARGET}) if(BUILD_WITH_QT6) qt_import_qml_plugins(TestAppExample) qt_finalize_target(TestAppExample) endif() install(TARGETS TestAppExample BUNDLE DESTINATION . RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ) frankosterfeld-qtkeychain-8ce634f/TestAppExample/TestAppExample.pro000066400000000000000000000014051517442653100256270ustar00rootroot00000000000000QT += quick CONFIG += c++11 # You can make your code fail to compile if it uses deprecated APIs. # In order to do so, uncomment the following line. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 include(../qtkeychain.pri) SOURCES += \ keychainclass.cpp \ main.cpp RESOURCES += qml.qrc # Additional import path used to resolve QML modules in Qt Creator's code model QML_IMPORT_PATH = # Additional import path used to resolve QML modules just for Qt Quick Designer QML_DESIGNER_IMPORT_PATH = # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin !isEmpty(target.path): INSTALLS += target HEADERS += \ keychainclass.h frankosterfeld-qtkeychain-8ce634f/TestAppExample/keychainclass.cpp000066400000000000000000000052521517442653100255420ustar00rootroot00000000000000// fix for https://github.com/frankosterfeld/qtkeychain/issues/288 #include #include "keychainclass.h" static const QString service = "keychain.example.project.app"; KeyChainClass::KeyChainClass(QObject *parent) : QObject(parent) { } void KeyChainClass::readKey(const QString &key) { auto readCredentialJob = new QKeychain::ReadPasswordJob(service); readCredentialJob->setAutoDelete(true); readCredentialJob->setKey(key); QObject::connect(readCredentialJob, &QKeychain::ReadPasswordJob::finished, this, [this, key](QKeychain::Job *job) { auto j = static_cast(job); if (j->error() == QKeychain::NoError) { emit keyRestored(key, j->textData()); } else { emit error(tr("Read key failed: %1").arg(qPrintable(j->errorString()))); } // no delete needed, autoDelete takes care of it }); readCredentialJob->start(); } void KeyChainClass::writeKey(const QString &key, const QString &value) { auto writeCredentialJob = new QKeychain::WritePasswordJob(service); writeCredentialJob->setAutoDelete(true); writeCredentialJob->setKey(key); writeCredentialJob->setTextData(value); QObject::connect(writeCredentialJob, &QKeychain::WritePasswordJob::finished, this, [this, key](QKeychain::Job *job) { auto j = static_cast(job); if (j->error() == QKeychain::NoError) { emit keyStored(key); } else { emit error(tr("Write key failed: %1").arg(qPrintable(j->errorString()))); } }); writeCredentialJob->start(); } void KeyChainClass::deleteKey(const QString &key) { auto deleteCredentialJob = new QKeychain::DeletePasswordJob(service); deleteCredentialJob->setAutoDelete(true); deleteCredentialJob->setKey(key); QObject::connect(deleteCredentialJob, &QKeychain::DeletePasswordJob::finished, this, [this, key](QKeychain::Job *job) { auto j = static_cast(job); if (j->error() == QKeychain::NoError) { emit keyDeleted(key); } else { emit error(tr("Delete key failed: %1").arg(qPrintable(j->errorString()))); } }); deleteCredentialJob->start(); } frankosterfeld-qtkeychain-8ce634f/TestAppExample/keychainclass.h000066400000000000000000000011601517442653100252010ustar00rootroot00000000000000#ifndef KEYCHAINCLASS_H #define KEYCHAINCLASS_H #include #include class KeyChainClass : public QObject { Q_OBJECT public: KeyChainClass(QObject *parent = nullptr); Q_INVOKABLE void readKey(const QString &key); Q_INVOKABLE void writeKey(const QString &key, const QString &value); Q_INVOKABLE void deleteKey(const QString &key); Q_SIGNALS: void keyStored(const QString &key); void keyRestored(const QString &key, const QString &value); void keyDeleted(const QString &key); void error(const QString &errorText); private: }; #endif // KEYCHAINCLASS_H frankosterfeld-qtkeychain-8ce634f/TestAppExample/main.cpp000066400000000000000000000014741517442653100236470ustar00rootroot00000000000000#include #include #include #include "keychainclass.h" int main(int argc, char *argv[]) { #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); #endif QGuiApplication app(argc, argv); QQmlApplicationEngine engine; const QUrl url(QStringLiteral("qrc:/main.qml")); QObject::connect( &engine, &QQmlApplicationEngine::objectCreated, &app, [url](QObject *obj, const QUrl &objUrl) { if (!obj && url == objUrl) QCoreApplication::exit(-1); }, Qt::QueuedConnection); KeyChainClass keyChainClass; engine.rootContext()->setContextProperty("KeyChain", &keyChainClass); engine.load(url); return app.exec(); } frankosterfeld-qtkeychain-8ce634f/TestAppExample/main.qml000066400000000000000000000070231517442653100236520ustar00rootroot00000000000000import QtQuick 2.13 import QtQuick.Window 2.13 import QtQuick.Controls 2.12 Window { id: root width: 640 height: 480 visible: true Column { anchors { fill: parent margins: 50 } spacing: 20 Label { text: 'Key name:' font.pixelSize: 20 } TextField { id: keyNameTextField width: parent.width height: 50 text: 'default key name' } Label { text: 'Key value:' font.pixelSize: 20 } TextField { id: keyValueTextField width: parent.width height: 50 text: 'some value' } Label { id: infoLabel width: parent.width wrapMode: Text.Wrap visible: false onVisibleChanged: if (visible) hideAnimation.start(); SequentialAnimation { id: hideAnimation PauseAnimation { duration: 10000 } ScriptAction { script: infoLabel.visible = false } } Component.onCompleted: { KeyChain.keyStored.connect((key) => { infoLabel.text = String("Key '%1' successfully stored").arg(key) infoLabel.color = 'green' infoLabel.visible = true }) KeyChain.keyRestored.connect((key, value) => { infoLabel.text = String("Key '%1' successfully restored with data '%2'").arg(key).arg(value) infoLabel.color = 'green' infoLabel.visible = true }) KeyChain.keyDeleted.connect((key) => { infoLabel.text = String("Key '%1' successfully deleted").arg(key) infoLabel.color = 'green' infoLabel.visible = true }) KeyChain.error.connect((errorText) => { infoLabel.text = errorText infoLabel.color = 'red' infoLabel.visible = true }) } } Row { width: parent.width height: 50 spacing: 20 Button { width: 80 height: parent.height text: 'Store' onClicked: { KeyChain.writeKey(keyNameTextField.text.trim(), keyValueTextField.text.trim()) } } Button { width: 80 height: parent.height text: 'Restore' onClicked: { KeyChain.readKey(keyNameTextField.text.trim()) } } Button { width: 80 height: parent.height text: 'Delete' onClicked: { KeyChain.deleteKey(keyNameTextField.text.trim()) } } } } } frankosterfeld-qtkeychain-8ce634f/TestAppExample/qml.qrc000066400000000000000000000001271517442653100235110ustar00rootroot00000000000000 main.qml frankosterfeld-qtkeychain-8ce634f/appveyor.yml000066400000000000000000000014541517442653100217110ustar00rootroot00000000000000version: '{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 frankosterfeld-qtkeychain-8ce634f/appveyorHelp.psm1000066400000000000000000000264651517442653100226120ustar00rootroot00000000000000Write-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") frankosterfeld-qtkeychain-8ce634f/autotest/000077500000000000000000000000001517442653100211655ustar00rootroot00000000000000frankosterfeld-qtkeychain-8ce634f/autotest/CMakeLists.txt000066400000000000000000000004631517442653100237300ustar00rootroot00000000000000include(ECMAddTests) if (BUILD_WITH_QT6) find_package(Qt6 CONFIG COMPONENTS Test REQUIRED) else() find_package(Qt5 CONFIG COMPONENTS Test REQUIRED) endif() ecm_add_tests(basic.cpp LINK_LIBRARIES ${QTKEYCHAIN_TARGET_NAME} Qt${QT_MAJOR_VERSION}::Test) set_property(TARGET basic PROPERTY AUTOMOC ON) frankosterfeld-qtkeychain-8ce634f/autotest/basic.cpp000066400000000000000000000076601517442653100227630ustar00rootroot00000000000000#include #include "qtkeychain/keychain.h" namespace { QByteArray generateRandomString(qsizetype size) { std::vector buffer(size, 0); QRandomGenerator::global()->fillRange(buffer.data(), size); return QByteArray(reinterpret_cast(buffer.data()), static_cast(size * sizeof(quint32))) .toBase64(QByteArray::Base64UrlEncoding) .mid(0, size); } } // namespace class BasicTest : public QObject { Q_OBJECT private Q_SLOTS: void test_data() { QTest::addColumn("password"); QTest::addColumn("usernames"); QTest::newRow("normal password") << QByteArrayLiteral("this is a password") << QStringList{"", "user1", "user2"}; QTest::newRow("1000") << generateRandomString(1000) << QStringList{"", "user1", "user2"}; QTest::newRow("2000") << generateRandomString(2000)<< QStringList{"", "user1", "user2"}; QTest::newRow("3000") << generateRandomString(3000)<< QStringList{"", "user1", "user2"}; QTest::newRow("10000") << generateRandomString(10000)<< QStringList{"", "user1", "user2"}; QTest::newRow("18944") << generateRandomString(18944)<< QStringList{"", "user1", "user2"}; } void test() { #ifdef Q_OS_MACOS QSKIP("This test case has no access to the keychain"); #endif const QStringList serviceKeys ={"", QStringLiteral("QtKeychainTest-%1").arg(QTest::currentDataTag())}; QFETCH(QByteArray, password); QFETCH(QStringList, usernames); for (const auto& serviceKey: serviceKeys) { for (const auto& username : usernames) { QKeychain::WritePasswordJob writeJob(serviceKey); writeJob.setKey(username); writeJob.setBinaryData(username.toUtf8()+password); QSignalSpy writeSpy(&writeJob, &QKeychain::WritePasswordJob::finished); writeJob.start(); writeSpy.wait(); #ifdef Q_OS_WIN QEXPECT_FAIL("18944", "Maximum for Windows is exceeded", Abort); #endif qDebug() << "[write]" << writeJob.error() << ": " << writeJob.errorString(); const auto expected = (serviceKey.isEmpty() && username.isEmpty()) ? QKeychain::EntryNotFound : QKeychain::NoError; QCOMPARE(writeJob.error(), expected); } } for (const auto& serviceKey: serviceKeys) { for (const auto& username : usernames) { QKeychain::ReadPasswordJob readJob(serviceKey); readJob.setKey(username); QSignalSpy readSpy(&readJob, &QKeychain::ReadPasswordJob::finished); readJob.start(); readSpy.wait(); qDebug() << "[read]" << readJob.error() << ": " << readJob.errorString(); const auto expected = (serviceKey.isEmpty() && username.isEmpty()) ? QKeychain::EntryNotFound : QKeychain::NoError; QCOMPARE(readJob.error(), expected); if (expected == QKeychain::NoError) { QCOMPARE(readJob.binaryData(), username.toUtf8()+password); } } } for (const auto& serviceKey: serviceKeys) { for (const auto& username : usernames) { QKeychain::DeletePasswordJob deleteJob(serviceKey); deleteJob.setKey(username); QSignalSpy deleteSpy(&deleteJob, &QKeychain::DeletePasswordJob::finished); deleteJob.start(); deleteSpy.wait(); qDebug() << "[delete]" << deleteJob.error() << ": " << deleteJob.errorString(); const auto expected = (serviceKey.isEmpty() && username.isEmpty()) ? QKeychain::EntryNotFound : QKeychain::NoError; QCOMPARE(deleteJob.error(), expected); } } } }; QTEST_MAIN(BasicTest) #include "basic.moc" frankosterfeld-qtkeychain-8ce634f/cmake/000077500000000000000000000000001517442653100203755ustar00rootroot00000000000000frankosterfeld-qtkeychain-8ce634f/cmake/Modules/000077500000000000000000000000001517442653100220055ustar00rootroot00000000000000frankosterfeld-qtkeychain-8ce634f/cmake/Modules/ECMAddTests.cmake000066400000000000000000000153201517442653100250500ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2013 Alexander Richardson # SPDX-FileCopyrightText: 2015 Alex Merry # # SPDX-License-Identifier: BSD-3-Clause #[=======================================================================[.rst: ECMAddTests ----------- Convenience functions for adding tests. :: ecm_add_tests( LINK_LIBRARIES [ [...]] [NAME_PREFIX ] [GUI] [TARGET_NAMES_VAR ] [TEST_NAMES_VAR ] [WORKING_DIRECTORY ] # Since 5.111 ) A convenience function for adding multiple tests, each consisting of a single source file. For each file in , an executable target will be created (the name of which will be the basename of the source file). This will be linked against the libraries given with ``LINK_LIBRARIES``. Each executable will be added as a test with the same name. If ``NAME_PREFIX`` is given, this prefix will be prepended to the test names, but not the target names. As a result, it will not prevent clashes between tests with the same name in different parts of the project, but it can be used to give an indication of where to look for a failing test. If the flag ``GUI`` is passed the test binaries will be GUI executables, otherwise the resulting binaries will be console applications (regardless of the value of ``CMAKE_WIN32_EXECUTABLE`` or ``CMAKE_MACOSX_BUNDLE``). Be aware that this changes the executable entry point on Windows (although some frameworks, such as Qt, abstract this difference away). The tests will be build with ``-DQT_FORCE_ASSERTS`` to enable assertions in the test executable even for release builds. The ``TARGET_NAMES_VAR`` and ``TEST_NAMES_VAR`` arguments, if given, should specify a variable name to receive the list of generated target and test names, respectively. This makes it convenient to apply properties to them as a whole, for example, using ``set_target_properties()`` or ``set_tests_properties()``. The generated target executables will have the effects of ``ecm_mark_as_test()`` (from the :module:`ECMMarkAsTest` module) applied to it. ``WORKING_DIRECTORY`` sets the test property `WORKING_DIRECTORY `_ in which to execute the test. By default the test will be run in ``${CMAKE_CURRENT_BINARY_DIR}``. The working directory can be specified using generator expressions. Since 5.111. :: ecm_add_test( LINK_LIBRARIES [ [...]] [TEST_NAME ] [NAME_PREFIX ] [GUI] [WORKING_DIRECTORY ] # Since 5.111 ) This is a single-test form of ``ecm_add_tests`` that allows multiple source files to be used for a single test. If using multiple source files, ``TEST_NAME`` must be given; this will be used for both the target and test names (and, as with ``ecm_add_tests()``, the ``NAME_PREFIX`` argument will be prepended to the test name). ``WORKING_DIRECTORY`` sets the test property `WORKING_DIRECTORY `_ in which to execute the test. By default the test will be run in ``${CMAKE_CURRENT_BINARY_DIR}``. The working directory can be specified using generator expressions. Since 5.111. Since pre-1.0.0. #]=======================================================================] include(ECMMarkAsTest) include(ECMMarkNonGuiExecutable) function(ecm_add_test) set(options GUI) # TARGET_NAME_VAR and TEST_NAME_VAR are undocumented args used by # ecm_add_tests set(oneValueArgs TEST_NAME NAME_PREFIX TARGET_NAME_VAR TEST_NAME_VAR WORKING_DIRECTORY) set(multiValueArgs LINK_LIBRARIES) cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) set(_sources ${ARG_UNPARSED_ARGUMENTS}) list(LENGTH _sources _sourceCount) if(ARG_TEST_NAME) set(_targetname ${ARG_TEST_NAME}) elseif(${_sourceCount} EQUAL "1") #use the source file name without extension as the testname get_filename_component(_targetname ${_sources} NAME_WE) else() #more than one source file passed, but no test name given -> error message(FATAL_ERROR "ecm_add_test() called with multiple source files but without setting \"TEST_NAME\"") endif() set(_testname ${ARG_NAME_PREFIX}${_targetname}) set(gui_args) if(ARG_GUI) set(gui_args WIN32 MACOSX_BUNDLE) endif() add_executable(${_targetname} ${gui_args} ${_sources}) if(NOT ARG_GUI) ecm_mark_nongui_executable(${_targetname}) endif() set(test_args) if(DEFINED ARG_WORKING_DIRECTORY) list(APPEND test_args WORKING_DIRECTORY ${ARG_WORKING_DIRECTORY}) endif() add_test(NAME ${_testname} COMMAND ${_targetname} ${test_args}) target_link_libraries(${_targetname} ${ARG_LINK_LIBRARIES}) target_compile_definitions(${_targetname} PRIVATE -DQT_FORCE_ASSERTS) ecm_mark_as_test(${_targetname}) if (CMAKE_LIBRARY_OUTPUT_DIRECTORY) set(_plugin_path ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) if (DEFINED ENV{QT_PLUGIN_PATH}) if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") # https://stackoverflow.com/questions/59862894/how-do-i-make-a-list-in-cmake-with-the-semicolon-value set(PATHSEP "\\\;") # Don't want cmake to treat it like a list else() # e.g. Linux set(PATHSEP ":") endif() set(_plugin_path "${_plugin_path}${PATHSEP}$ENV{QT_PLUGIN_PATH}") endif() set_property(TEST ${_testname} PROPERTY ENVIRONMENT "QT_PLUGIN_PATH=${_plugin_path}") endif() if (ARG_TARGET_NAME_VAR) set(${ARG_TARGET_NAME_VAR} "${_targetname}" PARENT_SCOPE) endif() if (ARG_TEST_NAME_VAR) set(${ARG_TEST_NAME_VAR} "${_testname}" PARENT_SCOPE) endif() endfunction() function(ecm_add_tests) set(options GUI) set(oneValueArgs NAME_PREFIX TARGET_NAMES_VAR TEST_NAMES_VAR WORKING_DIRECTORY) set(multiValueArgs LINK_LIBRARIES) cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if(ARG_GUI) set(_exe_type GUI) else() set(_exe_type "") endif() set(test_args) if(DEFINED ARG_WORKING_DIRECTORY) list(APPEND test_args WORKING_DIRECTORY ${ARG_WORKING_DIRECTORY}) endif() set(test_names) set(target_names) foreach(_test_source ${ARG_UNPARSED_ARGUMENTS}) ecm_add_test(${_test_source} NAME_PREFIX ${ARG_NAME_PREFIX} LINK_LIBRARIES ${ARG_LINK_LIBRARIES} TARGET_NAME_VAR target_name TEST_NAME_VAR test_name ${_exe_type} ${test_args} ) list(APPEND _test_names "${test_name}") list(APPEND _target_names "${target_name}") endforeach() if (ARG_TARGET_NAMES_VAR) set(${ARG_TARGET_NAMES_VAR} "${_target_names}" PARENT_SCOPE) endif() if (ARG_TEST_NAMES_VAR) set(${ARG_TEST_NAMES_VAR} "${_test_names}" PARENT_SCOPE) endif() endfunction() frankosterfeld-qtkeychain-8ce634f/cmake/Modules/ECMGeneratePriFile.cmake000066400000000000000000000253171517442653100263510ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2014 David Faure # # SPDX-License-Identifier: BSD-3-Clause #[=======================================================================[.rst: ECMGeneratePriFile ------------------ Generate a ``.pri`` file for the benefit of qmake-based projects. As well as the function below, this module creates the cache variable ``ECM_MKSPECS_INSTALL_DIR`` and sets the default value to ``mkspecs/modules``. This assumes Qt and the current project are both installed to the same non-system prefix. Packagers who use ``-DCMAKE_INSTALL_PREFIX=/usr`` will certainly want to set ``ECM_MKSPECS_INSTALL_DIR`` to something like ``share/qt5/mkspecs/modules``. The main thing is that this should be the ``modules`` subdirectory of either the default qmake ``mkspecs`` directory or of a directory that will be in the ``$QMAKEPATH`` environment variable when qmake is run. :: ecm_generate_pri_file(BASE_NAME LIB_NAME [VERSION ] # since 5.83 [DEPS " [ [...]]"] [FILENAME_VAR ] [INCLUDE_INSTALL_DIRS [ [...]]] # since 5.92 [INCLUDE_INSTALL_DIR ] # deprecated since 5.92 [LIB_INSTALL_DIR ]) If your CMake project produces a Qt-based library, you may expect there to be applications that wish to use it that use a qmake-based build system, rather than a CMake-based one. Creating a ``.pri`` file will make use of your library convenient for them, in much the same way that CMake config files make things convenient for CMake-based applications. ``ecm_generate_pri_file()`` generates just such a file. ``VERSION`` specifies the version of the library the ``.pri`` file describes. If not set, the value is taken from the context variable ``PROJECT_VERSION``. This variable is usually set by the ``project(... VERSION ...)`` command or, if CMake policy CMP0048 is not ``NEW``, by :module:`ECMSetupVersion`. For backward-compatibility with older ECM versions the ``PROJECT_VERSION_STRING`` variable as set by :module:`ECMSetupVersion` will be preferred over ``PROJECT_VERSION`` if set, unless the minimum required version of ECM is 5.83 and newer. Since 5.83. ``BASE_NAME`` specifies the name qmake project (.pro) files should use to refer to the library (eg: KArchive). ``LIB_NAME`` is the name of the actual library to link to (ie: the first argument to add_library()). ``DEPS`` is a space-separated list of the base names of other libraries (for Qt libraries, use the same names you use with the ``QT`` variable in a qmake project file, such as "core" for QtCore). ``FILENAME_VAR`` specifies the name of a variable to store the path to the generated file in. ``INCLUDE_INSTALL_DIRS`` are the paths (relative to ``CMAKE_INSTALL_PREFIX``) that include files will be installed to. It defaults to ``${INCLUDE_INSTALL_DIR}/`` if the ``INCLUDE_INSTALL_DIR`` variable is set. If that variable is not set, the ``CMAKE_INSTALL_INCLUDEDIR`` variable is used instead, and if neither are set ``include`` is used. ``LIB_INSTALL_DIR`` operates similarly for the installation location for libraries; it defaults to ``${LIB_INSTALL_DIR}``, ``${CMAKE_INSTALL_LIBDIR}`` or ``lib``, in that order. ``INCLUDE_INSTALL_DIR`` is the old variant of ``INCLUDE_INSTALL_DIRS``, taking only one directory. Example usage: .. code-block:: cmake ecm_generate_pri_file( BASE_NAME KArchive LIB_NAME KF5KArchive DEPS "core" FILENAME_VAR pri_filename VERSION 4.2.0 ) install(FILES ${pri_filename} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) A qmake-based project that wished to use this would then do:: QT += KArchive in their ``.pro`` file. Since pre-1.0.0. #]=======================================================================] # Replicate the logic from KDEInstallDirs.cmake as we can't depend on it # Ask qmake if we're using the same prefix as Qt set(_should_query_qt OFF) if(NOT DEFINED KDE_INSTALL_USE_QT_SYS_PATHS) include(ECMQueryQt) ecm_query_qt(qt_install_prefix_dir QT_INSTALL_PREFIX TRY) if(qt_install_prefix_dir STREQUAL "${CMAKE_INSTALL_PREFIX}") set(_should_query_qt ON) endif() endif() if(KDE_INSTALL_USE_QT_SYS_PATHS OR _should_query_qt) include(ECMQueryQt) ecm_query_qt(qt_install_prefix_dir QT_INSTALL_PREFIX) ecm_query_qt(qt_host_data_dir QT_HOST_DATA) if(qt_install_prefix_dir STREQUAL "${CMAKE_INSTALL_PREFIX}") file(RELATIVE_PATH qt_host_data_dir ${qt_install_prefix_dir} ${qt_host_data_dir}) endif() if(qt_host_data_dir STREQUAL "") set(mkspecs_install_dir mkspecs/modules) else() set(mkspecs_install_dir ${qt_host_data_dir}/mkspecs/modules) endif() set(ECM_MKSPECS_INSTALL_DIR ${mkspecs_install_dir} CACHE PATH "The directory where mkspecs will be installed to.") else() set(ECM_MKSPECS_INSTALL_DIR mkspecs/modules CACHE PATH "The directory where mkspecs will be installed to.") endif() function(ECM_GENERATE_PRI_FILE) set(options ) set(oneValueArgs BASE_NAME LIB_NAME DEPS FILENAME_VAR INCLUDE_INSTALL_DIR LIB_INSTALL_DIR VERSION) set(multiValueArgs INCLUDE_INSTALL_DIRS) 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(ECM_GLOBAL_FIND_VERSION VERSION_LESS 5.83.0) set(_support_backward_compat_version_string_var TRUE) else() set(_support_backward_compat_version_string_var FALSE) endif() if(NOT EGPF_BASE_NAME) message(FATAL_ERROR "Required argument BASE_NAME missing in ECM_GENERATE_PRI_FILE() call") endif() if(NOT EGPF_LIB_NAME) message(FATAL_ERROR "Required argument LIB_NAME missing in ECM_GENERATE_PRI_FILE() call") endif() if(NOT EGPF_VERSION) if(_support_backward_compat_version_string_var) if(NOT PROJECT_VERSION_STRING AND NOT PROJECT_VERSION) message(FATAL_ERROR "Required variable PROJECT_VERSION_STRING or PROJECT_VERSION not set before ECM_GENERATE_PRI_FILE() call. Missing call of ecm_setup_version() or project(VERSION)?") endif() else() if(NOT PROJECT_VERSION) message(FATAL_ERROR "Required variable PROJECT_VERSION not set before ECM_GENERATE_PRI_FILE() call. Missing call of ecm_setup_version() or project(VERSION)?") endif() endif() endif() if(EGPF_INCLUDE_INSTALL_DIR) if(EGPF_INCLUDE_INSTALL_DIRS) message(FATAL_ERROR "Only one argument of INCLUDE_INSTALL_DIR & INCLUDE_INSTALL_DIRS can be used in ECM_GENERATE_PRI_FILE() call") endif() set(EGPF_INCLUDE_INSTALL_DIRS ${EGPF_INCLUDE_INSTALL_DIR}) endif() if(NOT EGPF_INCLUDE_INSTALL_DIRS) if(INCLUDE_INSTALL_DIR) set(EGPF_INCLUDE_INSTALL_DIRS "${INCLUDE_INSTALL_DIR}/${EGPF_BASE_NAME}") elseif(CMAKE_INSTALL_INCLUDEDIR) set(EGPF_INCLUDE_INSTALL_DIRS "${CMAKE_INSTALL_INCLUDEDIR}/${EGPF_BASE_NAME}") else() set(EGPF_INCLUDE_INSTALL_DIRS "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() if(EGPF_VERSION) set(PRI_VERSION "${EGPF_VERSION}") else() if(_support_backward_compat_version_string_var AND PROJECT_VERSION_STRING) set(PRI_VERSION "${PROJECT_VERSION_STRING}") if(NOT PROJECT_VERSION_STRING STREQUAL PROJECT_VERSION) message(DEPRECATION "ECM_GENERATE_PRI_FILE() will no longer support PROJECT_VERSION_STRING when the required minimum version of ECM is 5.83 or newer. Set VERSION parameter or use PROJECT_VERSION instead.") endif() else() set(PRI_VERSION "${PROJECT_VERSION}") endif() endif() string(REGEX REPLACE "^([0-9]+)\\.[0-9]+\\.[0-9]+.*" "\\1" PRI_VERSION_MAJOR "${PRI_VERSION}") string(REGEX REPLACE "^[0-9]+\\.([0-9]+)\\.[0-9]+.*" "\\1" PRI_VERSION_MINOR "${PRI_VERSION}") string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" PRI_VERSION_PATCH "${PRI_VERSION}") # Prepare the right number of "../.." to go from ECM_MKSPECS_INSTALL_DIR to the install prefix # This allows to make the generated pri files relocatable (no absolute paths) 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}) set(PRI_TARGET_INCLUDES) foreach(_dir ${EGPF_INCLUDE_INSTALL_DIRS}) # separate list entries with space if(IS_ABSOLUTE "${_dir}") string(APPEND PRI_TARGET_INCLUDES " ${_dir}") else() string(APPEND PRI_TARGET_INCLUDES " ${BASEPATH}/${_dir}") endif() endforeach() if(IS_ABSOLUTE "${EGPF_LIB_INSTALL_DIR}") set(PRI_TARGET_LIBS "${EGPF_LIB_INSTALL_DIR}") else() 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 = ${PRI_VERSION} QT.${PRI_TARGET_BASENAME}.MAJOR_VERSION = ${PRI_VERSION_MAJOR} QT.${PRI_TARGET_BASENAME}.MINOR_VERSION = ${PRI_VERSION_MINOR} QT.${PRI_TARGET_BASENAME}.PATCH_VERSION = ${PRI_VERSION_PATCH} QT.${PRI_TARGET_BASENAME}.name = ${PRI_TARGET_LIBNAME} QT.${PRI_TARGET_BASENAME}.module = ${PRI_TARGET_LIBNAME} QT.${PRI_TARGET_BASENAME}.defines = ${PRI_TARGET_DEFINES} 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() frankosterfeld-qtkeychain-8ce634f/cmake/Modules/ECMMarkAsTest.cmake000066400000000000000000000022361517442653100253550ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2012 Stephen Kelly # SPDX-FileCopyrightText: 2012 Alex Neundorf # # SPDX-License-Identifier: BSD-3-Clause #[=======================================================================[.rst: ECMMarkAsTest ------------- Marks a target as only being required for tests. :: ecm_mark_as_test( [ [...]]) This will cause the specified targets to not be built unless either ``BUILD_TESTING`` is set to ``ON`` or the user invokes the ``buildtests`` target. ``BUILD_TESTING`` is created as a cache variable by the CTest module and by the :kde-module:`KDECMakeSettings` module. Since pre-1.0.0. #]=======================================================================] if (NOT BUILD_TESTING) if(NOT TARGET buildtests) add_custom_target(buildtests) endif() endif() function(ecm_mark_as_test) if (NOT BUILD_TESTING) foreach(_target ${ARGN}) set_target_properties(${_target} PROPERTIES EXCLUDE_FROM_ALL TRUE ) add_dependencies(buildtests ${_target}) endforeach() endif() endfunction() frankosterfeld-qtkeychain-8ce634f/cmake/Modules/ECMMarkNonGuiExecutable.cmake000066400000000000000000000016741517442653100273600ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2012 Stephen Kelly # # SPDX-License-Identifier: BSD-3-Clause #[=======================================================================[.rst: ECMMarkNonGuiExecutable ----------------------- Marks an executable target as not being a GUI application. :: ecm_mark_nongui_executable( [ [...]]) This will indicate to CMake that the specified targets should not be included in a MACOSX_BUNDLE and should not be WIN32_EXECUTABLEs. On platforms other than MacOS X or Windows, this will have no effect. Since pre-1.0.0. #]=======================================================================] function(ecm_mark_nongui_executable) foreach(_target ${ARGN}) set_target_properties(${_target} PROPERTIES WIN32_EXECUTABLE FALSE MACOSX_BUNDLE FALSE ) endforeach() endfunction() frankosterfeld-qtkeychain-8ce634f/cmake/Modules/ECMPackageConfigHelpers.cmake000066400000000000000000000156661517442653100273560ustar00rootroot00000000000000#.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() frankosterfeld-qtkeychain-8ce634f/cmake/Modules/ECMQueryQt.cmake000066400000000000000000000070711517442653100247530ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2014 Rohan Garg # SPDX-FileCopyrightText: 2014 Alex Merry # SPDX-FileCopyrightText: 2014-2016 Aleix Pol # SPDX-FileCopyrightText: 2017 Friedrich W. H. Kossebau # SPDX-FileCopyrightText: 2022 Ahmad Samir # # SPDX-License-Identifier: BSD-3-Clause #[=======================================================================[.rst: ECMQueryQt --------------- This module can be used to query the installation paths used by Qt. For Qt5 this uses ``qmake``, and for Qt6 this used ``qtpaths`` (the latter has built-in support to query the paths of a target platform when cross-compiling). This module defines the following function: :: ecm_query_qt( [TRY]) Passing ``TRY`` will result in the method not making the build fail if the executable used for querying has not been found, but instead simply print a warning message and return an empty string. Example usage: .. code-block:: cmake include(ECMQueryQt) ecm_query_qt(bin_dir QT_INSTALL_BINS) If the call succeeds ``${bin_dir}`` will be set to ``/path/to/bin/dir`` (e.g. ``/usr/lib64/qt/bin/``). Since: 5.93 #]=======================================================================] include(${CMAKE_CURRENT_LIST_DIR}/QtVersionOption.cmake) include(CheckLanguage) check_language(CXX) if (CMAKE_CXX_COMPILER) # Enable the CXX language to let CMake look for config files in library dirs. # See: https://gitlab.kitware.com/cmake/cmake/-/issues/23266 enable_language(CXX) endif() if (QT_MAJOR_VERSION STREQUAL "5") # QUIET to accommodate the TRY option find_package(Qt${QT_MAJOR_VERSION}Core QUIET) if(TARGET Qt5::qmake) get_target_property(_qmake_executable_default Qt5::qmake LOCATION) set(QUERY_EXECUTABLE ${_qmake_executable_default} CACHE FILEPATH "Location of the Qt5 qmake executable") set(_exec_name_text "Qt5 qmake") set(_cli_option "-query") endif() elseif(QT_MAJOR_VERSION STREQUAL "6") # QUIET to accommodate the TRY option find_package(Qt6 COMPONENTS CoreTools QUIET CONFIG) if (TARGET Qt6::qtpaths) get_target_property(_qtpaths_executable Qt6::qtpaths LOCATION) set(QUERY_EXECUTABLE ${_qtpaths_executable} CACHE FILEPATH "Location of the Qt6 qtpaths executable") set(_exec_name_text "Qt6 qtpaths") set(_cli_option "--query") endif() endif() function(ecm_query_qt result_variable qt_variable) set(options TRY) set(oneValueArgs) set(multiValueArgs) cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if(NOT QUERY_EXECUTABLE) if(ARGS_TRY) set(${result_variable} "" PARENT_SCOPE) message(STATUS "No ${_exec_name_text} executable found. Can't check ${qt_variable}") return() else() message(FATAL_ERROR "No ${_exec_name_text} executable found. Can't check ${qt_variable} as required") endif() endif() execute_process( COMMAND ${QUERY_EXECUTABLE} ${_cli_option} "${qt_variable}" RESULT_VARIABLE return_code OUTPUT_VARIABLE output ) if(return_code EQUAL 0) string(STRIP "${output}" output) file(TO_CMAKE_PATH "${output}" output_path) set(${result_variable} "${output_path}" PARENT_SCOPE) else() message(WARNING "Failed call: ${_command} \"${qt_variable}\"") message(FATAL_ERROR "${_exec_name_text} call failed: ${return_code}") endif() endfunction() frankosterfeld-qtkeychain-8ce634f/cmake/Modules/ECMSetupVersion.cmake000066400000000000000000000200631517442653100260030ustar00rootroot00000000000000#.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() frankosterfeld-qtkeychain-8ce634f/cmake/Modules/QtVersionOption.cmake000066400000000000000000000017351517442653100261400ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2021 Volker Krause # # SPDX-License-Identifier: BSD-3-Clause #[=======================================================================[.rst: QtVersionOption --------------- Adds a build option to select the major Qt version if necessary, that is, if the major Qt version has not yet been determined otherwise (e.g. by a corresponding ``find_package()`` call). This module is typically included by other modules requiring knowledge about the major Qt version. ``QT_MAJOR_VERSION`` is defined to either be "5" or "6". Since 5.82.0. #]=======================================================================] if (DEFINED QT_MAJOR_VERSION) return() endif() if (TARGET Qt5::Core) set(QT_MAJOR_VERSION 5) elseif (TARGET Qt6::Core) set(QT_MAJOR_VERSION 6) else() option(BUILD_WITH_QT6 "Build against Qt 6" OFF) if (BUILD_WITH_QT6) set(QT_MAJOR_VERSION 6) else() set(QT_MAJOR_VERSION 5) endif() endif() frankosterfeld-qtkeychain-8ce634f/qtkeychain.pri000066400000000000000000000054241517442653100221760ustar00rootroot00000000000000# 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/qtkeychain 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 = $$QTKEYCHAIN_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 { lessThan(QT_MAJOR_VERSION, 6) { 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 -lcrypt32 } 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 OBJECTIVE_SOURCES += $$QTKEYCHAIN_PWD/keychain_apple.mm } frankosterfeld-qtkeychain-8ce634f/qtkeychain/000077500000000000000000000000001517442653100214555ustar00rootroot00000000000000frankosterfeld-qtkeychain-8ce634f/qtkeychain/CMakeLists.txt000066400000000000000000000104241517442653100242160ustar00rootroot00000000000000 list(APPEND qtkeychain_LIBRARIES ${QTCORE_LIBRARIES}) set(qtkeychain_SOURCES keychain.cpp qkeychain_export.h keychain.h ) if(MSVC) # CMake < 3.15 sneaks in /W# flags for us, so we need a replacement, # or we'll get a warning (cf. CMP0092) if (CMAKE_CXX_FLAGS MATCHES "/W[0-4]") string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") else() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") endif() else() # MSVC's STL / Qt headers are not MSVC -Wall clean, so don't enable it there add_definitions( -Wall -Werror=return-type ) endif() if(WIN32) list(APPEND qtkeychain_SOURCES keychain_win.cpp) list(APPEND qtkeychain_LIBRARIES crypt32) if (NOT USE_CREDENTIAL_STORE) list(APPEND qtkeychain_SOURCES plaintextstore.cpp) endif() #FIXME: mingw bug; otherwise getting undefined refs to RtlSecureZeroMemory there if(MINGW) add_definitions( -O2 ) endif() set(CMAKE_CXX_STANDARD 17) add_definitions(-DUNICODE) if (MSVC) add_definitions(/utf-8) elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") add_definitions(-finput-charset=UTF-8 -fexec-charset=UTF-8) endif() endif() if(APPLE) list(APPEND qtkeychain_SOURCES keychain_apple.mm) list(APPEND qtkeychain_LIBRARIES "-framework Foundation" "-framework Security") endif() if(HAIKU) list(APPEND qtkeychain_SOURCES keychain_haiku.cpp) find_library(BE_LIBRARY be REQUIRED) list(APPEND qtkeychain_LIBRARIES ${BE_LIBRARY}) endif() if(UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU AND NOT EMSCRIPTEN) option(LIBSECRET_SUPPORT "Build with libsecret support" ON) if(LIBSECRET_SUPPORT) pkg_check_modules(LIBSECRET REQUIRED libsecret-1) add_definitions(-DHAVE_LIBSECRET=1) INCLUDE_DIRECTORIES(${LIBSECRET_INCLUDE_DIRS}) LINK_DIRECTORIES(${LIBSECRET_LIBRARY_DIRS}) list(APPEND qtkeychain_LIBRARIES_PRIVATE ${LIBSECRET_LIBRARIES}) endif() add_definitions(-DKEYCHAIN_DBUS=1) list(APPEND qtkeychain_SOURCES keychain_unix.cpp gnomekeyring.cpp libsecret.cpp plaintextstore.cpp) qt_add_dbus_interface(qtkeychain_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/org.kde.KWallet.xml kwallet_interface KWalletInterface) list(APPEND qtkeychain_LIBRARIES ${QTDBUS_LIBRARIES} ) endif() if(ANDROID) list(APPEND qtkeychain_SOURCES keychain_android.cpp androidkeystore.cpp plaintextstore.cpp) list(APPEND qtkeychain_LIBRARIES_PRIVATE ${QTANDROIDEXTRAS_LIBRARIES} ) endif() QT_WRAP_CPP(qtkeychain_MOC_OUTFILES keychain.h keychain_p.h gnomekeyring_p.h) add_library(${QTKEYCHAIN_TARGET_NAME} ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES} ${qtkeychain_QM_FILES}) if(WIN32) set_target_properties( ${QTKEYCHAIN_TARGET_NAME} PROPERTIES DEBUG_POSTFIX "d" ) endif() target_link_libraries(${QTKEYCHAIN_TARGET_NAME} PUBLIC ${qtkeychain_LIBRARIES} PRIVATE ${qtkeychain_LIBRARIES_PRIVATE}) if(NOT INTERFACE_INCLUDE_SUFFIX) set(INTERFACE_INCLUDE_SUFFIX include) endif() # Where to find includes when building the library and using it uninstalled: in the parent dir, so that the code has to use target_include_directories(${QTKEYCHAIN_TARGET_NAME} PUBLIC $ $) # Where to find includes when using the installed qtkeychain target_include_directories(${QTKEYCHAIN_TARGET_NAME} INTERFACE $) generate_export_header(${QTKEYCHAIN_TARGET_NAME} EXPORT_FILE_NAME qkeychain_export.h EXPORT_MACRO_NAME QKEYCHAIN_EXPORT ) set_target_properties(${QTKEYCHAIN_TARGET_NAME} PROPERTIES VERSION ${QTKEYCHAIN_VERSION} SOVERSION ${QTKEYCHAIN_SOVERSION} INSTALL_RPATH_USE_LINK_PATH TRUE ) if (NOT APPLE) set_target_properties(${QTKEYCHAIN_TARGET_NAME} PROPERTIES INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}" ) endif() install(FILES keychain.h ${CMAKE_CURRENT_BINARY_DIR}/qkeychain_export.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/qt${QTKEYCHAIN_VERSION_INFIX}keychain/ ) install(TARGETS ${QTKEYCHAIN_TARGET_NAME} EXPORT Qt${QTKEYCHAIN_VERSION_INFIX}KeychainLibraryDepends RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ) frankosterfeld-qtkeychain-8ce634f/qtkeychain/androidkeystore.cpp000066400000000000000000000234051517442653100253730ustar00rootroot00000000000000#include "androidkeystore_p.h" #if QT_VERSION < QT_VERSION_CHECK(5, 7, 0) # include "private/qjni_p.h" #endif #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) # include #endif 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, 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; } } // namespace 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;)V", 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(); } frankosterfeld-qtkeychain-8ce634f/qtkeychain/androidkeystore_p.h000066400000000000000000000174671517442653100253720ustar00rootroot00000000000000/****************************************************************************** * 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 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) # include #else # include # include typedef QJniObject QAndroidJniObject; typedef QJniEnvironment QAndroidJniEnvironment; #endif namespace QKeychain { namespace javax { namespace security { namespace auth { namespace x500 { class X500Principal; } } // namespace auth namespace cert { class Certificate; } } // namespace security } // namespace javax namespace java { namespace lang { class Object : protected QAndroidJniObject { public: inline Object(jobject object) : QAndroidJniObject(object) { } inline Object(const QAndroidJniObject &object) : QAndroidJniObject(object) { } inline 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 = nullptr) const; bool load(const LoadStoreParameter ¶m = 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 crypto 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 frankosterfeld-qtkeychain-8ce634f/qtkeychain/gnomekeyring.cpp000066400000000000000000000065671517442653100246750ustar00rootroot00000000000000#include "gnomekeyring_p.h" const char *GnomeKeyring::GNOME_KEYRING_DEFAULT = nullptr; bool GnomeKeyring::isAvailable() { const GnomeKeyring &keyring = instance(); return keyring.isLoaded() && keyring.NETWORK_PASSWORD && keyring.is_available && keyring.find_password && keyring.store_password && keyring.delete_password && keyring.is_available(); } GnomeKeyring::gpointer GnomeKeyring::store_network_password(const gchar *keyring, const gchar *display_name, const gchar *user, const gchar *server, const gchar *type, const gchar *password, OperationDoneCallback callback, gpointer data, GDestroyNotify destroy_data) { if (!isAvailable()) return nullptr; return instance().store_password(instance().NETWORK_PASSWORD, keyring, display_name, password, callback, data, destroy_data, "user", user, "server", server, "type", type, static_cast(nullptr)); } GnomeKeyring::gpointer GnomeKeyring::find_network_password(const gchar *user, const gchar *server, const gchar *type, OperationGetStringCallback callback, gpointer data, GDestroyNotify destroy_data) { if (!isAvailable()) return nullptr; return instance().find_password(instance().NETWORK_PASSWORD, callback, data, destroy_data, "user", user, "server", server, "type", type, static_cast(0)); } GnomeKeyring::gpointer GnomeKeyring::delete_network_password(const gchar *user, const gchar *server, OperationDoneCallback callback, gpointer data, GDestroyNotify destroy_data) { if (!isAvailable()) return nullptr; return instance().delete_password(instance().NETWORK_PASSWORD, callback, data, destroy_data, "user", user, "server", server, static_cast(0)); } GnomeKeyring::GnomeKeyring() : QLibrary(QLatin1String("gnome-keyring"), 0) { static const PasswordSchema schema = { ITEM_NETWORK_PASSWORD, { { "user", ATTRIBUTE_TYPE_STRING }, { "server", ATTRIBUTE_TYPE_STRING }, { "type", ATTRIBUTE_TYPE_STRING }, { nullptr, static_cast(0) } } }; NETWORK_PASSWORD = &schema; is_available = reinterpret_cast(resolve("gnome_keyring_is_available")); find_password = reinterpret_cast(resolve("gnome_keyring_find_password")); store_password = reinterpret_cast(resolve("gnome_keyring_store_password")); delete_password = reinterpret_cast(resolve("gnome_keyring_delete_password")); } GnomeKeyring &GnomeKeyring::instance() { static GnomeKeyring keyring; return keyring; } frankosterfeld-qtkeychain-8ce634f/qtkeychain/gnomekeyring_p.h000066400000000000000000000065761517442653100246610ustar00rootroot00000000000000#ifndef QTKEYCHAIN_GNOME_P_H #define QTKEYCHAIN_GNOME_P_H #include class GnomeKeyring : private QLibrary { Q_OBJECT public: enum Result { RESULT_OK, RESULT_DENIED, RESULT_NO_KEYRING_DAEMON, RESULT_ALREADY_UNLOCKED, RESULT_NO_SUCH_KEYRING, RESULT_BAD_ARGUMENTS, RESULT_IO_ERROR, RESULT_CANCELLED, RESULT_KEYRING_ALREADY_EXISTS, RESULT_NO_MATCH }; enum ItemType { ITEM_GENERIC_SECRET = 0, ITEM_NETWORK_PASSWORD, ITEM_NOTE, ITEM_CHAINED_KEYRING_PASSWORD, ITEM_ENCRYPTION_KEY_PASSWORD, ITEM_PK_STORAGE = 0x100 }; enum AttributeType { ATTRIBUTE_TYPE_STRING, ATTRIBUTE_TYPE_UINT32 }; typedef char gchar; typedef void *gpointer; typedef bool gboolean; typedef struct { ItemType item_type; struct { const gchar *name; AttributeType type; } attributes[32]; } PasswordSchema; typedef void (*OperationGetStringCallback)(Result result, bool binary, const char *string, gpointer data); typedef void (*OperationDoneCallback)(Result result, gpointer data); typedef void (*GDestroyNotify)(gpointer data); static const char *GNOME_KEYRING_DEFAULT; static bool isAvailable(); static gpointer store_network_password(const gchar *keyring, const gchar *display_name, const gchar *user, const gchar *server, const gchar *type, const gchar *password, OperationDoneCallback callback, gpointer data, GDestroyNotify destroy_data); static gpointer find_network_password(const gchar *user, const gchar *server, const gchar *type, OperationGetStringCallback callback, gpointer data, GDestroyNotify destroy_data); static gpointer delete_network_password(const gchar *user, const gchar *server, OperationDoneCallback callback, gpointer data, GDestroyNotify destroy_data); private: GnomeKeyring(); static GnomeKeyring &instance(); const PasswordSchema *NETWORK_PASSWORD; typedef gboolean(is_available_fn)(void); typedef gpointer(store_password_fn)(const PasswordSchema *schema, const gchar *keyring, const gchar *display_name, const gchar *password, OperationDoneCallback callback, gpointer data, GDestroyNotify destroy_data, ...); typedef gpointer(find_password_fn)(const PasswordSchema *schema, OperationGetStringCallback callback, gpointer data, GDestroyNotify destroy_data, ...); typedef gpointer(delete_password_fn)(const PasswordSchema *schema, OperationDoneCallback callback, gpointer data, GDestroyNotify destroy_data, ...); is_available_fn *is_available; find_password_fn *find_password; store_password_fn *store_password; delete_password_fn *delete_password; }; #endif frankosterfeld-qtkeychain-8ce634f/qtkeychain/keychain.cpp000066400000000000000000000130021517442653100237500ustar00rootroot00000000000000/****************************************************************************** * Copyright (C) 2011-2015 Frank Osterfeld * * * * This program is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * * or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution * * details, check the accompanying file 'COPYING'. * *****************************************************************************/ #include "keychain.h" #include "keychain_p.h" using namespace QKeychain; Job::Job(JobPrivate *q, QObject *parent) : QObject(parent), d(q) { } Job::~Job() { delete d; } QString Job::service() const { return d->service; } QSettings *Job::settings() const { return d->settings; } void Job::setSettings(QSettings *settings) { d->settings = settings; } void Job::start() { QMetaObject::invokeMethod(this, "doStart", Qt::QueuedConnection); } bool Job::autoDelete() const { return d->autoDelete; } void Job::setAutoDelete(bool autoDelete) { d->autoDelete = autoDelete; } bool Job::insecureFallback() const { return d->insecureFallback; } void Job::setInsecureFallback(bool insecureFallback) { d->insecureFallback = insecureFallback; } void Job::doStart() { JobExecutor::instance()->enqueue(this); } void Job::emitFinished() { emit finished(this); if (d->autoDelete) deleteLater(); } void Job::emitFinishedWithError(Error error, const QString &errorString) { d->error = error; d->errorString = errorString; emitFinished(); } void Job::scheduledStart() { if (d->service.isEmpty() && d->key.isEmpty()) { emitFinishedWithError(EntryNotFound, tr("Both service name and key are empty")); return; } d->scheduledStart(); } Error Job::error() const { return d->error; } QString Job::errorString() const { return d->errorString; } void Job::setError(Error error) { d->error = error; } void Job::setErrorString(const QString &errorString) { d->errorString = errorString; } ReadPasswordJob::ReadPasswordJob(const QString &service, QObject *parent) : Job(new ReadPasswordJobPrivate(service, this), parent) { } ReadPasswordJob::~ReadPasswordJob() { } QString ReadPasswordJob::textData() const { return QString::fromUtf8(d->data); } QByteArray ReadPasswordJob::binaryData() const { return d->data; } QString Job::key() const { return d->key; } void Job::setKey(const QString &key_) { d->key = key_; } WritePasswordJob::WritePasswordJob(const QString &service, QObject *parent) : Job(new WritePasswordJobPrivate(service, this), parent) { } WritePasswordJob::~WritePasswordJob() { } void WritePasswordJob::setBinaryData(const QByteArray &data) { d->data = data; d->mode = JobPrivate::Binary; } void WritePasswordJob::setTextData(const QString &data) { d->data = data.toUtf8(); d->mode = JobPrivate::Text; } DeletePasswordJob::DeletePasswordJob(const QString &service, QObject *parent) : Job(new DeletePasswordJobPrivate(service, this), parent) { } DeletePasswordJob::~DeletePasswordJob() { } DeletePasswordJobPrivate::DeletePasswordJobPrivate(const QString &service_, DeletePasswordJob *qq) : JobPrivate(service_, qq) { } JobExecutor::JobExecutor() : QObject(nullptr), m_jobRunning(false) { } void JobExecutor::enqueue(Job *job) { m_queue.enqueue(job); startNextIfNoneRunning(); } void JobExecutor::startNextIfNoneRunning() { if (m_queue.isEmpty() || m_jobRunning) return; QPointer next; while (!next && !m_queue.isEmpty()) { next = m_queue.dequeue(); } if (next) { connect(next, &Job::finished, this, &JobExecutor::jobFinished); connect(next, &Job::destroyed, this, &JobExecutor::jobDestroyed); m_jobRunning = true; next->scheduledStart(); } } void JobExecutor::jobDestroyed(QObject *object) { Job *job = static_cast(object); Q_UNUSED(object) // for release mode job->disconnect(this); m_jobRunning = false; startNextIfNoneRunning(); } void JobExecutor::jobFinished(Job *job) { Q_UNUSED(job) // for release mode job->disconnect(this); m_jobRunning = false; startNextIfNoneRunning(); } JobExecutor *JobExecutor::s_instance = nullptr; JobExecutor *JobExecutor::instance() { if (!s_instance) s_instance = new JobExecutor; return s_instance; } ReadPasswordJobPrivate::ReadPasswordJobPrivate(const QString &service_, ReadPasswordJob *qq) : JobPrivate(service_, qq) { } JobPrivate::JobPrivate(const QString &service_, Job *qq) : q(qq), mode(Text), error(NoError), service(service_), autoDelete(true), insecureFallback(false) { } QString JobPrivate::modeToString(Mode m) { switch (m) { case Text: return QLatin1String("Text"); case Binary: return QLatin1String("Binary"); } Q_ASSERT_X(false, Q_FUNC_INFO, "Unhandled Mode value"); return QString(); } JobPrivate::Mode JobPrivate::stringToMode(const QString &s) { if (s == QLatin1String("Text") || s == QLatin1String("1")) return Text; if (s == QLatin1String("Binary") || s == QLatin1String("2")) return Binary; qCritical("Unexpected mode string '%s'", qPrintable(s)); return Text; } WritePasswordJobPrivate::WritePasswordJobPrivate(const QString &service_, WritePasswordJob *qq) : JobPrivate(service_, qq) { } frankosterfeld-qtkeychain-8ce634f/qtkeychain/keychain.h000066400000000000000000000204721517442653100234260ustar00rootroot00000000000000/****************************************************************************** * Copyright (C) 2011-2015 Frank Osterfeld * * * * This program is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * * or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution * * details, check the accompanying file 'COPYING'. * *****************************************************************************/ #ifndef KEYCHAIN_H #define KEYCHAIN_H #if !defined(QTKEYCHAIN_NO_EXPORT) # include "qkeychain_export.h" #else # define QKEYCHAIN_EXPORT #endif #include #include class QSettings; #define QTKEYCHAIN_VERSION 0x000100 namespace QKeychain { /** * Error codes */ enum Error { NoError = 0, /**< No error occurred, operation was successful */ EntryNotFound, /**< For the given key no data was found */ CouldNotDeleteEntry, /**< Could not delete existing secret data */ AccessDeniedByUser, /**< User denied access to keychain */ AccessDenied, /**< Access denied for other reasons */ NoBackendAvailable, /**< No platform-specific keychain service available */ NotImplemented, /**< Not implemented on platform */ OtherError /**< Something else went wrong (errorString() might provide details) */ }; class JobExecutor; class JobPrivate; /** * @brief Abstract base class for all QKeychain jobs. */ class QKEYCHAIN_EXPORT Job : public QObject { Q_OBJECT public: ~Job() override; /** * @return The QSettings instance used as plaintext storage if insecureFallback() is true. * @see setSettings() * @see insecureFallback() */ QSettings *settings() const; /** * @return Set the QSettings instance that will be used as plaintext storage if * insecureFallback() is true. * @see settings() * @see insecureFallback() */ void setSettings(QSettings *settings); /** * Call this method to start the job. * Typically you want to connect some slot to the finished() signal first: * * \code * SomeClass::startJob() * { * connect(job, &Job::finished, this, &SomeClass::slotJobFinished); * job->start(); * } * * SomeClass::slotJobFinished(Job *job) * { * if (job->error()) { * // handle error * } else { * // do job-specific stuff * } * } * \endcode * * @see finished() */ void start(); QString service() const; /** * @note Call this method only after finished() has been emitted. * @return The error code of the job (0 if no error). */ Error error() const; /** * @return An error message that might provide details if error() returns OtherError. */ QString errorString() const; /** * @return Whether this job autodeletes itself once finished() has been emitted. Default is * true. * @see setAutoDelete() */ bool autoDelete() const; /** * Set whether this job should autodelete itself once finished() has been emitted. * @see autoDelete() */ void setAutoDelete(bool autoDelete); /** * @return Whether this job will use plaintext storage on unsupported platforms. Default is * false. * @see setInsecureFallback() */ bool insecureFallback() const; /** * Set whether this job should use plaintext storage on unsupported platforms. * @see insecureFallback() */ void setInsecureFallback(bool insecureFallback); /** * @return The string used as key by this job. * @see setKey() */ QString key() const; /** * Set the @p key that this job will use to read or write data from/to the keychain. * The key can be an empty string. * @see key() */ void setKey(const QString &key); void emitFinished(); void emitFinishedWithError(Error, const QString &errorString); Q_SIGNALS: /** * Emitted when this job is finished. * You can connect to this signal to be notified about the job's completion. * @see start() */ void finished(QKeychain::Job *); protected: explicit Job(JobPrivate *q, QObject *parent = nullptr); Q_INVOKABLE void doStart(); private: void setError(Error error); void setErrorString(const QString &errorString); void scheduledStart(); protected: JobPrivate *const d; friend class JobExecutor; friend class JobPrivate; friend class ReadPasswordJobPrivate; friend class WritePasswordJobPrivate; friend class DeletePasswordJobPrivate; }; class ReadPasswordJobPrivate; /** * @brief Job for reading secrets from the keychain. * You can use a ReadPasswordJob to read passwords or binary data from the keychain. * This job requires a "service" string, which is basically a namespace of keys within the keychain. * This means that you can read all the pairs stored in the same service string. */ class QKEYCHAIN_EXPORT ReadPasswordJob : public Job { Q_OBJECT public: /** * Create a new ReadPasswordJob. * @param service The service string used by this job (can be empty). * @param parent The parent of this job. */ explicit ReadPasswordJob(const QString &service, QObject *parent = nullptr); ~ReadPasswordJob() override; /** * @return The binary data stored as value of this job's key(). * @see Job::key() */ QByteArray binaryData() const; /** * @return The string stored as value of this job's key(). * @see Job::key() * @warning Returns meaningless data if the data was stored as binary data. * @see WritePasswordJob::setTextData() */ QString textData() const; private: friend class QKeychain::ReadPasswordJobPrivate; }; class WritePasswordJobPrivate; /** * @brief Job for writing secrets to the keychain. * You can use a WritePasswordJob to store passwords or binary data in the keychain. * This job requires a "service" string, which is basically a namespace of keys within the keychain. * This means that you can store different pairs under the same service string. */ class QKEYCHAIN_EXPORT WritePasswordJob : public Job { Q_OBJECT public: /** * Create a new WritePasswordJob. * @param service The service string used by this job (can be empty). * @param parent The parent of this job. */ explicit WritePasswordJob(const QString &service, QObject *parent = nullptr); ~WritePasswordJob() override; /** * Set the @p data that the job will store in the keychain as binary data. * @warning setBinaryData() and setTextData() are mutually exclusive. */ void setBinaryData(const QByteArray &data); /** * Set the @p data that the job will store in the keychain as string. * Typically @p data is a password. * @warning setBinaryData() and setTextData() are mutually exclusive. */ void setTextData(const QString &data); private: friend class QKeychain::WritePasswordJobPrivate; }; class DeletePasswordJobPrivate; /** * @brief Job for deleting secrets from the keychain. * You can use a DeletePasswordJob to delete passwords or binary data from the keychain. * This job requires a "service" string, which is basically a namespace of keys within the keychain. * This means that you can delete all the pairs stored in the same service string. */ class QKEYCHAIN_EXPORT DeletePasswordJob : public Job { Q_OBJECT public: /** * Create a new DeletePasswordJob. * @param service The service string used by this job (can be empty). * @param parent The parent of this job. */ explicit DeletePasswordJob(const QString &service, QObject *parent = nullptr); ~DeletePasswordJob() override; private: friend class QKeychain::DeletePasswordJobPrivate; }; /** * Checks whether there is a viable secure backend available. * This particularly matters on UNIX platforms where multiple different backends * exist and none might be available. * * Note that using the insecure fallback will work even if no secure backend is available. * * @since 0.14.0 */ QKEYCHAIN_EXPORT bool isAvailable(); } // namespace QKeychain #endif frankosterfeld-qtkeychain-8ce634f/qtkeychain/keychain_android.cpp000066400000000000000000000162701517442653100254620ustar00rootroot00000000000000/****************************************************************************** * 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" #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) # include #endif using namespace QKeychain; using android::content::Context; using android::security::KeyPairGeneratorSpec; using java::io::ByteArrayInputStream; using java::io::ByteArrayOutputStream; using java::security::KeyPair; using java::security::KeyPairGenerator; using java::security::KeyStore; using java::security::interfaces::RSAPrivateKey; using java::security::interfaces::RSAPublicKey; using java::util::Calendar; using javax::crypto::Cipher; 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; } } // namespace 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 auto 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 auto cipher = Cipher::getInstance(QStringLiteral("RSA/ECB/PKCS1Padding")); if (!cipher || !cipher.init(Cipher::DECRYPT_MODE, entry.getPrivateKey())) { q->emitFinishedWithError(Error::OtherError, tr("Could not create decryption cipher")); 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 auto start = Calendar::getInstance(); const auto end = Calendar::getInstance(); end.add(Calendar::YEAR, 99); const KeyPairGeneratorSpec spec = #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) KeyPairGeneratorSpec::Builder(Context(QtAndroid::androidActivity())) . #elif QT_VERSION < QT_VERSION_CHECK(6, 4, 0) KeyPairGeneratorSpec::Builder( Context(QNativeInterface::QAndroidApplication::context())) . #elif QT_VERSION < QT_VERSION_CHECK(6, 7, 0) KeyPairGeneratorSpec::Builder( Context((jobject)QNativeInterface::QAndroidApplication::context())) . #else KeyPairGeneratorSpec::Builder( Context(QNativeInterface::QAndroidApplication::context().object())) . #endif setAlias(alias) .setSubject( X500Principal(QStringLiteral("CN=QtKeychain, O=Android Authority"))) .setSerialNumber(java::math::BigInteger::ONE) .setStartDate(start.getTime()) .setEndDate(end.getTime()) .build(); const auto 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 auto cipher = Cipher::getInstance(QStringLiteral("RSA/ECB/PKCS1Padding")); if (!cipher || !cipher.init(Cipher::ENCRYPT_MODE, publicKey)) { q->emitFinishedWithError(Error::OtherError, tr("Could not create encryption cipher")); 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 auto 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(); } bool QKeychain::isAvailable() { return true; } frankosterfeld-qtkeychain-8ce634f/qtkeychain/keychain_apple.mm000066400000000000000000000260531517442653100247720ustar00rootroot00000000000000/****************************************************************************** * 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")); } }; @interface AppleKeychainInterface : NSObject - (instancetype)initWithJob:(Job *)job andPrivateJob:(JobPrivate *)privateJob; - (void)keychainTaskFinished; - (void)keychainReadTaskFinished:(NSData *)retrievedData; - (void)keychainTaskFinishedWithError:(OSStatus)status descriptiveMessage:(NSString *)descriptiveMessage; @end @interface AppleKeychainInterface () { QPointer _job; QPointer _privateJob; } @end @implementation AppleKeychainInterface - (instancetype)initWithJob:(Job *)job andPrivateJob:(JobPrivate *)privateJob { self = [super init]; if (self) { _job = job; _privateJob = privateJob; } return self; } - (void)dealloc { [NSNotificationCenter.defaultCenter removeObserver:self]; [super dealloc]; } - (void)keychainTaskFinished { if (_job) { _job->emitFinished(); } } - (void)keychainReadTaskFinished:(NSData *)retrievedData { if (_privateJob) { _privateJob->data.clear(); _privateJob->mode = JobPrivate::Binary; if (retrievedData != nil) { _privateJob->data = QByteArray::fromNSData(retrievedData); } } if (_job) { _job->emitFinished(); } } - (void)keychainTaskFinishedWithError:(OSStatus)status descriptiveMessage:(NSString *)descriptiveMessage { const auto localisedDescriptiveMessage = Job::tr([descriptiveMessage UTF8String]); const ErrorDescription error = ErrorDescription::fromStatus(status); const auto fullMessage = localisedDescriptiveMessage.isEmpty() ? error.message : QStringLiteral("%1: %2").arg(localisedDescriptiveMessage, error.message); if (_job) { _job->emitFinishedWithError(error.code, fullMessage); } } @end static void StartReadPassword(const QString &service, const QString &key, AppleKeychainInterface *const interface) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ NSDictionary *const query = @{ (__bridge NSString *)kSecClass : (__bridge NSString *)kSecClassGenericPassword, (__bridge NSString *)kSecAttrService : service.toNSString(), (__bridge NSString *)kSecAttrAccount : key.toNSString(), (__bridge NSString *)kSecReturnData : @YES, }; CFTypeRef dataRef = nil; const OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &dataRef); if (status == errSecSuccess) { const CFDataRef castedDataRef = (CFDataRef)dataRef; NSData *const data = (__bridge NSData *)castedDataRef; dispatch_async(dispatch_get_main_queue(), ^{ [interface keychainReadTaskFinished:data]; [interface release]; }); } else { NSString *const descriptiveErrorString = @"Could not retrieve private key from keystore"; dispatch_async(dispatch_get_main_queue(), ^{ [interface keychainTaskFinishedWithError:status descriptiveMessage:descriptiveErrorString]; [interface release]; }); } if (dataRef) { CFRelease(dataRef); } }); } static void StartWritePassword(const QString &service, const QString &key, const QByteArray &data, AppleKeychainInterface *const interface) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ NSDictionary *const query = @{ (__bridge NSString *)kSecClass : (__bridge NSString *)kSecClassGenericPassword, (__bridge NSString *)kSecAttrService : service.toNSString(), (__bridge NSString *)kSecAttrAccount : key.toNSString(), }; OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, nil); if (status == errSecSuccess) { NSDictionary *const update = @{ (__bridge NSString *)kSecValueData : data.toNSData(), }; status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)update); } else { NSDictionary *const insert = @{ (__bridge NSString *)kSecClass : (__bridge NSString *)kSecClassGenericPassword, (__bridge NSString *)kSecAttrService : service.toNSString(), (__bridge NSString *)kSecAttrAccount : key.toNSString(), (__bridge NSString *)kSecValueData : data.toNSData(), }; status = SecItemAdd((__bridge const CFDictionaryRef)insert, nil); } if (status == errSecSuccess) { dispatch_async(dispatch_get_main_queue(), ^{ [interface keychainTaskFinished]; [interface release]; }); } else { NSString *const descriptiveErrorString = @"Could not store data in settings"; dispatch_async(dispatch_get_main_queue(), ^{ [interface keychainTaskFinishedWithError:status descriptiveMessage:descriptiveErrorString]; [interface release]; }); } }); } static void StartDeletePassword(const QString &service, const QString &key, AppleKeychainInterface *const interface) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ NSDictionary *const query = @{ (__bridge NSString *)kSecClass : (__bridge NSString *)kSecClassGenericPassword, (__bridge NSString *)kSecAttrService : service.toNSString(), (__bridge NSString *)kSecAttrAccount : key.toNSString(), }; const OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query); if (status == errSecSuccess) { dispatch_async(dispatch_get_main_queue(), ^{ [interface keychainTaskFinished]; [interface release]; }); } else { NSString *const descriptiveErrorString = @"Could not remove private key from keystore"; dispatch_async(dispatch_get_main_queue(), ^{ [interface keychainTaskFinishedWithError:status descriptiveMessage:descriptiveErrorString]; [interface release]; }); } }); } void ReadPasswordJobPrivate::scheduledStart() { AppleKeychainInterface *const interface = [[AppleKeychainInterface alloc] initWithJob:q andPrivateJob:this]; StartReadPassword(service, key, interface); } void WritePasswordJobPrivate::scheduledStart() { AppleKeychainInterface *const interface = [[AppleKeychainInterface alloc] initWithJob:q andPrivateJob:this]; StartWritePassword(service, key, data, interface); } void DeletePasswordJobPrivate::scheduledStart() { AppleKeychainInterface *const interface = [[AppleKeychainInterface alloc] initWithJob:q andPrivateJob:this]; StartDeletePassword(service, key, interface); } bool QKeychain::isAvailable() { return true; } frankosterfeld-qtkeychain-8ce634f/qtkeychain/keychain_haiku.cpp000066400000000000000000000116321517442653100251400ustar00rootroot00000000000000/****************************************************************************** * 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(nullptr) { if (be_app) 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())); 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); } bool QKeychain::isAvailable() { return true; } frankosterfeld-qtkeychain-8ce634f/qtkeychain/keychain_p.h000066400000000000000000000104711517442653100237430ustar00rootroot00000000000000/****************************************************************************** * Copyright (C) 2011-2015 Frank Osterfeld * * * * This program is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * * or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution * * details, check the accompanying file 'COPYING'. * *****************************************************************************/ #ifndef KEYCHAIN_P_H #define KEYCHAIN_P_H #include #include #include #include #include #if defined(KEYCHAIN_DBUS) # include # include "kwallet_interface.h" #else class QDBusPendingCallWatcher; #endif #include "keychain.h" namespace QKeychain { class JobExecutor; class JobPrivate : public QObject { Q_OBJECT public: enum Mode { Text, Binary, Map }; 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; public 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 JobPrivate { Q_OBJECT public: explicit ReadPasswordJobPrivate(const QString &service_, ReadPasswordJob *qq); void scheduledStart() override; #if defined(KEYCHAIN_DBUS) void fallbackOnError(const QDBusError &err) override; private Q_SLOTS: void kwalletOpenFinished(QDBusPendingCallWatcher *watcher) override; void kwalletEntryTypeFinished(QDBusPendingCallWatcher *watcher); void kwalletFinished(QDBusPendingCallWatcher *watcher) override; #else // moc's too dumb to respect above macros, so just define empty slot implementations private Q_SLOTS: void kwalletOpenFinished(QDBusPendingCallWatcher *) override { } void kwalletEntryTypeFinished(QDBusPendingCallWatcher *) { } void kwalletFinished(QDBusPendingCallWatcher *) override { } #endif friend class ReadPasswordJob; }; class WritePasswordJobPrivate : public JobPrivate { Q_OBJECT public: explicit WritePasswordJobPrivate(const QString &service_, WritePasswordJob *qq); void scheduledStart() override; #if defined(KEYCHAIN_DBUS) void fallbackOnError(const QDBusError &err) override; #endif friend class WritePasswordJob; }; class DeletePasswordJobPrivate : public JobPrivate { Q_OBJECT public: explicit DeletePasswordJobPrivate(const QString &service_, DeletePasswordJob *qq); void scheduledStart() override; #if defined(KEYCHAIN_DBUS) void fallbackOnError(const QDBusError &err) override; #endif protected: void doStart(); friend class DeletePasswordJob; }; class JobExecutor : public QObject { Q_OBJECT public: static JobExecutor *instance(); void enqueue(Job *job); private: explicit JobExecutor(); void startNextIfNoneRunning(); private Q_SLOTS: void jobFinished(QKeychain::Job *); void jobDestroyed(QObject *object); private: static JobExecutor *s_instance; QQueue> m_queue; bool m_jobRunning; }; } // namespace QKeychain #endif // KEYCHAIN_P_H frankosterfeld-qtkeychain-8ce634f/qtkeychain/keychain_unix.cpp000066400000000000000000000607661517442653100250360ustar00rootroot00000000000000/****************************************************************************** * Copyright (C) 2011-2015 Frank Osterfeld * * * * This program is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * * or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution * * details, check the accompanying file 'COPYING'. * *****************************************************************************/ #include "keychain_p.h" #include "gnomekeyring_p.h" #include "libsecret_p.h" #include "plaintextstore_p.h" #include using namespace QKeychain; enum KeyringBackend { Backend_LibSecretKeyring, Backend_GnomeKeyring, Backend_Kwallet4, Backend_Kwallet5, Backend_Kwallet6, }; enum DesktopEnvironment { DesktopEnv_Gnome, DesktopEnv_Kde4, DesktopEnv_Plasma5, DesktopEnv_Plasma6, DesktopEnv_Unity, DesktopEnv_Xfce, DesktopEnv_Other }; static constexpr const char KWALLET6_DBUS_IFACE[] = "org.kde.kwalletd6"; static constexpr const char KWALLET6_DBUS_PATH[] = "/modules/kwalletd6"; static constexpr const char KWALLET5_DBUS_IFACE[] = "org.kde.kwalletd5"; static constexpr const char KWALLET5_DBUS_PATH[] = "/modules/kwalletd5"; static constexpr const char KWALLET4_DBUS_IFACE[] = "org.kde.kwalletd"; static constexpr const char KWALLET4_DBUS_PATH[] = "/modules/kwalletd"; // the following detection algorithm is derived from chromium, // licensed under BSD, see base/nix/xdg_util.cc static DesktopEnvironment getKdeVersion() { QByteArray value = qgetenv("KDE_SESSION_VERSION"); if (value == "6") { return DesktopEnv_Plasma6; } else if (value == "5") { return DesktopEnv_Plasma5; } else if (value == "4") { return DesktopEnv_Kde4; } else { // most likely KDE3 return DesktopEnv_Other; } } static DesktopEnvironment detectDesktopEnvironment() { QByteArray xdgCurrentDesktop = qgetenv("XDG_CURRENT_DESKTOP"); if (xdgCurrentDesktop == "GNOME") { return DesktopEnv_Gnome; } else if (xdgCurrentDesktop == "Unity") { return DesktopEnv_Unity; } else if (xdgCurrentDesktop == "KDE") { return getKdeVersion(); } else if (xdgCurrentDesktop == "XFCE") { return DesktopEnv_Xfce; } QByteArray desktopSession = qgetenv("DESKTOP_SESSION"); if (desktopSession == "gnome") { return DesktopEnv_Gnome; } else if (desktopSession == "kde") { return getKdeVersion(); } else if (desktopSession == "kde4") { return DesktopEnv_Kde4; } else if (desktopSession.contains("xfce") || desktopSession == "xubuntu") { return DesktopEnv_Xfce; } if (!qgetenv("GNOME_DESKTOP_SESSION_ID").isEmpty()) { return DesktopEnv_Gnome; } else if (!qgetenv("KDE_FULL_SESSION").isEmpty()) { return getKdeVersion(); } return DesktopEnv_Other; } static bool isKwalletAvailable(const char *dbusIface, const char *dbusPath) { if (!QDBusConnection::sessionBus().isConnected()) return false; org::kde::KWallet iface(QLatin1String(dbusIface), QLatin1String(dbusPath), QDBusConnection::sessionBus()); // At this point iface.isValid() can return false even though the // interface is activatable by making a call. Hence we check whether // a wallet can be opened. 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. */ // Check if user wants to override detection logic QByteArray backendOverride = qgetenv("QTKEYCHAIN_BACKEND"); if (backendOverride == "libsecret") { return Backend_LibSecretKeyring; } else if (backendOverride == "gnome") { return Backend_GnomeKeyring; } else if (backendOverride == "kwallet4") { return Backend_Kwallet4; } else if (backendOverride == "kwallet5") { return Backend_Kwallet5; } else if (backendOverride == "kwallet6") { return Backend_Kwallet6; } switch (detectDesktopEnvironment()) { case DesktopEnv_Kde4: return Backend_Kwallet4; case DesktopEnv_Plasma5: if (isKwalletAvailable(KWALLET5_DBUS_IFACE, KWALLET5_DBUS_PATH)) { return Backend_Kwallet5; } if (LibSecretKeyring::isAvailable()) { return Backend_LibSecretKeyring; } if (GnomeKeyring::isAvailable()) { return Backend_GnomeKeyring; } // During startup the keychain backend might just not have started yet return Backend_Kwallet5; case DesktopEnv_Plasma6: if (isKwalletAvailable(KWALLET6_DBUS_IFACE, KWALLET6_DBUS_PATH)) { return Backend_Kwallet6; } if (LibSecretKeyring::isAvailable()) { return Backend_LibSecretKeyring; } if (GnomeKeyring::isAvailable()) { return Backend_GnomeKeyring; } // During startup the keychain backend might just not have started yet return Backend_Kwallet6; case DesktopEnv_Gnome: case DesktopEnv_Unity: case DesktopEnv_Xfce: case DesktopEnv_Other: default: if (LibSecretKeyring::isAvailable()) { return Backend_LibSecretKeyring; } if (GnomeKeyring::isAvailable()) { return Backend_GnomeKeyring; } if (isKwalletAvailable(KWALLET6_DBUS_IFACE, KWALLET6_DBUS_PATH)) { return Backend_Kwallet6; } if (isKwalletAvailable(KWALLET5_DBUS_IFACE, KWALLET5_DBUS_PATH)) { return Backend_Kwallet5; } // During startup the keychain backend might just not have started yet // // This doesn't need to be libsecret because LibSecretKeyring::isAvailable() // only fails if the libsecret shared library couldn't be loaded. In contrast // to that GnomeKeyring::isAvailable() can return false if the shared library // *was* loaded but its libgnome_keyring::is_available() returned false. // // In the future there should be a difference between "API available" and // "keychain available". return Backend_GnomeKeyring; } } static KeyringBackend getKeyringBackend() { static KeyringBackend backend = detectKeyringBackend(); return backend; } static void kwalletReadPasswordScheduledStartImpl(const char *service, const char *path, ReadPasswordJobPrivate *priv) { if (QDBusConnection::sessionBus().isConnected()) { priv->iface = new org::kde::KWallet(QLatin1String(service), QLatin1String(path), QDBusConnection::sessionBus(), priv); const QDBusPendingReply reply = priv->iface->networkWallet(); auto watcher = new QDBusPendingCallWatcher(reply, priv); priv->connect(watcher, &QDBusPendingCallWatcher::finished, priv, &ReadPasswordJobPrivate::kwalletWalletFound); } else { // D-Bus is not reachable so none can tell us something about KWalletd QDBusError err(QDBusError::NoServer, ReadPasswordJobPrivate::tr("D-Bus is not running")); priv->fallbackOnError(err); } } void ReadPasswordJobPrivate::scheduledStart() { switch (getKeyringBackend()) { case Backend_LibSecretKeyring: { if (!LibSecretKeyring::findPassword(key, q->service(), this)) { q->emitFinishedWithError(OtherError, tr("Unknown error")); } } break; case Backend_GnomeKeyring: this->mode = JobPrivate::Text; if (!GnomeKeyring::find_network_password( key.toUtf8().constData(), q->service().toUtf8().constData(), "plaintext", reinterpret_cast( &JobPrivate::gnomeKeyring_readCb), this, nullptr)) q->emitFinishedWithError(OtherError, tr("Unknown error")); break; case Backend_Kwallet4: kwalletReadPasswordScheduledStartImpl(KWALLET4_DBUS_IFACE, KWALLET4_DBUS_PATH, this); break; case Backend_Kwallet5: kwalletReadPasswordScheduledStartImpl(KWALLET5_DBUS_IFACE, KWALLET5_DBUS_PATH, this); break; case Backend_Kwallet6: kwalletReadPasswordScheduledStartImpl(KWALLET6_DBUS_IFACE, KWALLET6_DBUS_PATH, this); break; } } void JobPrivate::kwalletWalletFound(QDBusPendingCallWatcher *watcher) { watcher->deleteLater(); const QDBusPendingReply reply = *watcher; // Don't timeout after 25s, but 24 days // This allows to wait for user to unlock wallet, e.g. at Plasma startup iface->setTimeout(0x7FFFFFFF); const QDBusPendingReply pendingReply = iface->open(reply.value(), 0, q->service()); auto pendingWatcher = new QDBusPendingCallWatcher(pendingReply, this); connect(pendingWatcher, &QDBusPendingCallWatcher::finished, this, &JobPrivate::kwalletOpenFinished); } static QPair mapGnomeKeyringError(int result) { Q_ASSERT(result != GnomeKeyring::RESULT_OK); switch (result) { case GnomeKeyring::RESULT_DENIED: return qMakePair(AccessDenied, QObject::tr("Access to keychain denied")); case GnomeKeyring::RESULT_NO_KEYRING_DAEMON: return qMakePair(NoBackendAvailable, QObject::tr("No keyring daemon")); case GnomeKeyring::RESULT_ALREADY_UNLOCKED: return qMakePair(OtherError, QObject::tr("Already unlocked")); case GnomeKeyring::RESULT_NO_SUCH_KEYRING: return qMakePair(OtherError, QObject::tr("No such keyring")); case GnomeKeyring::RESULT_BAD_ARGUMENTS: return qMakePair(OtherError, QObject::tr("Bad arguments")); case GnomeKeyring::RESULT_IO_ERROR: return qMakePair(OtherError, QObject::tr("I/O error")); case GnomeKeyring::RESULT_CANCELLED: return qMakePair(OtherError, QObject::tr("Cancelled")); case GnomeKeyring::RESULT_KEYRING_ALREADY_EXISTS: return qMakePair(OtherError, QObject::tr("Keyring already exists")); case GnomeKeyring::RESULT_NO_MATCH: return qMakePair(EntryNotFound, QObject::tr("No match")); default: break; } return qMakePair(OtherError, QObject::tr("Unknown error")); } void JobPrivate::gnomeKeyring_readCb(int result, const char *string, JobPrivate *self) { if (result == GnomeKeyring::RESULT_OK) { if (self->mode == JobPrivate::Text) self->data = QByteArray(string); else self->data = QByteArray::fromBase64(string); self->q->emitFinished(); } else if (self->mode == JobPrivate::Text) { self->mode = JobPrivate::Binary; if (!GnomeKeyring::find_network_password( self->key.toUtf8().constData(), self->q->service().toUtf8().constData(), "base64", reinterpret_cast( &JobPrivate::gnomeKeyring_readCb), self, nullptr)) self->q->emitFinishedWithError(OtherError, tr("Unknown error")); } else { const QPair errorResult = mapGnomeKeyringError(result); self->q->emitFinishedWithError(errorResult.first, errorResult.second); } } void ReadPasswordJobPrivate::fallbackOnError(const QDBusError &err) { PlainTextStore plainTextStore(q->service(), q->settings()); if (q->insecureFallback() && plainTextStore.contains(key)) { mode = plainTextStore.readMode(key); data = plainTextStore.readData(key); if (plainTextStore.error() != NoError) q->emitFinishedWithError(plainTextStore.error(), plainTextStore.errorString()); else q->emitFinished(); } else { if (err.type() == QDBusError::ServiceUnknown) // KWalletd not running q->emitFinishedWithError(NoBackendAvailable, tr("No keychain service available")); else q->emitFinishedWithError( OtherError, tr("Could not open wallet: %1; %2") .arg(QDBusError::errorString(err.type()), err.message())); } } void ReadPasswordJobPrivate::kwalletOpenFinished(QDBusPendingCallWatcher *watcher) { watcher->deleteLater(); const QDBusPendingReply reply = *watcher; if (reply.isError()) { fallbackOnError(reply.error()); return; } PlainTextStore plainTextStore(q->service(), q->settings()); if (plainTextStore.contains(key)) { // We previously stored data in the insecure QSettings, but now have KWallet available. // Do the migration data = plainTextStore.readData(key); const WritePasswordJobPrivate::Mode mode = plainTextStore.readMode(key); plainTextStore.remove(key); q->emitFinished(); auto j = new WritePasswordJob(q->service(), nullptr); j->setSettings(q->settings()); j->setKey(key); j->setAutoDelete(true); if (mode == WritePasswordJobPrivate::Binary) j->setBinaryData(data); else if (mode == WritePasswordJobPrivate::Text) j->setTextData(QString::fromUtf8(data)); else Q_ASSERT(false); j->start(); return; } walletHandle = reply.value(); if (walletHandle < 0) { q->emitFinishedWithError(AccessDenied, tr("Access to keychain denied")); return; } const QDBusPendingReply nextReply = iface->entryType(walletHandle, q->service(), key, q->service()); auto nextWatcher = new QDBusPendingCallWatcher(nextReply, this); connect(nextWatcher, &QDBusPendingCallWatcher::finished, this, &ReadPasswordJobPrivate::kwalletEntryTypeFinished); } // Must be in sync with KWallet::EntryType (kwallet.h) enum KWalletEntryType { Unknown = 0, Password, Stream, Map }; void ReadPasswordJobPrivate::kwalletEntryTypeFinished(QDBusPendingCallWatcher *watcher) { watcher->deleteLater(); if (watcher->isError()) { const auto err = watcher->error(); q->emitFinishedWithError(OtherError, tr("Could not determine data type: %1; %2") .arg(QDBusError::errorString(err.type()), err.message())); return; } const QDBusPendingReply reply = *watcher; const int value = reply.value(); switch (value) { case Unknown: q->emitFinishedWithError(EntryNotFound, tr("Entry not found")); return; case Password: mode = Text; break; case Stream: mode = Binary; break; case KWalletEntryType::Map: { mode = JobPrivate::Map; break; } default: q->emitFinishedWithError(OtherError, tr("Unknown kwallet entry type '%1'").arg(value)); return; } const auto nextReply = (mode == Text) ? QDBusPendingCall(iface->readPassword(walletHandle, q->service(), key, q->service())) : (mode == JobPrivate::Map) ? QDBusPendingCall(iface->readMap(walletHandle, q->service(), key, q->service())) : QDBusPendingCall(iface->readEntry(walletHandle, q->service(), key, q->service())); auto nextWatcher = new QDBusPendingCallWatcher(nextReply, this); connect(nextWatcher, &QDBusPendingCallWatcher::finished, this, &ReadPasswordJobPrivate::kwalletFinished); } void ReadPasswordJobPrivate::kwalletFinished(QDBusPendingCallWatcher *watcher) { if (!watcher->isError()) { if (mode == Binary) { QDBusPendingReply reply = *watcher; if (reply.isValid()) { data = reply.value(); } } else if (mode == Map) { QDBusPendingReply reply = *watcher; if (reply.isValid()) { QByteArray v = reply.value(); QMap map; QDataStream ds(&v, QIODevice::ReadOnly); ds.setVersion(QDataStream::Qt_5_15); ds >> map; QJsonObject json; for (auto it = map.constBegin(); it != map.constEnd(); ++it) { json.insert(it.key(), it.value()); } QJsonDocument doc(json); data = doc.toJson(QJsonDocument::Compact); } } else { QDBusPendingReply reply = *watcher; if (reply.isValid()) { data = reply.value().toUtf8(); } } } JobPrivate::kwalletFinished(watcher); } static void kwalletWritePasswordScheduledStart(const char *service, const char *path, JobPrivate *priv) { if (QDBusConnection::sessionBus().isConnected()) { priv->iface = new org::kde::KWallet(QLatin1String(service), QLatin1String(path), QDBusConnection::sessionBus(), priv); const QDBusPendingReply reply = priv->iface->networkWallet(); auto watcher = new QDBusPendingCallWatcher(reply, priv); priv->connect(watcher, &QDBusPendingCallWatcher::finished, priv, &JobPrivate::kwalletWalletFound); } else { // D-Bus is not reachable so none can tell us something about KWalletd QDBusError err(QDBusError::NoServer, WritePasswordJobPrivate::tr("D-Bus is not running")); priv->fallbackOnError(err); } } void WritePasswordJobPrivate::scheduledStart() { auto descr = service; if (service.isEmpty()) { descr = key; } else if (!key.isEmpty()) { descr = key + "@" + service; } switch (getKeyringBackend()) { case Backend_LibSecretKeyring: { if (!LibSecretKeyring::writePassword(descr, 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, descr.toUtf8().constData(), key.toUtf8().constData(), service.constData(), type.toUtf8().constData(), password.constData(), reinterpret_cast( &JobPrivate::gnomeKeyring_writeCb), this, nullptr)) q->emitFinishedWithError(OtherError, tr("Unknown error")); } break; case Backend_Kwallet4: kwalletWritePasswordScheduledStart(KWALLET4_DBUS_IFACE, KWALLET4_DBUS_PATH, this); break; case Backend_Kwallet5: kwalletWritePasswordScheduledStart(KWALLET5_DBUS_IFACE, KWALLET5_DBUS_PATH, this); break; case Backend_Kwallet6: kwalletWritePasswordScheduledStart(KWALLET6_DBUS_IFACE, KWALLET6_DBUS_PATH, this); break; } } void WritePasswordJobPrivate::fallbackOnError(const QDBusError &err) { if (!q->insecureFallback()) { q->emitFinishedWithError(OtherError, tr("Could not open wallet: %1; %2") .arg(QDBusError::errorString(err.type()), err.message())); return; } PlainTextStore plainTextStore(q->service(), q->settings()); plainTextStore.write(key, data, mode); if (plainTextStore.error() != NoError) q->emitFinishedWithError(plainTextStore.error(), plainTextStore.errorString()); else q->emitFinished(); } void JobPrivate::gnomeKeyring_writeCb(int result, JobPrivate *self) { if (result == GnomeKeyring::RESULT_OK) { self->q->emitFinished(); } else { const QPair errorResult = mapGnomeKeyringError(result); self->q->emitFinishedWithError(errorResult.first, errorResult.second); } } void JobPrivate::kwalletOpenFinished(QDBusPendingCallWatcher *watcher) { watcher->deleteLater(); QDBusPendingReply reply = *watcher; if (reply.isError()) { fallbackOnError(reply.error()); return; } PlainTextStore plainTextStore(q->service(), q->settings()); if (plainTextStore.contains(key)) { // If we had previously written to QSettings, but we now have a kwallet available, migrate // and delete old insecure data plainTextStore.remove(key); } const int handle = reply.value(); if (handle < 0) { q->emitFinishedWithError(AccessDenied, tr("Access to keychain denied")); return; } QDBusPendingReply nextReply; if (!data.isNull()) { if (mode == Text) { nextReply = iface->writePassword(handle, q->service(), key, QString::fromUtf8(data), q->service()); } else { Q_ASSERT(mode == Binary); nextReply = iface->writeEntry(handle, q->service(), key, data, q->service()); } } else { nextReply = iface->removeEntry(handle, q->service(), key, q->service()); } auto nextWatcher = new QDBusPendingCallWatcher(nextReply, this); connect(nextWatcher, &QDBusPendingCallWatcher::finished, this, &JobPrivate::kwalletFinished); } 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, nullptr)) q->emitFinishedWithError(OtherError, tr("Unknown error")); } break; case Backend_Kwallet4: kwalletWritePasswordScheduledStart(KWALLET4_DBUS_IFACE, KWALLET4_DBUS_PATH, this); break; case Backend_Kwallet5: kwalletWritePasswordScheduledStart(KWALLET5_DBUS_IFACE, KWALLET5_DBUS_PATH, this); break; case Backend_Kwallet6: kwalletWritePasswordScheduledStart(KWALLET6_DBUS_IFACE, KWALLET6_DBUS_PATH, this); break; } } void DeletePasswordJobPrivate::fallbackOnError(const QDBusError &err) { QScopedPointer local(!q->settings() ? new QSettings(q->service()) : nullptr); QSettings *actual = q->settings() ? q->settings() : local.data(); if (!q->insecureFallback()) { q->emitFinishedWithError(OtherError, tr("Could not open wallet: %1; %2") .arg(QDBusError::errorString(err.type()), err.message())); return; } actual->remove(key); actual->sync(); q->emitFinished(); } bool QKeychain::isAvailable() { return LibSecretKeyring::isAvailable() || GnomeKeyring::isAvailable() || isKwalletAvailable(KWALLET6_DBUS_IFACE, KWALLET6_DBUS_PATH) || isKwalletAvailable(KWALLET5_DBUS_IFACE, KWALLET5_DBUS_PATH); } frankosterfeld-qtkeychain-8ce634f/qtkeychain/keychain_win.cpp000066400000000000000000000242731517442653100246410ustar00rootroot00000000000000/****************************************************************************** * 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 "plaintextstore_p.h" #include #include #include #include #include #include using namespace QKeychain; namespace { const std::wstring PRODUCT_NAME = L"QtKeychain"; const std::wstring ENCRYPTED_DATA_KEY = L"QtKeychain-encrypted data"; const std::wstring ATTRIBUTE_KEY = L"QtKeychain Attrib"; constexpr quint64 MAX_ATTRIBUTE_SIZE = 256; constexpr quint64 MAX_ATTRIBUTE_COUNT = 64; constexpr qsizetype MAX_BLOB_SIZE = CRED_MAX_CREDENTIAL_BLOB_SIZE + MAX_ATTRIBUTE_SIZE * MAX_ATTRIBUTE_COUNT; QString formatWinError(ulong errorCode) { return QStringLiteral("WindowsError: %1: %2") .arg(QString::number(errorCode, 16), QString::fromWCharArray(_com_error(errorCode).ErrorMessage())); } // decrpyted data, error std::pair unprotectData(const QByteArray &encrypted) { DATA_BLOB blob_in, blob_out; blob_in.pbData = const_cast(reinterpret_cast(encrypted.data())); blob_in.cbData = encrypted.size(); if (!CryptUnprotectData(&blob_in, nullptr, nullptr, nullptr, nullptr, 0, &blob_out)) { return { {}, formatWinError(GetLastError()) }; } QByteArray decrypted(reinterpret_cast(blob_out.pbData), blob_out.cbData); SecureZeroMemory(blob_out.pbData, blob_out.cbData); LocalFree(blob_out.pbData); return { decrypted, {} }; } // encrypted data, error std::pair protectData(const QByteArray &data) { DATA_BLOB blob_in, blob_out; blob_in.pbData = const_cast(reinterpret_cast(data.data())); blob_in.cbData = data.size(); if (!CryptProtectData(&blob_in, ENCRYPTED_DATA_KEY.data(), nullptr, nullptr, nullptr, 0, &blob_out)) { return { {}, formatWinError(GetLastError()) }; } QByteArray encrypted(reinterpret_cast(blob_out.pbData), blob_out.cbData); LocalFree(blob_out.pbData); return { encrypted, {} }; } } // namespace #if defined(USE_CREDENTIAL_STORE) /*** * The credentials store has a limit of CRED_MAX_CREDENTIAL_BLOB_SIZE (5* 512) * As this might not be enough in some scenarios, for bigger payloads we use CryptProtectData which * offers similar protection as CredWrite in combination with CredWrite. We distribute the protected * payload to the PCREDENTIALW->CredentialBlob as well as PCREDENTIALW->AttributeCount. This * increases the max payload size to CRED_MAX_CREDENTIAL_BLOB_SIZE + 64 * 256 = 18944. As the * protected data requires more space than the original payload, the effective max payload is * smaller than that. As we continue to use PCREDENTIALW as storage medium, the credentials are * still roaming. */ void ReadPasswordJobPrivate::scheduledStart() { PCREDENTIALW cred = {}; if (!CredReadW(reinterpret_cast(key.utf16()), 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; } if (cred->AttributeCount == 0) { data = QByteArray(reinterpret_cast(cred->CredentialBlob), cred->CredentialBlobSize); } else { QByteArray encrypted; encrypted.reserve(CRED_MAX_CREDENTIAL_BLOB_SIZE + cred->AttributeCount * MAX_ATTRIBUTE_SIZE); encrypted.append(reinterpret_cast(cred->CredentialBlob), cred->CredentialBlobSize); for (ulong i = 0; i < cred->AttributeCount; ++i) { encrypted.append(reinterpret_cast(cred->Attributes[i].Value), cred->Attributes[i].ValueSize); } const auto result = unprotectData(encrypted); if (!result.second.isEmpty()) { q->emitFinishedWithError(OtherError, tr("Could not decrypt data: %1").arg(result.second)); return; } data = result.first; } CredFree(cred); q->emitFinished(); } void WritePasswordJobPrivate::scheduledStart() { CREDENTIALW cred = {}; cred.Comment = const_cast(PRODUCT_NAME.data()); cred.Type = CRED_TYPE_GENERIC; cred.TargetName = const_cast(reinterpret_cast(key.utf16())); cred.Persist = CRED_PERSIST_ENTERPRISE; QByteArray buffer; std::vector attributes; if (data.size() < CRED_MAX_CREDENTIAL_BLOB_SIZE) { cred.CredentialBlob = reinterpret_cast(data.data()); cred.CredentialBlobSize = data.size(); } else { // data is too big for CredentialBlob // we encrpyt it instead with CryptProtectData which also encrpyt the data with the users // credentials The data is also protected with the roaming profile { auto result = protectData(data); if (!result.second.isEmpty()) { q->emitFinishedWithError(OtherError, tr("Encryption failed: %1").arg(result.second)); return; } if (result.first.size() > MAX_BLOB_SIZE) { q->emitFinishedWithError(OtherError, tr("Credential size exceeds maximum size of %1: %2") .arg(QString::number(MAX_BLOB_SIZE), QString::number(result.first.size()))); return; } // the data must be valid outside of the scope of result buffer = std::move(result.first); } quint64 pos = 0; auto read = [&buffer, &pos](const quint64 size, auto &dest, auto &sizeOut) { dest = reinterpret_cast::type>( buffer.data()) + pos; sizeOut = std::min(size, buffer.size() - pos); pos += sizeOut; }; read(CRED_MAX_CREDENTIAL_BLOB_SIZE, cred.CredentialBlob, cred.CredentialBlobSize); cred.AttributeCount = std::ceil((buffer.size() - pos) / static_cast(MAX_ATTRIBUTE_SIZE)); attributes.resize(cred.AttributeCount, {}); cred.Attributes = attributes.data(); for (ulong i = 0; i < cred.AttributeCount; ++i) { attributes[i].Keyword = const_cast(ATTRIBUTE_KEY.data()); read(MAX_ATTRIBUTE_SIZE, attributes[i].Value, attributes[i].ValueSize); } } if (CredWriteW(&cred, 0)) { q->emitFinished(); return; } const 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_S_INVALID_BOUND) { const QString::size_type maxTargetName = CRED_MAX_GENERIC_TARGET_NAME_LENGTH; if (key.size() > maxTargetName) { q->emitFinishedWithError( OtherError, tr("Credential key exceeds maximum size of %1").arg(maxTargetName)); return; } } q->emitFinishedWithError(OtherError, tr("Writing credentials failed: %1").arg(formatWinError(err))); } void DeletePasswordJobPrivate::scheduledStart() { if (!CredDeleteW(reinterpret_cast(key.utf16()), 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; } const auto result = unprotectData(encrypted); if (!result.second.isEmpty()) { q->emitFinishedWithError(OtherError, tr("Could not decrypt data: %1").arg(result.second)); return; } data = result.first; q->emitFinished(); } void WritePasswordJobPrivate::scheduledStart() { const auto result = protectData(data); if (!result.second.isEmpty()) { q->emitFinishedWithError(OtherError, tr("Encryption failed: %1").arg(result.second)); return; } PlainTextStore plainTextStore(q->service(), q->settings()); plainTextStore.write(key, result.first, Binary); if (plainTextStore.error() != NoError) { q->emitFinishedWithError(plainTextStore.error(), plainTextStore.errorString()); return; } q->emitFinished(); } void DeletePasswordJobPrivate::scheduledStart() { PlainTextStore plainTextStore(q->service(), q->settings()); plainTextStore.remove(key); if (plainTextStore.error() != NoError) { q->emitFinishedWithError(plainTextStore.error(), plainTextStore.errorString()); } else { q->emitFinished(); } } #endif bool QKeychain::isAvailable() { return true; } frankosterfeld-qtkeychain-8ce634f/qtkeychain/libsecret.cpp000066400000000000000000000242161517442653100241420ustar00rootroot00000000000000#if defined(HAVE_LIBSECRET) # include #endif #include "libsecret_p.h" #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 = nullptr; static secret_password_lookup_finish_t secret_password_lookup_finish_fn = nullptr; static secret_password_store_t secret_password_store_fn = nullptr; static secret_password_store_finish_t secret_password_store_finish_fn = nullptr; static secret_password_clear_t secret_password_clear_fn = nullptr; static secret_password_clear_finish_t secret_password_clear_finish_fn = nullptr; static secret_password_free_t secret_password_free_fn = nullptr; static secret_error_get_quark_t secret_error_get_quark_fn = nullptr; static QKeychain::Error gerrorToCode(const GError *error) { if (error->domain != secret_error_get_quark_fn()) { return QKeychain::OtherError; } switch (error->code) { case SECRET_ERROR_NO_SUCH_OBJECT: return QKeychain::EntryNotFound; case SECRET_ERROR_IS_LOCKED: return QKeychain::AccessDenied; default: return QKeychain::OtherError; } } static void on_password_lookup(GObject *source, GAsyncResult *result, gpointer inst) { GError *error = nullptr; callbackArg *arg = (callbackArg *)inst; gchar *password = secret_password_lookup_finish_fn(result, &error); Q_UNUSED(source); if (arg) { if (error) { QKeychain::Error code = gerrorToCode(error); arg->self->q->emitFinishedWithError(code, QString::fromUtf8(error->message)); } else { if (password) { QByteArray raw = QByteArray(password); switch (arg->self->mode) { case QKeychain::JobPrivate::Binary: arg->self->data = QByteArray::fromBase64(raw); break; case QKeychain::JobPrivate::Text: default: arg->self->data = raw; } arg->self->q->emitFinished(); } else if (arg->self->mode == QKeychain::JobPrivate::Text) { arg->self->mode = QKeychain::JobPrivate::Binary; secret_password_lookup_fn(qtkeychainSchema(), nullptr, on_password_lookup, arg, "user", arg->user.toUtf8().constData(), "server", arg->server.toUtf8().constData(), "type", "base64", nullptr); return; } else { arg->self->q->emitFinishedWithError(QKeychain::EntryNotFound, QObject::tr("Entry not found")); } } } if (error) { g_error_free(error); } if (password) { secret_password_free_fn(password); } if (arg) { delete arg; } } static void on_password_stored(GObject *source, GAsyncResult *result, gpointer inst) { GError *error = nullptr; QKeychain::JobPrivate *self = (QKeychain::JobPrivate *)inst; Q_UNUSED(source); secret_password_store_finish_fn(result, &error); if (self) { if (error) { self->q->emitFinishedWithError(gerrorToCode(error), QString::fromUtf8(error->message)); } else { self->q->emitFinished(); } } if (error) { g_error_free(error); } } static void on_password_cleared(GObject *source, GAsyncResult *result, gpointer inst) { GError *error = nullptr; QKeychain::JobPrivate *self = (QKeychain::JobPrivate *)inst; gboolean removed = secret_password_clear_finish_fn(result, &error); Q_UNUSED(source); if (self) { if (error) { self->q->emitFinishedWithError(gerrorToCode(error), QString::fromUtf8(error->message)); } else { Q_UNUSED(removed); self->q->emitFinished(); } } if (error) { g_error_free(error); } } static QString modeToString(QKeychain::JobPrivate::Mode mode) { switch (mode) { case QKeychain::JobPrivate::Binary: return "base64"; default: return "plaintext"; } } #endif bool LibSecretKeyring::isAvailable() { #if defined(HAVE_LIBSECRET) const LibSecretKeyring &keyring = instance(); if (!keyring.isLoaded()) return false; if (secret_password_lookup_fn == nullptr) return false; if (secret_password_lookup_finish_fn == nullptr) return false; if (secret_password_store_fn == nullptr) return false; if (secret_password_store_finish_fn == nullptr) return false; if (secret_password_clear_fn == nullptr) return false; if (secret_password_clear_finish_fn == nullptr) return false; if (secret_password_free_fn == nullptr) return false; if (secret_error_get_quark_fn == nullptr) return false; return true; #else return false; #endif } bool LibSecretKeyring::findPassword(const QString &user, const QString &server, QKeychain::JobPrivate *self) { #if defined(HAVE_LIBSECRET) if (!isAvailable()) { return false; } self->mode = QKeychain::JobPrivate::Text; self->data = QByteArray(); callbackArg *arg = new callbackArg; arg->self = self; arg->user = user; arg->server = server; secret_password_lookup_fn(qtkeychainSchema(), nullptr, on_password_lookup, arg, "user", user.toUtf8().constData(), "server", server.toUtf8().constData(), "type", "plaintext", nullptr); return true; #else Q_UNUSED(user) Q_UNUSED(server) Q_UNUSED(self) return false; #endif } bool LibSecretKeyring::writePassword(const QString &display_name, const QString &user, const QString &server, const QKeychain::JobPrivate::Mode mode, const QByteArray &password, QKeychain::JobPrivate *self) { #if defined(HAVE_LIBSECRET) if (!isAvailable()) { return false; } QString type = modeToString(mode); QByteArray pwd; switch (mode) { case QKeychain::JobPrivate::Binary: pwd = password.toBase64(); break; default: pwd = password; break; } secret_password_store_fn( qtkeychainSchema(), SECRET_COLLECTION_DEFAULT, display_name.toUtf8().constData(), pwd.constData(), nullptr, on_password_stored, self, "user", user.toUtf8().constData(), "server", server.toUtf8().constData(), "type", type.toUtf8().constData(), nullptr); return true; #else Q_UNUSED(display_name) Q_UNUSED(user) Q_UNUSED(server) Q_UNUSED(mode) Q_UNUSED(password) Q_UNUSED(self) return false; #endif } bool LibSecretKeyring::deletePassword(const QString &key, const QString &service, QKeychain::JobPrivate *self) { #if defined(HAVE_LIBSECRET) if (!isAvailable()) { return false; } secret_password_clear_fn(qtkeychainSchema(), nullptr, on_password_cleared, self, "user", key.toUtf8().constData(), "server", service.toUtf8().constData(), nullptr); return true; #else Q_UNUSED(key) Q_UNUSED(service) Q_UNUSED(self) return false; #endif } LibSecretKeyring::LibSecretKeyring() : QLibrary(QLatin1String("secret-1"), 0) { #ifdef HAVE_LIBSECRET if (load()) { secret_password_lookup_fn = (secret_password_lookup_t)resolve("secret_password_lookup"); secret_password_lookup_finish_fn = (secret_password_lookup_finish_t)resolve("secret_password_lookup_finish"); secret_password_store_fn = (secret_password_store_t)resolve("secret_password_store"); secret_password_store_finish_fn = (secret_password_store_finish_t)resolve("secret_password_store_finish"); secret_password_clear_fn = (secret_password_clear_t)resolve("secret_password_clear"); secret_password_clear_finish_fn = (secret_password_clear_finish_t)resolve("secret_password_clear_finish"); secret_password_free_fn = (secret_password_free_t)resolve("secret_password_free"); secret_error_get_quark_fn = (secret_error_get_quark_t)resolve("secret_error_get_quark"); } #endif } LibSecretKeyring &LibSecretKeyring::instance() { static LibSecretKeyring instance; return instance; } frankosterfeld-qtkeychain-8ce634f/qtkeychain/libsecret_p.h000066400000000000000000000014561517442653100241270ustar00rootroot00000000000000#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 frankosterfeld-qtkeychain-8ce634f/qtkeychain/org.kde.KWallet.xml000066400000000000000000000250121517442653100250720ustar00rootroot00000000000000 frankosterfeld-qtkeychain-8ce634f/qtkeychain/plaintextstore.cpp000066400000000000000000000067351517442653100252610ustar00rootroot00000000000000/****************************************************************************** * 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 } // namespace PlainTextStore::PlainTextStore(const QString &service, QSettings *settings) : m_localSettings(settings ? nullptr : new QSettings(service)), m_actualSettings(settings ? settings : m_localSettings.data()), m_error(NoError) { } 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; } frankosterfeld-qtkeychain-8ce634f/qtkeychain/plaintextstore_p.h000066400000000000000000000032331517442653100252330ustar00rootroot00000000000000/****************************************************************************** * 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; }; } // namespace QKeychain #endif // QTKEYCHAIN_PLAINTEXTSTORE_P_H frankosterfeld-qtkeychain-8ce634f/testclient.cpp000066400000000000000000000114161517442653100222020ustar00rootroot00000000000000/****************************************************************************** * 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 #ifdef Q_OS_DARWIN # include #else # include #endif #include #include "qtkeychain/keychain.h" #include using namespace QKeychain; static int printUsage() { std::cerr << "testclient store " << std::endl; std::cerr << "testclient restore " << std::endl; std::cerr << "testclient delete " << std::endl; return 1; } int main(int argc, char **argv) { #ifdef Q_OS_DARWIN // Since we use NSNotificationCenter under the hood in keychain_apple, // we use QGuiApplication to automatically configure the platform // integration stuff done in this class and not in QCoreApplication QGuiApplication app(argc, argv); #else QCoreApplication app(argc, argv); #endif const QStringList args = app.arguments(); if (args.count() < 2) return printUsage(); QStringList::ConstIterator it = args.constBegin(); ++it; if (*it == QLatin1String("store")) { if (++it == args.constEnd()) return printUsage(); const QString acc = *it; if (++it == args.constEnd()) return printUsage(); const QString pass = *it; if (++it != args.constEnd()) return printUsage(); WritePasswordJob job(QLatin1String("qtkeychain-testclient")); job.setAutoDelete(false); job.setKey(acc); job.setTextData(pass); QEventLoop loop; job.connect(&job, &Job::finished, &loop, &QEventLoop::quit); job.start(); loop.exec(); if (job.error()) { std::cerr << "Storing password failed: " << qPrintable(job.errorString()) << std::endl; return 1; } std::cout << "Password stored successfully" << std::endl; } else if (*it == QLatin1String("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, &Job::finished, &loop, &QEventLoop::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(); const QString acc = *it; if (++it != args.constEnd()) return printUsage(); ReadPasswordJob job(QLatin1String("qtkeychain-testclient")); job.setAutoDelete(false); job.setKey(acc); QEventLoop loop; job.connect(&job, &Job::finished, &loop, &QEventLoop::quit); job.start(); loop.exec(); const QString pw = job.textData(); if (job.error()) { std::cerr << "Restoring password failed: " << qPrintable(job.errorString()) << std::endl; return 1; } std::cout << qPrintable(pw) << std::endl; } else if (*it == QLatin1String("delete")) { if (++it == args.constEnd()) return printUsage(); const QString acc = *it; if (++it != args.constEnd()) return printUsage(); DeletePasswordJob job(QLatin1String("qtkeychain-testclient")); job.setAutoDelete(false); job.setKey(acc); QEventLoop loop; job.connect(&job, &Job::finished, &loop, &QEventLoop::quit); job.start(); loop.exec(); if (job.error()) { std::cerr << "Deleting password failed: " << qPrintable(job.errorString()) << std::endl; return 1; } std::cout << "Password deleted successfully" << std::endl; } else { return printUsage(); } } frankosterfeld-qtkeychain-8ce634f/translations/000077500000000000000000000000001517442653100220365ustar00rootroot00000000000000frankosterfeld-qtkeychain-8ce634f/translations/qtkeychain_de.ts000066400000000000000000000162201517442653100252170ustar00rootroot00000000000000 QKeychain::ReadPasswordJobPrivate Unknown error Unbekannter Fehler D-Bus is not running No keychain service available Kein Schlüsselbund-Dienst verfügbar Could not open wallet: %1; %2 Konnte Brieftasche nicht öffnen: %1; %2 Access to keychain denied Zugriff auf Schlüsselbund verweigert Could not determine data type: %1; %2 Datentyp kann nicht ermittelt werden: %1: %2 Unsupported entry type 'Map' Unknown kwallet entry type '%1' Could not read password: %1; %2 Passwort konnte nicht ausgelesen werden: %1; %2 Password not found Passwort nicht gefunden Entry not found Eintrag nicht gefunden Could not decrypt data Kann Daten nicht entschlüsseln QKeychain::WritePasswordJobPrivate Unknown error Unbekannter Fehler D-Bus is not running Could not open wallet: %1; %2 Konnte Brieftasche nicht öffnen: %1; %2 Access to keychain denied Zugriff auf Schlüsselbund verweigert Could not delete encrypted data from settings: access error Kann verschlüsselte Daten nicht aus den Einstellungen entfernen: Zugriffsfehler Could not delete encrypted data from settings: format error Kann verschlüsselte Daten nicht aus den Einstellungen entfernen: Formatfehler Encryption failed Verschlüsselung fehlgeschlagen Could not store encrypted data in settings: access error Kann verschlüsselte Daten nicht in den Einstellungen speichern: Zugriffsfehler Could not store encrypted data in settings: format error Kann verschlüsselte Daten nicht in den Einstellungen speichern: Formatfehler QObject Access to keychain denied Zugriff auf Schlüsselbund verweigert No keyring daemon Kein Schlüsselbund-Dienst Already unlocked Bereits entsperrt No such keyring Kein solcher Schlüsselbund Bad arguments Ungültige Argumente I/O error Ein-/Ausgabe-Fehler Cancelled Abgebrochen Keyring already exists Schlüsselbund existiert bereits No match Kein Treffer Unknown error Unbekannter Fehler %1 (OSStatus %2) frankosterfeld-qtkeychain-8ce634f/translations/qtkeychain_fr.ts000066400000000000000000000256551517442653100252520ustar00rootroot00000000000000 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 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 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 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 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 frankosterfeld-qtkeychain-8ce634f/translations/qtkeychain_ka.ts000066400000000000000000000426771517442653100252410ustar00rootroot00000000000000 KeyChainClass Read key failed: %1 გასაღების წაკითხვა ჩავარდა: %1 Write key failed: %1 გასაღების ჩაწერის შეცდომა: %1 Delete key failed: %1 გასაღების წაშლის შეცდომა: %1 QKeychain::DeletePasswordJobPrivate Could not open keystore გასაღებების საცავის გახსნა შეუძლებელია Could not remove private key from keystore გასაღებების საცავიდან პირადი გასაღების წაშლა შეუძლებელია Password not found პაროლი აღმოჩენილი არაა Unknown error უცნობი შეცდომა Could not open wallet: %1; %2 გახსნის შეცდომა საფულისთვის: %1; %2 Password entry not found პაროლის ჩანაწერი აღმოჩენილი არაა Could not decrypt data მონაცემების გაშიფვრის შეცდომა QKeychain::JobPrivate Unknown error უცნობი შეცდომა Access to keychain denied ბრელოკთან წვდომა აკრძალულია QKeychain::PlainTextStore Could not store data in settings: access error მონაცემების პარამეტრებში შენახვა შეუძლებელია: წვდომის შეცდომა Could not store data in settings: format error მონაცემების პარამეტრებში შენახვა შეუძლებელია: ფორმატის შეცდომა Could not delete data from settings: access error პარამეტრებიდან მონაცემების წაშლის შეცდომა: წვდომის შეცდომა Could not delete data from settings: format error პარამეტრებიდან მონაცემების წაშლის შეცდომა: ფორმატის შეცდომა Entry not found ჩანაწერი ვერ ვიპოვე QKeychain::ReadPasswordJobPrivate Entry not found ჩანაწერი ვერ ვიპოვე Could not open keystore გასაღებების საცავის გახსნა შეუძლებელია Could not retrieve private key from keystore გასაღებების საცავიდან პირადი გასაღების მიღება შეუძლებელია Could not create decryption cipher გაშიფვრის შიფრის შექმნა შეუძლებელია Password not found პაროლი აღმოჩენილი არაა D-Bus is not running D-Bus is გაშვებული არაა Unknown error უცნობი შეცდომა No keychain service available ბრელოკი სერვისი ხელმისაწვდომი არაა Could not open wallet: %1; %2 გახსნის შეცდომა საფულისთვის: %1; %2 Access to keychain denied ბრელოკთან წვდომა აკრძალულია Could not determine data type: %1; %2 მონაცემთა ტიპის დადგენა შეუძლებელია: %1; %2 Unsupported entry type 'Map' მხარდაუჭერელი ჩანაწერის ტიპი 'Map' Unknown kwallet entry type '%1' უცნობი kwallet-ის ჩანაწერის ტიპი '%1' Password entry not found პაროლის ჩანაწერი აღმოჩენილი არაა Could not decrypt data მონაცემების გაშიფვრის შეცდომა Could not decrypt data: %1 მონაცემების გაშიფვრის შეცდომა: %1 QKeychain::WritePasswordJobPrivate Could not open keystore გასაღებების საცავის გახსნა შეუძლებელია Could not create private key generator პირადი გასაღების გენერატორის შექმნა შეუძლებელია Could not generate new private key ახალი პირადი გასაღების გენერაცია შეუძლებელია Could not retrieve private key from keystore გასაღებების საცავიდან პირადი გასაღების მიღება შეუძლებელია Could not create encryption cipher დაშიფვრის შიფრის შექმნა შეუძლებელია Could not encrypt data მონაცემების დაშიფვრა შეუძლებელია Password not found პაროლი აღმოჩენილი არაა D-Bus is not running D-Bus is გაშვებული არაა Unknown error უცნობი შეცდომა Could not open wallet: %1; %2 გახსნის შეცდომა საფულისთვის: %1; %2 Encryption failed: %1 დაშიფვრის შეცდომა: %1 Credential size exceeds maximum size of %1: %2 ავტორიზაციის დეტალების ზომა %1-ზე მეტია: %2 Credential key exceeds maximum size of %1 ავტორიზაციის დეტალების ზომა %1-ზე მეტია Writing credentials failed: %1 ავტორიზაციის დეტალების ჩაწერა ჩავარდა: %1 QObject error 0x%1: %2 შეცდომა 0x%1: %2 Access to keychain denied ბრელოკთან წვდომა აკრძალულია No keyring daemon ბრელოკის დემონის გარეშე Already unlocked უკვე განბლოკილია No such keyring ასეთი ბრელოკი არ არსებობს Bad arguments არასწორი არგუმენტები I/O error I/O შეცდომა Cancelled გაუქმებულია Keyring already exists ბრელოკი უნდა არსებობს No match დამთხვევის გარეშე Unknown error უცნობი შეცდომა Entry not found ჩანაწერი ვერ ვიპოვე frankosterfeld-qtkeychain-8ce634f/translations/qtkeychain_nl.ts000066400000000000000000000320231517442653100252370ustar00rootroot00000000000000 KeyChainClass Read key failed: %1 De sleutel kan niet worden ingelezen: %1 Write key failed: %1 De sleutel kan niet worden weggeschreven: %1 Delete key failed: %1 De sleutel kan niet worden verwijderd: %1 QKeychain::DeletePasswordJobPrivate Could not open keystore De sleutelbos kan niet worden geopend Could not remove private key from keystore De privésleutel kan niet worden verwijderd uit de sleutelbos Password not found Het wachtwoord is niet gevonden Unknown error Onbekende foutmelding Could not open wallet: %1; %2 De sleutelbos kan niet worden geopend: %1; %2 Password entry not found Het wachtwoord is niet gevonden Could not decrypt data De gegevens kunnen niet worden ongrendeld QKeychain::JobPrivate Unknown error Onbekende foutmelding Access to keychain denied U heeft geen toegang tot de sleutelbos QKeychain::PlainTextStore Could not store data in settings: access error De gegevens kunnen niet worden opgeslagen in de instellingen: toegangsfout Could not store data in settings: format error De gegevens kunnen niet worden opgeslagen in de instellingen: opmaakfout Could not delete data from settings: access error De gegevens kunnen niet worden verwijderd uit de instellingen: toegangsfout Could not delete data from settings: format error De gegevens kunnen niet worden verwijderd uit de instellingen: opmaakfout Entry not found Het item is niet gevonden QKeychain::ReadPasswordJobPrivate Entry not found Het item is niet gevonden Could not open keystore De sleutelbos kan niet worden geopend Could not retrieve private key from keystore De privésleutel kan niet worden verwijderd uit de sleutelbos Could not create decryption cipher De ontgrendelcode kan niet worden aangemaakt Password not found Het wachtwoord is niet gevonden D-Bus is not running D-Bus is niet actief Unknown error Onbekende foutmelding No keychain service available De sleutelbosdienst is niet beschikbaar Could not open wallet: %1; %2 De sleutelbos kan niet worden geopend: %1; %2 Access to keychain denied U heeft geen toegang tot de sleutelbos Could not determine data type: %1; %2 De gegevensdrager kan niet worden bepaald: %1; %2 Unsupported entry type 'Map' Niet-ondersteund itemtype ‘Map’ Unknown kwallet entry type '%1' Onbekend KWallet-item van type ‘%1’ Password entry not found Het wachtwoord is niet gevonden Could not decrypt data De gegevens kunnen niet worden ongrendeld QKeychain::WritePasswordJobPrivate Could not open keystore De sleutelbos kan niet worden geopend Could not create private key generator De privésleutelgenerator kan niet worden gestart Could not generate new private key Er kan geen nieuwe privésleutel worden gegenereerd Could not retrieve private key from keystore De privésleutel kan niet worden opgehaald uit de sleutelbos Could not create encryption cipher De vergrendelcode kan niet worden aangemaakt Could not encrypt data De gegevens kunnen niet worden ontgrendeld Password not found Het wachtwoord is niet gevonden D-Bus is not running D-Bus is niet actief Unknown error Onbekende foutmelding Could not open wallet: %1; %2 De sleutelbos kan niet worden geopend: %1; %2 Credential size exceeds maximum size of %1 De omvang overschrijdt de maximumomvang van %1 Credential key exceeds maximum size of %1 De sleutel overschrijdt de maximumomvang van %1 Writing credentials failed: Win32 error code %1 De gegevens kunnen niet worden weggeschreven: Win32-foutcode %1 Encryption failed Het ontgrendelen is mislukt QObject error 0x%1: %2 foutmelding 0x%1: %2 Access to keychain denied U heeft geen toegang tot de sleutelbos No keyring daemon De sleutelbosdienst is niet actief Already unlocked De sleutelbos is reeds ontgrendeld No such keyring De sleutelbos bestaat niet Bad arguments Onjuiste opties I/O error I/O-fout Cancelled Geannuleerd Keyring already exists De sleutelbos bestaat reeds No match Er zijn geen overeenkomsten Unknown error Onbekende foutmelding Entry not found Het item is niet gevonden frankosterfeld-qtkeychain-8ce634f/translations/qtkeychain_ro.ts000066400000000000000000000163021517442653100252500ustar00rootroot00000000000000 QKeychain::ReadPasswordJobPrivate Unknown error Eroare necunoscută D-Bus is not running D-Bus nu rulează No keychain service available Nu există niciun serviciu de chei disponibil Kein Schlüsselbund-Dienst verfügbar Could not open wallet: %1; %2 Nu se poate deschide portofelul: %1; %2 Access to keychain denied Acces interzis la serviciul de chei Could not determine data type: %1; %2 Nu se poate stabili tipul de date: %1: %2 Unsupported entry type 'Map' Tip de înregistrare nesuportat 'Map' Unknown kwallet entry type '%1' Tip de înregistrare kwallet necunoscut '%1' Could not read password: %1; %2 Nu se poate citi parola: %1; %2 Password not found Parola nu a fost găsită Entry not found Înregistrarea nu a fost găsită Could not decrypt data Nu se poate decripta data QKeychain::WritePasswordJobPrivate Unknown error Eroare necunoscută D-Bus is not running D-Bus nu rulează Could not open wallet: %1; %2 Nu se poate deschide portofelul: %1; %2 Access to keychain denied Acces interzis la serviciul de chei Could not delete encrypted data from settings: access error Nu se pot șterge datele criptate din setări: eroare de acces Could not delete encrypted data from settings: format error Nu se pot șterge datele criptate din setări: eroare de format Encryption failed Criptarea a eșuat Could not store encrypted data in settings: access error Nu se pot stoca datele criptate în setări: eroare de acces Could not store encrypted data in settings: format error Nu se pot stoca datele criptate în setări: eroare de format QObject Access to keychain denied Acces interzis la serviciul de chei No keyring daemon Niciun demon pentru inelul de chei Already unlocked Deja deblocat No such keyring Nu există astfel de inel de chei Bad arguments Argumente greșite I/O error Eroare de I/E Cancelled Anulat Keyring already exists Inelul de chei deja există No match Nicio potrivire Unknown error Eroare necunoscută %1 (OSStatus %2) %1 (OSStatus %2) frankosterfeld-qtkeychain-8ce634f/translations/qtkeychain_ru.ts000066400000000000000000000245711517442653100252650ustar00rootroot00000000000000 QKeychain::DeletePasswordJobPrivate Unknown error Неизвестная ошибка Could not open wallet: %1; %2 Не удалось открыть бумажник: %1; %2 Password entry not found Пароль не найден Could not decrypt data Не удалось расшифровать данные QKeychain::JobPrivate Unknown error Неизвестная ошибка Access to keychain denied Доступ к связке ключей запрещён QKeychain::PlainTextStore Could not store data in settings: access error Не удалось сохранить данные в настройках: ошибка доступа Could not store data in settings: format error Не удалось сохранить данные в настройках: ошибка формата Could not delete data from settings: access error Не удалось удалить данные из настроек: ошибка доступа Could not delete data from settings: format error Не удалось удалить данные из настроек: ошибка формата Entry not found Запись не найдена QKeychain::ReadPasswordJobPrivate Password not found Пароль не найден D-Bus is not running D-Bus не запущен Unknown error Неизвестная ошибка No keychain service available Служба связки ключей недоступна Could not open wallet: %1; %2 Не удалось открыть кошелёк: %1; %2 Access to keychain denied Доступ к связке ключей запрещён Could not determine data type: %1; %2 Не удалось определить тип данных: %1; %2 Entry not found Запись не найдена Unsupported entry type 'Map' Неподдерживаемый тип записи 'Map' Unknown kwallet entry type '%1' Неизвестный тип записи kwallet '%1' Password entry not found Пароль не найден Could not decrypt data Не удалось расшифровать данные QKeychain::WritePasswordJobPrivate D-Bus is not running D-Bus не запущен Unknown error Неизвестная ошибка Could not open wallet: %1; %2 Не удалось открыть кошелёк: %1; %2 Credential size exceeds maximum size of %1 Учётные данные превышают максимальный размер %1 Credential key exceeds maximum size of %1 Ключ учётных данных превышает максимальный размер %1 Writing credentials failed: Win32 error code %1 Не удалось сохранить учётные данные: код ошибки win32 %1 Encryption failed Шифрование не удалось QObject OS X Keychain error (OSStatus %1) Ошибка связки ключей OS X (OSStatus %1) %1 (OSStatus %2) %1 (OSStatus %2) Access to keychain denied Доступ к связке ключей запрещён No keyring daemon Нет демона связки ключей Already unlocked Уже разблокировано No such keyring Связка ключей не найдена Bad arguments Неверные аргументы I/O error Ошибка ввода/вывода Cancelled Отменено Keyring already exists Связка ключей уже существует No match Нет совпадений Unknown error Неизвестная ошибка Entry not found Запись не найдена frankosterfeld-qtkeychain-8ce634f/translations/qtkeychain_sv.ts000066400000000000000000000352261517442653100252660ustar00rootroot00000000000000 sv_SE Daniel Nylander <github@danielnylander.se> Poedit 3.6 Project-Id-Version,POT-Creation-Date,PO-Revision-Date,Last-Translator,Language-Team,Language,MIME-Version,Content-Type,Content-Transfer-Encoding,Plural-Forms,X-Language,X-Qt-Contexts,X-Generator KeyChainClass Read key failed: %1 Läsning av nyckel misslyckades: %1 Write key failed: %1 Skrivning av nyckel misslyckades: %1 Delete key failed: %1 Borttagning av nyckel misslyckades: %1 QKeychain::DeletePasswordJobPrivate Could not open keystore Det gick inte att öppna nyckellagret Could not remove private key from keystore Det gick inte att ta bort den privata nyckeln från nyckelagret Password not found Lösenordet hittades inte Unknown error Okänt fel Could not open wallet: %1; %2 Det gick inte att öppna plånboken: %1; %2 Password entry not found Lösenordet hittades inte Could not decrypt data Kunde inte avkryptera data QKeychain::JobPrivate Unknown error Okänt fel Access to keychain denied Åtkomst till nyckelring nekades QKeychain::PlainTextStore Could not store data in settings: access error Det gick inte att lagra data i inställningarna: åtkomstfel Could not store data in settings: format error Det gick inte att lagra data i inställningarna: formatfel Could not delete data from settings: access error Det gick inte att ta bort data från inställningarna: åtkomstfel Could not delete data from settings: format error Det gick inte att ta bort data från inställningarna: formatfel Entry not found Posten hittades inte QKeychain::ReadPasswordJobPrivate Entry not found Posten hittades inte Could not open keystore Det gick inte att öppna nyckellagret Could not retrieve private key from keystore Det gick inte att hämta den privata nyckeln från nyckellagret Could not create decryption cipher Kunde inte skapa avkrypteringsskiffer Password not found Lösenordet hittades inte D-Bus is not running D-Bus är inte igång Unknown error Okänt fel No keychain service available Ingen nyckelringstjänst tillgänglig Could not open wallet: %1; %2 Det gick inte att öppna plånboken: %1; %2 Access to keychain denied Åtkomst till nyckelring nekades Could not determine data type: %1; %2 Datatypen kunde inte fastställas: %1; %2 Unsupported entry type 'Map' Posttypen 'Map' stöds inte Unknown kwallet entry type '%1' Okänd typ av kwallet-post '%1' Password entry not found Lösenordsposten hittades inte Could not decrypt data Kunde inte avkryptera data Could not decrypt data: %1 Det gick inte att avkryptera data: %1 QKeychain::WritePasswordJobPrivate Could not open keystore Det gick inte att öppna nyckellagret Could not create private key generator Det gick inte att skapa en generator för privata nycklar Could not generate new private key Det gick inte att generera en ny privat nyckel Could not retrieve private key from keystore Det gick inte att hämta den privata nyckeln från nyckellagret Could not create encryption cipher Det gick inte att skapa krypteringschiffer Could not encrypt data Kunde inte kryptera data Password not found Lösenordet hittades inte D-Bus is not running D-Bus är inte igång Unknown error Okänt fel Could not open wallet: %1; %2 Det gick inte att öppna plånboken: %1; %2 Encryption failed: %1 Krypteringen misslyckades: %1 Credential size exceeds maximum size of %1: %2 Autentiseringsstorleken överskrider maxstorleken för %1: %2 Credential key exceeds maximum size of %1 Autentiseringsnyckeln överskrider den maximala storleken på %1 Writing credentials failed: %1 Skrivning av autentiseringsuppgifter misslyckades: %1 QObject error 0x%1: %2 fel 0x%1: %2 Access to keychain denied Åtkomst till nyckelring nekades No keyring daemon Ingen keyring-daemon Already unlocked Redan upplåst No such keyring Ingen sådan nyckelring Bad arguments Felaktiga argument I/O error In/ut-fel Cancelled Avbruten Keyring already exists Nyckelringen finns redan No match Ingen matchning Unknown error Okänt fel Entry not found Posten hittades inte frankosterfeld-qtkeychain-8ce634f/translations/qtkeychain_zh.ts000066400000000000000000000156141517442653100252560ustar00rootroot00000000000000 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 找不到項目 Could not decrypt data 無法解密資料 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 無法從設定刪除加密資料:格式錯誤 Encryption failed 加密失敗 Could not store encrypted data in settings: access error 無法將加密資料儲存至設定:存取錯誤 Could not store encrypted data in settings: format error 無法將加密資料儲存至設定:格式錯誤 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) frankosterfeld-qtkeychain-8ce634f/translations/translations.qrc.in000066400000000000000000000001301517442653100256650ustar00rootroot00000000000000 @QM_FILE_LIST@