diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..ae483f8 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,18 @@ +--- +Checks: ' + ,*, + ,-cppcoreguidelines-pro-bounds-array-to-pointer-decay, + ,-cppcoreguidelines-special-member-functions, + ,-cppcoreguidelines-pro-type-union-access, + ,-hicpp-special-member-functions, + ,-hicpp-use-override, + ,-google-build-using-namespace, + ,-modernize-use-default-member-init, + ,-modernize-use-override, + ,-readability-redundant-member-init, + ,-readability-implicit-bool-cast, +' + +WarningsAsErrors: '' +HeaderFilterRegex: '' +AnalyzeTemporaryDtors: false diff --git a/CMakeGraphVizOptions.cmake b/CMakeGraphVizOptions.cmake new file mode 100644 index 0000000..26b3ceb --- /dev/null +++ b/CMakeGraphVizOptions.cmake @@ -0,0 +1,12 @@ +######################################## +# Generate build dependency +# +# cmake --build . --target architecture +######################################## + +SET(GRAPHVIZ_IGNORE_TARGETS AusweisAppGlobal;AusweisAppExternal;AusweisAppUiCli;cvc;fuzz;OpenSsl;tlscheck;Test;Script) +SET(GRAPHVIZ_EXTERNAL_LIBS OFF) +SET(GRAPHVIZ_EXECUTABLES ON) +SET(GRAPHVIZ_GENERATE_PER_TARGET OFF) +SET(GRAPHVIZ_GENERATE_DEPENDERS OFF) +SET(GRAPHVIZ_GRAPH_NAME AusweisApp2) diff --git a/CMakeLists.txt b/CMakeLists.txt index e0ed608..b50ce73 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,10 @@ IF(POLICY CMP0071) CMAKE_POLICY(SET CMP0071 NEW) ENDIF() +IF(POLICY CMP0074) + CMAKE_POLICY(SET CMP0074 NEW) +ENDIF() + # "tools.only" can be defined to disable the normal build and enable # cmdline "tools" only. For example: "make format" or "make package_source" IF(tools.only) @@ -33,7 +37,7 @@ ELSE() ENDIF() -PROJECT(AusweisApp2 VERSION 1.14.3 LANGUAGES ${LANGUAGES}) +PROJECT(AusweisApp2 VERSION 1.16.0 LANGUAGES ${LANGUAGES}) # Set TWEAK if not defined in PROJECT_VERSION above to # have a valid tweak version without propagating it @@ -42,7 +46,7 @@ IF(NOT PROJECT_VERSION_TWEAK) ENDIF() -IF(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT OR ANDROID OR IOS) +IF(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) SET(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/dist" CACHE PATH "default install path" FORCE) ENDIF() SET(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") @@ -86,7 +90,7 @@ IF(IOS) MESSAGE(STATUS "USE_DISTRIBUTION_PROFILE: ${USE_DISTRIBUTION_PROFILE}") ENDIF() -IF("${PROJECT_BINARY_DIR}" STREQUAL "${PROJECT_SOURCE_DIR}") +IF("${PROJECT_BINARY_DIR}" STREQUAL "${PROJECT_SOURCE_DIR}" AND NOT FORCE_SOURCE_BUILD) MESSAGE(FATAL_ERROR "in tree building is not supported!") ENDIF() @@ -96,20 +100,18 @@ ELSE() SET(CMAKE_BUILD_TYPE "DEBUG" CACHE STRING "build type configuration" FORCE) ENDIF() -IF(NOT ${CMAKE_BUILD_TYPE} STREQUAL "DEBUG" AND NOT ${CMAKE_BUILD_TYPE} STREQUAL "RELEASE") - MESSAGE(FATAL_ERROR "CMAKE_BUILD_TYPE is invalid! Available options: RELEASE, DEBUG") +IF(DESKTOP) + SET(CMAKE_AUTOUIC ON) ENDIF() - SET(CMAKE_AUTOMOC ON) -SET(CMAKE_AUTOUIC ON) SET(CMAKE_INCLUDE_CURRENT_DIR ON) SET(SRC_DIR ${PROJECT_SOURCE_DIR}/src) SET(TEST_DIR ${PROJECT_SOURCE_DIR}/test) SET(RESOURCES_DIR ${PROJECT_SOURCE_DIR}/resources) SET(PACKAGING_DIR ${RESOURCES_DIR}/packaging) -SET(EXECUTABLE_BASE_NAME AusweisApp2) -SET(EXECUTABLE_NAME ${EXECUTABLE_BASE_NAME}${CMAKE_EXECUTABLE_SUFFIX}) +SET(COPYRIGHT_TEXT "Ⓒ\; 2014-2018 ${VENDOR}") +STRING(REPLACE " \& " " \& " COPYRIGHT_TEXT ${COPYRIGHT_TEXT}) INCLUDE(Tools) INCLUDE(DVCS) @@ -131,13 +133,16 @@ ADD_SUBDIRECTORY(src) IF("${CMAKE_BUILD_TYPE}" STREQUAL "DEBUG") INCLUDE(CTest) - CONFIGURE_FILE("${CMAKE_MODULE_PATH}/CTestCustom.cmake.in" "${CMAKE_BINARY_DIR}/CTestCustom.cmake" COPYONLY) + CONFIGURE_FILE("${CMAKE_MODULE_PATH}/CTestCustom.cmake.in" "${CMAKE_BINARY_DIR}/CTestCustom.cmake" @ONLY) + CONFIGURE_FILE("${RESOURCES_DIR}/sonar-project.properties.in" "${CMAKE_BINARY_DIR}/sonar-project.properties" @ONLY) ENDIF() IF(BUILD_TESTING) ADD_SUBDIRECTORY(test) ENDIF() -ADD_SUBDIRECTORY(utils) +IF(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/utils") + ADD_SUBDIRECTORY(utils) +ENDIF() INCLUDE(Packaging) diff --git a/LICENSE.officially.txt b/LICENSE.officially.txt index cdbc575..59b7e5a 100644 --- a/LICENSE.officially.txt +++ b/LICENSE.officially.txt @@ -466,14 +466,19 @@ Die verwendeten OpenSource-Bibliotheken unterliegen den folgenden Nutzungsbeding Qt Lizenz: LGPL v3 - Version: 5.9.3 + Version: 5.11.2 Adresse: https://www.qt.io/ http_parser Lizenz: MIT - Version: 2.7.1 + Version: 2.8.1 Adresse: https://github.com/nodejs/http-parser/ +Android Support Library V4 + Lizenz: Apache 2.0 + Version: 21.0.3 + Adresse: https://developer.android.com/topic/libraries/support-library/ + Die Lizenztexte lauten in ihrer originalen Fassung wie folgt: @@ -680,3 +685,210 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + + + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/LICENSE.txt b/LICENSE.txt index e43986d..13f65b5 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -416,14 +416,19 @@ Die verwendeten OpenSource-Bibliotheken unterliegen den folgenden Nutzungsbeding Qt Lizenz: LGPL v3 - Version: 5.9.3 + Version: 5.11.2 Adresse: https://www.qt.io/ http_parser Lizenz: MIT - Version: 2.7.1 + Version: 2.8.1 Adresse: https://github.com/nodejs/http-parser/ +Android Support Library V4 + Lizenz: Apache 2.0 + Version: 21.0.3 + Adresse: https://developer.android.com/topic/libraries/support-library/ + Die Lizenztexte lauten in ihrer originalen Fassung wie folgt: @@ -630,3 +635,210 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + + + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.rst b/README.rst index 3927a6e..b5fe6d6 100644 --- a/README.rst +++ b/README.rst @@ -4,7 +4,7 @@ AusweisApp2 Kontakt ------- | Governikus GmbH & Co. KG -| Am Fallturm 9 +| Hochschulring 4 | 28359 Bremen | ausweisapp2@governikus.de @@ -27,15 +27,6 @@ Die separate README und das Skript unter "libs" dienen dem Aufzusetzen der notwendigen Build-Umgebung und dem automatisierten Bauen der notwendigen Bibliotheken mit den entsprechenden Patches. -Derzeit ist es leider noch nicht möglich, die AusweisApp2 ohne Patches -an OpenSSL und Qt voll funktionsfähig auszuliefern. -Dies liegt unter anderem an dem notwendigen RSA-PSK-Patch für -OpenSSL 1.0.2, welcher mit OpenSSL 1.1.0 nicht mehr notwendig ist. -Diese OpenSSL-Version wird allerdings erst mit Qt 5.10 unterstützt. -(siehe https://bugreports.qt.io/browse/QTBUG-52905) - -OpenSSL 1.1.0 wird mit der AusweisApp2 1.14.0 unterstützt. -LibreSSL wird auf Grund des fehlenden RSA-PSK nicht unterstützt. Build diff --git a/Releasing.rst b/Releasing.rst index bcf2d8a..ceafb70 100644 --- a/Releasing.rst +++ b/Releasing.rst @@ -23,8 +23,8 @@ Tag bauen --------- Die Release-Jobs müssen nach dem Tag manuell gestartet werden! -Jenkins erstellt das Release anhand des Bookmarks "release" oder des tags/changesets, -welcher als Parameter übergeben wird. +Jenkins erstellt das Release anhand des Parameters 'changeset'. Dort sollte der angebrachte +Tag oder die jeweilige Revision übergeben werden. Nachdem die notwendigen Jobs (Windows/macOS/Docs/...) durchgelaufen sind, muss der Job für den AppCast gestartet werden. diff --git a/appveyor.yml b/appveyor.yml index 420db2c..c3db5f7 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,24 +4,28 @@ environment: matrix: - PlatformToolset: mingw-w64 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 - QTPath: C:\Qt\5.9\mingw53_32 + QTPath: C:\Qt\5.11\mingw53_32 + OPENSSLPath: C:\OpenSSL-v11-Win32 - PlatformToolset: v140 platform: x64 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 - QTPath: C:\Qt\5.9\msvc2015_64 + QTPath: C:\Qt\5.11\msvc2015_64 + OPENSSLPath: C:\OpenSSL-v11-Win64 ARCHI: amd64 - PlatformToolset: v140 platform: Win32 - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 - QTPath: C:\Qt\5.9\msvc2015 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + QTPath: C:\Qt\5.11\msvc2015 + OPENSSLPath: C:\OpenSSL-v11-Win32 ARCHI: x86 - PlatformToolset: v141 platform: x64 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - QTPath: C:\Qt\5.9\msvc2017_64 + QTPath: C:\Qt\5.11\msvc2017_64 + OPENSSLPath: C:\OpenSSL-v11-Win64 ARCHI: amd64 configuration: @@ -29,7 +33,7 @@ configuration: #- Debug install: - - if "%PlatformToolset%"=="mingw-w64" set PATH=C:\mingw-w64\i686-5.3.0-posix-dwarf-rt_v4-rev0\mingw32\bin;%PATH:C:\Program Files\Git\usr\bin;=% + - if "%PlatformToolset%"=="mingw-w64" set PATH=C:\mingw-w64\i686-6.3.0-posix-dwarf-rt_v5-rev1\mingw32\bin;%PATH:C:\Program Files\Git\usr\bin;=% - if "%PlatformToolset%"=="v140" call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %archi% - if "%PlatformToolset%"=="v141" call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" %archi% @@ -53,15 +57,15 @@ before_build: build_script: - cd "%APPVEYOR_BUILD_FOLDER%" - - set PATH=%QTPATH%;%QTPATH%/bin;%PATH% + - set OPENSSL_ROOT=%OPENSSLPath% + - set PATH=%QTPATH%;%QTPATH%/bin;%OPENSSLPath%;%PATH% - echo %PATH% + - echo %OPENSSL_ROOT% - mkdir _build - cd _build - ps: | - # Use -DFORCE_LEGACY_OPENSSL=ON to build with the available openssl 1.0.2 of the appveyor build image - # otherwise openssl 1.1.x is required with an additional download and build step - cmake -G "$generator" -DCMAKE_BUILD_TYPE="$env:CONFIGURATION" -DFORCE_LEGACY_OPENSSL=ON .. + cmake -G "$generator" -DCMAKE_BUILD_TYPE="$env:CONFIGURATION" .. if ($LastExitCode -ne 0) { throw "Exec: $ErrorMessage" } @@ -74,7 +78,7 @@ test_script: - cd "%APPVEYOR_BUILD_FOLDER%"/_build - set PATH=%QTPATH%;%QTPATH%/bin;%PATH% - echo %PATH% - - ctest -VV -C "%CONFIGURATION%" + - ctest --output-on-failure -C "%CONFIGURATION%" on_finish: - cd "%APPVEYOR_BUILD_FOLDER%" diff --git a/cmake/CTestCustom.cmake.in b/cmake/CTestCustom.cmake.in index 165bdde..a8fd13c 100644 --- a/cmake/CTestCustom.cmake.in +++ b/cmake/CTestCustom.cmake.in @@ -1,4 +1,15 @@ LIST(APPEND CTEST_CUSTOM_COVERAGE_EXCLUDE "/test/") LIST(APPEND CTEST_CUSTOM_COVERAGE_EXCLUDE "/external/") +LIST(APPEND CTEST_CUSTOM_COVERAGE_EXCLUDE "/utils/") LIST(APPEND CTEST_CUSTOM_COVERAGE_EXCLUDE \\.moc moc_ qrc_ ui_ _ui) LIST(APPEND CTEST_EXTRA_COVERAGE_GLOB *.cpp *.h) + +SET(CTEST_SOURCE_DIRECTORY "@PROJECT_SOURCE_DIR@") +SET(CTEST_BINARY_DIRECTORY "@PROJECT_BINARY_DIR@") +SET(CTEST_CMAKE_GENERATOR "@CMAKE_GENERATOR@") +SET(CTEST_COMMAND @CMAKE_CTEST_COMMAND@) + +IF(CMAKE_SCRIPT_MODE_FILE) + INCLUDE(CTestCoverageCollectGCOV) + ctest_coverage_collect_gcov(TARBALL ${CTEST_BINARY_DIRECTORY}/gcov.tar GCOV_COMMAND @COVERAGE_COMMAND@) +ENDIF() diff --git a/cmake/CompilerFlags.cmake b/cmake/CompilerFlags.cmake index 9896ef5..89a1895 100644 --- a/cmake/CompilerFlags.cmake +++ b/cmake/CompilerFlags.cmake @@ -2,11 +2,17 @@ ADD_DEFINITIONS(-DUNICODE) ADD_DEFINITIONS(-DQT_MESSAGELOGCONTEXT) ADD_DEFINITIONS(-DQT_NO_CAST_FROM_BYTEARRAY) ADD_DEFINITIONS(-DQT_NO_CAST_TO_ASCII) -ADD_DEFINITIONS(-DQT_RESTRICTED_CAST_FROM_ASCII) ADD_DEFINITIONS(-DQT_NO_FOREACH) ADD_DEFINITIONS(-DQT_NO_KEYWORDS) ADD_DEFINITIONS(-DQT_NO_EXCEPTIONS) +IF(CMAKE_VERSION VERSION_LESS "3.12") + ADD_DEFINITIONS(-DQT_RESTRICTED_CAST_FROM_ASCII) +ELSE() + ADD_COMPILE_DEFINITIONS($<$:QT_RESTRICTED_CAST_FROM_ASCII>) + ADD_COMPILE_DEFINITIONS($<$:QT_NO_CAST_FROM_ASCII>) +ENDIF() + IF(QT_VENDOR STREQUAL "Governikus") ADD_DEFINITIONS(-DGOVERNIKUS_QT) ADD_DEFINITIONS(-DQT_DEPRECATED_WARNINGS) @@ -31,13 +37,22 @@ ELSE() ADD_DEFINITIONS(-DQT_STRICT_ITERATORS) STRING(REPLACE "-fexceptions" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + STRING(REPLACE "-frtti" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wcast-qual -Wshadow") + SET(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} -g") + IF(ANDROID AND CMAKE_ANDROID_ARCH_ABI STREQUAL "arm64-v8a") + SET(USE_LD bfd) + ELSE() + SET(USE_LD gold) + ENDIF() + + ADD_FLAG(-fuse-ld=${USE_LD} VAR CMAKE_EXE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS LINK -fuse-ld=${USE_LD}) ADD_FLAG(-flto VAR CMAKE_EXE_LINKER_FLAGS_RELEASE CMAKE_SHARED_LINKER_FLAGS_RELEASE LINK -flto) + ADD_FLAG(-fno-rtti VAR CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_MINSIZEREL) ADD_FLAG(-fno-exceptions) ADD_FLAG(-fstack-protector-strong -fstack-protector) - ADD_FLAG(-fuse-ld=gold VAR CMAKE_EXE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS LINK -fuse-ld=gold) ADD_FLAG(-Wold-style-cast) ADD_FLAG(-Wmost) ADD_FLAG(-Wpedantic) @@ -58,6 +73,7 @@ ELSE() ADD_FLAG(-Winitializer-overrides) ADD_FLAG(-Wunreachable-code-aggressive) ADD_FLAG(-Wnewline-eof) + ADD_FLAG(-Wdate-time) ADD_FLAG(-Wno-gnu-zero-variadic-macro-arguments) # Qt (qDebug) is not compatible @@ -66,6 +82,8 @@ ELSE() SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffunction-sections -fdata-sections") IF(CMAKE_COMPILER_IS_GNUCXX) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -finline-limit=64") + ELSEIF(CMAKE_VERSION VERSION_LESS "3.13" AND CMAKE_VERSION VERSION_GREATER_EQUAL "3.12") + LIST(APPEND CMAKE_CXX_COMPILER_PREDEFINES_COMMAND "--target=${ANDROID_TOOLCHAIN_MACHINE_NAME}") ENDIF() SET(CMAKE_CXX_VISIBILITY_PRESET hidden) ENDIF() @@ -80,7 +98,7 @@ ELSE() ENDIF() IF(APPLE AND NOT CMAKE_SYSTEM_VERSION VERSION_LESS 14) - # Allow warning for LSSharedFileListItemResolve since we support OSX 10.9, too! + # Allow warning for SMCopyAllJobDictionaries, Apple will provide an alternative, until then we are stuck with this deprecated method SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=deprecated-declarations") ENDIF() @@ -96,12 +114,12 @@ ELSE() ENDIF() IF(CMAKE_CXX_COMPILER_ID STREQUAL Intel) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd1875,1682,2259,654,177") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd1875,1682,2259,654,177,1599") ENDIF() ENDIF() IF(APPLE AND NOT IOS) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ObjC++ -mmacosx-version-min=10.9") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ObjC++ -mmacosx-version-min=10.11") ENDIF() diff --git a/cmake/Helper.cmake b/cmake/Helper.cmake index 3a25513..4792414 100644 --- a/cmake/Helper.cmake +++ b/cmake/Helper.cmake @@ -121,7 +121,7 @@ FUNCTION(GET_FILE_EXTENSIONS _result) cmake_parse_arguments(_PARAM "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) SET(FILE_EXTENSIONS *.cpp) - IF(IOS) + IF(APPLE) LIST(APPEND FILE_EXTENSIONS *.m *.mm) ENDIF() @@ -244,7 +244,7 @@ FUNCTION(GET_ANDROID_TOOLCHAIN_VARS _prefix _machine) GET_FILENAME_COMPONENT(ANDROID_TOOLCHAIN_MACHINE_NAME "${CMAKE_CXX_ANDROID_TOOLCHAIN_PREFIX}" NAME) STRING(REGEX REPLACE "-$" "" ANDROID_TOOLCHAIN_MACHINE_NAME "${ANDROID_TOOLCHAIN_MACHINE_NAME}") STRING(REGEX MATCH "/toolchains/(.*)/prebuilt/" _unused "${CMAKE_CXX_ANDROID_TOOLCHAIN_PREFIX}") - STRING(REGEX REPLACE "-${CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION}$" "" ANDROID_TOOLCHAIN_PREFIX "${CMAKE_MATCH_1}") + STRING(REGEX REPLACE "-${ANDROID_NDK_TOOLCHAIN_VERSION}$" "" ANDROID_TOOLCHAIN_PREFIX "${CMAKE_MATCH_1}") SET(${_prefix} ${ANDROID_TOOLCHAIN_PREFIX} PARENT_SCOPE) SET(${_machine} ${ANDROID_TOOLCHAIN_MACHINE_NAME} PARENT_SCOPE) ENDFUNCTION() @@ -299,16 +299,14 @@ FUNCTION(FETCH_TARGET_LOCATION _destination _target) SET(multiValueArgs) cmake_parse_arguments(_PARAM "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - GET_TARGET_PROPERTY(tmp "${_target}" IMPORTED_LOCATION_${CMAKE_BUILD_TYPE}) - - IF(NOT tmp) - IF("${CMAKE_BUILD_TYPE}" STREQUAL "DEBUG") - GET_TARGET_PROPERTY(tmp "${_target}" IMPORTED_LOCATION_RELEASE) - ELSEIF("${CMAKE_BUILD_TYPE}" STREQUAL "RELEASE") - GET_TARGET_PROPERTY(tmp "${_target}" IMPORTED_LOCATION_DEBUG) - ENDIF() + IF(CMAKE_BUILD_TYPE STREQUAL "MINSIZEREL" OR CMAKE_BUILD_TYPE STREQUAL "RELWITHDEBINFO") + SET(BUILD_TYPE RELEASE) + ELSE() + SET(BUILD_TYPE ${CMAKE_BUILD_TYPE}) ENDIF() + GET_TARGET_PROPERTY(tmp "${_target}" IMPORTED_LOCATION_${BUILD_TYPE}) + IF(NOT tmp) GET_TARGET_PROPERTY(tmp "${_target}" IMPORTED_LOCATION) ENDIF() diff --git a/cmake/Install.cmake b/cmake/Install.cmake index b0729e3..cc49d59 100644 --- a/cmake/Install.cmake +++ b/cmake/Install.cmake @@ -19,6 +19,25 @@ SET(SEARCH_ADDITIONAL_DIRS " DIRLIST_OF_FILES(ADDITIONAL_DIRS ${CMAKE_BINARY_DIR}/src/*${CMAKE_SHARED_LIBRARY_SUFFIX}) ") +SET(DEPENDENCY_CHECK " + FUNCTION(gp_resolved_file_type_override file type) + MESSAGE(STATUS \"Collect dependency: \${file}\") + + IF(file MATCHES \"libstdc.+\.dll\" + OR file MATCHES \"libwinpthread-.+\.dll\" + OR file MATCHES \"libgcc_s_.+\.dll\" + OR file MATCHES \"libssp-.+\.dll\") + + get_filename_component(path \"${CMAKE_CXX_COMPILER}\" DIRECTORY) + get_filename_component(pathDest \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}\" DIRECTORY) + IF(NOT file MATCHES \"\${path}\" AND NOT file MATCHES \"\${pathDest}\") + MESSAGE(FATAL_ERROR \"Wrong source path detected: \${file} | Should be: \${path} or \${pathDest}\") + ENDIF() + + ENDIF() + ENDFUNCTION() +") + IF(WIN32) IF(MSVC) @@ -29,6 +48,10 @@ IF(WIN32) INCLUDE(InstallRequiredSystemLibraries) ENDIF() + IF(TARGET Qt5::Qml) + FETCH_TARGET_LOCATION(libQuickControls2 "Qt5::QuickControls2") + INSTALL(FILES ${libQuickControls2} DESTINATION . COMPONENT Runtime) + ENDIF() FETCH_TARGET_LOCATION(libSvg "Qt5::Svg") FETCH_TARGET_LOCATION(pluginSvg "Qt5::QSvgPlugin") FETCH_TARGET_LOCATION(pluginGif "Qt5::QGifPlugin") @@ -38,6 +61,7 @@ IF(WIN32) ELSE() FETCH_TARGET_LOCATION(platformWin "Qt5::QWindowsIntegrationPlugin") ENDIF() + FETCH_TARGET_LOCATION(styleVista "Qt5::QWindowsVistaStylePlugin") INSTALL(TARGETS AusweisApp DESTINATION . COMPONENT Application) INSTALL(FILES ${libSvg} DESTINATION . COMPONENT Runtime) @@ -45,20 +69,24 @@ IF(WIN32) INSTALL(FILES ${pluginGif} DESTINATION imageformats COMPONENT Runtime) INSTALL(FILES ${pluginJpeg} DESTINATION imageformats COMPONENT Runtime) INSTALL(FILES ${platformWin} DESTINATION platforms COMPONENT Runtime) + INSTALL(FILES ${styleVista} DESTINATION styles COMPONENT Runtime) INSTALL(CODE " + ${DEPENDENCY_CHECK} ${SEARCH_ADDITIONAL_DIRS} INCLUDE(BundleUtilities) - FIXUP_BUNDLE(\"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${EXECUTABLE_NAME}\" \"\" \"${TOOLCHAIN_BIN_PATH};\${ADDITIONAL_DIRS}\") + FIXUP_BUNDLE(\"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}${CMAKE_EXECUTABLE_SUFFIX}\" \"${libQuickControls2}\" \"${TOOLCHAIN_BIN_PATH};\${ADDITIONAL_DIRS}\") " COMPONENT Runtime) ELSEIF(APPLE AND NOT IOS) + SET(MACOS_BUNDLE_MACOS_DIR ../MacOS) SET(MACOS_BUNDLE_PLUGINS_DIR ../PlugIns) SET(MACOS_BUNDLE_FRAMEWORKS_DIR ../Frameworks) SET(MACOS_BUNDLE_RESOURCES_DIR ../Resources) + SET(MACOS_BUNDLE_LOGIN_ITEMS_DIR ../Library/LoginItems) # We need to include the following (i.e. all) image format plug-ins, # since those seem to be loaded upon program start-up. Not including @@ -67,38 +95,71 @@ ELSEIF(APPLE AND NOT IOS) # depend on to be loaded as well, thus resulting in two sets of Qt # libraries being loaded (ours from the bundle and the ones from the # installation) and the program misbehaving (crashing). - FETCH_TARGET_LOCATION(platformMac "Qt5::QCocoaIntegrationPlugin") - FOREACH (qtComponent QtCore Qt5Gui Qt5Network Qt5Svg Qt5Widgets) FOREACH(plugin ${${qtComponent}_PLUGINS}) GET_TARGET_PROPERTY(pluginPath ${plugin} LOCATION) GET_FILENAME_COMPONENT(pluginDir ${pluginPath} DIRECTORY) GET_FILENAME_COMPONENT(pluginName ${pluginPath} NAME) GET_FILENAME_COMPONENT(pluginDirName ${pluginDir} NAME) + + IF(pluginDirName STREQUAL "platforms" AND NOT plugin STREQUAL "Qt5::QCocoaIntegrationPlugin") + CONTINUE() + ENDIF() + INSTALL(FILES ${pluginPath} DESTINATION ${MACOS_BUNDLE_PLUGINS_DIR}/${pluginDirName} COMPONENT Runtime) LIST(APPEND ADDITIONAL_BUNDLE_FILES_TO_SIGN "/Contents/PlugIns/${pluginDirName}/${pluginName}") ENDFOREACH() ENDFOREACH() - INSTALL(TARGETS AusweisApp DESTINATION . COMPONENT Application) - INSTALL(FILES ${platformMac} DESTINATION ${MACOS_BUNDLE_PLUGINS_DIR}/platforms COMPONENT Runtime) + IF(TARGET Qt5::Qml) + FOREACH(entry QtQuick QtQuick.2 QtQml QtGraphicalEffects Qt) + SET(_dir "${QT_HOST_PREFIX}/qml") + FILE(GLOB_RECURSE DYLIB "${_dir}/${entry}/*.dylib") + FOREACH(_lib ${DYLIB}) + FILE(RELATIVE_PATH _lib_dest "${_dir}" "${_lib}") + IF(NOT _lib_dest MATCHES "XmlListModel|Particles.2|LocalStorage") # blacklist not needed stuff + GET_FILENAME_COMPONENT(_lib_dest_dir ${_lib_dest} DIRECTORY) + INSTALL(FILES ${_lib} DESTINATION ${MACOS_BUNDLE_RESOURCES_DIR}/qml/${_lib_dest_dir} COMPONENT Runtime) + LIST(APPEND ADDITIONAL_BUNDLE_FILES_TO_SIGN "/Contents/Resources/qml/${_lib_dest}") + ENDIF() + ENDFOREACH() + ENDFOREACH() + ENDIF() + + INSTALL(TARGETS AusweisApp DESTINATION ${MACOS_BUNDLE_MACOS_DIR} COMPONENT Application) + + INSTALL(TARGETS AusweisApp2AutostartHelper DESTINATION ${MACOS_BUNDLE_LOGIN_ITEMS_DIR} COMPONENT Application) INSTALL(CODE " + ${DEPENDENCY_CHECK} ${SEARCH_ADDITIONAL_DIRS} file(GLOB_RECURSE QTPLUGINS \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${MACOS_BUNDLE_PLUGINS_DIR}/*${CMAKE_SHARED_LIBRARY_SUFFIX}\") + file(GLOB_RECURSE QtQuickPLUGINS \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${MACOS_BUNDLE_RESOURCES_DIR}/*${CMAKE_SHARED_LIBRARY_SUFFIX}\") INCLUDE(BundleUtilities) - FIXUP_BUNDLE(\"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${EXECUTABLE_NAME}\" \"\${QTPLUGINS}\" \"${TOOLCHAIN_LIB_PATH};\${ADDITIONAL_DIRS}\") + FIXUP_BUNDLE(\"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${MACOS_BUNDLE_MACOS_DIR}/${PROJECT_NAME}\" \"\${QTPLUGINS};\${QtQuickPLUGINS}\" \"${TOOLCHAIN_LIB_PATH};\${ADDITIONAL_DIRS}\") " COMPONENT Runtime) + LIST(APPEND ADDITIONAL_BUNDLE_FILES_TO_SIGN "/Contents/Library/LoginItems/AusweisApp2AutostartHelper.app/Contents/MacOS/AusweisApp2AutostartHelper") + LIST(APPEND ADDITIONAL_BUNDLE_FILES_TO_SIGN "/Contents/Library/LoginItems/AusweisApp2AutostartHelper.app") + LIST(APPEND ADDITIONAL_BUNDLE_FILES_TO_SIGN "/Contents/Frameworks/QtCore.framework") LIST(APPEND ADDITIONAL_BUNDLE_FILES_TO_SIGN "/Contents/Frameworks/QtGui.framework") - LIST(APPEND ADDITIONAL_BUNDLE_FILES_TO_SIGN "/Contents/Frameworks/QtXml.framework") LIST(APPEND ADDITIONAL_BUNDLE_FILES_TO_SIGN "/Contents/Frameworks/QtNetwork.framework") LIST(APPEND ADDITIONAL_BUNDLE_FILES_TO_SIGN "/Contents/Frameworks/QtSvg.framework") LIST(APPEND ADDITIONAL_BUNDLE_FILES_TO_SIGN "/Contents/Frameworks/QtWidgets.framework") LIST(APPEND ADDITIONAL_BUNDLE_FILES_TO_SIGN "/Contents/Frameworks/QtPrintSupport.framework") # remove if disabled in Qt LIST(APPEND ADDITIONAL_BUNDLE_FILES_TO_SIGN "/Contents/Frameworks/QtWebSockets.framework") + LIST(APPEND ADDITIONAL_BUNDLE_FILES_TO_SIGN "/Contents/Frameworks/QtConcurrent.framework") + IF(TARGET Qt5::Qml) + LIST(APPEND ADDITIONAL_BUNDLE_FILES_TO_SIGN "/Contents/Frameworks/QtQml.framework") + LIST(APPEND ADDITIONAL_BUNDLE_FILES_TO_SIGN "/Contents/Frameworks/QtQuick.framework") + LIST(APPEND ADDITIONAL_BUNDLE_FILES_TO_SIGN "/Contents/Frameworks/QtQuickControls2.framework") + LIST(APPEND ADDITIONAL_BUNDLE_FILES_TO_SIGN "/Contents/Frameworks/QtQuickTemplates2.framework") + ENDIF() + IF(TARGET Qt5::Bluetooth) + LIST(APPEND ADDITIONAL_BUNDLE_FILES_TO_SIGN "/Contents/Frameworks/QtBluetooth.framework") + ENDIF() FETCH_TARGET_LOCATION(opensslCryptoName "OpenSSL::Crypto" NAME) FETCH_TARGET_LOCATION(opensslSslName "OpenSSL::SSL" NAME) @@ -131,13 +192,27 @@ ELSEIF(ANDROID) SET(ANDROID_PACKAGE_NAME "com.governikus.ausweisapp2") ENDIF() - FOREACH(entry ldpi mdpi hdpi xhdpi xxhdpi xxxhdpi) - INSTALL(FILES ${RESOURCES_IMG_ANDROID_DIR}/${entry}/${ANDROID_LAUNCHER_ICON} DESTINATION ${ANDROID_PACKAGE_SRC_DIR}/res/drawable-${entry} COMPONENT Runtime RENAME npa.png) - ENDFOREACH() + IF(ANDROID_BUILD_AAR) + SET(ANDROID_MANIFEST AndroidManifest.xml.aar.in) + FOREACH(entry network/WifiInfo ui/aidl/AidlBinder activation/intent/AusweisApp2Service) + SET(_java_file "${SRC_DIR}/${entry}.java") + IF(NOT EXISTS "${_java_file}") + MESSAGE(FATAL_ERROR "Cannot find file: ${_java_file}") + ENDIF() + LIST(APPEND JAVA_FILES "${_java_file}") + ENDFOREACH() + ELSE() + SET(ANDROID_MANIFEST AndroidManifest.xml.apk.in) - INSTALL(FILES ${PACKAGING_DIR}/android/styles.xml DESTINATION ${ANDROID_PACKAGE_SRC_DIR}/res/values COMPONENT Runtime) + FOREACH(entry ldpi mdpi hdpi xhdpi xxhdpi xxxhdpi) + INSTALL(FILES ${RESOURCES_IMG_ANDROID_DIR}/${entry}/${ANDROID_LAUNCHER_ICON} DESTINATION ${ANDROID_PACKAGE_SRC_DIR}/res/drawable-${entry} COMPONENT Runtime RENAME npa.png) + ENDFOREACH() + + INSTALL(FILES ${PACKAGING_DIR}/android/styles.xml DESTINATION ${ANDROID_PACKAGE_SRC_DIR}/res/values COMPONENT Runtime) + + FILE(GLOB_RECURSE JAVA_FILES "${SRC_DIR}/*.java") + ENDIF() - FILE(GLOB_RECURSE JAVA_FILES "${SRC_DIR}/*.java") INSTALL(FILES ${JAVA_FILES} DESTINATION ${ANDROID_PACKAGE_SRC_DIR}/src COMPONENT Runtime) INSTALL(FILES ${PACKAGING_DIR}/android/IAusweisApp2Sdk.aidl DESTINATION ${ANDROID_PACKAGE_SRC_DIR}/src/com/governikus/ausweisapp2/ COMPONENT Runtime) INSTALL(FILES ${PACKAGING_DIR}/android/IAusweisApp2SdkCallback.aidl DESTINATION ${ANDROID_PACKAGE_SRC_DIR}/src/com/governikus/ausweisapp2/ COMPONENT Runtime) @@ -147,10 +222,26 @@ ELSEIF(ANDROID) ELSE() SET(ANDROID_VERSION_NAME ${PROJECT_VERSION}) ENDIF() - CONFIGURE_FILE(${PACKAGING_DIR}/android/AndroidManifest.xml.in ${ANDROID_PACKAGE_SRC_DIR}/AndroidManifest.xml @ONLY) + CONFIGURE_FILE(${PACKAGING_DIR}/android/${ANDROID_MANIFEST} ${ANDROID_PACKAGE_SRC_DIR}/AndroidManifest.xml @ONLY) + CONFIGURE_FILE(${PACKAGING_DIR}/android/fileprovider.xml ${ANDROID_PACKAGE_SRC_DIR}/res/xml/fileprovider.xml COPYONLY) + + SET(ANDROID_APP_BINARY "${CMAKE_INSTALL_PREFIX}/${ANDROID_DEST}/libAusweisApp2.so") + SET(SYMBOL_FOLDER "${CMAKE_BINARY_DIR}/debug.symbols/${CMAKE_ANDROID_ARCH_ABI}") + SET(ANDROID_APP_SYMBOLS "${SYMBOL_FOLDER}/libAusweisApp2.so") + + INSTALL(CODE + " + EXECUTE_PROCESS(COMMAND \"${CMAKE_COMMAND}\" -E make_directory \"${SYMBOL_FOLDER}\") + EXECUTE_PROCESS(COMMAND \"${CMAKE_OBJCOPY}\" \"--only-keep-debug\" \"${ANDROID_APP_BINARY}\" \"${ANDROID_APP_SYMBOLS}\") + " COMPONENT Runtime CONFIGURATIONS RelWithDebInfo) + + IF(CMAKE_COMPILER_IS_GNUCXX) + SET(ANDROID_STL_PATH gnu-libstdc++/${CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION}) + ELSE() + SET(ANDROID_STL_PATH llvm-libc++) + ENDIF() SET(ANDROID_DEPLOYMENT_SETTINGS ${PROJECT_BINARY_DIR}/libAusweisApp2.so-deployment-settings.json CACHE INTERNAL "apk deployment" FORCE) - SET(ANDROID_APP_BINARY "${CMAKE_INSTALL_PREFIX}/${ANDROID_DEST}/libAusweisApp2.so") CONFIGURE_FILE(${PACKAGING_DIR}/android/libAusweisApp2.so-deployment-settings.json.in ${ANDROID_DEPLOYMENT_SETTINGS} @ONLY) SET(TRANSLATION_DESTINATION ${ANDROID_PACKAGE_SRC_DIR}/assets/translations) @@ -165,9 +256,10 @@ ELSEIF(UNIX) INSTALL(TARGETS AusweisApp DESTINATION ${DEFAULT_FILE_DESTINATION} COMPONENT Application) INSTALL(CODE " + ${DEPENDENCY_CHECK} ${SEARCH_ADDITIONAL_DIRS} INCLUDE(BundleUtilities) - FIXUP_BUNDLE(\"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${DEFAULT_FILE_DESTINATION}/${EXECUTABLE_NAME}\" \"\" \"\${ADDITIONAL_DIRS}\") + FIXUP_BUNDLE(\"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${DEFAULT_FILE_DESTINATION}/${PROJECT_NAME}\" \"\" \"\${ADDITIONAL_DIRS}\") " COMPONENT Runtime) CONFIGURE_FILE(${PACKAGING_DIR}/linux/AusweisApp2.desktop.in ${CMAKE_CURRENT_BINARY_DIR}/AusweisApp2.desktop @ONLY) @@ -191,7 +283,7 @@ IF(LINUX OR WIN32 OR MAC) INSTALL(CODE " EXECUTE_PROCESS(COMMAND - ${SELF_PACKER_FOR_EXECUTABLE} ${SELF_PACKER_FOR_EXECUTABLE_FLAGS} \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${DEFAULT_FILE_DESTINATION}/${EXECUTABLE_NAME}\") + ${SELF_PACKER_FOR_EXECUTABLE} ${SELF_PACKER_FOR_EXECUTABLE_FLAGS} \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${DEFAULT_FILE_DESTINATION}/${PROJECT_NAME}${CMAKE_EXECUTABLE_SUFFIX}\") " COMPONENT Application) ENDIF() ENDIF() @@ -210,16 +302,26 @@ ENDIF() IF(LINUX) INSTALL(FILES ${QM_FILES} DESTINATION ${TRANSLATION_DESTINATION} COMPONENT Translations) -ELSE() +ELSEIF(NOT ANDROID_BUILD_AAR) INSTALL(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/translations/ DESTINATION ${TRANSLATION_DESTINATION} COMPONENT Translations) ENDIF() -# resources file -INSTALL(FILES ${RCC} DESTINATION ${DEFAULT_FILE_DESTINATION} COMPONENT Runtime) +IF(NOT ANDROID_BUILD_AAR) + # resources file + INSTALL(FILES ${RCC} DESTINATION ${DEFAULT_FILE_DESTINATION} COMPONENT Runtime) +ENDIF() + +IF(NOT ANDROID AND NOT IOS) + # qtlogging.ini + INSTALL(FILES ${RESOURCES_DIR}/qtlogging.ini DESTINATION ${DEFAULT_FILE_DESTINATION} COMPONENT Runtime) +ENDIF() + +# qt qml plugins +IF(DESKTOP AND TARGET Qt5::Qml) + FOREACH(entry QtQuick QtQuick.2 QtQml QtGraphicalEffects Qt) + INSTALL(DIRECTORY ${QT_HOST_PREFIX}/qml/${entry} DESTINATION ${DEFAULT_FILE_DESTINATION}/qml COMPONENT Runtime PATTERN "*.dylib" EXCLUDE) + ENDFOREACH() +ENDIF() # secure storage file INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/config.json DESTINATION ${DEFAULT_FILE_DESTINATION} COMPONENT Runtime) - -# qtlogging.ini -INSTALL(FILES ${RESOURCES_DIR}/qtlogging.ini DESTINATION ${DEFAULT_FILE_DESTINATION} COMPONENT Runtime) - diff --git a/cmake/Libraries.cmake b/cmake/Libraries.cmake index 7728f19..f0e816e 100644 --- a/cmake/Libraries.cmake +++ b/cmake/Libraries.cmake @@ -5,14 +5,20 @@ IF(MINGW) SET(CMAKE_FIND_LIBRARY_SUFFIXES ".dll.a" ".a" ".lib") ENDIF() -SET(MIN_QT_VERSION 5.9) +SET(MIN_QT_VERSION 5.10) FIND_PACKAGE(Qt5Core ${MIN_QT_VERSION} REQUIRED) +FIND_PACKAGE(Qt5Concurrent ${MIN_QT_VERSION} REQUIRED) FIND_PACKAGE(Qt5Network ${MIN_QT_VERSION} REQUIRED) -FIND_PACKAGE(Qt5Xml ${MIN_QT_VERSION} REQUIRED) FIND_PACKAGE(Qt5Svg ${MIN_QT_VERSION} REQUIRED) FIND_PACKAGE(Qt5LinguistTools ${MIN_QT_VERSION} REQUIRED) FIND_PACKAGE(Qt5WebSockets ${MIN_QT_VERSION} REQUIRED) +IF(NOT DESKTOP AND NOT ANDROID_BUILD_AAR OR "${CMAKE_BUILD_TYPE}" STREQUAL "DEBUG") + FIND_PACKAGE(Qt5Qml ${MIN_QT_VERSION} REQUIRED) + FIND_PACKAGE(Qt5Quick ${MIN_QT_VERSION} REQUIRED) + FIND_PACKAGE(Qt5QuickControls2 ${MIN_QT_VERSION} REQUIRED) +ENDIF() + IF(DESKTOP) FIND_PACKAGE(Qt5Widgets ${MIN_QT_VERSION} REQUIRED) @@ -24,9 +30,6 @@ ENDIF() IF(ANDROID OR IOS OR WINDOWS_STORE OR "${CMAKE_BUILD_TYPE}" STREQUAL "DEBUG") FIND_PACKAGE(Qt5Bluetooth ${MIN_QT_VERSION} REQUIRED) FIND_PACKAGE(Qt5Nfc ${MIN_QT_VERSION} REQUIRED) - FIND_PACKAGE(Qt5Qml ${MIN_QT_VERSION} REQUIRED) - FIND_PACKAGE(Qt5Quick ${MIN_QT_VERSION} REQUIRED) - FIND_PACKAGE(Qt5QuickControls2 ${MIN_QT_VERSION} REQUIRED) ENDIF() IF(ANDROID) @@ -71,13 +74,6 @@ IF(tmp_crosscompile_enabled) SET(CMAKE_CROSSCOMPILING OFF) ENDIF() -IF(ANDROID) - GET_TARGET_PROPERTY(CryptoLib OpenSSL::Crypto IMPORTED_LOCATION) - STRING(REPLACE "libcrypto.so" "libgovcrypto.so" CryptoLib "${CryptoLib}") - MESSAGE(STATUS "Rewrite OpenSSL::Crypto: ${CryptoLib}") - SET_TARGET_PROPERTIES(OpenSSL::Crypto PROPERTIES IMPORTED_LOCATION "${CryptoLib}") -ENDIF() - IF(MINGW) SET(PCSC_LIBRARIES -lwinscard) @@ -101,6 +97,7 @@ ELSEIF(IOS) FIND_LIBRARY(IOS_SECURITY Security) FIND_LIBRARY(IOS_SYSTEMCONFIGURATION SystemConfiguration) FIND_LIBRARY(IOS_AUDIOTOOLBOX AudioToolbox) + FIND_LIBRARY(IOS_IMAGEIO ImageIO) ELSEIF(MAC) FIND_PATH(PCSC_INCLUDE_DIRS WinSCard.h) FIND_LIBRARY(PCSC_LIBRARIES NAMES PCSC WinSCard) @@ -108,6 +105,8 @@ ELSEIF(MAC) FIND_LIBRARY(OSX_APPKIT AppKit) FIND_LIBRARY(IOKIT NAMES IOKit) FIND_LIBRARY(OSX_SECURITY Security) + FIND_LIBRARY(OSX_FOUNDATION Foundation) + FIND_LIBRARY(OSX_SERVICEMANAGEMENT ServiceManagement) ELSEIF(UNIX) IF(LINUX) FIND_LIBRARY(LIBUDEV NAMES udev ludev libudev) @@ -122,8 +121,4 @@ ENDIF() IF("${CMAKE_BUILD_TYPE}" STREQUAL "DEBUG") FIND_PACKAGE(Qt5Test ${MIN_QT_VERSION} REQUIRED) FIND_PACKAGE(Qt5QuickTest ${MIN_QT_VERSION} REQUIRED) - - IF(DESKTOP AND NOT APPLE) - FIND_PACKAGE(Qt5UiPlugin ${MIN_QT_VERSION}) - ENDIF() ENDIF() diff --git a/cmake/Messages.cmake b/cmake/Messages.cmake index c149e3c..dfc993a 100644 --- a/cmake/Messages.cmake +++ b/cmake/Messages.cmake @@ -12,14 +12,6 @@ MESSAGE(STATUS "CMAKE_SYSROOT_COMPILE: ${CMAKE_SYSROOT_COMPILE}") MESSAGE(STATUS "CMAKE_SYSTEM_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}") IF(ANDROID) - FUNCTION(READ_REVISION _var _regex _file) - IF(EXISTS "${_file}") - FILE(READ "${_file}" content) - STRING(REGEX MATCH "${_regex}" _unused "${content}") - SET(${_var} ${CMAKE_MATCH_1} PARENT_SCOPE) - ENDIF() - ENDFUNCTION() - MESSAGE(STATUS "CMAKE_ANDROID_NDK: ${CMAKE_ANDROID_NDK}") MESSAGE(STATUS "CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG: ${CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG}") MESSAGE(STATUS "CMAKE_ANDROID_ARCH_ABI: ${CMAKE_ANDROID_ARCH_ABI}") @@ -31,10 +23,7 @@ IF(ANDROID) MESSAGE(STATUS "ANDROID_SDK: ${ANDROID_SDK}") MESSAGE(STATUS "ANDROID_BUILD_TOOLS_REVISION: ${ANDROID_BUILD_TOOLS_REVISION}") - READ_REVISION(ANDROID_NDK_REVISION ".*Revision = ([0-9|\\.]+)" "${CMAKE_ANDROID_NDK}/source.properties") MESSAGE(STATUS "ANDROID_NDK_REVISION: ${ANDROID_NDK_REVISION}") - - READ_REVISION(ANDROID_SDK_REVISION ".*Revision=([0-9|\\.]+)" "${ANDROID_SDK}/tools/source.properties") MESSAGE(STATUS "ANDROID_SDK_REVISION: ${ANDROID_SDK_REVISION}") ELSEIF(IOS) diff --git a/cmake/Packaging.cmake b/cmake/Packaging.cmake index 817cde3..c009456 100644 --- a/cmake/Packaging.cmake +++ b/cmake/Packaging.cmake @@ -3,7 +3,10 @@ SET(FILENAME ${PROJECT_NAME}-${PROJECT_VERSION}) -IF(ANDROID) +IF(ANDROID_BUILD_AAR) + STRING(TOLOWER "${FILENAME}" FILENAME) + STRING(REGEX REPLACE "[0-9]*-" "-" FILENAME "${FILENAME}") +ELSEIF(ANDROID) SET(FILENAME ${FILENAME}-${CMAKE_ANDROID_ARCH_ABI}) ENDIF() @@ -52,7 +55,7 @@ IF(APPLE AND NOT IOS) SET(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_BINARY_DIR}/LICENSE.txt") ENDIF() -IF(${CMAKE_BUILD_TYPE} STREQUAL "RELEASE") +IF(${CMAKE_BUILD_TYPE} STREQUAL "RELEASE" OR ${CMAKE_BUILD_TYPE} STREQUAL "MINSIZEREL") SET(CPACK_STRIP_FILES TRUE) ENDIF() @@ -69,16 +72,12 @@ LIST(APPEND CPACK_SOURCE_IGNORE_FILES "CMakeLists\\\\.txt\\\\.user") LIST(APPEND CPACK_SOURCE_IGNORE_FILES "\\\\.project") LIST(APPEND CPACK_SOURCE_IGNORE_FILES "\\\\.cproject") LIST(APPEND CPACK_SOURCE_IGNORE_FILES "\\\\.reviewboardrc") -LIST(APPEND CPACK_SOURCE_IGNORE_FILES "utils/tlscheck") -LIST(APPEND CPACK_SOURCE_IGNORE_FILES "utils/testbedtool") -LIST(APPEND CPACK_SOURCE_IGNORE_FILES "utils/fuzzing") +LIST(APPEND CPACK_SOURCE_IGNORE_FILES "utils") SET(CPACK_MONOLITHIC_INSTALL true) IF(WIN32) - SET(CPACK_PACKAGE_EXECUTABLES "AusweisApp2;AusweisApp2") - SET(CPACK_GENERATOR WIX) SET(CPACK_WIX_UPGRADE_GUID 4EE0E467-EAB7-483E-AB45-87BD1DB6B037) SET(CPACK_WIX_PRODUCT_ICON ${RESOURCES_DIR}/images/npa.ico) @@ -86,6 +85,7 @@ IF(WIN32) # disable above line, enable beneath line to build MSI for english # SET(CPACK_WIX_CULTURES en-US) SET(CPACK_WIX_TEMPLATE ${PACKAGING_DIR}/win/WIX.template.in) + SET(CPACK_WIX_EXTRA_SOURCES ${PACKAGING_DIR}/win/install_settings.wxs ${PACKAGING_DIR}/win/runtime_settings.wxs) SET(CPACK_WIX_UI_BANNER ${RESOURCES_DIR}/images/wix_banner.jpg) SET(CPACK_WIX_UI_DIALOG ${RESOURCES_DIR}/images/wix_dialog.jpg) SET(CPACK_WIX_EXTENSIONS WixUtilExtension) @@ -126,6 +126,7 @@ ELSEIF(APPLE) SET(CPACK_GENERATOR Bundle) SET(CPACK_INSTALL_CMAKE_PROJECTS ${CMAKE_BINARY_DIR};${PROJECT_NAME};ALL;/) SET(CPACK_BUNDLE_NAME ${PROJECT_NAME}) + SET(CPACK_BUNDLE_COPYRIGHT ${COPYRIGHT_TEXT}) SET(CPACK_BUNDLE_ICON ${RESOURCES_DIR}/images/bundle_icons.icns) SET(CPACK_BUNDLE_APPLE_CERT_APP "Developer ID Application: Governikus GmbH & Co. KG (G7EQCJU4BR)") @@ -153,10 +154,6 @@ ELSEIF(APPLE) CONFIGURE_FILE(${MACOS_PACKAGING_DIR}/${INFO_PLIST_FILE_NAME} ${INFO_PLIST_FILE_NAME} @ONLY) SET(CPACK_BUNDLE_PLIST ${INFO_PLIST_FILE_NAME}) - SET(STARTUP_FILE_NAME start-ausweisapp2.sh) - CONFIGURE_FILE(${MACOS_PACKAGING_DIR}/${STARTUP_FILE_NAME} ${STARTUP_FILE_NAME} @ONLY) - SET(CPACK_BUNDLE_STARTUP_COMMAND ${STARTUP_FILE_NAME}) - ELSEIF(ANDROID) FIND_PROGRAM(androiddeployqt androiddeployqt CMAKE_FIND_ROOT_PATH_BOTH) IF(NOT androiddeployqt) @@ -164,16 +161,40 @@ ELSEIF(ANDROID) ENDIF() MESSAGE(STATUS "Using androiddeployqt: ${androiddeployqt}") - OPTION(ANDROID_USE_GRADLE "Use gradle for androiddeployqt" OFF) + OPTION(ANDROID_USE_GRADLE "Use gradle for androiddeployqt" ON) - IF(${CMAKE_BUILD_TYPE} STREQUAL "RELEASE") - IF(APK_SIGN_KEYSTORE AND APK_SIGN_KEYSTORE_ALIAS AND APK_SIGN_KEYSTORE_PSW) + IF(ANDROID_USE_GRADLE) + FILE(READ "${QT_HOST_PREFIX}/src/android/templates/build.gradle" BUILD_GRADLE) + + IF(ANDROID_BUILD_AAR) + STRING(REPLACE "apply plugin: 'com.android.application'" "apply plugin: 'com.android.library'" BUILD_GRADLE "${BUILD_GRADLE}") + ENDIF() + + FILE(WRITE "${CMAKE_INSTALL_PREFIX}/build.gradle" "${BUILD_GRADLE}") + + FILE(READ "${PACKAGING_DIR}/android/build.gradle.append" BUILD_GRADLE) + FILE(APPEND "${CMAKE_INSTALL_PREFIX}/build.gradle" "${BUILD_GRADLE}") + ENDIF() + + IF(ANDROID_BUILD_AAR) + SET(ANDROID_FILE_EXT aar) + CONFIGURE_FILE(${PACKAGING_DIR}/android/pom.xml.in ${CMAKE_INSTALL_PREFIX}/${CPACK_PACKAGE_FILE_NAME}.pom @ONLY) + ELSE() + SET(ANDROID_FILE_EXT apk) + ENDIF() + MESSAGE(STATUS "Prepare ${ANDROID_FILE_EXT} file generation") + + IF(${CMAKE_BUILD_TYPE} STREQUAL "RELEASE" OR ${CMAKE_BUILD_TYPE} STREQUAL "RELWITHDEBINFO" OR ${CMAKE_BUILD_TYPE} STREQUAL "MINSIZEREL") + IF(ANDROID_BUILD_AAR) + SET(ANDROID_FILE dist-release.aar) + SET(DEPLOY_CMD_SIGN --release) + ELSEIF(APK_SIGN_KEYSTORE AND APK_SIGN_KEYSTORE_ALIAS AND APK_SIGN_KEYSTORE_PSW) MESSAGE(STATUS "Release build will be signed using: ${APK_SIGN_KEYSTORE} | Alias: ${APK_SIGN_KEYSTORE_ALIAS}") SET(DEPLOY_CMD_SIGN --sign ${APK_SIGN_KEYSTORE} ${APK_SIGN_KEYSTORE_ALIAS} --storepass ${APK_SIGN_KEYSTORE_PSW} --digestalg SHA-256 --sigalg SHA256WithRSA) IF(ANDROID_USE_GRADLE) - SET(APK_FILE dist-release-signed.apk) + SET(ANDROID_FILE dist-release-signed.apk) ELSE() - SET(APK_FILE QtApp-release-signed.apk) + SET(ANDROID_FILE QtApp-release-signed.apk) ENDIF() ELSE() MESSAGE(FATAL_ERROR "Cannot sign release build! Set APK_SIGN_KEYSTORE, APK_SIGN_KEYSTORE_ALIAS and APK_SIGN_KEYSTORE_PSW!") @@ -181,9 +202,9 @@ ELSEIF(ANDROID) ELSE() IF(ANDROID_USE_GRADLE) - SET(APK_FILE dist-debug.apk) + SET(ANDROID_FILE dist-debug.${ANDROID_FILE_EXT}) ELSE() - SET(APK_FILE QtApp-debug.apk) + SET(ANDROID_FILE QtApp-debug.apk) ENDIF() ENDIF() @@ -191,21 +212,43 @@ ELSEIF(ANDROID) IF(ANDROID_USE_GRADLE) SET(DEPLOY_CMD ${DEPLOY_CMD} --gradle) - SET(SOURCE_APK_FILE ${CMAKE_INSTALL_PREFIX}/build/outputs/apk/${APK_FILE}) + SET(SOURCE_ANDROID_FILE ${CMAKE_INSTALL_PREFIX}/build/outputs/${ANDROID_FILE_EXT}) + + IF("${Qt5Core_VERSION}" VERSION_GREATER_EQUAL "5.12.0" AND NOT ANDROID_BUILD_AAR) + IF(${CMAKE_BUILD_TYPE} STREQUAL "DEBUG") + SET(SOURCE_ANDROID_FILE ${SOURCE_ANDROID_FILE}/debug) + ELSE() + SET(SOURCE_ANDROID_FILE ${SOURCE_ANDROID_FILE}/release) + ENDIF() + ENDIF() + + SET(SOURCE_ANDROID_FILE ${SOURCE_ANDROID_FILE}/${ANDROID_FILE}) ELSE() - SET(SOURCE_APK_FILE ${CMAKE_INSTALL_PREFIX}/bin/${APK_FILE}) + IF(ANDROID_BUILD_AAR) + MESSAGE(FATAL_ERROR "Use gradle to build an AAR") + ENDIF() + SET(SOURCE_ANDROID_FILE ${CMAKE_INSTALL_PREFIX}/bin/${ANDROID_FILE}) ENDIF() - SET(DESTINATION_APK_FILE ${CMAKE_INSTALL_PREFIX}/${CPACK_PACKAGE_FILE_NAME}.apk) + SET(DESTINATION_ANDROID_FILE ${CMAKE_INSTALL_PREFIX}/${CPACK_PACKAGE_FILE_NAME}.${ANDROID_FILE_EXT}) # Add DEPENDS install someday # http://public.kitware.com/Bug/view.php?id=8438 - ADD_CUSTOM_TARGET(apk + ADD_CUSTOM_TARGET(${ANDROID_FILE_EXT} COMMAND ${DEPLOY_CMD} - COMMAND ${CMAKE_COMMAND} -E copy ${SOURCE_APK_FILE} ${DESTINATION_APK_FILE}) + COMMAND ${CMAKE_COMMAND} -E copy ${SOURCE_ANDROID_FILE} ${DESTINATION_ANDROID_FILE}) - FIND_PROGRAM(apksigner apksigner HINTS ${ANDROID_SDK}/build-tools/${ANDROID_BUILD_TOOLS_REVISION} CMAKE_FIND_ROOT_PATH_BOTH) - IF(apksigner) - ADD_CUSTOM_TARGET(verify.signature COMMAND ${apksigner} verify --verbose --print-certs -Werr ${DESTINATION_APK_FILE}) + IF(ANDROID_USE_GRADLE) + ADD_CUSTOM_COMMAND(TARGET ${ANDROID_FILE_EXT} POST_BUILD + COMMAND ${CMAKE_INSTALL_PREFIX}/gradlew sourcesJar lint + COMMAND ${CMAKE_COMMAND} -E copy build/libs/dist-sources.jar ${CPACK_PACKAGE_FILE_NAME}-sources.jar + WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}) + ENDIF() + + IF(NOT ANDROID_BUILD_AAR) + FIND_PROGRAM(apksigner apksigner HINTS ${ANDROID_SDK}/build-tools/${ANDROID_BUILD_TOOLS_REVISION} CMAKE_FIND_ROOT_PATH_BOTH) + IF(apksigner) + ADD_CUSTOM_TARGET(verify.signature COMMAND ${apksigner} verify --verbose --print-certs -Werr ${DESTINATION_ANDROID_FILE}) + ENDIF() ENDIF() ELSEIF(UNIX) diff --git a/cmake/Tools.cmake b/cmake/Tools.cmake index 5c6e512..d6f13b2 100644 --- a/cmake/Tools.cmake +++ b/cmake/Tools.cmake @@ -43,7 +43,7 @@ IF(COVERAGE) FIND_PROGRAM(GCOVR_BIN gcovr CMAKE_FIND_ROOT_PATH_BOTH) IF(GCOVR_BIN) SET(GCOVR_FILE "${PROJECT_BINARY_DIR}/gcovr.xml") - SET(GCOVR_CMD ${GCOVR_BIN} -x -o ${GCOVR_FILE} --exclude="src/external" --exclude="test" -r ${PROJECT_SOURCE_DIR} ${PROJECT_BINARY_DIR}) + SET(GCOVR_CMD ${GCOVR_BIN} -x -o ${GCOVR_FILE} --exclude="utils" --exclude="src/external" --exclude="test" -r ${PROJECT_SOURCE_DIR} ${PROJECT_BINARY_DIR}) ADD_CUSTOM_COMMAND(OUTPUT ${GCOVR_FILE} COMMAND ${GCOVR_CMD} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) ADD_CUSTOM_TARGET(gcovr DEPENDS ${GCOVR_FILE}) @@ -151,14 +151,11 @@ ENDIF() FIND_PROGRAM(QMLLINT_BIN qmllint CMAKE_FIND_ROOT_PATH_BOTH) IF(QMLLINT_BIN) FILE(GLOB_RECURSE TEST_FILES_QML ${TEST_DIR}/qml/*.qml) - FILE(GLOB_RECURSE TEST_FILES_QML_STATIONARY ${TEST_DIR}/qml_stationary/*.qml) FILE(GLOB_RECURSE FILES_QML ${RESOURCES_DIR}/qml/*.qml) - FILE(GLOB_RECURSE FILES_QML_STATIONARY ${RESOURCES_DIR}/qml_stationary/*.qml) FILE(GLOB_RECURSE FILES_JS ${RESOURCES_DIR}/qml/*.js) - FILE(GLOB_RECURSE FILES_JS_STATIONARY ${RESOURCES_DIR}/qml_stationary/*.js) - SET(QMLLINT_CMD ${QMLLINT_BIN} ${FILES_QML} ${FILES_QML_STATIONARY} ${FILES_JS}) + SET(QMLLINT_CMD ${QMLLINT_BIN} ${FILES_QML} ${FILES_JS}) - ADD_CUSTOM_TARGET(qmllint COMMAND ${QMLLINT_CMD} SOURCES ${TEST_FILES_QML} ${TEST_FILES_QML_STATIONARY} ${FILES_QML} ${FILES_QML_STATIONARY} ${FILES_JS} ${FILES_JS_STATIONARY}) + ADD_CUSTOM_TARGET(qmllint COMMAND ${QMLLINT_CMD} SOURCES ${TEST_FILES_QML} ${FILES_QML} ${FILES_JS}) ENDIF() # doc8 (https://pypi.python.org/pypi/doc8) @@ -175,8 +172,13 @@ ENDFUNCTION() FIND_PROGRAM(CONVERT convert CMAKE_FIND_ROOT_PATH_BOTH) IF(CONVERT) - SET(CONVERT_CMD convert) - SET(BACKGROUND_COLOR "transparent") + IF(IOS) + SET(CONVERT_CMD convert -alpha off) + SET(BACKGROUND_COLOR "#5489c2") + ELSE() + SET(CONVERT_CMD convert) + SET(BACKGROUND_COLOR "transparent") + ENDIF() ADD_CUSTOM_TARGET(npaicons.win COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -define icon:auto-resize=256,96,64,48,40,32,24,20,16 npa.svg npa.ico @@ -184,29 +186,29 @@ IF(CONVERT) ADD_CUSTOM_TARGET(npaicons.android.preview COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 36x36 npa_preview.svg android/ldpi/npa_preview.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 48x48 npa_preview.svg android/mdpi/npa_preview.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 72x72 npa_preview.svg android/hdpi/npa_preview.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 96x96 npa_preview.svg android/xhdpi/npa_preview.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 144x144 npa_preview.svg android/xxhdpi/npa_preview.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 192x192 npa_preview.svg android/xxxhdpi/npa_preview.png + COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -units PixelsPerInch -resample 160 -resize 48x48 npa_preview.svg android/mdpi/npa_preview.png + COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -units PixelsPerInch -resample 240 -resize 72x72 npa_preview.svg android/hdpi/npa_preview.png + COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -units PixelsPerInch -resample 320 -resize 96x96 npa_preview.svg android/xhdpi/npa_preview.png + COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -units PixelsPerInch -resample 480 -resize 144x144 npa_preview.svg android/xxhdpi/npa_preview.png + COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -units PixelsPerInch -resample 640 -resize 192x192 npa_preview.svg android/xxxhdpi/npa_preview.png WORKING_DIRECTORY ${RESOURCES_DIR}/images) ADD_CUSTOM_TARGET(npaicons.android.beta COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 36x36 npa_beta.svg android/ldpi/npa_beta.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 48x48 npa_beta.svg android/mdpi/npa_beta.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 72x72 npa_beta.svg android/hdpi/npa_beta.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 96x96 npa_beta.svg android/xhdpi/npa_beta.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 144x144 npa_beta.svg android/xxhdpi/npa_beta.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 192x192 npa_beta.svg android/xxxhdpi/npa_beta.png + COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -units PixelsPerInch -resample 160 -resize 48x48 npa_beta.svg android/mdpi/npa_beta.png + COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -units PixelsPerInch -resample 240 -resize 72x72 npa_beta.svg android/hdpi/npa_beta.png + COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -units PixelsPerInch -resample 320 -resize 96x96 npa_beta.svg android/xhdpi/npa_beta.png + COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -units PixelsPerInch -resample 480 -resize 144x144 npa_beta.svg android/xxhdpi/npa_beta.png + COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -units PixelsPerInch -resample 640 -resize 192x192 npa_beta.svg android/xxxhdpi/npa_beta.png WORKING_DIRECTORY ${RESOURCES_DIR}/images) ADD_CUSTOM_TARGET(npaicons.android COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 36x36 npa.svg android/ldpi/npa.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 48x48 npa.svg android/mdpi/npa.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 72x72 npa.svg android/hdpi/npa.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 96x96 npa.svg android/xhdpi/npa.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 144x144 npa.svg android/xxhdpi/npa.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 192x192 npa.svg android/xxxhdpi/npa.png + COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -units PixelsPerInch -resample 160 -resize 48x48 npa.svg android/mdpi/npa.png + COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -units PixelsPerInch -resample 240 -resize 72x72 npa.svg android/hdpi/npa.png + COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -units PixelsPerInch -resample 320 -resize 96x96 npa.svg android/xhdpi/npa.png + COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -units PixelsPerInch -resample 480 -resize 144x144 npa.svg android/xxhdpi/npa.png + COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -units PixelsPerInch -resample 640 -resize 192x192 npa.svg android/xxxhdpi/npa.png WORKING_DIRECTORY ${RESOURCES_DIR}/images) ADD_CUSTOM_TARGET(npaicons.ios.beta @@ -291,6 +293,9 @@ SET(PNGQUANT_CMD pngquant -f -o) WORKING_DIRECTORY ${RESOURCES_DIR}/images) ADD_CUSTOM_TARGET(pngquant.ios + COMMAND ${PNGQUANT_CMD} iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20.png -- iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20.png + COMMAND ${PNGQUANT_CMD} iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20@2x.png -- iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20@2x.png + COMMAND ${PNGQUANT_CMD} iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20@3x.png -- iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20@3x.png COMMAND ${PNGQUANT_CMD} iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall.png -- iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall.png COMMAND ${PNGQUANT_CMD} iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall@2x.png -- iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall@2x.png COMMAND ${PNGQUANT_CMD} iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall@3x.png -- iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall@3x.png @@ -302,6 +307,7 @@ SET(PNGQUANT_CMD pngquant -f -o) COMMAND ${PNGQUANT_CMD} iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon76.png -- iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon76.png COMMAND ${PNGQUANT_CMD} iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon76@2x.png -- iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon76@2x.png COMMAND ${PNGQUANT_CMD} iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon83.5@2x.png -- iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon83.5@2x.png + COMMAND ${PNGQUANT_CMD} iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon1024.png -- iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon1024.png COMMAND ${PNGQUANT_CMD} iOS/launchImages/Default-568h@2x.png -- iOS/launchImages/Default-568h@2x.png WORKING_DIRECTORY ${RESOURCES_DIR}/images) @@ -368,4 +374,55 @@ IF(JAVA_EXECUTABLE) ENDIF() ENDIF() + +FIND_PROGRAM(DOT dot CMAKE_FIND_ROOT_PATH_BOTH) +IF(DOT) + SET(architecture_file Architecture) + SET(ARCHI_PDF_DEPENDS) + + ADD_CUSTOM_TARGET(architecture.graphviz ${CMAKE_COMMAND} --graphviz=${architecture_file} . WORKING_DIRECTORY ${PROJECT_BINARY_DIR}) + LIST(APPEND ARCHI_PDF_DEPENDS architecture.graphviz) + + FIND_PROGRAM(SED sed CMAKE_FIND_ROOT_PATH_BOTH) + IF(SED) + # 1. Strip line of plugins as it is misleading + # 2. Strip "AusweisApp" prefix + ADD_CUSTOM_TARGET(architecture.sed + COMMAND ${SED} -i -E '/AusweisApp -> AusweisApp\(Ui|Card|Activation\).+/d' ${architecture_file} + COMMAND ${SED} -i'' -e 's/"AusweisApp"/"AusweisApp2"/' ${architecture_file} + COMMAND ${SED} -i'' -e 's/"AusweisApp2"/"REPLACE"/' ${architecture_file} + COMMAND ${SED} -i'' -e 's/AusweisApp//' ${architecture_file} + COMMAND ${SED} -i'' -e 's/"REPLACE"/"AusweisApp2"/' ${architecture_file} + COMMAND ${SED} -i'' -e 's/diamond/box/' ${architecture_file} + DEPENDS ${ARCHI_PDF_DEPENDS} + WORKING_DIRECTORY ${PROJECT_BINARY_DIR}) + + LIST(APPEND ARCHI_PDF_DEPENDS architecture.sed) + ENDIF() + + ADD_CUSTOM_TARGET(architecture + ${DOT} -O -Tpdf ${architecture_file} + DEPENDS ${ARCHI_PDF_DEPENDS} + WORKING_DIRECTORY ${PROJECT_BINARY_DIR}) +ENDIF() + +FIND_PACKAGE(PythonInterp 2.7) +IF(PYTHONINTERP_FOUND) + ADD_CUSTOM_TARGET(checkproviderurls + COMMAND ${PYTHON_EXECUTABLE} "${PROJECT_SOURCE_DIR}/utils/providercheck/check-urls.py" "${PROJECT_SOURCE_DIR}/resources/updatable-files/supported-providers.json") +ENDIF() + +FIND_PROGRAM(SED sed CMAKE_FIND_ROOT_PATH_BOTH) +IF(SED) + FILE(GLOB FILES_TO_GENERATE ${RESOURCES_DIR}/images/tutorial/src/*.svg) + SET(TARGET_DIR ${RESOURCES_DIR}/images/tutorial/generated) + ADD_CUSTOM_TARGET(generate_composite_images.sed) + FOREACH(SRC ${FILES_TO_GENERATE}) + GET_FILENAME_COMPONENT(SRC_NAME ${SRC} NAME) + ADD_CUSTOM_COMMAND(TARGET generate_composite_images.sed PRE_BUILD + COMMAND ${SED} -E 's/xlink:href=\\"[\\.\\/]+/xlink:href=\\":\\//' ${SRC} > ${TARGET_DIR}/${SRC_NAME}) + ENDFOREACH(SRC) + +ENDIF() + INCLUDE(Sphinx) diff --git a/cmake/android.toolchain.cmake b/cmake/android.toolchain.cmake index d820900..4515882 100644 --- a/cmake/android.toolchain.cmake +++ b/cmake/android.toolchain.cmake @@ -1,5 +1,17 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.7.1) +FUNCTION(READ_REVISION _var _regex _file) + IF(EXISTS "${_file}") + FILE(READ "${_file}" content) + STRING(REGEX MATCH "${_regex}" _unused "${content}") + SET(${_var} ${CMAKE_MATCH_1} PARENT_SCOPE) + ENDIF() +ENDFUNCTION() + +IF(NOT CMAKE_ANDROID_NDK) + SET(CMAKE_ANDROID_NDK $ENV{ANDROID_NDK}) +ENDIF() + SET(ANDROID_SDK $ENV{ANDROID_HOME}) SET(ANDROID_BUILD_TOOLS_REVISION $ENV{ANDROID_BUILD_TOOLS_REVISION}) @@ -18,19 +30,30 @@ IF(NOT ANDROID_BUILD_TOOLS_REVISION) GET_FILENAME_COMPONENT(ANDROID_BUILD_TOOLS_REVISION "${build_tools}" NAME) ENDIF() +READ_REVISION(ANDROID_NDK_REVISION ".*Revision = ([0-9|\\.]+)" "${CMAKE_ANDROID_NDK}/source.properties") +READ_REVISION(ANDROID_SDK_REVISION ".*Revision=([0-9|\\.]+)" "${ANDROID_SDK}/tools/source.properties") + +IF(NOT CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION AND ANDROID_NDK_REVISION VERSION_GREATER_EQUAL "11") + SET(CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION clang) +ENDIF() +SET(ANDROID_NDK_TOOLCHAIN_VERSION 4.9) + SET(CMAKE_SYSTEM_NAME Android) -SET(CMAKE_ANDROID_STL_TYPE gnustl_shared) +SET(CMAKE_SYSTEM_VERSION 21) + +IF(CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION MATCHES "clang") + SET(CMAKE_ANDROID_STL_TYPE c++_shared) +ELSE() + SET(CMAKE_ANDROID_STL_TYPE gnustl_shared) +ENDIF() IF(NOT CMAKE_ANDROID_ARCH_ABI) SET(CMAKE_ANDROID_ARCH_ABI armeabi-v7a) ENDIF() -IF(CMAKE_ANDROID_ARCH_ABI MATCHES "arm64-v8a") - SET(CMAKE_SYSTEM_VERSION 21) -ELSE() - SET(CMAKE_SYSTEM_VERSION 18) -ENDIF() -SET(CMAKE_FIND_ROOT_PATH ${CMAKE_PREFIX_PATH} CACHE string "android find search path root") +OPTION(ANDROID_BUILD_AAR "Build AAR file instead of APK" OFF) + +SET(CMAKE_FIND_ROOT_PATH ${CMAKE_PREFIX_PATH} CACHE string "android find search path root") SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/cmake/cmd.cmake b/cmake/cmd.cmake index 935cf71..0de660a 100644 --- a/cmake/cmd.cmake +++ b/cmake/cmd.cmake @@ -35,6 +35,19 @@ FUNCTION(CREATE_HASH) ENDFOREACH() ENDFUNCTION() +FUNCTION(CHECK_WIX_WARNING) + FILE(STRINGS "${FILE}" WIX_WARNINGS REGEX "warning") + LIST(LENGTH WIX_WARNINGS WARNING_COUNT) + IF(WARNING_COUNT GREATER 0) + FOREACH(m ${WIX_WARNINGS}) + MESSAGE(STATUS "${m}\n") + ENDFOREACH() + MESSAGE(FATAL_ERROR "Found ${WARNING_COUNT} new WIX warnings") + ELSE() + MESSAGE(STATUS "No WIX warnings found") + ENDIF() +ENDFUNCTION() + @@ -47,6 +60,8 @@ ENDIF() IF(CMD STREQUAL "HASH") CREATE_HASH() +ELSEIF(CMD STREQUAL "CHECK_WIX_WARNING") + CHECK_WIX_WARNING() ELSE() MESSAGE(FATAL_ERROR "Unknown CMD: ${CMD}") ENDIF() diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index 1f1beee..7c650e8 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -2,6 +2,8 @@ IF(SPHINX_FOUND) SPHINX_GEN("${CMAKE_CURRENT_SOURCE_DIR}/releasenotes" "notes" BUILDER singlehtml html latex) SPHINX_GEN("${CMAKE_CURRENT_SOURCE_DIR}/sdk" "sdk" BUILDER changes html latex DEFAULT_LANG en) + + SPHINX_GEN("${CMAKE_CURRENT_SOURCE_DIR}/installation" "inst" BUILDER changes html latex) ELSE() MESSAGE(STATUS "No documentation will be generated") ENDIF() diff --git a/docs/installation/README.de.rst b/docs/installation/README.de.rst new file mode 100644 index 0000000..b6bc769 --- /dev/null +++ b/docs/installation/README.de.rst @@ -0,0 +1,147 @@ +Deutsch +======= + +Windows +------- + +Der Installer der AusweisApp2 kann über die Kommandozeile gestartet werden, um +den Installationsprozess zu konfigurieren und systemweite Standardeinstellungen +vorzugeben. Neben den üblichen Parametern [1]_ enthält das folgende Kommando +alle unterstützten Parameter, die im Anschluss erläutert werden. + +.. code-block:: winbatch + + msiexec /i AusweisApp2-X.YY.Z.msi /quiet INSTALL_ROOT="C:\AusweisApp2" SYSTEMSETTINGS=false DESKTOPSHORTCUT=false AUTOSTART=false AUTOHIDE=false REMINDTOCLOSE=false ASSISTANT=false TRANSPORTPINREMINDER=false UPDATECHECK=false ONSCREENKEYBOARD=true HISTORY=false + +INSTALL_ROOT + Gibt das Installationsverzeichnis an. Ohne Angabe wird der Ordner + "C:\\Programme (x86)\\AusweisApp2 X.YY.Z" genutzt. + +SYSTEMSETTINGS + Betrifft die Erstellung von Firewall-Regeln der Windows Firewall. Ohne Angabe + des Parameters werden die Firewall-Regeln erstellt (true). Durch Angabe von + SYSTEMSETTINGS=false werden keine Firewall-Regeln erstellt. + +DESKTOPSHORTCUT + Durch Angabe von DESKTOPSHORTCUT=false kann die Erstellung einer + Desktop-Verknüpfung vermieden werden. Ohne Angabe des Parameters wird eine + Desktop-Verknüpfung für alle Benutzer erstellt (true). + +AUTOSTART + Durch Angabe von AUTOSTART=true wird ein Autostart-Eintrag für alle Benutzer + erstellt. Die Deaktivierung des Autostarts ist den Benutzern in der AusweisApp2 + dadurch nicht möglich. Ohne Angabe wird der Autostart-Eintrag nicht erstellt + (false). In diesem Fall ist es jedoch jedem Benutzer möglich, die Autostart- + Funktion innerhalb der AusweisApp2 für sich zu aktivieren. + +AUTOHIDE + Betrifft die automatische Minimierung nach Abschluss einer erfolgreichen + Authentisierung. Ohne Angabe ist diese aktiviert (true). Durch AUTOHIDE=false + wird diese deaktiviert. Der Benutzer kann diese Einstellung anpassen. + +REMINDTOCLOSE + Wenn der Benutzer die AusweisApp2 per Klick auf das X schließt, wird er darauf + hingewiesen, dass nur die Benutzeroberfläche geschlossen wird und die + AusweisApp2 weiterhin im Infobereich zur Verfügung steht. Zu diesem Zeitpunkt + ist es möglich, den Hinweis zukünftig zu unterdrücken. Durch REMINDTOCLOSE=false + kann dieser Hinweis von vornherein deaktiviert werden. Ohne Angabe ist er + aktiviert (true). + +ASSISTANT + Startet der Benutzer die AusweisApp2 zum ersten Mal, wird die Benutzeroberfläche + geöffnet und ein Einrichtungsassistent angezeigt. Bei jedem weiteren Start wird + die AusweisApp2 im Hintergrund gestartet und der Einrichtungsassistent erscheint + nicht. Durch ASSISTANT=false wird die AusweisApp2 auch beim ersten Start im + Hintergrund ohne Einrichtungsassistenten gestartet. Ohne Angabe ist der + Einrichtungsassistent aktiviert (true). + +TRANSPORTPINREMINDER + Zu Beginn einer Selbstauskunft oder Authentisierung wird der Benutzer einmalig + danach gefragt, ob er die Transport-PIN schon geändert hat. Durch + TRANSPORTPINREMINDER=false kann diese Abfrage deaktiviert werden. Ohne Angabe + ist die Abfrage aktiviert (true). + +UPDATECHECK + Wird die Benutzeroberfläche der AusweisApp2 geöffnet, wird eine Überprüfung auf + eine neue Version der AusweisApp2 gestartet, falls seit der letzten Überprüfung + mindestens 24 Stunden vergangen sind. Liegt eine neue Version vor, wird der + Benutzer darüber in einem Dialog informiert. Durch Setzen von UPDATECHECK auf + false oder true kann diese Überprüfung deaktiviert bzw. aktiviert werden. + Die Einstellung kann dann durch den Benutzer in der AusweisApp2 nicht geändert + werden. Ohne Angabe ist die Überprüfung aktiviert, der Benutzer kann die + Einstellung jedoch ändern. + +ONSCREENKEYBOARD + Für die Eingabe von PIN, CAN und PUK kann eine Bildschirmtastatur verwendet + werden. Durch Setzen von ONSCREENKEYBOARD auf false oder true kann diese + deaktiviert bzw. aktiviert werden. Der Benutzer kann diese Einstellung anpassen. + +HISTORY + Jede Selbstauskunft oder Authentisierung wird im Verlauf gespeichert. Dabei + werden jedoch keine persönlichen Daten gespeichert, sondern nur der Zeitpunkt, + der Diensteanbieter und die ausgelesenen Datenfelder (ohne Inhalt). Durch Setzen + von HISTORY auf false oder true kann der Verlauf deaktiviert bzw. aktiviert + werden. Der Benutzer kann diese Einstellung anpassen. + +Alternativ kann mit Orca [2]_ eine MST-Datei erzeugt werden, die die oben +genannten Parameter definiert. Die Parameter sind in den Tabellen "Directory" +und "Property" verfügbar. Übergeben lässt sich die MST-Datei mit dem folgenden +Kommando: + +.. code-block:: winbatch + + msiexec /i AusweisApp2-X.YY.Z.msi /quiet TRANSFORMS=file.mst + +macOS +----- + +Unter macOS ist keine Installation per Kommandozeile vorgesehen. Jedoch können +einige der oben genannten Einstellung durch eine plist-Datei im Verzeichnis +/Library/Preferences systemweit vorgegeben werden. Diese plist-Datei muss dabei +manuell durch den Administrator des Systems hinterlegt werden und wird von allen +(zukünftigen) Installationen der AusweisApp2 verwendet. Alle nicht genannten +Einstellungen werden auf macOS nicht unterstützt. Der Name der Datei muss +"com.governikus.AusweisApp2.plist" lauten. Der Inhalt wird im folgenden +dargestellt: + +.. code-block:: xml + + + + + + autoCloseWindow + + remindToClose + + showSetupAssistant + + transportPinReminder + + common.autoUpdateCheck + + common.keylessPassword + + history.enable + + + + +Für die einzelnen Werte gelten die gleichen Beschreibungen wie für die +Windows-Version wobei die Bennennung der Attribute der folgenden Tabelle zu +entnehmen ist. + +====================== ==================== +macOS Windows +====================== ==================== +autoCloseWindow AUTOHIDE +remindToClose REMINDTOCLOSE +showSetupAssistant ASSISTANT +transportPinReminder TRANSPORTPINREMINDER +common.autoUpdateCheck UPDATECHECK +common.keylessPassword ONSCREENKEYBOARD +history.enable HISTORY +====================== ==================== + +.. [1] https://docs.microsoft.com/de-de/windows/desktop/msi/standard-installer-command-line-options +.. [2] https://docs.microsoft.com/de-de/windows/desktop/Msi/orca-exe diff --git a/docs/installation/README.en.rst b/docs/installation/README.en.rst new file mode 100644 index 0000000..290bb75 --- /dev/null +++ b/docs/installation/README.en.rst @@ -0,0 +1,136 @@ +English +======= + +Windows +------- + +Start the installer of AusweisApp2 using the command line to configure the +installation process and preset system-wide default settings. In addition to the +usual arguments [1]_, the following command contains all supported arguments, +which are explained below. + +.. code-block:: winbatch + + msiexec /i AusweisApp2-X.YY.Z.msi /quiet INSTALL_ROOT="C:\AusweisApp2" SYSTEMSETTINGS=false DESKTOPSHORTCUT=false AUTOSTART=false AUTOHIDE=false REMINDTOCLOSE=false ASSISTANT=false TRANSPORTPINREMINDER=false UPDATECHECK=false ONSCREENKEYBOARD=true HISTORY=false + +INSTALL_ROOT + States the installation directory. If not specified, the folder + "C:\\Program Files (x86)\\AusweisApp2 X.YY.Z" is used. + +SYSTEMSETTINGS + Concerns the settings of firewall rules of the Windows Firewall. When not + specifying the argument, firewall rules are created (true). By indicating + SYSTEMSETTINGS=false, no firewall rules are created. + +DESKTOPSHORTCUT + By specifying DESKTOPSHORTCUT=false, no desktop shortcut is created. Without + specifying the argument, the desktop shortcut is created for all users (true). + +AUTOSTART + Setting AUTOSTART=true creates autostart entry for all users. Users are unable + to deactivate the autostart function in the AusweisApp2. Not specified, no + autostart entry is created (false). In that case, users are able to activate the + autostart function in the AusweisApp2. + +AUTOHIDE + Concerns the automatic minimization after a successful authentication. Not + specified, it is activated (true). Setting AUTOHIDE=false, it is deactivated. + Users can adjust this setting to their preferences. + +REMINDTOCLOSE + Closing the AusweisApp2 by clicking on the X, the user is notified that only the + user interface is closed and that the AusweisApp2 is still available in the info + tray. At this point, it is possible to prevent future notifications. Setting + REMINDTOCLOSE=false deactivates this notification from the outset. Not + specified, it is activated (true). + +ASSISTANT + Starting the AusweisApp2 for the first time, the user interface is displayed and + the installation wizard is shown. With each subsequent start, the AusweisApp2 + is started in the background, without the installation wizard being shown. By + indicating ASSISTANT=false, the AusweisApp2 is started in the background without + the installation wizard from the outset. Not specified, the installation + wizard is activated (true). + +TRANSPORTPINREMINDER + Prior to the first authentication, the user is asked once whether they have + changed their transport PIN. Setting TRANSPORTPINREMINDER=false deactivates this + reminder. Not specified, the reminder is activated (true). + +UPDATECHECK + Upon opening the user interface of the AusweisApp2, an update check is started, + provided that at least 24 hours have elapsed since the last update check. If a + newer version is available, the user is notified accordingly. Setting + UPDATECHECK to false or true deactivates or activates the update check + respectively. Users are unable to change this setting in the AusweisApp2. Not + specified, the update check is activated, but users can adjust the settings. + +ONSCREENKEYBOARD + An on-screen keyboard is available to enter PIN, CAN or PUK. It is deactivated or + activated by setting ONSCREENKEYBOARD to false or true. Users are able to adjust + the settings. + +HISTORY + Each authentication is saved in the history. No personal data is saved, only the + time of authentication, the service provider and the selected fields (without + content). Indicating HISTORY as false or true, the history function is + deactivated or activated. Users are able to adjust the settings. + +Alternatively, Orca [2]_ can be used to create an MST file that defines the +above arguments. The arguments are available in the "Directory" and "Property" +tables. The MST file can be transferred with the following command: + +.. code-block:: winbatch + + msiexec /i AusweisApp2-X.YY.Z.msi /quiet TRANSFORMS=file.mst + +macOS +----- + +MacOS does not provide a command line installation. However, some of the above +settings can be specified system-wide by a plist file in the +/Library/Preferences directory. This plist file must be manually stored by the +administrator of the system and will be used by all (future) installations of +AusweisApp2. All not mentioned settings are not supported on macOS. The name of +the file must be "com.governikus.AusweisApp2.plist". The content is shown below: + +.. code-block:: xml + + + + + + autoCloseWindow + + remindToClose + + showSetupAssistant + + transportPinReminder + + common.autoUpdateCheck + + common.keylessPassword + + history.enable + + + + +The description for each value is applicable for both Windows and macOS, +although the naming of the attributes differs, as shown in the following table: + +====================== ==================== +macOS Windows +====================== ==================== +autoCloseWindow AUTOHIDE +remindToClose REMINDTOCLOSE +showSetupAssistant ASSISTANT +transportPinReminder TRANSPORTPINREMINDER +common.autoUpdateCheck UPDATECHECK +common.keylessPassword ONSCREENKEYBOARD +history.enable HISTORY +====================== ==================== + +.. [1] https://docs.microsoft.com/en-us/windows/desktop/msi/standard-installer-command-line-options +.. [2] https://docs.microsoft.com/en-us/windows/desktop/Msi/orca-exe diff --git a/docs/installation/conf.py.in b/docs/installation/conf.py.in new file mode 100644 index 0000000..271c0e6 --- /dev/null +++ b/docs/installation/conf.py.in @@ -0,0 +1,170 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import sys +import os +import shlex + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +needs_sphinx = '1.4' + +# If true, figures, tables and code-blocks are automatically numbered +# if they has caption. For now, it works only with the HTML builder. +# Default is False. +numfig = True + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [] + +locale_dirs = ['@SPHINX_DOCS_DIR@/locales/'] + +gettext_additional_targets = ['image'] +gettext_location = False +gettext_compact = True + +# Add any paths that contain templates here, relative to this directory. +#templates_path = ['@SPHINX_DOCS_DIR@/_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = 'AusweisApp2 Installation' +copyright = '2018, Governikus GmbH & Co. KG' +author = 'Governikus GmbH & Co. KG' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '@PROJECT_VERSION@' +# The full version, including alpha/beta/rc tags. +release = '@VERSION_DVCS@' + +today = ' ' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = 'en' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +#exclude_patterns = [''] + + + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +html_favicon = '@SPHINX_DOCS_DIR@/../../resources/images/npa.ico' + +#html_theme_path = ['@SPHINX_DOCS_DIR@/_themes'] + +#html_theme = 'appcast' +html_theme = 'sphinx_rtd_theme' + +# If false, no module index is generated. +html_domain_indices = True + +# If false, no index is generated. +html_use_index = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +html_show_sphinx = False + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +html_show_copyright = True + +html_scaled_image_link = False + +# Output file base name for HTML help builder. +htmlhelp_basename = 'AusweisApp2Installation' + +html_context = { + 'display_github': False, + 'display_bitbucket': False, + 'show_source': False, + 'html_show_sourcelink': False, +} + +html_add_permalinks = "" + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +'papersize': 'a4paper', + +# The font size ('10pt', '11pt' or '12pt'). +'pointsize': '11pt', + +# Additional stuff for the LaTeX preamble. +'preamble': ''' +\hypersetup{pdfauthor={Governikus GmbH \& Co. KG}, + pdftitle={AusweisApp2}, + pdfsubject={Installation}, + pdfkeywords={installation}, + pdfproducer={LaTeX}, + pdfcreator={Sphinx} +} +''', + +# Override tableofcontents +'tableofcontents': ''' +\\tableofcontents +\\newpage +\\pagestyle{plain} +\\pagenumbering{arabic} +''', + +# Latex figure (float) alignment +'figure_align': 'H', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'AusweisApp2-@VERSION_DVCS@-NetInstallation.tex', 'AusweisApp2 Installation', + 'Governikus GmbH \& Co. KG', 'howto'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +latex_logo = '@SPHINX_DOCS_DIR@/../../resources/images/android/xhdpi/npa.png' + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +latex_show_pagerefs = True + +# If true, show URL addresses after external links. +latex_show_urls = 'footnote' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True diff --git a/docs/installation/index.rst b/docs/installation/index.rst new file mode 100644 index 0000000..15bd5d2 --- /dev/null +++ b/docs/installation/index.rst @@ -0,0 +1,13 @@ +Table of contents +----------------- + +.. raw:: latex + + \clearpage + +.. toctree:: + :maxdepth: 2 + :caption: Installation + + README.de + README.en diff --git a/docs/releasenotes/1.16.0.rst b/docs/releasenotes/1.16.0.rst new file mode 100644 index 0000000..7c0b7aa --- /dev/null +++ b/docs/releasenotes/1.16.0.rst @@ -0,0 +1,70 @@ +AusweisApp2 1.16.0 +^^^^^^^^^^^^^^^^^^ + +**Releasedatum:** 20. Dezember 2018 + + + +Anwender +"""""""" + - Kleinere Fehlerbehebungen. + + - Hinzufügen eines Tutorials um Hilfestellung bei der + Verwendung unter Android zu geben. + + - Unterstützung von Android 4.3 und 4.4 wurde eingestellt. + + - Unterstützung von OS X 10.10 wurde eingestellt. + + - Unterstützung von macOS 10.14. + + - Es ist nun möglich nach einer erfolgreichen Authentisierung + Daten eines verwendeten Android Smartphones anonym zu + übermitteln. + + - Erweiterung der Diagnosefunktion. + + - Bewertungsnachfrage unter Android hinzugefügt. + + - Möglichkeit zur Log-Anzeige unter Android hinzugefügt. + + - Unter Android wird nun eindeutiger zwischen 5 und 6-stelliger + PIN unterschieden. + + - Das Entfernen des Ausweisdokumentes während der Eingabe von + PIN/CAN/PUK löst nicht mehr den Abbruch der Authentisierung + oder der PIN-Änderung aus. + + +Entwickler +"""""""""" + - Unterstützung von firmenweiten Installationen des MSI-Paketes + (separate Dokumentation). + + - Unterstützung eines SDKs mittels WebSockets unter Windows und + macOS (separate Dokumentation). + + - Konkurrierende Zugriffe von verschiedenen Anwendungen auf + Kartenlesegeräte, die über PC/SC angeschlossen sind, sind + nun möglich. + + - Aktualisierung von OpenSSL auf die Version 1.1.1. + + - Aktualisierung von Qt auf die Version 5.11.2. + + - Unterstützung von TLS v1.1 wurde eingestellt. + + - Unterstützung folgender TLS-Cipher wurde eingestellt: + + - DHE-DSS-AES256-GCM-SHA384 + - DHE-DSS-AES256-SHA256 + - DHE-DSS-AES128-GCM-SHA256 + - DHE-DSS-AES128-SHA256 + - DHE-DSS-AES256-SHA + - DHE-DSS-AES128-SHA + - ECDHE-ECDSA-AES256-SHA + - ECDHE-RSA-AES256-SHA + - DHE-RSA-AES256-SHA + - ECDHE-ECDSA-AES128-SHA + - ECDHE-RSA-AES128-SHA + - DHE-RSA-AES128-SHA diff --git a/docs/releasenotes/announce.rst b/docs/releasenotes/announce.rst index 72ef486..9af78d0 100644 --- a/docs/releasenotes/announce.rst +++ b/docs/releasenotes/announce.rst @@ -1,10 +1,18 @@ Abkündigungen ============= +Mit der Version 1.18.0 der AusweisApp2 wird die Unterstützung +folgender Systeme eingestellt. + + - x86-Architektur unter Android + + Mit der Version 1.16.0 der AusweisApp2 wird die Unterstützung folgender Systeme eingestellt. - OS X 10.10 + - Android 4.3 + - Android 4.4 Mit der Version 1.16.0 der AusweisApp2 wird die Unterstützung @@ -13,7 +21,7 @@ folgender TLS-Cipher eingestellt. - DHE-DSS-AES256-GCM-SHA384 - DHE-DSS-AES256-SHA256 - DHE-DSS-AES128-GCM-SHA256 - - DHE-DSS-AES128-SHA256" + - DHE-DSS-AES128-SHA256 - DHE-DSS-AES256-SHA - DHE-DSS-AES128-SHA - ECDHE-ECDSA-AES256-SHA diff --git a/docs/releasenotes/appcast.rst b/docs/releasenotes/appcast.rst index ac580b2..64130f4 100644 --- a/docs/releasenotes/appcast.rst +++ b/docs/releasenotes/appcast.rst @@ -4,9 +4,6 @@ Release Notes .. toctree:: :maxdepth: 1 - 1.14.3 - 1.14.2 - 1.14.1 - 1.14.0 + 1.16.0 announce issues diff --git a/docs/releasenotes/issues.rst b/docs/releasenotes/issues.rst index d477d87..8a3140e 100644 --- a/docs/releasenotes/issues.rst +++ b/docs/releasenotes/issues.rst @@ -24,3 +24,19 @@ Die nachfolgende Liste bezieht sich auf die aktuelle Version der AusweisApp2. - Unter Umständen kommt es zu Stabilitätsproblemen der NFC-Schnittstelle auf Android. + + - Längere Texte können unter Android u.U. bei kleinen Bildschirmgrößen + abgeschnitten sein. + + - Unter macOS wird der Hinweis bei falscher PIN-Bestätigung während der + PIN-Änderung nur sehr kurz angezeigt. + + - Bei der Nutzung eines entfernten Kartenlesegeräts mit aktiviertem + Tastaturmodus kann bei einer Authentisierung nicht in die PIN-Änderung + gewechselt werden, wenn nur eine 5-stellige PIN vorhanden ist. + + - Bei der Nutzung eines entfernten Kartenlesegeräts mit aktiviertem + Tastaturmodus merkt sich das entfernte Kartenlesegerät fälschlicherweise + die zuletzt ausgewählte Länge der PIN (5/6 Stellen). Ein Neustart des + Fernzugriffs auf dem entfernten Kartenlesegerät setzt die Länge auf + 6 Stellen zurück. diff --git a/docs/releasenotes/singlehtml.conf.py.in b/docs/releasenotes/singlehtml.conf.py.in index cda68ef..5f22de9 100644 --- a/docs/releasenotes/singlehtml.conf.py.in +++ b/docs/releasenotes/singlehtml.conf.py.in @@ -42,7 +42,7 @@ master_doc = 'appcast' # General information about the project. project = 'AusweisApp2' -copyright = '2016-2017, Governikus GmbH & Co. KG' +copyright = '2016-2018, Governikus GmbH & Co. KG' author = 'Governikus GmbH & Co. KG' # The version info for the project you're documenting, acts as replacement for diff --git a/docs/releasenotes/support.rst b/docs/releasenotes/support.rst index aaf28d4..0f920d6 100644 --- a/docs/releasenotes/support.rst +++ b/docs/releasenotes/support.rst @@ -8,27 +8,27 @@ der AusweisApp2 unterstützt. Betriebssysteme """"""""""""""" - - OS X 10.10 - - OS X 10.11 - macOS 10.12 - macOS 10.13 + - macOS 10.14 + - Windows 7 SP1 (32bit / 64bit) - Windows 8.1 (64bit) - Windows 10 (64bit) - - Android 4.3 und höher (x86, armeabi-v7a, arm64-v8a) + - Android 5.0 und höher (x86, armeabi-v7a) Karten """""" - - Neuer Personalausweis + - Personalausweis - Elektronischer Aufenthaltstitel @@ -48,13 +48,15 @@ und sollte daher mit allen marktüblichen Browsern verwendet werden können. Im Rahmen der Qualitätssicherung werden die folgenden Browserversionen getestet. - - Firefox 61 + - Firefox 64 - - Chrome 67 + - Chrome 71 - Internet Explorer 11 - - Safari 11.1.1 + - Safari 12 + + - Edge 44 @@ -112,11 +114,11 @@ Im mobilen Umfeld ist die Funktionalität jedoch abhängig von der vom Diensteanbieter umgesetzten Aktivierung. Daher empfehlen wir einen der folgenden Browser zu verwenden. - - Firefox Klar 5.2 + - Firefox Klar 8 - - Chrome 67 + - Chrome 71 - - Android System WebView 67 + - Android System WebView 70 diff --git a/docs/releasenotes/versions.rst b/docs/releasenotes/versions.rst index ee2da8e..62d6c75 100644 --- a/docs/releasenotes/versions.rst +++ b/docs/releasenotes/versions.rst @@ -1,6 +1,14 @@ Versionen ========= +Versionszweig 1.16 +------------------ +.. toctree:: + :maxdepth: 1 + + 1.16.0 + + Versionszweig 1.14 ------------------ .. toctree:: diff --git a/docs/sdk/android.rst b/docs/sdk/android.rst index c93f02f..7592c5d 100644 --- a/docs/sdk/android.rst +++ b/docs/sdk/android.rst @@ -5,15 +5,70 @@ The AusweisApp2 core is encapsulated into an **Android service** which is running in the background without a user interface. This service is interfaced via an Android specific interprocess communication (IPC) mechanism. The basics of this mechanism - the **Android Interface Definition Language** (AIDL) - -are introduced in the following section. The following section deals with the -cryptographic verification of the SDKs authenticity. This step is necessary to -ensure that the SDK has not been modified in a malicious way. Subsequent -sections deal with the SDK interface itself and explain which steps are -necessary in order to talk to the AusweisApp2 SDK. +are introduced in the following section. Subsequent sections deal with the +SDK interface itself and explain which steps are necessary in order to talk +to the AusweisApp2 SDK. + +The AusweisApp2 is available as an integrated and as an external variant. +The integrated version is provided as an AAR package that can automatically +be fetched by Android's default build system **gradle**. The external variant +is available as an APK in Google's PlayStore. It is required that the user +has manually installed the AusweisApp2 like any other app to connect to the +external variant. + +.. important:: + The integrated variant is available in jcenter for free. + If you need enterprise support feel free to contact us. + + + +Integrated +---------- +The integrated SDK is distributed as an AAR package that contains +native **arm64-v8a** libraries only. +The AAR package is available in the default repository of Android. +The following listing shows the required **jcenter** in **build.gradle**. + +.. code-block:: groovy + + buildscript { + repositories { + jcenter() + } + } + + +The integrated AusweisApp2 will be fetched automatically as a dependency by +your **app/build.gradle** file. +It is recommended to always use the latest version by **1.+** of AusweisApp2. +But you are free to add a concrete version like **1.16.0**. + +.. code-block:: groovy + + dependencies { + implementation 'com.governikus:ausweisapp:1.16.+' + } + + + +.. seealso:: + The AAR package provides an **AndroidManifest.xml** to register required + permissions and the background service. If your own AndroidManifest has + conflicts with our provided file you can add some attributes to resolve + those conflicts. + + https://developer.android.com/studio/build/manifest-merge.html + + + +External +-------- +The APK is available in Google's PlayStore and needs to be installed by +the user. The external SDK is distributed as 32-bit and 64-bit. Security --------- +^^^^^^^^ The following listing provides information about the solution to provide a secure connection to AusweisApp2. @@ -39,82 +94,15 @@ secure connection to AusweisApp2. -.. _android_import_aidl: - -Import the AIDL files ---------------------- -Android provides an interprocess communication (IPC) mechanism which is based on -messages consisting of primitive types. -In order to abstract from this very basic mechanism, there is the Android -Interface Definition Language (AIDL). -It allows the definition of Java like interfaces. -The Android SDK generates the necessary interface implementations from supplied -AIDL files in order to perform IPC, as if this function had been called directly -in the current process. - -In order to interact with the AusweisApp2 SDK there are two AIDL interfaces. -The first one is given to the client application by the SDK and allows the -client to establish a session with the SDK, -to send JSON commands to the SDK and to pass discovered NFC tags to the SDK. - -The second AIDL interface is given to the SDK by the client application. It -enables the client to receive the intial session parameters as well as -JSON messages from the SDK. Furthermore it has a function which is called -when an existing connection with the SDK is dropped by the SDK. Both interfaces -are listed below and you need to import them into your build environment. - -.. important:: - It is required that you place the AIDL files under subdirectory - "aidl/com.governikus.ausweisapp2". Also the interface methods - names must be exactly the same. - -.. seealso:: - - https://developer.android.com/guide/components/aidl.html - - - -Interface -^^^^^^^^^ - -.. code-block:: java - - package com.governikus.ausweisapp2; - - import com.governikus.ausweisapp2.IAusweisApp2SdkCallback; - import android.nfc.Tag; - - interface IAusweisApp2Sdk - { - boolean connectSdk(IAusweisApp2SdkCallback pCallback); - boolean send(String pSessionId, String pMessageFromClient); - boolean updateNfcTag(String pSessionId, in Tag pTag); - } - - - -Callback -^^^^^^^^ - -.. code-block:: java - - package com.governikus.ausweisapp2; - - interface IAusweisApp2SdkCallback - { - void sessionIdGenerated(String pSessionId, boolean pIsSecureSessionId); - void receive(String pJson); - void sdkDisconnected(); - } - - - - Verify the authenticity of AusweisApp2 --------------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The following section deals with the cryptographic verification of the SDK's +authenticity. This step is necessary to ensure that the SDK has not been +modified in a malicious way. + Fingerprint -^^^^^^^^^^^ +""""""""""" In order to verify that the AusweisApp2 SDK is authentic and has not been modified in a malicious way, it is required to verify its authenticity before establishing a connection with it. @@ -133,7 +121,7 @@ fingerprint of the authentic SDK certificate is the following: Example -^^^^^^^ +""""""" The following example code demonstrates how the certificate hash value of a signed application on Android can be verified. @@ -167,19 +155,101 @@ hash value of a signed application on Android can be verified. } +.. _android_import_aidl: + +Import the AIDL files +^^^^^^^^^^^^^^^^^^^^^ +Android provides an interprocess communication (IPC) mechanism which is based on +messages consisting of primitive types. +In order to abstract from this very basic mechanism, there is the Android +Interface Definition Language (AIDL). +It allows the definition of Java like interfaces. +The Android SDK generates the necessary interface implementations from supplied +AIDL files in order to perform IPC, as if this function had been called directly +in the current process. + +In order to interact with the AusweisApp2 SDK there are two AIDL interfaces. +The first one is given to the client application by the SDK and allows the +client to establish a session with the SDK, +to send JSON commands to the SDK and to pass discovered NFC tags to the SDK. + +The second AIDL interface is given to the SDK by the client application. It +enables the client to receive the intial session parameters as well as +JSON messages from the SDK. Furthermore it has a function which is called +when an existing connection with the SDK is dropped by the SDK. Both interfaces +are listed below and you need to import them into your build environment. + +.. important:: + It is required that you place the AIDL files under subdirectory + "aidl/com.governikus.ausweisapp2". Also the interface methods + names must be exactly the same. + +.. seealso:: + + https://developer.android.com/guide/components/aidl.html + +.. note:: + If you implement the integrated variant beside the external variant you do + **not** need to manually add AIDL files as the AAR package already provides + those interfaces. + + +Interface +""""""""" + +.. code-block:: java + + package com.governikus.ausweisapp2; + + import com.governikus.ausweisapp2.IAusweisApp2SdkCallback; + import android.nfc.Tag; + + interface IAusweisApp2Sdk + { + boolean connectSdk(IAusweisApp2SdkCallback pCallback); + boolean send(String pSessionId, String pMessageFromClient); + boolean updateNfcTag(String pSessionId, in Tag pTag); + } + + + +Callback +"""""""" + +.. code-block:: java + + package com.governikus.ausweisapp2; + + interface IAusweisApp2SdkCallback + { + void sessionIdGenerated(String pSessionId, boolean pIsSecureSessionId); + void receive(String pJson); + void sdkDisconnected(); + } + + + + +Background service +------------------ +The integrated and external variants use the same method to establish +a connection to the AusweisApp2 SDK. The AusweisApp2 SDK is a background +service in the external AusweisApp2 or an embedded background service +in your own application. .. _android_binding_service: Binding to the service ----------------------- +^^^^^^^^^^^^^^^^^^^^^^ In order to start the AusweisApp2 SDK it is necessary to bind to the Android service supplied by the SDK. This binding fulfils two purposes: - First it starts the SDK. - - Second it enables the client to establish an IPC connection as mentioned above. + - Second it enables the client to establish an IPC connection as + mentioned above. Due to the nature of an Android service, there can be only one instance of @@ -194,12 +264,11 @@ section, section :ref:`android_create_session`. Create connection -^^^^^^^^^^^^^^^^^ +""""""""""""""""" First of all, in order to bind to the service, one needs to instantiate an Android ServiceConnection. Subsequently, the object is passed to the Android API and the contained -methods are invoked -by Android on service connection and disconnection. +methods are invoked by Android on service connection and disconnection. .. code-block:: java @@ -225,15 +294,20 @@ by Android on service connection and disconnection. +.. _android_raw_connection: + Bind service to raw connection -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +"""""""""""""""""""""""""""""" In order to perform the actual binding a directed Intent, which identifies the AusweisApp2 SDK, is created. -This Intent is send to -the Android API along with the ServiceConnection created above. -This API call either starts up the SDK if it is the first client, -or connects to the running SDK instance -if there is already another client bound. +This Intent is sent to the Android API along with the ServiceConnection +created above. This API call either starts up the SDK if it is the +first client, or connects to the running SDK instance if there is already +another client bound. + +If you use the external variant of AusweisApp2 you need to pass the package +name of Governikus. Otherwise you need to pass your own package name +as the integrated variant is a background service of your application. .. code-block:: java @@ -244,9 +318,15 @@ if there is already another client bound. // [...] + String pkg = "com.governikus.ausweisapp2"; + + boolean useIntegrated = true; // use external or integrated + if (useIntegrated) + pkg = getApplicationContext().getPackageName(); + String name = "com.governikus.ausweisapp2.START_SERVICE"; Intent serviceIntent = new Intent(name); - serviceIntent.setPackage("com.governikus.ausweisapp2"); + serviceIntent.setPackage(pkg); bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE); .. seealso:: @@ -258,8 +338,8 @@ if there is already another client bound. Redirect to Play Store -^^^^^^^^^^^^^^^^^^^^^^ -It is necessary that AusweisApp2 is installed in order to use the SDK. +"""""""""""""""""""""" +It is necessary that AusweisApp2 is installed in order to use the external SDK. It is recommended to check and display a message in case the user needs to install AusweisApp2 first. Also, the user should be redirected to the Play Store entry to find the app. @@ -286,13 +366,15 @@ the Play Store entry to find the app. } } +.. note:: + This is not necessary if you use the integrated variant. .. _android_init_aidl: Initializing the AIDL connection -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +"""""""""""""""""""""""""""""""" Once the Android service of the AusweisApp2 SDK is successfully started and bound to by the client, the Android system calls the onServiceConnected method of the ServiceConnection @@ -348,7 +430,7 @@ The example below stores this instance in the member variable mSdk. .. _android_create_session: Create session to AusweisApp2 ------------------------------ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Once your client is bound to the AusweisApp2 SDK service and you have initialized the AIDL IPC mechanism, you are ready to use the actual SDK API. @@ -437,7 +519,7 @@ and establishing a session. Send command -^^^^^^^^^^^^ +"""""""""""" In order to send a JSON command to the AusweisApp2 SDK, you need to invoke the **send** function of your instance of **IAusweisApp2Sdk**. For this command to be processed by the SDK you need to supply the session ID which you have @@ -464,7 +546,7 @@ previously received. The listing below shows an example. Receive message -^^^^^^^^^^^^^^^ +""""""""""""""" Messages from the AusweisApp2 SDK are passed to you via the same instance of **IAusweisApp2SdkCallback** in which you have received the session ID. The **receive** method is called each time the SDK sends a message. @@ -477,7 +559,7 @@ The **receive** method is called each time the SDK sends a message. .. _android_disconnect_sdk: Disconnect from SDK -------------------- +^^^^^^^^^^^^^^^^^^^ In order to disconnect from the AusweisApp2 SDK you need to invalidate your instance of **IBinder**. There are two possibilities to do this. The first one is to unbind from the SDK Android service to undo your binding, like @@ -500,7 +582,7 @@ shown in the code listing below. The second one is to return false in the .. _android_nfc_tags: Passing NFC tags to the SDK ---------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^ NFC tags can only be detected by applications which have a foreground window on the Android platform. A common workaround for this problem is to equip background services with a transparent window which is shown @@ -521,7 +603,7 @@ Android displaying an App Chooser. Permissions in AndroidManifest.xml -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +"""""""""""""""""""""""""""""""""" The client applications needs to register the NFC permission as shown in the listing below in order to access the NFC reader hardware. @@ -534,11 +616,14 @@ listing below in order to access the NFC reader hardware. https://developer.android.com/guide/topics/security/permissions.html +.. note:: + The integrated variant already provides an **AndroidManifest.xml** with + prepared permissions. Intent-Filter in AndroidManifest.xml -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +"""""""""""""""""""""""""""""""""""" In order to be informed about attached NFC tags by Android, the client application is required to register an intent filter. The appropriate filter is shown in the listing below. @@ -556,7 +641,7 @@ filter is shown in the listing below. NFC Technology Filter -^^^^^^^^^^^^^^^^^^^^^ +""""""""""""""""""""" Since there are many different kinds of NFC tags, Android requires the application to register a technology filter for the kind of tags the application wants to receive. The proper filter for the German eID card is shown @@ -573,7 +658,7 @@ in the listing below. Implementation -^^^^^^^^^^^^^^ +"""""""""""""" As it is common on the Android platform, information is send to applications encapsulated in instances of the **Intent** class. In order to process newly discovered NFC tags, Intents which are given to the application need to be @@ -614,7 +699,7 @@ The listing below shows an example for the described process. Dispatching NFC tags in foreground ----------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ As already mentioned under :ref:`android_nfc_tags`, an App Chooser is displayed for discovered NFC tags by Android if multiple applications which are able to dispatch NFC tags are installed. An application can suppress this App Chooser diff --git a/docs/sdk/commands.rst b/docs/sdk/commands.rst index 7c719fa..c6b9592 100644 --- a/docs/sdk/commands.rst +++ b/docs/sdk/commands.rst @@ -294,9 +294,14 @@ If the last attempt to enter the PIN failed, AusweisApp2 will send the message :ref:`enter_puk` as the retryCounter is decreased to **0**. +.. versionchanged:: 1.16.0 + The parameter "value" must be omitted if the used :ref:`reader` has a + keypad. + - **value**: The personal identification number (PIN) of the card. - This must be 6 digits. + This must be 6 digits if the :ref:`reader` has no keypad, otherwise + this parameter must be omitted. .. code-block:: json @@ -325,9 +330,14 @@ to send this command to unblock the last retry of :ref:`set_pin`. The AusweisApp2 will send an :ref:`enter_can` message on error. Otherwise the workflow will continue with :ref:`enter_pin`. +.. versionchanged:: 1.16.0 + The parameter "value" must be omitted if the used :ref:`reader` has a + keypad. + - **value**: The card access number (CAN) of the card. - This must be 6 digits. + This must be 6 digits if the :ref:`reader` has no keypad, otherwise + this parameter must be omitted. .. code-block:: json @@ -358,8 +368,14 @@ or if the PUK is operative. Otherwise the workflow will continue with :ref:`enter_pin`. For detailed information see message :ref:`enter_puk`. +.. versionchanged:: 1.16.0 + The parameter "value" must be omitted if the used :ref:`reader` has a + keypad. + + - **value**: The personal unblocking key (PUK) of the card. - This must be 10 digits. + This must be 10 digits if the :ref:`reader` has no keypad, otherwise + this parameter must be omitted. .. code-block:: json diff --git a/docs/sdk/desktop.rst b/docs/sdk/desktop.rst new file mode 100644 index 0000000..77d2fe1 --- /dev/null +++ b/docs/sdk/desktop.rst @@ -0,0 +1,104 @@ +Desktop +======= +This chapter deals with the desktop specific properties of the AusweisApp2 SDK. +The AusweisApp2 core is reachable over a **WebSocket** which is running by +default since AusweisApp2 1.16.0. Subsequent sections deal with the SDK +interface itself and explain which steps are necessary in order to talk to +the AusweisApp2 SDK. + + + +WebSocket +--------- +The AusweisApp2 uses the same default port as defined in TR-03124-1. +Your application can connect to ``ws://localhost:24727/eID-Kernel`` to +establish a bidirectional connection. + +You can check the version of AusweisApp2 by the ``Server`` header of the HTTP +response or by an additional query to get the :ref:`status`. + +If the WebSocket handshake was succesful your application can send :doc:`commands` +and receive :doc:`messages`. +The AusweisApp2 will send an HTTP error 503 "Service Unavailable" if the WebSocket +is disabled. + +.. seealso:: + + https://tools.ietf.org/html/rfc6455 + + + +User installed +^^^^^^^^^^^^^^ +Your application can connect to a user installed AusweisApp2. If the +user already has an active workflow your request will be refused by +an HTTP error 409 "Conflict". Also it is not possible to connect +multiple times to the WebSocket as only one connection is allowed and +will be refused by an HTTP error 429 "Too Many Requests". Once an +application is connected to the WebSocket the graphical user interface +of the AusweisApp2 will be blocked and shows a hint that another +application uses the AusweisApp2. + +.. important:: + + Please provide a ``User-Agent`` in your HTTP upgrade request! The AusweisApp2 + will show the content to the user as a hint which application uses the AusweisApp2. + + + +Integrated +^^^^^^^^^^ +You can deliver separate AusweisApp2 binaries inside your own application or +start an already installed AusweisApp2. +If your application spawns a separate process you should provide the cmdline +parameter ``--port 0`` to avoid conflicts with a user started AusweisApp2 and +other processes that uses a specified port. + +The AusweisApp2 will create a text file in the system temporary directory to provide +the selected port. The port filename contains the PID of the running process to allow +multiple instances at the same time. + +Example: **$TMPDIR/AusweisApp2.12345.port** + +Your application can avoid the graphical interface of AusweisApp2 by providing the +cmdline parameter ``--ui websocket``. + + + +.. _status: + +Status +------ +TR-03124-1 defined a query for status information. This is useful to fetch current +version of installed AusweisApp2 to check if the version supports the WebSocket-API. + +You can get this by a HTTP GET query to ``http://localhost:24727/eID-Client?Status``. +If you prefer the JSON syntax you can add it to the parameter ``?Status=json`` to get +the following information. + +.. code-block:: json + + { + "Implementation-Title": "AusweisApp2", + "Implementation-Vendor": "Governikus GmbH & Co. KG", + "Implementation-Version": "1.16.0", + "Name": "AusweisApp2", + "Specification-Title": "TR-03124", + "Specification-Vendor": "Federal Office for Information Security", + "Specification-Version": "1.3" + } + + +.. seealso:: + + The AusweisApp2 SDK provides a :ref:`get_info` command and an :ref:`info` message + to fetch the same information to check the compatibility of used AusweisApp2. + + + +Reader +------ +The AusweisApp2 SDK uses PC/SC and paired Smartphones as card reader. If the +user wants to use the "smartphone as card reader" feature it is necessary +to pair the devices by the graphical interface of AusweisApp2. The AusweisApp2 SDK +provides no API to pair those devices. diff --git a/docs/sdk/index.rst b/docs/sdk/index.rst index 433d147..2aafb08 100644 --- a/docs/sdk/index.rst +++ b/docs/sdk/index.rst @@ -1,6 +1,6 @@ .. only:: html - .. image:: ../../resources/images/AppLogo_AutentApp2_2014.png + .. image:: ../../resources/images/Logo_AusweisApp2.png :alt: AusweisApp2 :align: center :width: 200pt @@ -27,12 +27,14 @@ Table of contents +.. _connection: .. toctree:: :maxdepth: 3 :caption: Connection android + desktop .. raw:: latex @@ -42,6 +44,7 @@ Table of contents +.. _protocol: .. toctree:: :maxdepth: 2 diff --git a/docs/sdk/intro.rst b/docs/sdk/intro.rst index 94fac2d..e303281 100644 --- a/docs/sdk/intro.rst +++ b/docs/sdk/intro.rst @@ -5,12 +5,12 @@ the AusweisApp2 as an additional service. It distinguishes between a connection to the application and the communication between your application and AusweisApp2. -The section "Connection" will show you what you need to do to +The section :ref:`connection` will show you what you need to do to set up a connection to AusweisApp2. Once you have established a connection you can send and receive JSON documents in a bi-directional manner. There are different commands and messages. These are listed -and described in the section "Protocol". The protocol +and described in the section :ref:`protocol`. The protocol is split up in :doc:`commands` and :doc:`messages`. Commands will be sent by your application to control AusweisApp2. Messages contain additional information to your command diff --git a/docs/sdk/messages.rst b/docs/sdk/messages.rst index b420bb3..da71caa 100644 --- a/docs/sdk/messages.rst +++ b/docs/sdk/messages.rst @@ -364,6 +364,7 @@ again but without an error parameter. { "name": "NFC", "attached": true, + "keypad": false, "card": { "inoperative": false, @@ -424,6 +425,7 @@ AusweisApp2 will send an :ref:`enter_pin` again with a retryCounter of **3**. { "name": "NFC", "attached": true, + "keypad": false, "card": { "inoperative": false, @@ -482,6 +484,7 @@ Please see the note for more information. { "name": "NFC", "attached": true, + "keypad": false, "card": { "inoperative": false, @@ -642,10 +645,16 @@ If a workflow is in progress and a card with disabled eID functionality was inserted, this message will still be sent, but the workflow will be paused until a card with enabled eID functionality is inserted. +.. versionadded:: 1.16.0 + Parameter **keypad** added. + - **name**: Identifier of card reader. - - **attached**: Indicates if a card reader is connected or disconnected. + - **attached**: Indicates whether a card reader is connected or disconnected. + + - **keypad**: Indicates whether a card reader has a keypad. The parameter + is only shown when a reader is attached. - **card**: Provides information about inserted card, otherwise null. @@ -665,6 +674,7 @@ until a card with enabled eID functionality is inserted. "msg": "READER", "name": "NFC", "attached": true, + "keypad": false, "card": { "inoperative": false, @@ -695,12 +705,14 @@ Provides information about all connected card readers. { "name": "Example reader 1 [SmartCard] (1234567) 01 00", "attached": true, + "keypad": true, "card": null }, { "name": "NFC", "attached": true, + "keypad": false, "card": { "inoperative": false, diff --git a/docs/sdk/workflow.rst b/docs/sdk/workflow.rst index 1ef2145..1fc4bb0 100644 --- a/docs/sdk/workflow.rst +++ b/docs/sdk/workflow.rst @@ -32,7 +32,7 @@ into the connected card reader. {"cmd": "ACCEPT"} - {"msg": "ENTER_PIN", "reader": {"attached":true,"card":{"inoperative":false,"deactivated":false,"inserted":true,"retryCounter":3},"name":"NFC"}} + {"msg": "ENTER_PIN", "reader": {"attached":true,"card":{"inoperative":false,"deactivated":false,"retryCounter":3},"keypad":false,"name":"NFC"}} {"cmd": "SET_PIN", "value": "123456"} @@ -62,23 +62,23 @@ into the connected card reader. {"msg": "INSERT_CARD"} - {"msg": "ENTER_PIN", "reader": {"attached":true,"card":{"inoperative":false,"deactivated":false,"inserted":true,"retryCounter":3},"name":"NFC"}} + {"msg": "ENTER_PIN", "reader": {"attached":true,"card":{"inoperative":false,"deactivated":false,"retryCounter":3},"keypad":false,"name":"NFC"}} {"cmd": "SET_PIN", "value": "000000"} - {"msg": "ENTER_PIN", "reader": {"attached":true,"card":{"inoperative":false,"deactivated":false,"inserted":true,"retryCounter":2},"name":"NFC"}} + {"msg": "ENTER_PIN", "reader": {"attached":true,"card":{"inoperative":false,"deactivated":false,"retryCounter":2},"keypad":false,"name":"NFC"}} {"cmd": "SET_PIN", "value": "000001"} - {"msg": "ENTER_CAN", "reader": {"attached":true,"card":{"inoperative":false,"deactivated":false,"inserted":true,"retryCounter":1},"name":"NFC"}} + {"msg": "ENTER_CAN", "reader": {"attached":true,"card":{"inoperative":false,"deactivated":false,"retryCounter":1},"keypad":false,"name":"NFC"}} {"cmd": "SET_CAN", "value": "000000"} - {"msg": "ENTER_CAN", "reader": {"attached":true,"card":{"inoperative":false,"deactivated":false,"inserted":true,"retryCounter":1},"name":"NFC"}} + {"msg": "ENTER_CAN", "reader": {"attached":true,"card":{"inoperative":false,"deactivated":false,"retryCounter":1},"keypad":false,"name":"NFC"}} {"cmd": "SET_CAN", "value": "654321"} - {"msg": "ENTER_PIN", "reader": {"attached":true,"card":{"inoperative":false,"deactivated":false,"inserted":true,"retryCounter":1},"name":"NFC"}} + {"msg": "ENTER_PIN", "reader": {"attached":true,"card":{"inoperative":false,"deactivated":false,"retryCounter":1},"keypad":false,"name":"NFC"}} {"cmd": "SET_PIN", "value": "123456"} diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt index e98ad56..fd9aab6 100644 --- a/libs/CMakeLists.txt +++ b/libs/CMakeLists.txt @@ -26,8 +26,6 @@ IF(ANDROID) ENDIF() ENDIF() -ADD_FLAG(-fstack-protector-strong -fstack-protector NOQUOTES VAR COMPILER_FLAGS) - IF(MSVC) FIND_PROGRAM(MAKE nmake CMAKE_FIND_ROOT_PATH_BOTH) @@ -61,11 +59,15 @@ ELSE() SET(CMAKE_BUILD_TYPE "DEBUG" CACHE STRING "build type configuration" FORCE) ENDIF() -IF(NOT ${CMAKE_BUILD_TYPE} STREQUAL "DEBUG" AND NOT ${CMAKE_BUILD_TYPE} STREQUAL "RELEASE") - MESSAGE(FATAL_ERROR "CMAKE_BUILD_TYPE is invalid! Available options: RELEASE, DEBUG") +IF(NOT ${CMAKE_BUILD_TYPE} STREQUAL "DEBUG" AND NOT ${CMAKE_BUILD_TYPE} STREQUAL "RELEASE" AND NOT ${CMAKE_BUILD_TYPE} STREQUAL "RELWITHDEBINFO") + MESSAGE(FATAL_ERROR "CMAKE_BUILD_TYPE is invalid! Available options: RELEASE, RELWITHDEBINFO, DEBUG") ENDIF() -FIND_HOST_PACKAGE(Perl REQUIRED) +IF(MSVC) + FIND_HOST_PACKAGE(Perl REQUIRED) +ELSE() + SET(PERL_EXECUTABLE perl) +ENDIF() FIND_HOST_PACKAGE(PythonInterp 2.7 REQUIRED) IF(NOT "${PYTHON_VERSION_MAJOR}" STREQUAL "2") @@ -84,9 +86,6 @@ ENDIF() IF(NOT DESTINATION_DIR) SET(DESTINATION_DIR ${PROJECT_BINARY_DIR}/dist) ENDIF() -IF(MSYS) - STRING(REPLACE "C:/msys/1.0" "" DESTINATION_DIR ${DESTINATION_DIR}) -ENDIF() SET(PATCHES_DIR ${PROJECT_SOURCE_DIR}/../patches) @@ -103,15 +102,15 @@ SET_DIRECTORY_PROPERTIES(PROPERTIES EP_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/b) INCLUDE(Messages) -################################## Versionen -SET(QT 5.9.3) -SET(QT_HASH 57acd8f03f830c2d7dc29fbe28aaa96781b2b9bdddce94196e6761a0f88c6046) +################################## Versions +SET(QT 5.11.2) +SET(QT_HASH c6104b840b6caee596fa9a35bc5f57f67ed5a99d6a36497b6fe66f990a53ca81) -SET(OPENSSL 1.0.2o) -SET(OPENSSL_HASH ec3f5c9714ba0fd45cb4e087301eb1336c317e0d20b575a125050470e8089e4d) +SET(OPENSSL 1.1.1) +SET(OPENSSL_HASH 2836875a0f89c03d0fdf483941512613a50cfb421d6fd94b9f41d7279d586a3d) ################################## Files -SET(QT_FILE qt-everywhere-opensource-src-${QT}.tar.xz) +SET(QT_FILE qt-everywhere-src-${QT}.tar.xz) SET(OPENSSL_FILE openssl-${OPENSSL}.tar.gz) ################################## Downloads @@ -120,61 +119,64 @@ IF("${QT}" MATCHES "alpha|beta|rc") ELSE() SET(QT_DEST_DIR official_releases) ENDIF() -STRING(SUBSTRING ${QT} 0 3 QT_SUBVERSION) +STRING(SUBSTRING ${QT} 0 4 QT_SUBVERSION) SET(QT_URL https://download.qt.io/${QT_DEST_DIR}/qt/${QT_SUBVERSION}/${QT}/single) SET(OPENSSL_URL https://www.openssl.org/source) -IF(ANDROID OR APPLE) - FIND_PROGRAM(SH_CMD sh CMAKE_FIND_ROOT_PATH_BOTH) - IF(SH_CMD) - MESSAGE(STATUS "Using 'sh' command... ${SH_CMD}") - ELSE() - MESSAGE(FATAL_ERROR "Cannot find 'sh' command") - ENDIF() -ENDIF() - - SET(ENABLED_TARGETS) ################################## OpenSSL ######################################################################### LIST(APPEND ENABLED_TARGETS openssl) -SET(OPENSSL_CONFIGURE_FLAGS no-ssl2 no-ssl3 no-ssl3-method no-dtls no-srp no-idea no-mdc2 no-rc5 no-hw no-engine no-dso -DOPENSSL_NO_HEARTBEATS shared) + +SET(OPENSSL_CONFIGURE_FLAGS no-camellia no-bf no-aria no-seed no-poly1305 no-srp no-gost no-ocsp no-idea no-mdc2 no-rc2 no-rc4 no-rc5 no-srtp no-hw no-sm2 no-sm3 no-sm4) +SET(OPENSSL_CONFIGURE_FLAGS ${OPENSSL_CONFIGURE_FLAGS} no-cast no-chacha no-blake2 no-rmd160 no-scrypt no-siphash no-whirlpool no-md4 no-des) +SET(OPENSSL_CONFIGURE_FLAGS ${OPENSSL_CONFIGURE_FLAGS} no-tls1 no-tls1-method no-tls1_1 no-tls1_1-method no-tls1_3 no-ssl3 no-ssl3-method no-dtls no-dtls1-method no-dtls1_2-method) +SET(OPENSSL_CONFIGURE_FLAGS ${OPENSSL_CONFIGURE_FLAGS} no-deprecated no-engine no-async no-dso no-comp no-ts no-makedepend no-tests shared) + +IF(${CMAKE_BUILD_TYPE} STREQUAL "DEBUG") + SET(OPENSSL_CONFIGURE_FLAGS --debug ${OPENSSL_CONFIGURE_FLAGS}) +ELSE() + ADD_FLAG(-Os NOQUOTES VAR OPENSSL_COMPILER_FLAGS) +ENDIF() + +ADD_FLAG(-fstack-protector-strong -fstack-protector NOQUOTES VAR OPENSSL_COMPILER_FLAGS) IF(IOS) - SET(OPENSSL_PATCH_COMMAND ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/openssl_iOS.patch && ) - SET(OPENSSL_CONFIGURE_FLAGS ${OPENSSL_CONFIGURE_FLAGS} iphoneos-cross) + SET(OPENSSL_CONFIGURE_FLAGS ${OPENSSL_CONFIGURE_FLAGS} ios64-cross) SET(OPENSSL_ENV export CROSS_TOP=${CMAKE_IOS_DEVELOPER_ROOT} && export CROSS_SDK=iPhoneOS.sdk &&) - SET(OPENSSL_COMPILER_FLAGS "-arch arm64") ELSEIF(APPLE) SET(OPENSSL_CONFIGURE_FLAGS ${OPENSSL_CONFIGURE_FLAGS} darwin64-x86_64-cc) - SET(COMPILER_FLAGS "${COMPILER_FLAGS} -mmacosx-version-min=10.9") + SET(OPENSSL_COMPILER_FLAGS ${OPENSSL_COMPILER_FLAGS} -mmacosx-version-min=10.11) ELSEIF(MINGW) SET(OPENSSL_CONFIGURE_FLAGS ${OPENSSL_CONFIGURE_FLAGS} mingw) ELSEIF(MSVC) - SET(OPENSSL_PREBUILD ms\\do_ms.bat) SET(OPENSSL_CONFIGURE_FLAGS ${OPENSSL_CONFIGURE_FLAGS} no-asm VC-WIN32) - SET(OPENSSL_ADDITIONAL_MAKE -f ms/ntdll.mak) - SET(OPENSSL_INSTALL_TARGET install) ELSEIF(ANDROID) IF(CMAKE_ANDROID_ARCH_ABI STREQUAL "armeabi-v7a") - SET(OPENSSL_ARCH android-armv7) - SET(OPENSSL_COMPILER_FLAGS "-mfloat-abi=softfp") + SET(OPENSSL_ARCH android-arm) + SET(OPENSSL_COMPILER_FLAGS ${OPENSSL_COMPILER_FLAGS} -mfloat-abi=softfp) ELSEIF(CMAKE_ANDROID_ARCH_ABI STREQUAL "x86") SET(OPENSSL_ARCH android-x86) ELSEIF(CMAKE_ANDROID_ARCH_ABI STREQUAL "arm64-v8a") - SET(OPENSSL_ARCH android) + SET(OPENSSL_ARCH android-arm64) ELSE() MESSAGE(FATAL_ERROR "CMAKE_ANDROID_ARCH_ABI not supported by openssl") ENDIF() + SET(OPENSSL_CONFIGURE_FLAGS ${OPENSSL_CONFIGURE_FLAGS} no-stdio ${OPENSSL_ARCH}) - SET(OPENSSL_ENV export ANDROID_DEV=${CMAKE_SYSROOT}/usr &&) - IF(UNIFIED_INCLUDE) - SET(OPENSSL_CONFIGURE_FLAGS ${OPENSSL_CONFIGURE_FLAGS} -D__ANDROID_API__=${CMAKE_SYSTEM_VERSION} -isystem${UNIFIED_INCLUDE} -isystem${UNIFIED_INCLUDE}/${ANDROID_TOOLCHAIN_MACHINE_NAME}) + GET_FILENAME_COMPONENT(toolchain_bin "${CMAKE_C_COMPILER}" DIRECTORY) + SET(OPENSSL_ENV export PATH=${toolchain_bin}/:$ENV{PATH} &&) + IF(NOT CMAKE_COMPILER_IS_GNUCXX) + SET(OPENSSL_ENV ${OPENSSL_ENV} export CC=clang && export CXX=clang++ &&) ENDIF() - SET(OPENSSL_CONFIGURE_FLAGS ${OPENSSL_CONFIGURE_FLAGS} --cross-compile-prefix=${CMAKE_C_ANDROID_TOOLCHAIN_PREFIX} ${OPENSSL_ARCH}) + + IF(UNIFIED_INCLUDE) + SET(OPENSSL_COMPILER_FLAGS ${OPENSSL_COMPILER_FLAGS} -isystem${UNIFIED_INCLUDE} -isystem${UNIFIED_INCLUDE}/${ANDROID_TOOLCHAIN_MACHINE_NAME}) + ENDIF() + SET(OPENSSL_COMPILER_FLAGS ${OPENSSL_COMPILER_FLAGS} -D__ANDROID_API__=${CMAKE_SYSTEM_VERSION}) ELSEIF(BSD) SET(OPENSSL_CONFIGURE_FLAGS ${OPENSSL_CONFIGURE_FLAGS} BSD-x86_64) ELSEIF(LINUX) @@ -188,56 +190,45 @@ ELSE() MESSAGE(FATAL_ERROR "Unsupported system") ENDIF() -IF(NOT OPENSSL_PREBUILD) - SET(OPENSSL_PREBUILD ${MAKE} depend) -ENDIF() - -IF(NOT OPENSSL_INSTALL_TARGET) - SET(OPENSSL_INSTALL_TARGET install_sw) -ENDIF() - -# OpenSSL does not support multiple make jobs! ExternalProject_Add(openssl URL ${OPENSSL_URL}/${OPENSSL_FILE} URL_HASH SHA256=${OPENSSL_HASH} DOWNLOAD_DIR ${PACKAGES_DIR} - PATCH_COMMAND - ${OPENSSL_PATCH_COMMAND} - ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/openssl-fix-no-engine-build.patch && - ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/openssl-Revert-Configure-use-a-better-method-to-identify-gcc.patch && - ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/openssl-RSA-key-generation-ensure-BN_mod_inverse-and-BN_mod_.patch && - ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/openssl-Reject-excessively-large-primes-in-DH-key-generation.patch && - ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/openssl_rsa_psk.patch - - CONFIGURE_COMMAND ${OPENSSL_ENV} ${PERL_EXECUTABLE} Configure --prefix=${DESTINATION_DIR} ${OPENSSL_CONFIGURE_FLAGS} "${COMPILER_FLAGS}" "${OPENSSL_COMPILER_FLAGS}" - BUILD_COMMAND ${OPENSSL_ENV} ${MAKE} ${OPENSSL_ADDITIONAL_MAKE} + PATCH_COMMAND ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/openssl-android-shlib_variant.patch && + ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/openssl-Ignore-disabled-ciphers.patch && + ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/openssl-DSA-mod-inverse-fix.patch && + ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/openssl-Timing-vulnerability-in-DSA-signature-generation-CVE.patch && + ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/openssl-Timing-vulnerability-in-ECDSA-signature-generation-C.patch + CONFIGURE_COMMAND ${OPENSSL_ENV} ${PERL_EXECUTABLE} Configure --prefix=${DESTINATION_DIR} ${OPENSSL_CONFIGURE_FLAGS} "${OPENSSL_COMPILER_FLAGS}" + BUILD_COMMAND ${OPENSSL_ENV} ${MAKE} ${MAKE_JOBS} BUILD_IN_SOURCE 1 - INSTALL_COMMAND ${MAKE} ${OPENSSL_ADDITIONAL_MAKE} ${OPENSSL_INSTALL_TARGET} + INSTALL_COMMAND ${OPENSSL_ENV} ${MAKE} ${MAKE_JOBS} install_sw ) -ExternalProject_Add_Step(openssl prebuild - COMMAND ${OPENSSL_ENV} ${OPENSSL_PREBUILD} +ExternalProject_Add_Step(openssl configdata + COMMAND ${PERL_EXECUTABLE} configdata.pm --dump DEPENDEES configure DEPENDERS build WORKING_DIRECTORY ) -IF(UNIX) - ADD_CUSTOM_COMMAND(TARGET openssl POST_BUILD COMMAND chmod 755 ${DESTINATION_DIR}/lib/libssl*${CMAKE_SHARED_LIBRARY_SUFFIX} ${DESTINATION_DIR}/lib/libcrypto*${CMAKE_SHARED_LIBRARY_SUFFIX}) -ENDIF() -IF(ANDROID) - ADD_CUSTOM_COMMAND(TARGET openssl POST_BUILD - COMMAND ${CMAKE_C_COMPILER} --sysroot ${CMAKE_SYSROOT} -o ${DESTINATION_DIR}/lib/libgovcrypto${CMAKE_SHARED_LIBRARY_SUFFIX} -shared -Wl,-soname=libgovcrypto${CMAKE_SHARED_LIBRARY_SUFFIX} -Wl,--whole-archive ${DESTINATION_DIR}/lib/libcrypto${CMAKE_STATIC_LIBRARY_SUFFIX} -Wl,--no-whole-archive - COMMAND ${CMAKE_C_COMPILER} --sysroot ${CMAKE_SYSROOT} -o ${DESTINATION_DIR}/lib/libgovssl${CMAKE_SHARED_LIBRARY_SUFFIX} -shared -Wl,-soname=libgovssl${CMAKE_SHARED_LIBRARY_SUFFIX} -Wl,--whole-archive ${DESTINATION_DIR}/lib/libssl${CMAKE_STATIC_LIBRARY_SUFFIX} -Wl,--no-whole-archive ${DESTINATION_DIR}/lib/libgovcrypto${CMAKE_SHARED_LIBRARY_SUFFIX}) -ELSEIF(MAC) - SET(OPENSSL_FILE_VERSION 1.0.0) +IF(MAC) + SET(OPENSSL_FILE_VERSION 1.1) ADD_CUSTOM_COMMAND(TARGET openssl POST_BUILD COMMAND install_name_tool -id libcrypto.${OPENSSL_FILE_VERSION}${CMAKE_SHARED_LIBRARY_SUFFIX} ${DESTINATION_DIR}/lib/libcrypto.${OPENSSL_FILE_VERSION}${CMAKE_SHARED_LIBRARY_SUFFIX} COMMAND install_name_tool -id libssl.${OPENSSL_FILE_VERSION}${CMAKE_SHARED_LIBRARY_SUFFIX} ${DESTINATION_DIR}/lib/libssl.${OPENSSL_FILE_VERSION}${CMAKE_SHARED_LIBRARY_SUFFIX} COMMAND install_name_tool -change ${DESTINATION_DIR}/lib/libcrypto.${OPENSSL_FILE_VERSION}${CMAKE_SHARED_LIBRARY_SUFFIX} libcrypto.${OPENSSL_FILE_VERSION}${CMAKE_SHARED_LIBRARY_SUFFIX} ${DESTINATION_DIR}/lib/libssl.${OPENSSL_FILE_VERSION}${CMAKE_SHARED_LIBRARY_SUFFIX}) ENDIF() +IF(IOS) + # Remove this work-around! Do not build any .dylib or be able to use .dylib + # Globbing is not supported by cmake command mode! This will work if executed with unix shell only. + ADD_CUSTOM_COMMAND(TARGET openssl POST_BUILD COMMAND ${CMAKE_COMMAND} -E remove ${DESTINATION_DIR}/lib/*.dylib) +ELSEIF(ANDROID) + ADD_CUSTOM_COMMAND(TARGET openssl POST_BUILD COMMAND ${CMAKE_COMMAND} -E remove ${DESTINATION_DIR}/lib/*.a) +ENDIF() + ################################## Qt ######################################################################### LIST(APPEND ENABLED_TARGETS qt) @@ -246,21 +237,29 @@ IF(${CMAKE_BUILD_TYPE} STREQUAL "DEBUG") SET(QT_CONFIGURE_FLAGS -debug -qml-debug) SET(QT_PATCH_COMMAND ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-Enable-debug-output-for-OpenSSL.patch &&) ELSE() - SET(QT_CONFIGURE_FLAGS -release -no-qml-debug) + SET(QT_CONFIGURE_FLAGS -release -optimize-size -no-qml-debug) + SET(QT_CONFIGURE_FLAGS ${QT_CONFIGURE_FLAGS} $<$:-force-debug-info>) ENDIF() +SET(QT_CONFIGURE_FLAGS ${QT_CONFIGURE_FLAGS} -prefix ${DESTINATION_DIR} -opensource -confirm-license -c++std c++11 -qt-zlib -no-mtdev -qt-libpng -qt-libjpeg -no-dbus -no-harfbuzz -qt-pcre -system-proxies -no-compile-examples -nomake examples -nomake tests -no-sql-sqlite -openssl-linked -I ${DESTINATION_DIR}/include -L ${DESTINATION_DIR}/lib) -SET(QT_CONFIGURE_FLAGS ${QT_CONFIGURE_FLAGS} -prefix ${DESTINATION_DIR} -opensource -confirm-license -c++std c++11 -qt-zlib -no-mtdev -qt-libpng -qt-libjpeg -no-harfbuzz -qt-pcre -system-proxies -no-compile-examples -nomake examples -nomake tests -no-sql-sqlite -openssl-linked -I ${DESTINATION_DIR}/include -L ${DESTINATION_DIR}/lib) +IF(NOT ANDROID) + LIST(APPEND NO_FEATURES bearermanagement) +ENDIF() -LIST(APPEND NO_FEATURES bearermanagement ftp paint_debug) +LIST(APPEND NO_FEATURES ftp paint_debug lcdnumber mdiarea) +LIST(APPEND NO_FEATURES calendarwidget colordialog cups dial fontcombobox fontdialog) LIST(APPEND NO_FEATURES imageformat_bmp imageformat_ppm imageformat_xbm) -LIST(APPEND NO_FEATURES sharedmemory textodfwriter) +LIST(APPEND NO_FEATURES sharedmemory textodfwriter filesystemwatcher) +LIST(APPEND NO_FEATURES undocommand undogroup undostack undoview) +LIST(APPEND NO_FEATURES printer printdialog printpreviewdialog printpreviewwidget) +LIST(APPEND NO_FEATURES splashscreen syntaxhighlighter dom sql) FOREACH(feature ${NO_FEATURES}) SET(QT_CONFIGURE_FLAGS ${QT_CONFIGURE_FLAGS} -no-feature-${feature}) ENDFOREACH() -SET(QT_CONFIGURE_FLAGS_OTHER -no-journald -no-dbus -no-directfb -no-linuxfb) -SET(QT_CONFIGURE_FLAGS_SKIP_MODULES -skip qtscxml -skip qtxmlpatterns -skip qtwebchannel -skip qtwebengine -skip qtscript -skip qtactiveqt -skip qtlocation -skip qtserialbus -skip qtserialport -skip qtgamepad -skip qtvirtualkeyboard -skip qtcanvas3d -skip qtcharts -skip qtdatavis3d -skip qt3d -skip qtpurchasing -skip qtwayland -skip qtremoteobjects -skip qtspeech -skip qtwebview) +SET(QT_CONFIGURE_FLAGS_OTHER -no-journald -no-directfb -no-linuxfb) +SET(QT_CONFIGURE_FLAGS_SKIP_MODULES -skip qtwebglplugin -skip qtscxml -skip qtxmlpatterns -skip qtwebchannel -skip qtwebengine -skip qtscript -skip qtactiveqt -skip qtlocation -skip qtserialbus -skip qtserialport -skip qtgamepad -skip qtvirtualkeyboard -skip qtcanvas3d -skip qtcharts -skip qtdatavis3d -skip qt3d -skip qtpurchasing -skip qtwayland -skip qtremoteobjects -skip qtspeech -skip qtwebview -skip multimedia -skip qtquickcontrols) SET(QT_CONFIGURE ./configure) IF(IOS) @@ -280,20 +279,30 @@ ELSEIF(APPLE) ELSEIF(WIN32) IF(MSVC) SET(QT_PLATFORM win32-msvc2015) - SET(QT_OPENSSL OPENSSL_LIBS=-llibeay32\ -lssleay32) + SET(QT_OPENSSL OPENSSL_LIBS=-llibcrypto\ -llibssl) ELSE() SET(QT_PLATFORM win32-g++) SET(QT_OPENSSL OPENSSL_LIBS=-lcrypto\ -lssl) ENDIF() - SET(QT_CONFIGURE_FLAGS ${QT_CONFIGURE_FLAGS} ${QT_OPENSSL} -opengl desktop -no-icu -no-sql-odbc -platform ${QT_PLATFORM}) + SET(QT_CONFIGURE_FLAGS ${QT_CONFIGURE_FLAGS} ${QT_OPENSSL} -opengl dynamic -no-icu -no-sql-odbc -platform ${QT_PLATFORM}) SET(QT_CONFIGURE configure.bat) ELSEIF(ANDROID) + IF(CMAKE_COMPILER_IS_GNUCXX) + SET(ANDROID_XPLATFORM android-g++) + ELSE() + SET(ANDROID_XPLATFORM android-clang) + ENDIF() SET(QT_CONFIGURE_FLAGS ${QT_CONFIGURE_FLAGS} ${QT_CONFIGURE_FLAGS_OTHER} -android-sdk ${ANDROID_SDK} -android-ndk ${CMAKE_ANDROID_NDK} -android-ndk-platform android-${CMAKE_SYSTEM_VERSION} -android-ndk-host ${CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG} - -android-arch ${CMAKE_ANDROID_ARCH_ABI} -android-toolchain-version ${CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION} - -xplatform android-g++) - SET(QT_ENV export OPENSSL_LIBS=-lgovcrypto\ -lgovssl &&) + -android-arch ${CMAKE_ANDROID_ARCH_ABI} -android-toolchain-version ${ANDROID_NDK_TOOLCHAIN_VERSION} + -xplatform ${ANDROID_XPLATFORM}) + + IF(CMAKE_ANDROID_ARCH_ABI STREQUAL "arm64-v8a") + SET(QT_CONFIGURE_FLAGS ${QT_CONFIGURE_FLAGS} -no-use-gold-linker) + ENDIF() + + SET(QT_ENV export OPENSSL_LIBS=-lcrypto-gov\ -lssl-gov &&) ELSE() SET(QT_CONFIGURE_FLAGS ${QT_CONFIGURE_FLAGS} ${QT_CONFIGURE_FLAGS_OTHER} -no-libproxy) ENDIF() @@ -309,21 +318,28 @@ ExternalProject_Add(qt DOWNLOAD_DIR ${PACKAGES_DIR} PATCH_COMMAND ${QT_PATCH_COMMAND} - ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-Make-server-side-signature-algorithms-configurable.patch && - ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-Register-additional-meta-types.patch && - ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-Change-build-configuration-for-Qt-on-iOS.patch && ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-Disable-unused-imageformats.patch && - ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-Avoid-using-deprecated-APIs-on-iOS-10.0.patch && ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-Add-IsoDep-to-the-techList-on-Android.patch && - ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-macOS-iOS-Fix-garbled-text-under-some-conditions.patch && - ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-QCoreTextFontEngine-Fix-build-with-Xcode-9.3.patch && + ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-Introduce-reportError-to-fix-QMetaObject-invokeMethod.patch && + ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-Add-work-around-for-freebsd-build.patch && + ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-Remove-Qt-Labs-specific-plugins-from-the-build.patch && + ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-configure-refactor-directx-checks.patch && + ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-configure-detect-fxc.exe-more-thoroughly.patch && + ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-disable-designer.patch && + ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-fix-macOS-no-printer.patch && + ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-Use-QUrl-toString-when-forming-the-Host-header.patch && + ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-QUrl-Support-IPv6-addresses-with-zone-id.patch && + ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-Android-fix-compile-with-NDK-r18.patch && + ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-Android-Fix-crash.patch && + ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-QObject-Fix-isSignalConnected-when-signals-have-been.patch && + ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-Use-user-provided-session-data-if-available.patch && ${CMAKE_COMMAND} -E touch qtbase/.gitignore CONFIGURE_COMMAND ${QT_ENV} ${QT_CONFIGURE} ${QT_CONFIGURE_FLAGS} ${QT_CONFIGURE_FLAGS_SKIP_MODULES} BUILD_COMMAND ${MAKE} ${MAKE_JOBS} BUILD_IN_SOURCE 1 ) -ADD_CUSTOM_COMMAND(TARGET qt POST_BUILD COMMAND cmake -E touch ${DESTINATION_DIR}/mkspecs/qt_vendor_governikus) +ADD_CUSTOM_COMMAND(TARGET qt POST_BUILD COMMAND ${CMAKE_COMMAND} -E touch ${DESTINATION_DIR}/mkspecs/qt_vendor_governikus) ######################################################################### @@ -354,9 +370,6 @@ ENDIF() IF(IOS) SET(SYSTEM_NAME iOS) - # Remove this work-around! Do not build any .dylib or be able to use .dylib - # Globbing is not supported by cmake command mode! This will work if executed with unix shell only. - SET(CLEANUP_FILES ${CMAKE_COMMAND} -E remove ${DESTINATION_DIR}/lib/*.dylib) ELSEIF(ANDROID) SET(SYSTEM_NAME ${CMAKE_SYSTEM_NAME}_${CMAKE_CXX_COMPILER_ID}_${CMAKE_ANDROID_ARCH_ABI}) ELSE() @@ -373,9 +386,7 @@ ENDIF() SET(COMPRESSION cfJ) SET(COMPRESSION_FILENDING tar.xz) ADD_CUSTOM_TARGET(compress.pre ${compressed_filename} - COMMAND ${CLEANUP_FILES} COMMAND ${CMAKE_COMMAND} -E remove_directory "${DESTINATION_DIR}/doc" - COMMAND ${CMAKE_COMMAND} -E remove_directory "${DESTINATION_DIR}/lib/engines" COMMAND ${CMAKE_COMMAND} -E remove_directory "${DESTINATION_DIR}/share" COMMAND ${SIGN_COMMAND} DEPENDS ${COMPRESS_TARGETS} diff --git a/libs/README.rst b/libs/README.rst index aa4d77a..ef17004 100644 --- a/libs/README.rst +++ b/libs/README.rst @@ -17,7 +17,7 @@ Unterstützte Compiler: Notwendige Bibliotheken: -- Qt >= 5.9 +- Qt >= 5.10 - http://www.qt.io/download/ @@ -25,11 +25,7 @@ Notwendige Bibliotheken: - https://www.openssl.org/source/ - - Aus dem Ordner "patches" müssen die folgenden Patches angewandt werden. - (Sofern der automatische Build mittels CMake gestartet wird, werden - die Patches automatisch angewandt.) - - - openssl_rsa_psk.patch + - LibreSSL wird auf Grund des fehlenden RSA-PSK nicht unterstützt. - pcsclite >= 1.8 (nur Linux/FreeBSD) @@ -97,8 +93,8 @@ Beispiel: Innerhalb von /Users/governikus/AusweisApp2 befindet sich der Quellcod Windows MinGW ^^^^^^^^^^^^^ -Unter Windows ist es derzeit empfohlen einen Teil der Toolchain mittels MSYS zu bauen. -Perl muss dafür ebenfalls installiert sein. +Unter Windows ist es derzeit empfohlen einen Teil der Toolchain mittels MSYS2 zu bauen. +Perl muss dafür in MSYS2 nachinstalliert werden. Außerdem ist das Windows SDK notwendig. MinGW """"" @@ -109,20 +105,28 @@ MinGW https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/5.3.0/threads-posix/dwarf/i686-5.3.0-release-posix-dwarf-rt_v4-rev0.7z/download -MSYS -"""" +MSYS2 +""""" -- http://www.mingw.org/wiki/msys +- https://sourceforge.net/projects/msys2/files/Base/x86_64/ - - Getestet: 1.0.11 + - Getestet: msys2-base-x86_64-20180531.tar.xz -Perl -"""" +Windows SDK +""""""""""" -- http://www.activestate.com/activeperl/downloads +- https://developer.microsoft.com/de-de/windows/downloads/windows-10-sdk - - Getestet: 5.24.0 + - Getestet: 10.0.14393.795 und 10.0.16299.0 + + +Python 2 +"""""""" + +- https://www.python.org/downloads/ + + - Getestet: python-2.7.15.amd64.msi Vorbereitung @@ -131,46 +135,72 @@ Vorbereitung #. Eintragen des Ordners "bin" von der MinGW-Installation in %PATH%. -#. Installation von MSYS, welche sich auf die MinGW-Installation bezieht. +#. Entpacken von MSYS2. -#. Der Ordner von Perl muss sich in %PATH% befinden. +#. Start von "msys2_shell.cmd -use-full-path". -#. In der Datei "fstab" unter C:\msys\1.0\etc folgende Einträge mit den jeweiligen Pfaden: +#. System aktualisieren mittels "pacman -Syu" (danach MSYS2 neustarten). + +#. Perl nachinstallieren mittels "pacman -S perl". + +#. Installation des Windows SDK und setzen einer Systemvariable: :: - C:/mingw32/i686-5.3.0-release-posix-dwarf-rt_v4-rev0 /mingw - C:/Perl /perl - + Für das Windows SDK 10.0.15063.0 und neuer: + WindowsSdkVerBinPath = C:\Program Files (x86)\Windows Kits\10\bin\%VERSION% + Für alle älteren Versionen: + WindowsSdkDir = C:\Program Files (x86)\Windows Kits\10 Durch einige Probleme mit Unix-Shell-Skripten ist es derzeit leider -notwendig die Toolchain in zwei Schritten aufzubauen. +notwendig die Toolchain in zwei Schritten zu bauen. Hierzu muss OpenSSL und Qt separat gebaut werden. +#. Download und Start der Installation von Python. + +#. Sicherstellen, dass die python.exe während der Installation zum PATH hinzugefügt wird. + + +Eventuell muss für MinGW folgende Option gesetzt werden (QTBUG-16443): + +#. Windows --> gpedit.msc --> Enter (als Administrator) + +#. Richtlinien für Lokaler Computer + +#. Computerkonfiguration + +#. Administrative Vorlagen + +#. System + +#. Dateisystem + +#. Lange Win32-Pfade aktivieren + openssl / Qt """""""""""" Da Qt mittels Batchskript gebaut werden muss, ist es leider nicht möglich dies innerhalb -von MSYS zu bauen [2]. Daher wird OpenSSL und Qt mittels Windows-CLI konfiguriert. -Dabei wird Qt über Windows-CLI und OpenSSL unter MSYS gebaut. +von MSYS2 zu bauen [2]. Daher wird OpenSSL und Qt mittels Windows-CLI konfiguriert. +Dabei wird Qt über Windows-CLI und OpenSSL unter MSYS2 gebaut. #. cmd.exe von Windows starten -#. mkdir c:\msys\1.0\home\user\qt ("user" ist der Benutzer, der unter MSYS verwendet wird) +#. mkdir c:\msys64\home\user\qt ("user" ist der Benutzer, der unter MSYS2 verwendet wird) -#. cd c:\msys\1.0\home\user\qt +#. cd c:\msys64\home\user\qt #. cmake -DCMAKE_BUILD_TYPE=release -DPACKAGES_DIR=C:/packages C:/AusweisApp2/libs -G "MinGW Makefiles" -#. MSYS Shell starten +#. MSYS2 Shell starten ("msys2_shell.cmd -use-full-path") #. cd qt #. mingw32-make openssl -#. MSYS Shell verlassen +#. MSYS2 Shell verlassen -#. In der cmd.exe: c:\msys\1.0\home\user\qt +#. In der cmd.exe: c:\msys64\home\user\qt #. mingw32-make qt @@ -182,7 +212,7 @@ iOS """ Die Toolchain für iOS kann nur auf MacOS gebaut werden. Dabei müssen XCode und die Command Line Tools (siehe "xcode-select -p" bzw. "xcode-select --install") -auf dem Mac vorhanden sein. Die folgende Anleitung wurde unter MacOS 10.9 und 10.11 getestet. +auf dem Mac vorhanden sein. Die folgende Anleitung wurde unter macOS 10.12 getestet. Ebenfalls muss für den Build-Vorgang von Qt ein iOS Developer-Zertifikat mit Wildcard (*) im Keystore von MacOS hinterlegt sein. @@ -207,30 +237,33 @@ Komponenten vorhanden sein: - https://developer.android.com/tools/sdk/ndk/index.html - - Getestet: r10e (https://wiki.qt.io/Qt_for_Android_known_issues) + - Getestet: r18 (https://wiki.qt.io/Qt_for_Android_known_issues) - Android SDK mit gesetztem ANDROID_HOME - https://developer.android.com/studio/releases/sdk-tools.html - - Getestet: 25.2.5 + - Getestet: 26.1.1 - - Qt ist derzeit nicht mit aktuelleren kompatibel: https://bugreports.qt.io/browse/QTBUG-61988 +- SDK build tools - - Unter bestimmten Umständen kann es vorkommen, dass die Build-Tools-Version nicht erkannt - wird. Dies kann mittels der Umgebungsvariable ANDROID_BUILD_TOOLS_REVISION behoben werden. - Die genaue Version ist im Android Manager vom Android SDK (./tools/android) hinterlegt. + - https://developer.android.com/studio/releases/build-tools - - Getestet: 27.0.1 + - Getestet: 28.0.3 -- Um Qt erfolgreich zu bauen, sind verschiedene API Level von Android notwendig. - Diese sollten mindestens Level 18 und 21 sein. Nähere Informationen dazu +- SDK platform tools + + - https://developer.android.com/studio/releases/platform-tools + + - Getestet: 28.0.1 + +- Um Qt erfolgreich zu bauen, ist mindestens ein API-Levelpaket von Android notwendig. + Dieses sollte mindestens Level 21 sein. Nähere Informationen dazu sind im Wiki von Qt enthalten: http://wiki.qt.io/Android Die Plattformen können mittels Android Manager nachinstalliert werden. - JDK mit gesetztem JAVA_HOME -- Apache Ant mit gesetztem ANT_HOME Beispiel: Innerhalb von /home/governikus/AusweisApp2 befindet sich der Quellcode. diff --git a/patches/openssl-DSA-mod-inverse-fix.patch b/patches/openssl-DSA-mod-inverse-fix.patch new file mode 100644 index 0000000..b1e511a --- /dev/null +++ b/patches/openssl-DSA-mod-inverse-fix.patch @@ -0,0 +1,78 @@ +From f1b12b8713a739f27d74e6911580b2e70aea2fa4 Mon Sep 17 00:00:00 2001 +From: Pauli +Date: Mon, 29 Oct 2018 06:50:51 +1000 +Subject: [PATCH 1/3] DSA mod inverse fix + +There is a side channel attack against the division used to calculate one of +the modulo inverses in the DSA algorithm. This change takes advantage of the +primality of the modulo and Fermat's little theorem to calculate the inverse +without leaking information. + +Thanks to Samuel Weiser for finding and reporting this. + +Reviewed-by: Matthias St. Pierre +Reviewed-by: Bernd Edlinger +(Merged from https://github.com/openssl/openssl/pull/7487) + +(cherry picked from commit 415c33563528667868c3c653a612e6fc8736fd79) +--- + crypto/dsa/dsa_ossl.c | 32 +++++++++++++++++++++++++++++++- + 1 file changed, 31 insertions(+), 1 deletion(-) + +diff --git x/crypto/dsa/dsa_ossl.c y/crypto/dsa/dsa_ossl.c +index ac1f65a51a..ca20811200 100644 +--- x/crypto/dsa/dsa_ossl.c ++++ y/crypto/dsa/dsa_ossl.c +@@ -23,6 +23,8 @@ static int dsa_do_verify(const unsigned char *dgst, int dgst_len, + DSA_SIG *sig, DSA *dsa); + static int dsa_init(DSA *dsa); + static int dsa_finish(DSA *dsa); ++static BIGNUM *dsa_mod_inverse_fermat(const BIGNUM *k, const BIGNUM *q, ++ BN_CTX *ctx); + + static DSA_METHOD openssl_dsa_meth = { + "OpenSSL DSA method", +@@ -259,7 +261,7 @@ static int dsa_sign_setup(DSA *dsa, BN_CTX *ctx_in, + goto err; + + /* Compute part of 's = inv(k) (m + xr) mod q' */ +- if ((kinv = BN_mod_inverse(NULL, k, dsa->q, ctx)) == NULL) ++ if ((kinv = dsa_mod_inverse_fermat(k, dsa->q, ctx)) == NULL) + goto err; + + BN_clear_free(*kinvp); +@@ -393,3 +395,31 @@ static int dsa_finish(DSA *dsa) + BN_MONT_CTX_free(dsa->method_mont_p); + return 1; + } ++ ++/* ++ * Compute the inverse of k modulo q. ++ * Since q is prime, Fermat's Little Theorem applies, which reduces this to ++ * mod-exp operation. Both the exponent and modulus are public information ++ * so a mod-exp that doesn't leak the base is sufficient. A newly allocated ++ * BIGNUM is returned which the caller must free. ++ */ ++static BIGNUM *dsa_mod_inverse_fermat(const BIGNUM *k, const BIGNUM *q, ++ BN_CTX *ctx) ++{ ++ BIGNUM *res = NULL; ++ BIGNUM *r, *e; ++ ++ if ((r = BN_new()) == NULL) ++ return NULL; ++ ++ BN_CTX_start(ctx); ++ if ((e = BN_CTX_get(ctx)) != NULL ++ && BN_set_word(r, 2) ++ && BN_sub(e, q, r) ++ && BN_mod_exp_mont(r, k, e, q, ctx, NULL)) ++ res = r; ++ else ++ BN_free(r); ++ BN_CTX_end(ctx); ++ return res; ++} +-- +2.19.1 + diff --git a/patches/openssl-Ignore-disabled-ciphers.patch b/patches/openssl-Ignore-disabled-ciphers.patch new file mode 100644 index 0000000..aab52cf --- /dev/null +++ b/patches/openssl-Ignore-disabled-ciphers.patch @@ -0,0 +1,30 @@ +From 2bcd8e6e1fe62ef5667c1bb8ad1bfe54a0aeaa99 Mon Sep 17 00:00:00 2001 +From: Matt Caswell +Date: Wed, 24 Oct 2018 10:11:00 +0100 +Subject: [PATCH] Ignore disabled ciphers when deciding if we are using ECC + +use_ecc() was always returning 1 because there are default (TLSv1.3) +ciphersuites that use ECC - even if those ciphersuites are disabled by +other options. + +Fixes #7471 +--- + ssl/statem/extensions_clnt.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git x/ssl/statem/extensions_clnt.c y/ssl/statem/extensions_clnt.c +index 4b5e6fe2b87..dcadc2a3231 100644 +--- x/ssl/statem/extensions_clnt.c ++++ y/ssl/statem/extensions_clnt.c +@@ -128,6 +128,10 @@ static int use_ecc(SSL *s) + for (i = 0; i < end; i++) { + const SSL_CIPHER *c = sk_SSL_CIPHER_value(cipher_stack, i); + ++ /* Skip disabled ciphers */ ++ if (ssl_cipher_disabled(s, c, SSL_SECOP_CIPHER_SUPPORTED, 0)) ++ continue; ++ + alg_k = c->algorithm_mkey; + alg_a = c->algorithm_auth; + if ((alg_k & (SSL_kECDHE | SSL_kECDHEPSK)) + diff --git a/patches/openssl-RSA-key-generation-ensure-BN_mod_inverse-and-BN_mod_.patch b/patches/openssl-RSA-key-generation-ensure-BN_mod_inverse-and-BN_mod_.patch deleted file mode 100644 index a724b2c..0000000 --- a/patches/openssl-RSA-key-generation-ensure-BN_mod_inverse-and-BN_mod_.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 349a41da1ad88ad87825414752a8ff5fdd6a6c3f Mon Sep 17 00:00:00 2001 -From: Billy Brumley -Date: Wed, 11 Apr 2018 10:10:58 +0300 -Subject: [PATCH] RSA key generation: ensure BN_mod_inverse and BN_mod_exp_mont - both get called with BN_FLG_CONSTTIME flag set. - -CVE-2018-0737 - -Reviewed-by: Rich Salz -Reviewed-by: Matt Caswell -(cherry picked from commit 6939eab03a6e23d2bd2c3f5e34fe1d48e542e787) ---- - crypto/rsa/rsa_gen.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git x/crypto/rsa/rsa_gen.c y/crypto/rsa/rsa_gen.c -index 9ca5dfefb7..42b89a8dfa 100644 ---- x/crypto/rsa/rsa_gen.c -+++ y/crypto/rsa/rsa_gen.c -@@ -156,6 +156,8 @@ static int rsa_builtin_keygen(RSA *rsa, int bits, BIGNUM *e_value, - if (BN_copy(rsa->e, e_value) == NULL) - goto err; - -+ BN_set_flags(rsa->p, BN_FLG_CONSTTIME); -+ BN_set_flags(rsa->q, BN_FLG_CONSTTIME); - BN_set_flags(r2, BN_FLG_CONSTTIME); - /* generate p and q */ - for (;;) { --- -2.17.0 - diff --git a/patches/openssl-Reject-excessively-large-primes-in-DH-key-generation.patch b/patches/openssl-Reject-excessively-large-primes-in-DH-key-generation.patch deleted file mode 100644 index c64bc24..0000000 --- a/patches/openssl-Reject-excessively-large-primes-in-DH-key-generation.patch +++ /dev/null @@ -1,42 +0,0 @@ -From 3984ef0b72831da8b3ece4745cac4f8575b19098 Mon Sep 17 00:00:00 2001 -From: Guido Vranken -Date: Mon, 11 Jun 2018 19:38:54 +0200 -Subject: [PATCH] Reject excessively large primes in DH key generation. - -CVE-2018-0732 - -Signed-off-by: Guido Vranken - -(cherry picked from commit 91f7361f47b082ae61ffe1a7b17bb2adf213c7fe) - -Reviewed-by: Tim Hudson -Reviewed-by: Matt Caswell -(Merged from https://github.com/openssl/openssl/pull/6457) ---- - crypto/dh/dh_key.c | 7 ++++++- - 1 file changed, 6 insertions(+), 1 deletion(-) - -diff --git x/crypto/dh/dh_key.c y/crypto/dh/dh_key.c -index 387558f146..f235e0d682 100644 ---- x/crypto/dh/dh_key.c -+++ y/crypto/dh/dh_key.c -@@ -130,10 +130,15 @@ static int generate_key(DH *dh) - int ok = 0; - int generate_new_key = 0; - unsigned l; -- BN_CTX *ctx; -+ BN_CTX *ctx = NULL; - BN_MONT_CTX *mont = NULL; - BIGNUM *pub_key = NULL, *priv_key = NULL; - -+ if (BN_num_bits(dh->p) > OPENSSL_DH_MAX_MODULUS_BITS) { -+ DHerr(DH_F_GENERATE_KEY, DH_R_MODULUS_TOO_LARGE); -+ return 0; -+ } -+ - ctx = BN_CTX_new(); - if (ctx == NULL) - goto err; --- -2.18.0 - diff --git a/patches/openssl-Revert-Configure-use-a-better-method-to-identify-gcc.patch b/patches/openssl-Revert-Configure-use-a-better-method-to-identify-gcc.patch deleted file mode 100644 index f8797e1..0000000 --- a/patches/openssl-Revert-Configure-use-a-better-method-to-identify-gcc.patch +++ /dev/null @@ -1,75 +0,0 @@ -From 2a33b07d56c7e30a18dda5760111af267271c236 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Andr=C3=A9=20Klitzing?= -Date: Tue, 24 Apr 2018 16:13:56 +0200 -Subject: [PATCH] Revert "Configure: use a better method to identify gcc and - derivates" - -This reverts commit 78e9e3f945935c91d8dfe0e832a95d6ea8d05f34. ---- - Configure | 22 ++++++++-------------- - 1 file changed, 8 insertions(+), 14 deletions(-) - -diff --git x/Configure y/Configure -index 744b493b96..fe7565ebd9 100755 ---- x/Configure -+++ y/Configure -@@ -1269,7 +1269,7 @@ my ($prelflags,$postlflags)=split('%',$lflags); - if (defined($postlflags)) { $lflags=$postlflags; } - else { $lflags=$prelflags; undef $prelflags; } - --if ($target =~ /^mingw/ && `$cross_compile_prefix$cc --target-help 2>&1` !~ m/\-mno\-cygwin/m) -+if ($target =~ /^mingw/ && `$cc --target-help 2>&1` !~ m/\-mno\-cygwin/m) - { - $cflags =~ s/\-mno\-cygwin\s*//; - $shared_ldflag =~ s/\-mno\-cygwin\s*//; -@@ -1661,25 +1661,18 @@ if ($shlib_version_number =~ /(^[0-9]*)\.([0-9\.]*)/) - $shlib_minor=$2; - } - --my %predefined; -- --# collect compiler pre-defines from gcc or gcc-alike... --open(PIPE, "$cross_compile_prefix$cc -dM -E -x c /dev/null 2>&1 |"); --while () { -- m/^#define\s+(\w+(?:\(\w+\))?)(?:\s+(.+))?/ or last; -- $predefined{$1} = defined($2) ? $2 : ""; --} --close(PIPE); -+my $ecc = $cc; -+$ecc = "clang" if `$cc --version 2>&1` =~ /clang/; - - if ($strict_warnings) - { - my $wopt; -- die "ERROR --strict-warnings requires gcc or clang" unless defined($predefined{__GNUC__}); -+ die "ERROR --strict-warnings requires gcc or clang" unless ($ecc =~ /gcc$/ or $ecc =~ /clang$/); - foreach $wopt (split /\s+/, $gcc_devteam_warn) - { - $cflags .= " $wopt" unless ($cflags =~ /(^|\s)$wopt(\s|$)/) - } -- if (defined($predefined{__clang__})) -+ if ($ecc eq "clang") - { - foreach $wopt (split /\s+/, $clang_devteam_warn) - { -@@ -1730,14 +1723,15 @@ while () - s/^NM=\s*/NM= \$\(CROSS_COMPILE\)/; - s/^RANLIB=\s*/RANLIB= \$\(CROSS_COMPILE\)/; - s/^RC=\s*/RC= \$\(CROSS_COMPILE\)/; -- s/^MAKEDEPPROG=.*$/MAKEDEPPROG= \$\(CROSS_COMPILE\)$cc/ if $predefined{__GNUC__} >= 3; -+ s/^MAKEDEPPROG=.*$/MAKEDEPPROG= \$\(CROSS_COMPILE\)$cc/ if $cc eq "gcc"; - } - else { - s/^CC=.*$/CC= $cc/; - s/^AR=\s*ar/AR= $ar/; - s/^RANLIB=.*/RANLIB= $ranlib/; - s/^RC=.*/RC= $windres/; -- s/^MAKEDEPPROG=.*$/MAKEDEPPROG= $cc/ if $predefined{__GNUC__} >= 3; -+ s/^MAKEDEPPROG=.*$/MAKEDEPPROG= $cc/ if $cc eq "gcc"; -+ s/^MAKEDEPPROG=.*$/MAKEDEPPROG= $cc/ if $ecc eq "gcc" || $ecc eq "clang"; - } - s/^CFLAG=.*$/CFLAG= $cflags/; - s/^DEPFLAG=.*$/DEPFLAG=$depflags/; --- -2.17.0 - diff --git a/patches/openssl-Timing-vulnerability-in-DSA-signature-generation-CVE.patch b/patches/openssl-Timing-vulnerability-in-DSA-signature-generation-CVE.patch new file mode 100644 index 0000000..a5d7611 --- /dev/null +++ b/patches/openssl-Timing-vulnerability-in-DSA-signature-generation-CVE.patch @@ -0,0 +1,109 @@ +From 8abfe72e8c1de1b95f50aa0d9134803b4d00070f Mon Sep 17 00:00:00 2001 +From: Pauli +Date: Wed, 24 Oct 2018 07:42:46 +1000 +Subject: [PATCH 2/3] Timing vulnerability in DSA signature generation + (CVE-2018-0734). + +Avoid a timing attack that leaks information via a side channel that +triggers when a BN is resized. Increasing the size of the BNs +prior to doing anything with them suppresses the attack. + +Thanks due to Samuel Weiser for finding and locating this. + +Reviewed-by: Bernd Edlinger +(Merged from https://github.com/openssl/openssl/pull/7486) + +(cherry picked from commit a9cfb8c2aa7254a4aa6a1716909e3f8cb78049b6) +--- + crypto/dsa/dsa_ossl.c | 28 +++++++++++++++------------- + 1 file changed, 15 insertions(+), 13 deletions(-) + +diff --git x/crypto/dsa/dsa_ossl.c y/crypto/dsa/dsa_ossl.c +index ca20811200..2dd2d7489a 100644 +--- x/crypto/dsa/dsa_ossl.c ++++ y/crypto/dsa/dsa_ossl.c +@@ -9,6 +9,7 @@ + + #include + #include "internal/cryptlib.h" ++#include "internal/bn_int.h" + #include + #include + #include "dsa_locl.h" +@@ -180,9 +181,9 @@ static int dsa_sign_setup(DSA *dsa, BN_CTX *ctx_in, + { + BN_CTX *ctx = NULL; + BIGNUM *k, *kinv = NULL, *r = *rp; +- BIGNUM *l, *m; ++ BIGNUM *l; + int ret = 0; +- int q_bits; ++ int q_bits, q_words; + + if (!dsa->p || !dsa->q || !dsa->g) { + DSAerr(DSA_F_DSA_SIGN_SETUP, DSA_R_MISSING_PARAMETERS); +@@ -191,8 +192,7 @@ static int dsa_sign_setup(DSA *dsa, BN_CTX *ctx_in, + + k = BN_new(); + l = BN_new(); +- m = BN_new(); +- if (k == NULL || l == NULL || m == NULL) ++ if (k == NULL || l == NULL) + goto err; + + if (ctx_in == NULL) { +@@ -203,9 +203,9 @@ static int dsa_sign_setup(DSA *dsa, BN_CTX *ctx_in, + + /* Preallocate space */ + q_bits = BN_num_bits(dsa->q); +- if (!BN_set_bit(k, q_bits) +- || !BN_set_bit(l, q_bits) +- || !BN_set_bit(m, q_bits)) ++ q_words = bn_get_top(dsa->q); ++ if (!bn_wexpand(k, q_words + 2) ++ || !bn_wexpand(l, q_words + 2)) + goto err; + + /* Get random k */ +@@ -240,14 +240,17 @@ static int dsa_sign_setup(DSA *dsa, BN_CTX *ctx_in, + * small timing information leakage. We then choose the sum that is + * one bit longer than the modulus. + * +- * TODO: revisit the BN_copy aiming for a memory access agnostic +- * conditional copy. ++ * There are some concerns about the efficacy of doing this. More ++ * specificly refer to the discussion starting with: ++ * https://github.com/openssl/openssl/pull/7486#discussion_r228323705 ++ * The fix is to rework BN so these gymnastics aren't required. + */ + if (!BN_add(l, k, dsa->q) +- || !BN_add(m, l, dsa->q) +- || !BN_copy(k, BN_num_bits(l) > q_bits ? l : m)) ++ || !BN_add(k, l, dsa->q)) + goto err; + ++ BN_consttime_swap(BN_is_bit_set(l, q_bits), k, l, q_words + 2); ++ + if ((dsa)->meth->bn_mod_exp != NULL) { + if (!dsa->meth->bn_mod_exp(dsa, r, dsa->g, k, dsa->p, ctx, + dsa->method_mont_p)) +@@ -260,7 +263,7 @@ static int dsa_sign_setup(DSA *dsa, BN_CTX *ctx_in, + if (!BN_mod(r, r, dsa->q, ctx)) + goto err; + +- /* Compute part of 's = inv(k) (m + xr) mod q' */ ++ /* Compute part of 's = inv(k) (m + xr) mod q' */ + if ((kinv = dsa_mod_inverse_fermat(k, dsa->q, ctx)) == NULL) + goto err; + +@@ -275,7 +278,6 @@ static int dsa_sign_setup(DSA *dsa, BN_CTX *ctx_in, + BN_CTX_free(ctx); + BN_clear_free(k); + BN_clear_free(l); +- BN_clear_free(m); + return ret; + } + +-- +2.19.1 + diff --git a/patches/openssl-Timing-vulnerability-in-ECDSA-signature-generation-C.patch b/patches/openssl-Timing-vulnerability-in-ECDSA-signature-generation-C.patch new file mode 100644 index 0000000..807236d --- /dev/null +++ b/patches/openssl-Timing-vulnerability-in-ECDSA-signature-generation-C.patch @@ -0,0 +1,44 @@ +From b1d6d55ece1c26fa2829e2b819b038d7b6d692b4 Mon Sep 17 00:00:00 2001 +From: Pauli +Date: Fri, 26 Oct 2018 10:54:58 +1000 +Subject: [PATCH 3/3] Timing vulnerability in ECDSA signature generation + (CVE-2018-0735) + +Preallocate an extra limb for some of the big numbers to avoid a reallocation +that can potentially provide a side channel. + +Reviewed-by: Bernd Edlinger +(Merged from https://github.com/openssl/openssl/pull/7486) + +(cherry picked from commit 99540ec79491f59ed8b46b4edf130e17dc907f52) +--- + crypto/ec/ec_mult.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git x/crypto/ec/ec_mult.c y/crypto/ec/ec_mult.c +index 7e1b3650e7..0e0a5e1394 100644 +--- x/crypto/ec/ec_mult.c ++++ y/crypto/ec/ec_mult.c +@@ -206,8 +206,8 @@ int ec_scalar_mul_ladder(const EC_GROUP *group, EC_POINT *r, + */ + cardinality_bits = BN_num_bits(cardinality); + group_top = bn_get_top(cardinality); +- if ((bn_wexpand(k, group_top + 1) == NULL) +- || (bn_wexpand(lambda, group_top + 1) == NULL)) { ++ if ((bn_wexpand(k, group_top + 2) == NULL) ++ || (bn_wexpand(lambda, group_top + 2) == NULL)) { + ECerr(EC_F_EC_SCALAR_MUL_LADDER, ERR_R_BN_LIB); + goto err; + } +@@ -244,7 +244,7 @@ int ec_scalar_mul_ladder(const EC_GROUP *group, EC_POINT *r, + * k := scalar + 2*cardinality + */ + kbit = BN_is_bit_set(lambda, cardinality_bits); +- BN_consttime_swap(kbit, k, lambda, group_top + 1); ++ BN_consttime_swap(kbit, k, lambda, group_top + 2); + + group_top = bn_get_top(group->field); + if ((bn_wexpand(s->X, group_top) == NULL) +-- +2.19.1 + diff --git a/patches/openssl-android-shlib_variant.patch b/patches/openssl-android-shlib_variant.patch new file mode 100644 index 0000000..2c8c7db --- /dev/null +++ b/patches/openssl-android-shlib_variant.patch @@ -0,0 +1,11 @@ +--- x/Configurations/15-android.conf ++++ y/Configurations/15-android.conf +@@ -136,6 +136,8 @@ + bn_ops => sub { android_ndk()->{bn_ops} }, + bin_cflags => "-pie", + enable => [ ], ++ shlib_variant => '-gov', ++ shared_extension => '.so', + }, + "android-arm" => { + ################################################################ diff --git a/patches/openssl-fix-no-engine-build.patch b/patches/openssl-fix-no-engine-build.patch deleted file mode 100644 index c7b24ea..0000000 --- a/patches/openssl-fix-no-engine-build.patch +++ /dev/null @@ -1,75 +0,0 @@ -From aeae7469061c1675d651224789fc664d6809b0d9 Mon Sep 17 00:00:00 2001 -From: "Dr. Stephen Henson" -Date: Sat, 16 Jan 2016 16:11:34 +0000 -Subject: [PATCH] fix no-engine build - -Reviewed-by: Rich Salz -Reviewed-by: Richard Levitte ---- - crypto/ts/ts.h | 2 ++ - util/libeay.num | 4 ++-- - util/mk1mf.pl | 3 +-- - 3 files changed, 5 insertions(+), 4 deletions(-) - -diff --git x/crypto/ts/ts.h y/crypto/ts/ts.h -index 2daa1b2fb5..fc8c14b2d0 100644 ---- x/crypto/ts/ts.h -+++ y/crypto/ts/ts.h -@@ -737,9 +737,11 @@ EVP_PKEY *TS_CONF_load_key(const char *file, const char *pass); - const char *TS_CONF_get_tsa_section(CONF *conf, const char *section); - int TS_CONF_set_serial(CONF *conf, const char *section, TS_serial_cb cb, - TS_RESP_CTX *ctx); -+#ifndef OPENSSL_NO_ENGINE - int TS_CONF_set_crypto_device(CONF *conf, const char *section, - const char *device); - int TS_CONF_set_default_engine(const char *name); -+#endif - int TS_CONF_set_signer_cert(CONF *conf, const char *section, - const char *cert, TS_RESP_CTX *ctx); - int TS_CONF_set_certs(CONF *conf, const char *section, const char *certs, -diff --git x/util/libeay.num y/util/libeay.num -index fddfe1cbb2..a76424ceab 100755 ---- x/util/libeay.num -+++ y/util/libeay.num -@@ -3874,7 +3874,7 @@ b2i_PVK_bio 4250 EXIST::FUNCTION:RC4 - ASN1_UTCTIME_adj 4251 EXIST::FUNCTION: - TS_TST_INFO_new 4252 EXIST::FUNCTION: - EVP_MD_do_all_sorted 4253 EXIST::FUNCTION: --TS_CONF_set_default_engine 4254 EXIST::FUNCTION: -+TS_CONF_set_default_engine 4254 EXIST::FUNCTION:ENGINE - TS_ACCURACY_set_seconds 4255 EXIST::FUNCTION: - TS_TST_INFO_get_time 4256 EXIST::FUNCTION: - PKCS8_pkey_get0 4257 EXIST::FUNCTION: -@@ -4099,7 +4099,7 @@ EVP_PKEY_meth_find 4469 EXIST::FUNCTION: - EVP_PKEY_id 4470 EXIST::FUNCTION: - TS_TST_INFO_set_serial 4471 EXIST::FUNCTION: - a2i_GENERAL_NAME 4472 EXIST::FUNCTION: --TS_CONF_set_crypto_device 4473 EXIST::FUNCTION: -+TS_CONF_set_crypto_device 4473 EXIST::FUNCTION:ENGINE - EVP_PKEY_verify_init 4474 EXIST::FUNCTION: - TS_CONF_set_policies 4475 EXIST::FUNCTION: - ASN1_PCTX_new 4476 EXIST::FUNCTION: -diff --git x/util/mk1mf.pl y/util/mk1mf.pl -index 6b31496ed1..ccfb24ca55 100755 ---- x/util/mk1mf.pl -+++ y/util/mk1mf.pl -@@ -428,7 +428,6 @@ EOF - { - $extra_install .= <<"EOF" - \$(MKDIR) \"\$(INSTALLTOP)${o}lib${o}engines\" -- \$(CP) \$(E_SHLIB) \"\$(INSTALLTOP)${o}lib${o}engines\" - EOF - } - } -@@ -597,7 +596,7 @@ init: \$(TMP_D) \$(LIB_D) \$(INC_D) \$(INCO_D) \$(BIN_D) \$(TEST_D) headers - - headers: \$(HEADER) \$(EXHEADER) - --lib: \$(LIBS_DEP) \$(E_SHLIB) -+lib: \$(LIBS_DEP) - - exe: \$(T_EXE) \$(BIN_D)$o\$(E_EXE)$exep - --- -2.15.0 - diff --git a/patches/openssl_iOS.patch b/patches/openssl_iOS.patch deleted file mode 100644 index 83ba1cd..0000000 --- a/patches/openssl_iOS.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -ruN openssl-1.0.1j.orig/crypto/ui/ui_openssl.c openssl-1.0.1j/crypto/ui/ui_openssl.c ---- openssl-1.0.1j.orig/crypto/ui/ui_openssl.c 2014-10-15 14:53:39.000000000 +0200 -+++ openssl-1.0.1j/crypto/ui/ui_openssl.c 2014-11-05 13:10:13.574510723 +0100 -@@ -410,7 +410,7 @@ - return 1; - } - --static volatile sig_atomic_t intr_signal; -+static volatile int intr_signal; - #endif - - static int read_string_inner(UI *ui, UI_STRING *uis, int echo, int strip_nl) diff --git a/patches/openssl_rsa_psk.patch b/patches/openssl_rsa_psk.patch deleted file mode 100644 index 80d3cad..0000000 --- a/patches/openssl_rsa_psk.patch +++ /dev/null @@ -1,917 +0,0 @@ -From e681bc2125a396ff34aab4c3f629683dd0ce28bb Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Andr=C3=A9=20Klitzing?= -Date: Thu, 23 Apr 2015 20:59:30 +0200 -Subject: [PATCH] Introduce TLS-RSA-PSK support - -Build on the existing PSK support and introduce RSA-PSK -(cf. RFC 4279, 5487). -Based on the original patch by Christian J. Dietrich. - -This work has been sponsored by Governikus GmbH & Co. KG. - -PR: 2464 ---- - doc/apps/ciphers.pod | 12 +++ - ssl/s3_clnt.c | 106 ++++++++++++++++++++---- - ssl/s3_lib.c | 206 +++++++++++++++++++++++++++++++++++++++++++++- - ssl/s3_srvr.c | 226 ++++++++++++++++++++++++++++++++++++++++++++++++--- - ssl/ssl.h | 2 + - ssl/ssl_ciph.c | 9 +- - ssl/ssl_lib.c | 6 ++ - ssl/ssl_locl.h | 2 + - ssl/tls1.h | 36 ++++++++ - 9 files changed, 572 insertions(+), 33 deletions(-) - -diff --git x/doc/apps/ciphers.pod y/doc/apps/ciphers.pod -index fa16124d08..45db06c168 100644 ---- x/doc/apps/ciphers.pod -+++ y/doc/apps/ciphers.pod -@@ -585,10 +585,22 @@ Note: these ciphers can also be used in SSL v3. - - =head2 Pre shared keying (PSK) cipheruites - -+ TLS_RSA_PSK_WITH_RC4_128_SHA RSA-PSK-RC4-SHA -+ TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA RSA-PSK-3DES-EDE-CBC-SHA -+ TLS_RSA_PSK_WITH_AES_128_CBC_SHA RSA-PSK-AES128-CBC-SHA -+ TLS_RSA_PSK_WITH_AES_256_CBC_SHA RSA-PSK-AES256-CBC-SHA -+ TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 RSA-PSK-AES128-CBC-SHA256 -+ TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 RSA-PSK-AES256-CBC-SHA384 -+ TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 RSA-PSK-AES128-GCM-SHA256 -+ TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 RSA-PSK-AES256-GCM-SHA384 - TLS_PSK_WITH_RC4_128_SHA PSK-RC4-SHA - TLS_PSK_WITH_3DES_EDE_CBC_SHA PSK-3DES-EDE-CBC-SHA - TLS_PSK_WITH_AES_128_CBC_SHA PSK-AES128-CBC-SHA - TLS_PSK_WITH_AES_256_CBC_SHA PSK-AES256-CBC-SHA -+ TLS_PSK_WITH_AES_128_CBC_SHA256 PSK-AES128-CBC-SHA256 -+ TLS_PSK_WITH_AES_256_CBC_SHA384 PSK-AES256-CBC-SHA384 -+ TLS_PSK_WITH_AES_128_GCM_SHA256 PSK-AES128-GCM-SHA256 -+ TLS_PSK_WITH_AES_256_GCM_SHA384 PSK-AES256-GCM-SHA384 - - =head2 Deprecated SSL v2.0 cipher suites. - -diff --git x/ssl/s3_clnt.c y/ssl/s3_clnt.c -index 5b8b2da59f..ae0d4d840c 100644 ---- x/ssl/s3_clnt.c -+++ y/ssl/s3_clnt.c -@@ -342,7 +342,7 @@ int ssl3_connect(SSL *s) - } - #endif - /* Check if it is anon DH/ECDH, SRP auth */ -- /* or PSK */ -+ /* or plain PSK */ - if (! - (s->s3->tmp. - new_cipher->algorithm_auth & (SSL_aNULL | SSL_aSRP)) -@@ -1424,9 +1424,9 @@ int ssl3_get_key_exchange(SSL *s) - } - #ifndef OPENSSL_NO_PSK - /* -- * In plain PSK ciphersuite, ServerKeyExchange can be omitted if no -- * identity hint is sent. Set session->sess_cert anyway to avoid -- * problems later. -+ * In PSK ciphersuites, ServerKeyExchange can be omitted if no -+ * identity hint is sent. Set session->sess_cert for plain PSK -+ * anyway to avoid problems later. - */ - if (alg_k & SSL_kPSK) { - s->session->sess_cert = ssl_sess_cert_new(); -@@ -1471,7 +1471,12 @@ int ssl3_get_key_exchange(SSL *s) - al = SSL_AD_DECODE_ERROR; - - #ifndef OPENSSL_NO_PSK -- if (alg_k & SSL_kPSK) { -+ /* handle PSK identity hint */ -+ if (alg_k & SSL_kPSK -+#ifndef OPENSSL_NO_RSA -+ || alg_k & SSL_kRSAPSK -+#endif -+ ) { - param_len = 2; - if (param_len > n) { - SSLerr(SSL_F_SSL3_GET_KEY_EXCHANGE, SSL_R_LENGTH_TOO_SHORT); -@@ -2041,7 +2046,7 @@ int ssl3_get_key_exchange(SSL *s) - } - } else { - /* aNULL, aSRP or kPSK do not need public keys */ -- if (!(alg_a & (SSL_aNULL | SSL_aSRP)) && !(alg_k & SSL_kPSK)) { -+ if (!(alg_a & (SSL_aNULL | SSL_aSRP)) && !(alg_k & SSL_kPSK) && !(alg_k & SSL_kRSAPSK)) { - /* Might be wrong key type, check it */ - if (ssl3_check_cert_and_algorithm(s)) - /* Otherwise this shouldn't happen */ -@@ -3130,7 +3135,11 @@ int ssl3_send_client_key_exchange(SSL *s) - } - #endif - #ifndef OPENSSL_NO_PSK -- else if (alg_k & SSL_kPSK) { -+ else if (alg_k & SSL_kPSK -+#ifndef OPENSSL_NO_RSA -+ || alg_k & SSL_kRSAPSK -+#endif -+ ) { - /* - * The callback needs PSK_MAX_IDENTITY_LEN + 1 bytes to return a - * \0-terminated identity. The last byte is for us for simulating -@@ -3138,8 +3147,8 @@ int ssl3_send_client_key_exchange(SSL *s) - */ - char identity[PSK_MAX_IDENTITY_LEN + 2]; - size_t identity_len; -- unsigned char *t = NULL; - unsigned char psk_or_pre_ms[PSK_MAX_PSK_LEN * 2 + 4]; -+ unsigned char *t = psk_or_pre_ms; - unsigned int pre_ms_len = 0, psk_len = 0; - int psk_err = 1; - -@@ -3171,14 +3180,34 @@ int ssl3_send_client_key_exchange(SSL *s) - ERR_R_INTERNAL_ERROR); - goto psk_err; - } -- /* create PSK pre_master_secret */ -- pre_ms_len = 2 + psk_len + 2 + psk_len; -- t = psk_or_pre_ms; -- memmove(psk_or_pre_ms + psk_len + 4, psk_or_pre_ms, psk_len); -- s2n(psk_len, t); -- memset(t, 0, psk_len); -- t += psk_len; -- s2n(psk_len, t); -+ -+ if (alg_k & SSL_kPSK) { -+ /* create PSK pre_master_secret */ -+ pre_ms_len = 2 + psk_len + 2 + psk_len; -+ memmove(psk_or_pre_ms + psk_len + 4, psk_or_pre_ms, psk_len); -+ s2n(psk_len, t); -+ memset(t, 0, psk_len); -+ t += psk_len; -+ s2n(psk_len, t); -+ } -+#ifndef OPENSSL_NO_RSA -+ else if (alg_k & SSL_kRSAPSK) { -+ const unsigned int pre_ms_prefix = 48; -+ -+ pre_ms_len = 2 + 2 + 46 + 2 + psk_len; -+ memmove(psk_or_pre_ms + 52, psk_or_pre_ms, psk_len); -+ s2n(pre_ms_prefix, t); -+ -+ psk_or_pre_ms[2] = s->client_version >> 8; -+ psk_or_pre_ms[3] = s->client_version & 0xff; -+ t += 2; -+ -+ if (RAND_bytes(psk_or_pre_ms + 4, 46) <= 0) -+ goto psk_err; -+ t += 46; -+ s2n(psk_len, t); -+ } -+#endif - - if (s->session->psk_identity_hint != NULL) - OPENSSL_free(s->session->psk_identity_hint); -@@ -3208,8 +3237,41 @@ int ssl3_send_client_key_exchange(SSL *s) - pre_ms_len); - s2n(identity_len, p); - memcpy(p, identity, identity_len); -+ p += identity_len; - n = 2 + identity_len; -+ -+#ifndef OPENSSL_NO_RSA -+ if (alg_k & SSL_kRSAPSK) { -+ RSA *rsa; -+ int enc_n; -+ -+ if (s->session->sess_cert->peer_rsa_tmp != NULL) { -+ rsa = s->session->sess_cert->peer_rsa_tmp; -+ } else { -+ pkey = X509_get_pubkey(s->session->sess_cert->peer_pkeys[SSL_PKEY_RSA_ENC].x509); -+ if ((pkey == NULL) || (pkey->type != EVP_PKEY_RSA) || (pkey->pkey.rsa == NULL)) { -+ SSLerr(SSL_F_SSL3_SEND_CLIENT_KEY_EXCHANGE, ERR_R_INTERNAL_ERROR); -+ goto psk_err; -+ } -+ rsa = pkey->pkey.rsa; -+ EVP_PKEY_free(pkey); -+ } -+ -+ enc_n = RSA_public_encrypt(48, psk_or_pre_ms + 2, p + 2, rsa, RSA_PKCS1_PADDING); -+ if (enc_n <= 0) { -+ SSLerr(SSL_F_SSL3_SEND_CLIENT_KEY_EXCHANGE, SSL_R_BAD_RSA_ENCRYPT); -+ goto psk_err; -+ } -+ -+ n += enc_n; -+ -+ s2n(enc_n, p); -+ n += 2; -+ } -+#endif -+ - psk_err = 0; -+ - psk_err: - OPENSSL_cleanse(identity, sizeof(identity)); - OPENSSL_cleanse(psk_or_pre_ms, sizeof(psk_or_pre_ms)); -@@ -3580,7 +3642,11 @@ int ssl3_check_cert_and_algorithm(SSL *s) - } - #endif - #ifndef OPENSSL_NO_RSA -- if (alg_k & SSL_kRSA) { -+ if (alg_k & SSL_kRSA -+#ifndef OPENSSL_NO_PSK -+ || alg_k & SSL_kRSAPSK -+#endif -+ ) { - if (!SSL_C_IS_EXPORT(s->s3->tmp.new_cipher) && - !has_bits(i, EVP_PK_RSA | EVP_PKT_ENC)) { - SSLerr(SSL_F_SSL3_CHECK_CERT_AND_ALGORITHM, -@@ -3647,7 +3713,11 @@ int ssl3_check_cert_and_algorithm(SSL *s) - if (SSL_C_IS_EXPORT(s->s3->tmp.new_cipher) && - pkey_bits > SSL_C_EXPORT_PKEYLENGTH(s->s3->tmp.new_cipher)) { - #ifndef OPENSSL_NO_RSA -- if (alg_k & SSL_kRSA) { -+ if (alg_k & SSL_kRSA -+#ifndef OPENSSL_NO_PSK -+ || alg_k & SSL_kRSAPSK -+#endif -+ ) { - if (rsa == NULL) { - SSLerr(SSL_F_SSL3_CHECK_CERT_AND_ALGORITHM, - SSL_R_MISSING_EXPORT_TMP_RSA_KEY); -diff --git x/ssl/s3_lib.c y/ssl/s3_lib.c -index 1014a3fce1..0187d508a1 100644 ---- x/ssl/s3_lib.c -+++ y/ssl/s3_lib.c -@@ -1765,6 +1765,74 @@ OPENSSL_GLOBAL SSL_CIPHER ssl3_ciphers[] = { - 256, - 256, - }, -+ -+ -+#ifndef OPENSSL_NO_RSA -+ /* RSA-PSK ciphersuites from RFC4279 */ -+ /* Cipher 92 */ -+ { -+ 1, -+ TLS1_TXT_RSA_PSK_WITH_RC4_128_SHA, -+ TLS1_CK_RSA_PSK_WITH_RC4_128_SHA, -+ SSL_kRSAPSK, -+ SSL_aRSA, -+ SSL_RC4, -+ SSL_SHA1, -+ SSL_TLSV1, -+ SSL_NOT_EXP|SSL_MEDIUM, -+ SSL_HANDSHAKE_MAC_DEFAULT|TLS1_PRF, -+ 128, -+ 128, -+ }, -+ -+ /* Cipher 93 */ -+ { -+ 1, -+ TLS1_TXT_RSA_PSK_WITH_3DES_EDE_CBC_SHA, -+ TLS1_CK_RSA_PSK_WITH_3DES_EDE_CBC_SHA, -+ SSL_kRSAPSK, -+ SSL_aRSA, -+ SSL_3DES, -+ SSL_SHA1, -+ SSL_TLSV1, -+ SSL_NOT_EXP|SSL_HIGH, -+ SSL_HANDSHAKE_MAC_DEFAULT|TLS1_PRF, -+ 112, -+ 168, -+ }, -+ -+ /* Cipher 94 */ -+ { -+ 1, -+ TLS1_TXT_RSA_PSK_WITH_AES_128_CBC_SHA, -+ TLS1_CK_RSA_PSK_WITH_AES_128_CBC_SHA, -+ SSL_kRSAPSK, -+ SSL_aRSA, -+ SSL_AES128, -+ SSL_SHA1, -+ SSL_TLSV1, -+ SSL_NOT_EXP|SSL_HIGH, -+ SSL_HANDSHAKE_MAC_DEFAULT|TLS1_PRF, -+ 128, -+ 128, -+ }, -+ -+ /* Cipher 95 */ -+ { -+ 1, -+ TLS1_TXT_RSA_PSK_WITH_AES_256_CBC_SHA, -+ TLS1_CK_RSA_PSK_WITH_AES_256_CBC_SHA, -+ SSL_kRSAPSK, -+ SSL_aRSA, -+ SSL_AES256, -+ SSL_SHA1, -+ SSL_TLSV1, -+ SSL_NOT_EXP|SSL_HIGH, -+ SSL_HANDSHAKE_MAC_DEFAULT|TLS1_PRF, -+ 256, -+ 256, -+ }, -+#endif /* OPENSSL_NO_RSA */ - #endif /* OPENSSL_NO_PSK */ - - #ifndef OPENSSL_NO_SEED -@@ -2077,6 +2145,142 @@ OPENSSL_GLOBAL SSL_CIPHER ssl3_ciphers[] = { - 0}, - #endif - -+#ifndef OPENSSL_NO_PSK -+ /* PSK ciphersuites from RFC5487 */ -+ -+ /* Cipher A8 */ -+ { -+ 1, -+ TLS1_TXT_PSK_WITH_AES_128_GCM_SHA256, -+ TLS1_CK_PSK_WITH_AES_128_GCM_SHA256, -+ SSL_kPSK, -+ SSL_aPSK, -+ SSL_AES128GCM, -+ SSL_AEAD, -+ SSL_TLSV1_2, -+ SSL_NOT_EXP|SSL_HIGH, -+ SSL_HANDSHAKE_MAC_SHA256|TLS1_PRF_SHA256, -+ 128, -+ 128, -+ }, -+ -+ /* Cipher A9 */ -+ { -+ 1, -+ TLS1_TXT_PSK_WITH_AES_256_GCM_SHA384, -+ TLS1_CK_PSK_WITH_AES_256_GCM_SHA384, -+ SSL_kPSK, -+ SSL_aPSK, -+ SSL_AES256GCM, -+ SSL_AEAD, -+ SSL_TLSV1_2, -+ SSL_NOT_EXP|SSL_HIGH, -+ SSL_HANDSHAKE_MAC_SHA384|TLS1_PRF_SHA384, -+ 256, -+ 256, -+ }, -+ -+ #ifndef OPENSSL_NO_RSA -+ /* Cipher AC */ -+ { -+ 1, -+ TLS1_TXT_RSA_PSK_WITH_AES_128_GCM_SHA256, -+ TLS1_CK_RSA_PSK_WITH_AES_128_GCM_SHA256, -+ SSL_kRSAPSK, -+ SSL_aRSA, -+ SSL_AES128GCM, -+ SSL_AEAD, -+ SSL_TLSV1_2, -+ SSL_NOT_EXP|SSL_HIGH, -+ SSL_HANDSHAKE_MAC_SHA256|TLS1_PRF_SHA256, -+ 128, -+ 128, -+ }, -+ -+ /* Cipher AD */ -+ { -+ 1, -+ TLS1_TXT_RSA_PSK_WITH_AES_256_GCM_SHA384, -+ TLS1_CK_RSA_PSK_WITH_AES_256_GCM_SHA384, -+ SSL_kRSAPSK, -+ SSL_aRSA, -+ SSL_AES256GCM, -+ SSL_AEAD, -+ SSL_TLSV1_2, -+ SSL_NOT_EXP|SSL_HIGH, -+ SSL_HANDSHAKE_MAC_SHA384|TLS1_PRF_SHA384, -+ 256, -+ 256, -+ }, -+#endif /* OPENSSL_NO_RSA */ -+ -+ /* Cipher AE */ -+ { -+ 1, -+ TLS1_TXT_PSK_WITH_AES_128_CBC_SHA256, -+ TLS1_CK_PSK_WITH_AES_128_CBC_SHA256, -+ SSL_kPSK, -+ SSL_aPSK, -+ SSL_AES128, -+ SSL_SHA256, -+ SSL_TLSV1, -+ SSL_NOT_EXP|SSL_HIGH, -+ SSL_HANDSHAKE_MAC_SHA256|TLS1_PRF_SHA256, -+ 128, -+ 128, -+ }, -+ -+ /* Cipher AF */ -+ { -+ 1, -+ TLS1_TXT_PSK_WITH_AES_256_CBC_SHA384, -+ TLS1_CK_PSK_WITH_AES_256_CBC_SHA384, -+ SSL_kPSK, -+ SSL_aPSK, -+ SSL_AES256, -+ SSL_SHA384, -+ SSL_TLSV1, -+ SSL_NOT_EXP|SSL_HIGH, -+ SSL_HANDSHAKE_MAC_SHA384|TLS1_PRF_SHA384, -+ 256, -+ 256, -+ }, -+ -+ #ifndef OPENSSL_NO_RSA -+ /* Cipher B6 */ -+ { -+ 1, -+ TLS1_TXT_RSA_PSK_WITH_AES_128_CBC_SHA256, -+ TLS1_CK_RSA_PSK_WITH_AES_128_CBC_SHA256, -+ SSL_kRSAPSK, -+ SSL_aRSA, -+ SSL_AES128, -+ SSL_SHA256, -+ SSL_TLSV1, -+ SSL_NOT_EXP|SSL_HIGH, -+ SSL_HANDSHAKE_MAC_SHA256|TLS1_PRF_SHA256, -+ 128, -+ 128, -+ }, -+ -+ /* Cipher B7 */ -+ { -+ 1, -+ TLS1_TXT_RSA_PSK_WITH_AES_256_CBC_SHA384, -+ TLS1_CK_RSA_PSK_WITH_AES_256_CBC_SHA384, -+ SSL_kRSAPSK, -+ SSL_aRSA, -+ SSL_AES256, -+ SSL_SHA384, -+ SSL_TLSV1, -+ SSL_NOT_EXP|SSL_HIGH, -+ SSL_HANDSHAKE_MAC_SHA384|TLS1_PRF_SHA384, -+ 256, -+ 256, -+ }, -+#endif /* OPENSSL_NO_RSA */ -+#endif /* OPENSSL_NO_PSK */ -+ - #ifndef OPENSSL_NO_ECDH - /* Cipher C001 */ - { -@@ -4169,7 +4373,7 @@ SSL_CIPHER *ssl3_choose_cipher(SSL *s, STACK_OF(SSL_CIPHER) *clnt, - #endif /* OPENSSL_NO_KRB5 */ - #ifndef OPENSSL_NO_PSK - /* with PSK there must be server callback set */ -- if ((alg_k & SSL_kPSK) && s->psk_server_callback == NULL) -+ if ((alg_k & SSL_kPSK || alg_k & SSL_kRSAPSK) && s->psk_server_callback == NULL) - continue; - #endif /* OPENSSL_NO_PSK */ - -diff --git x/ssl/s3_srvr.c y/ssl/s3_srvr.c -index 0fb4845d44..3498836e7d 100644 ---- x/ssl/s3_srvr.c -+++ y/ssl/s3_srvr.c -@@ -467,19 +467,22 @@ int ssl3_accept(SSL *s) - - /* - * only send if a DH key exchange, fortezza or RSA but we have a -- * sign only certificate PSK: may send PSK identity hints For -- * ECC ciphersuites, we send a serverKeyExchange message only if -+ * sign only certificate -+ * -+ * PSK|RSAPSK: may send PSK identity hints. -+ * Send ServerKeyExchange if PSK identity hint is provided. -+ * -+ * For ECC ciphersuites, we send a serverKeyExchange message only if - * the cipher suite is either ECDH-anon or ECDHE. In other cases, - * the server certificate contains the server's public key for - * key exchange. - */ - if (0 -- /* -- * PSK: send ServerKeyExchange if PSK identity hint if -- * provided -- */ - #ifndef OPENSSL_NO_PSK -- || ((alg_k & SSL_kPSK) && s->ctx->psk_identity_hint) -+ || (alg_k & SSL_kPSK && s->ctx->psk_identity_hint) -+#ifndef OPENSSL_NO_RSA -+ || (alg_k & SSL_kRSAPSK && s->ctx->psk_identity_hint) -+#endif - #endif - #ifndef OPENSSL_NO_SRP - /* SRP: send ServerKeyExchange */ -@@ -535,11 +538,14 @@ int ssl3_accept(SSL *s) - (s->s3->tmp.new_cipher->algorithm_auth & SSL_aKRB5) || - /* don't request certificate for SRP auth */ - (s->s3->tmp.new_cipher->algorithm_auth & SSL_aSRP) -+#ifndef OPENSSL_NO_PSK - /* -- * With normal PSK Certificates and Certificate Requests -+ * With normal PSK, Certificates and Certificate Requests - * are omitted - */ -- || (s->s3->tmp.new_cipher->algorithm_mkey & SSL_kPSK)) { -+ || (s->s3->tmp.new_cipher->algorithm_mkey & SSL_kPSK) -+#endif -+ ) { - /* no cert request */ - skip = 1; - s->s3->tmp.cert_request = 0; -@@ -1835,7 +1841,11 @@ int ssl3_send_server_key_exchange(SSL *s) - } else - #endif /* !OPENSSL_NO_ECDH */ - #ifndef OPENSSL_NO_PSK -- if (type & SSL_kPSK) { -+ if (type & SSL_kPSK -+#ifndef OPENSSL_NO_RSA -+ || type & SSL_kRSAPSK -+#endif -+ ) { - /* - * reserve size for record length and PSK identity hint - */ -@@ -1884,7 +1894,8 @@ int ssl3_send_server_key_exchange(SSL *s) - } - - if (!(s->s3->tmp.new_cipher->algorithm_auth & (SSL_aNULL | SSL_aSRP)) -- && !(s->s3->tmp.new_cipher->algorithm_mkey & SSL_kPSK)) { -+ && !(s->s3->tmp.new_cipher->algorithm_mkey & SSL_kPSK) -+ && !(s->s3->tmp.new_cipher->algorithm_mkey & SSL_kRSAPSK)) { - if ((pkey = ssl_get_sign_pkey(s, s->s3->tmp.new_cipher, &md)) - == NULL) { - al = SSL_AD_DECODE_ERROR; -@@ -1899,6 +1910,12 @@ int ssl3_send_server_key_exchange(SSL *s) - } else { - pkey = NULL; - kn = 0; -+ /* Allow space for signature algorithm */ -+ if (SSL_USE_SIGALGS(s)) { -+ kn += 4; -+ const unsigned char *sig; -+ kn += tls12_get_psigalgs(s, 1, &sig); -+ } - } - - if (!BUF_MEM_grow_clean(buf, n + SSL_HM_HEADER_LENGTH(s) + kn)) { -@@ -1958,7 +1975,11 @@ int ssl3_send_server_key_exchange(SSL *s) - #endif - - #ifndef OPENSSL_NO_PSK -- if (type & SSL_kPSK) { -+ if (type & SSL_kPSK -+#ifndef OPENSSL_NO_RSA -+ || type & SSL_kRSAPSK -+#endif -+ ) { - /* copy PSK identity hint */ - s2n(strlen(s->ctx->psk_identity_hint), p); - strncpy((char *)p, s->ctx->psk_identity_hint, -@@ -1974,7 +1995,11 @@ int ssl3_send_server_key_exchange(SSL *s) - * points to the space at the end. - */ - #ifndef OPENSSL_NO_RSA -- if (pkey->type == EVP_PKEY_RSA && !SSL_USE_SIGALGS(s)) { -+ if (pkey->type == EVP_PKEY_RSA && !SSL_USE_SIGALGS(s) -+#ifndef OPENSSL_NO_PSK -+ && !(type & SSL_kRSAPSK) -+#endif -+ ) { - q = md_buf; - j = 0; - for (num = 2; num > 0; num--) { -@@ -2870,6 +2895,181 @@ int ssl3_get_client_key_exchange(SSL *s) - goto f_err; - } else - #endif -+#ifndef OPENSSL_NO_RSA -+#ifndef OPENSSL_NO_PSK -+ if (alg_k & SSL_kRSAPSK) { -+ unsigned char rand_premaster_secret[SSL_MAX_MASTER_KEY_LENGTH]; -+ int decrypt_len; -+ unsigned char decrypt_good, version_good; -+ unsigned char *orig_p = p; -+ -+ unsigned int psk_len; -+ -+ const unsigned int pre_master_secret_prefix = 48; -+ unsigned char psk_or_pre_ms[PSK_MAX_PSK_LEN * 2 + 4]; -+ unsigned int pre_ms_len; -+ unsigned char *t = psk_or_pre_ms; -+ -+ char identity[PSK_MAX_IDENTITY_LEN + 1]; -+ int identity_len; -+ -+ int epms_len; -+ int psk_err = 1; -+ -+ /* No server callback? Bail out */ -+ if (s->psk_server_callback == NULL) { -+ SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE, SSL_R_PSK_NO_SERVER_CB); -+ goto f_err; -+ } -+ -+ /* FIX THIS UP EAY EAY EAY EAY */ -+ if (s->s3->tmp.use_rsa_tmp) { -+ if ((s->cert != NULL) && (s->cert->rsa_tmp != NULL)) -+ rsa=s->cert->rsa_tmp; -+ /* -+ * Don't do a callback because rsa_tmp should be sent already -+ */ -+ if (rsa == NULL) { -+ al=SSL_AD_HANDSHAKE_FAILURE; -+ SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE, -+ SSL_R_MISSING_TMP_RSA_PKEY); -+ goto f_err; -+ } -+ } else { -+ pkey=s->cert->pkeys[SSL_PKEY_RSA_ENC].privatekey; -+ if ((pkey == NULL) || -+ (pkey->type != EVP_PKEY_RSA) || (pkey->pkey.rsa == NULL)) { -+ al=SSL_AD_HANDSHAKE_FAILURE; -+ SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE, -+ SSL_R_MISSING_RSA_CERTIFICATE); -+ goto f_err; -+ } -+ rsa = pkey->pkey.rsa; -+ } -+ -+ /* Extract the PSK identity */ -+ if (n < (2 + 2)) { /* 2 bytes for the identity len, 2 bytes for the epms len */ -+ SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE, -+ SSL_R_LENGTH_MISMATCH); -+ goto f_err; -+ } -+ -+ n2s(p, identity_len); -+ -+ if (identity_len > PSK_MAX_IDENTITY_LEN) { -+ SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE, -+ SSL_R_DATA_LENGTH_TOO_LONG); -+ goto f_err; -+ } -+ -+ if (n < (2 + identity_len + 2)) { /* as above, plus the identity len */ -+ SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE, -+ SSL_R_LENGTH_MISMATCH); -+ goto f_err; -+ } -+ -+ memset(identity, 0, sizeof(identity)); -+ memcpy(identity, p, identity_len); -+ p += identity_len; -+ -+ /* fill the pre master secret with random bytes */ -+ if (RAND_pseudo_bytes(psk_or_pre_ms, sizeof(psk_or_pre_ms)) <= 0) -+ goto err; -+ -+ /* read the psk (into the beginning of the psk_or_pre_ms buffer */ -+ psk_len = s->psk_server_callback(s, identity, psk_or_pre_ms, sizeof(psk_or_pre_ms)); -+ -+ if (psk_len > PSK_MAX_PSK_LEN) { -+ SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE, -+ ERR_R_INTERNAL_ERROR); -+ goto rsapsk_err; -+ } else if (psk_len == 0) { -+ SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE, -+ SSL_R_PSK_IDENTITY_NOT_FOUND); -+ al=SSL_AD_UNKNOWN_PSK_IDENTITY; -+ goto rsapsk_err; -+ } -+ -+ /* move on onto decoding the 48 encrypted bytes */ -+ -+ /* how many bytes to decode? */ -+ n2s(p, epms_len); -+ -+ if (n != (2 + identity_len + 2 + epms_len)) { /* as above */ -+ SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE, -+ SSL_R_LENGTH_MISMATCH); -+ goto rsapsk_err; -+ } -+ -+ /* decode in place into p */ -+ decrypt_len = RSA_private_decrypt(epms_len, p, p, rsa, RSA_PKCS1_PADDING); -+ decrypt_good = constant_time_eq_int_8(decrypt_len, 48); -+ -+ /* check the version sent by the client */ -+ version_good = constant_time_eq_8(p[0], (unsigned)(s->client_version>>8)); -+ version_good &= constant_time_eq_8(p[1], (unsigned)(s->client_version&0xff)); -+ -+ decrypt_good &= version_good; -+ -+ for (i = 0; i < (int) sizeof(rand_premaster_secret); i++) -+ p[i] = constant_time_select_8(decrypt_good, p[i], rand_premaster_secret[i]); -+ -+ /* -+ * build the pre master secret. it should look like this: -+ * 48 (2b) + version (2b) + random (46b) + psk_len (2b) + psk -+ */ -+ pre_ms_len = 2 + 2 + 46 + 2 + psk_len; -+ -+ /* the PSK is at the beginning of psk_or_pre_ms, move at the end */ -+ memmove(psk_or_pre_ms + 52, psk_or_pre_ms, psk_len); -+ -+ /* fill the "48" in */ -+ s2n(pre_master_secret_prefix, t); -+ -+ /* fill the 2 bytes version + the 46 random bytes (decrypted earlier with RSA) */ -+ memcpy(t, p, 48); -+ t += 48; -+ -+ /* fill the psk_len */ -+ s2n(psk_len, t); -+ -+ /* psk_or_pre_ms now contains the pre master secret */ -+ -+ /* set the identity in the session */ -+ if (s->session->psk_identity != NULL) -+ OPENSSL_free(s->session->psk_identity); -+ -+ s->session->psk_identity = BUF_strdup(identity); -+ OPENSSL_cleanse(identity, sizeof(identity)); -+ -+ if (s->session->psk_identity == NULL) { -+ SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE, ERR_R_MALLOC_FAILURE); -+ goto rsapsk_err; -+ } -+ -+ /* set the identity hint in the session */ -+ if (s->session->psk_identity_hint != NULL) -+ OPENSSL_free(s->session->psk_identity_hint); -+ s->session->psk_identity_hint = BUF_strdup(s->ctx->psk_identity_hint); -+ if (s->ctx->psk_identity_hint != NULL && s->session->psk_identity_hint == NULL) { -+ SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE, ERR_R_MALLOC_FAILURE); -+ goto rsapsk_err; -+ } -+ -+ /* set the premaster key */ -+ s->session->master_key_length = -+ s->method->ssl3_enc->generate_master_secret(s, -+ s->session->master_key, -+ psk_or_pre_ms, pre_ms_len); -+ -+ psk_err = 0; -+ rsapsk_err: -+ OPENSSL_cleanse(orig_p, n); /* clear the whole payload area */ -+ if (psk_err != 0) -+ goto f_err; -+ } else -+#endif -+#endif - #ifndef OPENSSL_NO_SRP - if (alg_k & SSL_kSRP) { - int param_len; -diff --git x/ssl/ssl.h y/ssl/ssl.h -index 90aeb0ce4e..78cf2212ed 100644 ---- x/ssl/ssl.h -+++ y/ssl/ssl.h -@@ -254,6 +254,7 @@ extern "C" { - # define SSL_TXT_kEECDH "kEECDH" - # define SSL_TXT_kECDHE "kECDHE"/* alias for kEECDH */ - # define SSL_TXT_kPSK "kPSK" -+# define SSL_TXT_kRSAPSK "kRSAPSK" - # define SSL_TXT_kGOST "kGOST" - # define SSL_TXT_kSRP "kSRP" - -@@ -282,6 +283,7 @@ extern "C" { - # define SSL_TXT_ECDSA "ECDSA" - # define SSL_TXT_KRB5 "KRB5" - # define SSL_TXT_PSK "PSK" -+# define SSL_TXT_RSAPSK "RSAPSK" - # define SSL_TXT_SRP "SRP" - - # define SSL_TXT_DES "DES" -diff --git x/ssl/ssl_ciph.c y/ssl/ssl_ciph.c -index ccdf00fa1b..19c4ac0656 100644 ---- x/ssl/ssl_ciph.c -+++ y/ssl/ssl_ciph.c -@@ -263,6 +263,7 @@ static const SSL_CIPHER cipher_aliases[] = { - 0, 0, 0}, - - {0, SSL_TXT_kPSK, 0, SSL_kPSK, 0, 0, 0, 0, 0, 0, 0, 0}, -+ {0, SSL_TXT_kRSAPSK, 0, SSL_kRSAPSK, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, SSL_TXT_kSRP, 0, SSL_kSRP, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, SSL_TXT_kGOST, 0, SSL_kGOST, 0, 0, 0, 0, 0, 0, 0, 0}, - -@@ -294,6 +295,7 @@ static const SSL_CIPHER cipher_aliases[] = { - {0, SSL_TXT_ADH, 0, SSL_kEDH, SSL_aNULL, 0, 0, 0, 0, 0, 0, 0}, - {0, SSL_TXT_AECDH, 0, SSL_kEECDH, SSL_aNULL, 0, 0, 0, 0, 0, 0, 0}, - {0, SSL_TXT_PSK, 0, SSL_kPSK, SSL_aPSK, 0, 0, 0, 0, 0, 0, 0}, -+ {0, SSL_TXT_RSAPSK, 0, SSL_kRSAPSK, SSL_aRSA, 0, 0, 0, 0, 0, 0, 0}, - {0, SSL_TXT_SRP, 0, SSL_kSRP, 0, 0, 0, 0, 0, 0, 0, 0}, - - /* symmetric encryption aliases */ -@@ -756,7 +758,7 @@ static void ssl_cipher_get_disabled(unsigned long *mkey, unsigned long *auth, - *auth |= SSL_aECDH; - #endif - #ifdef OPENSSL_NO_PSK -- *mkey |= SSL_kPSK; -+ *mkey |= SSL_kPSK | SSL_kRSAPSK; - *auth |= SSL_aPSK; - #endif - #ifdef OPENSSL_NO_SRP -@@ -1555,6 +1557,8 @@ STACK_OF(SSL_CIPHER) *ssl_create_cipher_list(const SSL_METHOD *ssl_method, STACK - */ - ssl_cipher_apply_rule(0, SSL_kRSA, 0, 0, 0, 0, 0, CIPHER_ORD, -1, &head, - &tail); -+ ssl_cipher_apply_rule(0, SSL_kRSAPSK, 0, 0, 0, 0, 0, CIPHER_ORD, -1, &head, -+ &tail); - ssl_cipher_apply_rule(0, SSL_kPSK, 0, 0, 0, 0, 0, CIPHER_ORD, -1, &head, - &tail); - ssl_cipher_apply_rule(0, SSL_kKRB5, 0, 0, 0, 0, 0, CIPHER_ORD, -1, &head, -@@ -1731,6 +1735,9 @@ char *SSL_CIPHER_description(const SSL_CIPHER *cipher, char *buf, int len) - case SSL_kPSK: - kx = "PSK"; - break; -+ case SSL_kRSAPSK: -+ kx = "RSAPSK"; -+ break; - case SSL_kSRP: - kx = "SRP"; - break; -diff --git x/ssl/ssl_lib.c y/ssl/ssl_lib.c -index 3539f4b8d2..df6a45bdc4 100644 ---- x/ssl/ssl_lib.c -+++ y/ssl/ssl_lib.c -@@ -2442,8 +2442,14 @@ void ssl_set_cert_masks(CERT *c, const SSL_CIPHER *cipher) - - #ifndef OPENSSL_NO_PSK - mask_k |= SSL_kPSK; -+#ifndef OPENSSL_NO_RSA -+ mask_k |= SSL_kRSAPSK; -+#endif - mask_a |= SSL_aPSK; - emask_k |= SSL_kPSK; -+#ifndef OPENSSL_NO_RSA -+ emask_k |= SSL_kRSAPSK; -+#endif - emask_a |= SSL_aPSK; - #endif - -diff --git x/ssl/ssl_locl.h y/ssl/ssl_locl.h -index aeffc00634..25b9f1d5b1 100644 ---- x/ssl/ssl_locl.h -+++ y/ssl/ssl_locl.h -@@ -314,6 +314,8 @@ - # define SSL_kGOST 0x00000200L - /* SRP */ - # define SSL_kSRP 0x00000400L -+/* RSA PSK */ -+# define SSL_kRSAPSK 0x00000800L - - /* Bits for algorithm_auth (server authentication) */ - /* RSA auth */ -diff --git x/ssl/tls1.h y/ssl/tls1.h -index dd1d8c109e..e04e7ddabc 100644 ---- x/ssl/tls1.h -+++ y/ssl/tls1.h -@@ -410,6 +410,24 @@ SSL_CTX_callback_ctrl(ssl,SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB,(void (*)(void))cb) - # define TLS1_CK_PSK_WITH_AES_128_CBC_SHA 0x0300008C - # define TLS1_CK_PSK_WITH_AES_256_CBC_SHA 0x0300008D - -+/* PSK ciphersuites from 5487 */ -+# define TLS1_CK_PSK_WITH_AES_128_GCM_SHA256 0x030000A8 -+# define TLS1_CK_PSK_WITH_AES_256_GCM_SHA384 0x030000A9 -+# define TLS1_CK_PSK_WITH_AES_128_CBC_SHA256 0x030000AE -+# define TLS1_CK_PSK_WITH_AES_256_CBC_SHA384 0x030000AF -+ -+/* RSA-PSK ciphersuites from 4279 */ -+# define TLS1_CK_RSA_PSK_WITH_RC4_128_SHA 0x03000092 -+# define TLS1_CK_RSA_PSK_WITH_3DES_EDE_CBC_SHA 0x03000093 -+# define TLS1_CK_RSA_PSK_WITH_AES_128_CBC_SHA 0x03000094 -+# define TLS1_CK_RSA_PSK_WITH_AES_256_CBC_SHA 0x03000095 -+ -+/* RSA-PSK ciphersuites from 5487 */ -+# define TLS1_CK_RSA_PSK_WITH_AES_128_GCM_SHA256 0x030000AC -+# define TLS1_CK_RSA_PSK_WITH_AES_256_GCM_SHA384 0x030000AD -+# define TLS1_CK_RSA_PSK_WITH_AES_128_CBC_SHA256 0x030000B6 -+# define TLS1_CK_RSA_PSK_WITH_AES_256_CBC_SHA384 0x030000B7 -+ - /* - * Additional TLS ciphersuites from expired Internet Draft - * draft-ietf-tls-56-bit-ciphersuites-01.txt (available if -@@ -629,6 +647,24 @@ SSL_CTX_callback_ctrl(ssl,SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB,(void (*)(void))cb) - # define TLS1_TXT_PSK_WITH_AES_128_CBC_SHA "PSK-AES128-CBC-SHA" - # define TLS1_TXT_PSK_WITH_AES_256_CBC_SHA "PSK-AES256-CBC-SHA" - -+/* PSK ciphersuites from RFC 5487 */ -+# define TLS1_TXT_PSK_WITH_AES_128_GCM_SHA256 "PSK-AES128-GCM-SHA256" -+# define TLS1_TXT_PSK_WITH_AES_256_GCM_SHA384 "PSK-AES256-GCM-SHA384" -+# define TLS1_TXT_PSK_WITH_AES_128_CBC_SHA256 "PSK-AES128-CBC-SHA256" -+# define TLS1_TXT_PSK_WITH_AES_256_CBC_SHA384 "PSK-AES256-CBC-SHA384" -+ -+/* RSA-PSK ciphersuites from RFC 4279 */ -+# define TLS1_TXT_RSA_PSK_WITH_RC4_128_SHA "RSA-PSK-RC4-SHA" -+# define TLS1_TXT_RSA_PSK_WITH_3DES_EDE_CBC_SHA "RSA-PSK-3DES-EDE-CBC-SHA" -+# define TLS1_TXT_RSA_PSK_WITH_AES_128_CBC_SHA "RSA-PSK-AES128-CBC-SHA" -+# define TLS1_TXT_RSA_PSK_WITH_AES_256_CBC_SHA "RSA-PSK-AES256-CBC-SHA" -+ -+/* RSA-PSK ciphersuites from RFC 5487 */ -+# define TLS1_TXT_RSA_PSK_WITH_AES_128_GCM_SHA256 "RSA-PSK-AES128-GCM-SHA256" -+# define TLS1_TXT_RSA_PSK_WITH_AES_256_GCM_SHA384 "RSA-PSK-AES256-GCM-SHA384" -+# define TLS1_TXT_RSA_PSK_WITH_AES_128_CBC_SHA256 "RSA-PSK-AES128-CBC-SHA256" -+# define TLS1_TXT_RSA_PSK_WITH_AES_256_CBC_SHA384 "RSA-PSK-AES256-CBC-SHA384" -+ - /* SRP ciphersuite from RFC 5054 */ - # define TLS1_TXT_SRP_SHA_WITH_3DES_EDE_CBC_SHA "SRP-3DES-EDE-CBC-SHA" - # define TLS1_TXT_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA "SRP-RSA-3DES-EDE-CBC-SHA" --- -2.15.0 - diff --git a/patches/qt-Add-IsoDep-to-the-techList-on-Android.patch b/patches/qt-Add-IsoDep-to-the-techList-on-Android.patch index 04b5938..4446aa2 100644 --- a/patches/qt-Add-IsoDep-to-the-techList-on-Android.patch +++ b/patches/qt-Add-IsoDep-to-the-techList-on-Android.patch @@ -1,4 +1,4 @@ -From e06d2d0d163501fdb0926175d7c539c7bb413d70 Mon Sep 17 00:00:00 2001 +From 1d9b03157657e398a99f3f183bb7399b8905eb55 Mon Sep 17 00:00:00 2001 From: Lars Schmertmann Date: Wed, 22 Nov 2017 07:35:56 +0100 Subject: Add IsoDep to the techList on Android @@ -21,5 +21,5 @@ index 345b87d3..a1ae5c37 100644 {"android.nfc.tech.NdefFormatable"} }; -- -2.14.2 +2.18.0 diff --git a/patches/qt-Add-work-around-for-freebsd-build.patch b/patches/qt-Add-work-around-for-freebsd-build.patch new file mode 100644 index 0000000..80c02d2 --- /dev/null +++ b/patches/qt-Add-work-around-for-freebsd-build.patch @@ -0,0 +1,27 @@ +From 9e482ce286ad39677e64392e0ca18afc4cf5396c Mon Sep 17 00:00:00 2001 +From: Lars Schmertmann +Date: Fri, 13 Apr 2018 08:22:18 +0200 +Subject: [PATCH] Add work-around for freebsd build + +Change-Id: I14e66e072f9667479815693e3dbbac71385797e7 +Task-number: QTBUG-65425 +--- + qmake/Makefile.unix | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git x/qtbase/qmake/Makefile.unix y/qtbase/qmake/Makefile.unix +index 426387f0c2..b785127ed2 100644 +--- x/qtbase/qmake/Makefile.unix ++++ y/qtbase/qmake/Makefile.unix +@@ -269,7 +269,7 @@ qlibraryinfo.o: $(SOURCE_PATH)/src/corelib/global/qlibraryinfo.cpp + $(CXX) -c -o $@ $(CXXFLAGS) -DQT_BUILD_QMAKE_BOOTSTRAP $< + + qlibraryinfo_final.o: $(SOURCE_PATH)/src/corelib/global/qlibraryinfo.cpp $(BUILD_PATH)/src/corelib/global/qconfig.cpp +- $(CXX) -c -o $@ $(CXXFLAGS) $< ++ $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/global/qlibraryinfo.cpp + + qnumeric.o: $(SOURCE_PATH)/src/corelib/global/qnumeric.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $< +-- +2.17.0 + diff --git a/patches/qt-Android-Fix-crash.patch b/patches/qt-Android-Fix-crash.patch new file mode 100644 index 0000000..e9b4415 --- /dev/null +++ b/patches/qt-Android-Fix-crash.patch @@ -0,0 +1,30 @@ +From ca8779363fd30a1b8fd80ce4ebacc4741b041c76 Mon Sep 17 00:00:00 2001 +From: BogDan Vatra +Date: Tue, 20 Mar 2018 10:36:43 +0200 +Subject: [PATCH] Android: Fix crash + +Android doesn't like nor use RTLD_NODELETE + +Tasnk-number: QTBUG-64654 +Change-Id: I2d884bbf22a681cca592942eba84ba97327ba974 +Reviewed-by: Eskil Abrahamsen Blomfeldt +--- + src/corelib/plugin/qlibrary_unix.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git x/qtbase/src/corelib/plugin/qlibrary_unix.cpp y/qtbase/src/corelib/plugin/qlibrary_unix.cpp +index 23b9ad6434..296af9da7a 100644 +--- x/qtbase/src/corelib/plugin/qlibrary_unix.cpp ++++ y/qtbase/src/corelib/plugin/qlibrary_unix.cpp +@@ -155,7 +155,7 @@ bool QLibraryPrivate::load_sys() + // Do not unload the library during dlclose(). Consequently, the + // library's specific static variables are not reinitialized if the + // library is reloaded with dlopen() at a later time. +-#ifdef RTLD_NODELETE ++#if defined(RTLD_NODELETE) && !defined(Q_OS_ANDROID) + if (loadHints & QLibrary::PreventUnloadHint) { + dlFlags |= RTLD_NODELETE; + } +-- +2.19.1 + diff --git a/patches/qt-Android-fix-compile-with-NDK-r18.patch b/patches/qt-Android-fix-compile-with-NDK-r18.patch new file mode 100644 index 0000000..4fcd09e --- /dev/null +++ b/patches/qt-Android-fix-compile-with-NDK-r18.patch @@ -0,0 +1,37 @@ +From 48789f354ffe99aa28c08f55240e0b0a4deaa377 Mon Sep 17 00:00:00 2001 +From: BogDan Vatra +Date: Mon, 1 Oct 2018 15:46:46 +0300 +Subject: [PATCH] Android: fix compile with NDK r18+ + +In NDK r18, libc++.so was renamed to libc++.so.XX where XX is the Android +API level. + +[ChangeLog][Android] Fixed build issue with NDK r18+. + +Task-number: QTBUG-70631 +Task-number: QTBUG-70779 +Change-Id: Id0d2955648197e3054e3c69263b5a90d57964f6c +--- + mkspecs/android-clang/qmake.conf | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git x/qtbase/mkspecs/android-clang/qmake.conf y/qtbase/mkspecs/android-clang/qmake.conf +index b665000d00..1f5e690329 100644 +--- x/qtbase/mkspecs/android-clang/qmake.conf ++++ y/qtbase/mkspecs/android-clang/qmake.conf +@@ -40,7 +40,11 @@ QMAKE_CFLAGS += -DANDROID_HAS_WSTRING --sysroot=$$NDK_ROOT/sysroot \ + ANDROID_SOURCES_CXX_STL_LIBDIR = $$NDK_ROOT/sources/cxx-stl/llvm-libc++/libs/$$ANDROID_TARGET_ARCH + + ANDROID_STDCPP_PATH = $$ANDROID_SOURCES_CXX_STL_LIBDIR/libc++_shared.so +-ANDROID_CXX_STL_LIBS = -lc++ ++ ++exists($$ANDROID_SOURCES_CXX_STL_LIBDIR/libc++.so): \ ++ ANDROID_CXX_STL_LIBS = -lc++ ++else: \ ++ ANDROID_CXX_STL_LIBS = $$ANDROID_SOURCES_CXX_STL_LIBDIR/libc++.so.$$replace(ANDROID_PLATFORM, "android-", "") + + QMAKE_CFLAGS_OPTIMIZE_SIZE = -Oz + +-- +2.19.0 + diff --git a/patches/qt-Avoid-using-deprecated-APIs-on-iOS-10.0.patch b/patches/qt-Avoid-using-deprecated-APIs-on-iOS-10.0.patch deleted file mode 100644 index 5a5d405..0000000 --- a/patches/qt-Avoid-using-deprecated-APIs-on-iOS-10.0.patch +++ /dev/null @@ -1,73 +0,0 @@ -From 26383dba15ceed74b36dd71e5b1837c63aade927 Mon Sep 17 00:00:00 2001 -From: Lars Schmertmann -Date: Thu, 14 Sep 2017 12:47:11 +0200 -Subject: Avoid using deprecated APIs on iOS 10.0+ - -Change-Id: Ic9dc6a24ef793a29c2652ad37bc11120e2e6ceef ---- - src/gui/util/qdesktopservices.cpp | 13 +++++++++++++ - src/plugins/platforms/ios/qiosservices.mm | 14 ++++++++++++-- - 2 files changed, 25 insertions(+), 2 deletions(-) - -diff --git x/qtbase/src/gui/util/qdesktopservices.cpp y/qtbase/src/gui/util/qdesktopservices.cpp -index c9747877f7..77ccc02aa5 100644 ---- x/qtbase/src/gui/util/qdesktopservices.cpp -+++ y/qtbase/src/gui/util/qdesktopservices.cpp -@@ -177,6 +177,19 @@ void QOpenUrlHandlerRegistry::handlerDestroyed(QObject *handler) - still fail to launch or fail to open the requested URL. This result will not be reported back - to the application. - -+ \warning URLs passed to this function on iOS will not load unless their schemes are -+ listed in the \c LSApplicationQueriesSchemes key of the application's Info.plist file. -+ For more information, see the Apple Developer Documentation for -+ \l{https://developer.apple.com/documentation/uikit/uiapplication/1622952-canopenurl}{canOpenURL(_:)}. -+ For example, the following lines enable URLs with the HTTPS scheme: -+ -+ \code -+ LSApplicationQueriesSchemes -+ -+ https -+ -+ \endcode -+ - \sa setUrlHandler() - */ - bool QDesktopServices::openUrl(const QUrl &url) -diff --git x/qtbase/src/plugins/platforms/ios/qiosservices.mm y/qtbase/src/plugins/platforms/ios/qiosservices.mm -index 0ecc8e123f..a963a5c05d 100644 ---- x/qtbase/src/plugins/platforms/ios/qiosservices.mm -+++ y/qtbase/src/plugins/platforms/ios/qiosservices.mm -@@ -41,6 +41,7 @@ - - #include - #include -+#include - - #import - -@@ -55,11 +56,20 @@ bool QIOSServices::openUrl(const QUrl &url) - return openDocument(url); - - NSURL *nsUrl = url.toNSURL(); -+ UIApplication *application = [UIApplication sharedApplication]; - -- if (![[UIApplication sharedApplication] canOpenURL:nsUrl]) -+ if (![application canOpenURL:nsUrl]) - return false; - -- return [[UIApplication sharedApplication] openURL:nsUrl]; -+#if QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_NA, 100000, 100000, __WATCHOS_NA) -+ if (QOperatingSystemVersion::current() >= QOperatingSystemVersion(QOperatingSystemVersion::IOS, 10)) { -+ [application openURL:nsUrl options:@{} completionHandler:nil]; -+ return true; -+ } else -+#endif -+ { -+ return [application openURL:nsUrl]; -+ } - } - - bool QIOSServices::openDocument(const QUrl &url) --- -2.14.1 - diff --git a/patches/qt-Change-build-configuration-for-Qt-on-iOS.patch b/patches/qt-Change-build-configuration-for-Qt-on-iOS.patch deleted file mode 100644 index 18a0870..0000000 --- a/patches/qt-Change-build-configuration-for-Qt-on-iOS.patch +++ /dev/null @@ -1,32 +0,0 @@ -From 1f505127d1dba4b755fc00360a5bffff8163acb7 Mon Sep 17 00:00:00 2001 -From: Lars Schmertmann -Date: Wed, 19 Jul 2017 09:44:01 +0200 -Subject: Change build configuration for Qt on iOS ---- - mkspecs/macx-ios-clang/qmake.conf | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git x/qtbase/mkspecs/macx-ios-clang/qmake.conf y/qtbase/mkspecs/macx-ios-clang/qmake.conf -index 825e03aa85..fe783faee3 100644 ---- x/qtbase/mkspecs/macx-ios-clang/qmake.conf -+++ y/qtbase/mkspecs/macx-ios-clang/qmake.conf -@@ -2,13 +2,13 @@ - # qmake configuration for macx-ios-clang - # - --QMAKE_IOS_DEPLOYMENT_TARGET = 8.0 -+QMAKE_IOS_DEPLOYMENT_TARGET = 10.0 - - # Universal target (iPhone and iPad) - QMAKE_APPLE_TARGETED_DEVICE_FAMILY = 1,2 - --QMAKE_APPLE_DEVICE_ARCHS = armv7 arm64 --QMAKE_APPLE_SIMULATOR_ARCHS = i386 x86_64 -+QMAKE_APPLE_DEVICE_ARCHS = arm64 -+QMAKE_APPLE_SIMULATOR_ARCHS = x86_64 - - include(../common/ios.conf) - include(../common/gcc-base-mac.conf) --- -2.13.2 - diff --git a/patches/qt-Disable-unused-imageformats.patch b/patches/qt-Disable-unused-imageformats.patch index a1f3496..4b88c99 100644 --- a/patches/qt-Disable-unused-imageformats.patch +++ b/patches/qt-Disable-unused-imageformats.patch @@ -1,4 +1,4 @@ -From 978caa044d4e1c52c90a87490defbac387db58d6 Mon Sep 17 00:00:00 2001 +From e1c05843ae1609075807d5a789fc4e6cd8154520 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Klitzing?= Date: Mon, 25 Sep 2017 14:10:56 +0200 Subject: [PATCH] Disable unused imageformats @@ -8,10 +8,10 @@ Subject: [PATCH] Disable unused imageformats 1 file changed, 1 insertion(+), 2 deletions(-) diff --git x/qtimageformats/src/plugins/imageformats/imageformats.pro y/qtimageformats/src/plugins/imageformats/imageformats.pro -index 8c79379..2aa80f5 100644 +index d6c59ee..f1fb6d0 100644 --- x/qtimageformats/src/plugins/imageformats/imageformats.pro +++ y/qtimageformats/src/plugins/imageformats/imageformats.pro -@@ -16,8 +16,7 @@ config_jasper { +@@ -18,8 +18,7 @@ config_jasper { SUBDIRS += macjp2 } @@ -22,5 +22,5 @@ index 8c79379..2aa80f5 100644 webp -} -- -2.14.1 +2.16.2 diff --git a/patches/qt-Enable-debug-output-for-OpenSSL.patch b/patches/qt-Enable-debug-output-for-OpenSSL.patch index fcec7bb..ade57b8 100644 --- a/patches/qt-Enable-debug-output-for-OpenSSL.patch +++ b/patches/qt-Enable-debug-output-for-OpenSSL.patch @@ -1,16 +1,17 @@ -From 5cad7717db911f113355b353a6eeb3687f6fbf9a Mon Sep 17 00:00:00 2001 +From fc591411928c982f763c2fee060c0665a5b6b8b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Klitzing?= -Date: Thu, 23 Jul 2015 12:16:01 +0200 +Date: Tue, 10 Oct 2017 13:44:48 +0200 Subject: [PATCH] Enable debug output for OpenSSL --- - src/network/ssl/qsslsocket.cpp | 2 +- - src/network/ssl/qsslsocket_mac.cpp | 1 + - src/network/ssl/qsslsocket_openssl.cpp | 4 ++-- - 3 files changed, 4 insertions(+), 3 deletions(-) + src/network/ssl/qsslsocket.cpp | 2 +- + src/network/ssl/qsslsocket_mac.cpp | 1 + + src/network/ssl/qsslsocket_mac_shared.cpp | 4 ++-- + src/network/ssl/qsslsocket_openssl.cpp | 2 +- + 4 files changed, 5 insertions(+), 4 deletions(-) diff --git x/qtbase/src/network/ssl/qsslsocket.cpp y/qtbase/src/network/ssl/qsslsocket.cpp -index 3e7a30a..61ff7a1 100644 +index 5c9ebac283..b843191e89 100644 --- x/qtbase/src/network/ssl/qsslsocket.cpp +++ y/qtbase/src/network/ssl/qsslsocket.cpp @@ -39,7 +39,7 @@ @@ -23,7 +24,7 @@ index 3e7a30a..61ff7a1 100644 /*! \class QSslSocket diff --git x/qtbase/src/network/ssl/qsslsocket_mac.cpp y/qtbase/src/network/ssl/qsslsocket_mac.cpp -index 99ae792..13339c8 100644 +index 68c8ccff89..15d887c1f9 100644 --- x/qtbase/src/network/ssl/qsslsocket_mac.cpp +++ y/qtbase/src/network/ssl/qsslsocket_mac.cpp @@ -37,6 +37,7 @@ @@ -34,11 +35,11 @@ index 99ae792..13339c8 100644 #include "qsslsocket.h" #include "qssl_p.h" -diff --git x/qtbase/src/network/ssl/qsslsocket_openssl.cpp y/qtbase/src/network/ssl/qsslsocket_openssl.cpp -index 4f62f53..f8d8174 100644 ---- x/qtbase/src/network/ssl/qsslsocket_openssl.cpp -+++ y/qtbase/src/network/ssl/qsslsocket_openssl.cpp -@@ -53,8 +53,8 @@ +diff --git x/qtbase/src/network/ssl/qsslsocket_mac_shared.cpp y/qtbase/src/network/ssl/qsslsocket_mac_shared.cpp +index d239fe23dd..59436ca276 100644 +--- x/qtbase/src/network/ssl/qsslsocket_mac_shared.cpp ++++ y/qtbase/src/network/ssl/qsslsocket_mac_shared.cpp +@@ -38,8 +38,8 @@ ** ****************************************************************************/ @@ -47,8 +48,21 @@ index 4f62f53..f8d8174 100644 +#define QSSLSOCKET_DEBUG +#define QT_DECRYPT_SSL_TRAFFIC + #include "qssl_p.h" + #include "qsslsocket.h" +diff --git x/qtbase/src/network/ssl/qsslsocket_openssl.cpp y/qtbase/src/network/ssl/qsslsocket_openssl.cpp +index 2d771b5637..beb361d744 100644 +--- x/qtbase/src/network/ssl/qsslsocket_openssl.cpp ++++ y/qtbase/src/network/ssl/qsslsocket_openssl.cpp +@@ -53,7 +53,7 @@ + ** + ****************************************************************************/ + +-//#define QSSLSOCKET_DEBUG ++#define QSSLSOCKET_DEBUG + #include "qssl_p.h" #include "qsslsocket_openssl_p.h" -- -2.8.0 +2.14.2 diff --git a/patches/qt-Introduce-reportError-to-fix-QMetaObject-invokeMethod.patch b/patches/qt-Introduce-reportError-to-fix-QMetaObject-invokeMethod.patch new file mode 100644 index 0000000..9967f2d --- /dev/null +++ b/patches/qt-Introduce-reportError-to-fix-QMetaObject-invokeMethod.patch @@ -0,0 +1,262 @@ +From 9f00179a95ef729fa7871b4d408c76bc50e4eb4e Mon Sep 17 00:00:00 2001 +From: Lars Schmertmann +Date: Wed, 20 Jun 2018 11:56:19 +0200 +Subject: Introduce reportError to fix "QMetaObject::invokeMethod: No such method" + +Task-number: QTBUG-67958 +Change-Id: Ia5a21cb19f0318844ac436adcc3f0fff9a3185b5 +Reviewed-by: Alex Blasche +--- + src/nfc/qnearfieldtagtype1.cpp | 8 ++---- + src/nfc/qnearfieldtarget.cpp | 13 ++++++++++ + src/nfc/qnearfieldtarget.h | 2 ++ + src/nfc/qnearfieldtarget_android.cpp | 36 +++++++-------------------- + src/nfc/qnearfieldtarget_emulator.cpp | 20 ++++----------- + src/nfc/qnearfieldtarget_neard_p.h | 8 ++---- + 6 files changed, 33 insertions(+), 54 deletions(-) + +diff --git x/qtconnectivity/src/nfc/qnearfieldtagtype1.cpp y/qtconnectivity/src/nfc/qnearfieldtagtype1.cpp +index 34f2c8b8..7f27fbe9 100644 +--- x/qtconnectivity/src/nfc/qnearfieldtagtype1.cpp ++++ y/qtconnectivity/src/nfc/qnearfieldtagtype1.cpp +@@ -440,9 +440,7 @@ QNearFieldTarget::RequestId QNearFieldTagType1::readNdefMessages() + if (d->m_readNdefMessageState == QNearFieldTagType1Private::NotReadingNdefMessage) { + d->progressToNextNdefReadMessageState(); + } else { +- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, +- Q_ARG(QNearFieldTarget::Error, NdefReadError), +- Q_ARG(QNearFieldTarget::RequestId, d->m_readNdefRequestId)); ++ reportError(QNearFieldTarget::NdefReadError, d->m_readNdefRequestId); + } + + return d->m_readNdefRequestId; +@@ -462,9 +460,7 @@ QNearFieldTarget::RequestId QNearFieldTagType1::writeNdefMessages(const QListm_ndefWriteMessages = messages; + d->progressToNextNdefWriteMessageState(); + } else { +- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, +- Q_ARG(QNearFieldTarget::Error, NdefWriteError), +- Q_ARG(QNearFieldTarget::RequestId, d->m_readNdefRequestId)); ++ reportError(QNearFieldTarget::NdefWriteError, d->m_readNdefRequestId); + } + + return d->m_writeNdefRequestId; +diff --git x/qtconnectivity/src/nfc/qnearfieldtarget.cpp y/qtconnectivity/src/nfc/qnearfieldtarget.cpp +index e642824b..e9a6fa11 100644 +--- x/qtconnectivity/src/nfc/qnearfieldtarget.cpp ++++ y/qtconnectivity/src/nfc/qnearfieldtarget.cpp +@@ -530,4 +530,17 @@ bool QNearFieldTarget::handleResponse(const QNearFieldTarget::RequestId &id, + return true; + } + ++/*! ++ \since 5.12 ++ ++ Reports the \a error for the request \a id by appending the signal emission to the event queue. ++*/ ++void QNearFieldTarget::reportError(QNearFieldTarget::Error error, ++ const QNearFieldTarget::RequestId &id) ++{ ++ QMetaObject::invokeMethod(this, [this, error, id]() { ++ Q_EMIT this->error(error, id); ++ }, Qt::QueuedConnection); ++} ++ + QT_END_NAMESPACE +diff --git x/qtconnectivity/src/nfc/qnearfieldtarget.h y/qtconnectivity/src/nfc/qnearfieldtarget.h +index e51960f7..868b52d5 100644 +--- x/qtconnectivity/src/nfc/qnearfieldtarget.h ++++ y/qtconnectivity/src/nfc/qnearfieldtarget.h +@@ -153,6 +153,8 @@ protected: + Q_INVOKABLE virtual bool handleResponse(const QNearFieldTarget::RequestId &id, + const QByteArray &response); + ++ void reportError(QNearFieldTarget::Error error, const QNearFieldTarget::RequestId &id); ++ + Q_SIGNALS: + void disconnected(); + +diff --git x/qtconnectivity/src/nfc/qnearfieldtarget_android.cpp y/qtconnectivity/src/nfc/qnearfieldtarget_android.cpp +index e656996e..78da6ac2 100644 +--- x/qtconnectivity/src/nfc/qnearfieldtarget_android.cpp ++++ y/qtconnectivity/src/nfc/qnearfieldtarget_android.cpp +@@ -147,25 +147,19 @@ QNearFieldTarget::RequestId NearFieldTarget::readNdefMessages() + // Making sure that target is still in range + QNearFieldTarget::RequestId requestId(new QNearFieldTarget::RequestIdPrivate); + if (!m_intent.isValid()) { +- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, +- Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::TargetOutOfRangeError), +- Q_ARG(QNearFieldTarget::RequestId&, requestId)); ++ reportError(QNearFieldTarget::TargetOutOfRangeError, requestId); + return requestId; + } + + // Getting Ndef technology object + if (!setTagTechnology({NDEFTECHNOLOGY})) { +- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, +- Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::UnsupportedError), +- Q_ARG(QNearFieldTarget::RequestId&, requestId)); ++ reportError(QNearFieldTarget::UnsupportedError, requestId); + return requestId; + } + + // Connect + if (!connect()) { +- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, +- Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::TargetOutOfRangeError), +- Q_ARG(QNearFieldTarget::RequestId&, requestId)); ++ reportError(QNearFieldTarget::TargetOutOfRangeError, requestId); + return requestId; + } + +@@ -174,9 +168,7 @@ QNearFieldTarget::RequestId NearFieldTarget::readNdefMessages() + if (catchJavaExceptions()) + ndefMessage = QAndroidJniObject(); + if (!ndefMessage.isValid()) { +- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, +- Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::NdefReadError), +- Q_ARG(QNearFieldTarget::RequestId&, requestId)); ++ reportError(QNearFieldTarget::NdefReadError, requestId); + return requestId; + } + +@@ -249,9 +241,7 @@ QNearFieldTarget::RequestId NearFieldTarget::sendCommand(const QByteArray &comma + // Connecting + QNearFieldTarget::RequestId requestId = QNearFieldTarget::RequestId(new QNearFieldTarget::RequestIdPrivate()); + if (!connect()) { +- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, +- Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::TargetOutOfRangeError), +- Q_ARG(QNearFieldTarget::RequestId&, requestId)); ++ reportError(QNearFieldTarget::TargetOutOfRangeError, requestId); + return requestId; + } + +@@ -263,9 +253,7 @@ QNearFieldTarget::RequestId NearFieldTarget::sendCommand(const QByteArray &comma + // Writing + QAndroidJniObject myNewVal = m_tagTech.callObjectMethod("transceive", "([B)[B", jba); + if (catchJavaExceptions()) { +- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, +- Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::CommandError), +- Q_ARG(QNearFieldTarget::RequestId&, requestId)); ++ reportError(QNearFieldTarget::CommandError, requestId); + return requestId; + } + QByteArray result = jbyteArrayToQByteArray(myNewVal.object()); +@@ -315,9 +303,7 @@ QNearFieldTarget::RequestId NearFieldTarget::writeNdefMessages(const QListSetByteArrayRegion(jba.object(), 0, ba.size(), reinterpret_cast(ba.data())); + QAndroidJniObject jmessage = QAndroidJniObject("android/nfc/NdefMessage", "([B)V", jba.object()); + if (catchJavaExceptions()) { +- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, +- Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::UnknownError), +- Q_ARG(QNearFieldTarget::RequestId&, requestId)); ++ reportError(QNearFieldTarget::UnknownError, requestId); + return requestId; + } + + // Writing + m_tagTech.callMethod(writeMethod, "(Landroid/nfc/NdefMessage;)V", jmessage.object()); + if (catchJavaExceptions()) { +- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, +- Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::NdefWriteError), +- Q_ARG(QNearFieldTarget::RequestId&, requestId)); ++ reportError(QNearFieldTarget::NdefWriteError, requestId); + return requestId; + } + +diff --git x/qtconnectivity/src/nfc/qnearfieldtarget_emulator.cpp y/qtconnectivity/src/nfc/qnearfieldtarget_emulator.cpp +index 29b1f74d..030718cc 100644 +--- x/qtconnectivity/src/nfc/qnearfieldtarget_emulator.cpp ++++ y/qtconnectivity/src/nfc/qnearfieldtarget_emulator.cpp +@@ -82,9 +82,7 @@ QNearFieldTarget::RequestId TagType1::sendCommand(const QByteArray &command) + + // tag not in proximity + if (!tagMap.value(m_tag)) { +- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, +- Q_ARG(QNearFieldTarget::Error, TargetOutOfRangeError), +- Q_ARG(QNearFieldTarget::RequestId, id)); ++ reportError(QNearFieldTarget::TargetOutOfRangeError, id); + return id; + } + +@@ -93,17 +91,13 @@ QNearFieldTarget::RequestId TagType1::sendCommand(const QByteArray &command) + QByteArray response = m_tag->processCommand(command + char(crc & 0xff) + char(crc >> 8)); + + if (response.isEmpty()) { +- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, +- Q_ARG(QNearFieldTarget::Error, NoResponseError), +- Q_ARG(QNearFieldTarget::RequestId, id)); ++ reportError(QNearFieldTarget::NoResponseError, id); + return id; + } + + // check crc + if (qChecksum(response.constData(), response.length(), Qt::ChecksumItuV41) != 0) { +- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, +- Q_ARG(QNearFieldTarget::Error, ChecksumMismatchError), +- Q_ARG(QNearFieldTarget::RequestId, id)); ++ reportError(QNearFieldTarget::ChecksumMismatchError, id); + return id; + } + +@@ -152,9 +146,7 @@ QNearFieldTarget::RequestId TagType2::sendCommand(const QByteArray &command) + + // tag not in proximity + if (!tagMap.value(m_tag)) { +- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, +- Q_ARG(QNearFieldTarget::Error, TargetOutOfRangeError), +- Q_ARG(QNearFieldTarget::RequestId, id)); ++ reportError(QNearFieldTarget::TargetOutOfRangeError, id); + return id; + } + +@@ -168,9 +160,7 @@ QNearFieldTarget::RequestId TagType2::sendCommand(const QByteArray &command) + if (response.length() > 1) { + // check crc + if (qChecksum(response.constData(), response.length(), Qt::ChecksumItuV41) != 0) { +- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, +- Q_ARG(QNearFieldTarget::Error, ChecksumMismatchError), +- Q_ARG(QNearFieldTarget::RequestId, id)); ++ reportError(QNearFieldTarget::ChecksumMismatchError, id); + return id; + } + +diff --git x/qtconnectivity/src/nfc/qnearfieldtarget_neard_p.h y/qtconnectivity/src/nfc/qnearfieldtarget_neard_p.h +index 053df141..625cee67 100644 +--- x/qtconnectivity/src/nfc/qnearfieldtarget_neard_p.h ++++ y/qtconnectivity/src/nfc/qnearfieldtarget_neard_p.h +@@ -359,9 +359,7 @@ private: + Q_EMIT this->requestCompleted(this->m_currentReadRequestId); + }, Qt::QueuedConnection); + } else { +- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, +- Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::UnknownError), +- Q_ARG(QNearFieldTarget::RequestId, m_currentReadRequestId)); ++ this->reportError(QNearFieldTarget::UnknownError, m_currentReadRequestId); + } + + m_readRequested = false; +@@ -389,9 +387,7 @@ private: + reply.waitForFinished(); + if (reply.isError()) { + qCWarning(QT_NFC_NEARD) << "Error writing to NFC tag" << reply.error(); +- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, +- Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::UnknownError), +- Q_ARG(QNearFieldTarget::RequestId, m_currentWriteRequestId)); ++ this->reportError(QNearFieldTarget::UnknownError, m_currentWriteRequestId); + } + + QMetaObject::invokeMethod(this, "ndefMessagesWritten", Qt::QueuedConnection); +-- +2.19.1 + diff --git a/patches/qt-Make-server-side-signature-algorithms-configurable.patch b/patches/qt-Make-server-side-signature-algorithms-configurable.patch deleted file mode 100644 index 7da685e..0000000 --- a/patches/qt-Make-server-side-signature-algorithms-configurable.patch +++ /dev/null @@ -1,261 +0,0 @@ -From b0404383ab573d7550a6564405bb9b1316ff193a Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Sebastian=20L=C3=B6sch?= -Date: Thu, 21 Apr 2016 09:19:19 +0200 -Subject: [PATCH] Make server side signature algorithms configurable - -Signature algorithms are used during the TLS handshake phase to protect -transferred security parameters, e.g the message ServerKeyExchange. -This patch enables the configuration of allowed algorithms used by the -server side. - -Change-Id: Ia178efd4778b91863fcc919bf50219115b300d77 ---- - src/network/ssl/qsslconfiguration.cpp | 42 ++++++++++++++++++++++++ - src/network/ssl/qsslconfiguration.h | 8 ++++- - src/network/ssl/qsslconfiguration_p.h | 5 +++ - src/network/ssl/qsslcontext_openssl.cpp | 45 ++++++++++++++++++++++++++ - src/network/ssl/qsslcontext_openssl_p.h | 1 + - src/network/ssl/qsslsocket.cpp | 2 ++ - src/network/ssl/qsslsocket_openssl_symbols_p.h | 5 +++ - 7 files changed, 107 insertions(+), 1 deletion(-) - -diff --git x/qtbase/src/network/ssl/qsslconfiguration.cpp y/qtbase/src/network/ssl/qsslconfiguration.cpp -index 75a880f115..37f99feef1 100644 ---- x/qtbase/src/network/ssl/qsslconfiguration.cpp -+++ y/qtbase/src/network/ssl/qsslconfiguration.cpp -@@ -221,6 +221,7 @@ bool QSslConfiguration::operator==(const QSslConfiguration &other) const - d->peerVerifyMode == other.d->peerVerifyMode && - d->peerVerifyDepth == other.d->peerVerifyDepth && - d->allowRootCertOnDemandLoading == other.d->allowRootCertOnDemandLoading && -+ d->signatureAndHashAlgorithms == other.d->signatureAndHashAlgorithms && - d->sslOptions == other.d->sslOptions && - d->sslSession == other.d->sslSession && - d->sslSessionTicketLifeTimeHint == other.d->sslSessionTicketLifeTimeHint && -@@ -263,6 +264,7 @@ bool QSslConfiguration::isNull() const - d->privateKey.isNull() && - d->peerCertificate.isNull() && - d->peerCertificateChain.count() == 0 && -+ d->signatureAndHashAlgorithms.isEmpty() && - d->sslOptions == QSslConfigurationPrivate::defaultSslOptions && - d->sslSession.isNull() && - d->sslSessionTicketLifeTimeHint == -1 && -@@ -869,6 +871,46 @@ void QSslConfiguration::setDiffieHellmanParameters(const QSslDiffieHellmanParame - d->dhParams = dhparams; - } - -+/*! -+ \since 5.9 -+ -+ Returns the connection's current list of supported signature -+ algorithms if enabled. Enable it by calling -+ setSignatureAndHashAlgorithms(). -+ -+ \sa setSignatureAndHashAlgorithms() -+ */ -+QVector > QSslConfiguration::signatureAndHashAlgorithms() const -+{ -+ return d->signatureAndHashAlgorithms; -+} -+ -+/*! -+ \since 5.9 -+ -+ Sets the list of signature algorithms to be used for the current -+ connection. The algorithms are expected to be ordered by descending -+ preference (i.e., the first algorithm is the most preferred one). -+ Notice that this restricts the list of supported ciphers (e.g. -+ configuring the signature algorithm RSA+SHA1 will restrict the ciphers -+ to RSA ciphers). -+ -+ When configuring the client side this are the algorithms set in the -+ Signature Algorithms TLS extension, see RFC 5246 for details. Although -+ this extension will be ignored for TLS protocol versions prior 1.2 -+ this still restricts the supported ciphers as mentioned above. -+ -+ By default, the handshake phase can choose any of the algorithms -+ supported by this system's SSL libraries, which may vary from -+ system to system. -+ -+ \sa signatureAndHashAlgorithms() -+ */ -+void QSslConfiguration::setSignatureAndHashAlgorithms(const QVector > &algorithms) -+{ -+ d->signatureAndHashAlgorithms = algorithms; -+} -+ - /*! - \since 5.3 - -diff --git x/qtbase/src/network/ssl/qsslconfiguration.h y/qtbase/src/network/ssl/qsslconfiguration.h -index 1c57bebd65..4d3e5129d5 100644 ---- x/qtbase/src/network/ssl/qsslconfiguration.h -+++ y/qtbase/src/network/ssl/qsslconfiguration.h -@@ -56,10 +56,13 @@ - #ifndef QSSLCONFIGURATION_H - #define QSSLCONFIGURATION_H - --#include -+#include -+#include - #include -+#include - #include - #include -+#include - - #ifndef QT_NO_SSL - -@@ -149,6 +152,9 @@ public: - QSslDiffieHellmanParameters diffieHellmanParameters() const; - void setDiffieHellmanParameters(const QSslDiffieHellmanParameters &dhparams); - -+ QVector > signatureAndHashAlgorithms() const; -+ void setSignatureAndHashAlgorithms(const QVector > &algorithms); -+ - static QSslConfiguration defaultConfiguration(); - static void setDefaultConfiguration(const QSslConfiguration &configuration); - -diff --git x/qtbase/src/network/ssl/qsslconfiguration_p.h y/qtbase/src/network/ssl/qsslconfiguration_p.h -index 6adf2c9b54..7be253973b 100644 ---- x/qtbase/src/network/ssl/qsslconfiguration_p.h -+++ y/qtbase/src/network/ssl/qsslconfiguration_p.h -@@ -75,6 +75,9 @@ - #include "qsslkey.h" - #include "qsslellipticcurve.h" - #include "qssldiffiehellmanparameters.h" -+#include -+#include -+#include - - QT_BEGIN_NAMESPACE - -@@ -123,6 +126,8 @@ public: - - QSslDiffieHellmanParameters dhParams; - -+ QVector > signatureAndHashAlgorithms; -+ - QByteArray sslSession; - int sslSessionTicketLifeTimeHint; - -diff --git x/qtbase/src/network/ssl/qsslcontext_openssl.cpp y/qtbase/src/network/ssl/qsslcontext_openssl.cpp -index c92d8fc3f8..29df53abc0 100644 ---- x/qtbase/src/network/ssl/qsslcontext_openssl.cpp -+++ y/qtbase/src/network/ssl/qsslcontext_openssl.cpp -@@ -42,6 +42,7 @@ - - #include - #include -+#include - #include - - #include "private/qssl_p.h" -@@ -78,6 +79,11 @@ QSslContext::~QSslContext() - q_SSL_SESSION_free(session); - } - -+static inline QString msgErrorSettingSignatureAlgorithms(const QString &why) -+{ -+ return QSslSocket::tr("Error when setting the signature algorithms (%1)").arg(why); -+} -+ - static inline QString msgErrorSettingEllipticCurves(const QString &why) - { - return QSslSocket::tr("Error when setting the elliptic curves (%1)").arg(why); -@@ -371,6 +377,45 @@ init_context: - sslContext->errorCode = QSslError::UnspecifiedError; - } - } -+ -+ const auto& sigAndHashAlgorithms = sslContext->sslConfiguration.signatureAndHashAlgorithms(); -+ if (!sigAndHashAlgorithms.isEmpty()) { -+#if OPENSSL_VERSION_NUMBER >= 0x10002000L -+ if (q_SSLeay() >= 0x10002000L) { -+ QMetaEnum hashMetaEnum = QMetaEnum::fromType(); -+ QByteArrayList algorithmList; -+ for (int i=0; i < sigAndHashAlgorithms.size(); ++i) { -+ QByteArray sig; -+ switch (sigAndHashAlgorithms[i].first) { -+ case QSsl::KeyAlgorithm::Rsa: -+ sig = QByteArrayLiteral("RSA"); -+ break; -+ case QSsl::KeyAlgorithm::Dsa: -+ sig = QByteArrayLiteral("DSA"); -+ break; -+ case QSsl::KeyAlgorithm::Ec: -+ sig = QByteArrayLiteral("ECDSA"); -+ break; -+ case QSsl::KeyAlgorithm::Opaque: -+ qCWarning(lcSsl, "Invalid value KeyAlgorithm::Opaque will be ignored"); -+ continue; -+ } -+ QByteArray hash = QByteArray(hashMetaEnum.valueToKey(sigAndHashAlgorithms[i].second)).toUpper(); -+ algorithmList += sig + QByteArrayLiteral("+") + hash; -+ } -+ QByteArray algorithms = algorithmList.join(':'); -+ if (!q_SSL_CTX_set1_sigalgs_list(sslContext->ctx, algorithms.data())) { -+ sslContext->errorStr = msgErrorSettingSignatureAlgorithms(QSslSocketBackendPrivate::getErrorsFromOpenSsl()); -+ sslContext->errorCode = QSslError::UnspecifiedError; -+ } -+ } else -+#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L -+ { -+ // specific algorithms requested, but not possible to set -> error -+ sslContext->errorStr = msgErrorSettingSignatureAlgorithms(QSslSocket::tr("OpenSSL version too old, need at least v1.0.2")); -+ sslContext->errorCode = QSslError::UnspecifiedError; -+ } -+ } - } - - QSslContext* QSslContext::fromConfiguration(QSslSocket::SslMode mode, const QSslConfiguration &configuration, bool allowRootCertOnDemandLoading) -diff --git x/qtbase/src/network/ssl/qsslcontext_openssl_p.h y/qtbase/src/network/ssl/qsslcontext_openssl_p.h -index 06a31af5e5..c8c8e1941b 100644 ---- x/qtbase/src/network/ssl/qsslcontext_openssl_p.h -+++ y/qtbase/src/network/ssl/qsslcontext_openssl_p.h -@@ -54,6 +54,7 @@ - // - - #include -+#include - #include - #include - #include -diff --git x/qtbase/src/network/ssl/qsslsocket.cpp y/qtbase/src/network/ssl/qsslsocket.cpp -index 8eba5db9fe..c0aa8b9bdf 100644 ---- x/qtbase/src/network/ssl/qsslsocket.cpp -+++ y/qtbase/src/network/ssl/qsslsocket.cpp -@@ -922,6 +922,7 @@ void QSslSocket::setSslConfiguration(const QSslConfiguration &configuration) - d->configuration.peerVerifyDepth = configuration.peerVerifyDepth(); - d->configuration.peerVerifyMode = configuration.peerVerifyMode(); - d->configuration.protocol = configuration.protocol(); -+ d->configuration.signatureAndHashAlgorithms = configuration.signatureAndHashAlgorithms(); - d->configuration.sslOptions = configuration.d->sslOptions; - d->configuration.sslSession = configuration.sessionTicket(); - d->configuration.sslSessionTicketLifeTimeHint = configuration.sessionTicketLifeTimeHint(); -@@ -2249,6 +2250,7 @@ void QSslConfigurationPrivate::deepCopyDefaultConfiguration(QSslConfigurationPri - ptr->peerVerifyDepth = global->peerVerifyDepth; - ptr->sslOptions = global->sslOptions; - ptr->ellipticCurves = global->ellipticCurves; -+ ptr->signatureAndHashAlgorithms = global->signatureAndHashAlgorithms; - } - - /*! -diff --git x/qtbase/src/network/ssl/qsslsocket_openssl_symbols_p.h y/qtbase/src/network/ssl/qsslsocket_openssl_symbols_p.h -index b35a895d38..d4cd493c45 100644 ---- x/qtbase/src/network/ssl/qsslsocket_openssl_symbols_p.h -+++ y/qtbase/src/network/ssl/qsslsocket_openssl_symbols_p.h -@@ -517,6 +517,11 @@ int q_EC_curve_nist2nid(const char *name); - #define q_SSL_get_server_tmp_key(ssl, key) q_SSL_ctrl((ssl), SSL_CTRL_GET_SERVER_TMP_KEY, 0, (char *)key) - #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L - -+// Signature algorithm extension -+#if OPENSSL_VERSION_NUMBER >= 0x10002000L -+#define q_SSL_CTX_set1_sigalgs_list(ctx, s) q_SSL_CTX_ctrl((ctx), SSL_CTRL_SET_SIGALGS_LIST, 0, (char *)s) -+#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L -+ - // PKCS#12 support - int q_PKCS12_parse(PKCS12 *p12, const char *pass, EVP_PKEY **pkey, X509 **cert, STACK_OF(X509) **ca); - PKCS12 *q_d2i_PKCS12_bio(BIO *bio, PKCS12 **pkcs12); --- -2.15.0 - diff --git a/patches/qt-QCoreTextFontEngine-Fix-build-with-Xcode-9.3.patch b/patches/qt-QCoreTextFontEngine-Fix-build-with-Xcode-9.3.patch deleted file mode 100644 index 860230c..0000000 --- a/patches/qt-QCoreTextFontEngine-Fix-build-with-Xcode-9.3.patch +++ /dev/null @@ -1,35 +0,0 @@ -From 05eed1cd4505bf9912b84ed39ab1ad22846e7d09 Mon Sep 17 00:00:00 2001 -From: Gabriel de Dietrich -Date: Fri, 30 Mar 2018 11:58:16 -0700 -Subject: QCoreTextFontEngine: Fix build with Xcode 9.3 - -Apple LLVM version 9.1.0 (clang-902.0.39.1) - -Error message: - -.../qfontengine_coretext.mm:827:20: error: qualified reference to - 'QFixed' is a constructor name rather than a type in this context - return QFixed::QFixed(int(CTFontGetUnitsPerEm(ctfont))); - -Change-Id: Iebe26b3b087a16b10664208fc8851cbddb47f043 -Reviewed-by: Konstantin Ritt ---- - src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git x/qtbase/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm y/qtbase/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm -index 66baf162d9..89794ef109 100644 ---- x/qtbase/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm -+++ y/qtbase/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm -@@ -830,7 +830,7 @@ void QCoreTextFontEngine::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, gl - - QFixed QCoreTextFontEngine::emSquareSize() const - { -- return QFixed::QFixed(int(CTFontGetUnitsPerEm(ctfont))); -+ return QFixed(int(CTFontGetUnitsPerEm(ctfont))); - } - - QFontEngine *QCoreTextFontEngine::cloneWithSize(qreal pixelSize) const --- -2.16.2 - diff --git a/patches/qt-QObject-Fix-isSignalConnected-when-signals-have-been.patch b/patches/qt-QObject-Fix-isSignalConnected-when-signals-have-been.patch new file mode 100644 index 0000000..f80031e --- /dev/null +++ b/patches/qt-QObject-Fix-isSignalConnected-when-signals-have-been.patch @@ -0,0 +1,185 @@ +From 78ab3263caae535a3bd31fa35c733ae2a28ca8ba Mon Sep 17 00:00:00 2001 +From: Kari Oikarinen +Date: Wed, 26 Sep 2018 10:29:14 +0300 +Subject: [PATCH] QObject: Fix isSignalConnected() when signals have been + disconnected +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The bitmap cache for the first 64 signals being connected was only set when the +connection is added. It was never unset when the connection was removed. + +Internal use of the connectedSignals bitmap is not hurt by it occasionally +saying a signal is connected even though it is not, since the purpose of those +checks is avoiding expensive operations that are not necessary if nothing is +connected to the signal. + +However, the public API using this cache meant that it also never spotted +signals being disconnected. This was not documented. Fix the behavior by only +using the cache if it is up to date. If it is not, use a slower path that gives +the correct answer. + +To avoid making disconnections and QObject destructions slower, the cache is +only updated to unset disconnected signals when new signal connections are +added. No extra work is done in the common case where signals are only +removed in the end of the QObject's lifetime. + +Fixes: QTBUG-32340 +Change-Id: Ieb6e498060157153cec60d9c8f1c33056993fda1 +Reviewed-by: Ville Voutilainen +Reviewed-by: Thiago Macieira +Reviewed-by: Olivier Goffart (Woboq GmbH) +Reviewed-by: Jędrzej Nowacki +--- + src/corelib/kernel/qobject.cpp | 34 ++++++++---- + .../corelib/kernel/qobject/tst_qobject.cpp | 53 +++++++++++++++++++ + 2 files changed, 76 insertions(+), 11 deletions(-) + +diff --git x/qtbase/src/corelib/kernel/qobject.cpp y/qtbase/src/corelib/kernel/qobject.cpp +index c6fe787e03..4532eacf0c 100644 +--- x/qtbase/src/corelib/kernel/qobject.cpp ++++ y/qtbase/src/corelib/kernel/qobject.cpp +@@ -418,6 +418,7 @@ void QObjectPrivate::cleanConnectionLists() + { + if (connectionLists->dirty && !connectionLists->inUse) { + // remove broken connections ++ bool allConnected = false; + for (int signal = -1; signal < connectionLists->count(); ++signal) { + QObjectPrivate::ConnectionList &connectionList = + (*connectionLists)[signal]; +@@ -429,11 +430,13 @@ void QObjectPrivate::cleanConnectionLists() + + QObjectPrivate::Connection **prev = &connectionList.first; + QObjectPrivate::Connection *c = *prev; ++ bool connected = false; // whether the signal is still connected somewhere + while (c) { + if (c->receiver) { + last = c; + prev = &c->nextConnectionList; + c = *prev; ++ connected = true; + } else { + QObjectPrivate::Connection *next = c->nextConnectionList; + *prev = next; +@@ -445,6 +448,14 @@ void QObjectPrivate::cleanConnectionLists() + // Correct the connection list's last pointer. + // As conectionList.last could equal last, this could be a noop + connectionList.last = last; ++ ++ if (!allConnected && !connected && signal >= 0 ++ && size_t(signal) < sizeof(connectedSignals) * 8) { ++ // This signal is no longer connected ++ connectedSignals[signal >> 5] &= ~(1 << (signal & 0x1f)); ++ } else if (signal == -1) { ++ allConnected = connected; ++ } + } + connectionLists->dirty = false; + } +@@ -2503,19 +2514,20 @@ bool QObject::isSignalConnected(const QMetaMethod &signal) const + + signalIndex += QMetaObjectPrivate::signalOffset(signal.mobj); + +- if (signalIndex < sizeof(d->connectedSignals) * 8) ++ QMutexLocker locker(signalSlotLock(this)); ++ if (!d->connectionLists) ++ return false; ++ ++ if (signalIndex < sizeof(d->connectedSignals) * 8 && !d->connectionLists->dirty) + return d->isSignalConnected(signalIndex); + +- QMutexLocker locker(signalSlotLock(this)); +- if (d->connectionLists) { +- if (signalIndex < uint(d->connectionLists->count())) { +- const QObjectPrivate::Connection *c = +- d->connectionLists->at(signalIndex).first; +- while (c) { +- if (c->receiver) +- return true; +- c = c->nextConnectionList; +- } ++ if (signalIndex < uint(d->connectionLists->count())) { ++ const QObjectPrivate::Connection *c = ++ d->connectionLists->at(signalIndex).first; ++ while (c) { ++ if (c->receiver) ++ return true; ++ c = c->nextConnectionList; + } + } + return false; +diff --git x/qtbase/tests/auto/corelib/kernel/qobject/tst_qobject.cpp y/qtbase/tests/auto/corelib/kernel/qobject/tst_qobject.cpp +index ec57522f48..20ce905265 100644 +--- x/qtbase/tests/auto/corelib/kernel/qobject/tst_qobject.cpp ++++ y/qtbase/tests/auto/corelib/kernel/qobject/tst_qobject.cpp +@@ -104,6 +104,7 @@ private slots: + void deleteQObjectWhenDeletingEvent(); + void overloads(); + void isSignalConnected(); ++ void isSignalConnectedAfterDisconnection(); + void qMetaObjectConnect(); + void qMetaObjectDisconnectOne(); + void sameName(); +@@ -3843,6 +3844,58 @@ void tst_QObject::isSignalConnected() + QVERIFY(!o.isSignalConnected(QMetaMethod())); + } + ++void tst_QObject::isSignalConnectedAfterDisconnection() ++{ ++ ManySignals o; ++ const QMetaObject *meta = o.metaObject(); ++ ++ const QMetaMethod sig00 = meta->method(meta->indexOfSignal("sig00()")); ++ QVERIFY(!o.isSignalConnected(sig00)); ++ QObject::connect(&o, &ManySignals::sig00, qt_noop); ++ QVERIFY(o.isSignalConnected(sig00)); ++ QVERIFY(QObject::disconnect(&o, &ManySignals::sig00, 0, 0)); ++ QVERIFY(!o.isSignalConnected(sig00)); ++ ++ const QMetaMethod sig69 = meta->method(meta->indexOfSignal("sig69()")); ++ QVERIFY(!o.isSignalConnected(sig69)); ++ QObject::connect(&o, &ManySignals::sig69, qt_noop); ++ QVERIFY(o.isSignalConnected(sig69)); ++ QVERIFY(QObject::disconnect(&o, &ManySignals::sig69, 0, 0)); ++ QVERIFY(!o.isSignalConnected(sig69)); ++ ++ { ++ ManySignals o2; ++ QObject::connect(&o, &ManySignals::sig00, &o2, &ManySignals::sig00); ++ QVERIFY(o.isSignalConnected(sig00)); ++ // o2 is destructed ++ } ++ QVERIFY(!o.isSignalConnected(sig00)); ++ ++ const QMetaMethod sig01 = meta->method(meta->indexOfSignal("sig01()")); ++ QObject::connect(&o, &ManySignals::sig00, qt_noop); ++ QObject::connect(&o, &ManySignals::sig01, qt_noop); ++ QObject::connect(&o, &ManySignals::sig69, qt_noop); ++ QVERIFY(o.isSignalConnected(sig00)); ++ QVERIFY(o.isSignalConnected(sig01)); ++ QVERIFY(o.isSignalConnected(sig69)); ++ QVERIFY(QObject::disconnect(&o, &ManySignals::sig69, 0, 0)); ++ QVERIFY(o.isSignalConnected(sig00)); ++ QVERIFY(o.isSignalConnected(sig01)); ++ QVERIFY(!o.isSignalConnected(sig69)); ++ QVERIFY(QObject::disconnect(&o, &ManySignals::sig00, 0, 0)); ++ QVERIFY(!o.isSignalConnected(sig00)); ++ QVERIFY(o.isSignalConnected(sig01)); ++ QVERIFY(!o.isSignalConnected(sig69)); ++ QObject::connect(&o, &ManySignals::sig69, qt_noop); ++ QVERIFY(!o.isSignalConnected(sig00)); ++ QVERIFY(o.isSignalConnected(sig01)); ++ QVERIFY(o.isSignalConnected(sig69)); ++ QVERIFY(QObject::disconnect(&o, &ManySignals::sig01, 0, 0)); ++ QVERIFY(!o.isSignalConnected(sig00)); ++ QVERIFY(!o.isSignalConnected(sig01)); ++ QVERIFY(o.isSignalConnected(sig69)); ++} ++ + void tst_QObject::qMetaObjectConnect() + { + SenderObject *s = new SenderObject; +-- +2.19.1 + diff --git a/patches/qt-QUrl-Support-IPv6-addresses-with-zone-id.patch b/patches/qt-QUrl-Support-IPv6-addresses-with-zone-id.patch new file mode 100644 index 0000000..bd5616f --- /dev/null +++ b/patches/qt-QUrl-Support-IPv6-addresses-with-zone-id.patch @@ -0,0 +1,108 @@ +From 2e492dc6a6cf9e73a04f65e133ea4e97324a68da Mon Sep 17 00:00:00 2001 +From: Robbert Proost +Date: Thu, 18 Jan 2018 09:52:49 +0100 +Subject: [PATCH] QUrl: Support IPv6 addresses with zone id + +Task-number: QTBUG-25550 +Change-Id: I37ec02b655abe2779aa11945e20550ce00e43723 +--- + src/corelib/io/qurl.cpp | 63 ++++++++++++++++--------- + tests/auto/corelib/io/qurl/tst_qurl.cpp | 52 ++++++++++++++++++++ + 2 files changed, 92 insertions(+), 23 deletions(-) + +diff --git x/qtbase/src/corelib/io/qurl.cpp y/qtbase/src/corelib/io/qurl.cpp +index 4587b9fcd6..e2a66c8459 100644 +--- x/qtbase/src/corelib/io/qurl.cpp ++++ y/qtbase/src/corelib/io/qurl.cpp +@@ -1203,16 +1203,18 @@ inline void QUrlPrivate::setQuery(const QString &value, int from, int iend) + + inline void QUrlPrivate::appendHost(QString &appendTo, QUrl::FormattingOptions options) const + { +- // EncodeUnicode is the only flag that matters +- if ((options & QUrl::FullyDecoded) == QUrl::FullyDecoded) +- options = 0; +- else +- options &= QUrl::EncodeUnicode; + if (host.isEmpty()) + return; + if (host.at(0).unicode() == '[') { +- // IPv6Address and IPvFuture address never require any transformation +- appendTo += host; ++ // IPv6 addresses might contain a zone-id which needs to be recoded ++ QString hostInCorrectFormat; ++ if (options != 0) ++ qt_urlRecode(hostInCorrectFormat, host.constBegin(), host.constEnd(), options, 0); ++ ++ if (hostInCorrectFormat.isEmpty()) ++ hostInCorrectFormat = host; ++ ++ appendTo += hostInCorrectFormat; + } else { + // this is either an IPv4Address or a reg-name + // if it is a reg-name, it is already stored in Unicode form +@@ -1278,31 +1280,46 @@ static const QChar *parseIpFuture(QString &host, const QChar *begin, const QChar + // ONLY the IPv6 address is parsed here, WITHOUT the brackets + static const QChar *parseIp6(QString &host, const QChar *begin, const QChar *end, QUrl::ParsingMode mode) + { +- QIPAddressUtils::IPv6Address address; +- const QChar *ret = QIPAddressUtils::parseIp6(address, begin, end); +- if (ret) { +- // this struct is kept in automatic storage because it's only 4 bytes ++ QString decoded; ++ if (mode == QUrl::TolerantMode) { + const ushort decodeColon[] = { decode(':'), 0 }; ++ if (qt_urlRecode(decoded, begin, end, QUrl::ComponentFormattingOption::PrettyDecoded, decodeColon) == 0) { ++ decoded = QString(begin, end-begin); ++ } ++ } ++ else { ++ decoded = QString(begin, end-begin); ++ } + +- // IPv6 failed parsing, check if it was a percent-encoded character in +- // the middle and try again +- QString decoded; +- if (mode == QUrl::TolerantMode && qt_urlRecode(decoded, begin, end, 0, decodeColon)) { +- // recurse +- // if the parsing fails again, the qt_urlRecode above will return 0 +- ret = parseIp6(host, decoded.constBegin(), decoded.constEnd(), mode); ++ const QLatin1String zoneIdIdentifier("%25"); ++ QIPAddressUtils::IPv6Address address; ++ QString zoneId; ++ ++ const QChar *endBeforeZoneId = decoded.constEnd(); ++ ++ int zoneIdPosition = decoded.indexOf(zoneIdIdentifier); ++ if ((zoneIdPosition != -1) && (decoded.lastIndexOf(zoneIdIdentifier) == zoneIdPosition)) { ++ zoneId = decoded.mid(zoneIdPosition + zoneIdIdentifier.size()); ++ endBeforeZoneId = decoded.constBegin() + zoneIdPosition; + +- // we can't return ret, otherwise it would be dangling +- return ret ? end : 0; ++ if (zoneId.isEmpty() == true) { ++ return end; + } ++ } + +- // no transformation, nothing to re-parse +- return ret; ++ const QChar *ret = QIPAddressUtils::parseIp6(address, decoded.constBegin(), endBeforeZoneId); ++ if (ret) { ++ return begin + (ret - decoded.constBegin()); + } + +- host.reserve(host.size() + (end - begin)); ++ host.reserve(host.size() + (decoded.constEnd() - decoded.constBegin())); + host += QLatin1Char('['); + QIPAddressUtils::toString(host, address); ++ ++ if (zoneId.isEmpty() == false) { ++ host += zoneIdIdentifier; ++ host += zoneId; ++ } + host += QLatin1Char(']'); + return 0; + } +-- +2.18.0 + diff --git a/patches/qt-Register-additional-meta-types.patch b/patches/qt-Register-additional-meta-types.patch deleted file mode 100644 index 047614f..0000000 --- a/patches/qt-Register-additional-meta-types.patch +++ /dev/null @@ -1,123 +0,0 @@ -From 3885257e655cefd1f8b18247aff76020c75379e1 Mon Sep 17 00:00:00 2001 -From: Lars Schmertmann -Date: Fri, 24 Mar 2017 11:20:14 +0100 -Subject: [PATCH] Register additional meta types - -Register QLowEnergyCharacteristic and QLowEnergyDescriptor -as meta types because they are used in signals. - -[ChangeLog][QtBluetooth] Register QLowEnergyCharacteristic -and QLowEnergyDescriptor as meta types. It is therefore -necessary to declare them as meta types in the header files. -This commit will cause conflicts with existing meta type -declarations in applications using Qt. These declarations -need to be removed. - -Change-Id: I18f33b1b2f159cffd6efbacc37178286b86a06e0 -Reviewed-by: Alex Blasche ---- - src/bluetooth/osx/osxbtcentralmanager.mm | 2 -- - src/bluetooth/qlowenergycharacteristic.h | 2 ++ - src/bluetooth/qlowenergycontroller_p.h | 5 ----- - src/bluetooth/qlowenergydescriptor.h | 2 ++ - src/bluetooth/qlowenergyservice.cpp | 2 ++ - .../test/tst_qlowenergycontroller-gattserver.cpp | 4 ---- - tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp | 3 --- - 7 files changed, 6 insertions(+), 14 deletions(-) - -diff --git x/qtconnectivity/src/bluetooth/osx/osxbtcentralmanager.mm y/qtconnectivity/src/bluetooth/osx/osxbtcentralmanager.mm -index ec046d1b..70473f1f 100644 ---- x/qtconnectivity/src/bluetooth/osx/osxbtcentralmanager.mm -+++ y/qtconnectivity/src/bluetooth/osx/osxbtcentralmanager.mm -@@ -48,8 +48,6 @@ - #include - #include - --Q_DECLARE_METATYPE(QLowEnergyCharacteristic) --Q_DECLARE_METATYPE(QLowEnergyDescriptor) - Q_DECLARE_METATYPE(QLowEnergyHandle) - - QT_BEGIN_NAMESPACE -diff --git x/qtconnectivity/src/bluetooth/qlowenergycharacteristic.h y/qtconnectivity/src/bluetooth/qlowenergycharacteristic.h -index b991e9a2..154c9936 100644 ---- x/qtconnectivity/src/bluetooth/qlowenergycharacteristic.h -+++ y/qtconnectivity/src/bluetooth/qlowenergycharacteristic.h -@@ -107,4 +107,6 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QLowEnergyCharacteristic::PropertyTypes) - - QT_END_NAMESPACE - -+Q_DECLARE_METATYPE(QLowEnergyCharacteristic) -+ - #endif // QLOWENERGYCHARACTERISTIC_H -diff --git x/qtconnectivity/src/bluetooth/qlowenergycontroller_p.h y/qtconnectivity/src/bluetooth/qlowenergycontroller_p.h -index b92716e9..6e866144 100644 ---- x/qtconnectivity/src/bluetooth/qlowenergycontroller_p.h -+++ y/qtconnectivity/src/bluetooth/qlowenergycontroller_p.h -@@ -497,11 +497,6 @@ Q_DECLARE_TYPEINFO(QLowEnergyControllerPrivate::Attribute, Q_MOVABLE_TYPE); - - QT_END_NAMESPACE - --#ifdef QT_WINRT_BLUETOOTH --Q_DECLARE_METATYPE(QLowEnergyCharacteristic) --Q_DECLARE_METATYPE(QLowEnergyDescriptor) --#endif // QT_WINRT_BLUETOOTH -- - #endif // QT_OSX_BLUETOOTH || QT_IOS_BLUETOOTH - - #endif // QLOWENERGYCONTROLLERPRIVATE_P_H -diff --git x/qtconnectivity/src/bluetooth/qlowenergydescriptor.h y/qtconnectivity/src/bluetooth/qlowenergydescriptor.h -index 1dfe1c35..9e71fc56 100644 ---- x/qtconnectivity/src/bluetooth/qlowenergydescriptor.h -+++ y/qtconnectivity/src/bluetooth/qlowenergydescriptor.h -@@ -89,4 +89,6 @@ protected: - - QT_END_NAMESPACE - -+Q_DECLARE_METATYPE(QLowEnergyDescriptor) -+ - #endif // QLOWENERGYDESCRIPTOR_H -diff --git x/qtconnectivity/src/bluetooth/qlowenergyservice.cpp y/qtconnectivity/src/bluetooth/qlowenergyservice.cpp -index 6e33c565..9d3129fd 100644 ---- x/qtconnectivity/src/bluetooth/qlowenergyservice.cpp -+++ y/qtconnectivity/src/bluetooth/qlowenergyservice.cpp -@@ -380,6 +380,8 @@ QLowEnergyService::QLowEnergyService(QSharedPointer p, - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); -+ qRegisterMetaType(); -+ qRegisterMetaType(); - - connect(p.data(), SIGNAL(error(QLowEnergyService::ServiceError)), - this, SIGNAL(error(QLowEnergyService::ServiceError))); -diff --git x/qtconnectivity/tests/auto/qlowenergycontroller-gattserver/test/tst_qlowenergycontroller-gattserver.cpp y/qtconnectivity/tests/auto/qlowenergycontroller-gattserver/test/tst_qlowenergycontroller-gattserver.cpp -index 3df27d92..e01457eb 100644 ---- x/qtconnectivity/tests/auto/qlowenergycontroller-gattserver/test/tst_qlowenergycontroller-gattserver.cpp -+++ y/qtconnectivity/tests/auto/qlowenergycontroller-gattserver/test/tst_qlowenergycontroller-gattserver.cpp -@@ -243,10 +243,6 @@ void TestQLowEnergyControllerGattServer::advertisedData() - QVERIFY(m_serverInfo.serviceUuids().contains(QBluetoothUuid(quint16(0x2000)))); - } - --// TODO: Why on earth is this not in the library??? --Q_DECLARE_METATYPE(QLowEnergyCharacteristic) --Q_DECLARE_METATYPE(QLowEnergyDescriptor) -- - void TestQLowEnergyControllerGattServer::serverCommunication() - { - qRegisterMetaType(); -diff --git x/qtconnectivity/tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp y/qtconnectivity/tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp -index 7b02dbcd..c6fd83e6 100644 ---- x/qtconnectivity/tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp -+++ y/qtconnectivity/tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp -@@ -91,9 +91,6 @@ private: - QList foundServices; - }; - --Q_DECLARE_METATYPE(QLowEnergyCharacteristic) --Q_DECLARE_METATYPE(QLowEnergyDescriptor) -- - tst_QLowEnergyController::tst_QLowEnergyController() - { - qRegisterMetaType(); --- -2.14.2 - diff --git a/patches/qt-Remove-Qt-Labs-specific-plugins-from-the-build.patch b/patches/qt-Remove-Qt-Labs-specific-plugins-from-the-build.patch new file mode 100644 index 0000000..0c7dea9 --- /dev/null +++ b/patches/qt-Remove-Qt-Labs-specific-plugins-from-the-build.patch @@ -0,0 +1,52 @@ +From 2e80dec588c21cfeb086912cf6c6a011c6b6b896 Mon Sep 17 00:00:00 2001 +From f3e9a6e63740d922577d331f6cbe57fd43888472 Mon Sep 17 00:00:00 2001 +From: Lars Schmertmann +Date: Mon, 16 Apr 2018 08:34:42 +0200 +Subject: Remove Qt Labs specific plugins from the build +--- + qtquickcontrols2/src/imports/imports.pro | 2 -- + qtdeclarative/src/imports/imports.pro | 4 ---- + 2 file changed, 6 deletions(-) + +diff --git x/qtquickcontrols2/src/imports/imports.pro y/qtquickcontrols2/src/imports/imports.pro +index e32bded7..944c9292 100644 +--- x/qtquickcontrols2/src/imports/imports.pro ++++ y/qtquickcontrols2/src/imports/imports.pro +@@ -1,8 +1,6 @@ + TEMPLATE = subdirs + SUBDIRS += \ + controls \ +- calendar \ +- platform \ + templates + + SUBDIRS += \ +diff --git x/qtdeclarative/src/imports/imports.pro y/qtdeclarative/src/imports/imports.pro +index 5d7e43488..33390bc42 100644 +--- x/qtdeclarative/src/imports/imports.pro ++++ y/qtdeclarative/src/imports/imports.pro +@@ -5,7 +5,6 @@ + SUBDIRS += \ + builtins \ + qtqml \ +- folderlistmodel \ + models + + qtHaveModule(sql): SUBDIRS += localstorage +@@ -14,13 +13,11 @@ + + qtHaveModule(quick) { + SUBDIRS += \ +- handlers \ + layouts \ + qtquick2 \ + window + + qtHaveModule(testlib): SUBDIRS += testlib +- qtConfig(systemsemaphore): SUBDIRS += sharedimage + qtConfig(quick-particles): \ + SUBDIRS += particles + +-- +2.16.2 + diff --git a/patches/qt-Use-QUrl-toString-when-forming-the-Host-header.patch b/patches/qt-Use-QUrl-toString-when-forming-the-Host-header.patch new file mode 100644 index 0000000..f9b4ab3 --- /dev/null +++ b/patches/qt-Use-QUrl-toString-when-forming-the-Host-header.patch @@ -0,0 +1,55 @@ +From 4ab766863d88a491f91fa81731dbde75d0122d89 Mon Sep 17 00:00:00 2001 +From: Timur Pocheptsov +Date: Mon, 14 May 2018 12:41:23 +0200 +Subject: [PATCH] Use QUrl::toString() when forming the 'Host' header +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This allows to correctly use IPv6 literals and also +deals (correctly) with a port (if it's set at all). + +Task-number: QTBUG-68245 +Change-Id: I6d29543887c4ab58d70f0970a6f0a1b822c301df +Reviewed-by: Thiago Macieira +Reviewed-by: Mårten Nordheim +--- + src/websockets/qwebsocket_p.cpp | 22 ++++++++++++---------- + 1 file changed, 12 insertions(+), 10 deletions(-) + +diff --git x/qtwebsockets/src/websockets/qwebsocket_p.cpp y/qtwebsockets/src/websockets/qwebsocket_p.cpp +index d233b66..9b27ad2 100644 +--- x/qtwebsockets/src/websockets/qwebsocket_p.cpp ++++ y/qtwebsockets/src/websockets/qwebsocket_p.cpp +@@ -1108,16 +1108,18 @@ void QWebSocketPrivate::processStateChanged(QAbstractSocket::SocketState socketS + headers << qMakePair(QString::fromLatin1(key), + QString::fromLatin1(m_request.rawHeader(key))); + +- const QString handshake = +- createHandShakeRequest(m_resourceName, +- m_request.url().host() +- % QStringLiteral(":") +- % QString::number(m_request.url().port(port)), +- origin(), +- QString(), +- QString(), +- m_key, +- headers); ++ const auto format = QUrl::RemoveScheme | QUrl::RemoveUserInfo ++ | QUrl::RemovePath | QUrl::RemoveQuery ++ | QUrl::RemoveFragment | QUrl::RemovePort; ++ const QString host = m_request.url().toString(format).mid(2); ++ const QString handshake = createHandShakeRequest(m_resourceName, ++ host % QStringLiteral(":") ++ % QString::number(m_request.url().port(port)), ++ origin(), ++ QString(), ++ QString(), ++ m_key, ++ headers); + if (handshake.isEmpty()) { + m_pSocket->abort(); + Q_EMIT q->error(QAbstractSocket::ConnectionRefusedError); +-- +2.19.0 + diff --git a/patches/qt-Use-user-provided-session-data-if-available.patch b/patches/qt-Use-user-provided-session-data-if-available.patch new file mode 100644 index 0000000..30b05fb --- /dev/null +++ b/patches/qt-Use-user-provided-session-data-if-available.patch @@ -0,0 +1,33 @@ +From bbd5c6feea8632c8321d3cd7a16a262d90f856e8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Andr=C3=A9=20Klitzing?= +Date: Thu, 22 Nov 2018 11:44:39 +0100 +Subject: [PATCH] Use user provided session data if available + +If a user uses persistent sessions and provide the session to +Qt again with setSessionTicket it should be used with a higher +priority. +This is also a work-around for QTBUG-71967. + +Change-Id: I7351b669b6de2863136d6106dc4f73fa5c7b8c51 +--- + src/network/ssl/qsslcontext_openssl.cpp | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git x/qtbase/src/network/ssl/qsslcontext_openssl.cpp y/qtbase/src/network/ssl/qsslcontext_openssl.cpp +index 41b759364b..24dbc9ecbc 100644 +--- x/qtbase/src/network/ssl/qsslcontext_openssl.cpp ++++ y/qtbase/src/network/ssl/qsslcontext_openssl.cpp +@@ -134,8 +134,9 @@ SSL* QSslContext::createSsl() + SSL* ssl = q_SSL_new(ctx); + q_SSL_clear(ssl); + +- if (!session && !sessionASN1().isEmpty() ++ if (!m_sessionASN1.isEmpty() + && !sslConfiguration.testSslOption(QSsl::SslOptionDisableSessionPersistence)) { ++ q_SSL_SESSION_free(session); + const unsigned char *data = reinterpret_cast(m_sessionASN1.constData()); + session = q_d2i_SSL_SESSION(0, &data, m_sessionASN1.size()); // refcount is 1 already, set by function above + } +-- +2.19.1 + diff --git a/patches/qt-configure-detect-fxc.exe-more-thoroughly.patch b/patches/qt-configure-detect-fxc.exe-more-thoroughly.patch new file mode 100644 index 0000000..831bd65 --- /dev/null +++ b/patches/qt-configure-detect-fxc.exe-more-thoroughly.patch @@ -0,0 +1,132 @@ +From d64ee0c6be7e9ce672382709d8ea38e30d1d5e0d Mon Sep 17 00:00:00 2001 +From: Oliver Wolff +Date: Tue, 12 Dec 2017 20:02:55 +0100 +Subject: configure: detect fxc.exe more thoroughly + +When building ANGLE, we need the shader compiler (fxc.exe), which is not +shipped with MinGW. Previously, we required an installed DirectX SDK. +For Windows versions >= 8, the DX SDK is also part of the Windows Kit, +so we also allow the user to specify the location of the Windows Kit. +We also detect fxc on 64-bit hosts now, and in newer SDK versions which +version the binary directory. + +The detected binary is now exported by configure, so the ANGLE project +file does not need to duplicate the logic anymore. + +Task-number: QTBUG-52487 +Change-Id: I41a17992909041dd84291b69498195cc8b8fab8a +--- + src/angle/src/common/common.pri | 16 +--------------- + src/gui/configure.json | 11 +++-------- + src/gui/configure.pri | 31 +++++++++++++++++++++++++++---- + 3 files changed, 31 insertions(+), 27 deletions(-) + +diff --git x/qtbase/src/angle/src/common/common.pri y/qtbase/src/angle/src/common/common.pri +index c1fad14951..2c0af02b58 100644 +--- x/qtbase/src/angle/src/common/common.pri ++++ y/qtbase/src/angle/src/common/common.pri +@@ -21,20 +21,6 @@ lib_replace.replace = \$\$\$\$[QT_INSTALL_LIBS] + lib_replace.CONFIG = path + QMAKE_PRL_INSTALL_REPLACE += lib_replace + +-# DirectX is included in the Windows 8 Kit, but everything else requires the DX SDK. +-winrt|msvc { +- FXC = fxc.exe +-} else { +- DX_DIR = $$(DXSDK_DIR) +- isEmpty(DX_DIR) { +- error("Cannot determine DirectX SDK location. Please set DXSDK_DIR environment variable.") +- } +- +- equals(QMAKE_TARGET.arch, x86_64) { +- FXC = \"$${DX_DIR}Utilities\\bin\\x64\\fxc.exe\" +- } else { +- FXC = \"$${DX_DIR}Utilities\\bin\\x86\\fxc.exe\" +- } +-} ++FXC = $$shell_quote($$shell_path($$QMAKE_FXC_LOCATION)) + + static: DEFINES *= LIBGLESV2_EXPORT_H_ ANGLE_EXPORT= +diff --git x/qtbase/src/gui/configure.json y/qtbase/src/gui/configure.json +index 4145ceddf6..2a96bc5a75 100644 +--- x/qtbase/src/gui/configure.json ++++ y/qtbase/src/gui/configure.json +@@ -702,10 +702,6 @@ + } + }, + +- "testTypeAliases": { +- "files": [ "fxc" ] +- }, +- + "tests": { + "angle_d3d11_qdtd": { + "label": "D3D11_QUERY_DATA_TIMESTAMP_DISJOINT", +@@ -721,9 +717,7 @@ + "fxc": { + "label": "Direct3D Shader Compiler", + "type": "fxc", +- "files": [ +- "fxc.exe" +- ] ++ "log": "value" + }, + "egl-x11": { + "label": "EGL on X11", +@@ -976,7 +970,8 @@ + "condition": "features.dxguid && tests.fxc && (features.direct3d9 || (config.winrt && features.direct3d11 && libs.d3dcompiler))", + "output": [ + "publicFeature", +- { "type": "define", "name": "QT_OPENGL_ES_2_ANGLE" } ++ { "type": "define", "name": "QT_OPENGL_ES_2_ANGLE" }, ++ { "type": "varAssign", "name": "QMAKE_FXC_LOCATION", "value": "tests.fxc.value" } + ] + }, + "angle_d3d11_qdtd": { +diff --git x/qtbase/src/gui/configure.pri y/qtbase/src/gui/configure.pri +index f53a93063c..bc27a756a3 100644 +--- x/qtbase/src/gui/configure.pri ++++ y/qtbase/src/gui/configure.pri +@@ -20,12 +20,35 @@ defineTest(qtConfLibrary_freetype) { + # DXSDK_DIR variable. Starting with Windows Kit 8, it is included in + # the Windows SDK. + defineTest(qtConfTest_fxc) { +- dxdir = $$getenv("DXSDK_DIR") +- !isEmpty(dxdir) { +- EXTRA_PATH += $$dxdir/Utilities/bin/x86 ++ !mingw { ++ fxc = $$qtConfFindInPath("fxc.exe") ++ } else { ++ equals(QMAKE_HOST.arch, x86_64): \ ++ fns = x64/fxc.exe ++ else: \ ++ fns = x86/fxc.exe ++ dxdir = $$(DXSDK_DIR) ++ !isEmpty(dxdir) { ++ fxc = $$dxdir/Utilities/bin/$$fns ++ } else { ++ winkitbindir = $$(WindowsSdkVerBinPath) ++ !isEmpty(winkitbindir) { ++ fxc = $$winkitbindir/$$fns ++ } else { ++ winkitdir = $$(WindowsSdkDir) ++ !isEmpty(winkitdir): \ ++ fxc = $$winkitdir/bin/$$fns ++ } ++ } + } + +- qtConfTest_files($${1}): return(true) ++ !isEmpty(fxc):exists($$fxc) { ++ $${1}.value = $$clean_path($$fxc) ++ export($${1}.value) ++ $${1}.cache += value ++ export($${1}.cache) ++ return(true) ++ } + return(false) + } + +-- +2.17.0 + diff --git a/patches/qt-configure-refactor-directx-checks.patch b/patches/qt-configure-refactor-directx-checks.patch new file mode 100644 index 0000000..0de8fb3 --- /dev/null +++ b/patches/qt-configure-refactor-directx-checks.patch @@ -0,0 +1,442 @@ +From 6b73c48ac35de82b95b74f8dd614fe282209cd61 Mon Sep 17 00:00:00 2001 +From: Oswald Buddenhagen +Date: Tue, 12 Dec 2017 12:21:16 +0100 +Subject: configure: refactor directx checks + +properly atomize the libraries and express their dependencies, and +adjust the project files accordingly. + +note that we don't try to use any additional paths, as all SDKs we +currently support have built-in directx 11 support: +- msvc2013 comes with win sdk 8.1; that is also used for win7 targets +- mingw-64 5.3 (though this one is missing fxc, which is why the code + path for using an external sdk for that remains) + +Change-Id: Ib44e389ef46567308293c2bbcad20a96e8ef70c7 +--- + src/angle/src/common/gles_common.pri | 6 +- + src/angle/src/libEGL/libEGL.pro | 5 +- + src/gui/configure.json | 176 ++++++++++++++---- + src/gui/configure.pri | 19 +- + .../fontdatabases/windows/windows.pri | 9 +- + .../fontdatabases/winrt/winrt.pri | 4 +- + src/plugins/platforms/direct2d/direct2d.pro | 3 +- + src/plugins/platforms/platforms.pro | 6 +- + src/plugins/platforms/windows/windows.pri | 2 + + src/plugins/platforms/winrt/winrt.pro | 3 +- + 10 files changed, 168 insertions(+), 65 deletions(-) + +diff --git x/qtbase/src/angle/src/common/gles_common.pri y/qtbase/src/angle/src/common/gles_common.pri +index 82d38a62e6..927949d758 100644 +--- x/qtbase/src/angle/src/common/gles_common.pri ++++ y/qtbase/src/angle/src/common/gles_common.pri +@@ -5,11 +5,11 @@ INCLUDEPATH += $$OUT_PWD/.. $$ANGLE_DIR/src/libANGLE + + # Remember to adapt src/gui/configure.* if the Direct X version changes. + !winrt: \ +- LIBS_PRIVATE += -ld3d9 ++ QMAKE_USE_PRIVATE += d3d9 + winrt: \ +- LIBS_PRIVATE += -ld3dcompiler -ldxgi -ld3d11 ++ QMAKE_USE_PRIVATE += d3dcompiler d3d11 dxgi + +-LIBS_PRIVATE += -ldxguid ++QMAKE_USE_PRIVATE += dxguid + + STATICLIBS = translator preprocessor + for(libname, STATICLIBS) { +diff --git x/qtbase/src/angle/src/libEGL/libEGL.pro y/qtbase/src/angle/src/libEGL/libEGL.pro +index 9e9c639002..ad2117f2fc 100644 +--- x/qtbase/src/angle/src/libEGL/libEGL.pro ++++ y/qtbase/src/angle/src/libEGL/libEGL.pro +@@ -1,9 +1,10 @@ + include(../common/common.pri) + DEF_FILE_TARGET = $${TARGET} + TARGET = $$qtLibraryTarget($${LIBEGL_NAME}) +-winrt: LIBS_PRIVATE += -ld3d11 ++winrt: QMAKE_USE_PRIVATE += d3d11 ++QMAKE_USE_PRIVATE += dxguid + +-LIBS_PRIVATE += -ldxguid -L$$QT_BUILD_TREE/lib -l$$qtLibraryTarget($${LIBGLESV2_NAME}) ++LIBS_PRIVATE += -L$$QT_BUILD_TREE/lib -l$$qtLibraryTarget($${LIBGLESV2_NAME}) + + DEFINES += GL_APICALL= GL_GLEXT_PROTOTYPES= EGLAPI= LIBEGL_IMPLEMENTATION + +diff --git x/qtbase/src/gui/configure.json y/qtbase/src/gui/configure.json +index 219385a108..4145ceddf6 100644 +--- x/qtbase/src/gui/configure.json ++++ y/qtbase/src/gui/configure.json +@@ -65,21 +65,79 @@ + "-lbcm_host" + ] + }, +- "direct2d": { +- "label": "Direct 2D", +- "export": "", ++ "dxguid": { ++ "label": "DirectX GUID", ++ "sources": [ ++ "-ldxguid" ++ ] ++ }, ++ "dxgi": { ++ "label": "DirectX GI", ++ "headers": [ "dxgi.h" ], ++ "sources": [ ++ "-ldxgi" ++ ] ++ }, ++ "dxgi1_2": { ++ "label": "DirectX GI 1.2", + "test": { +- "include": [ "d3d11_1.h", "d2d1_1.h", "d2d1_1helper.h", "dxgi1_2.h", "wrl.h", "dwrite.h" ], +- "tail": "using Microsoft::WRL::ComPtr;", + "main": [ +- "ComPtr d2dFactory;", +- "D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, d2dFactory.ReleaseAndGetAddressOf());", +- "ComPtr surface;", ++ "// fails with mingw-w64 5.4.0 - declaration is missing from header", ++ "IDXGISurface1 *surface;", + "(void) surface;" + ] + }, ++ "headers": [ "dxgi1_2.h" ], ++ "sources": [ ++ "-ldxgi" ++ ] ++ }, ++ "d3d9": { ++ "label": "Direct3D 9", ++ "headers": "d3d9.h", ++ "sources": [ ++ "-ld3d9" ++ ] ++ }, ++ "d3d11": { ++ "label": "Direct3D 11", ++ "headers": "d3d11.h", ++ "sources": [ ++ "-ld3d11" ++ ] ++ }, ++ "d3d11_1": { ++ "label": "Direct3D 11.1", ++ "headers": "d3d11_1.h", ++ "sources": [ ++ "-ld3d11" ++ ] ++ }, ++ "d3dcompiler": { ++ "label": "Direct3D Shader Compiler Library", ++ "headers": "d3dcompiler.h", ++ "sources": [ ++ "-ld3dcompiler" ++ ] ++ }, ++ "d2d1": { ++ "label": "Direct2D 1", ++ "headers": [ "d2d1.h", "d2d1helper.h" ], ++ "sources": [ ++ "-ld2d1" ++ ] ++ }, ++ "d2d1_1": { ++ "label": "Direct2D 1.1", ++ "test": { ++ "main": [ ++ "ID2D1Factory1 *d2dFactory;", ++ "D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &d2dFactory);" ++ ] ++ }, ++ "headers": [ "d2d1_1.h", "d2d1_1helper.h" ], + "sources": [ +- "-ld2d1 -ldwrite -ld3d11" ++ "-ld2d1" + ] + }, + "directfb": { +@@ -96,9 +154,8 @@ + { "type": "pkgConfig", "args": "directfb" } + ] + }, +- "directwrite": { ++ "dwrite": { + "label": "DirectWrite", +- "export": "", + "test": { + "include": [ "dwrite.h", "d2d1.h" ], + "main": [ +@@ -107,6 +164,29 @@ + " (IUnknown **)(&factory));" + ] + }, ++ "headers": "dwrite.h", ++ "sources": [ ++ "-ldwrite" ++ ] ++ }, ++ "dwrite_1": { ++ "label": "DirectWrite 1", ++ "headers": "dwrite_1.h", ++ "sources": [ ++ "-ldwrite" ++ ] ++ }, ++ "dwrite_2": { ++ "label": "DirectWrite 2", ++ "test": { ++ "main": [ ++ "IUnknown *factory = 0;", ++ "(void)(size_t(DWRITE_E_NOCOLOR) + sizeof(IDWriteFontFace2));", ++ "DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory2),", ++ " &factory);" ++ ] ++ }, ++ "headers": "dwrite_2.h", + "sources": [ + "-ldwrite" + ] +@@ -623,7 +703,7 @@ + }, + + "testTypeAliases": { +- "files": [ "directX" ] ++ "files": [ "fxc" ] + }, + + "tests": { +@@ -638,26 +718,10 @@ + ] + } + }, +- "directwrite2": { +- "label": "DirectWrite 2", +- "type": "compile", +- "test": { +- "include": [ "dwrite_2.h", "d2d1.h" ], +- "main": [ +- "IUnknown *factory = 0;", +- "(void)(size_t(DWRITE_E_NOCOLOR) + sizeof(IDWriteFontFace2));", +- "DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory2),", +- " &factory);" +- ] +- }, +- "use": "directwrite" +- }, +- "directx": { +- "label": "DirectX SDK", +- "type": "directX", ++ "fxc": { ++ "label": "Direct3D Shader Compiler", ++ "type": "fxc", + "files": [ +- "d3dcompiler.h", +- "d3d11.lib", + "fxc.exe" + ] + }, +@@ -909,7 +973,7 @@ + "angle": { + "label": "ANGLE", + "autoDetect": "features.opengles2 || features.opengl-dynamic", +- "condition": "config.win32 && tests.directx", ++ "condition": "features.dxguid && tests.fxc && (features.direct3d9 || (config.winrt && features.direct3d11 && libs.d3dcompiler))", + "output": [ + "publicFeature", + { "type": "define", "name": "QT_OPENGL_ES_2_ANGLE" } +@@ -936,19 +1000,59 @@ + "directwrite": { + "label": "DirectWrite", + "emitIf": "config.win32", +- "condition": "libs.directwrite", ++ "condition": "libs.dwrite", ++ "output": [ "privateFeature" ] ++ }, ++ "directwrite1": { ++ "label": "DirectWrite 1", ++ "emitIf": "config.win32", ++ "condition": "libs.dwrite_1", + "output": [ "privateFeature" ] + }, + "directwrite2": { + "label": "DirectWrite 2", + "emitIf": "config.win32", +- "condition": "features.directwrite && tests.directwrite2", ++ "condition": "features.directwrite1 && libs.dwrite_2", ++ "output": [ "privateFeature" ] ++ }, ++ "dxguid": { ++ "label": "DirectX GUID", ++ "condition": "config.win32 && libs.dxguid", ++ "output": [ "privateFeature" ] ++ }, ++ "direct3d9": { ++ "label": "Direct 3D 9", ++ "condition": "config.win32 && !config.winrt && libs.d3d9", ++ "output": [ "privateFeature" ] ++ }, ++ "dxgi": { ++ "label": "DirectX GI", ++ "condition": "config.win32 && libs.dxgi", ++ "output": [ "privateFeature" ] ++ }, ++ "dxgi1_2": { ++ "label": "DirectX GI 1.2", ++ "condition": "features.dxgi && libs.dxgi1_2", ++ "output": [ "privateFeature" ] ++ }, ++ "direct3d11": { ++ "label": "Direct 3D 11", ++ "condition": "features.dxgi && libs.d3d11", ++ "output": [ "privateFeature" ] ++ }, ++ "direct3d11_1": { ++ "label": "Direct 3D 11.1", ++ "condition": "features.direct3d11 && features.dxgi1_2 && libs.d3d11_1", + "output": [ "privateFeature" ] + }, + "direct2d": { + "label": "Direct 2D", +- "section": "Platform plugins", +- "condition": "config.win32 && !config.winrt && libs.direct2d", ++ "condition": "config.win32 && !config.winrt && features.direct3d11 && libs.d2d1", ++ "output": [ "privateFeature" ] ++ }, ++ "direct2d1_1": { ++ "label": "Direct 2D 1.1", ++ "condition": "features.direct2d && libs.d2d1_1", + "output": [ "privateFeature" ] + }, + "evdev": { +diff --git x/qtbase/src/gui/configure.pri y/qtbase/src/gui/configure.pri +index fcd2d1f73e..f53a93063c 100644 +--- x/qtbase/src/gui/configure.pri ++++ y/qtbase/src/gui/configure.pri +@@ -15,29 +15,16 @@ defineTest(qtConfLibrary_freetype) { + return(true) + } + +-# Check for Direct X SDK (include, lib, and direct shader compiler 'fxc'). ++# Check for Direct X shader compiler 'fxc'. + # Up to Direct X SDK June 2010 and for MinGW, this is pointed to by the + # DXSDK_DIR variable. Starting with Windows Kit 8, it is included in +-# the Windows SDK. Checking for the header is not sufficient, since it +-# is also present in MinGW. +-defineTest(qtConfTest_directX) { ++# the Windows SDK. ++defineTest(qtConfTest_fxc) { + dxdir = $$getenv("DXSDK_DIR") + !isEmpty(dxdir) { +- EXTRA_INCLUDEPATH += $$dxdir/include +- equals(QT_ARCH, x86_64): \ +- EXTRA_LIBDIR += $$dxdir/lib/x64 +- else: \ +- EXTRA_LIBDIR += $$dxdir/lib/x86 + EXTRA_PATH += $$dxdir/Utilities/bin/x86 + } + +- $$qtConfEvaluate("features.sse2") { +- ky = $$size($${1}.files._KEYS_) +- $${1}.files._KEYS_ += $$ky +- # Not present on MinGW-32 +- $${1}.files.$${ky} = "intrin.h" +- } +- + qtConfTest_files($${1}): return(true) + return(false) + } +diff --git x/qtbase/src/platformsupport/fontdatabases/windows/windows.pri y/qtbase/src/platformsupport/fontdatabases/windows/windows.pri +index 0e64084cf1..9c529f55ea 100644 +--- x/qtbase/src/platformsupport/fontdatabases/windows/windows.pri ++++ y/qtbase/src/platformsupport/fontdatabases/windows/windows.pri +@@ -15,9 +15,14 @@ qtConfig(freetype) { + HEADERS += $$PWD/qwindowsfontdatabase_ft_p.h + } + +-qtConfig(directwrite) { +- qtConfig(directwrite2): \ ++qtConfig(directwrite):qtConfig(direct2d) { ++ qtConfig(directwrite2) { ++ QMAKE_USE_PRIVATE += dwrite_2 + DEFINES *= QT_USE_DIRECTWRITE2 ++ } else { ++ QMAKE_USE_PRIVATE += dwrite ++ } ++ QMAKE_USE_PRIVATE += d2d1 + + SOURCES += $$PWD/qwindowsfontenginedirectwrite.cpp + HEADERS += $$PWD/qwindowsfontenginedirectwrite_p.h +diff --git x/qtbase/src/platformsupport/fontdatabases/winrt/winrt.pri y/qtbase/src/platformsupport/fontdatabases/winrt/winrt.pri +index 291ada220f..7617df2e7a 100644 +--- x/qtbase/src/platformsupport/fontdatabases/winrt/winrt.pri ++++ y/qtbase/src/platformsupport/fontdatabases/winrt/winrt.pri +@@ -8,4 +8,6 @@ HEADERS += \ + + DEFINES += __WRL_NO_DEFAULT_LIB__ + +-LIBS += -lws2_32 -ldwrite ++LIBS += -lws2_32 ++ ++QMAKE_USE_PRIVATE += dwrite_1 +diff --git x/qtbase/src/plugins/platforms/direct2d/direct2d.pro y/qtbase/src/plugins/platforms/direct2d/direct2d.pro +index 3bfd02bdc8..9764272632 100644 +--- x/qtbase/src/plugins/platforms/direct2d/direct2d.pro ++++ y/qtbase/src/plugins/platforms/direct2d/direct2d.pro +@@ -8,7 +8,8 @@ QT += \ + qtConfig(accessibility): QT += accessibility_support-private + qtConfig(vulkan): QT += vulkan_support-private + +-LIBS += -ldwmapi -ld2d1 -ld3d11 -ldwrite -lversion -lgdi32 ++LIBS += -ldwmapi -lversion -lgdi32 ++QMAKE_USE_PRIVATE += dwrite_1 d2d1_1 d3d11_1 dxgi1_2 + + include(../windows/windows.pri) + +diff --git x/qtbase/src/plugins/platforms/platforms.pro y/qtbase/src/plugins/platforms/platforms.pro +index e61887618f..b70d8d5996 100644 +--- x/qtbase/src/plugins/platforms/platforms.pro ++++ y/qtbase/src/plugins/platforms/platforms.pro +@@ -14,10 +14,10 @@ qtConfig(xcb) { + uikit:!watchos: SUBDIRS += ios + osx: SUBDIRS += cocoa + +-win32:!winrt: SUBDIRS += windows +-winrt: SUBDIRS += winrt ++win32:!winrt:qtConfig(direct3d9): SUBDIRS += windows ++winrt:qtConfig(direct3d11): SUBDIRS += winrt + +-qtConfig(direct2d) { ++qtConfig(direct3d11_1):qtConfig(direct2d1_1):qtConfig(directwrite1) { + SUBDIRS += direct2d + } + +diff --git x/qtbase/src/plugins/platforms/windows/windows.pri y/qtbase/src/plugins/platforms/windows/windows.pri +index f4c396f7c5..2f244d8b71 100644 +--- x/qtbase/src/plugins/platforms/windows/windows.pri ++++ y/qtbase/src/plugins/platforms/windows/windows.pri +@@ -9,6 +9,8 @@ mingw: LIBS *= -luuid + # For the dialog helpers: + LIBS += -lshlwapi -lshell32 -ladvapi32 + ++QMAKE_USE_PRIVATE += d3d9/nolink ++ + DEFINES *= QT_NO_CAST_FROM_ASCII + + SOURCES += \ +diff --git x/qtbase/src/plugins/platforms/winrt/winrt.pro y/qtbase/src/plugins/platforms/winrt/winrt.pro +index 042b270cff..fc70eba979 100644 +--- x/qtbase/src/plugins/platforms/winrt/winrt.pro ++++ y/qtbase/src/plugins/platforms/winrt/winrt.pro +@@ -8,7 +8,8 @@ QT += \ + + DEFINES *= QT_NO_CAST_FROM_ASCII __WRL_NO_DEFAULT_LIB__ + +-LIBS += -lws2_32 -ld3d11 ++LIBS += -lws2_32 ++QMAKE_USE_PRIVATE += d3d11 + + SOURCES = \ + main.cpp \ +-- +2.17.0 + diff --git a/patches/qt-disable-designer.patch b/patches/qt-disable-designer.patch new file mode 100644 index 0000000..4e303ad --- /dev/null +++ b/patches/qt-disable-designer.patch @@ -0,0 +1,36 @@ +diff --git x/qttools/src/linguist/linguist.pro y/qttools/src/linguist/linguist.pro +index 103336da..2fe9656d 100644 +--- x/qttools/src/linguist/linguist.pro ++++ y/qttools/src/linguist/linguist.pro +@@ -3,11 +3,6 @@ SUBDIRS = \ + lrelease \ + lupdate \ + lconvert +-!no-png:qtHaveModule(widgets):qtConfig(process): SUBDIRS += linguist +- +-qtNomakeTools( \ +- linguist \ +-) + + equals(QMAKE_HOST.os, Windows): CMAKE_BIN_SUFFIX = ".exe" + +diff --git x/qttools/src/src.pro y/qttools/src/src.pro +index 41064a5d..fcef4dd8 100644 +--- x/qttools/src/src.pro ++++ y/qttools/src/src.pro +@@ -3,12 +3,6 @@ TEMPLATE = subdirs + qtHaveModule(widgets) { + no-png { + message("Some graphics-related tools are unavailable without PNG support") +- } else { +- SUBDIRS = assistant \ +- pixeltool \ +- designer +- +- linguist.depends = designer + } + } + +-- +2.14.3 + diff --git a/patches/qt-fix-macOS-no-printer.patch b/patches/qt-fix-macOS-no-printer.patch new file mode 100644 index 0000000..f336acb --- /dev/null +++ b/patches/qt-fix-macOS-no-printer.patch @@ -0,0 +1,19 @@ +--- x/qtbase/src/plugins/platforms/cocoa/qprintengine_mac_p.h ++++ y/qtbase/src/plugins/platforms/cocoa/qprintengine_mac_p.h +@@ -52,6 +52,7 @@ + // + + #include ++#include + + #ifndef QT_NO_PRINTER + +--- x/qtbase/src/plugins/plugins.pro ++++ y/qtbase/src/plugins/plugins.pro +@@ -9,6 +9,3 @@ + !android:qtConfig(library): SUBDIRS *= generic + } + qtHaveModule(widgets): SUBDIRS += styles +- +-!winrt:qtHaveModule(printsupport): \ +- SUBDIRS += printsupport diff --git a/patches/qt-macOS-iOS-Fix-garbled-text-under-some-conditions.patch b/patches/qt-macOS-iOS-Fix-garbled-text-under-some-conditions.patch deleted file mode 100644 index 34ba698..0000000 --- a/patches/qt-macOS-iOS-Fix-garbled-text-under-some-conditions.patch +++ /dev/null @@ -1,90 +0,0 @@ -From 13f25c979fe4396e6d5a76bf183341229da2bacd Mon Sep 17 00:00:00 2001 -From: Eskil Abrahamsen Blomfeldt -Date: Thu, 30 Nov 2017 15:00:26 +0100 -Subject: [PATCH] macOS/iOS: Fix garbled text under some conditions - -There seems to be an issue in CoreText which may cause an existing -font descriptor to give unreliable results if it refers to one of -the system theme fonts. Since we do not know all function calls -or events that may trigger this bug, the safe route is to always -create fresh font descriptors when creating fonts for these -descriptors. The impact on performance should be small, as Qt has -its own internal caches. - -[ChangeLog][macOS/iOS][Text] Fixed an issue where text using -one of the system theme fonts would under certain circumstances -display random glyphs. - -Task-number: QTBUG-63476 -Change-Id: I9e9b253018c63976345eec1439a6b78de2cab869 ---- - .../fontdatabases/mac/qcoretextfontdatabase.mm | 24 ++++++++++++++-------- - .../fontdatabases/mac/qcoretextfontdatabase_p.h | 4 +++- - 2 files changed, 19 insertions(+), 9 deletions(-) - -diff --git x/qtbase/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm y/qtbase/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm -index 6347d4d231..237e8a89a5 100644 ---- x/qtbase/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm -+++ y/qtbase/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm -@@ -416,7 +416,19 @@ extern CGAffineTransform qt_transform_from_fontdef(const QFontDef &fontDef); - template <> - QFontEngine *QCoreTextFontDatabaseEngineFactory::fontEngine(const QFontDef &fontDef, void *usrPtr) - { -- CTFontDescriptorRef descriptor = static_cast(usrPtr); -+ QCFType descriptor = QCFType::constructFromGet( -+ static_cast(usrPtr)); -+ -+ // CoreText will sometimes invalidate information in font descriptors that refer -+ // to system fonts in certain function calls or application states. While the descriptor -+ // looks the same from the outside, some internal plumbing is different, causing the results -+ // of creating CTFonts from those descriptors unreliable. The work-around for this -+ // is to copy the attributes of those descriptors each time we make a new CTFont -+ // from them instead of referring to the original, as that may trigger the CoreText bug. -+ if (m_systemFontDescriptors.contains(descriptor)) { -+ QCFType attributes = CTFontDescriptorCopyAttributes(descriptor); -+ descriptor = CTFontDescriptorCreateWithAttributes(attributes); -+ } - - // Since we do not pass in the destination DPI to CoreText when making - // the font, we need to pass in a point size which is scaled to include -@@ -427,14 +439,10 @@ QFontEngine *QCoreTextFontDatabaseEngineFactory::fontEngine - qreal scaledPointSize = fontDef.pixelSize; - - CGAffineTransform matrix = qt_transform_from_fontdef(fontDef); -- CTFontRef font = CTFontCreateWithFontDescriptor(descriptor, scaledPointSize, &matrix); -- if (font) { -- QFontEngine *engine = new QCoreTextFontEngine(font, fontDef); -- CFRelease(font); -- return engine; -- } -+ if (QCFType font = CTFontCreateWithFontDescriptor(descriptor, scaledPointSize, &matrix)) -+ return new QCoreTextFontEngine(font, fontDef); - -- return NULL; -+ return nullptr; - } - - #ifndef QT_NO_FREETYPE -diff --git x/qtbase/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase_p.h y/qtbase/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase_p.h -index 9612b909f1..e14d1d6e6e 100644 ---- x/qtbase/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase_p.h -+++ y/qtbase/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase_p.h -@@ -91,12 +91,14 @@ public: - QFont *themeFont(QPlatformTheme::Font) const; - const QHash &themeFonts() const; - -+protected: -+ mutable QSet m_systemFontDescriptors; -+ - private: - void populateFromDescriptor(CTFontDescriptorRef font, const QString &familyName = QString()); - - mutable QString defaultFontName; - -- mutable QSet m_systemFontDescriptors; - mutable QHash m_themeFonts; - bool m_hasPopulatedAliases; - }; --- -2.15.1 - diff --git a/resources/CMakeLists.txt b/resources/CMakeLists.txt index 2590f11..5d40bc5 100644 --- a/resources/CMakeLists.txt +++ b/resources/CMakeLists.txt @@ -14,17 +14,20 @@ SET(QRC_FILES "ausweisapp.qrc") IF(IOS OR ANDROID OR ${CMAKE_BUILD_TYPE} STREQUAL "DEBUG") LIST(APPEND QRC_FILES "ausweisapp_mobile.qrc") - - SET(ausweisapp_qml.qrc "${CMAKE_CURRENT_BINARY_DIR}/ausweisapp_qml.qrc") - WRITE_QRC("${ausweisapp_qml.qrc}" "${CMAKE_CURRENT_SOURCE_DIR}/qml" "qml") - LIST(APPEND QRC_FILES "${ausweisapp_qml.qrc}") ENDIF() IF(DESKTOP) LIST(APPEND QRC_FILES "ausweisapp_desktop.qrc") ENDIF() +IF(TARGET Qt5::Qml) + SET(ausweisapp_qml.qrc "${CMAKE_CURRENT_BINARY_DIR}/ausweisapp_qml.qrc") + WRITE_QRC("${ausweisapp_qml.qrc}" "${CMAKE_CURRENT_SOURCE_DIR}/qml" "qml") + LIST(APPEND QRC_FILES "${ausweisapp_qml.qrc}") + + SET(QML_IMPORT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/qml CACHE string "qml files" FORCE) +ENDIF() + SET(RCC ${CMAKE_BINARY_DIR}/src/${PROJECT_NAME}.rcc) SET(RCC ${RCC} PARENT_SCOPE) qt5_add_binary_resources(AusweisAppRcc "${QRC_FILES}" DESTINATION ${RCC}) -SET(QML_IMPORT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/qml ${CMAKE_CURRENT_SOURCE_DIR}/qml_stationary CACHE string "qml files" FORCE) diff --git a/resources/asan_blacklist b/resources/asan_blacklist index 60992d5..a04a67a 100644 --- a/resources/asan_blacklist +++ b/resources/asan_blacklist @@ -1,7 +1,9 @@ # Recommended usage: -# LSAN_OPTIONS=suppressions=/home/dev/src/asan_blacklist ./AusweisApp2 +# LSAN_OPTIONS=suppressions=/home/dev/AusweisApp2.src/resources/asan_blacklist ./AusweisApp2 leak:g_malloc* leak:CRYPTO_malloc leak:libxcb* -leak:Q* +leak:QQuick* +leak:QKde* +leak:QQml* diff --git a/resources/ausweisapp.qrc b/resources/ausweisapp.qrc index 922c7be..a845aba 100644 --- a/resources/ausweisapp.qrc +++ b/resources/ausweisapp.qrc @@ -10,17 +10,13 @@ images/bt_3b.svg images/bt_4.svg images/bt_4b.svg - images/bspd1.svg images/canHint.png images/icon_attention.svg images/icon_ok.png images/icon_cancelled.png images/Icon_Checked.svg - images/android/search_icon.svg - images/android/search_cancel.svg - images/iOS/search_icon.svg - images/iOS/search_cancel.svg - images/back-chevron.png + images/search.svg + images/cancel.svg images/iOS/tabBar/Anbieter-off.png images/iOS/tabBar/Anbieter-on.png images/iOS/tabBar/Ausweisen-off.png @@ -32,14 +28,12 @@ images/icon_Pin.svg images/iOS/tabBar/More-off.svg images/iOS/tabBar/More-on.svg - images/rotes_X.svg - images/gruener_Haken.svg + images/status_error.svg + images/status_info.svg + images/status_ok.svg images/iOS/CheckedCheckbox.png - images/iOS/Header-Ausweisapp@3x.png - images/delete.png images/delete.svg images/NFCPhoneCard.png - images/submit.png images/submit.svg images/ausweis.png images/provider/information.svg @@ -86,5 +80,7 @@ images/location_flag_de.svg images/location_flag_en.svg images/siteWithLogo.png + images/icon_pair.svg + images/icon_settings.svg diff --git a/resources/ausweisapp_desktop.qrc b/resources/ausweisapp_desktop.qrc index d8f5c60..8a439b1 100644 --- a/resources/ausweisapp_desktop.qrc +++ b/resources/ausweisapp_desktop.qrc @@ -4,18 +4,25 @@ html_templates/error.html stylesheets/desktop.qss images/beta.svg - images/green_check_mark.svg images/MenuSelected.png images/MenuUnselected.png images/MenuUnselectedDisabled.png images/npa.ico - images/Logo_AutentApp2_2014.png - images/AppLogo_AutentApp2_2014.png + images/Logo_Governikus.png + images/Logo_AusweisApp2.png images/start_nPA_eAT.png images/busy_animation.gif images/html_message_section.jpg - images/padlock.svg - images/padlock_empty.svg + images/desktop/background.png + images/desktop/help_icon.svg + images/desktop/main_history.svg + images/desktop/main_identify.svg + images/desktop/main_pin.svg + images/desktop/main_provider.svg + images/desktop/sandglass.svg + images/desktop/settings_icon.svg + images/desktop/titlebar_arrow.svg + images/desktop/continue_arrow.svg images/randompin/btn_normal_0.png images/randompin/btn_normal_1.png images/randompin/btn_normal_2.png @@ -56,8 +63,8 @@ updatable-files/reader/img_Gemalto_Prox_SU_mit_ausweis.png updatable-files/reader/img_HID_Global_OMNIKEY_5321_V2.png updatable-files/reader/img_HID_Global_OMNIKEY_5321_V2_mit_ausweis.png - updatable-files/reader/img_HID_Omnikey_5421.png - updatable-files/reader/img_HID_Omnikey_5421_mit_ausweis.png + updatable-files/reader/img_HID_Omnikey_542x.png + updatable-files/reader/img_HID_Omnikey_542x_mit_ausweis.png updatable-files/reader/img_HID_Omnikey_Mobile_Reader_502X_CL.png updatable-files/reader/img_HID_Omnikey_Mobile_Reader_502X_CL_mit_ausweis.png updatable-files/reader/img_HID_Omnikey_Mobile_Reader_4121_CL.png @@ -86,5 +93,9 @@ updatable-files/reader/img_Reiner_SCT_cyberjack_RFID_standard_mit_ausweis.png updatable-files/reader/img_RemoteReader.png updatable-files/reader/img_RemoteReader_mit_ausweis.png + updatable-files/reader/img_Cherry_ST_1275.png + updatable-files/reader/img_Cherry_ST_1275_mit_ausweis.png + updatable-files/reader/img_Signotec_Omega_Pad.png + updatable-files/reader/img_Signotec_Omega_Pad_mit_ausweis.png diff --git a/resources/ausweisapp_mobile.qrc b/resources/ausweisapp_mobile.qrc index 7a6761e..94ce12b 100644 --- a/resources/ausweisapp_mobile.qrc +++ b/resources/ausweisapp_mobile.qrc @@ -3,43 +3,133 @@ qtquickcontrols2.conf images/zahnraeder.svg images/check.svg - images/iOS/arrowLeft.svg - images/iOS/arrowRight.svg - images/iOS/list_item_arrow.svg - images/iOS/radio_button_check_ios.svg - images/iOS/toggle_on.png - images/iOS/toggle_off.png - images/iOS/toggle_disabled.png + images/arrowRight.svg + images/share.svg images/android/navigation/ausweisen.svg images/android/navigation/anbieter.svg images/android/navigation/balloon.svg images/android/navigation/remoteleser.svg images/android/navigation/verlauf.svg images/android/navigation/pin.svg - images/android/navigation/versionsinformation.svg images/android/navigation/faq.svg images/android/navigation/support.svg images/android/navigation/bewerten.svg - images/android/navigation/teilen.svg - images/android/checkbox_0.svg - images/android/checkbox_1.svg - images/android/tabDivider.svg - images/android/arrowRight.svg - images/android/arrowLeft.svg - images/android/arrowRightWhite.svg - images/android/arrowLeftWhite.svg - images/android/android_toggle_on.png - images/android/android_toggle_off.png - images/android/android_toggle_on_disabled.png - images/android/android_toggle_off_disabled.png - images/android/android_arrow_back.svg - images/android/android_arrow_back_white.svg + images/android/navigation/tutorial.svg + images/iOS/search_icon.svg + images/iOS/search_cancel.svg images/iOS/more/icon_mehr_favorit.svg images/iOS/more/icon_mehr_fragen.svg images/iOS/more/icon_mehr_info.svg images/iOS/more/icon_mehr_license.svg images/iOS/more/icon_mehr_remotereader.svg images/iOS/more/icon_mehr_upload.svg + images/iOS/more/icon_mehr_log.svg + images/iOS/more/icon_mehr_tutorial.svg + images/tutorial/main_menu_what_caret.svg + images/tutorial/main_menu_where_caret.svg + images/tutorial/main_menu_how_caret.svg + images/tutorial/main_menu_important_caret.svg + images/tutorial/arrow_blue.svg + images/tutorial/arrows.svg + images/tutorial/button_de.png + images/tutorial/button_en.png + images/tutorial/idcard.svg + images/tutorial/identify.svg + images/tutorial/questionmark.svg + images/tutorial/hint.svg + images/tutorial/thumb_up.svg + images/tutorial/hand.svg + images/tutorial/check.svg + images/tutorial/click.svg + images/tutorial/save.svg + images/tutorial/bva.svg + images/tutorial/provider_home.svg + images/tutorial/rectangles.svg + images/tutorial/zoom_triangle.svg + images/tutorial/laptop.svg + images/tutorial/tablet.svg + images/tutorial/tablet-nfc.svg + images/tutorial/tablet-no-nfc.svg + images/tutorial/reader.svg + images/tutorial/desktop.svg + images/tutorial/phone.svg + images/tutorial/phone_list.svg + images/tutorial/phone-screen.svg + images/tutorial/nfc.svg + images/tutorial/no-nfc.svg + images/tutorial/wifi.svg + images/tutorial/bluetooth.svg + images/tutorial/letters.svg + images/tutorial/usb.svg + images/tutorial/circle-1.svg + images/tutorial/circle-2.svg + images/tutorial/circle-3.svg + images/tutorial/circle-4.svg + images/tutorial/circle-lock.svg + images/tutorial/circle-lock-2.svg + images/tutorial/up_icon.svg + images/tutorial/phone_screen_de.jpg + images/tutorial/phone_screen_en.jpg + images/tutorial/pin-5@2x.png + images/tutorial/pin-6@2x.png + images/tutorial/user-tine@3x.png + images/tutorial/providericons.png + images/tutorial/play_movie.png + images/tutorial/screenshot_cert_de.png + images/tutorial/screenshot_cert_en.png + images/tutorial/screenshot_providerlist_de.png + images/tutorial/screenshot_providerlist_en.png + images/tutorial/screenshot_menu_providerlist_de.png + images/tutorial/screenshot_menu_providerlist_en.png + images/tutorial/screenshot_pairing_de.png + images/tutorial/screenshot_pairing_en.png + images/tutorial/screenshot_sac_menu_de.png + images/tutorial/screenshot_sac_menu_en.png + images/tutorial/screenshot_choose_reader_de.png + images/tutorial/screenshot_choose_reader_en.png + images/tutorial/screenshot_pin_management_menu_en.png + images/tutorial/screenshot_pin_management_menu_de.png + images/tutorial/section_seperator_what.svg + images/tutorial/section_seperator_where.svg + images/tutorial/section_seperator_how.svg + images/tutorial/section_seperator_important.svg + images/tutorial/generated/where_overview_question.svg + images/tutorial/generated/where_providerlist_screenshot_de.svg + images/tutorial/generated/where_providerlist_screenshot_en.svg + images/tutorial/generated/where_identify_now_de.svg + images/tutorial/generated/where_identify_now_en.svg + images/tutorial/generated/where_userdata_example_de.svg + images/tutorial/generated/where_userdata_example_en.svg + images/tutorial/generated/where_lay_down_id.svg + images/tutorial/generated/where_pin6.svg + images/tutorial/generated/how_questions_everywhere.svg + images/tutorial/generated/how_device_lineup.svg + images/tutorial/generated/how_method_nfc.svg + images/tutorial/generated/how_method_sac_desktop.svg + images/tutorial/generated/how_method_sac_mobile.svg + images/tutorial/generated/how_method_bluetooth.svg + images/tutorial/generated/how_form_no_fun.svg + images/tutorial/generated/how_desktop.svg + images/tutorial/generated/important_pin5.svg + images/tutorial/generated/important_pin6.svg + images/tutorial/generated/important_lets_go.svg + images/tutorial/generated/important_space_questionmark.svg + images/tutorial/generated/reader_nfc_provider_on_smartphone.svg + images/tutorial/generated/reader_nfc_npa_on_smartphone.svg + images/tutorial/generated/reader_nfc_smartphone_nfc_position.svg + images/tutorial/generated/reader_nfc_finished.svg + images/tutorial/generated/reader_nfc_pin6.svg + images/tutorial/generated/reader_nfc_userdata_example_de.svg + images/tutorial/generated/reader_nfc_userdata_example_en.svg + images/tutorial/generated/reader_sac_provider_on_laptop.svg + images/tutorial/generated/reader_sac_npa_on_laptop.svg + images/tutorial/generated/reader_sac_aa2_ok.svg + images/tutorial/generated/reader_sac_menu_de.svg + images/tutorial/generated/reader_sac_menu_en.svg + images/tutorial/generated/reader_sac_no_nfc_devices.svg + images/tutorial/generated/reader_sac_no_nfc_provider.svg + images/tutorial/generated/reader_bluetooth_card_inserted.svg + images/tutorial/generated/reader_bluetooth_connection.svg images/provider/categoryIcons/general.svg images/provider/categoryIcons/citizen.svg images/provider/categoryIcons/finance.svg @@ -58,5 +148,7 @@ images/phone_to_pc.svg images/android/navigation/remotesettings.svg images/trash_icon.svg + images/trash_icon_all.svg + images/trash_icon_white.svg diff --git a/resources/config.json.in b/resources/config.json.in index a8759fe..3204cd6 100644 --- a/resources/config.json.in +++ b/resources/config.json.in @@ -3,11 +3,13 @@ "_comment_1": "array of CVCs; hex encoded", "_comment_2": [ + "DECVCAeID00104_DECVCAeID00105", "DECVCAeID00103_DECVCAeID00104", "DECVCAeID00102_DECVCAeID00103", "DECVCAeID00102" ], "cvRootCertificates": [ + "7F218201B67F4E82016E5F290100420E44454356434165494430303130347F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A786410459D1A08A2572022E17FED831982A9CB904518003CF83DABED88DBED28963768B88DBA22A153558BD21DDAA99318378B7CD7EBFE849557CD43F8526ABE72734538701015F200E44454356434165494430303130357F4C12060904007F0007030102025305FC0F13FFFF5F25060108000601025F24060201000601025F37402FBEEAE1B1ADE6CFB6CAB9D9B19F6A6EFACB7D9701997F43CF608BF72CF496F485F691E75190DEC25C672B92CF9BA917883659A6DC16793D22BBEC4019C616CC", "7F218201B67F4E82016E5F290100420E44454356434165494430303130337F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7864104241D8627338B64F20077FFD558909A096C635DDB222852038EAAE642E869A40173D588F817D95DB2A6A0F077EA5EE63596A20F85BC3CB176D2F98D88D90219AA8701015F200E44454356434165494430303130347F4C12060904007F0007030102025305FC0F13FFFF5F25060105000901045F24060108000901045F3740313A81ED8734E7A8C45F16B55FB603E63027B7F44C2DE3A8E782552D35949DB221CA33BD41A01DA6A1288C7885714FC3A03FA45683B75D3884930EC6738AF8A0", "7F218201B67F4E82016E5F290100420E44454356434165494430303130327F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A78641048925419FC7F194922CFC6B8DD25AE6A19C1B59216E6CF06270E5D75CFD64205F55CF867BBFEFEEFD6E680E1FD197F18AB684484901362568EFC9ADB5C6018D728701015F200E44454356434165494430303130337F4C12060904007F0007030102025305FC0F13FFFF5F25060102010200035F24060105010200035F37404D6F08A86A4F18409F6685387DD3C6A7FF5D68EA4F7714A861BBB3BB721D05D3014ADF1763C9292F715D8E94EE9B3E1B73AB1382414EBF39DFB3B0FB6C09DBEB", "7F218201B67F4E82016E5F290100420E44454356434165494430303130327F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A78641043347ECF96FFB4BD9B8554EFBCCFC7D0B242F1071E29B4C9C622C79E339D840AF67BEB9B912692265D9C16C62573F4579FFD4DE2DE92BAB409DD5C5D48244A9F78701015F200E44454356434165494430303130327F4C12060904007F0007030102025305FE0F01FFFF5F25060100010001085F24060103010001085F37405067145C68CAE9520F5BB34817F1CA9C43593DB56406C6A3B006CBF3F314E7349ACF0CC6BFEBCBDEFD10B4DCF0F231DA56977D88F9F90182D199076A56506451" @@ -15,19 +17,21 @@ "_comment_3": "array of Test-CVCs; hex encoded", "_comment_4": [ - "DETESTeID00001", - "DETESTeID00001_DETESTeID00002", - "DETESTeID00002_DETESTeID00004", + "DETESTeID00005_DETESTeID00006", "DETESTeID00004_DETESTeID00005", + "DETESTeID00002_DETESTeID00004", + "DETESTeID00001_DETESTeID00002", + "DETESTeID00001", "DECVCAeIDCTL0401_DECVCAeIDCTL0402", "DECVCAeIDCT00001_DECVCAeIDCTL0401", "DECVCAeIDCT00001_DECVCAeIDCT00001" ], "cvRootCertificatesTest": [ - "7F218201B67F4E82016E5F290100420E44455445535465494430303030317F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7864104184BB519FC2A8F52DC0DC73112FACFE914F2A49B678DD5799A2B1DFE95E1A66359014E22FA8D66438413CEBA6CF0E215576B673376BF617AF4DFE9761D2290148701015F200E44455445535465494430303030317F4C12060904007F0007030102025305FE0F01FFFF5F25060100000801035F24060103000801035F37409F25EBFAF4B91E4C60A1683754C5DC076A3179753EF97D9F8CB01FE1DCD3B8C83E7A26602AB1F344BE5706006D79A9FF6A9716404DC83B9F30E1213B393128A2", - "7F218201B67F4E82016E5F290100420E44455445535465494430303030317F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7864104096EB58BFD86252238EC2652185C43C3A56C320681A21E37A8E69DDC387C0C5F5513856EFE2FDC656E604893212E29449B365E304605AC5413E75BE31E641F128701015F200E44455445535465494430303030327F4C12060904007F0007030102025305FE0F01FFFF5F25060100000902015F24060103000902015F3740141120A0FDFC011A52F3F72B387A3DC7ACA88B4868D5AE9741780B6FF8A0B49E5F55169A2D298EF5CF95935DCA0C3DF3E9D42DC45F74F2066317154961E6C746", - "7F218201B67F4E82016E5F290100420E44455445535465494430303030327F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A786410474FF63AB838C73C303AC003DFEE95CF8BF55F91E8FEBCB7395D942036E47CF1845EC786EC95BB453AAC288AD023B6067913CF9B63F908F49304E5CFC8B3050DD8701015F200E44455445535465494430303030347F4C12060904007F0007030102025305FC0F13FFFF5F25060102000501015F24060105000501015F37405C035A0611B6C58F0B5261FDD009DECAB7DC7A79482D5248CCA119059B7D82B2157CF0C4A499BCF441EFDD35E294A58C0AF19A34A0762159533285ACF170A505", + "7F218201B67F4E82016E5F290100420E44455445535465494430303030357F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A786410425AB80F9C7BCA0AB1759D8E469F911CC006D02131552AA5F248B2A38D7C72CFB3317EA6881FD24D8B31A2E75FBEDA87964B60787095F75C753CD8BC5264D3C9A8701015F200E44455445535465494430303030367F4C12060904007F0007030102025305FC0F13FFFF5F25060108000200055F24060201000200055F37402E55923ED687CB104D609DD183402E8292DB03C3EFFE5EF3FAC597D2A8DB27370269EAAD7341D72447C9184CD817AE0E2BD4DF6FCF89DC52F455D490F077E5E9", "7F218201B67F4E82016E5F290100420E44455445535465494430303030347F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A78641049BFEBA8DC7FAAB6E3BDEB3FF794DBB800848FE4F6940A4CC7EECB5159C87DA5395505892026D420A22596CD014ED1FD872DADA597DB0F8D64441041198F62D448701015F200E44455445535465494430303030357F4C12060904007F0007030102025305FC0F13FFFF5F25060105000500045F24060108000500045F37402D2468416D66BCBE259B9B907A73395BC1EF94ED75F9C17615210246E9EFB06E6753E9055CE76623B7699B9EFB1A7D3A9DD83F6E6E09E55A33EA0A5F62A1C719", + "7F218201B67F4E82016E5F290100420E44455445535465494430303030327F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A786410474FF63AB838C73C303AC003DFEE95CF8BF55F91E8FEBCB7395D942036E47CF1845EC786EC95BB453AAC288AD023B6067913CF9B63F908F49304E5CFC8B3050DD8701015F200E44455445535465494430303030347F4C12060904007F0007030102025305FC0F13FFFF5F25060102000501015F24060105000501015F37405C035A0611B6C58F0B5261FDD009DECAB7DC7A79482D5248CCA119059B7D82B2157CF0C4A499BCF441EFDD35E294A58C0AF19A34A0762159533285ACF170A505", + "7F218201B67F4E82016E5F290100420E44455445535465494430303030317F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7864104096EB58BFD86252238EC2652185C43C3A56C320681A21E37A8E69DDC387C0C5F5513856EFE2FDC656E604893212E29449B365E304605AC5413E75BE31E641F128701015F200E44455445535465494430303030327F4C12060904007F0007030102025305FE0F01FFFF5F25060100000902015F24060103000902015F3740141120A0FDFC011A52F3F72B387A3DC7ACA88B4868D5AE9741780B6FF8A0B49E5F55169A2D298EF5CF95935DCA0C3DF3E9D42DC45F74F2066317154961E6C746", + "7F218201B67F4E82016E5F290100420E44455445535465494430303030317F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7864104184BB519FC2A8F52DC0DC73112FACFE914F2A49B678DD5799A2B1DFE95E1A66359014E22FA8D66438413CEBA6CF0E215576B673376BF617AF4DFE9761D2290148701015F200E44455445535465494430303030317F4C12060904007F0007030102025305FE0F01FFFF5F25060100000801035F24060103000801035F37409F25EBFAF4B91E4C60A1683754C5DC076A3179753EF97D9F8CB01FE1DCD3B8C83E7A26602AB1F344BE5706006D79A9FF6A9716404DC83B9F30E1213B393128A2", "7F218201BA7F4E8201725F290100421044454356434165494443544C303430317F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A78641043EE15015916563E31459045924DE804C1D93A77652AA25D0B753730DBA3233886A9A9B28A06AF84CC5A40F78E9167CA40B8098724A3A0332283D0A52C5453FE08701015F201044454356434165494443544C303430327F4C12060904007F0007030102025305FE1FFFFFFF5F25060106000300015F24060109000300015F374068261CEB4DC915301371C9B273377F33CEB25AFA07E70EDB3498ACF0327DC13B9AF99A9D694A6D048F0DB6FF1774E882CA8F41C8A0B96FDDF6F6DDABFD55CB43", "7F218201BA7F4E8201725F2901004210444543564341654944435430303030317F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A78641042C037C6CF8C0B62E36E220B7D411404AF248A2C83C569A49FAB02FC232D2395B3A5FF80DDB01D0DFAFCEF55E54ACCBA4C56E528F0746BEF1108E7D9B0122EBA78701015F201044454356434165494443544C303430317F4C12060904007F0007030102025305FE1FFFFFFF5F25060106000300015F24060109000300015F37404FB2F9F17D38656EEC2846CBB1711E07D739E6A584D487B3AFBA5C723C73A10236995E6A19499DE941DFE3CC044E2AACB6B5869C0E46C9585AEF892E2236F62E", "7F218201BA7F4E8201725F2901004210444543564341654944435430303030317F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7864104206160B85B82B2BE249DCD7B15FD1AD46CA03F39FE675C07535806E57CE24349392BBB9F73B364672F12243F18083DCC49ACA613767F0873AEB60715FC605A288701015F2010444543564341654944435430303030317F4C12060904007F0007030102025305FE1FFFFFFF5F25060106000300015F24060109000300015F37402B62927D46BF675DB387FC4A425FF3B604B20DD5ED6FFCDAFA9DA1DD920DC996245B358167C66A721DF39C6897864E4D648746339A97D22D08659885F92BDE21" @@ -40,33 +44,21 @@ ], "tlsSettings": { - "protocolVersion": "TlsV1_0OrLater", + "protocolVersion": "TlsV1_2", "_comment_1": "ciphers are ordered by preference", "ciphers": [ "ECDHE-ECDSA-AES256-GCM-SHA384", "ECDHE-RSA-AES256-GCM-SHA384", - "DHE-DSS-AES256-GCM-SHA384", "DHE-RSA-AES256-GCM-SHA384", "ECDHE-ECDSA-AES256-SHA384", "ECDHE-RSA-AES256-SHA384", - "DHE-DSS-AES256-SHA256", "DHE-RSA-AES256-SHA256", "ECDHE-ECDSA-AES128-GCM-SHA256", "ECDHE-RSA-AES128-GCM-SHA256", - "DHE-DSS-AES128-GCM-SHA256", "DHE-RSA-AES128-GCM-SHA256", "ECDHE-ECDSA-AES128-SHA256", "ECDHE-RSA-AES128-SHA256", - "DHE-DSS-AES128-SHA256", - "DHE-RSA-AES128-SHA256", - "ECDHE-ECDSA-AES256-SHA", - "ECDHE-RSA-AES256-SHA", - "DHE-DSS-AES256-SHA", - "DHE-RSA-AES256-SHA", - "ECDHE-ECDSA-AES128-SHA", - "ECDHE-RSA-AES128-SHA", - "DHE-DSS-AES128-SHA", - "DHE-RSA-AES128-SHA" + "DHE-RSA-AES128-SHA256" ], "_comment_2": "prime256v1 := secp256r1", "ellipticCurves": [ @@ -78,23 +70,23 @@ "secp224r1" ], "signatureAlgorithms": [ - "Rsa+Sha512", - "Dsa+Sha512", - "Ec+Sha512", - "Rsa+Sha384", - "Dsa+Sha384", - "Ec+Sha384", - "Rsa+Sha256", - "Dsa+Sha256", - "Ec+Sha256", - "Rsa+Sha224", - "Dsa+Sha224", - "Ec+Sha224" + "RSA+SHA512", + "DSA+SHA512", + "ECDSA+SHA512", + "RSA+SHA384", + "DSA+SHA384", + "ECDSA+SHA384", + "RSA+SHA256", + "DSA+SHA256", + "ECDSA+SHA256", + "RSA+SHA224", + "DSA+SHA224", + "ECDSA+SHA224" ] }, "tlsSettingsPsk": { - "protocolVersion": "TlsV1_1OrLater", + "protocolVersion": "TlsV1_2", "_comment_1": "ciphers are ordered by preference", "ciphers": [ "RSA-PSK-AES256-GCM-SHA384", @@ -105,15 +97,15 @@ ], "_comment_2": "ellipticCurves not needed", "signatureAlgorithms": [ - "Rsa+Sha512", - "Rsa+Sha384", - "Rsa+Sha256", - "Rsa+Sha224" + "RSA+SHA512", + "RSA+SHA384", + "RSA+SHA256", + "RSA+SHA224" ] }, "tlsSettingsRemoteReader": { - "protocolVersion": "TlsV1_2OrLater", + "protocolVersion": "TlsV1_2", "_comment_1": "ciphers are ordered by preference", "ciphers": [ "ECDHE-RSA-AES256-GCM-SHA384", @@ -133,14 +125,14 @@ "secp224r1" ], "signatureAlgorithms": [ - "Rsa+Sha512", - "Rsa+Sha384", - "Rsa+Sha256" + "RSA+SHA512", + "RSA+SHA384", + "RSA+SHA256" ] }, "tlsSettingsRemoteReaderPairing": { - "protocolVersion": "TlsV1_2OrLater", + "protocolVersion": "TlsV1_2", "_comment_1": "ciphers are ordered by preference", "ciphers": [ "RSA-PSK-AES256-GCM-SHA384", @@ -150,9 +142,9 @@ "RSA-PSK-AES256-CBC-SHA" ], "signatureAlgorithms": [ - "Rsa+Sha512", - "Rsa+Sha384", - "Rsa+Sha256" + "RSA+SHA512", + "RSA+SHA384", + "RSA+SHA256" ] }, @@ -171,14 +163,18 @@ "selfAuthentication": { "_comment_1": "TCTokenURL for self authentication (AusweisAuskunft)", - "url": "https://www.autentapp.de/AusweisAuskunft/WebServiceRequesterServlet?mode=xml", - "testUrl": "https://test.governikus-eid.de/AusweisAuskunft/WebServiceRequesterServlet?mode=xml" + "url": "https://www.autentapp.de/AusweisAuskunft/WebServiceRequesterServlet?mode=json", + "testUrl": "https://test.governikus-eid.de/AusweisAuskunft/WebServiceRequesterServlet?mode=json" }, "updateServer": { "baseUrl": "@REMOTE_CONFIG_URL@/updatable-files" }, + "whitelistServer": { + "baseUrl": "https://appl.governikus-asp.de/whitelistserver" + }, + "updates": { "release": "@REMOTE_CONFIG_URL@@REMOTE_CONFIG_PATH_APPCAST@/Appcast.json", "beta": "@REMOTE_CONFIG_URL@@REMOTE_CONFIG_PATH_APPCAST_BETA@/Appcast.json" diff --git a/resources/html_templates/alreadyactive.html b/resources/html_templates/alreadyactive.html index 6754df5..e5e3cb8 100644 --- a/resources/html_templates/alreadyactive.html +++ b/resources/html_templates/alreadyactive.html @@ -81,7 +81,7 @@ diff --git a/resources/html_templates/error.html b/resources/html_templates/error.html index bb74813..54fe062 100644 --- a/resources/html_templates/error.html +++ b/resources/html_templates/error.html @@ -81,7 +81,7 @@ diff --git a/resources/images/AusweisApp_2_Logo.svg b/resources/images/AusweisApp_2_Logo.svg deleted file mode 100644 index fb5a615..0000000 --- a/resources/images/AusweisApp_2_Logo.svg +++ /dev/null @@ -1,224 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/resources/images/AppLogo_AutentApp2_2014.png b/resources/images/Logo_AusweisApp2.png similarity index 100% rename from resources/images/AppLogo_AutentApp2_2014.png rename to resources/images/Logo_AusweisApp2.png diff --git a/resources/images/Logo_AutentApp2_2014.png b/resources/images/Logo_Governikus.png similarity index 100% rename from resources/images/Logo_AutentApp2_2014.png rename to resources/images/Logo_Governikus.png diff --git a/resources/images/android/android_arrow_back.svg b/resources/images/android/android_arrow_back.svg deleted file mode 100644 index 67c0a0e..0000000 --- a/resources/images/android/android_arrow_back.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - diff --git a/resources/images/android/android_arrow_back_white.svg b/resources/images/android/android_arrow_back_white.svg deleted file mode 100644 index 9b1cf74..0000000 --- a/resources/images/android/android_arrow_back_white.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - diff --git a/resources/images/android/android_nav_button_icon_history.svg b/resources/images/android/android_nav_button_icon_history.svg deleted file mode 100644 index 68c9f08..0000000 --- a/resources/images/android/android_nav_button_icon_history.svg +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/resources/images/android/android_nav_button_icon_identify.svg b/resources/images/android/android_nav_button_icon_identify.svg deleted file mode 100644 index 73a1890..0000000 --- a/resources/images/android/android_nav_button_icon_identify.svg +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/resources/images/android/android_nav_button_icon_provider.svg b/resources/images/android/android_nav_button_icon_provider.svg deleted file mode 100644 index 9d0ee0c..0000000 --- a/resources/images/android/android_nav_button_icon_provider.svg +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/resources/images/android/android_nav_button_icon_settings.svg b/resources/images/android/android_nav_button_icon_settings.svg deleted file mode 100644 index 752d582..0000000 --- a/resources/images/android/android_nav_button_icon_settings.svg +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/resources/images/android/android_toggle_off.png b/resources/images/android/android_toggle_off.png deleted file mode 100644 index 986210d..0000000 Binary files a/resources/images/android/android_toggle_off.png and /dev/null differ diff --git a/resources/images/android/android_toggle_off_disabled.png b/resources/images/android/android_toggle_off_disabled.png deleted file mode 100644 index 2dd84ab..0000000 Binary files a/resources/images/android/android_toggle_off_disabled.png and /dev/null differ diff --git a/resources/images/android/android_toggle_on.png b/resources/images/android/android_toggle_on.png deleted file mode 100644 index 0927313..0000000 Binary files a/resources/images/android/android_toggle_on.png and /dev/null differ diff --git a/resources/images/android/android_toggle_on_disabled.png b/resources/images/android/android_toggle_on_disabled.png deleted file mode 100644 index 76a78d3..0000000 Binary files a/resources/images/android/android_toggle_on_disabled.png and /dev/null differ diff --git a/resources/images/android/arrowLeft.svg b/resources/images/android/arrowLeft.svg deleted file mode 100644 index 028c9f2..0000000 --- a/resources/images/android/arrowLeft.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - diff --git a/resources/images/android/arrowLeftWhite.svg b/resources/images/android/arrowLeftWhite.svg deleted file mode 100644 index bbdc59c..0000000 --- a/resources/images/android/arrowLeftWhite.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/resources/images/android/arrowRightWhite.svg b/resources/images/android/arrowRightWhite.svg deleted file mode 100644 index b017764..0000000 --- a/resources/images/android/arrowRightWhite.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/resources/images/android/checkbox_0.svg b/resources/images/android/checkbox_0.svg deleted file mode 100644 index b4edf1b..0000000 --- a/resources/images/android/checkbox_0.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - diff --git a/resources/images/android/checkbox_1.svg b/resources/images/android/checkbox_1.svg deleted file mode 100644 index 11586f5..0000000 --- a/resources/images/android/checkbox_1.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - diff --git a/resources/images/android/hdpi/npa.png b/resources/images/android/hdpi/npa.png index c269b72..d6b982b 100644 Binary files a/resources/images/android/hdpi/npa.png and b/resources/images/android/hdpi/npa.png differ diff --git a/resources/images/android/hdpi/npa_beta.png b/resources/images/android/hdpi/npa_beta.png index 0011af6..7c38a02 100644 Binary files a/resources/images/android/hdpi/npa_beta.png and b/resources/images/android/hdpi/npa_beta.png differ diff --git a/resources/images/android/hdpi/npa_preview.png b/resources/images/android/hdpi/npa_preview.png index e37ee9c..6c1a5a4 100644 Binary files a/resources/images/android/hdpi/npa_preview.png and b/resources/images/android/hdpi/npa_preview.png differ diff --git a/resources/images/android/ldpi/npa.png b/resources/images/android/ldpi/npa.png index f9ed660..4deb765 100644 Binary files a/resources/images/android/ldpi/npa.png and b/resources/images/android/ldpi/npa.png differ diff --git a/resources/images/android/ldpi/npa_beta.png b/resources/images/android/ldpi/npa_beta.png index 53f292c..82ac719 100644 Binary files a/resources/images/android/ldpi/npa_beta.png and b/resources/images/android/ldpi/npa_beta.png differ diff --git a/resources/images/android/ldpi/npa_preview.png b/resources/images/android/ldpi/npa_preview.png index a6a0483..8e507b6 100644 Binary files a/resources/images/android/ldpi/npa_preview.png and b/resources/images/android/ldpi/npa_preview.png differ diff --git a/resources/images/android/mdpi/npa.png b/resources/images/android/mdpi/npa.png index d92b9d1..f8a973a 100644 Binary files a/resources/images/android/mdpi/npa.png and b/resources/images/android/mdpi/npa.png differ diff --git a/resources/images/android/mdpi/npa_beta.png b/resources/images/android/mdpi/npa_beta.png index e0e3d42..daae842 100644 Binary files a/resources/images/android/mdpi/npa_beta.png and b/resources/images/android/mdpi/npa_beta.png differ diff --git a/resources/images/android/mdpi/npa_preview.png b/resources/images/android/mdpi/npa_preview.png index 6babd5c..3f43bda 100644 Binary files a/resources/images/android/mdpi/npa_preview.png and b/resources/images/android/mdpi/npa_preview.png differ diff --git a/resources/images/android/navigation/teilen.svg b/resources/images/android/navigation/teilen.svg deleted file mode 100644 index 5528674..0000000 --- a/resources/images/android/navigation/teilen.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/resources/images/android/navigation/tutorial.svg b/resources/images/android/navigation/tutorial.svg new file mode 100644 index 0000000..82e5b2f --- /dev/null +++ b/resources/images/android/navigation/tutorial.svg @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/resources/images/android/navigation/versionsinformation.svg b/resources/images/android/navigation/versionsinformation.svg deleted file mode 100644 index e0634d7..0000000 --- a/resources/images/android/navigation/versionsinformation.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/resources/images/android/tabDivider.svg b/resources/images/android/tabDivider.svg deleted file mode 100644 index cf26d4a..0000000 --- a/resources/images/android/tabDivider.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - diff --git a/resources/images/android/xhdpi/npa.png b/resources/images/android/xhdpi/npa.png index 54ebfaa..02c2f5c 100644 Binary files a/resources/images/android/xhdpi/npa.png and b/resources/images/android/xhdpi/npa.png differ diff --git a/resources/images/android/xhdpi/npa_beta.png b/resources/images/android/xhdpi/npa_beta.png index 6bd309d..e2ca263 100644 Binary files a/resources/images/android/xhdpi/npa_beta.png and b/resources/images/android/xhdpi/npa_beta.png differ diff --git a/resources/images/android/xhdpi/npa_preview.png b/resources/images/android/xhdpi/npa_preview.png index 4e39bff..c88dc5f 100644 Binary files a/resources/images/android/xhdpi/npa_preview.png and b/resources/images/android/xhdpi/npa_preview.png differ diff --git a/resources/images/android/xxhdpi/npa.png b/resources/images/android/xxhdpi/npa.png index cac9cb0..d4ca03c 100644 Binary files a/resources/images/android/xxhdpi/npa.png and b/resources/images/android/xxhdpi/npa.png differ diff --git a/resources/images/android/xxhdpi/npa_beta.png b/resources/images/android/xxhdpi/npa_beta.png index 2a93327..9f19d8c 100644 Binary files a/resources/images/android/xxhdpi/npa_beta.png and b/resources/images/android/xxhdpi/npa_beta.png differ diff --git a/resources/images/android/xxhdpi/npa_preview.png b/resources/images/android/xxhdpi/npa_preview.png index 37d7f97..28b65aa 100644 Binary files a/resources/images/android/xxhdpi/npa_preview.png and b/resources/images/android/xxhdpi/npa_preview.png differ diff --git a/resources/images/android/xxxhdpi/npa.png b/resources/images/android/xxxhdpi/npa.png index 9804a73..fe60df1 100644 Binary files a/resources/images/android/xxxhdpi/npa.png and b/resources/images/android/xxxhdpi/npa.png differ diff --git a/resources/images/android/xxxhdpi/npa_beta.png b/resources/images/android/xxxhdpi/npa_beta.png index 2ec73a1..49a1fa4 100644 Binary files a/resources/images/android/xxxhdpi/npa_beta.png and b/resources/images/android/xxxhdpi/npa_beta.png differ diff --git a/resources/images/android/xxxhdpi/npa_preview.png b/resources/images/android/xxxhdpi/npa_preview.png index 5936f91..26251a9 100644 Binary files a/resources/images/android/xxxhdpi/npa_preview.png and b/resources/images/android/xxxhdpi/npa_preview.png differ diff --git a/resources/images/android/arrowRight.svg b/resources/images/arrowRight.svg similarity index 100% rename from resources/images/android/arrowRight.svg rename to resources/images/arrowRight.svg diff --git a/resources/images/back-chevron.png b/resources/images/back-chevron.png deleted file mode 100644 index 94a20d9..0000000 Binary files a/resources/images/back-chevron.png and /dev/null differ diff --git a/resources/images/bspd1.svg b/resources/images/bspd1.svg deleted file mode 100644 index a7c03ac..0000000 --- a/resources/images/bspd1.svg +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - diff --git a/resources/images/android/search_cancel.svg b/resources/images/cancel.svg similarity index 100% rename from resources/images/android/search_cancel.svg rename to resources/images/cancel.svg diff --git a/resources/images/delete.png b/resources/images/delete.png deleted file mode 100644 index 0ac6348..0000000 Binary files a/resources/images/delete.png and /dev/null differ diff --git a/resources/images/iOS/Header-Ausweisapp@3x.png b/resources/images/desktop/background.png similarity index 100% rename from resources/images/iOS/Header-Ausweisapp@3x.png rename to resources/images/desktop/background.png diff --git a/resources/images/desktop/continue_arrow.svg b/resources/images/desktop/continue_arrow.svg new file mode 100644 index 0000000..90ee763 --- /dev/null +++ b/resources/images/desktop/continue_arrow.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/resources/images/desktop/help_icon.svg b/resources/images/desktop/help_icon.svg new file mode 100644 index 0000000..9e16901 --- /dev/null +++ b/resources/images/desktop/help_icon.svg @@ -0,0 +1,6 @@ + + + + diff --git a/resources/images/desktop/main_history.svg b/resources/images/desktop/main_history.svg new file mode 100644 index 0000000..64e1b68 --- /dev/null +++ b/resources/images/desktop/main_history.svg @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/resources/images/desktop/main_identify.svg b/resources/images/desktop/main_identify.svg new file mode 100644 index 0000000..71b841c --- /dev/null +++ b/resources/images/desktop/main_identify.svg @@ -0,0 +1,9 @@ + + + + + + diff --git a/resources/images/desktop/main_pin.svg b/resources/images/desktop/main_pin.svg new file mode 100644 index 0000000..113cff4 --- /dev/null +++ b/resources/images/desktop/main_pin.svg @@ -0,0 +1,9 @@ + + + + + + diff --git a/resources/images/desktop/main_provider.svg b/resources/images/desktop/main_provider.svg new file mode 100644 index 0000000..2252adb --- /dev/null +++ b/resources/images/desktop/main_provider.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/resources/images/desktop/sandglass.svg b/resources/images/desktop/sandglass.svg new file mode 100755 index 0000000..b0a3f3a --- /dev/null +++ b/resources/images/desktop/sandglass.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/resources/images/desktop/settings_icon.svg b/resources/images/desktop/settings_icon.svg new file mode 100644 index 0000000..6de9512 --- /dev/null +++ b/resources/images/desktop/settings_icon.svg @@ -0,0 +1,80 @@ + + + + + + + + diff --git a/resources/images/desktop/titlebar_arrow.svg b/resources/images/desktop/titlebar_arrow.svg new file mode 100644 index 0000000..64838f7 --- /dev/null +++ b/resources/images/desktop/titlebar_arrow.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/resources/images/green_check_mark.svg b/resources/images/green_check_mark.svg deleted file mode 100755 index 9ebfea5..0000000 --- a/resources/images/green_check_mark.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/resources/images/gruener_Haken.svg b/resources/images/gruener_Haken.svg deleted file mode 100755 index 59e2423..0000000 --- a/resources/images/gruener_Haken.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - diff --git a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon1024.png b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon1024.png index 6c006dd..20e8cba 100644 Binary files a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon1024.png and b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon1024.png differ diff --git a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20.png b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20.png index fd50e25..2509bd6 100644 Binary files a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20.png and b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20.png differ diff --git a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20@2x.png b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20@2x.png index be8af9c..23f6f0d 100644 Binary files a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20@2x.png and b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20@2x.png differ diff --git a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20@3x.png b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20@3x.png index eeaaec4..9145e70 100644 Binary files a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20@3x.png and b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20@3x.png differ diff --git a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon60@2x.png b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon60@2x.png index 6f82d20..863ded6 100644 Binary files a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon60@2x.png and b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon60@2x.png differ diff --git a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon60@3x.png b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon60@3x.png index 415d941..bc99dc2 100644 Binary files a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon60@3x.png and b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon60@3x.png differ diff --git a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon76.png b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon76.png index 7963669..9c824ad 100644 Binary files a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon76.png and b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon76.png differ diff --git a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon76@2x.png b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon76@2x.png index 796f589..505dd27 100644 Binary files a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon76@2x.png and b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon76@2x.png differ diff --git a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon83.5@2x.png b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon83.5@2x.png index 06dc3bb..15eb314 100644 Binary files a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon83.5@2x.png and b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon83.5@2x.png differ diff --git a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall.png b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall.png index 5428afb..a31b807 100644 Binary files a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall.png and b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall.png differ diff --git a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall40.png b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall40.png index 1a967c2..feea787 100644 Binary files a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall40.png and b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall40.png differ diff --git a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall40@2x.png b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall40@2x.png index 93e1942..bad568f 100644 Binary files a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall40@2x.png and b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall40@2x.png differ diff --git a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall40@3x.png b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall40@3x.png index 6f82d20..863ded6 100644 Binary files a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall40@3x.png and b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall40@3x.png differ diff --git a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall@2x.png b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall@2x.png index 79e6754..a72a7e8 100644 Binary files a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall@2x.png and b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall@2x.png differ diff --git a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall@3x.png b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall@3x.png index 8e66b90..72ea975 100644 Binary files a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall@3x.png and b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall@3x.png differ diff --git a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon1024.png b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon1024.png index 86a7efd..683b4c2 100644 Binary files a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon1024.png and b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon1024.png differ diff --git a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon20.png b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon20.png index ab2797f..6f016d6 100644 Binary files a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon20.png and b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon20.png differ diff --git a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon20@2x.png b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon20@2x.png index 45db7be..5e08203 100644 Binary files a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon20@2x.png and b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon20@2x.png differ diff --git a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon20@3x.png b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon20@3x.png index 0fe9859..8ca64e6 100644 Binary files a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon20@3x.png and b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon20@3x.png differ diff --git a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon60@2x.png b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon60@2x.png index c66dd0b..4765459 100644 Binary files a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon60@2x.png and b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon60@2x.png differ diff --git a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon60@3x.png b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon60@3x.png index 0e33ae8..cb4dc95 100644 Binary files a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon60@3x.png and b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon60@3x.png differ diff --git a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon76.png b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon76.png index 0394f1f..d1e6f96 100644 Binary files a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon76.png and b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon76.png differ diff --git a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon76@2x.png b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon76@2x.png index acbc9bc..0d3c796 100644 Binary files a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon76@2x.png and b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon76@2x.png differ diff --git a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon83.5@2x.png b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon83.5@2x.png index 931fb4f..759f0f2 100644 Binary files a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon83.5@2x.png and b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon83.5@2x.png differ diff --git a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall.png b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall.png index 1d44829..db44873 100644 Binary files a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall.png and b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall.png differ diff --git a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall40.png b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall40.png index a1e3f67..5e08203 100644 Binary files a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall40.png and b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall40.png differ diff --git a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall40@2x.png b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall40@2x.png index 3f19d76..8171cb0 100644 Binary files a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall40@2x.png and b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall40@2x.png differ diff --git a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall40@3x.png b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall40@3x.png index c66dd0b..4765459 100644 Binary files a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall40@3x.png and b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall40@3x.png differ diff --git a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall@2x.png b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall@2x.png index 37e8b9b..9066b7f 100644 Binary files a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall@2x.png and b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall@2x.png differ diff --git a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall@3x.png b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall@3x.png index d3a0140..23c131b 100644 Binary files a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall@3x.png and b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall@3x.png differ diff --git a/resources/images/iOS/arrowLeft.svg b/resources/images/iOS/arrowLeft.svg deleted file mode 100644 index 37ac0c1..0000000 --- a/resources/images/iOS/arrowLeft.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - diff --git a/resources/images/iOS/arrowRight.svg b/resources/images/iOS/arrowRight.svg deleted file mode 100644 index 8cd0e02..0000000 --- a/resources/images/iOS/arrowRight.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - diff --git a/resources/images/iOS/launchImages/Default-568h@2x.png b/resources/images/iOS/launchImages/Default-568h@2x.png index 3e96d42..9ea220a 100644 Binary files a/resources/images/iOS/launchImages/Default-568h@2x.png and b/resources/images/iOS/launchImages/Default-568h@2x.png differ diff --git a/resources/images/iOS/list_item_arrow.svg b/resources/images/iOS/list_item_arrow.svg deleted file mode 100644 index 78403c8..0000000 --- a/resources/images/iOS/list_item_arrow.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - diff --git a/resources/images/iOS/more/icon_mehr_log.svg b/resources/images/iOS/more/icon_mehr_log.svg new file mode 100644 index 0000000..cac5ce0 --- /dev/null +++ b/resources/images/iOS/more/icon_mehr_log.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + diff --git a/resources/images/iOS/more/icon_mehr_tutorial.svg b/resources/images/iOS/more/icon_mehr_tutorial.svg new file mode 100644 index 0000000..6b50646 --- /dev/null +++ b/resources/images/iOS/more/icon_mehr_tutorial.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + diff --git a/resources/images/iOS/radio_button_check_ios.svg b/resources/images/iOS/radio_button_check_ios.svg deleted file mode 100644 index fac6a61..0000000 --- a/resources/images/iOS/radio_button_check_ios.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - diff --git a/resources/images/iOS/toggle_disabled.png b/resources/images/iOS/toggle_disabled.png deleted file mode 100644 index b7b3102..0000000 Binary files a/resources/images/iOS/toggle_disabled.png and /dev/null differ diff --git a/resources/images/iOS/toggle_off.png b/resources/images/iOS/toggle_off.png deleted file mode 100644 index d392888..0000000 Binary files a/resources/images/iOS/toggle_off.png and /dev/null differ diff --git a/resources/images/iOS/toggle_on.png b/resources/images/iOS/toggle_on.png deleted file mode 100644 index 4d1ebde..0000000 Binary files a/resources/images/iOS/toggle_on.png and /dev/null differ diff --git a/resources/images/icon_pair.svg b/resources/images/icon_pair.svg new file mode 100644 index 0000000..54005a3 --- /dev/null +++ b/resources/images/icon_pair.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/resources/images/icon_settings.svg b/resources/images/icon_settings.svg new file mode 100644 index 0000000..0f4b57b --- /dev/null +++ b/resources/images/icon_settings.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + diff --git a/resources/images/padlock.svg b/resources/images/padlock.svg deleted file mode 100644 index d7ee69c..0000000 --- a/resources/images/padlock.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - diff --git a/resources/images/padlock_empty.svg b/resources/images/padlock_empty.svg deleted file mode 100644 index 4090a66..0000000 --- a/resources/images/padlock_empty.svg +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/resources/images/reader/default_more_reader.png b/resources/images/reader/default_more_reader.png old mode 100755 new mode 100644 index ebacd8c..d0fd5ca Binary files a/resources/images/reader/default_more_reader.png and b/resources/images/reader/default_more_reader.png differ diff --git a/resources/images/reader/default_reader.png b/resources/images/reader/default_reader.png old mode 100755 new mode 100644 index ebacd8c..d0fd5ca Binary files a/resources/images/reader/default_reader.png and b/resources/images/reader/default_reader.png differ diff --git a/resources/images/reader/src/img_Personalausweis.png b/resources/images/reader/src/img_Personalausweis.png index a80502e..448ed85 100644 Binary files a/resources/images/reader/src/img_Personalausweis.png and b/resources/images/reader/src/img_Personalausweis.png differ diff --git a/resources/images/reader/src/img_Remote_Display.png b/resources/images/reader/src/img_Remote_Display.png old mode 100755 new mode 100644 index b919fac..bb30187 Binary files a/resources/images/reader/src/img_Remote_Display.png and b/resources/images/reader/src/img_Remote_Display.png differ diff --git a/resources/images/rotes_X.svg b/resources/images/rotes_X.svg deleted file mode 100644 index 65c75c9..0000000 --- a/resources/images/rotes_X.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - diff --git a/resources/images/android/search_icon.svg b/resources/images/search.svg similarity index 100% rename from resources/images/android/search_icon.svg rename to resources/images/search.svg diff --git a/resources/images/share.svg b/resources/images/share.svg new file mode 100644 index 0000000..92e7ebb --- /dev/null +++ b/resources/images/share.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/resources/images/status_error.svg b/resources/images/status_error.svg new file mode 100644 index 0000000..ce3d085 --- /dev/null +++ b/resources/images/status_error.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/resources/images/status_info.svg b/resources/images/status_info.svg new file mode 100644 index 0000000..3a8f587 --- /dev/null +++ b/resources/images/status_info.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/resources/images/status_ok.svg b/resources/images/status_ok.svg new file mode 100755 index 0000000..1d83b6a --- /dev/null +++ b/resources/images/status_ok.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/resources/images/submit.png b/resources/images/submit.png deleted file mode 100644 index 5e36bb7..0000000 Binary files a/resources/images/submit.png and /dev/null differ diff --git a/resources/images/trash_icon.svg b/resources/images/trash_icon.svg index 4ed5556..9d360a0 100644 --- a/resources/images/trash_icon.svg +++ b/resources/images/trash_icon.svg @@ -4,4 +4,5 @@ - \ No newline at end of file + + diff --git a/resources/images/trash_icon_all.svg b/resources/images/trash_icon_all.svg new file mode 100644 index 0000000..9371891 --- /dev/null +++ b/resources/images/trash_icon_all.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/resources/images/trash_icon_white.svg b/resources/images/trash_icon_white.svg new file mode 100644 index 0000000..e0014f0 --- /dev/null +++ b/resources/images/trash_icon_white.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/resources/images/tutorial/arrow_blue.svg b/resources/images/tutorial/arrow_blue.svg new file mode 100644 index 0000000..a6b6125 --- /dev/null +++ b/resources/images/tutorial/arrow_blue.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/resources/images/tutorial/arrows.svg b/resources/images/tutorial/arrows.svg new file mode 100644 index 0000000..cb955c0 --- /dev/null +++ b/resources/images/tutorial/arrows.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/images/tutorial/bluetooth.svg b/resources/images/tutorial/bluetooth.svg new file mode 100644 index 0000000..d0c8ab8 --- /dev/null +++ b/resources/images/tutorial/bluetooth.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/images/tutorial/button_de.png b/resources/images/tutorial/button_de.png new file mode 100644 index 0000000..1ae97dc Binary files /dev/null and b/resources/images/tutorial/button_de.png differ diff --git a/resources/images/tutorial/button_en.png b/resources/images/tutorial/button_en.png new file mode 100644 index 0000000..cd48a74 Binary files /dev/null and b/resources/images/tutorial/button_en.png differ diff --git a/resources/images/tutorial/bva.svg b/resources/images/tutorial/bva.svg new file mode 100644 index 0000000..11f3c24 --- /dev/null +++ b/resources/images/tutorial/bva.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/resources/images/tutorial/check.svg b/resources/images/tutorial/check.svg new file mode 100644 index 0000000..df7b14f --- /dev/null +++ b/resources/images/tutorial/check.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/images/tutorial/circle-1.svg b/resources/images/tutorial/circle-1.svg new file mode 100644 index 0000000..6e764ff --- /dev/null +++ b/resources/images/tutorial/circle-1.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/resources/images/tutorial/circle-2.svg b/resources/images/tutorial/circle-2.svg new file mode 100644 index 0000000..f2dac72 --- /dev/null +++ b/resources/images/tutorial/circle-2.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/resources/images/tutorial/circle-3.svg b/resources/images/tutorial/circle-3.svg new file mode 100644 index 0000000..798844e --- /dev/null +++ b/resources/images/tutorial/circle-3.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/resources/images/tutorial/circle-4.svg b/resources/images/tutorial/circle-4.svg new file mode 100644 index 0000000..61edd80 --- /dev/null +++ b/resources/images/tutorial/circle-4.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/resources/images/tutorial/circle-lock-2.svg b/resources/images/tutorial/circle-lock-2.svg new file mode 100644 index 0000000..f917a70 --- /dev/null +++ b/resources/images/tutorial/circle-lock-2.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/resources/images/tutorial/circle-lock.svg b/resources/images/tutorial/circle-lock.svg new file mode 100644 index 0000000..0c1b37c --- /dev/null +++ b/resources/images/tutorial/circle-lock.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/resources/images/tutorial/click.svg b/resources/images/tutorial/click.svg new file mode 100644 index 0000000..f67b5ab --- /dev/null +++ b/resources/images/tutorial/click.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/images/tutorial/desktop.svg b/resources/images/tutorial/desktop.svg new file mode 100644 index 0000000..af7c501 --- /dev/null +++ b/resources/images/tutorial/desktop.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/images/tutorial/generated/how_desktop.svg b/resources/images/tutorial/generated/how_desktop.svg new file mode 100644 index 0000000..d04c9e8 --- /dev/null +++ b/resources/images/tutorial/generated/how_desktop.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/images/tutorial/generated/how_device_lineup.svg b/resources/images/tutorial/generated/how_device_lineup.svg new file mode 100644 index 0000000..8c29283 --- /dev/null +++ b/resources/images/tutorial/generated/how_device_lineup.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/images/tutorial/generated/how_form_no_fun.svg b/resources/images/tutorial/generated/how_form_no_fun.svg new file mode 100644 index 0000000..5b1a737 --- /dev/null +++ b/resources/images/tutorial/generated/how_form_no_fun.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/resources/images/tutorial/generated/how_idcard_cycle.svg b/resources/images/tutorial/generated/how_idcard_cycle.svg new file mode 100644 index 0000000..df4a3ce --- /dev/null +++ b/resources/images/tutorial/generated/how_idcard_cycle.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/resources/images/tutorial/generated/how_method_bluetooth.svg b/resources/images/tutorial/generated/how_method_bluetooth.svg new file mode 100644 index 0000000..03acc30 --- /dev/null +++ b/resources/images/tutorial/generated/how_method_bluetooth.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/resources/images/tutorial/generated/how_method_nfc.svg b/resources/images/tutorial/generated/how_method_nfc.svg new file mode 100644 index 0000000..96f4930 --- /dev/null +++ b/resources/images/tutorial/generated/how_method_nfc.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/resources/images/tutorial/generated/how_method_sac_desktop.svg b/resources/images/tutorial/generated/how_method_sac_desktop.svg new file mode 100644 index 0000000..2239b3b --- /dev/null +++ b/resources/images/tutorial/generated/how_method_sac_desktop.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/resources/images/tutorial/generated/how_method_sac_mobile.svg b/resources/images/tutorial/generated/how_method_sac_mobile.svg new file mode 100644 index 0000000..0d685d3 --- /dev/null +++ b/resources/images/tutorial/generated/how_method_sac_mobile.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/resources/images/tutorial/generated/how_questions_everywhere.svg b/resources/images/tutorial/generated/how_questions_everywhere.svg new file mode 100644 index 0000000..d20926d --- /dev/null +++ b/resources/images/tutorial/generated/how_questions_everywhere.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/resources/images/tutorial/generated/important_lets_go.svg b/resources/images/tutorial/generated/important_lets_go.svg new file mode 100644 index 0000000..d89b57d --- /dev/null +++ b/resources/images/tutorial/generated/important_lets_go.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/resources/images/tutorial/generated/important_pin5.svg b/resources/images/tutorial/generated/important_pin5.svg new file mode 100644 index 0000000..7ac62bc --- /dev/null +++ b/resources/images/tutorial/generated/important_pin5.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/resources/images/tutorial/generated/important_pin6.svg b/resources/images/tutorial/generated/important_pin6.svg new file mode 100644 index 0000000..fa61cc9 --- /dev/null +++ b/resources/images/tutorial/generated/important_pin6.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/resources/images/tutorial/generated/important_space_questionmark.svg b/resources/images/tutorial/generated/important_space_questionmark.svg new file mode 100644 index 0000000..8d11b55 --- /dev/null +++ b/resources/images/tutorial/generated/important_space_questionmark.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/resources/images/tutorial/generated/reader_bluetooth_card_inserted.svg b/resources/images/tutorial/generated/reader_bluetooth_card_inserted.svg new file mode 100644 index 0000000..80691e6 --- /dev/null +++ b/resources/images/tutorial/generated/reader_bluetooth_card_inserted.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/resources/images/tutorial/generated/reader_bluetooth_connection.svg b/resources/images/tutorial/generated/reader_bluetooth_connection.svg new file mode 100644 index 0000000..36bb2ed --- /dev/null +++ b/resources/images/tutorial/generated/reader_bluetooth_connection.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/resources/images/tutorial/generated/reader_nfc_finished.svg b/resources/images/tutorial/generated/reader_nfc_finished.svg new file mode 100644 index 0000000..ebc1c94 --- /dev/null +++ b/resources/images/tutorial/generated/reader_nfc_finished.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/resources/images/tutorial/generated/reader_nfc_npa_on_smartphone.svg b/resources/images/tutorial/generated/reader_nfc_npa_on_smartphone.svg new file mode 100644 index 0000000..2ea871d --- /dev/null +++ b/resources/images/tutorial/generated/reader_nfc_npa_on_smartphone.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/resources/images/tutorial/generated/reader_nfc_pin6.svg b/resources/images/tutorial/generated/reader_nfc_pin6.svg new file mode 100644 index 0000000..39f88e1 --- /dev/null +++ b/resources/images/tutorial/generated/reader_nfc_pin6.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/images/tutorial/generated/reader_nfc_provider_on_smartphone.svg b/resources/images/tutorial/generated/reader_nfc_provider_on_smartphone.svg new file mode 100644 index 0000000..5aff152 --- /dev/null +++ b/resources/images/tutorial/generated/reader_nfc_provider_on_smartphone.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/resources/images/tutorial/generated/reader_nfc_smartphone_nfc_position.svg b/resources/images/tutorial/generated/reader_nfc_smartphone_nfc_position.svg new file mode 100644 index 0000000..0c1e07f --- /dev/null +++ b/resources/images/tutorial/generated/reader_nfc_smartphone_nfc_position.svg @@ -0,0 +1,24 @@ + + + + + NFC? + NFC? + NFC? + NFC? + diff --git a/resources/images/tutorial/generated/reader_nfc_userdata_example_de.svg b/resources/images/tutorial/generated/reader_nfc_userdata_example_de.svg new file mode 100644 index 0000000..8279abd --- /dev/null +++ b/resources/images/tutorial/generated/reader_nfc_userdata_example_de.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + Zum Beispiel + Name? + Vorname? + Geburtsdatum? + Adresse? + diff --git a/resources/images/tutorial/generated/reader_nfc_userdata_example_en.svg b/resources/images/tutorial/generated/reader_nfc_userdata_example_en.svg new file mode 100644 index 0000000..37a7e76 --- /dev/null +++ b/resources/images/tutorial/generated/reader_nfc_userdata_example_en.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + For Example + Surname? + Given Name? + Date of Birth? + Address? + diff --git a/resources/images/tutorial/generated/reader_sac_aa2_ok.svg b/resources/images/tutorial/generated/reader_sac_aa2_ok.svg new file mode 100644 index 0000000..ee31ab7 --- /dev/null +++ b/resources/images/tutorial/generated/reader_sac_aa2_ok.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/resources/images/tutorial/generated/reader_sac_menu_de.svg b/resources/images/tutorial/generated/reader_sac_menu_de.svg new file mode 100644 index 0000000..7700d27 --- /dev/null +++ b/resources/images/tutorial/generated/reader_sac_menu_de.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/resources/images/tutorial/generated/reader_sac_menu_en.svg b/resources/images/tutorial/generated/reader_sac_menu_en.svg new file mode 100644 index 0000000..0c4f728 --- /dev/null +++ b/resources/images/tutorial/generated/reader_sac_menu_en.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/resources/images/tutorial/generated/reader_sac_no_nfc_devices.svg b/resources/images/tutorial/generated/reader_sac_no_nfc_devices.svg new file mode 100644 index 0000000..d89c18d --- /dev/null +++ b/resources/images/tutorial/generated/reader_sac_no_nfc_devices.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/images/tutorial/generated/reader_sac_no_nfc_provider.svg b/resources/images/tutorial/generated/reader_sac_no_nfc_provider.svg new file mode 100644 index 0000000..ad26567 --- /dev/null +++ b/resources/images/tutorial/generated/reader_sac_no_nfc_provider.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/images/tutorial/generated/reader_sac_npa_on_laptop.svg b/resources/images/tutorial/generated/reader_sac_npa_on_laptop.svg new file mode 100644 index 0000000..318a2b8 --- /dev/null +++ b/resources/images/tutorial/generated/reader_sac_npa_on_laptop.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/resources/images/tutorial/generated/reader_sac_provider_on_laptop.svg b/resources/images/tutorial/generated/reader_sac_provider_on_laptop.svg new file mode 100644 index 0000000..029f402 --- /dev/null +++ b/resources/images/tutorial/generated/reader_sac_provider_on_laptop.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/resources/images/tutorial/generated/where_identify_now_de.svg b/resources/images/tutorial/generated/where_identify_now_de.svg new file mode 100644 index 0000000..ba51b50 --- /dev/null +++ b/resources/images/tutorial/generated/where_identify_now_de.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/resources/images/tutorial/generated/where_identify_now_en.svg b/resources/images/tutorial/generated/where_identify_now_en.svg new file mode 100644 index 0000000..0f308f3 --- /dev/null +++ b/resources/images/tutorial/generated/where_identify_now_en.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/resources/images/tutorial/generated/where_lay_down_id.svg b/resources/images/tutorial/generated/where_lay_down_id.svg new file mode 100644 index 0000000..d3693bd --- /dev/null +++ b/resources/images/tutorial/generated/where_lay_down_id.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/resources/images/tutorial/generated/where_overview_question.svg b/resources/images/tutorial/generated/where_overview_question.svg new file mode 100644 index 0000000..1ea069f --- /dev/null +++ b/resources/images/tutorial/generated/where_overview_question.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/images/tutorial/generated/where_pin6.svg b/resources/images/tutorial/generated/where_pin6.svg new file mode 100644 index 0000000..8fc336f --- /dev/null +++ b/resources/images/tutorial/generated/where_pin6.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/resources/images/tutorial/generated/where_providerlist_screenshot_de.svg b/resources/images/tutorial/generated/where_providerlist_screenshot_de.svg new file mode 100644 index 0000000..8977815 --- /dev/null +++ b/resources/images/tutorial/generated/where_providerlist_screenshot_de.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/resources/images/tutorial/generated/where_providerlist_screenshot_en.svg b/resources/images/tutorial/generated/where_providerlist_screenshot_en.svg new file mode 100644 index 0000000..420d9b8 --- /dev/null +++ b/resources/images/tutorial/generated/where_providerlist_screenshot_en.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/resources/images/tutorial/generated/where_userdata_example_de.svg b/resources/images/tutorial/generated/where_userdata_example_de.svg new file mode 100644 index 0000000..5b1d34b --- /dev/null +++ b/resources/images/tutorial/generated/where_userdata_example_de.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + Zum Beispiel + Name? + Vorname? + Geburtsdatum? + Adresse? + diff --git a/resources/images/tutorial/generated/where_userdata_example_en.svg b/resources/images/tutorial/generated/where_userdata_example_en.svg new file mode 100644 index 0000000..c3d77f3 --- /dev/null +++ b/resources/images/tutorial/generated/where_userdata_example_en.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + For Example + Surname? + Given Name? + Date of Birth? + Address? + diff --git a/resources/images/tutorial/hand.svg b/resources/images/tutorial/hand.svg new file mode 100644 index 0000000..87eb6bb --- /dev/null +++ b/resources/images/tutorial/hand.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/resources/images/tutorial/hint.svg b/resources/images/tutorial/hint.svg new file mode 100644 index 0000000..3a46e88 --- /dev/null +++ b/resources/images/tutorial/hint.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/resources/images/tutorial/idcard.svg b/resources/images/tutorial/idcard.svg new file mode 100644 index 0000000..d83f080 --- /dev/null +++ b/resources/images/tutorial/idcard.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/resources/images/tutorial/identify.svg b/resources/images/tutorial/identify.svg new file mode 100644 index 0000000..b391d9f --- /dev/null +++ b/resources/images/tutorial/identify.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/images/tutorial/laptop.svg b/resources/images/tutorial/laptop.svg new file mode 100644 index 0000000..abb0e0a --- /dev/null +++ b/resources/images/tutorial/laptop.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/resources/images/tutorial/letters.svg b/resources/images/tutorial/letters.svg new file mode 100644 index 0000000..d92c692 --- /dev/null +++ b/resources/images/tutorial/letters.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/images/tutorial/main_menu_how_caret.svg b/resources/images/tutorial/main_menu_how_caret.svg new file mode 100644 index 0000000..ecb1aef --- /dev/null +++ b/resources/images/tutorial/main_menu_how_caret.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/resources/images/tutorial/main_menu_important_caret.svg b/resources/images/tutorial/main_menu_important_caret.svg new file mode 100644 index 0000000..8ccd62d --- /dev/null +++ b/resources/images/tutorial/main_menu_important_caret.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/resources/images/tutorial/main_menu_what_caret.svg b/resources/images/tutorial/main_menu_what_caret.svg new file mode 100644 index 0000000..faf15ce --- /dev/null +++ b/resources/images/tutorial/main_menu_what_caret.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/resources/images/tutorial/main_menu_where_caret.svg b/resources/images/tutorial/main_menu_where_caret.svg new file mode 100644 index 0000000..5f1eabe --- /dev/null +++ b/resources/images/tutorial/main_menu_where_caret.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/resources/images/tutorial/nfc.svg b/resources/images/tutorial/nfc.svg new file mode 100644 index 0000000..468e070 --- /dev/null +++ b/resources/images/tutorial/nfc.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/resources/images/tutorial/no-nfc.svg b/resources/images/tutorial/no-nfc.svg new file mode 100644 index 0000000..86ced3a --- /dev/null +++ b/resources/images/tutorial/no-nfc.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/resources/images/tutorial/phone-screen.svg b/resources/images/tutorial/phone-screen.svg new file mode 100644 index 0000000..0515b8a --- /dev/null +++ b/resources/images/tutorial/phone-screen.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/resources/images/tutorial/phone.svg b/resources/images/tutorial/phone.svg new file mode 100644 index 0000000..32ff6bd --- /dev/null +++ b/resources/images/tutorial/phone.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/images/tutorial/phone_list.svg b/resources/images/tutorial/phone_list.svg new file mode 100755 index 0000000..ea011fc --- /dev/null +++ b/resources/images/tutorial/phone_list.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/resources/images/tutorial/phone_screen_de.jpg b/resources/images/tutorial/phone_screen_de.jpg new file mode 100644 index 0000000..2ae2b87 Binary files /dev/null and b/resources/images/tutorial/phone_screen_de.jpg differ diff --git a/resources/images/tutorial/phone_screen_en.jpg b/resources/images/tutorial/phone_screen_en.jpg new file mode 100644 index 0000000..0af3ba0 Binary files /dev/null and b/resources/images/tutorial/phone_screen_en.jpg differ diff --git a/resources/images/tutorial/pin-5@2x.png b/resources/images/tutorial/pin-5@2x.png new file mode 100644 index 0000000..06b5ba3 Binary files /dev/null and b/resources/images/tutorial/pin-5@2x.png differ diff --git a/resources/images/tutorial/pin-6@2x.png b/resources/images/tutorial/pin-6@2x.png new file mode 100644 index 0000000..9c5c09b Binary files /dev/null and b/resources/images/tutorial/pin-6@2x.png differ diff --git a/resources/images/tutorial/play_movie.png b/resources/images/tutorial/play_movie.png new file mode 100644 index 0000000..af0e3bf Binary files /dev/null and b/resources/images/tutorial/play_movie.png differ diff --git a/resources/images/tutorial/provider_home.svg b/resources/images/tutorial/provider_home.svg new file mode 100644 index 0000000..05c0274 --- /dev/null +++ b/resources/images/tutorial/provider_home.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/resources/images/tutorial/providericons.png b/resources/images/tutorial/providericons.png new file mode 100644 index 0000000..2ec75f2 Binary files /dev/null and b/resources/images/tutorial/providericons.png differ diff --git a/resources/images/tutorial/questionmark.svg b/resources/images/tutorial/questionmark.svg new file mode 100644 index 0000000..66abe3d --- /dev/null +++ b/resources/images/tutorial/questionmark.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/images/tutorial/reader.svg b/resources/images/tutorial/reader.svg new file mode 100644 index 0000000..afd7975 --- /dev/null +++ b/resources/images/tutorial/reader.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/resources/images/tutorial/rectangles.svg b/resources/images/tutorial/rectangles.svg new file mode 100644 index 0000000..51132d2 --- /dev/null +++ b/resources/images/tutorial/rectangles.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/resources/images/tutorial/save.svg b/resources/images/tutorial/save.svg new file mode 100644 index 0000000..81e1e27 --- /dev/null +++ b/resources/images/tutorial/save.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/resources/images/tutorial/screenshot_cert_de.png b/resources/images/tutorial/screenshot_cert_de.png new file mode 100644 index 0000000..9215ebd Binary files /dev/null and b/resources/images/tutorial/screenshot_cert_de.png differ diff --git a/resources/images/tutorial/screenshot_cert_en.png b/resources/images/tutorial/screenshot_cert_en.png new file mode 100644 index 0000000..06d6687 Binary files /dev/null and b/resources/images/tutorial/screenshot_cert_en.png differ diff --git a/resources/images/tutorial/screenshot_choose_reader_de.png b/resources/images/tutorial/screenshot_choose_reader_de.png new file mode 100644 index 0000000..1aed789 Binary files /dev/null and b/resources/images/tutorial/screenshot_choose_reader_de.png differ diff --git a/resources/images/tutorial/screenshot_choose_reader_en.png b/resources/images/tutorial/screenshot_choose_reader_en.png new file mode 100644 index 0000000..71a3156 Binary files /dev/null and b/resources/images/tutorial/screenshot_choose_reader_en.png differ diff --git a/resources/images/tutorial/screenshot_menu_providerlist_de.png b/resources/images/tutorial/screenshot_menu_providerlist_de.png new file mode 100644 index 0000000..4c29d09 Binary files /dev/null and b/resources/images/tutorial/screenshot_menu_providerlist_de.png differ diff --git a/resources/images/tutorial/screenshot_menu_providerlist_en.png b/resources/images/tutorial/screenshot_menu_providerlist_en.png new file mode 100644 index 0000000..030c303 Binary files /dev/null and b/resources/images/tutorial/screenshot_menu_providerlist_en.png differ diff --git a/resources/images/tutorial/screenshot_pairing_de.png b/resources/images/tutorial/screenshot_pairing_de.png new file mode 100644 index 0000000..ce26346 Binary files /dev/null and b/resources/images/tutorial/screenshot_pairing_de.png differ diff --git a/resources/images/tutorial/screenshot_pairing_en.png b/resources/images/tutorial/screenshot_pairing_en.png new file mode 100644 index 0000000..85933f3 Binary files /dev/null and b/resources/images/tutorial/screenshot_pairing_en.png differ diff --git a/resources/images/tutorial/screenshot_pin_management_menu_de.png b/resources/images/tutorial/screenshot_pin_management_menu_de.png new file mode 100644 index 0000000..89cc3f3 Binary files /dev/null and b/resources/images/tutorial/screenshot_pin_management_menu_de.png differ diff --git a/resources/images/tutorial/screenshot_pin_management_menu_en.png b/resources/images/tutorial/screenshot_pin_management_menu_en.png new file mode 100644 index 0000000..1280605 Binary files /dev/null and b/resources/images/tutorial/screenshot_pin_management_menu_en.png differ diff --git a/resources/images/tutorial/screenshot_providerlist_de.png b/resources/images/tutorial/screenshot_providerlist_de.png new file mode 100644 index 0000000..d42b0aa Binary files /dev/null and b/resources/images/tutorial/screenshot_providerlist_de.png differ diff --git a/resources/images/tutorial/screenshot_providerlist_en.png b/resources/images/tutorial/screenshot_providerlist_en.png new file mode 100644 index 0000000..c1346c1 Binary files /dev/null and b/resources/images/tutorial/screenshot_providerlist_en.png differ diff --git a/resources/images/tutorial/screenshot_sac_menu_de.png b/resources/images/tutorial/screenshot_sac_menu_de.png new file mode 100644 index 0000000..85884fc Binary files /dev/null and b/resources/images/tutorial/screenshot_sac_menu_de.png differ diff --git a/resources/images/tutorial/screenshot_sac_menu_en.png b/resources/images/tutorial/screenshot_sac_menu_en.png new file mode 100644 index 0000000..1bb3b25 Binary files /dev/null and b/resources/images/tutorial/screenshot_sac_menu_en.png differ diff --git a/resources/images/tutorial/section_seperator_how.svg b/resources/images/tutorial/section_seperator_how.svg new file mode 100644 index 0000000..d3b4c02 --- /dev/null +++ b/resources/images/tutorial/section_seperator_how.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/resources/images/tutorial/section_seperator_important.svg b/resources/images/tutorial/section_seperator_important.svg new file mode 100644 index 0000000..7975506 --- /dev/null +++ b/resources/images/tutorial/section_seperator_important.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/resources/images/tutorial/section_seperator_what.svg b/resources/images/tutorial/section_seperator_what.svg new file mode 100644 index 0000000..d4a02a3 --- /dev/null +++ b/resources/images/tutorial/section_seperator_what.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/resources/images/tutorial/section_seperator_where.svg b/resources/images/tutorial/section_seperator_where.svg new file mode 100644 index 0000000..690ce66 --- /dev/null +++ b/resources/images/tutorial/section_seperator_where.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/resources/images/tutorial/src/how_desktop.svg b/resources/images/tutorial/src/how_desktop.svg new file mode 100644 index 0000000..6a23bb9 --- /dev/null +++ b/resources/images/tutorial/src/how_desktop.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/images/tutorial/src/how_device_lineup.svg b/resources/images/tutorial/src/how_device_lineup.svg new file mode 100644 index 0000000..2dde5f2 --- /dev/null +++ b/resources/images/tutorial/src/how_device_lineup.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/images/tutorial/src/how_form_no_fun.svg b/resources/images/tutorial/src/how_form_no_fun.svg new file mode 100644 index 0000000..bd836a7 --- /dev/null +++ b/resources/images/tutorial/src/how_form_no_fun.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/resources/images/tutorial/src/how_idcard_cycle.svg b/resources/images/tutorial/src/how_idcard_cycle.svg new file mode 100644 index 0000000..b69f49f --- /dev/null +++ b/resources/images/tutorial/src/how_idcard_cycle.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/resources/images/tutorial/src/how_method_bluetooth.svg b/resources/images/tutorial/src/how_method_bluetooth.svg new file mode 100644 index 0000000..bc60bc5 --- /dev/null +++ b/resources/images/tutorial/src/how_method_bluetooth.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/resources/images/tutorial/src/how_method_nfc.svg b/resources/images/tutorial/src/how_method_nfc.svg new file mode 100644 index 0000000..e1ca760 --- /dev/null +++ b/resources/images/tutorial/src/how_method_nfc.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/resources/images/tutorial/src/how_method_sac_desktop.svg b/resources/images/tutorial/src/how_method_sac_desktop.svg new file mode 100644 index 0000000..732f480 --- /dev/null +++ b/resources/images/tutorial/src/how_method_sac_desktop.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/resources/images/tutorial/src/how_method_sac_mobile.svg b/resources/images/tutorial/src/how_method_sac_mobile.svg new file mode 100644 index 0000000..3f2276b --- /dev/null +++ b/resources/images/tutorial/src/how_method_sac_mobile.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/resources/images/tutorial/src/how_questions_everywhere.svg b/resources/images/tutorial/src/how_questions_everywhere.svg new file mode 100644 index 0000000..74489d4 --- /dev/null +++ b/resources/images/tutorial/src/how_questions_everywhere.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/resources/images/tutorial/src/important_lets_go.svg b/resources/images/tutorial/src/important_lets_go.svg new file mode 100644 index 0000000..8441c68 --- /dev/null +++ b/resources/images/tutorial/src/important_lets_go.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/resources/images/tutorial/src/important_pin5.svg b/resources/images/tutorial/src/important_pin5.svg new file mode 100644 index 0000000..90bb983 --- /dev/null +++ b/resources/images/tutorial/src/important_pin5.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/resources/images/tutorial/src/important_pin6.svg b/resources/images/tutorial/src/important_pin6.svg new file mode 100644 index 0000000..e0adf0f --- /dev/null +++ b/resources/images/tutorial/src/important_pin6.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/resources/images/tutorial/src/important_space_questionmark.svg b/resources/images/tutorial/src/important_space_questionmark.svg new file mode 100644 index 0000000..854502e --- /dev/null +++ b/resources/images/tutorial/src/important_space_questionmark.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/resources/images/tutorial/src/reader_bluetooth_card_inserted.svg b/resources/images/tutorial/src/reader_bluetooth_card_inserted.svg new file mode 100644 index 0000000..a8ac112 --- /dev/null +++ b/resources/images/tutorial/src/reader_bluetooth_card_inserted.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/resources/images/tutorial/src/reader_bluetooth_connection.svg b/resources/images/tutorial/src/reader_bluetooth_connection.svg new file mode 100644 index 0000000..401a5cd --- /dev/null +++ b/resources/images/tutorial/src/reader_bluetooth_connection.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/resources/images/tutorial/src/reader_nfc_finished.svg b/resources/images/tutorial/src/reader_nfc_finished.svg new file mode 100644 index 0000000..6e67f1c --- /dev/null +++ b/resources/images/tutorial/src/reader_nfc_finished.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/resources/images/tutorial/src/reader_nfc_npa_on_smartphone.svg b/resources/images/tutorial/src/reader_nfc_npa_on_smartphone.svg new file mode 100644 index 0000000..e0b062d --- /dev/null +++ b/resources/images/tutorial/src/reader_nfc_npa_on_smartphone.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/resources/images/tutorial/src/reader_nfc_pin6.svg b/resources/images/tutorial/src/reader_nfc_pin6.svg new file mode 100644 index 0000000..8af2c71 --- /dev/null +++ b/resources/images/tutorial/src/reader_nfc_pin6.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/images/tutorial/src/reader_nfc_provider_on_smartphone.svg b/resources/images/tutorial/src/reader_nfc_provider_on_smartphone.svg new file mode 100644 index 0000000..1ad2c81 --- /dev/null +++ b/resources/images/tutorial/src/reader_nfc_provider_on_smartphone.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/resources/images/tutorial/src/reader_nfc_smartphone_nfc_position.svg b/resources/images/tutorial/src/reader_nfc_smartphone_nfc_position.svg new file mode 100644 index 0000000..04e6e93 --- /dev/null +++ b/resources/images/tutorial/src/reader_nfc_smartphone_nfc_position.svg @@ -0,0 +1,24 @@ + + + + + NFC? + NFC? + NFC? + NFC? + diff --git a/resources/images/tutorial/src/reader_nfc_userdata_example_de.svg b/resources/images/tutorial/src/reader_nfc_userdata_example_de.svg new file mode 100644 index 0000000..174266e --- /dev/null +++ b/resources/images/tutorial/src/reader_nfc_userdata_example_de.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + Zum Beispiel + Name? + Vorname? + Geburtsdatum? + Adresse? + diff --git a/resources/images/tutorial/src/reader_nfc_userdata_example_en.svg b/resources/images/tutorial/src/reader_nfc_userdata_example_en.svg new file mode 100644 index 0000000..8276811 --- /dev/null +++ b/resources/images/tutorial/src/reader_nfc_userdata_example_en.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + For Example + Surname? + Given Name? + Date of Birth? + Address? + diff --git a/resources/images/tutorial/src/reader_sac_aa2_ok.svg b/resources/images/tutorial/src/reader_sac_aa2_ok.svg new file mode 100644 index 0000000..7e2a50a --- /dev/null +++ b/resources/images/tutorial/src/reader_sac_aa2_ok.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/resources/images/tutorial/src/reader_sac_menu_de.svg b/resources/images/tutorial/src/reader_sac_menu_de.svg new file mode 100644 index 0000000..e5e262a --- /dev/null +++ b/resources/images/tutorial/src/reader_sac_menu_de.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/resources/images/tutorial/src/reader_sac_menu_en.svg b/resources/images/tutorial/src/reader_sac_menu_en.svg new file mode 100644 index 0000000..40a4fb8 --- /dev/null +++ b/resources/images/tutorial/src/reader_sac_menu_en.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/resources/images/tutorial/src/reader_sac_no_nfc_devices.svg b/resources/images/tutorial/src/reader_sac_no_nfc_devices.svg new file mode 100644 index 0000000..cc73398 --- /dev/null +++ b/resources/images/tutorial/src/reader_sac_no_nfc_devices.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/images/tutorial/src/reader_sac_no_nfc_provider.svg b/resources/images/tutorial/src/reader_sac_no_nfc_provider.svg new file mode 100644 index 0000000..f1edd8b --- /dev/null +++ b/resources/images/tutorial/src/reader_sac_no_nfc_provider.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/images/tutorial/src/reader_sac_npa_on_laptop.svg b/resources/images/tutorial/src/reader_sac_npa_on_laptop.svg new file mode 100644 index 0000000..ae51d8d --- /dev/null +++ b/resources/images/tutorial/src/reader_sac_npa_on_laptop.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/resources/images/tutorial/src/reader_sac_provider_on_laptop.svg b/resources/images/tutorial/src/reader_sac_provider_on_laptop.svg new file mode 100644 index 0000000..106abd7 --- /dev/null +++ b/resources/images/tutorial/src/reader_sac_provider_on_laptop.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/resources/images/tutorial/src/where_identify_now_de.svg b/resources/images/tutorial/src/where_identify_now_de.svg new file mode 100644 index 0000000..a8faa61 --- /dev/null +++ b/resources/images/tutorial/src/where_identify_now_de.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/resources/images/tutorial/src/where_identify_now_en.svg b/resources/images/tutorial/src/where_identify_now_en.svg new file mode 100644 index 0000000..4634f36 --- /dev/null +++ b/resources/images/tutorial/src/where_identify_now_en.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/resources/images/tutorial/src/where_lay_down_id.svg b/resources/images/tutorial/src/where_lay_down_id.svg new file mode 100644 index 0000000..9304b11 --- /dev/null +++ b/resources/images/tutorial/src/where_lay_down_id.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/resources/images/tutorial/src/where_overview_question.svg b/resources/images/tutorial/src/where_overview_question.svg new file mode 100644 index 0000000..56cfbb0 --- /dev/null +++ b/resources/images/tutorial/src/where_overview_question.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/images/tutorial/src/where_pin6.svg b/resources/images/tutorial/src/where_pin6.svg new file mode 100644 index 0000000..f43fbb2 --- /dev/null +++ b/resources/images/tutorial/src/where_pin6.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/resources/images/tutorial/src/where_providerlist_screenshot_de.svg b/resources/images/tutorial/src/where_providerlist_screenshot_de.svg new file mode 100644 index 0000000..116237e --- /dev/null +++ b/resources/images/tutorial/src/where_providerlist_screenshot_de.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/resources/images/tutorial/src/where_providerlist_screenshot_en.svg b/resources/images/tutorial/src/where_providerlist_screenshot_en.svg new file mode 100644 index 0000000..1ff21dc --- /dev/null +++ b/resources/images/tutorial/src/where_providerlist_screenshot_en.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/resources/images/tutorial/src/where_userdata_example_de.svg b/resources/images/tutorial/src/where_userdata_example_de.svg new file mode 100644 index 0000000..fe0b08b --- /dev/null +++ b/resources/images/tutorial/src/where_userdata_example_de.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + Zum Beispiel + Name? + Vorname? + Geburtsdatum? + Adresse? + diff --git a/resources/images/tutorial/src/where_userdata_example_en.svg b/resources/images/tutorial/src/where_userdata_example_en.svg new file mode 100644 index 0000000..a4dd021 --- /dev/null +++ b/resources/images/tutorial/src/where_userdata_example_en.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + For Example + Surname? + Given Name? + Date of Birth? + Address? + diff --git a/resources/images/tutorial/tablet-nfc.svg b/resources/images/tutorial/tablet-nfc.svg new file mode 100644 index 0000000..b32fb82 --- /dev/null +++ b/resources/images/tutorial/tablet-nfc.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/resources/images/tutorial/tablet-no-nfc.svg b/resources/images/tutorial/tablet-no-nfc.svg new file mode 100644 index 0000000..480a407 --- /dev/null +++ b/resources/images/tutorial/tablet-no-nfc.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/resources/images/tutorial/tablet.svg b/resources/images/tutorial/tablet.svg new file mode 100644 index 0000000..c207d5d --- /dev/null +++ b/resources/images/tutorial/tablet.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/resources/images/tutorial/thumb_up.svg b/resources/images/tutorial/thumb_up.svg new file mode 100644 index 0000000..311fcb5 --- /dev/null +++ b/resources/images/tutorial/thumb_up.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/images/tutorial/up_icon.svg b/resources/images/tutorial/up_icon.svg new file mode 100644 index 0000000..d5c0888 --- /dev/null +++ b/resources/images/tutorial/up_icon.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/resources/images/tutorial/usb.svg b/resources/images/tutorial/usb.svg new file mode 100644 index 0000000..7dddd2e --- /dev/null +++ b/resources/images/tutorial/usb.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/images/tutorial/user-tine@3x.png b/resources/images/tutorial/user-tine@3x.png new file mode 100644 index 0000000..3047f77 Binary files /dev/null and b/resources/images/tutorial/user-tine@3x.png differ diff --git a/resources/images/tutorial/wifi.svg b/resources/images/tutorial/wifi.svg new file mode 100644 index 0000000..3297568 --- /dev/null +++ b/resources/images/tutorial/wifi.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/resources/images/tutorial/zoom_triangle.svg b/resources/images/tutorial/zoom_triangle.svg new file mode 100644 index 0000000..6d3f0a5 --- /dev/null +++ b/resources/images/tutorial/zoom_triangle.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/resources/jenkins/changelog.sh b/resources/jenkins/changelog.sh deleted file mode 100755 index afa1119..0000000 --- a/resources/jenkins/changelog.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/sh - -BASEDIR=$(dirname $0) -CURRENT_CHANGELOG=$1 -LAST_CHANGELOG=$2 - -if [ "$CURRENT_CHANGELOG" == "" ] || [ "$LAST_CHANGELOG" == "" ]; then - echo "Usage: CURRENT_CHANGELOG LAST_CHANGELOG" - exit 1 -fi - -command -v stat >/dev/null || { echo "stat not found"; exit 1; } - -CURRENT_CHANGELOG_SIZE=`stat -c %s $CURRENT_CHANGELOG` -if [ $CURRENT_CHANGELOG_SIZE -lt 66 ]; then - cp $LAST_CHANGELOG $CURRENT_CHANGELOG - CURRENT_CHANGELOG=$LAST_CHANGELOG -fi - -command -v xsltproc >/dev/null || { echo "xsltproc not found"; exit 1; } - -CONTENT=`xsltproc $BASEDIR/changelog.xsl $CURRENT_CHANGELOG 2> /dev/null` - -if [ $? != 0 ] || [ "$CONTENT" == "" ]; then - echo "Erstellung des Changelogs ist fehlgeschlagen!" - exit 1 -fi - -echo $CONTENT diff --git a/resources/jenkins/changelog.xsl b/resources/jenkins/changelog.xsl deleted file mode 100644 index 5749e01..0000000 --- a/resources/jenkins/changelog.xsl +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - -

Changeset :

- Author:
- Files:
- Comment:
-
-
- -
- -
- -
diff --git a/resources/jenkins/docker/arch/Dockerfile b/resources/jenkins/docker/arch/Dockerfile index 6f4f61a..2b38814 100644 --- a/resources/jenkins/docker/arch/Dockerfile +++ b/resources/jenkins/docker/arch/Dockerfile @@ -2,7 +2,7 @@ FROM scratch MAINTAINER Governikus KG ARG box="busybox-x86_64" -ARG version="2017.10.01" +ARG version="2018.01.01" ADD $box /tmp/busybox ADD archlinux-bootstrap-$version-x86_64.tar.gz / @@ -26,6 +26,7 @@ RUN rm -rf /tmp/busybox /root.x86_64 RUN pacman-key --init && pacman-key --populate archlinux RUN echo 'Server = http://mirrors.kernel.org/archlinux/$repo/os/$arch' > /etc/pacman.d/mirrorlist &&\ + echo 'Server = http://ftp.uni-hannover.de/archlinux/$repo/os/$arch' >> /etc/pacman.d/mirrorlist &&\ echo 'en_US.UTF-8 UTF-8' > /etc/locale.gen &&\ echo 'LANG="en_US.UTF-8"' > /etc/locale.conf diff --git a/resources/jenkins/docker/generate.sh b/resources/jenkins/docker/generate.sh index 7eb8ef5..a5648f0 100755 --- a/resources/jenkins/docker/generate.sh +++ b/resources/jenkins/docker/generate.sh @@ -25,12 +25,12 @@ ls busybox* >/dev/null echo "Building base Arch ..." docker build -t arch:latest . +cd .. images=(android) for i in "${images[@]}" do echo "Building $i ..." - cd ../$i - docker build -t arch:$i . + docker build -t arch:$i -f $i/Dockerfile . done diff --git a/resources/jenkins/dsl/Builds/Build_Android.groovy b/resources/jenkins/dsl/Builds/Build_Android.groovy index 1196747..8bd5a02 100644 --- a/resources/jenkins/dsl/Builds/Build_Android.groovy +++ b/resources/jenkins/dsl/Builds/Build_Android.groovy @@ -2,15 +2,17 @@ import common.Build import common.Constants import static common.Constants.strip -for(ARCH in Constants.AndroidArch) + +// ----------------------------------------------------------------- APK +for(ARCH in Constants.AndroidArchAPK) { def j = new Build ( name: 'Android_APK_' + ARCH, - libraries: 'Android_' + ARCH, + libraries: ['Android_' + ARCH], label: 'Android', - artifacts: 'build/dist/**/AusweisApp2-*.apk' + artifacts: 'build/dist/**/AusweisApp2-*.apk,build/debug.symbols/**/libAusweisApp2.so' ).generate(this) @@ -21,7 +23,7 @@ j.with shell(strip("""\ cd build; cmake ../source - -DCMAKE_BUILD_TYPE=release + -DCMAKE_BUILD_TYPE=MinSizeRel -DCMAKE_PREFIX_PATH=\${WORKSPACE}/libs/build/dist -DCMAKE_TOOLCHAIN_FILE=../source/cmake/android.toolchain.cmake -DCMAKE_CXX_COMPILER_LAUNCHER=ccache @@ -35,6 +37,58 @@ j.with shell('cd build; make apk') shell('cd build; make verify.signature') } + + publishers { + androidLint('build/dist/build/outputs/lint-results-*.xml') + } +} + +} + + +// ----------------------------------------------------------------- AAR +def neededLibraries = [] +for(ARCH in Constants.AndroidArchAAR) +{ + neededLibraries.add('Android_' + ARCH) +} + +def j = new Build + ( + name: 'Android_AAR', + libraries: neededLibraries, + label: 'Android', + artifacts: 'build/**/dist/**/ausweisapp-*.aar,build/**/dist/**/ausweisapp-*.pom,build/**/dist/**/ausweisapp-*.jar,build/dist/libs/**/debug.symbols/libAusweisApp2.so' + ).generate(this) + + +for(ARCH in Constants.AndroidArchAAR) +{ + +j.with +{ + steps + { + shell(strip("""\ + mkdir -p build/${ARCH}; + cd build/${ARCH}; + cmake ../../source + -DANDROID_BUILD_AAR=ON + -DCMAKE_BUILD_TYPE=MinSizeRel + -DCMAKE_INSTALL_PREFIX=\${WORKSPACE}/build/dist + -DCMAKE_PREFIX_PATH="\${WORKSPACE}/libs/${ARCH}/build/dist;\${WORKSPACE}/libs/build/dist" + -DCMAKE_TOOLCHAIN_FILE=../source/cmake/android.toolchain.cmake + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + -DCMAKE_ANDROID_ARCH_ABI=${ARCH} + """)) + + shell("cd build/${ARCH}; make \${MAKE_FLAGS} install") + shell("cd build/${ARCH}; make aar") + } + + publishers { + androidLint('build/dist/build/outputs/lint-results-*.xml') + } } } diff --git a/resources/jenkins/dsl/Builds/Build_Docs.groovy b/resources/jenkins/dsl/Builds/Build_Docs.groovy index 9c7eb15..7e4f918 100644 --- a/resources/jenkins/dsl/Builds/Build_Docs.groovy +++ b/resources/jenkins/dsl/Builds/Build_Docs.groovy @@ -22,6 +22,8 @@ j.with shell('cd build; make sdk.latex.pdf') shell('cd build/docs/sdk/html; cmake -E tar cfJ ../AusweisApp2_SDK.tar.xz .') + shell('cd build; make inst.latex.pdf') + shell('cd build; make doc8') } } diff --git a/resources/jenkins/dsl/Builds/Build_FreeBSD.groovy b/resources/jenkins/dsl/Builds/Build_FreeBSD.groovy index eddb26d..5100b9a 100644 --- a/resources/jenkins/dsl/Builds/Build_FreeBSD.groovy +++ b/resources/jenkins/dsl/Builds/Build_FreeBSD.groovy @@ -4,7 +4,7 @@ import static common.Constants.strip def j = new Build ( name: 'FreeBSD', - libraries: 'FreeBSD', + libraries: ['FreeBSD'], label: 'FreeBSD', xunit: true ).generate(this) @@ -40,7 +40,7 @@ j.with export QML2_IMPORT_PATH=$WORKSPACE/libs/build/dist/qml export LD_LIBRARY_PATH=$WORKSPACE/libs/build/dist/lib:$LD_LIBRARY_PATH export ASAN_OPTIONS=detect_leaks=0,new_delete_type_mismatch=0 - cd build; ctest ${MAKE_FLAGS} + cd build; ctest --output-on-failure ${MAKE_FLAGS} '''.stripIndent().trim()) } } diff --git a/resources/jenkins/dsl/Builds/Build_Linux.groovy b/resources/jenkins/dsl/Builds/Build_Linux.groovy index 1cb9455..174295d 100644 --- a/resources/jenkins/dsl/Builds/Build_Linux.groovy +++ b/resources/jenkins/dsl/Builds/Build_Linux.groovy @@ -4,7 +4,7 @@ import static common.Constants.strip def j = new Build ( name: 'Linux', - libraries: 'Linux', + libraries: ['Linux'], label: 'Linux', xunit: true ).generate(this) @@ -41,7 +41,7 @@ j.with export QML2_IMPORT_PATH=$WORKSPACE/libs/build/dist/qml export LD_LIBRARY_PATH=$WORKSPACE/libs/build/dist/lib:$LD_LIBRARY_PATH export ASAN_OPTIONS=detect_leaks=0,new_delete_type_mismatch=0 - cd build; ctest ${MAKE_FLAGS} + cd build; ctest --output-on-failure ${MAKE_FLAGS} '''.stripIndent().trim()) shell('cd build; make gcovr') diff --git a/resources/jenkins/dsl/Builds/Build_MacOS.groovy b/resources/jenkins/dsl/Builds/Build_MacOS.groovy index ecd9e67..5712a26 100644 --- a/resources/jenkins/dsl/Builds/Build_MacOS.groovy +++ b/resources/jenkins/dsl/Builds/Build_MacOS.groovy @@ -4,7 +4,7 @@ import static common.Constants.strip def j = new Build ( name: 'MacOS', - libraries: 'MacOS', + libraries: ['MacOS'], label: 'MacOS', xunit: true ).generate(this) @@ -36,7 +36,7 @@ j.with export DYLD_LIBRARY_PATH=${WORKSPACE}/libs/build/dist/lib export QT_PLUGIN_PATH=${WORKSPACE}/libs/build/dist/plugins export QML2_IMPORT_PATH=${WORKSPACE}/libs/build/dist/qml - cd build; ctest ${MAKE_FLAGS} + cd build; ctest --output-on-failure ${MAKE_FLAGS} '''.stripIndent().trim()) } } diff --git a/resources/jenkins/dsl/Builds/Build_MacOS_DMG.groovy b/resources/jenkins/dsl/Builds/Build_MacOS_DMG.groovy index 42643b3..76cbcc5 100644 --- a/resources/jenkins/dsl/Builds/Build_MacOS_DMG.groovy +++ b/resources/jenkins/dsl/Builds/Build_MacOS_DMG.groovy @@ -4,7 +4,7 @@ import static common.Constants.strip def j = new Build ( name: 'MacOS_DMG', - libraries: 'MacOS', + libraries: ['MacOS'], label: 'MacOS', artifacts: 'build/*.dmg' ).generate(this) @@ -21,7 +21,7 @@ j.with cmake ../source -DCMAKE_PREFIX_PATH=\${WORKSPACE}/libs/build/dist -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - -DCMAKE_BUILD_TYPE=release + -DCMAKE_BUILD_TYPE=MinSizeRel -DOSX_TIMESTAMP=OFF -DJENKINS_APPCAST=${MERCURIAL_REVISION_BRANCH}_Appcast """)) diff --git a/resources/jenkins/dsl/Builds/Build_Translation.groovy b/resources/jenkins/dsl/Builds/Build_Translation.groovy index fc35211..231e22f 100644 --- a/resources/jenkins/dsl/Builds/Build_Translation.groovy +++ b/resources/jenkins/dsl/Builds/Build_Translation.groovy @@ -4,7 +4,7 @@ import static common.Constants.strip def j = new Build ( name: 'Translation', - libraries: 'Linux', + libraries: ['Linux'], label: 'Linux', artifacts: 'source/resources/translations/*.ts' ).generate(this) diff --git a/resources/jenkins/dsl/Builds/Build_Win32_GNU.groovy b/resources/jenkins/dsl/Builds/Build_Win32_GNU.groovy index 718cc55..d64b8b0 100644 --- a/resources/jenkins/dsl/Builds/Build_Win32_GNU.groovy +++ b/resources/jenkins/dsl/Builds/Build_Win32_GNU.groovy @@ -4,8 +4,8 @@ import static common.Constants.strip def j = new Build ( name: 'Win32_GNU', - libraries: 'Win32_GNU', - label: "Windows7_${MERCURIAL_REVISION_BRANCH}", + libraries: ['Win32_GNU'], + label: 'Windows', xunit: true ).generate(this) @@ -28,7 +28,7 @@ j.with set PATH=%WORKSPACE%/build/src;%WORKSPACE%/build/test/helper;%PATH% set QT_PLUGIN_PATH=%WORKSPACE%/libs/build/dist/plugins set QML2_IMPORT_PATH=%WORKSPACE%/libs/build/dist/qml - cd build & ctest %MAKE_FLAGS% + cd build & ctest --output-on-failure %MAKE_FLAGS% '''.stripIndent().trim()) } } diff --git a/resources/jenkins/dsl/Builds/Build_Win32_GNU_MSI.groovy b/resources/jenkins/dsl/Builds/Build_Win32_GNU_MSI.groovy index 6439341..4a79486 100644 --- a/resources/jenkins/dsl/Builds/Build_Win32_GNU_MSI.groovy +++ b/resources/jenkins/dsl/Builds/Build_Win32_GNU_MSI.groovy @@ -4,8 +4,8 @@ import static common.Constants.strip def j = new Build ( name: 'Win32_GNU_MSI', - libraries: 'Win32_GNU', - label: "Windows7_${MERCURIAL_REVISION_BRANCH}", + libraries: ['Win32_GNU'], + label: 'Windows', artifacts: 'build/*.msi' ).generate(this) @@ -19,16 +19,17 @@ j.with cmake ../source -G\"MinGW Makefiles\" -DCMAKE_PREFIX_PATH=%WORKSPACE%\\libs\\build\\dist -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - -DCMAKE_BUILD_TYPE=release + -DCMAKE_BUILD_TYPE=MinSizeRel -DJENKINS_APPCAST=${MERCURIAL_REVISION_BRANCH}_Appcast -DWIN_SIGN_KEYSTORE=%WIN_SIGN_KEYSTORE% -DWIN_SIGN_KEYSTORE_PSW=%WIN_SIGN_KEYSTORE_PSW% -DWIN_SIGN_SUBJECT_NAME=%WIN_SIGN_SUBJECT_NAME% - -DSELFPACKER=true """)) batchFile('cd build & mingw32-make %MAKE_FLAGS% package') + batchFile('cd build & cmake -DCMD=CHECK_WIX_WARNING -DFILE=./_CPack_Packages/win32/WIX/wix.log -P ../source/cmake/cmd.cmake') + batchFile('cd build & mingw32-make package.sign') } } diff --git a/resources/jenkins/dsl/Builds/Build_Win32_GNU_MSI_dev.groovy b/resources/jenkins/dsl/Builds/Build_Win32_GNU_MSI_dev.groovy index 84a8b26..d85539f 100644 --- a/resources/jenkins/dsl/Builds/Build_Win32_GNU_MSI_dev.groovy +++ b/resources/jenkins/dsl/Builds/Build_Win32_GNU_MSI_dev.groovy @@ -4,8 +4,8 @@ import static common.Constants.strip def j = new Build ( name: 'Win32_GNU_MSI_dev', - libraries: 'Win32_GNU_dev', - label: "Windows7_${MERCURIAL_REVISION_BRANCH}", + libraries: ['Win32_GNU_dev'], + label: 'Windows', artifacts: 'build/*.msi' ).generate(this) @@ -27,6 +27,8 @@ j.with batchFile('cd build & mingw32-make %MAKE_FLAGS% package') + batchFile('cd build & cmake -DCMD=CHECK_WIX_WARNING -DFILE=./_CPack_Packages/win32/WIX/wix.log -P ../source/cmake/cmd.cmake') + batchFile('cd build & mingw32-make package.sign') } } diff --git a/resources/jenkins/dsl/Builds/Build_Win32_MSVC.groovy b/resources/jenkins/dsl/Builds/Build_Win32_MSVC.groovy index f6d9735..138c739 100644 --- a/resources/jenkins/dsl/Builds/Build_Win32_MSVC.groovy +++ b/resources/jenkins/dsl/Builds/Build_Win32_MSVC.groovy @@ -3,7 +3,7 @@ import common.Build def j = new Build ( name: 'Win32_MSVC', - libraries: 'Win32_MSVC_dev', + libraries: ['Win32_MSVC_dev'], label: 'MSVC', xunit: true ).generate(this) @@ -29,7 +29,7 @@ j.with set PATH=%WORKSPACE%/libs/build/dist/bin;%PATH% set QT_PLUGIN_PATH=%WORKSPACE%/libs/build/dist/plugins set QML2_IMPORT_PATH=%WORKSPACE%/libs/build/dist/qml - cd build & ctest %MAKE_FLAGS% + cd build & ctest --output-on-failure %MAKE_FLAGS% '''.stripIndent().trim()) } } diff --git a/resources/jenkins/dsl/Builds/Build_Win32_MSVC_MSI.groovy b/resources/jenkins/dsl/Builds/Build_Win32_MSVC_MSI.groovy index 035ad75..8ea38a3 100644 --- a/resources/jenkins/dsl/Builds/Build_Win32_MSVC_MSI.groovy +++ b/resources/jenkins/dsl/Builds/Build_Win32_MSVC_MSI.groovy @@ -4,7 +4,7 @@ import static common.Constants.strip def j = new Build ( name: 'Win32_MSVC_MSI', - libraries: 'Win32_MSVC', + libraries: ['Win32_MSVC'], label: 'MSVC', artifacts: 'build/*.msi' ).generate(this) @@ -17,7 +17,7 @@ j.with batchFile('''\ cd build call vcvarsall.bat - cmake ../source -DCMAKE_BUILD_TYPE=release -DCMAKE_CXX_COMPILER=clcache -DCMAKE_PREFIX_PATH=%WORKSPACE%/libs/build/dist -GNinja -DWIN_SIGN_KEYSTORE=%WIN_SIGN_KEYSTORE% -DWIN_SIGN_KEYSTORE_PSW=%WIN_SIGN_KEYSTORE_PSW% -DWIN_SIGN_SUBJECT_NAME=%WIN_SIGN_SUBJECT_NAME% + cmake ../source -DCMAKE_BUILD_TYPE=MinSizeRel -DCMAKE_CXX_COMPILER=clcache -DCMAKE_PREFIX_PATH=%WORKSPACE%/libs/build/dist -GNinja -DWIN_SIGN_KEYSTORE=%WIN_SIGN_KEYSTORE% -DWIN_SIGN_KEYSTORE_PSW=%WIN_SIGN_KEYSTORE_PSW% -DWIN_SIGN_SUBJECT_NAME=%WIN_SIGN_SUBJECT_NAME% '''.stripIndent().trim()) batchFile('''\ diff --git a/resources/jenkins/dsl/Builds/Build_iOS_IPA.groovy b/resources/jenkins/dsl/Builds/Build_iOS_IPA.groovy index 904a0cc..3df53a8 100644 --- a/resources/jenkins/dsl/Builds/Build_iOS_IPA.groovy +++ b/resources/jenkins/dsl/Builds/Build_iOS_IPA.groovy @@ -4,7 +4,7 @@ import static common.Constants.strip def j = new Build ( name: 'iOS_IPA', - libraries: 'iOS', + libraries: ['iOS'], label: 'iOS', artifacts: 'build/*.ipa' ).generate(this) diff --git a/resources/jenkins/dsl/Libraries/Libs_Android.groovy b/resources/jenkins/dsl/Libraries/Libs_Android.groovy index 9e635ce..85d3024 100644 --- a/resources/jenkins/dsl/Libraries/Libs_Android.groovy +++ b/resources/jenkins/dsl/Libraries/Libs_Android.groovy @@ -15,7 +15,7 @@ j.with { steps { - shell("cd build; cmake ../source/libs -DCMAKE_BUILD_TYPE=release -DCMAKE_TOOLCHAIN_FILE=../source/cmake/android.toolchain.cmake -DPACKAGES_DIR=\${PACKAGES_DIR} -DCMAKE_ANDROID_ARCH_ABI=${ARCH}") + shell("cd build; cmake ../source/libs -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_TOOLCHAIN_FILE=../source/cmake/android.toolchain.cmake -DPACKAGES_DIR=\${PACKAGES_DIR} -DCMAKE_ANDROID_ARCH_ABI=${ARCH}") shell('cd build; make compress') } diff --git a/resources/jenkins/dsl/Libraries/Libs_Win32_GNU.groovy b/resources/jenkins/dsl/Libraries/Libs_Win32_GNU.groovy index c8d546c..2eaf61f 100644 --- a/resources/jenkins/dsl/Libraries/Libs_Win32_GNU.groovy +++ b/resources/jenkins/dsl/Libraries/Libs_Win32_GNU.groovy @@ -3,18 +3,26 @@ import common.Library def j = new Library ( name: 'Win32_GNU', - label: "Windows7_${MERCURIAL_REVISION_BRANCH}" + label: 'Windows' ).generate(this) j.with { + wrappers + { + environmentVariables + { + env('MSYS2_PATH_TYPE', 'inherit') + } + } + steps { batchFile("cd build & cmake ../source/libs -DCMAKE_BUILD_TYPE=release -DPACKAGES_DIR=%PACKAGES_DIR% -G\"MinGW Makefiles\" -DWIN_SIGN_KEYSTORE=%WIN_SIGN_KEYSTORE% -DWIN_SIGN_KEYSTORE_PSW=%WIN_SIGN_KEYSTORE_PSW% -DWIN_SIGN_SUBJECT_NAME=%WIN_SIGN_SUBJECT_NAME%") shell('''\ - #!c:\\msys\\1.0\\bin\\sh --login + #!c:\\msys64\\usr\\bin\\bash --login cd /jenkins/$JOB_NAME/build mingw32-make openssl '''.stripIndent().trim()) diff --git a/resources/jenkins/dsl/Libraries/Libs_Win32_GNU_dev.groovy b/resources/jenkins/dsl/Libraries/Libs_Win32_GNU_dev.groovy index 243404a..9b1c0b9 100644 --- a/resources/jenkins/dsl/Libraries/Libs_Win32_GNU_dev.groovy +++ b/resources/jenkins/dsl/Libraries/Libs_Win32_GNU_dev.groovy @@ -3,18 +3,26 @@ import common.Library def j = new Library ( name: 'Win32_GNU_dev', - label: "Windows7_${MERCURIAL_REVISION_BRANCH}" + label: 'Windows' ).generate(this) j.with { + wrappers + { + environmentVariables + { + env('MSYS2_PATH_TYPE', 'inherit') + } + } + steps { batchFile("cd build & cmake ../source/libs -DPACKAGES_DIR=%PACKAGES_DIR% -G\"MinGW Makefiles\" -DWIN_SIGN_KEYSTORE=%WIN_SIGN_KEYSTORE% -DWIN_SIGN_KEYSTORE_PSW=%WIN_SIGN_KEYSTORE_PSW% -DWIN_SIGN_SUBJECT_NAME=%WIN_SIGN_SUBJECT_NAME%") shell('''\ - #!c:\\msys\\1.0\\bin\\sh --login + #!c:\\msys64\\usr\\bin\\bash --login cd /jenkins/$JOB_NAME/build mingw32-make openssl '''.stripIndent().trim()) diff --git a/resources/jenkins/dsl/Releases/Release_Android.groovy b/resources/jenkins/dsl/Releases/Release_Android.groovy index 9d4db61..9a57c4e 100644 --- a/resources/jenkins/dsl/Releases/Release_Android.groovy +++ b/resources/jenkins/dsl/Releases/Release_Android.groovy @@ -2,15 +2,17 @@ import common.Release import common.Constants import static common.Constants.strip -for(ARCH in Constants.AndroidArch) + +// ----------------------------------------------------------------- APK +for(ARCH in Constants.AndroidArchAPK) { def j = new Release ( name: 'Android_APK_' + ARCH, - libraries: 'Android_' + ARCH, + libraries: ['Android_' + ARCH], label: 'Android', - artifacts: 'build/dist/**/AusweisApp2-*.apk' + artifacts: 'build/dist/**/AusweisApp2-*.apk,build/debug.symbols/**/libAusweisApp2.so' ).generate(this) @@ -30,7 +32,7 @@ j.with cd build; cmake ../source -DCMAKE_PREFIX_PATH=\${WORKSPACE}/libs/build/dist -DCMAKE_TOOLCHAIN_FILE=../source/cmake/android.toolchain.cmake - -DCMAKE_BUILD_TYPE=release + -DCMAKE_BUILD_TYPE=MinSizeRel -DCMAKE_ANDROID_ARCH_ABI=${ARCH} -DAPK_SIGN_KEYSTORE=\${APK_SIGN_KEYSTORE} -DAPK_SIGN_KEYSTORE_ALIAS=\${APK_SIGN_KEYSTORE_ALIAS} @@ -46,3 +48,59 @@ j.with } } + + +// ----------------------------------------------------------------- AAR +def neededLibraries = [] +for(ARCH in Constants.AndroidArchAAR) +{ + neededLibraries.add('Android_' + ARCH) +} + +def j = new Release + ( + name: 'Android_AAR', + libraries: neededLibraries, + label: 'Android', + artifacts: 'build/**/dist/**/ausweisapp-*.aar,build/**/dist/**/ausweisapp-*.pom,build/**/dist/**/ausweisapp-*.jar,build/dist/libs/**/debug.symbols/libAusweisApp2.so' + ).generate(this) + +j.with +{ + parameters + { + booleanParam("BUILD_PREVIEW", false, "Use com.governikus.ausweisapp2.dev as package name") + } + + steps + { + buildDescription('', 'BUILD_PREVIEW: ${BUILD_PREVIEW}') + } +} + +for(ARCH in Constants.AndroidArchAAR) +{ + +j.with +{ + steps + { + shell(strip("""\ + mkdir -p build/${ARCH}; + cd build/${ARCH}; + cmake ../../source + -DANDROID_BUILD_AAR=ON + -DCMAKE_INSTALL_PREFIX=\${WORKSPACE}/build/dist + -DCMAKE_PREFIX_PATH="\${WORKSPACE}/libs/${ARCH}/build/dist;\${WORKSPACE}/libs/build/dist" + -DCMAKE_TOOLCHAIN_FILE=../source/cmake/android.toolchain.cmake + -DCMAKE_BUILD_TYPE=MinSizeRel + -DCMAKE_ANDROID_ARCH_ABI=${ARCH} + -DBUILD_PREVIEW=\${BUILD_PREVIEW} + """)) + + shell("cd build/${ARCH}; make \${MAKE_FLAGS} install") + shell("cd build/${ARCH}; make aar") + } +} + +} diff --git a/resources/jenkins/dsl/Releases/Release_Docs.groovy b/resources/jenkins/dsl/Releases/Release_Docs.groovy index f9cfbfa..3f35731 100644 --- a/resources/jenkins/dsl/Releases/Release_Docs.groovy +++ b/resources/jenkins/dsl/Releases/Release_Docs.groovy @@ -21,5 +21,7 @@ j.with shell('cd build; make sdk') shell('cd build; make sdk.latex.pdf') shell('cd build/docs/sdk/html; cmake -E tar cfJ ../AusweisApp2_SDK.tar.xz .') + + shell('cd build; make inst.latex.pdf') } } diff --git a/resources/jenkins/dsl/Releases/Release_MacOS.groovy b/resources/jenkins/dsl/Releases/Release_MacOS.groovy index df8cb0d..5589ca4 100644 --- a/resources/jenkins/dsl/Releases/Release_MacOS.groovy +++ b/resources/jenkins/dsl/Releases/Release_MacOS.groovy @@ -4,7 +4,7 @@ import static common.Constants.strip def j = new Release ( name: 'MacOS_DMG', - libraries: 'MacOS', + libraries: ['MacOS'], label: 'MacOS', artifacts: 'build/*.dmg' ).generate(this) @@ -21,7 +21,7 @@ j.with cmake ../source -DCMAKE_PREFIX_PATH=\${WORKSPACE}/libs/build/dist -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - -DCMAKE_BUILD_TYPE=release + -DCMAKE_BUILD_TYPE=MinSizeRel """)) shell('''\ diff --git a/resources/jenkins/dsl/Releases/Release_Win32_GNU.groovy b/resources/jenkins/dsl/Releases/Release_Win32_GNU.groovy index 642d86c..3073681 100644 --- a/resources/jenkins/dsl/Releases/Release_Win32_GNU.groovy +++ b/resources/jenkins/dsl/Releases/Release_Win32_GNU.groovy @@ -4,23 +4,14 @@ import static common.Constants.strip def j = new Release ( name: 'Win32_GNU_MSI', - libraries: 'Win32_GNU', + libraries: ['Win32_GNU'], + label: 'Windows', artifacts: 'build/*.msi' ).generate(this) j.with { - parameters - { - nodeParam('BuildHost') - { - description('Jenkins slave') - defaultNodes(['Windows7_stable']) - allowedNodes(['Windows7_default', 'Windows7_stable']) - } - } - steps { batchFile(strip("""\ @@ -28,11 +19,10 @@ j.with cmake ../source -G\"MinGW Makefiles\" -DCMAKE_PREFIX_PATH=%WORKSPACE%\\libs\\build\\dist -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - -DCMAKE_BUILD_TYPE=release + -DCMAKE_BUILD_TYPE=MinSizeRel -DWIN_SIGN_KEYSTORE=%WIN_SIGN_KEYSTORE% -DWIN_SIGN_KEYSTORE_PSW=%WIN_SIGN_KEYSTORE_PSW% -DWIN_SIGN_SUBJECT_NAME=%WIN_SIGN_SUBJECT_NAME% - -DSELFPACKER=true """)) batchFile('cd build & mingw32-make %MAKE_FLAGS% package') diff --git a/resources/jenkins/dsl/Releases/Release_iOS.groovy b/resources/jenkins/dsl/Releases/Release_iOS.groovy index 3c62fa8..76e370e 100644 --- a/resources/jenkins/dsl/Releases/Release_iOS.groovy +++ b/resources/jenkins/dsl/Releases/Release_iOS.groovy @@ -4,7 +4,7 @@ import static common.Constants.strip def j = new Release ( name: 'iOS_IPA', - libraries: 'iOS', + libraries: ['iOS'], label: 'iOS', artifacts: 'build/*.ipa' ).generate(this) diff --git a/resources/jenkins/dsl/Reviews/Review_Android.groovy b/resources/jenkins/dsl/Reviews/Review_Android.groovy index a0cb896..0011f32 100644 --- a/resources/jenkins/dsl/Reviews/Review_Android.groovy +++ b/resources/jenkins/dsl/Reviews/Review_Android.groovy @@ -2,13 +2,15 @@ import common.Review import common.Constants import static common.Constants.strip -for(ARCH in Constants.AndroidArch) + +// ----------------------------------------------------------------- APK +for(ARCH in Constants.AndroidArchAPK) { def j = new Review ( name: 'Android_APK_' + ARCH, - libraries: 'Android_' + ARCH, + libraries: ['Android_' + ARCH], label: 'Android', artifacts: 'build/dist/**/AusweisApp2-*.apk' ).generate(this) @@ -34,6 +36,65 @@ j.with shell('cd build; make apk') shell('cd build; make verify.signature') } + + publishers { + androidLint('build/dist/build/outputs/lint-results-*.xml') + } +} + +} + + +// ----------------------------------------------------------------- AAR +def neededLibraries = [] +for(ARCH in Constants.AndroidArchAAR) +{ + neededLibraries.add('Android_' + ARCH) +} + +def j = new Review + ( + name: 'Android_AAR', + libraries: neededLibraries, + label: 'Android', + artifacts: 'build/**/dist/**/ausweisapp-*.aar' + ).generate(this) + +j.with +{ + steps + { + shell('cd source; python resources/jenkins/import.py') + } +} + +for(ARCH in Constants.AndroidArchAAR) +{ + +j.with +{ + steps + { + shell(strip("""\ + mkdir -p build/${ARCH}; + cd build/${ARCH}; + cmake -Werror=dev ../../source + -DANDROID_BUILD_AAR=ON + -DCMAKE_BUILD_TYPE=debug + -DCMAKE_INSTALL_PREFIX=\${WORKSPACE}/build/dist + -DCMAKE_PREFIX_PATH="\${WORKSPACE}/libs/${ARCH}/build/dist;\${WORKSPACE}/libs/build/dist" + -DCMAKE_TOOLCHAIN_FILE=../source/cmake/android.toolchain.cmake + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + -DCMAKE_ANDROID_ARCH_ABI=${ARCH} + """)) + + shell("cd build/${ARCH}; make \${MAKE_FLAGS} install") + shell("cd build/${ARCH}; make aar") + } + + publishers { + androidLint('build/dist/build/outputs/lint-results-*.xml') + } } } diff --git a/resources/jenkins/dsl/Reviews/Review_Docs.groovy b/resources/jenkins/dsl/Reviews/Review_Docs.groovy index 10ba186..6db69b0 100644 --- a/resources/jenkins/dsl/Reviews/Review_Docs.groovy +++ b/resources/jenkins/dsl/Reviews/Review_Docs.groovy @@ -26,6 +26,8 @@ j.with shell('cd build; make sdk.latex.pdf') shell('cd build/docs/sdk/html; cmake -E tar cfJ ../AusweisApp2_SDK.tar.xz .') + shell('cd build; make inst.latex.pdf') + shell('cd build; make doc8') } } diff --git a/resources/jenkins/dsl/Reviews/Review_FreeBSD.groovy b/resources/jenkins/dsl/Reviews/Review_FreeBSD.groovy index bc61b77..a4f2906 100644 --- a/resources/jenkins/dsl/Reviews/Review_FreeBSD.groovy +++ b/resources/jenkins/dsl/Reviews/Review_FreeBSD.groovy @@ -4,9 +4,9 @@ import static common.Constants.strip def j = new Review ( name: 'FreeBSD', - libraries: 'FreeBSD', + libraries: ['FreeBSD'], label: 'FreeBSD', - artifacts: 'tmp/AusweisApp2.*.log', + artifacts: 'tmp/*.log', allowEmptyArtifacts: true, xunit: true, ).generate(this) @@ -44,7 +44,7 @@ j.with export QML2_IMPORT_PATH=${WORKSPACE}/libs/build/dist/qml export LD_LIBRARY_PATH=$WORKSPACE/libs/build/dist/lib:$LD_LIBRARY_PATH export ASAN_OPTIONS=detect_leaks=0,new_delete_type_mismatch=0 - cd build; ctest ${MAKE_FLAGS} + cd build; ctest --output-on-failure ${MAKE_FLAGS} '''.stripIndent().trim()) } } diff --git a/resources/jenkins/dsl/Reviews/Review_Libs_Win32_GNU.groovy b/resources/jenkins/dsl/Reviews/Review_Libs_Win32_GNU.groovy index 129f0c9..beb8429 100644 --- a/resources/jenkins/dsl/Reviews/Review_Libs_Win32_GNU.groovy +++ b/resources/jenkins/dsl/Reviews/Review_Libs_Win32_GNU.groovy @@ -3,12 +3,20 @@ import common.LibraryReview def j = new LibraryReview ( name: 'Win32_GNU', - label: "Windows7_${MERCURIAL_REVISION_BRANCH}" + label: 'Windows' ).generate(this) j.with { + wrappers + { + environmentVariables + { + env('MSYS2_PATH_TYPE', 'inherit') + } + } + steps { batchFile('cd source & python resources/jenkins/import.py') @@ -16,7 +24,7 @@ j.with batchFile("cd build & cmake ../source/libs -DCMAKE_BUILD_TYPE=release -DPACKAGES_DIR=%PACKAGES_DIR% -G\"MinGW Makefiles\" -DWIN_SIGN_KEYSTORE=%WIN_SIGN_KEYSTORE% -DWIN_SIGN_KEYSTORE_PSW=%WIN_SIGN_KEYSTORE_PSW% -DWIN_SIGN_SUBJECT_NAME=%WIN_SIGN_SUBJECT_NAME%") shell('''\ - #!c:\\msys\\1.0\\bin\\sh --login + #!c:\\msys64\\usr\\bin\\bash --login cd /jenkins/$JOB_NAME/build mingw32-make openssl '''.stripIndent().trim()) diff --git a/resources/jenkins/dsl/Reviews/Review_Linux.groovy b/resources/jenkins/dsl/Reviews/Review_Linux.groovy index d300569..6da3069 100644 --- a/resources/jenkins/dsl/Reviews/Review_Linux.groovy +++ b/resources/jenkins/dsl/Reviews/Review_Linux.groovy @@ -5,9 +5,9 @@ import static common.Constants.strip def j = new Review ( name: 'Linux', - libraries: 'Linux', + libraries: ['Linux'], label: 'Linux', - artifacts: 'tmp/AusweisApp2.*.log', + artifacts: 'tmp/*.log', allowEmptyArtifacts: true, xunit: true, jobType: JobType.Matrix @@ -52,7 +52,7 @@ j.with export QML2_IMPORT_PATH=${WORKSPACE}/libs/build/dist/qml export LD_LIBRARY_PATH=$WORKSPACE/libs/build/dist/lib:$LD_LIBRARY_PATH export ASAN_OPTIONS=detect_leaks=0,new_delete_type_mismatch=0 - cd build; ctest ${MAKE_FLAGS} + cd build; ctest --output-on-failure ${MAKE_FLAGS} '''.stripIndent().trim()) } } diff --git a/resources/jenkins/dsl/Reviews/Review_MacOS.groovy b/resources/jenkins/dsl/Reviews/Review_MacOS.groovy index 1ad32f7..92cb143 100644 --- a/resources/jenkins/dsl/Reviews/Review_MacOS.groovy +++ b/resources/jenkins/dsl/Reviews/Review_MacOS.groovy @@ -4,9 +4,9 @@ import static common.Constants.strip def j = new Review ( name: 'MacOS', - libraries: 'MacOS', + libraries: ['MacOS'], label: 'MacOS', - artifacts: 'tmp/AusweisApp2.*.log', + artifacts: 'tmp/*.log', allowEmptyArtifacts: true, xunit: true ).generate(this) @@ -40,7 +40,8 @@ j.with export DYLD_LIBRARY_PATH=${WORKSPACE}/libs/build/dist/lib export QT_PLUGIN_PATH=${WORKSPACE}/libs/build/dist/plugins export QML2_IMPORT_PATH=${WORKSPACE}/libs/build/dist/qml - cd build; ctest ${MAKE_FLAGS} + export ASAN_OPTIONS=detect_leaks=0,new_delete_type_mismatch=0 + cd build; ctest --output-on-failure ${MAKE_FLAGS} '''.stripIndent().trim()) } } diff --git a/resources/jenkins/dsl/Reviews/Review_MacOS_DMG.groovy b/resources/jenkins/dsl/Reviews/Review_MacOS_DMG.groovy index dab4e21..b2b4d30 100644 --- a/resources/jenkins/dsl/Reviews/Review_MacOS_DMG.groovy +++ b/resources/jenkins/dsl/Reviews/Review_MacOS_DMG.groovy @@ -4,7 +4,7 @@ import static common.Constants.strip def j = new Review ( name: 'MacOS_DMG', - libraries: 'MacOS', + libraries: ['MacOS'], label: 'MacOS', artifacts: 'build/*.dmg' ).generate(this) @@ -23,7 +23,7 @@ j.with cmake -Werror=dev ../source -DCMAKE_PREFIX_PATH=\${WORKSPACE}/libs/build/dist -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - -DCMAKE_BUILD_TYPE=release + -DCMAKE_BUILD_TYPE=MinSizeRel -DOSX_TIMESTAMP=OFF -DJENKINS_APPCAST=${MERCURIAL_REVISION_BRANCH}_Appcast """)) diff --git a/resources/jenkins/dsl/Reviews/Review_Trigger.groovy b/resources/jenkins/dsl/Reviews/Review_Trigger.groovy index 611a047..8410360 100644 --- a/resources/jenkins/dsl/Reviews/Review_Trigger.groovy +++ b/resources/jenkins/dsl/Reviews/Review_Trigger.groovy @@ -6,8 +6,8 @@ import static common.Constants.createReviewMessage def getJobs() { - def list = ['Formatting', 'Source', 'Docs', 'MacOS_DMG', 'Win32_GNU_MSI', 'Win32_MSVC_MSI', 'iOS_IPA'] - for(ARCH in Constants.AndroidArch) + def list = ['Formatting', 'Source', 'Docs', 'MacOS_DMG', 'Win32_GNU_MSI', 'Win32_MSVC_MSI', 'iOS_IPA', 'Android_AAR'] + for(ARCH in Constants.AndroidArchAPK) { list << 'Android_APK_' + ARCH } @@ -62,7 +62,9 @@ j.with phase('Packages') { - for(ARCH in Constants.AndroidArch) + phaseJob(getName('Android_AAR')) + + for(ARCH in Constants.AndroidArchAPK) { phaseJob(getName('Android_APK_' + ARCH)) } diff --git a/resources/jenkins/dsl/Reviews/Review_Win32_GNU.groovy b/resources/jenkins/dsl/Reviews/Review_Win32_GNU.groovy index a37c515..0f2ca38 100644 --- a/resources/jenkins/dsl/Reviews/Review_Win32_GNU.groovy +++ b/resources/jenkins/dsl/Reviews/Review_Win32_GNU.groovy @@ -4,9 +4,9 @@ import static common.Constants.strip def j = new Review ( name: 'Win32_GNU', - libraries: 'Win32_GNU', - label: "Windows7_${MERCURIAL_REVISION_BRANCH}", - artifacts: 'tmp/AusweisApp2.*.log', + libraries: ['Win32_GNU'], + label: 'Windows', + artifacts: 'tmp/*.log', allowEmptyArtifacts: true, xunit: true ).generate(this) @@ -32,7 +32,7 @@ j.with set PATH=%WORKSPACE%/build/src;%WORKSPACE%/build/test/helper;%PATH% set QT_PLUGIN_PATH=%WORKSPACE%/libs/build/dist/plugins set QML2_IMPORT_PATH=%WORKSPACE%/libs/build/dist/qml - cd build & ctest %MAKE_FLAGS% + cd build & ctest --output-on-failure %MAKE_FLAGS% '''.stripIndent().trim()) } } diff --git a/resources/jenkins/dsl/Reviews/Review_Win32_GNU_MSI.groovy b/resources/jenkins/dsl/Reviews/Review_Win32_GNU_MSI.groovy index ca4557e..474befd 100644 --- a/resources/jenkins/dsl/Reviews/Review_Win32_GNU_MSI.groovy +++ b/resources/jenkins/dsl/Reviews/Review_Win32_GNU_MSI.groovy @@ -4,8 +4,8 @@ import static common.Constants.strip def j = new Review ( name: 'Win32_GNU_MSI', - libraries: 'Win32_GNU', - label: "Windows7_${MERCURIAL_REVISION_BRANCH}", + libraries: ['Win32_GNU'], + label: 'Windows', artifacts: 'build/*.msi' ).generate(this) @@ -21,16 +21,17 @@ j.with cmake -Werror=dev ../source -G\"MinGW Makefiles\" -DCMAKE_PREFIX_PATH=%WORKSPACE%\\libs\\build\\dist -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - -DCMAKE_BUILD_TYPE=release + -DCMAKE_BUILD_TYPE=MinSizeRel -DJENKINS_APPCAST=${MERCURIAL_REVISION_BRANCH}_Appcast -DWIN_SIGN_KEYSTORE=%WIN_SIGN_KEYSTORE% -DWIN_SIGN_KEYSTORE_PSW=%WIN_SIGN_KEYSTORE_PSW% -DWIN_SIGN_SUBJECT_NAME=%WIN_SIGN_SUBJECT_NAME% - -DSELFPACKER=true """)) batchFile('cd build & mingw32-make %MAKE_FLAGS% package') + batchFile('cd build & cmake -DCMD=CHECK_WIX_WARNING -DFILE=./_CPack_Packages/win32/WIX/wix.log -P ../source/cmake/cmd.cmake') + batchFile('cd build & mingw32-make package.sign') } } diff --git a/resources/jenkins/dsl/Reviews/Review_Win32_MSVC.groovy b/resources/jenkins/dsl/Reviews/Review_Win32_MSVC.groovy index 7346423..cf6a1ae 100644 --- a/resources/jenkins/dsl/Reviews/Review_Win32_MSVC.groovy +++ b/resources/jenkins/dsl/Reviews/Review_Win32_MSVC.groovy @@ -3,9 +3,9 @@ import common.Review def j = new Review ( name: 'Win32_MSVC', - libraries: 'Win32_MSVC_dev', + libraries: ['Win32_MSVC_dev'], label: 'MSVC', - artifacts: 'tmp/AusweisApp2.*.log', + artifacts: 'tmp/*.log', allowEmptyArtifacts: true, xunit: true ).generate(this) @@ -33,7 +33,7 @@ j.with set PATH=%WORKSPACE%/libs/build/dist/bin;%PATH% set QT_PLUGIN_PATH=%WORKSPACE%/libs/build/dist/plugins set QML2_IMPORT_PATH=%WORKSPACE%/libs/build/dist/qml - cd build & ctest %MAKE_FLAGS% + cd build & ctest --output-on-failure %MAKE_FLAGS% '''.stripIndent().trim()) } } diff --git a/resources/jenkins/dsl/Reviews/Review_Win32_MSVC_MSI.groovy b/resources/jenkins/dsl/Reviews/Review_Win32_MSVC_MSI.groovy index 6185f5a..389fb37 100644 --- a/resources/jenkins/dsl/Reviews/Review_Win32_MSVC_MSI.groovy +++ b/resources/jenkins/dsl/Reviews/Review_Win32_MSVC_MSI.groovy @@ -3,7 +3,7 @@ import common.Review def j = new Review ( name: 'Win32_MSVC_MSI', - libraries: 'Win32_MSVC', + libraries: ['Win32_MSVC'], label: 'MSVC', artifacts: 'build/*.msi' ).generate(this) @@ -18,7 +18,7 @@ j.with batchFile('''\ cd build call vcvarsall.bat - cmake -Werror=dev ../source -DCMAKE_BUILD_TYPE=release -DCMAKE_CXX_COMPILER=clcache -DCMAKE_PREFIX_PATH=%WORKSPACE%/libs/build/dist -GNinja -DWIN_SIGN_KEYSTORE=%WIN_SIGN_KEYSTORE% -DWIN_SIGN_KEYSTORE_PSW=%WIN_SIGN_KEYSTORE_PSW% -DWIN_SIGN_SUBJECT_NAME=%WIN_SIGN_SUBJECT_NAME% + cmake -Werror=dev ../source -DCMAKE_BUILD_TYPE=MinSizeRel -DCMAKE_CXX_COMPILER=clcache -DCMAKE_PREFIX_PATH=%WORKSPACE%/libs/build/dist -GNinja -DWIN_SIGN_KEYSTORE=%WIN_SIGN_KEYSTORE% -DWIN_SIGN_KEYSTORE_PSW=%WIN_SIGN_KEYSTORE_PSW% -DWIN_SIGN_SUBJECT_NAME=%WIN_SIGN_SUBJECT_NAME% '''.stripIndent().trim()) batchFile('''\ diff --git a/resources/jenkins/dsl/Reviews/Review_iOS_IPA.groovy b/resources/jenkins/dsl/Reviews/Review_iOS_IPA.groovy index dd03563..192f4f2 100644 --- a/resources/jenkins/dsl/Reviews/Review_iOS_IPA.groovy +++ b/resources/jenkins/dsl/Reviews/Review_iOS_IPA.groovy @@ -4,7 +4,7 @@ import static common.Constants.strip def j = new Review ( name: 'iOS_IPA', - libraries: 'iOS', + libraries: ['iOS'], label: 'iOS', artifacts: 'build/*.ipa' ).generate(this) diff --git a/resources/jenkins/dsl/common/Build.groovy b/resources/jenkins/dsl/common/Build.groovy index 3400061..e3fcd2f 100644 --- a/resources/jenkins/dsl/common/Build.groovy +++ b/resources/jenkins/dsl/common/Build.groovy @@ -13,15 +13,14 @@ class Build JobType jobType = JobType.Freestyle String name String namePrefix = '' - String libraries + String[] libraries String artifacts String label String trigger = '@daily' String excludePattern = 'source/**' - List oldBuilds = [7, 15] + List oldBuilds = [7, 30] boolean releaseJob = false boolean xunit = false - boolean cleanup = false boolean sendMail = true boolean disableChangelog = false boolean allowEmptyArtifacts = false @@ -52,7 +51,7 @@ class Build return buildName(getNamePrefix(), getName()) } - String getLibName() + String getLibName(String partialLibName) { def prefix = 'Libs_' @@ -61,7 +60,7 @@ class Build prefix = '${MERCURIAL_REVISION_BRANCH}_' + prefix } - return buildName(prefix, getLibraries(), true) + return buildName(prefix, partialLibName, true) } @@ -148,11 +147,21 @@ class Build steps { - if(getLibraries()) + String[] requestedLibs = getLibraries() + String defaultTargetDestDir = 'libs' + + for(String partLibName : requestedLibs) { - copyArtifacts(getLibName()) + String targetDestDir = defaultTargetDestDir + if(requestedLibs.length > 1) { - targetDirectory('libs') + String arch = partLibName.split('_').last() + targetDestDir += '/' + arch + } + + copyArtifacts(getLibName(partLibName)) + { + targetDirectory(targetDestDir) buildSelector { latestSuccessful(true) @@ -160,9 +169,9 @@ class Build } if(getName().contains('_Win')) - batchFile('cd libs/build & FOR %%a in (Toolchain_*) DO cmake -E tar xf %%a') + batchFile("cd ${targetDestDir}/build & FOR %%a in (Toolchain_*) DO cmake -E tar xf %%a") else - shell('cd libs/build; cmake -E tar xf Toolchain_*') + shell("cd ${targetDestDir}/build; cmake -E tar xf Toolchain_*") } if(getName().contains('_Win')) @@ -212,15 +221,14 @@ class Build } } - if(getCleanup()) + wsCleanup() { - wsCleanup() - { - cleanWhenUnstable(false) - cleanWhenFailure(false) - cleanWhenNotBuilt(false) - cleanWhenAborted(false) - } + cleanWhenUnstable(false) + cleanWhenFailure(false) + cleanWhenNotBuilt(false) + cleanWhenAborted(false) + excludePattern(getExcludePattern()) + deleteDirectories(true) } if(getSendMail()) diff --git a/resources/jenkins/dsl/common/Constants.groovy b/resources/jenkins/dsl/common/Constants.groovy index 1ab601d..79effca 100644 --- a/resources/jenkins/dsl/common/Constants.groovy +++ b/resources/jenkins/dsl/common/Constants.groovy @@ -2,7 +2,9 @@ package common class Constants { - static final AndroidArch = ["armeabi-v7a", "x86", "arm64-v8a"] + static final AndroidArchAPK = ["armeabi-v7a", "x86", "arm64-v8a"] + static final AndroidArchAAR = ["arm64-v8a"] + static final AndroidArch = (AndroidArchAPK + AndroidArchAAR).unique() static String strip(String content) { diff --git a/resources/jenkins/dsl/common/Library.groovy b/resources/jenkins/dsl/common/Library.groovy index 55d2ec6..f903f30 100644 --- a/resources/jenkins/dsl/common/Library.groovy +++ b/resources/jenkins/dsl/common/Library.groovy @@ -10,8 +10,7 @@ class Library extends Build String artifacts = 'build/Toolchain_*' String namePrefix = 'Libs_' // See copyArtifacts in Build.groovy String trigger = null - List oldBuilds = [30, -1] - boolean cleanup = true + List oldBuilds = [-1, 10] int timeout = -1 int weight = 2 } diff --git a/resources/jenkins/dsl/common/Release.groovy b/resources/jenkins/dsl/common/Release.groovy index 127ee53..fe599cc 100644 --- a/resources/jenkins/dsl/common/Release.groovy +++ b/resources/jenkins/dsl/common/Release.groovy @@ -11,7 +11,6 @@ class Release extends Build String trigger = null List oldBuilds = null boolean releaseJob = true - boolean cleanup = true boolean sendMail = false Job generate(DslFactory dslFactory) diff --git a/resources/jenkins/dsl/common/Review.groovy b/resources/jenkins/dsl/common/Review.groovy index 962aa0f..7c6ec57 100644 --- a/resources/jenkins/dsl/common/Review.groovy +++ b/resources/jenkins/dsl/common/Review.groovy @@ -8,6 +8,6 @@ class Review extends Trigger String label = 'Review' String trigger = null List oldBuilds = [14, -1] - int timeout = 60 + int timeout = 90 boolean review = true } diff --git a/resources/packaging/android/AndroidManifest.xml.aar.in b/resources/packaging/android/AndroidManifest.xml.aar.in new file mode 100644 index 0000000..734fc53 --- /dev/null +++ b/resources/packaging/android/AndroidManifest.xml.aar.in @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/packaging/android/AndroidManifest.xml.apk.in b/resources/packaging/android/AndroidManifest.xml.apk.in new file mode 100644 index 0000000..f6dae62 --- /dev/null +++ b/resources/packaging/android/AndroidManifest.xml.apk.in @@ -0,0 +1,176 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/packaging/android/AndroidManifest.xml.in b/resources/packaging/android/AndroidManifest.xml.in deleted file mode 100644 index 4939f67..0000000 --- a/resources/packaging/android/AndroidManifest.xml.in +++ /dev/null @@ -1,169 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/resources/packaging/android/build.gradle.append b/resources/packaging/android/build.gradle.append new file mode 100644 index 0000000..19efe91 --- /dev/null +++ b/resources/packaging/android/build.gradle.append @@ -0,0 +1,17 @@ + +task sourcesJar(type: Jar) { + from android.sourceSets.main.java.srcDirs + classifier = 'sources' +} + +dependencies { + compile "com.android.support:support-v4:21.0.3" +} + +allprojects { + repositories { + maven { + url "https://maven.google.com" + } + } +} diff --git a/resources/packaging/android/fileprovider.xml b/resources/packaging/android/fileprovider.xml new file mode 100644 index 0000000..e48c591 --- /dev/null +++ b/resources/packaging/android/fileprovider.xml @@ -0,0 +1,4 @@ + + + + diff --git a/resources/packaging/android/libAusweisApp2.so-deployment-settings.json.in b/resources/packaging/android/libAusweisApp2.so-deployment-settings.json.in index 00866a1..7986f07 100644 --- a/resources/packaging/android/libAusweisApp2.so-deployment-settings.json.in +++ b/resources/packaging/android/libAusweisApp2.so-deployment-settings.json.in @@ -6,9 +6,10 @@ "sdkBuildToolsRevision": "@ANDROID_BUILD_TOOLS_REVISION@", "toolchain-prefix": "@ANDROID_TOOLCHAIN_PREFIX@", "tool-prefix": "@ANDROID_TOOLCHAIN_MACHINE_NAME@", - "toolchain-version": "@CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION@", + "toolchain-version": "@ANDROID_NDK_TOOLCHAIN_VERSION@", "ndk-host": "@CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG@", "target-architecture": "@CMAKE_ANDROID_ARCH_ABI@", "android-package-source-directory": "@ANDROID_PACKAGE_SRC_DIR@", + "stdcpp-path": "@CMAKE_ANDROID_NDK@/sources/cxx-stl/@ANDROID_STL_PATH@/libs/@CMAKE_ANDROID_ARCH_ABI@/lib@CMAKE_ANDROID_STL_TYPE@.so", "application-binary": "@ANDROID_APP_BINARY@" } diff --git a/resources/packaging/android/pom.xml.in b/resources/packaging/android/pom.xml.in new file mode 100644 index 0000000..696384d --- /dev/null +++ b/resources/packaging/android/pom.xml.in @@ -0,0 +1,16 @@ + + + 4.0.0 + com.governikus + ausweisapp + @PROJECT_VERSION@ + aar + Governikus AusweisApp2 + https://github.com/Governikus/AusweisApp2/ + + + EUPL-v1.2 + https://eupl.eu/1.2/en/ + + + diff --git a/resources/packaging/linux/AusweisApp2.desktop.in b/resources/packaging/linux/AusweisApp2.desktop.in index 758337e..8a01027 100644 --- a/resources/packaging/linux/AusweisApp2.desktop.in +++ b/resources/packaging/linux/AusweisApp2.desktop.in @@ -5,6 +5,7 @@ Exec=@CMAKE_INSTALL_PREFIX@/bin/AusweisApp2 Icon=AusweisApp2 StartupNotify=true Terminal=false -Categories=Network;Utility +Categories=Utility;Accessibility; +GenericName=Authentication App Keywords=nPA,eID,eAT,Personalausweis,Aufenthaltstitel,Identity,Card Name=AusweisApp2 diff --git a/resources/packaging/macos/Info.plist b/resources/packaging/macos/Info.plist index b02a2ab..0d5b222 100644 --- a/resources/packaging/macos/Info.plist +++ b/resources/packaging/macos/Info.plist @@ -2,6 +2,11 @@ + LSEnvironment + + QT_MAC_DISABLE_FOREGROUND_APPLICATION_TRANSFORM + true + CFBundleDevelopmentRegion de_DE CFBundleDisplayName @@ -25,12 +30,14 @@ CFBundleSignature aaii NSHumanReadableCopyright - © 2014 Governikus GmbH & Co. KG + @CPACK_BUNDLE_COPYRIGHT@ LSBackgroundOnly 0 LSMinimumSystemVersion - 10.9.0 + 10.11.0 LSUIElement 1 + NSHighResolutionCapable + True diff --git a/resources/packaging/macos/autostart_helper/Info.plist b/resources/packaging/macos/autostart_helper/Info.plist new file mode 100644 index 0000000..376b3a2 --- /dev/null +++ b/resources/packaging/macos/autostart_helper/Info.plist @@ -0,0 +1,34 @@ + + + + + CFBundleDevelopmentRegion + de_DE + CFBundleDisplayName + @PROJECT_NAME@@AUTOSTART_HELPER_NAME@ + CFBundleExecutable + @PROJECT_NAME@@AUTOSTART_HELPER_NAME@ + CFBundleIdentifier + com.governikus.@PROJECT_NAME@.@AUTOSTART_HELPER_NAME@ + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + @PROJECT_NAME@@AUTOSTART_HELPER_NAME@ + CFBundlePackageType + APPL + CFBundleVersion + @VERSION@ + CFBundleShortVersionString + @VERSION@ + CFBundleSignature + aaii + NSHumanReadableCopyright + @COPYRIGHT_TEXT@ + LSBackgroundOnly + 1 + LSMinimumSystemVersion + 10.11.0 + NSPrincipalClass + NSApplication + + diff --git a/resources/packaging/macos/start-ausweisapp2.sh b/resources/packaging/macos/start-ausweisapp2.sh deleted file mode 100755 index eb483ba..0000000 --- a/resources/packaging/macos/start-ausweisapp2.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh - -realpath() -{ - [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}" -} - -AA2_BASE=`realpath "$0"` -AA2_CONTENTS="${AA2_BASE%/*/*}" -AA2_RESOURCES="$AA2_CONTENTS/Resources" -AA2_PLUGINS="$AA2_CONTENTS/PlugIns" - -export QT_LOGGING_CONF="$AA2_RESOURCES/qtlogging.ini" -export QT_MAC_DISABLE_FOREGROUND_APPLICATION_TRANSFORM=true -export QT_QPA_PLATFORM_PLUGIN_PATH=$AA2_PLUGINS - -# For debugging what libraries are loaded. -#export DYLD_PRINT_LIBRARIES=1 - -exec "$AA2_RESOURCES/@EXECUTABLE_NAME@" $@ diff --git a/resources/packaging/win/WIX.Texts.de-DE.wxl b/resources/packaging/win/WIX.Texts.de-DE.wxl index 73c5c0e..16976e2 100644 --- a/resources/packaging/win/WIX.Texts.de-DE.wxl +++ b/resources/packaging/win/WIX.Texts.de-DE.wxl @@ -35,6 +35,12 @@ Möchten Sie die Installation der [ProductName] wirklich abbrechen? 1031 + Personalausweis, Authentisierung + Installationspaket für die AusweisApp2 + Offizieller eID-Client des Bundes + https://www.ausweisapp.bund.de/fragen-und-antworten/support + https://www.ausweisapp.bund.de + https://www.ausweisapp.bund.de/download Eine aktuellere Version der [ProductName] ist bereits installiert. Die Installation wird nun beendet. Die [ProductName] unterstützt keine Windows Server. [ProductName] unterstützt nur Windows 7 SP1 und höher. diff --git a/resources/packaging/win/WIX.template.in b/resources/packaging/win/WIX.template.in index 6c9ecd7..8694e24 100644 --- a/resources/packaging/win/WIX.template.in +++ b/resources/packaging/win/WIX.template.in @@ -12,17 +12,27 @@ Manufacturer="$(var.CPACK_PACKAGE_VENDOR)" UpgradeCode="$(var.CPACK_WIX_UPGRADE_GUID)"> - + + + + + + + - + @@ -46,89 +56,29 @@ - - - - - - - SYSTEMSETTINGS - - - - - - - - - - - - - - - - SYSTEMSETTINGS and NOT Installed - - - - - - Installed and Not REINSTALL - - - - - - - - - - INSTALLDESKTOPSHORTCUT - - - - - - - - - - - - + + + - - + + + + + + + + + + + + + + - - - - + + + @@ -137,7 +87,7 @@ Control="Finish" Order="1" Event="DoAction" - Value="LaunchApplication">WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed + Value="LaunchApplication">(WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1) AND NOT Installed @@ -218,8 +168,8 @@ - - + + diff --git a/resources/packaging/win/install_settings.wxs b/resources/packaging/win/install_settings.wxs new file mode 100644 index 0000000..010cea8 --- /dev/null +++ b/resources/packaging/win/install_settings.wxs @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + SYSTEMSETTINGS = "true" + + + + + + + + + + + + + + (SYSTEMSETTINGS = "true") AND NOT Installed + + + + + + Installed AND NOT REINSTALL + + + + + + + + + + DESKTOPSHORTCUT = "true" + + + + + + + + + + + + + + + + STARTMENUSHORTCUT = "true" + + + + + + + + + diff --git a/resources/packaging/win/runtime_settings.wxs b/resources/packaging/win/runtime_settings.wxs new file mode 100644 index 0000000..0364e0c --- /dev/null +++ b/resources/packaging/win/runtime_settings.wxs @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + AUTOSTART = "true" + + + + + + + AUTOHIDE = "true" OR AUTOHIDE = "false" + + + + + + + REMINDTOCLOSE = "true" OR REMINDTOCLOSE = "false" + + + + + + + ASSISTANT = "true" OR ASSISTANT = "false" + + + + + + + TRANSPORTPINREMINDER = "true" OR TRANSPORTPINREMINDER = "false" + + + + + + + UPDATECHECK = "true" OR UPDATECHECK = "false" + + + + + + + ONSCREENKEYBOARD = "true" OR ONSCREENKEYBOARD = "false" + + + + + + + HISTORY = "true" OR HISTORY = "false" + + + + + + + diff --git a/resources/qml/+android/ContentArea.qml b/resources/qml/+android/ContentArea.qml deleted file mode 100644 index f32096a..0000000 --- a/resources/qml/+android/ContentArea.qml +++ /dev/null @@ -1,75 +0,0 @@ -import QtQuick 2.5 - -import Governikus.Global 1.0 -import Governikus.PinView 1.0 -import Governikus.InformationView 1.0 -import Governikus.RemoteServiceView 1.0 -import Governikus.FeedbackView 1.0 -import Governikus.DeveloperView 1.0 -import Governikus.IdentifyView 1.0 -import Governikus.ProviderView 1.0 -import Governikus.HistoryView 1.0 - -Item { - id: baseItem - property alias ready: identifyView.ready - readonly property var visibleItem: visibleChildren[0] - readonly property var currentSectionPage: if (visibleItem) visibleItem.currentSectionPage - - TabBarView { - id: identifyView - anchors.fill: parent - visible: baseItem.state === "identify" - sourceComponent: IdentifyView {} - } - - TabBarView { - anchors.fill: parent - visible: baseItem.state === "provider" - prefetch: identifyView.ready - sourceComponent: ProviderView {} - } - - TabBarView { - anchors.fill: parent - visible: baseItem.state === "history" - prefetch: identifyView.ready - sourceComponent: HistoryView {} - } - - TabBarView { - id: pinView - anchors.fill: parent - visible: baseItem.state === "pin" - prefetch: identifyView.ready - sourceComponent: PinView {} - } - - TabBarView { - anchors.fill: parent - visible: baseItem.state === "remoteservice" - prefetch: identifyView.ready - sourceComponent: RemoteServiceView {} - } - - TabBarView { - anchors.fill: parent - visible: baseItem.state === "feedback" - prefetch: identifyView.ready - sourceComponent: Feedback {} - } - - TabBarView { - anchors.fill: parent - visible: baseItem.state === "information" - prefetch: identifyView.ready - sourceComponent: Information {} - } - - TabBarView { - anchors.fill: parent - visible: baseItem.state === "developeroptions" - prefetch: identifyView.ready - sourceComponent: DeveloperView {} - } -} diff --git a/resources/qml/+desktop/main.qml b/resources/qml/+desktop/main.qml new file mode 100644 index 0000000..60b5267 --- /dev/null +++ b/resources/qml/+desktop/main.qml @@ -0,0 +1,139 @@ +import Governikus.Global 1.0 +import Governikus.TitleBar 1.0 +import Governikus.MainView 1.0 +import Governikus.IdentifyView 1.0 +import Governikus.ProviderView 1.0 +import Governikus.View 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.UiModule 1.0 + +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import QtGraphicalEffects 1.0 + + +ApplicationWindow { + readonly property int initialWidth: Utils.dp(1600) + readonly property int initialHeight: Utils.dp(1200) + + id: appWindow + visible: true + width: initialWidth + height: initialHeight + title: "AusweisApp2" + background: Item { + Rectangle { + anchors.fill: parent + color: Constants.background_color + visible: plugin.useFlatStyleOnDesktop() + } + Image { + anchors.fill: parent + fillMode: Image.PreserveAspectCrop + source: visible ? "qrc:///images/desktop/background.png" : "" + visible: !plugin.useFlatStyleOnDesktop() + + LinearGradient { + anchors.fill: parent + start: Qt.point(0, 0) + end: Qt.point(0, appWindow.height) + gradient: Gradient { + GradientStop { + position: 0 + color: "#AA659bcd" + } + GradientStop { + position: 1 + color: "#FF659bcd" + } + } + } + } + } + + onWidthChanged: setScaleFactor(); + onHeightChanged: setScaleFactor(); + function setScaleFactor() { + ApplicationModel.scaleFactor = Math.min(width / initialWidth, height / initialHeight) + } + + property int activeView: 0 + function activateView(pName) { + if (pName < 1) { + console.warn("Unknown view requested: " + pName) + return; + } + + activeView = pName + titleBar.updateActions() + } + + onClosing: { + hide() + plugin.hide(); + } + + Connections { + target: plugin + onFireShowRequest: { + showNormal() + raise() + requestActivate() + if (pModule === UiModule.IDENTIFY) activateView(SectionPage.Views.Identify) + } + } + + Action { + shortcut: "Ctrl+Alt+R" + onTriggered: plugin.developerBuild ? plugin.doRefresh() : "" + } + + menuBar: TitleBar { + id: titleBar + contentRoot: contentArea + onRootClicked: activateView(SectionPage.Views.Main) + navSuccessor: { + if (viewMain.visible) { + return viewMain + } + if (viewIdentify.visible) { + return viewIdentify.tabTarget + } + if (viewProvider.visible) { + return viewProvider + } + return null + } + } + + Item { + id: contentArea + anchors.fill: parent + + MainView { + id: viewMain + + visible: appWindow.activeView === 0 || appWindow.activeView === SectionPage.Views.Main + onVisibleChildrenChanged: titleBar.updateActions() + onNextView: activateView(pName) + navSuccessor: titleBar + } + + IdentifyView { + id: viewIdentify + + visible: appWindow.activeView === SectionPage.Views.Identify + onNextView: activateView(pName) + navSuccessor: titleBar + } + + ProviderView { + id: viewProvider + + visible: appWindow.activeView === SectionPage.Views.Provider + onVisibleChildrenChanged: titleBar.updateActions() + onNextView: activateView(pName) + navSuccessor: titleBar + } + } +} diff --git a/resources/qml/+ios/ContentArea.qml b/resources/qml/+ios/ContentArea.qml deleted file mode 100644 index 4234bbd..0000000 --- a/resources/qml/+ios/ContentArea.qml +++ /dev/null @@ -1,52 +0,0 @@ -import QtQuick 2.5 - -import Governikus.Global 1.0 -import Governikus.PinView 1.0 -import Governikus.MoreView 1.0 -import Governikus.IdentifyView 1.0 -import Governikus.ProviderView 1.0 -import Governikus.HistoryView 1.0 - -Item { - id: baseItem - property alias ready: identifyView.ready - readonly property var visibleItem: visibleChildren[0] - readonly property var currentSectionPage: if (visibleItem) visibleItem.currentSectionPage - - TabBarView { - id: identifyView - anchors.fill: parent - visible: baseItem.state === "identify" - sourceComponent: IdentifyView {} - } - - TabBarView { - anchors.fill: parent - visible: baseItem.state === "provider" - prefetch: identifyView.ready - sourceComponent: ProviderView {} - } - - TabBarView { - anchors.fill: parent - visible: baseItem.state === "history" - prefetch: identifyView.ready - sourceComponent: HistoryView {} - } - - TabBarView { - id: pinView - anchors.fill: parent - visible: baseItem.state === "pin" - prefetch: identifyView.ready - sourceComponent: PinView {} - } - - TabBarView { - anchors.fill: parent - visible: baseItem.state === "more" - prefetch: identifyView.ready - sourceComponent: MoreView {} - } - -} diff --git a/resources/qml/+mobile/main.qml b/resources/qml/+mobile/main.qml new file mode 100644 index 0000000..c062cae --- /dev/null +++ b/resources/qml/+mobile/main.qml @@ -0,0 +1,130 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import QtQuick.Window 2.2 + +import Governikus.Global 1.0 +import Governikus.TitleBar 1.0 +import Governikus.Navigation 1.0 +import Governikus.SplashScreen 1.0 +import Governikus.View 1.0 +import Governikus.FeedbackView 1.0 +import Governikus.Type.ApplicationModel 1.0 + +ApplicationWindow { + id: appWindow + visible: true + width: 750 / 2 //Screen.desktopAvailableWidth + height: 1334 / 2 + title: "Governikus AusweisApp2" + color: Constants.background_color + + property var lastCloseInvocation: 0 + + Action { + shortcut: "Ctrl+Alt+R" + onTriggered: plugin.developerBuild ? plugin.doRefresh() : "" + } + + Action { + shortcut: "Escape" + onTriggered: appWindow.close() + } + + menuBar: TitleBar { + id: titleBar + visible: !splashScreen.visible && contentArea.state !== "tutorial" + + titleBarOpacity: contentArea.visibleItem && contentArea.visibleItem.stack.currentItem ? contentArea.visibleItem.stack.currentItem.titleBarOpacity : 1 + + property var currentSectionPage: if (contentArea) contentArea.currentSectionPage + + leftAction: if (currentSectionPage) currentSectionPage.leftTitleBarAction + titleItem: if (currentSectionPage) currentSectionPage.headerTitleBarAction + rightAction: if (currentSectionPage) currentSectionPage.rightTitleBarAction + subTitleBarAction: if (currentSectionPage) currentSectionPage.subTitleBarAction + color: if (currentSectionPage) currentSectionPage.titleBarColor + } + + ContentAreaLoader { + id: contentArea + state: navBar.state + anchors.left: Constants.leftNavigation ? navBar.right : parent.left + anchors.top: parent.top + anchors.right: parent.right + anchors.bottom: Constants.bottomNavigation ? navBar.top : parent.bottom + } + + Navigation { + id: navBar + visible: !splashScreen.visible + anchors.left: parent.left + anchors.top: Constants.leftNavigation ? parent.top : undefined + anchors.right: Constants.bottomNavigation ? parent.right : undefined + anchors.bottom: parent.bottom + } + + onClosing: { // back button pressed + if (contentArea.visibleItem) + { + var activeStackView = contentArea.visibleItem.stack + var leftTitleBarAction = activeStackView.currentItem.leftTitleBarAction + + if (activeStackView.depth <= 1 + && (!leftTitleBarAction || leftTitleBarAction.state === "") + && contentArea.state != "tutorial") // Don't go back to "identify" section page when tutorial is active + { + if (contentArea.state != "identify") { + navBar.state = "identify" + navBar.currentIndex = 0 + } else { + var currentTime = new Date().getTime(); + if( currentTime - lastCloseInvocation < 1000 ) { + plugin.fireQuitApplicationRequest() + return + } + + lastCloseInvocation = currentTime + qmlExtension.showFeedback(qsTr("To close the app, quickly press the back button twice.")) + } + } + else if (leftTitleBarAction) { + if (navBar.isOpen) { + navBar.close() + } + else if (leftTitleBarAction.state !== "hidden") { + leftTitleBarAction.clicked(undefined) + } + } + } + + close.accepted = false + } + + StoreFeedbackPopup { + id: feedback + + x: (appWindow.width - width) / 2 + y: (appWindow.height - height) / 2 + + property alias ready: contentArea.ready + onReadyChanged: { + if (ApplicationModel.areStoreFeedbackDialogConditionsMet()) { + ApplicationModel.hideFutureStoreFeedbackDialogs() + feedback.open() + } + } + } + + SplashScreen { + id: splashScreen + color: appWindow.color + + property alias ready: contentArea.ready + onReadyChanged: { + splashScreen.hide() + if (!ApplicationModel.currentWorkflow && !settingsModel.showTutorialOnStart) { + navBar.lockedAndHidden = false + } + } + } +} diff --git a/resources/qml/ContentArea.qml b/resources/qml/ContentArea.qml deleted file mode 100644 index 4b11736..0000000 --- a/resources/qml/ContentArea.qml +++ /dev/null @@ -1,3 +0,0 @@ -Item { - id: dummy -} diff --git a/resources/qml/ContentAreaLoader.qml b/resources/qml/ContentAreaLoader.qml deleted file mode 100644 index fb5ecfd..0000000 --- a/resources/qml/ContentAreaLoader.qml +++ /dev/null @@ -1,28 +0,0 @@ -import QtQuick 2.5 - -import Governikus.Global 1.0 - -// This proxy Component interrupts the QML parsing process by using a URL for -// the Loader. The parsing of the included file is continued in a background -// process by the loader. This way the splash screen is shown while the Loader -// parses the given source. -Item { - id: baseItem - property bool ready: false - readonly property var visibleItem: if (loader.item) loader.item.visibleChildren[0] - readonly property var currentSectionPage: if (visibleItem) visibleItem.currentSectionPage - - Loader { - id: loader - anchors.fill: parent - - asynchronous: true - source: "ContentAreaSelector.qml" - onStatusChanged: { - if (status === Loader.Ready){ - baseItem.ready = Qt.binding(function() {return loader.item.ready}) - item.state = Qt.binding(function() {return baseItem.state}) - } - } - } -} diff --git a/resources/qml/ContentAreaSelector.qml b/resources/qml/ContentAreaSelector.qml deleted file mode 100644 index 2b5109e..0000000 --- a/resources/qml/ContentAreaSelector.qml +++ /dev/null @@ -1,8 +0,0 @@ -import QtQuick 2.5 - -// This proxy file is required since a Component loaded via the Loader.source URL -// does not know which platform selector is in charge. -ContentArea { - id: contentArea - anchors.fill: parent -} diff --git a/resources/qml/Governikus/ChangePinView/+android/ChangePinViewContent.qml b/resources/qml/Governikus/ChangePinView/+android/ChangePinViewContent.qml new file mode 100644 index 0000000..56d19e0 --- /dev/null +++ b/resources/qml/Governikus/ChangePinView/+android/ChangePinViewContent.qml @@ -0,0 +1,64 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.Type.ChangePinModel 1.0 + + +Item { + readonly property int spacing: (height - pinIcon.height - pinHeader.height - pinDesc.height - govButton.height - Utils.dp(40)) / 3 + + Image { + id: pinIcon + + height: parent.height * 0.25 + width: height + + anchors.top: parent.top + anchors.topMargin: spacing + anchors.horizontalCenter: parent.horizontalCenter + + fillMode: Image.PreserveAspectFit + smooth: true + source: "qrc:///images/icon_Pin.svg" + } + + Text { + id: pinHeader + + text: qsTr("PIN Management") + settingsModel.translationTrigger + + anchors.top: pinIcon.bottom + anchors.topMargin: spacing + anchors.horizontalCenter: parent.horizontalCenter + + font.pixelSize: Constants.header_font_size + color: Constants.blue + } + + Text { + id: pinDesc + color: Constants.secondary_text + + anchors.margins: Utils.dp(10) + anchors.top: pinHeader.bottom + anchors.topMargin: Utils.dp(10) + anchors.horizontalCenter: parent.horizontalCenter + + text: qsTr("You have the opportunity to change your transport PIN into a personal PIN. You can also change the PIN at any time or unblock the PIN using the personal unblocking key (PUK). The transport PIN and the PUK can be found in the letter sent to you by your competent authority.") + settingsModel.translationTrigger + font.pixelSize: Constants.normal_font_size + horizontalAlignment: Text.AlignHCenter + width: parent.width - Utils.dp(60) + wrapMode: Text.WordWrap + } + + GButton { + id: govButton + + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottomMargin: Utils.dp(30) + + text: qsTr("Change PIN now") + settingsModel.translationTrigger + onClicked: ChangePinModel.startWorkflow() + } +} diff --git a/resources/qml/Governikus/ChangePinView/+ios/ChangePinViewContent.qml b/resources/qml/Governikus/ChangePinView/+ios/ChangePinViewContent.qml new file mode 100644 index 0000000..f55910f --- /dev/null +++ b/resources/qml/Governikus/ChangePinView/+ios/ChangePinViewContent.qml @@ -0,0 +1,64 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.Type.ChangePinModel 1.0 + + +Item { + readonly property int spacing: (height - pinHeader.height - pinDesc.height - pinIcon.height - govButton.height - Utils.dp(40)) / 3 + + Text { + id: pinHeader + + text: qsTr("PIN Management") + settingsModel.translationTrigger + + anchors.top: parent.top + anchors.topMargin: spacing + anchors.horizontalCenter: parent.horizontalCenter + + font.pixelSize: Constants.header_font_size + color: Constants.blue + } + + Text { + id: pinDesc + color: Constants.secondary_text + + width: parent.width * 0.9 + + text: qsTr("You have the opportunity to change your transport PIN into a personal PIN. You can also change the PIN at any time or unblock the PIN using the personal unblocking key (PUK). The transport PIN and the PUK can be found in the letter sent to you by your competent authority.") + settingsModel.translationTrigger + font.pixelSize: Constants.normal_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + + anchors.top: pinHeader.bottom + anchors.topMargin: Utils.dp(10) + anchors.horizontalCenter: parent.horizontalCenter + } + + Image { + id: pinIcon + + height: parent.height * 0.25 + width: height + + anchors.top: pinDesc.bottom + anchors.topMargin: spacing + anchors.horizontalCenter: parent.horizontalCenter + + fillMode: Image.PreserveAspectFit + smooth: true + source: "qrc:///images/icon_Pin.svg" + } + + GButton { + id: govButton + + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottomMargin: Utils.dp(30) + + text: qsTr("Change PIN now") + settingsModel.translationTrigger + onClicked: ChangePinModel.startWorkflow() + } +} diff --git a/resources/qml/Governikus/ChangePinView/ChangePinController.qml b/resources/qml/Governikus/ChangePinView/ChangePinController.qml new file mode 100644 index 0000000..1b0cd8d --- /dev/null +++ b/resources/qml/Governikus/ChangePinView/ChangePinController.qml @@ -0,0 +1,139 @@ +import QtQuick 2.10 + +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.ChangePinModel 1.0 +import Governikus.Type.NumberModel 1.0 +import Governikus.Type.ReaderPlugIn 1.0 +import Governikus.Type.PacePasswordId 1.0 + + +Item { + enum WorkflowStates { + Initial, + Reader, + Card, + Update, + Puk, + Can, + Pin, + NewPin, + Processing + } + + QtObject { + id: d + readonly property int readerPlugInType: ChangePinModel.readerPlugInType + } + + id: controller + readonly property string currentState: ChangePinModel.currentState + readonly property bool bluetoothEnabled: ApplicationModel.bluetoothEnabled + + property bool showRemoveCardFeedback: false + property int workflowState: 0 + + function sufficientBluetoothRights() { + return ApplicationModel.bluetoothEnabled && (!ApplicationModel.locationPermissionRequired || locationPermissionConfirmed) + } + + property bool locationPermissionConfirmed: false + onLocationPermissionConfirmedChanged: { + // If the user has given location permission: continue Bluetooth workflow. + if (d.readerPlugInType === ReaderPlugIn.BLUETOOTH && sufficientBluetoothRights()) { + ChangePinModel.continueWorkflow() + } + } + + onBluetoothEnabledChanged: { + if (d.readerPlugInType === ReaderPlugIn.BLUETOOTH && sufficientBluetoothRights()) { + ChangePinModel.continueWorkflow() + } + } + + Connections { + target: ChangePinModel + + onFireNewContextSet: { + navBar.lockedAndHidden = true + navBar.state = "pin" + navBar.currentIndex = 3 + enterPinView.state = "INITIAL" + controller.workflowState = ChangePinController.WorkflowStates.Initial + ChangePinModel.setInitialPluginType() + } + + onFireCurrentStateChanged: processStateChange() + // This is necessary because onCurrentStateChanged is not + // working, when we need to process a state a second time + } + + function processStateChange() { + switch (currentState) { + case "": + break + case "StateSelectReader": + fireReplace(pinWorkflow) + if (d.readerPlugInType === ReaderPlugIn.BLUETOOTH && !sufficientBluetoothRights()) { + // Stop the workflow here until the user has enabled bluetooth and confirmed the location permission. + controller.workflowState = ChangePinController.WorkflowStates.Reader + } + else { + setPinWorkflowStateAndContinue(ChangePinController.WorkflowStates.Reader) + } + break + case "StateConnectCard": + setPinWorkflowStateAndContinue(ChangePinController.WorkflowStates.Card) + break + case "StatePreparePace": + fireReplace(pinProgressView) + setPinWorkflowStateAndContinue(ChangePinController.WorkflowStates.Update) + break + case "StateEnterPacePassword": + if (NumberModel.establishPaceChannelType == PacePasswordId.PACE_PIN) { + setPinWorkflowStateAndRequestInput(ChangePinController.WorkflowStates.Pin, "PIN") + } + else if (NumberModel.establishPaceChannelType == PacePasswordId.PACE_CAN) { + setPinWorkflowStateAndRequestInput(ChangePinController.WorkflowStates.Can, "CAN") + } + else if (NumberModel.establishPaceChannelType == PacePasswordId.PACE_PUK) { + setPinWorkflowStateAndRequestInput(ChangePinController.WorkflowStates.Puk, "PUK") + } + break + case "StateUnfortunateCardPosition": + baseItem.firePush(cardPositionView) + break + case "StateEnterNewPacePin": + setPinWorkflowStateAndRequestInput(ChangePinController.WorkflowStates.NewPin, "PIN_NEW") + break + case "StateCleanUpReaderManager": + controller.showRemoveCardFeedback = ChangePinModel.selectedReaderHasCard() && !ChangePinModel.error; + setPinWorkflowStateAndContinue(ChangePinController.WorkflowStates.Processing) + break + case "FinalState": + if (controller.showRemoveCardFeedback) { + controller.showRemoveCardFeedback = false + qmlExtension.showFeedback(qsTr("You may now remove your ID card from the device.")) + } + baseItem.firePush(pinResult) + navBar.lockedAndHidden = true + break + default: + ChangePinModel.continueWorkflow() + } + } + + function setPinWorkflowStateAndContinue(pState) { + controller.workflowState = pState + ChangePinModel.continueWorkflow() + } + + function setPinWorkflowStateAndRequestInput(pState, pInput) { + controller.workflowState = pState + if (ChangePinModel.isBasicReader) { + enterPinView.state = pInput + baseItem.firePush(enterPinView) + } else { + ChangePinModel.continueWorkflow() + } + } +} diff --git a/resources/qml/Governikus/ChangePinView/ChangePinView.qml b/resources/qml/Governikus/ChangePinView/ChangePinView.qml new file mode 100644 index 0000000..143874c --- /dev/null +++ b/resources/qml/Governikus/ChangePinView/ChangePinView.qml @@ -0,0 +1,113 @@ +import QtQuick 2.10 + +import Governikus.EnterPinView 1.0 +import Governikus.Global 1.0 +import Governikus.TitleBar 1.0 +import Governikus.ProgressView 1.0 +import Governikus.ResultView 1.0 +import Governikus.View 1.0 +import Governikus.Workflow 1.0 +import Governikus.Type.ChangePinModel 1.0 +import Governikus.Type.NumberModel 1.0 + + +SectionPage { + id: baseItem + + disableFlicking: true + headerTitleBarAction: TitleBarAction { text: qsTr("PIN Management") + settingsModel.translationTrigger; font.bold: true } + + ChangePinController { + id: changePinController + } + + content: ChangePinViewContent { + height: baseItem.height + width: baseItem.width + } + + GeneralWorkflow { + id: pinWorkflow + visible: false + + controller: changePinController + workflowModel: ChangePinModel + workflowTitle: qsTr("PIN Management") + settingsModel.translationTrigger + + waitingFor: switch (changePinController.workflowState) { + case ChangePinController.WorkflowStates.Reader: + return Workflow.WaitingFor.Reader + case ChangePinController.WorkflowStates.Card: + return Workflow.WaitingFor.Card + case ChangePinController.WorkflowStates.Puk: + case ChangePinController.WorkflowStates.Can: + case ChangePinController.WorkflowStates.Pin: + case ChangePinController.WorkflowStates.NewPin: + return Workflow.WaitingFor.Password + default: + return Workflow.WaitingFor.None + } + } + + ResultView { + id: cardPositionView + headerTitleBarAction: TitleBarAction { text: qsTr("PIN Management") + settingsModel.translationTrigger; font.bold: true } + resultType: ResultView.Type.IsInfo + buttonText: qsTr("Retry") + settingsModel.translationTrigger + text: qsTr("Weak NFC signal. Please reposition your card.") + settingsModel.translationTrigger + onClicked: { + firePop() + ChangePinModel.continueWorkflow() + } + visible: false + } + + ResultView { + id: pinResult + headerTitleBarAction: TitleBarAction { text: qsTr("PIN Management") + settingsModel.translationTrigger; font.bold: true } + resultType: ChangePinModel.error ? ResultView.Type.IsError : ResultView.Type.IsSuccess + text: ChangePinModel.resultString + onClicked: { + ChangePinModel.continueWorkflow() + firePopAll() + navBar.lockedAndHidden = false + } + visible: false + } + + EnterPinView { + id: enterPinView + leftTitleBarAction: TitleBarAction { state: "cancel"; onClicked: ChangePinModel.cancelWorkflow() } + headerTitleBarAction: TitleBarAction { text: qsTr("Change PIN") + settingsModel.translationTrigger } + visible: false + + onPinEntered: { + firePop() + ChangePinModel.continueWorkflow() + } + + onChangePinLength: NumberModel.requestTransportPin = !NumberModel.requestTransportPin + } + + ProgressView { + id: pinProgressView + leftTitleBarAction: TitleBarAction { state: ChangePinModel.isBasicReader ? "cancel" : "hidden"; onClicked: ChangePinModel.cancelWorkflow() } + headerTitleBarAction: TitleBarAction { text: qsTr("PIN Management") + settingsModel.translationTrigger; font.bold: true } + visible: false + text: qsTr("Change PIN") + settingsModel.translationTrigger + subText: (!visible ? "" + : ChangePinModel.isBasicReader ? qsTr("Please wait a moment...") + : !!NumberModel.inputError ? NumberModel.inputError + : NumberModel.pinDeactivated ? qsTr("The online identification function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the online identification function.") + : changePinController.workflowState === ChangePinController.WorkflowStates.Update + || changePinController.workflowState === ChangePinController.WorkflowStates.Pin + || changePinController.workflowState === ChangePinController.WorkflowStates.NewPin ? qsTr("Please observe the display of your card reader.") + : changePinController.workflowState === ChangePinController.WorkflowStates.Can ? qsTr("You have entered the wrong PIN twice. Prior to a third attempt, you have to enter your six-digit card access number first. You can find your card access number on the front of your ID card.") + : changePinController.workflowState === ChangePinController.WorkflowStates.Puk ? qsTr("You have entered a wrong PIN three times. Your PIN is now blocked. You have to enter the PUK now for unblocking.") + : qsTr("Please wait a moment...")) + settingsModel.translationTrigger + subTextColor: !ChangePinModel.isBasicReader && (NumberModel.inputError + || NumberModel.pinDeactivated + || changePinController.workflowState === ChangePinController.WorkflowStates.Can + || changePinController.workflowState === ChangePinController.WorkflowStates.Puk) ? Constants.red : Constants.secondary_text + } +} diff --git a/resources/qml/Governikus/ChangePinView/qmldir b/resources/qml/Governikus/ChangePinView/qmldir new file mode 100644 index 0000000..68fde0b --- /dev/null +++ b/resources/qml/Governikus/ChangePinView/qmldir @@ -0,0 +1,6 @@ +module ChangePinView + +internal ChangePinController ChangePinController.qml +internal ChangePinViewContent ChangePinViewContent.qml + +ChangePinView 1.0 ChangePinView.qml diff --git a/resources/qml/Governikus/DeveloperView/DeveloperView.qml b/resources/qml/Governikus/DeveloperView/DeveloperView.qml index 40cecc0..fb07abf 100644 --- a/resources/qml/Governikus/DeveloperView/DeveloperView.qml +++ b/resources/qml/Governikus/DeveloperView/DeveloperView.qml @@ -1,9 +1,9 @@ -import QtQuick 2.5 -import QtQuick.Layouts 1.2 -import QtQuick.Controls 2.0 +import QtQuick 2.10 +import QtQuick.Controls 2.3 import Governikus.Global 1.0 import Governikus.TitleBar 1.0 +import Governikus.View 1.0 SectionPage { @@ -11,87 +11,121 @@ SectionPage { leftTitleBarAction: TitleBarAction { state: !topLevelPage ? "back" : ""; onClicked: firePop() } headerTitleBarAction: TitleBarAction { text: qsTr("Developer options") + settingsModel.translationTrigger; font.bold: true } - content: Item - { - height: pane.height + 2 * Constants.component_spacing + content: Column { + id: mainColumn width: root.width + padding: Constants.pane_padding + spacing: Constants.component_spacing - Column - { - anchors.fill: parent - anchors.margins: Constants.component_spacing + readonly property int usableWidth: width - 2 * padding - Pane { - id: pane + Item { + id: testUriContainer + width: parent.usableWidth + height: Math.max(testUriText.height, testUriSwitch.height) - GroupBox { - title: "Change the layout style:" - height: implicitHeight - width: implicitWidth - - RowLayout { - anchors.fill: parent - - RadioButton { - text: "iOS" - checked: plugin.platformStyle === text.toLowerCase() - onCheckedChanged: if (checked) { plugin.applyPlatformStyle(text.toLowerCase()) } - } - RadioButton { - text: "Android" - checked: plugin.platformStyle === text.toLowerCase() - onCheckedChanged: if (checked) { plugin.applyPlatformStyle(text.toLowerCase()) } - } - RadioButton { - text: "Tablet,Android" - checked: plugin.platformStyle === text.toLowerCase() - onCheckedChanged: if (checked) { plugin.applyPlatformStyle(text.toLowerCase()) } - } - } + Item { + id: testUriText + height: testUriNameText.height + testUriDateText.height + anchors.left: testUriContainer.left + anchors.right: testUriSwitch.left + anchors.rightMargin: Constants.component_spacing + anchors.verticalCenter: testUriContainer.verticalCenter + Text { + id: testUriNameText + anchors.bottomMargin: Utils.dp(2) + font.pixelSize: Utils.dp(16) + color: Constants.secondary_text + opacity: 0.87 + text: qsTr("Test environment") + settingsModel.translationTrigger } - GroupBox { - title: "Developer Mode:" - height: implicitHeight - width: implicitWidth - - RowLayout { - anchors.fill: parent - - RadioButton { - text: "Enabled" - checked: settingsModel.developerMode - onCheckedChanged: if (checked) { settingsModel.developerMode = true } - } - RadioButton { - text: "Disabled" - checked: !settingsModel.developerMode - onCheckedChanged: if (checked) { settingsModel.developerMode = false } - } - } - } - - GroupBox { - title: "Use test uri for selfauthentication:" - height: implicitHeight - width: implicitWidth - - RowLayout { - anchors.fill: parent - - RadioButton { - text: "Enabled" - checked: settingsModel.useSelfauthenticationTestUri - onCheckedChanged: if (checked) { settingsModel.useSelfauthenticationTestUri = true } - } - RadioButton { - text: "Disabled" - checked: !settingsModel.useSelfauthenticationTestUri - onCheckedChanged: if (checked) { settingsModel.useSelfauthenticationTestUri = false } - } - } + Text { + id: testUriDateText + width: parent.width + anchors.top: testUriNameText.bottom + font.pixelSize: Utils.dp(14) + color: Constants.secondary_text + opacity: 0.38 + text: qsTr("Use the test environment during a selfauthentication") + settingsModel.translationTrigger + wrapMode: Text.WordWrap } } + + GSwitch { + id: testUriSwitch + anchors.right: testUriContainer.right + anchors.verticalCenter: testUriContainer.verticalCenter + initialState: settingsModel.useSelfauthenticationTestUri + onSwitched: settingsModel.useSelfauthenticationTestUri = testUriSwitch.isOn + } + } + + Item { + id: devModeContainer + width: parent.usableWidth + height: Math.max(devModeText.height, devModeSwitch.height) + + Item { + id: devModeText + height: devModeNameText.height + devModeDateText.height + anchors.left: devModeContainer.left + anchors.right: devModeSwitch.left + anchors.rightMargin: Constants.component_spacing + anchors.verticalCenter: devModeContainer.verticalCenter + Text { + id: devModeNameText + anchors.bottomMargin: Utils.dp(2) + font.pixelSize: Utils.dp(16) + color: Constants.secondary_text + opacity: 0.87 + text: qsTr("Developer Mode") + settingsModel.translationTrigger + } + + Text { + id: devModeDateText + width: parent.width + anchors.top: devModeNameText.bottom + font.pixelSize: Utils.dp(14) + color: Constants.secondary_text + opacity: 0.38 + text: qsTr("Use a more tolerant mode") + settingsModel.translationTrigger + wrapMode: Text.WordWrap + } + } + + GSwitch { + id: devModeSwitch + anchors.right: devModeContainer.right + anchors.verticalCenter: devModeContainer.verticalCenter + initialState: settingsModel.developerMode + onSwitched: settingsModel.developerMode = devModeSwitch.isOn + } + } + + Text { + text: qsTr("Change the layout style") + settingsModel.translationTrigger + font.pixelSize: Constants.normal_font_size + font.bold: true + color: Constants.blue + } + + GRadioButton { + text: qsTr("iOS") + settingsModel.translationTrigger + checked: plugin.platformStyle === text.toLowerCase() + onCheckedChanged: if (checked) { plugin.applyPlatformStyle(text.toLowerCase()) } + } + + GRadioButton { + text: qsTr("Android") + settingsModel.translationTrigger + checked: plugin.platformStyle === text.toLowerCase() + onCheckedChanged: if (checked) { plugin.applyPlatformStyle(text.toLowerCase()) } + } + + GRadioButton { + text: qsTr("Tablet, Android") + settingsModel.translationTrigger + checked: plugin.platformStyle === text.toLowerCase() + onCheckedChanged: if (checked) { plugin.applyPlatformStyle(text.toLowerCase()) } } } } diff --git a/resources/qml/Governikus/DeveloperView/qmldir b/resources/qml/Governikus/DeveloperView/qmldir index fc88bd6..d323dc5 100644 --- a/resources/qml/Governikus/DeveloperView/qmldir +++ b/resources/qml/Governikus/DeveloperView/qmldir @@ -1,2 +1,3 @@ module DeveloperView + DeveloperView 1.0 DeveloperView.qml diff --git a/resources/qml/Governikus/EnterPinView/EnterPinView.qml b/resources/qml/Governikus/EnterPinView/EnterPinView.qml index d215ea7..ca037d6 100644 --- a/resources/qml/Governikus/EnterPinView/EnterPinView.qml +++ b/resources/qml/Governikus/EnterPinView/EnterPinView.qml @@ -1,13 +1,19 @@ -import QtQuick 2.5 +import QtQuick 2.10 import QtQuick.Layouts 1.1 import Governikus.Global 1.0 +import Governikus.View 1.0 +import Governikus.Type.RemoteServiceModel 1.0 +import Governikus.Type.NumberModel 1.0 + SectionPage { id: baseItem property string remoteDeviceId: "" + property alias enableTransportPinLink: transportPinLink.enableTransportPinLink signal pinEntered() + signal changePinLength() onVisibleChanged: { pinField.text = "" @@ -27,7 +33,7 @@ SectionPage Item {/*spacer*/ Layout.fillWidth: true; height: parent.height} Image { - anchors.verticalCenter: parent.verticalCenter + Layout.alignment: Qt.AlignVCenter width: Utils.dp(58) height: width // because RowLayout uses implicitHeight that is based on sourceSize we have to explicitly set the sourceSize @@ -38,31 +44,52 @@ SectionPage } Text { - anchors.verticalCenter: parent.verticalCenter + Layout.alignment: Qt.AlignVCenter Layout.fillWidth: true Layout.maximumWidth: Math.ceil(implicitWidth) wrapMode: Text.WordWrap - font.pixelSize: text.length > 150 && !PlatformConstants.is_tablet? Utils.dp(15) : Constants.header_font_size + font.pixelSize: text.length > 150 && !Constants.is_tablet? Utils.dp(15) : Constants.header_font_size font.bold: true color: { - if (!pinField.confirmedInput || !!numberModel.inputError || baseItem.state === "CAN" || baseItem.state === "PUK") { + if (!pinField.confirmedInput || !!NumberModel.inputError || baseItem.state === "CAN" || baseItem.state === "PUK") { Constants.red } else { Constants.blue } } - text: (!pinField.confirmedInput ? qsTr("The entered PIN does not match the new PIN. Please correct your input.") : - !!numberModel.inputError ? numberModel.inputError : - baseItem.state === "CAN" && numberModel.isCanAllowedMode ? qsTr("Please enter the six-digit card access number. You can find the card access number on the front of the ID card.") : - baseItem.state === "CAN" ? qsTr("You have entered the wrong PIN twice. Prior to a third attempt, you have to enter your six-digit card access number first. You can find your card access number on the front of your ID card.") : - baseItem.state === "PUK" ? qsTr("You have entered a wrong PIN three times. Your PIN is now blocked. You have to enter the PUK now for unblocking.") : - baseItem.state === "PIN_NEW" ? qsTr("Please enter a new 6-digit PIN of your choice.") : - baseItem.state === "PIN_NEW_AGAIN" ? qsTr("Please enter your new 6-digit PIN again.") : - baseItem.state === "PIN" ? qsTr("Please enter your personal PIN.") : - baseItem.state === "REMOTE_PIN" ? qsTr("Enter the pairing code shown on your other device to use it as a card reader.") : - /*"PIN_OR_TRANSPORT_PIN"*/ qsTr("Please enter your current PIN or your initial transport PIN first.") - ) + settingsModel.translationTrigger + text: { + settingsModel.translationTrigger + + if (!pinField.confirmedInput) { + return qsTr("The entered PIN does not match the new PIN. Please correct your input.") + } + if (!!NumberModel.inputError) { + return NumberModel.inputError + } + if (baseItem.state === "CAN") { + if (NumberModel.isCanAllowedMode) { + return qsTr("Please enter the six-digit card access number. You can find the card access number on the front of the ID card.") + } + return qsTr("You have entered the wrong PIN twice. Prior to a third attempt, you have to enter your six-digit card access number first. You can find your card access number on the front of your ID card.") + } + if (baseItem.state === "PUK") { + return qsTr("You have entered a wrong PIN three times. Your PIN is now blocked. You have to enter the PUK now for unblocking.") + } + if (baseItem.state === "PIN_NEW") { + return qsTr("Please enter a new 6-digit PIN of your choice.") + } + if (baseItem.state === "PIN_NEW_AGAIN") { + return qsTr("Please enter your new 6-digit PIN again.") + } + if (baseItem.state === "PIN" && NumberModel.requestTransportPin) { + return qsTr("Please enter your transport PIN.") + } + if (baseItem.state === "REMOTE_PIN") { + return qsTr("Enter the pairing code shown on your other device to use it as a card reader.") + } + return qsTr("Please enter your personal PIN.") + } } Item {/*spacer*/ Layout.fillWidth: true; height: parent.height} } @@ -71,16 +98,40 @@ SectionPage PinField { id: pinField - anchors.horizontalCenter: parent.horizontalCenter - state: baseItem.state + Layout.alignment: Qt.AlignHCenter + passwordLength: baseItem.state === "REMOTE_PIN" ? 4 + : baseItem.state === "PIN" && NumberModel.requestTransportPin ? 5 + : baseItem.state === "PUK" ? 10 + : 6 + Layout.preferredHeight: height Layout.preferredWidth: width } + Text { + id: transportPinLink + property alias enableTransportPinLink: myMouse.enabled + + visible: baseItem.state === "PIN" && enableTransportPinLink + text: (NumberModel.requestTransportPin ? qsTr("Your PIN has 6 digits?") : qsTr("Your PIN has 5 digits?")) + settingsModel.translationTrigger + Layout.alignment: Qt.AlignHCenter + font.pixelSize: Constants.small_font_size + color: Constants.blue + font.underline: true + + MouseArea { + id: myMouse + enabled: true + anchors.fill: parent + anchors.margins: -Utils.dp(12) + onClicked: baseItem.changePinLength() + } + } + Item {/*spacer*/ height: Constants.component_spacing; width: parent.width } PinPad { - anchors.horizontalCenter: parent.horizontalCenter state: baseItem.state + Layout.alignment: Qt.AlignHCenter Layout.preferredWidth: width Layout.preferredHeight: height @@ -91,9 +142,7 @@ SectionPage onSubmitPressed: { switch(baseItem.state) { case "PIN": - /* fall through */ - case "PIN_OR_TRANSPORT_PIN": - numberModel.pin = pinField.text + NumberModel.pin = pinField.text baseItem.pinEntered() break case "PIN_NEW": @@ -102,19 +151,19 @@ SectionPage baseItem.state = "PIN_NEW_AGAIN" break case "PIN_NEW_AGAIN": - numberModel.newPin = pinField.text + NumberModel.newPin = pinField.text baseItem.pinEntered() break case "CAN": - numberModel.can = pinField.text + NumberModel.can = pinField.text baseItem.pinEntered() break case "PUK": - numberModel.puk = pinField.text + NumberModel.puk = pinField.text baseItem.pinEntered() break case "REMOTE_PIN": - remoteServiceModel.connectToServer(remoteDeviceId, pinField.text) + RemoteServiceModel.connectToServer(remoteDeviceId, pinField.text) baseItem.pinEntered() break } @@ -123,5 +172,4 @@ SectionPage Item {/*spacer*/ Layout.fillHeight: true; width: parent.width } } - } diff --git a/resources/qml/Governikus/EnterPinView/PinField.qml b/resources/qml/Governikus/EnterPinView/PinField.qml index 2f09d64..7f34f8e 100644 --- a/resources/qml/Governikus/EnterPinView/PinField.qml +++ b/resources/qml/Governikus/EnterPinView/PinField.qml @@ -1,4 +1,4 @@ -import QtQuick 2.7 +import QtQuick 2.10 import Governikus.Global 1.0 @@ -6,6 +6,7 @@ Item { id: baseItem property alias text: echoField.text + property int passwordLength: 6 property string inputConfirmation readonly property bool confirmedInput: inputConfirmation.length != text.length || inputConfirmation === text readonly property bool validInput: echoField.acceptableInput && confirmedInput @@ -26,25 +27,21 @@ Item { color: Constants.secondary_text verticalAlignment: TextInput.AlignVCenter echoMode: TextInput.Password - font.pixelSize: Utils.sp(18) + font.pixelSize: Utils.dp(18) font.letterSpacing: Utils.dp(10) passwordMaskDelay: 500 cursorVisible: false activeFocusOnPress: false focus: false validator: RegExpValidator { - regExp: baseItem.state === "PUK" ? /[0-9]{10}/ : - baseItem.state === "PIN_OR_TRANSPORT_PIN" ? /[0-9]{5,6}/ : - baseItem.state === "REMOTE_PIN" ? /[0-9]{4}/ : /[0-9]{6}/ + regExp: new RegExp("[0-9]{" + echoField.maximumLength + "}") } - maximumLength: baseItem.state === "PUK" ? 10 : - baseItem.state === "REMOTE_PIN" ? 4 : 6 + maximumLength: baseItem.passwordLength clip: true } TextInput { id: sample - color: Constants.secondary_text visible: false echoMode: echoField.echoMode font: echoField.font @@ -58,14 +55,12 @@ Item { anchors.left: echoField.left Repeater { - model: baseItem.state === "PUK" ? 10 : - baseItem.state === "REMOTE_PIN" ? 4 : 6 - delegate: - Rectangle { - width: sample.contentWidth - sample.font.letterSpacing - height: 1 - color: "black" - } + model: baseItem.passwordLength + delegate: Rectangle { + width: sample.contentWidth - sample.font.letterSpacing + height: 1 + color: "black" + } } } } diff --git a/resources/qml/Governikus/EnterPinView/PinPad.qml b/resources/qml/Governikus/EnterPinView/PinPad.qml index 47232f0..fccc4c6 100644 --- a/resources/qml/Governikus/EnterPinView/PinPad.qml +++ b/resources/qml/Governikus/EnterPinView/PinPad.qml @@ -1,4 +1,4 @@ -import QtQuick 2.5 +import QtQuick 2.10 import QtQuick.Layouts 1.1 import Governikus.Global 1.0 diff --git a/resources/qml/Governikus/EnterPinView/PinPadButton.qml b/resources/qml/Governikus/EnterPinView/PinPadButton.qml index 80a6f6e..0e62880 100644 --- a/resources/qml/Governikus/EnterPinView/PinPadButton.qml +++ b/resources/qml/Governikus/EnterPinView/PinPadButton.qml @@ -1,4 +1,4 @@ -import QtQuick 2.5 +import QtQuick 2.10 import QtGraphicalEffects 1.0 import Governikus.Global 1.0 @@ -11,7 +11,7 @@ MouseArea { Text { id: textItem anchors.centerIn: parent - font.pixelSize: Utils.sp(24) + font.pixelSize: Utils.dp(24) wrapMode: Text.WordWrap color: Constants.secondary_text } diff --git a/resources/qml/Governikus/EnterPinView/qmldir b/resources/qml/Governikus/EnterPinView/qmldir index 94099f3..f333009 100644 --- a/resources/qml/Governikus/EnterPinView/qmldir +++ b/resources/qml/Governikus/EnterPinView/qmldir @@ -1,2 +1,7 @@ module EnterPinView + +internal PinField PinField.qml +internal PinPadButton PinPadButton.qml +internal PinPad PinPad.qml + EnterPinView 1.0 EnterPinView.qml diff --git a/resources/qml/Governikus/FeedbackView/+mobile/Feedback.qml b/resources/qml/Governikus/FeedbackView/+mobile/Feedback.qml new file mode 100644 index 0000000..7f01216 --- /dev/null +++ b/resources/qml/Governikus/FeedbackView/+mobile/Feedback.qml @@ -0,0 +1,113 @@ +import QtQuick 2.10 +import QtQuick.Layouts 1.2 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 +import Governikus.Type.LogModel 1.0 + +import Governikus.Type.ApplicationModel 1.0 + + +SectionPage { + id: root + headerTitleBarAction: TitleBarAction { text: qsTr("Help & Feedback") + settingsModel.translationTrigger; font.bold: true } + + Component { + id: lineSeparator + Rectangle { + height: 1 + color: Constants.grey + } + } + Component { + id: subMenu + Item { + height: column.height + Column { + id: column + anchors.left: parent.left + anchors.right: parent.right + spacing: Constants.component_spacing + Text { + width: parent.width + font.pixelSize: Utils.dp(18) + color: Constants.blue + wrapMode: Text.WordWrap + text: titleText + } + Text { + color: Constants.secondary_text + width: parent.width + font.pixelSize: Constants.normal_font_size + wrapMode: Text.WordWrap + text: descriptionText + } + } + MouseArea { + anchors.fill: parent + onClicked: onClickFunction() + } + } + } + Log { + id: logPage + visible: false + } + + content: Item { + width: root.width + height: childrenRect.height + + Column { + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: Constants.component_spacing + spacing: Constants.component_spacing + padding: Constants.component_spacing + + Pane { + Loader { + readonly property string titleText: qsTr("FAQ") + settingsModel.translationTrigger + readonly property string descriptionText: qsTr("Do you have questions how to use AusweisApp2?") + settingsModel.translationTrigger + function onClickFunction() { Qt.openUrlExternally(qsTr("https://www.ausweisapp.bund.de/en/questions-and-answers/frequently-asked-questions/")) } + width: parent.width + sourceComponent: subMenu + } + Loader { width: parent.width; sourceComponent: lineSeparator } + Loader { + readonly property string titleText: qsTr("Support") + settingsModel.translationTrigger + readonly property string descriptionText: qsTr("You need further help?") + settingsModel.translationTrigger + function onClickFunction() { Qt.openUrlExternally(qsTr("https://www.ausweisapp.bund.de/en/questions-and-answers/support/")) } + width: parent.width + sourceComponent: subMenu + } + Loader { width: parent.width; sourceComponent: lineSeparator } + Loader { + readonly property string titleText: qsTr("Rate AusweisApp2") + settingsModel.translationTrigger + readonly property string descriptionText: qsTr("Please rate us in the Google Play Store.") + settingsModel.translationTrigger + function onClickFunction() { Qt.openUrlExternally("market://details?id=" + ApplicationModel.packageName) } + width: parent.width + sourceComponent: subMenu + } + Loader { width: parent.width; sourceComponent: lineSeparator } + Loader { + readonly property string titleText: qsTr("Report error") + settingsModel.translationTrigger + readonly property string descriptionText: qsTr("You found a bug? Please tell us, so we can fix it.") + settingsModel.translationTrigger + function onClickFunction() { LogModel.mailLog() } + width: parent.width + sourceComponent: subMenu + } + Loader { width: parent.width; sourceComponent: lineSeparator } + Loader { + readonly property string titleText: qsTr("Show log") + settingsModel.translationTrigger + readonly property string descriptionText: qsTr("You can view the logs of the AusweisApp2 here.") + settingsModel.translationTrigger + function onClickFunction() { firePush(logPage) } + width: parent.width + sourceComponent: subMenu + } + } + } + } +} diff --git a/resources/qml/Governikus/FeedbackView/+mobile/Log.qml b/resources/qml/Governikus/FeedbackView/+mobile/Log.qml new file mode 100644 index 0000000..94080ce --- /dev/null +++ b/resources/qml/Governikus/FeedbackView/+mobile/Log.qml @@ -0,0 +1,143 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 +import Governikus.Type.LogModel 1.0 + + +SectionPage +{ + id: root + leftTitleBarAction: TitleBarAction { state: "back"; onClicked: firePop() } + headerTitleBarAction: TitleBarAction { text: qsTr("Log") + settingsModel.translationTrigger; font.bold: true } + rightTitleBarAction: LogTitleBarControls { + allowRemove: comboBox.currentIndex > 0 + + onShare: { + LogModel.shareLog() + } + + onRemove: { + confirmationPopup.deleteAll = false + confirmationPopup.open() + } + + onRemoveAll: { + confirmationPopup.deleteAll = true + confirmationPopup.open() + } + } + + Item { + id: logSelector + height: comboBox.height + width: parent.width - 2 * Constants.pane_padding + anchors.top: parent.top + anchors.topMargin: Constants.pane_padding + anchors.horizontalCenter: parent.horizontalCenter + + Text { + id: comboText + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + + color: Constants.black + font.pixelSize: Constants.normal_font_size + text: qsTr("Log:") + settingsModel.translationTrigger + } + + GComboBox { + id: comboBox + width: parent.width - comboText.width - Constants.component_spacing + anchors.right: parent.right + model: LogModel.logFiles + onActivated: LogModel.setLogfile(comboBox.currentIndex) + } + } + + Pane { + id: pane + anchors.top: logSelector.bottom + anchors.bottom: parent.bottom + anchors.margins: Constants.pane_padding + + Item { + height: pane.height - 2 * Constants.pane_padding + width: pane.width - 2 * Constants.pane_padding + + ListView { + id: logView + clip: true + model: LogModel + anchors.fill: parent + maximumFlickVelocity: Constants.scrolling_speed + + delegate: Text { + width: pane.width - 2* Constants.pane_padding + wrapMode: Text.Wrap + font.pixelSize: Constants.small_font_size + font.bold: !(index % 2) + text: model.display + textFormat: Text.PlainText + } + + onAtYEndChanged: LogModel.autoFlick = atYEnd + + onContentYChanged: { + if (visibleArea.yPosition < 0.1) { + LogModel.moveView(-20) + return + } + + if (visibleArea.yPosition + visibleArea.heightRatio > 0.9) { + LogModel.moveView(20) + } + } + + Connections { + target: LogModel + onFireNewLogMsg: logView.positionViewAtEnd() + } + } + + ScrollBar { + id: mainScroll + width: Utils.dp(8) + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.right + anchors.horizontalCenterOffset: Constants.pane_padding / 2 + + active: true + orientation: Qt.Vertical + minimumSize: 2 * (width / height) + size: LogModel.heightRatio * logView.visibleArea.heightRatio + position: LogModel.yPosition + LogModel.heightRatio * logView.visibleArea.yPosition + onPressedChanged: { + if (pressed) { + logView.cancelFlick() + } else { + active = true + LogModel.yPosition = position + } + } + } + } + } + + ConfirmationPopup { + id: confirmationPopup + + property bool deleteAll: true + + baseItem: root + title: (deleteAll ? qsTr("Delete all") : qsTr("Delete")) + settingsModel.translationTrigger + text: (deleteAll ? qsTr("Please confirm that you want to delete your old logfiles.") + : qsTr("Please confirm that you want to delete the logfile.") + ) + settingsModel.translationTrigger + confirmText: qsTr("Delete") + settingsModel.translationTrigger + onConfirmed: deleteAll ? LogModel.removeOtherLogfiles() : LogModel.removeCurrentLogfile() + } +} diff --git a/resources/qml/Governikus/FeedbackView/+mobile/LogTitleBarControls.qml b/resources/qml/Governikus/FeedbackView/+mobile/LogTitleBarControls.qml new file mode 100644 index 0000000..3aabb00 --- /dev/null +++ b/resources/qml/Governikus/FeedbackView/+mobile/LogTitleBarControls.qml @@ -0,0 +1,58 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 + +Row { + id: logControls + + signal share() + signal remove() + signal removeAll() + + readonly property int contentWidth: width + property alias allowRemove: removeButton.visible + + spacing: Utils.dp(18) + anchors.verticalCenter: parent ? parent.verticalCenter : undefined + + Image { + height: Constants.titlebar_font_size * 1.5 + width: height + fillMode: Image.PreserveAspectFit + source: "qrc:///images/share.svg" + + MouseArea { + anchors.fill: parent + anchors.margins: -Utils.dp(8) + onClicked: logControls.share() + } + } + + Image { + id: removeButton + height: Constants.titlebar_font_size * 1.5 + width: height + fillMode: Image.PreserveAspectFit + source: "qrc:///images/trash_icon_white.svg" + + MouseArea { + anchors.fill: parent + anchors.margins: -Utils.dp(8) + onClicked: logControls.remove() + } + } + + Image { + height: Constants.titlebar_font_size * 1.5 + width: height + fillMode: Image.PreserveAspectFit + source: "qrc:///images/trash_icon_all.svg" + + MouseArea { + anchors.fill: parent + anchors.margins: -Utils.dp(8) + onClicked: logControls.removeAll() + } + } + } diff --git a/resources/qml/Governikus/FeedbackView/+mobile/StoreFeedbackPopup.qml b/resources/qml/Governikus/FeedbackView/+mobile/StoreFeedbackPopup.qml new file mode 100644 index 0000000..460b6d2 --- /dev/null +++ b/resources/qml/Governikus/FeedbackView/+mobile/StoreFeedbackPopup.qml @@ -0,0 +1,67 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 + +import Governikus.Type.ApplicationModel 1.0 + + +Popup { + id: popup + + modal: true + focus: true + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent + + width: Utils.dp(350) + height: contentColumn.height + 2 * Constants.pane_padding + padding: Constants.pane_padding + + Column { + id: contentColumn + width: parent.width + spacing: Constants.pane_spacing + + Text { + id: header + color: Constants.secondary_text + font.pixelSize: Constants.normal_font_size + font.bold: true + wrapMode: Text.WordWrap + text: qsTr("Would you like to rate this app?") + settingsModel.translationTrigger + } + + Text { + id: info + color: Constants.secondary_text + width: parent.width + wrapMode: Text.WordWrap + font.pixelSize: Constants.normal_font_size + text: qsTr("We would be very grateful if you could leave a rating on the Google Play Store!") + settingsModel.translationTrigger + } + + Row { + width: parent.width + spacing: Constants.pane_spacing + + GButton { + text: qsTr("No, Thanks") + settingsModel.translationTrigger + width: (parent.width - Constants.pane_spacing) / 2 + + onClicked: { + close() + } + } + + GButton { + text: qsTr("Rate") + settingsModel.translationTrigger + width: (parent.width - Constants.pane_spacing) / 2 + + onClicked: { + Qt.openUrlExternally("market://details?id=" + ApplicationModel.packageName) + close() + } + } + } + } +} diff --git a/resources/qml/Governikus/FeedbackView/Feedback.qml b/resources/qml/Governikus/FeedbackView/Feedback.qml deleted file mode 100644 index c461544..0000000 --- a/resources/qml/Governikus/FeedbackView/Feedback.qml +++ /dev/null @@ -1,119 +0,0 @@ -import QtQuick 2.7 -import QtQuick.Layouts 1.2 -import QtQuick.Controls 2.0 - -import Governikus.Global 1.0 -import Governikus.TitleBar 1.0 - - -SectionPage { - id: root - headerTitleBarAction: TitleBarAction { text: qsTr("Help & Feedback") + settingsModel.translationTrigger; font.bold: true } - - Component { - id: lineSeparator - Rectangle { - height: 1 - color: Constants.grey - } - } - Component { - id: subMenu - Item { - height: column.height - Column { - id: column - anchors.left: parent.left - anchors.right: parent.right - spacing: Constants.component_spacing - Text { - width: parent.width - font.pixelSize: Utils.sp(18) - color: Constants.blue - wrapMode: Text.WordWrap - text: titleText - } - Text { - color: Constants.secondary_text - width: parent.width - font.pixelSize: Constants.normal_font_size - wrapMode: Text.WordWrap - text: descriptionText - } - } - MouseArea { - anchors.fill: parent - onClicked: onClickFunction() - } - } - } - - content: Item { - width: root.width - height: childrenRect.height - - Column { - anchors.left: parent.left - anchors.right: parent.right - anchors.margins: Constants.component_spacing - spacing: Constants.component_spacing - padding: Constants.component_spacing - - Text { - id: title - anchors.left: parent.left - anchors.right: parent.right - text: qsTr("Your opinion matters") + settingsModel.translationTrigger - font.pixelSize: Constants.header_font_size - color: Constants.blue - wrapMode: Text.WordWrap - } - Text { - id: subtitle - color: Constants.secondary_text - anchors.left: parent.left - anchors.right: parent.right - text: qsTr("We are happy about every feedback on our software.") + settingsModel.translationTrigger - font.pixelSize: Constants.normal_font_size - wrapMode: Text.WordWrap - } - - Pane { - Loader { - readonly property string titleText: qsTr("FAQ") + settingsModel.translationTrigger - readonly property string descriptionText: qsTr("Do you have questions how to use AusweisApp2?") + settingsModel.translationTrigger - function onClickFunction() { Qt.openUrlExternally(qsTr("https://www.ausweisapp.bund.de/en/questions-and-answers/frequently-asked-questions/")) } - width: parent.width - sourceComponent: subMenu - } - Loader { width: parent.width; sourceComponent: lineSeparator } - Loader { - readonly property string titleText: qsTr("Support") + settingsModel.translationTrigger - readonly property string descriptionText: qsTr("You need further help?") + settingsModel.translationTrigger - function onClickFunction() { Qt.openUrlExternally(qsTr("https://www.ausweisapp.bund.de/en/questions-and-answers/support/")) } - width: parent.width - sourceComponent: subMenu - } - Loader { width: parent.width; sourceComponent: lineSeparator } - Loader { - readonly property string titleText: qsTr("Rate AusweisApp2") + settingsModel.translationTrigger - readonly property string descriptionText: qsTr("Please rate us in the Google Play Store.") + settingsModel.translationTrigger - function onClickFunction() { Qt.openUrlExternally("market://details?id=com.governikus.ausweisapp2") } - width: parent.width - sourceComponent: subMenu - } - Loader { width: parent.width; sourceComponent: lineSeparator } - Loader { - readonly property string titleText: qsTr("Report error") + settingsModel.translationTrigger - readonly property string descriptionText: qsTr("You found a bug? Please tell us, so we can fix it.") + settingsModel.translationTrigger - readonly property string emailAddress: "support.ausweisapp2@governikus.de" - readonly property string emailSubject: qsTr("Android log file") + settingsModel.translationTrigger - readonly property string emailBody: qsTr("") + settingsModel.translationTrigger - function onClickFunction() { qmlExtension.mailLog(emailAddress, emailSubject, emailBody) } - width: parent.width - sourceComponent: subMenu - } - } - } - } -} diff --git a/resources/qml/Governikus/FeedbackView/qmldir b/resources/qml/Governikus/FeedbackView/qmldir index da44e42..3dd910f 100644 --- a/resources/qml/Governikus/FeedbackView/qmldir +++ b/resources/qml/Governikus/FeedbackView/qmldir @@ -1,2 +1,7 @@ module FeedbackView + +internal LogTitleBarControls LogTitleBarControls.qml + Feedback 1.0 Feedback.qml +Log 1.0 Log.qml +StoreFeedbackPopup 1.0 StoreFeedbackPopup.qml diff --git a/resources/qml/Governikus/Global/+android/+tablet/PlatformConstants.qml b/resources/qml/Governikus/Global/+android/+tablet/PlatformConstants.qml deleted file mode 100644 index 861146c..0000000 --- a/resources/qml/Governikus/Global/+android/+tablet/PlatformConstants.qml +++ /dev/null @@ -1,29 +0,0 @@ -pragma Singleton - -import QtQuick 2.5 - -import "Utils.js" as Utils - -Item { - readonly property color grey_light: "#bbbbbb" - readonly property color grey_border: "lightslategrey" - readonly property color blue_dark: "#324d66" - readonly property color blue_light: "#659bcd" - readonly property color primary_text: "#ffffff" - readonly property color secondary_text: "#444444" - readonly property color accent_color: "#7879b2" - readonly property color second_accent_color: "#a3cb7f" - readonly property int titlebar_font_size: Utils.sp(18) - readonly property int provider_section_height: Utils.dp(62) - readonly property int history_section_height: Utils.dp(120) - readonly property int history_delegate_spacing: Utils.dp(10) - readonly property color history_delegate_address_color: "#7879b2" - readonly property int button_height: Utils.dp(36) - readonly property bool use_history_list_delete_area: false - - readonly property bool is_layout_android: true - readonly property bool is_layout_ios: false - readonly property bool is_tablet: true - readonly property bool leftNavigation: true - readonly property bool bottomNavigation: false -} diff --git a/resources/qml/Governikus/Global/+android/GButton.qml b/resources/qml/Governikus/Global/+android/GButton.qml deleted file mode 100644 index 75d4264..0000000 --- a/resources/qml/Governikus/Global/+android/GButton.qml +++ /dev/null @@ -1,102 +0,0 @@ -import QtQuick 2.5 -import QtGraphicalEffects 1.0 - -import "Utils.js" as Utils -import "." as Gov - -/* - * Custom implementation to be replaced with template specialization of Qt.labs.controls Button - * Android style guide for material design is adapted. - */ -Item { - property alias text: textItem.text - property color buttonColor: Gov.Constants.blue - property int maxWidth: 0 - property alias iconSource: icon.source - - signal clicked - - height: Gov.Constants.button_height - width: Math.max(textItem.implicitWidth + (icon.visible ? (icon.width + icon.anchors.leftMargin) : 0) + (2 * Utils.dp(16)), Utils.dp(88)) - - state: "normal" - states: [ - State { name: "normal"; when: !mouseArea.pressed - PropertyChanges { target: darkLayer; width: 0 } - PropertyChanges { target: shadow; verticalOffset: Utils.dp(2) } - }, - State { name: "pressed"; when: mouseArea.pressed - PropertyChanges { target: darkLayer; width: 2 * rect.width } - PropertyChanges { target: shadow; verticalOffset: Utils.dp(8) } - } - ] - transitions: [ - Transition { - from: "normal"; to: "pressed"; reversible: false - PropertyAnimation { target: darkLayer; property: "width"} - PropertyAnimation { target: shadow; property: "verticalOffset"} - } - ] - - Rectangle { - id: rect - anchors.fill: parent - color: enabled ? buttonColor : "#10000000" - radius: Utils.dp(3) - - Item { - anchors.fill: parent - clip: true - Rectangle { - id: darkLayer - x: mouseArea.containsMouse ? mouseArea.mouseX - width * 0.5 : 0 - height: parent.height - color: "#000000" - opacity: 0.2 - radius: Utils.dp(3) - } - } - - } - - DropShadow { - id: shadow - anchors.fill: rect - radius: 8.0 - fast: true - color: "#40000000" - source: rect - } - - Image { - id: icon - visible: source.toString().length > 0 - height: rect.height - Utils.dp(10) - width: height - anchors.left: rect.left - anchors.leftMargin: Utils.dp(5) - anchors.verticalCenter: rect.verticalCenter - } - - Text { - id: textItem - anchors.left: rect.left - anchors.right: rect.right - anchors.verticalCenter: rect.verticalCenter - anchors.leftMargin: icon.visible ? icon.width + icon.anchors.leftMargin : 0 - horizontalAlignment: Text.AlignHCenter - color: enabled ? "white" : "#40000000" - font.capitalization: Font.AllUppercase - font.bold: true - font.pixelSize: Utils.dp(16) - } - - MouseArea { - id: mouseArea - anchors.fill: parent - preventStealing: true - hoverEnabled: true - onClicked: parent.clicked() - } -} - diff --git a/resources/qml/Governikus/Global/+android/GCheckBox.qml b/resources/qml/Governikus/Global/+android/GCheckBox.qml deleted file mode 100644 index cd8f645..0000000 --- a/resources/qml/Governikus/Global/+android/GCheckBox.qml +++ /dev/null @@ -1,52 +0,0 @@ -import QtQuick 2.7 - -import "Utils.js" as Utils -import "." as Gov - -Item { - property alias checked: box.checked - property alias text: description.text - - height: Utils.dp(20) - width: row.width - - Row { - id: row - height: parent.height - spacing: Utils.dp(6) - - Rectangle { - id: box - property bool checked - - height: parent.height - width: height - - color: enabled ? (checked ? Gov.Constants.accent_color : "white") : Gov.Constants.grey - border.color: checked ? Gov.Constants.accent_color : "black" - border.width: Utils.dp(2) - radius: Utils.dp(2) - - Image { - source: "qrc:///images/check.svg" - anchors.fill: parent - anchors.margins: Utils.dp(3) - fillMode: Image.PreserveAspectFit - visible: checked && enabled - } - } - - Text { - id: description - color: Gov.Constants.secondary_text - visible: text !== "" - anchors.verticalCenter: box.verticalCenter - font.pixelSize: Gov.Constants.normal_font_size - } - } - - MouseArea { - anchors.fill: row - onClicked: if (enabled) box.checked = !box.checked - } -} diff --git a/resources/qml/Governikus/Global/+android/LabeledText.qml b/resources/qml/Governikus/Global/+android/LabeledText.qml deleted file mode 100644 index cf3b769..0000000 --- a/resources/qml/Governikus/Global/+android/LabeledText.qml +++ /dev/null @@ -1,41 +0,0 @@ -import QtQuick 2.5 - -import "." as Gov - -Item { - property alias label: labelText.text - property alias text: bodyText.text - property alias textFormat: bodyText.textFormat - property int margin - property int fontUppercase - - signal linkActivated(string link) - - height: childrenRect.height + margin - - Text { - id: bodyText - anchors.top: parent.top - anchors.left: parent.left - anchors.leftMargin: margin - anchors.right: parent.right - anchors.rightMargin: margin - font.pixelSize: Gov.Constants.normal_font_size - font.capitalization: fontUppercase - color: Gov.Constants.secondary_text - wrapMode: Text.WordWrap - onLinkActivated: parent.linkActivated(link) - } - - Text { - id: labelText - anchors.top: bodyText.bottom - anchors.left: parent.left - anchors.leftMargin: margin - anchors.right: parent.right - anchors.rightMargin: margin - font.pixelSize: Gov.Constants.label_font_size - color: Gov.Constants.blue - wrapMode: Text.WordWrap - } -} diff --git a/resources/qml/Governikus/Global/+android/Pane.qml b/resources/qml/Governikus/Global/+android/Pane.qml deleted file mode 100644 index 2ebe8a2..0000000 --- a/resources/qml/Governikus/Global/+android/Pane.qml +++ /dev/null @@ -1,50 +0,0 @@ -import QtQuick 2.5 -import QtGraphicalEffects 1.0 - -import "." as Gov - - -Rectangle { - id: root - property alias title: titleText.text - default property alias paneChildren: paneContent.children - - anchors.left: parent.left - anchors.right: parent.right - height: childrenRect.height - color: "white" - radius: 16 - - Column { - anchors.left: parent.left - anchors.right: parent.right - anchors.leftMargin: Gov.Constants.pane_padding - anchors.rightMargin: Gov.Constants.pane_padding - - Text { - id: titleText - height: implicitHeight * 2 - width: parent.width - visible: text !== "" - verticalAlignment: Text.AlignVCenter - font.pixelSize: Gov.Constants.header_font_size - color: Gov.Constants.blue - } - Item { width: parent.width; height: Gov.Constants.pane_padding } - Column { - id: paneContent - width: parent.width - spacing: Gov.Constants.pane_spacing - } - Item { width: parent.width; height: Gov.Constants.pane_padding } - } - - layer.enabled: true - layer.effect: DropShadow { - radius: 8 - samples: 8 - source: root - color: Gov.Constants.grey - verticalOffset: 2 - } -} diff --git a/resources/qml/Governikus/Global/+android/PlatformConstants.qml b/resources/qml/Governikus/Global/+android/PlatformConstants.qml deleted file mode 100644 index f7f8bc8..0000000 --- a/resources/qml/Governikus/Global/+android/PlatformConstants.qml +++ /dev/null @@ -1,29 +0,0 @@ -pragma Singleton - -import QtQuick 2.5 - -import "Utils.js" as Utils - -Item { - readonly property color grey_light: "#bbbbbb" - readonly property color grey_border: "lightslategrey" - readonly property color blue_dark: "#324d66" - readonly property color blue_light: "#659bcd" - readonly property color primary_text: "#ffffff" - readonly property color secondary_text: "#4a4a4a" - readonly property color accent_color: "#7879b2" - readonly property color second_accent_color: "#a3cb7f" - readonly property int titlebar_font_size: Utils.sp(18) - readonly property int provider_section_height: Utils.dp(62) - readonly property int history_section_height: Utils.dp(120) - readonly property int history_delegate_spacing: Utils.dp(10) - readonly property color history_delegate_address_color: "#7879b2" - readonly property int button_height: Utils.dp(36) - readonly property bool use_history_list_delete_area: false - - readonly property bool is_layout_android: true - readonly property bool is_layout_ios: false - readonly property bool is_tablet: false - readonly property bool leftNavigation: true - readonly property bool bottomNavigation: false -} diff --git a/resources/qml/Governikus/Global/+desktop/+mac/BrandConstants.qml b/resources/qml/Governikus/Global/+desktop/+mac/BrandConstants.qml new file mode 100644 index 0000000..6406eff --- /dev/null +++ b/resources/qml/Governikus/Global/+desktop/+mac/BrandConstants.qml @@ -0,0 +1,6 @@ +import QtQuick 2.10 + +import Governikus.Type.ApplicationModel 1.0 + +Item { +} diff --git a/resources/qml/Governikus/Global/+desktop/+win/BrandConstants.qml b/resources/qml/Governikus/Global/+desktop/+win/BrandConstants.qml new file mode 100644 index 0000000..6406eff --- /dev/null +++ b/resources/qml/Governikus/Global/+desktop/+win/BrandConstants.qml @@ -0,0 +1,6 @@ +import QtQuick 2.10 + +import Governikus.Type.ApplicationModel 1.0 + +Item { +} diff --git a/resources/qml/Governikus/Global/+desktop/ContinueButton.qml b/resources/qml/Governikus/Global/+desktop/ContinueButton.qml new file mode 100644 index 0000000..3637884 --- /dev/null +++ b/resources/qml/Governikus/Global/+desktop/ContinueButton.qml @@ -0,0 +1,37 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.View 1.0 + + +Button { + id: control + width: height + + Accessible.role: Accessible.Button + Accessible.name: qsTr("Continue") + + FocusFrame {} + + background: Rectangle { + anchors.fill: parent + anchors.margins: control.down ? control.height / 32 : 0 + radius: height / 2 + border.width: height / 40; + border.color: Constants.white + color: "transparent" + + Rectangle { + radius: height / 2 + anchors.fill: parent + anchors.margins: parent.height / 8; + color: Qt.lighter(Constants.blue, 1.1) + + Image { + anchors.centerIn: parent + sourceSize.height: parent.height / 2; + source: "qrc:///images/desktop/continue_arrow.svg" + } + } + } +} diff --git a/resources/qml/Governikus/Global/+desktop/GButton.qml b/resources/qml/Governikus/Global/+desktop/GButton.qml new file mode 100644 index 0000000..e4ec061 --- /dev/null +++ b/resources/qml/Governikus/Global/+desktop/GButton.qml @@ -0,0 +1,53 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 +import Governikus.View 1.0 +import Governikus.Type.ApplicationModel 1.0 + + +Button { + id: control + padding: Constants.pane_padding / 3 + + contentItem: Row { + spacing: Constants.groupbox_spacing + + Image { + source: control.icon.source + visible: source.toString().length > 0 + sourceSize.height: textContainer.height + } + + Item { + id: textContainer + height: originalSize.height + width: originalSize.width + + Text { + id: originalSize + visible: false + text: control.text + font.bold: true + font.pixelSize: Constants.normal_font_size + } + + Text { + text: control.text + color: Constants.white + font.bold: true + font.pixelSize: Constants.normal_font_size - ApplicationModel.scaleFactor * (control.down ? 1 : 0) + anchors.centerIn: parent + + FocusFrame { + scope: control + } + } + } + } + + background: Rectangle { + color: Constants.blue + radius: ApplicationModel.scaleFactor * 4 + } +} diff --git a/resources/qml/Governikus/Global/+desktop/GCheckBox.qml b/resources/qml/Governikus/Global/+desktop/GCheckBox.qml new file mode 100644 index 0000000..39c0b40 --- /dev/null +++ b/resources/qml/Governikus/Global/+desktop/GCheckBox.qml @@ -0,0 +1,24 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 +import Governikus.Type.ApplicationModel 1.0 + + +CheckBox { + id: control + + indicator: Rectangle { + anchors.fill: parent + color: Constants.blue + radius: Math.max(ApplicationModel.scaleFactor * 4, 1) + + Image { + source: "qrc:///images/check.svg" + anchors.fill: parent + anchors.margins: Math.max(ApplicationModel.scaleFactor * 4, 1) + fillMode: Image.PreserveAspectFit + visible: control.checked + } + } +} diff --git a/resources/qml/Governikus/Global/+desktop/GText.qml b/resources/qml/Governikus/Global/+desktop/GText.qml new file mode 100644 index 0000000..c5b278c --- /dev/null +++ b/resources/qml/Governikus/Global/+desktop/GText.qml @@ -0,0 +1,5 @@ +import QtQuick 2.10 + +Text { + renderType: Text.NativeRendering +} diff --git a/resources/qml/Governikus/Global/+desktop/GTextField.qml b/resources/qml/Governikus/Global/+desktop/GTextField.qml new file mode 100644 index 0000000..4b6ad7f --- /dev/null +++ b/resources/qml/Governikus/Global/+desktop/GTextField.qml @@ -0,0 +1,41 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Type.ApplicationModel 1.0 + + +Item { + property alias text: field.text + property alias font: field.font + property alias displayText: field.displayText + property alias echoMode: field.echoMode + signal accepted + + property bool valid: true + + height: field.height + 2 * textBackground.radius + width: ApplicationModel.scaleFactor * 240 + + Rectangle { + id: textBackground + radius: ApplicationModel.scaleFactor * 6 + anchors.fill: parent + border.color: Constants.red + color: enabled ? "white" : Constants.grey + border.width: valid ? 0 : ApplicationModel.scaleFactor * 2 + } + + TextField { + id: field + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: textBackground.radius + padding: 0 + font.pixelSize: Constants.normal_font_size + onAccepted: parent.accepted() + background: Item {} + } + + onActiveFocusChanged: if (focus) field.forceActiveFocus() +} diff --git a/resources/qml/Governikus/Global/+desktop/LabeledText.qml b/resources/qml/Governikus/Global/+desktop/LabeledText.qml new file mode 100644 index 0000000..ae27d5d --- /dev/null +++ b/resources/qml/Governikus/Global/+desktop/LabeledText.qml @@ -0,0 +1,32 @@ +import QtQuick 2.10 + + +Column { + property alias label: label.text + property alias text: body.text + property alias textFormat: body.textFormat + property alias textUppercase: body.font.capitalization + + signal linkActivated(string link) + + width: Math.max(label.implicitWidth, body.implicitWidth) + + Text { + id: label + anchors.left: parent.left + anchors.right: parent.right + font.pixelSize: Constants.normal_font_size + color: Constants.blue + wrapMode: Text.WordWrap + } + + Text { + id: body + color: Constants.black + anchors.left: parent.left + anchors.right: parent.right + font.pixelSize: Constants.normal_font_size + wrapMode: Text.WordWrap + onLinkActivated: parent.linkActivated(link) + } +} diff --git a/resources/qml/Governikus/Global/+desktop/Pane.qml b/resources/qml/Governikus/Global/+desktop/Pane.qml new file mode 100644 index 0000000..5dc37b1 --- /dev/null +++ b/resources/qml/Governikus/Global/+desktop/Pane.qml @@ -0,0 +1,46 @@ +import QtQuick 2.10 +import QtGraphicalEffects 1.0 + +import Governikus.Type.ApplicationModel 1.0 + + +Rectangle { + id: root + property alias title: titleText.text + default property alias paneChildren: paneContent.children + + anchors.left: parent.left + anchors.right: parent.right + height: childrenRect.height + color: Constants.white + radius: ApplicationModel.scaleFactor * 10 + + Column { + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: Constants.pane_padding + anchors.rightMargin: Constants.pane_padding + topPadding: Constants.pane_padding + bottomPadding: Constants.pane_padding + spacing: Constants.pane_spacing + + PaneTitle { + id: titleText + } + + Column { + id: paneContent + width: parent.width + spacing: Constants.pane_spacing + } + } + + layer.enabled: true + layer.effect: DropShadow { + radius: 8 + samples: 8 + source: root + color: Qt.darker(Constants.grey, 1.2) + verticalOffset: 2 + } +} diff --git a/resources/qml/Governikus/Global/+desktop/PlatformConstants.qml b/resources/qml/Governikus/Global/+desktop/PlatformConstants.qml new file mode 100644 index 0000000..3193a0e --- /dev/null +++ b/resources/qml/Governikus/Global/+desktop/PlatformConstants.qml @@ -0,0 +1,28 @@ +import QtQuick 2.10 + +import Governikus.Type.ApplicationModel 1.0 + +BrandConstants { + readonly property color blue: Qt.darker(background_color, 1.2) + readonly property color background_color: "#659bcd" + readonly property color grey_light: "#bbbbbb" + readonly property color secondary_text: "#dadada" + readonly property color accent_color: "#7879b2" + + readonly property int header_font_size: ApplicationModel.scaleFactor * 42 + readonly property int pane_title_font_size: ApplicationModel.scaleFactor * 32 + readonly property int normal_font_size: ApplicationModel.scaleFactor * 26 + readonly property int label_font_size: ApplicationModel.scaleFactor * 20 + readonly property int small_font_size: ApplicationModel.scaleFactor * 18 + + readonly property int titlebar_padding: ApplicationModel.scaleFactor * 20 + readonly property int titlebar_spacing: ApplicationModel.scaleFactor * 20 + readonly property int titlebar_font_size: ApplicationModel.scaleFactor * 24 + + readonly property int component_spacing: ApplicationModel.scaleFactor * 30 + readonly property int pane_padding: ApplicationModel.scaleFactor * 30 + readonly property int pane_spacing: ApplicationModel.scaleFactor * 30 + readonly property int groupbox_spacing: ApplicationModel.scaleFactor * 20 + + readonly property bool is_desktop: true +} diff --git a/resources/qml/Governikus/Global/+ios/+tablet/Pane.qml b/resources/qml/Governikus/Global/+ios/+tablet/Pane.qml deleted file mode 100644 index d1ab6fd..0000000 --- a/resources/qml/Governikus/Global/+ios/+tablet/Pane.qml +++ /dev/null @@ -1,50 +0,0 @@ -import QtQuick 2.5 -import QtGraphicalEffects 1.0 - -import "." - - -Rectangle { - id: root - property alias title: titleText.text - property alias spacing: paneContent.spacing - default property alias paneChildren: paneContent.children - - anchors.left: parent.left - anchors.right: parent.right - height: childrenRect.height - color: "white" - - Column { - anchors.left: parent.left - anchors.right: parent.right - anchors.leftMargin: Constants.pane_padding - anchors.rightMargin: Constants.pane_padding - - Text { - id: titleText - height: implicitHeight * 2 - width: parent.width - visible: text !== "" - verticalAlignment: Text.AlignVCenter - font.pixelSize: Constants.header_font_size - color: Constants.blue - } - Item { width: parent.width; height: Constants.pane_padding } - Column { - id: paneContent - width: parent.width - spacing: Constants.pane_spacing - } - Item { width: parent.width; height: Constants.pane_padding } - } - - layer.enabled: true - layer.effect: DropShadow { - radius: 8 - samples: 8 - source: root - color: Constants.grey - verticalOffset: 2 - } -} diff --git a/resources/qml/Governikus/Global/+ios/+tablet/PlatformConstants.qml b/resources/qml/Governikus/Global/+ios/+tablet/PlatformConstants.qml deleted file mode 100644 index 27380a6..0000000 --- a/resources/qml/Governikus/Global/+ios/+tablet/PlatformConstants.qml +++ /dev/null @@ -1,29 +0,0 @@ -pragma Singleton - -import QtQuick 2.5 - -import "Utils.js" as Utils - -Item { - readonly property color grey_light: "#8e8e93" - readonly property color grey_border: "lightslategrey" - readonly property color blue_dark: "#0076ff" - readonly property color blue_light: "#54c7fc" - readonly property color primary_text: "#ffffff" - readonly property color secondary_text: "#000000" - readonly property color accent_color: "#000000" - readonly property color second_accent_color: "#000000" - readonly property int titlebar_font_size: Utils.sp(16) - readonly property int provider_section_height: Utils.dp(50) - readonly property int history_section_height: Utils.dp(60) - readonly property int history_delegate_spacing: 0 - readonly property color history_delegate_address_color: "#00878F" - readonly property int button_height: Utils.dp(40) - readonly property bool use_history_list_delete_area: true - - readonly property bool is_layout_android: false - readonly property bool is_layout_ios: true - readonly property bool is_tablet: true - readonly property bool leftNavigation: false - readonly property bool bottomNavigation: true -} diff --git a/resources/qml/Governikus/Global/+ios/GButton.qml b/resources/qml/Governikus/Global/+ios/GButton.qml deleted file mode 100644 index 3c6ef32..0000000 --- a/resources/qml/Governikus/Global/+ios/GButton.qml +++ /dev/null @@ -1,61 +0,0 @@ -import QtQuick 2.5 -import QtGraphicalEffects 1.0 - -import "." - -/* Custom implementation to be replaced with template specialization of Qt.labs.controls Button*/ -Rectangle { - id: rect - property alias text: textItem.text - property color buttonColor : Constants.blue - property int maxWidth: 0 - property int preferedWidth: parent.width - 4 * Constants.component_spacing - property alias iconSource: icon.source - - signal clicked - - color: enabled ? buttonColor : "#10000000" - height: Constants.button_height - width: maxWidth > 0 ? Math.min(maxWidth, preferedWidth) : preferedWidth - clip: true - - Image { - id: icon - visible: source.toString().length > 0 - height: rect.height - Utils.dp(10) - width: height - anchors.left: rect.left - anchors.leftMargin: Utils.dp(5) - anchors.verticalCenter: rect.verticalCenter - } - - Text { - id: textItem - anchors.centerIn: parent - color: enabled ? "white" : "#40000000" - opacity: mouseArea.containsMouse ? 0.5 : 1 - anchors.leftMargin: icon.visible ? icon.width + icon.anchors.leftMargin : 0 - font.pixelSize: Utils.dp(16) - } - - MouseArea{ - id: mouseArea - anchors.fill: parent - preventStealing: true - hoverEnabled: true - onClicked: parent.clicked() - } - - RadialGradient { - x: mouseArea.mouseX - width * 0.5 - height: parent.height - width: height * 2 - visible: mouseArea.pressed - opacity: 1 - gradient: Gradient { - GradientStop { position: 0.0; color: Qt.rgba(255,255,255,1) } - GradientStop { position: 0.2; color: Qt.rgba(255,255,255,0.5) } - GradientStop { position: 0.4; color: Qt.rgba(255,255,255,0) } - } - } -} diff --git a/resources/qml/Governikus/Global/+ios/GCheckBox.qml b/resources/qml/Governikus/Global/+ios/GCheckBox.qml deleted file mode 100644 index e418ea4..0000000 --- a/resources/qml/Governikus/Global/+ios/GCheckBox.qml +++ /dev/null @@ -1,38 +0,0 @@ -import QtQuick 2.7 - -import "." - -Item { - property bool checked: false - property alias text: description.text - - height: Utils.dp(20) - width: row.width - - Row { - id: row - height: parent.height - spacing: Utils.dp(6) - - Image { - id: image - source: checked && enabled ? "qrc:///images/iOS/CheckedCheckbox.png" : "" - height: parent.height - width: height - fillMode: Image.PreserveAspectFit - } - - Text { - id: description - color: Constants.secondary_text - visible: text !== "" - anchors.verticalCenter: image.verticalCenter - font.pixelSize: Constants.normal_font_size - } - } - - MouseArea { - anchors.fill: row - onClicked: if (enabled) checked = !checked - } -} diff --git a/resources/qml/Governikus/Global/+ios/LabeledText.qml b/resources/qml/Governikus/Global/+ios/LabeledText.qml deleted file mode 100644 index 3a52721..0000000 --- a/resources/qml/Governikus/Global/+ios/LabeledText.qml +++ /dev/null @@ -1,41 +0,0 @@ -import QtQuick 2.5 - -import "." - -Item { - property alias label: labelText.text - property alias text: bodyText.text - property alias textFormat: bodyText.textFormat - property int margin - property int fontUppercase - - signal linkActivated(string link) - - height: childrenRect.height + margin - - Text { - id: labelText - anchors.top: parent.top - anchors.left: parent.left - anchors.leftMargin: margin - anchors.right: parent.right - anchors.rightMargin: margin - font.pixelSize: Constants.normal_font_size - color: Constants.blue - wrapMode: Text.WordWrap - } - - Text { - id: bodyText - color: Constants.secondary_text - anchors.top: labelText.bottom - anchors.left: parent.left - anchors.leftMargin: margin - anchors.right: parent.right - anchors.rightMargin: margin - font.pixelSize: Constants.normal_font_size - font.capitalization: fontUppercase - wrapMode: Text.WordWrap - onLinkActivated: parent.linkActivated(link) - } -} diff --git a/resources/qml/Governikus/Global/+ios/Pane.qml b/resources/qml/Governikus/Global/+ios/Pane.qml deleted file mode 100644 index d6a6a05..0000000 --- a/resources/qml/Governikus/Global/+ios/Pane.qml +++ /dev/null @@ -1,47 +0,0 @@ -import QtQuick 2.5 - -import "." - - -Column { - id: root - property alias title: titleText.text - property alias spacing: paneContent.spacing - default property alias paneData: paneContent.data - - anchors.left: parent.left - anchors.right: parent.right - - Text { - id: titleText - height: Utils.dp(30) - visible: text !== "" - anchors.left: parent.left - anchors.leftMargin: Constants.pane_padding - font.pixelSize: Constants.header_font_size - font.capitalization: Font.AllUppercase - color: Constants.grey - } - - Rectangle { width: parent.width; height: 1; color: Constants.grey} - Rectangle { - color: "white" - width: parent.width - height: childrenRect.height - - Column { - width: parent.width - Item { width: parent.width; height: Constants.pane_padding } - Column { - id: paneContent - anchors.left: parent.left - anchors.leftMargin: Constants.pane_padding - anchors.right: parent.right - anchors.rightMargin: Constants.pane_padding - spacing: Constants.component_spacing - } - Item { width: parent.width; height: Constants.pane_padding } - } - } - Rectangle { width: parent.width; height: 1; color: Constants.grey} -} diff --git a/resources/qml/Governikus/Global/+ios/PlatformConstants.qml b/resources/qml/Governikus/Global/+ios/PlatformConstants.qml deleted file mode 100644 index 4b8ad5d..0000000 --- a/resources/qml/Governikus/Global/+ios/PlatformConstants.qml +++ /dev/null @@ -1,29 +0,0 @@ -pragma Singleton - -import QtQuick 2.5 - -import "Utils.js" as Utils - -Item { - readonly property color grey_light: "#8e8e93" - readonly property color grey_border: "lightslategrey" - readonly property color blue_dark: "#0076ff" - readonly property color blue_light: "#54c7fc" - readonly property color primary_text: "#ffffff" - readonly property color secondary_text: "#000000" - readonly property color accent_color: "#000000" - readonly property color second_accent_color: "#000000" - readonly property int titlebar_font_size: Utils.sp(16) - readonly property int provider_section_height: Utils.dp(50) - readonly property int history_section_height: Utils.dp(85) - readonly property int history_delegate_spacing: 0 - readonly property color history_delegate_address_color: "#00878F" - readonly property int button_height: Utils.dp(40) - readonly property bool use_history_list_delete_area: true - - readonly property bool is_layout_android: false - readonly property bool is_layout_ios: true - readonly property bool is_tablet: false - readonly property bool leftNavigation: false - readonly property bool bottomNavigation: true -} diff --git a/resources/qml/Governikus/Global/+mobile/+android/+phone/DeviceConstants.qml b/resources/qml/Governikus/Global/+mobile/+android/+phone/DeviceConstants.qml new file mode 100644 index 0000000..2f7c0dc --- /dev/null +++ b/resources/qml/Governikus/Global/+mobile/+android/+phone/DeviceConstants.qml @@ -0,0 +1,7 @@ +import QtQuick 2.10 + +import "Utils.js" as Utils + +Item { + readonly property bool is_tablet: false +} diff --git a/resources/qml/Governikus/Global/+mobile/+android/+tablet/DeviceConstants.qml b/resources/qml/Governikus/Global/+mobile/+android/+tablet/DeviceConstants.qml new file mode 100644 index 0000000..11a5f6c --- /dev/null +++ b/resources/qml/Governikus/Global/+mobile/+android/+tablet/DeviceConstants.qml @@ -0,0 +1,7 @@ +import QtQuick 2.10 + +import "Utils.js" as Utils + +Item { + readonly property bool is_tablet: true +} diff --git a/resources/qml/Governikus/Global/+mobile/+android/BrandConstants.qml b/resources/qml/Governikus/Global/+mobile/+android/BrandConstants.qml new file mode 100644 index 0000000..0a230b0 --- /dev/null +++ b/resources/qml/Governikus/Global/+mobile/+android/BrandConstants.qml @@ -0,0 +1,22 @@ +import QtQuick 2.10 + +import "Utils.js" as Utils + +DeviceConstants { + readonly property color grey_light: "#bbbbbb" + readonly property color blue_dark: "#324d66" + readonly property color blue_light: "#659bcd" + readonly property color accent_color: "#7879b2" + readonly property color primary_text: "#ffffff" + readonly property color secondary_text: "#4a4a4a" + readonly property int titlebar_font_size: Utils.dp(18) + readonly property int provider_section_height: Utils.dp(62) + readonly property int history_delegate_spacing: Utils.dp(10) + readonly property color history_delegate_address_color: "#7879b2" + readonly property int button_height: Utils.dp(36) + + readonly property bool is_layout_android: true + readonly property bool is_layout_ios: false + readonly property bool leftNavigation: true + readonly property bool bottomNavigation: false +} diff --git a/resources/qml/Governikus/Global/+mobile/+android/GButton.qml b/resources/qml/Governikus/Global/+mobile/+android/GButton.qml new file mode 100644 index 0000000..6237298 --- /dev/null +++ b/resources/qml/Governikus/Global/+mobile/+android/GButton.qml @@ -0,0 +1,100 @@ +import QtQuick 2.10 +import QtGraphicalEffects 1.0 + +import "Utils.js" as Utils + +/* + * Custom implementation to be replaced with template specialization of Qt.labs.controls Button + * Android style guide for material design is adapted. + */ +Item { + property alias text: textItem.text + property color buttonColor: Constants.blue + property int maxWidth: 0 + property alias iconSource: icon.source + + signal clicked + + height: Constants.button_height + width: Math.max(textItem.implicitWidth + (icon.visible ? (icon.width + icon.anchors.leftMargin) : 0) + (2 * Utils.dp(16)), Utils.dp(88)) + + state: "normal" + states: [ + State { name: "normal"; when: !mouseArea.pressed + PropertyChanges { target: darkLayer; width: 0 } + PropertyChanges { target: shadow; verticalOffset: Utils.dp(2) } + }, + State { name: "pressed"; when: mouseArea.pressed + PropertyChanges { target: darkLayer; width: 2 * rect.width } + PropertyChanges { target: shadow; verticalOffset: Utils.dp(8) } + } + ] + transitions: [ + Transition { + from: "normal"; to: "pressed"; reversible: false + PropertyAnimation { target: darkLayer; property: "width"} + PropertyAnimation { target: shadow; property: "verticalOffset"} + } + ] + + Rectangle { + id: rect + anchors.fill: parent + color: enabled ? buttonColor : "#10000000" + radius: Utils.dp(3) + + Item { + anchors.fill: parent + clip: true + Rectangle { + id: darkLayer + x: mouseArea.containsMouse ? mouseArea.mouseX - width * 0.5 : 0 + height: parent.height + color: "#000000" + opacity: 0.2 + radius: Utils.dp(3) + } + } + } + + DropShadow { + id: shadow + anchors.fill: rect + radius: 8.0 + fast: true + color: "#40000000" + source: rect + } + + Image { + id: icon + visible: source.toString().length > 0 + height: rect.height - Utils.dp(10) + width: height + anchors.left: rect.left + anchors.leftMargin: Utils.dp(5) + anchors.verticalCenter: rect.verticalCenter + } + + Text { + id: textItem + anchors.left: rect.left + anchors.right: rect.right + anchors.verticalCenter: rect.verticalCenter + anchors.leftMargin: icon.visible ? icon.width + icon.anchors.leftMargin : 0 + horizontalAlignment: Text.AlignHCenter + color: enabled ? "white" : "#40000000" + font.capitalization: Font.AllUppercase + font.bold: true + font.pixelSize: Utils.dp(16) + } + + MouseArea { + id: mouseArea + anchors.fill: parent + preventStealing: true + hoverEnabled: true + onClicked: parent.clicked() + } +} + diff --git a/resources/qml/Governikus/Global/+mobile/+android/GCheckBox.qml b/resources/qml/Governikus/Global/+mobile/+android/GCheckBox.qml new file mode 100644 index 0000000..c57af7b --- /dev/null +++ b/resources/qml/Governikus/Global/+mobile/+android/GCheckBox.qml @@ -0,0 +1,30 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 + +import "Utils.js" as Utils + + +CheckBox { + id: control + + indicator: Rectangle { + implicitHeight: Utils.dp(20) + implicitWidth: Utils.dp(20) + anchors.centerIn: parent + + color: control.checked ? Constants.accent_color : Constants.white + border.color: control.checked ? Constants.accent_color : Constants.black + border.width: Utils.dp(2) + radius: Utils.dp(2) + + Image { + source: "qrc:///images/check.svg" + anchors.fill: parent + anchors.margins: Utils.dp(3) + fillMode: Image.PreserveAspectFit + visible: control.checked + } + } +} diff --git a/resources/qml/Governikus/Global/+mobile/+android/LabeledText.qml b/resources/qml/Governikus/Global/+mobile/+android/LabeledText.qml new file mode 100644 index 0000000..b3317c2 --- /dev/null +++ b/resources/qml/Governikus/Global/+mobile/+android/LabeledText.qml @@ -0,0 +1,39 @@ +import QtQuick 2.10 + +Item { + property alias label: labelText.text + property alias text: bodyText.text + property alias textFormat: bodyText.textFormat + property int margin + property int fontUppercase + + signal linkActivated(string link) + + height: childrenRect.height + margin + + Text { + id: bodyText + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: margin + anchors.right: parent.right + anchors.rightMargin: margin + font.pixelSize: Constants.normal_font_size + font.capitalization: fontUppercase + color: Constants.secondary_text + wrapMode: Text.WordWrap + onLinkActivated: parent.linkActivated(link) + } + + Text { + id: labelText + anchors.top: bodyText.bottom + anchors.left: parent.left + anchors.leftMargin: margin + anchors.right: parent.right + anchors.rightMargin: margin + font.pixelSize: Constants.label_font_size + color: Constants.blue + wrapMode: Text.WordWrap + } +} diff --git a/resources/qml/Governikus/Global/+mobile/+android/Pane.qml b/resources/qml/Governikus/Global/+mobile/+android/Pane.qml new file mode 100644 index 0000000..9fe7d08 --- /dev/null +++ b/resources/qml/Governikus/Global/+mobile/+android/Pane.qml @@ -0,0 +1,43 @@ +import QtQuick 2.10 +import QtGraphicalEffects 1.0 + +Rectangle { + id: root + property alias title: titleText.text + default property alias paneChildren: paneContent.children + + anchors.left: parent.left + anchors.right: parent.right + height: childrenRect.height + color: "white" + radius: 16 + + Column { + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: Constants.pane_padding + anchors.rightMargin: Constants.pane_padding + topPadding: Constants.pane_padding + bottomPadding: Constants.pane_padding + spacing: Constants.pane_spacing + + PaneTitle { + id: titleText + } + + Column { + id: paneContent + width: parent.width + spacing: Constants.pane_spacing + } + } + + layer.enabled: true + layer.effect: DropShadow { + radius: 8 + samples: 8 + source: root + color: Constants.grey + verticalOffset: 2 + } +} diff --git a/resources/qml/Governikus/Global/+mobile/+ios/+phone/DeviceConstants.qml b/resources/qml/Governikus/Global/+mobile/+ios/+phone/DeviceConstants.qml new file mode 100644 index 0000000..59348c1 --- /dev/null +++ b/resources/qml/Governikus/Global/+mobile/+ios/+phone/DeviceConstants.qml @@ -0,0 +1,9 @@ +import QtQuick 2.10 + +import "Utils.js" as Utils + +Item { + readonly property int history_section_height: Utils.dp(85) + + readonly property bool is_tablet: false +} diff --git a/resources/qml/Governikus/Global/+mobile/+ios/+phone/Pane.qml b/resources/qml/Governikus/Global/+mobile/+ios/+phone/Pane.qml new file mode 100644 index 0000000..091b234 --- /dev/null +++ b/resources/qml/Governikus/Global/+mobile/+ios/+phone/Pane.qml @@ -0,0 +1,43 @@ +import QtQuick 2.10 + +import "Utils.js" as Utils + +Column { + id: root + property alias title: titleText.text + property alias spacing: paneContent.spacing + default property alias paneData: paneContent.data + + anchors.left: parent.left + anchors.right: parent.right + + PaneTitle { + id: titleText + height: Utils.dp(30) + anchors.margins: Constants.pane_padding + font.capitalization: Font.AllUppercase + color: Constants.grey + } + + Rectangle { width: parent.width; height: 1; color: Constants.grey} + Rectangle { + color: "white" + width: parent.width + height: childrenRect.height + + Column { + width: parent.width + Item { width: parent.width; height: Constants.pane_padding } + Column { + id: paneContent + anchors.left: parent.left + anchors.leftMargin: Constants.pane_padding + anchors.right: parent.right + anchors.rightMargin: Constants.pane_padding + spacing: Constants.component_spacing + } + Item { width: parent.width; height: Constants.pane_padding } + } + } + Rectangle { width: parent.width; height: 1; color: Constants.grey} +} diff --git a/resources/qml/Governikus/Global/+mobile/+ios/+tablet/DeviceConstants.qml b/resources/qml/Governikus/Global/+mobile/+ios/+tablet/DeviceConstants.qml new file mode 100644 index 0000000..91e4b85 --- /dev/null +++ b/resources/qml/Governikus/Global/+mobile/+ios/+tablet/DeviceConstants.qml @@ -0,0 +1,9 @@ +import QtQuick 2.10 + +import "Utils.js" as Utils + +Item { + readonly property int history_section_height: Utils.dp(60) + + readonly property bool is_tablet: true +} diff --git a/resources/qml/Governikus/Global/+mobile/+ios/+tablet/Pane.qml b/resources/qml/Governikus/Global/+mobile/+ios/+tablet/Pane.qml new file mode 100644 index 0000000..4919635 --- /dev/null +++ b/resources/qml/Governikus/Global/+mobile/+ios/+tablet/Pane.qml @@ -0,0 +1,44 @@ +import QtQuick 2.10 +import QtGraphicalEffects 1.0 + + +Rectangle { + id: root + property alias title: titleText.text + property alias spacing: paneContent.spacing + default property alias paneChildren: paneContent.children + + anchors.left: parent.left + anchors.right: parent.right + height: childrenRect.height + color: "white" + + Column { + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: Constants.pane_padding + anchors.rightMargin: Constants.pane_padding + + Text { + id: titleText + height: implicitHeight * 2 + verticalAlignment: Text.AlignVCenter + } + Item { width: parent.width; height: Constants.pane_padding } + Column { + id: paneContent + width: parent.width + spacing: Constants.pane_spacing + } + Item { width: parent.width; height: Constants.pane_padding } + } + + layer.enabled: true + layer.effect: DropShadow { + radius: 8 + samples: 8 + source: root + color: Constants.grey + verticalOffset: 2 + } +} diff --git a/resources/qml/Governikus/Global/+mobile/+ios/BrandConstants.qml b/resources/qml/Governikus/Global/+mobile/+ios/BrandConstants.qml new file mode 100644 index 0000000..83e0a90 --- /dev/null +++ b/resources/qml/Governikus/Global/+mobile/+ios/BrandConstants.qml @@ -0,0 +1,24 @@ +import QtQuick 2.10 + +import "Utils.js" as Utils + +DeviceConstants { + readonly property color grey_light: "#8e8e93" + readonly property color blue_dark: "#0076ff" + readonly property color blue_light: "#54c7fc" + readonly property color accent_color: "#000000" + readonly property color secondary_text: "#000000" + readonly property int titlebar_font_size: Utils.dp(16) + readonly property int provider_section_height: Utils.dp(50) + readonly property int history_delegate_spacing: 0 + readonly property color history_delegate_address_color: "#00878F" + readonly property int button_height: Utils.dp(40) + + readonly property bool is_layout_android: false + readonly property bool is_layout_ios: true + readonly property bool leftNavigation: false + readonly property bool bottomNavigation: true + + readonly property int searchbar_height: Utils.dp(48) + readonly property int tabbar_height: Utils.dp(48) +} diff --git a/resources/qml/Governikus/Global/+mobile/+ios/GButton.qml b/resources/qml/Governikus/Global/+mobile/+ios/GButton.qml new file mode 100644 index 0000000..9ac38bb --- /dev/null +++ b/resources/qml/Governikus/Global/+mobile/+ios/GButton.qml @@ -0,0 +1,61 @@ +import QtQuick 2.10 +import QtGraphicalEffects 1.0 + +import "Utils.js" as Utils + +/* Custom implementation to be replaced with template specialization of Qt.labs.controls Button*/ +Rectangle { + id: rect + property alias text: textItem.text + property color buttonColor : Constants.blue + property int maxWidth: 0 + property int preferedWidth: parent.width - 4 * Constants.component_spacing + property alias iconSource: icon.source + + signal clicked + + color: enabled ? buttonColor : "#10000000" + height: Constants.button_height + width: maxWidth > 0 ? Math.min(maxWidth, preferedWidth) : preferedWidth + clip: true + + Image { + id: icon + visible: source.toString().length > 0 + height: rect.height - Utils.dp(10) + width: height + anchors.left: rect.left + anchors.leftMargin: Utils.dp(5) + anchors.verticalCenter: rect.verticalCenter + } + + Text { + id: textItem + anchors.centerIn: parent + color: enabled ? "white" : "#40000000" + opacity: mouseArea.containsMouse ? 0.5 : 1 + anchors.leftMargin: icon.visible ? icon.width + icon.anchors.leftMargin : 0 + font.pixelSize: Utils.dp(16) + } + + MouseArea{ + id: mouseArea + anchors.fill: parent + preventStealing: true + hoverEnabled: true + onClicked: parent.clicked() + } + + RadialGradient { + x: mouseArea.mouseX - width * 0.5 + height: parent.height + width: height * 2 + visible: mouseArea.pressed + opacity: 1 + gradient: Gradient { + GradientStop { position: 0.0; color: Qt.rgba(255,255,255,1) } + GradientStop { position: 0.2; color: Qt.rgba(255,255,255,0.5) } + GradientStop { position: 0.4; color: Qt.rgba(255,255,255,0) } + } + } +} diff --git a/resources/qml/Governikus/Global/+mobile/+ios/GCheckBox.qml b/resources/qml/Governikus/Global/+mobile/+ios/GCheckBox.qml new file mode 100644 index 0000000..c098ea7 --- /dev/null +++ b/resources/qml/Governikus/Global/+mobile/+ios/GCheckBox.qml @@ -0,0 +1,29 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 + +import "Utils.js" as Utils + + +CheckBox { + id: control + + indicator: Rectangle { + implicitHeight: Utils.dp(26) + implicitWidth: Utils.dp(26) + anchors.centerIn: parent + + color: Constants.white + border.color: Constants.black + border.width: Utils.dp(1) + + Image { + source: "qrc:///images/iOS/CheckedCheckbox.png" + anchors.fill: parent + anchors.margins: Utils.dp(2) + fillMode: Image.PreserveAspectFit + visible: control.checked + } + } +} diff --git a/resources/qml/Governikus/Global/+mobile/+ios/LabeledText.qml b/resources/qml/Governikus/Global/+mobile/+ios/LabeledText.qml new file mode 100644 index 0000000..a5e05f0 --- /dev/null +++ b/resources/qml/Governikus/Global/+mobile/+ios/LabeledText.qml @@ -0,0 +1,40 @@ +import QtQuick 2.10 + + +Item { + property alias label: labelText.text + property alias text: bodyText.text + property alias textFormat: bodyText.textFormat + property int margin + property int fontUppercase + + signal linkActivated(string link) + + height: childrenRect.height + margin + + Text { + id: labelText + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: margin + anchors.right: parent.right + anchors.rightMargin: margin + font.pixelSize: Constants.normal_font_size + color: Constants.blue + wrapMode: Text.WordWrap + } + + Text { + id: bodyText + color: Constants.secondary_text + anchors.top: labelText.bottom + anchors.left: parent.left + anchors.leftMargin: margin + anchors.right: parent.right + anchors.rightMargin: margin + font.pixelSize: Constants.normal_font_size + font.capitalization: fontUppercase + wrapMode: Text.WordWrap + onLinkActivated: parent.linkActivated(link) + } +} diff --git a/resources/qml/Governikus/Global/+mobile/ConfirmationPopup.qml b/resources/qml/Governikus/Global/+mobile/ConfirmationPopup.qml new file mode 100644 index 0000000..fb9369a --- /dev/null +++ b/resources/qml/Governikus/Global/+mobile/ConfirmationPopup.qml @@ -0,0 +1,87 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 + +Popup { + id: popupObject + signal confirmed + + property var baseItem: Item { } + x: (baseItem.width - width) / 2 + y: (baseItem.height - height) / 2 + + property alias title: header.text + property alias text: info.text + property alias confirmText: textItemDelete.text + + modal: true + focus: true + closePolicy: Popup.CloseOnPressOutside | Popup.CloseOnEscape + width: Utils.dp(250) + height: popupColumn.height + + Column { + id: popupColumn + width: parent.width + spacing: Constants.pane_spacing + padding: Constants.pane_padding + + Text { + id: header + color: Constants.secondary_text + font.pixelSize: Constants.header_font_size + font.bold: true + } + + Text { + id: info + color: Constants.secondary_text + width: parent.width - 2 * Constants.pane_padding + wrapMode: Text.WordWrap + font.pixelSize: Constants.normal_font_size + } + + Row { + id: buttonBox + anchors.right: parent.right + anchors.rightMargin: Constants.pane_padding + spacing: Constants.pane_spacing + + MouseArea { + height: textItemCancel.height + 2 * Utils.dp(10) + width: textItemCancel.width + 2 * Utils.dp(10) + + Text { + id: textItemCancel + anchors.centerIn: parent + text: qsTr("Cancel") + settingsModel.translationTrigger + color: Constants.blue + font.pixelSize: Constants.titlebar_font_size + } + + onClicked: { + popupObject.close() + } + } + + MouseArea { + height: textItemDelete.height + 2 * Utils.dp(10) + width: textItemDelete.width + 2 * Utils.dp(10) + + Text { + id: textItemDelete + anchors.centerIn: parent + color: Constants.blue + font.pixelSize: Constants.titlebar_font_size + } + + onClicked: { + popupObject.confirmed() + popupObject.close() + } + } + } + } +} + diff --git a/resources/qml/Governikus/Global/+mobile/GComboBox.qml b/resources/qml/Governikus/Global/+mobile/GComboBox.qml new file mode 100644 index 0000000..bfb3fd6 --- /dev/null +++ b/resources/qml/Governikus/Global/+mobile/GComboBox.qml @@ -0,0 +1,88 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import "Utils.js" as Utils + +ComboBox { + id: control + spacing: Constants.groupbox_spacing + font.pixelSize: Constants.normal_font_size + + QtObject { + id: d + readonly property var mainColor: Constants.black + readonly property var pressedColor: Constants.black + } + + onPressedChanged: canvas.requestPaint() + Component.onCompleted: canvas.requestPaint() + + delegate: ItemDelegate { + width: control.width + contentItem: Text { + text: modelData + color: d.mainColor + font: control.font + elide: Text.ElideRight + verticalAlignment: Text.AlignVCenter + } + highlighted: control.highlightedIndex === index + } + + indicator: Canvas { + id: canvas + x: control.width - width - control.rightPadding + y: control.topPadding + (control.availableHeight - height) / 2 + width: Utils.dp(12) + height: Utils.dp(8) + contextType: "2d" + + onPaint: { + context.reset(); + context.moveTo(0, 0); + context.lineTo(width, 0); + context.lineTo(width / 2, height); + context.closePath(); + context.fillStyle = control.pressed ? d.pressedColor : d.mainColor; + context.fill(); + } + } + + contentItem: Text { + padding: control.spacing + rightPadding: control.indicator.width + control.spacing + + text: control.displayText + font: control.font + color: control.pressed ? d.pressedColor : d.mainColor + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + background: Rectangle { + border.color: control.pressed ? d.pressedColor : d.mainColor + border.width: control.visualFocus ? Utils.dp(2) : Utils.dp(1) + radius: Utils.dp(2) + } + + popup: Popup { + y: control.height - 1 + width: control.width + implicitHeight: contentItem.implicitHeight + padding: Utils.dp(1) + + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: control.popup.visible ? control.delegateModel : null + currentIndex: control.highlightedIndex + + ScrollIndicator.vertical: ScrollIndicator { } + } + + background: Rectangle { + border.color: d.mainColor + radius: Utils.dp(2) + } + } +} diff --git a/resources/qml/Governikus/Global/+mobile/GRadioButton.qml b/resources/qml/Governikus/Global/+mobile/GRadioButton.qml new file mode 100644 index 0000000..f806df0 --- /dev/null +++ b/resources/qml/Governikus/Global/+mobile/GRadioButton.qml @@ -0,0 +1,36 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import "Utils.js" as Utils + +RadioButton { + id: control + spacing: Constants.groupbox_spacing + + indicator: Rectangle { + implicitWidth: Utils.dp(26) + implicitHeight: implicitWidth + x: control.leftPadding + y: parent.height / 2 - height / 2 + radius: height / 2 + border.color: Qt.darker(Constants.blue, control.down ? 1.5 : 1) + + Rectangle { + width: Utils.dp(14) + height: width + x: (parent.width - width) / 2 + y: x + radius: width / 2 + color: Qt.darker(Constants.blue, control.down ? 1.5 : 1) + visible: control.checked + } + } + + contentItem: Text { + text: control.text + font.pixelSize: Constants.label_font_size + color: Constants.secondary_text + verticalAlignment: Text.AlignVCenter + leftPadding: control.indicator.width + control.spacing + } +} diff --git a/resources/qml/Governikus/Global/+mobile/GSwitch.qml b/resources/qml/Governikus/Global/+mobile/GSwitch.qml new file mode 100644 index 0000000..2f0c793 --- /dev/null +++ b/resources/qml/Governikus/Global/+mobile/GSwitch.qml @@ -0,0 +1,89 @@ +import QtQuick 2.10 + +import "Utils.js" as Utils + +MouseArea { + id: baseItem + + property bool initialState: false + property color color: Constants.blue + readonly property alias isOn: toggleswitch.isOn + signal switched() + + width: Utils.dp(60) + height: Utils.dp(48) + + onClicked: toggleswitch.toggle() + Component.onCompleted: toggleswitch.isOn = baseItem.initialState + + Item { + id: toggleswitch + anchors.fill: parent + anchors.topMargin: Utils.dp(12) + anchors.bottomMargin: Utils.dp(12) + + onIsOnChanged: updateState() + property bool isOn: false + readonly property int dragMax: width - height + + function updateState() { + state = (isOn ? "on" : "off") + } + + function toggle() { + isOn = !isOn + switched() + } + + function captureSwitch() { + state = "moving" + } + + function releaseSwitch() { + if (isOn !== (button.x > dragMax / 2)) toggle() + updateState() + } + + Rectangle { + id: background + anchors.fill: parent + anchors.margins: parent.height / 4 + radius: height / 2 + color: isOn ? Qt.lighter(baseItem.color, 1.55) : "lightgray" + } + + Rectangle { + id: button + height: parent.height + width: height + radius: width + color: isOn ? baseItem.color : "darkgray" + + MouseArea { + anchors.fill: parent + drag.target: button + drag.axis: Drag.XAxis + drag.minimumX: 0 + drag.maximumX: toggleswitch.dragMax + onClicked: toggleswitch.toggle() + onPressed: toggleswitch.captureSwitch() + onReleased: toggleswitch.releaseSwitch() + } + } + + states: [ + State { + name: "on" + PropertyChanges { target: button; x: toggleswitch.dragMax } + }, + State { + name: "off" + PropertyChanges { target: button; x: 0 } + } + ] + + transitions: Transition { + NumberAnimation { properties: "x"; easing.type: Easing.InOutQuad; duration: 200 } + } + } +} diff --git a/resources/qml/Governikus/Global/+mobile/GTextField.qml b/resources/qml/Governikus/Global/+mobile/GTextField.qml new file mode 100644 index 0000000..eb90814 --- /dev/null +++ b/resources/qml/Governikus/Global/+mobile/GTextField.qml @@ -0,0 +1,37 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import "Utils.js" as Utils + +Item { + property alias text: field.text + property alias displayText: field.displayText + property alias echoMode: field.echoMode + signal accepted + + property bool valid: true + + height: Utils.dp(24) + width: Utils.dp(240) + + Rectangle { + radius: Utils.dp(6) + anchors.fill: parent + border.color: Constants.red + color: enabled ? "white" : Constants.grey + border.width: valid ? 0 : Utils.dp(2) + } + + TextField { + id: field + anchors.fill: parent + anchors.leftMargin: Utils.dp(6) + anchors.rightMargin: Utils.dp(6) + padding: 0 + font.pixelSize: Constants.normal_font_size + onAccepted: parent.accepted() + background: Item {} + } + + onActiveFocusChanged: if (focus) field.forceActiveFocus() +} diff --git a/resources/qml/Governikus/Global/+mobile/LocationButton.qml b/resources/qml/Governikus/Global/+mobile/LocationButton.qml new file mode 100644 index 0000000..ff9806f --- /dev/null +++ b/resources/qml/Governikus/Global/+mobile/LocationButton.qml @@ -0,0 +1,48 @@ +import QtQuick 2.10 + +import "Utils.js" as Utils + +MouseArea { + property string language + property string name + property string image + + height: Utils.dp(35) + width: height + + onClicked: { + settingsModel.language = language + if (typeof(navigationController) !== "undefined") { + navigationController.close() + } + } + + Rectangle { + opacity: 0.1 + border.color: "black" + border.width: settingsModel.language === language ? 0 : Utils.dp(1) + color: settingsModel.language === language ? "black" : Constants.background_color + anchors.fill: parent + radius: Utils.dp(3) + } + + Text { + text: name + color: Constants.secondary_text + + anchors.margins: Utils.dp(2) + anchors.top: parent.top + anchors.horizontalCenter: parent.horizontalCenter + font.pixelSize: Constants.small_font_size + } + + Image { + source: image + fillMode: Image.PreserveAspectFit + + anchors.margins: Utils.dp(4) + anchors.left: parent.left + anchors.bottom: parent.bottom + anchors.right: parent.right + } +} diff --git a/resources/qml/Governikus/Global/+mobile/PlatformConstants.qml b/resources/qml/Governikus/Global/+mobile/PlatformConstants.qml new file mode 100644 index 0000000..56c6f8b --- /dev/null +++ b/resources/qml/Governikus/Global/+mobile/PlatformConstants.qml @@ -0,0 +1,49 @@ +import QtQuick 2.10 +import QtQuick.Window 2.2 + +import "Utils.js" as Utils + +BrandConstants { + readonly property color blue: "#659bcd" + readonly property color background_color: "#dcebf6" + readonly property color grey_border: "lightslategrey" + + readonly property color tutorial_orange: "#f9a501" + readonly property color tutorial_green: "#73d7b3" + readonly property color tutorial_blue: "#659bcd" + readonly property color tutorial_red: "#fb7a59" + readonly property color tutorial_very_light_grey: "#f2f2f2" + + readonly property int header_font_size: thresholdReduce(22) + readonly property int pane_title_font_size: header_font_size + readonly property int normal_font_size: thresholdReduce(16) + readonly property int label_font_size: Utils.dp(14) + readonly property int small_font_size: Utils.dp(12) + + readonly property int tutorial_component_spacing: Utils.dp(40) + readonly property int tutorial_header_font_size: Utils.dp(60) + readonly property int tutorial_content_header_h1_font_size: Utils.dp(26) + readonly property int tutorial_content_header_h2_font_size: Utils.dp(20) + readonly property int tutorial_content_font_size: Utils.dp(16) + + readonly property int menubar_width: Utils.dp(60) + + readonly property int titlebar_height: Utils.dp(48) + readonly property int titlebar_padding: Utils.dp(12) + readonly property int titlebar_spacing: Utils.dp(18) + + readonly property int component_spacing: Utils.dp(20) + readonly property int pane_padding: Utils.dp(20) + readonly property int pane_spacing: Utils.dp(20) + readonly property int groupbox_spacing: Utils.dp(10) + + readonly property bool is_desktop: false + + function thresholdReduce(value) { + var w = Screen.width + if (w > 415) { + return Utils.dp(value) + } + return Utils.dp(value * w / 415) + } +} diff --git a/resources/qml/Governikus/Global/Constants.qml b/resources/qml/Governikus/Global/Constants.qml index a9e4e36..2b89145 100644 --- a/resources/qml/Governikus/Global/Constants.qml +++ b/resources/qml/Governikus/Global/Constants.qml @@ -1,17 +1,10 @@ pragma Singleton -import QtQuick 2.5 -import QtQuick.Window 2.2 +import QtQuick 2.10 -import "Utils.js" as Utils -import "." as Gov +import Governikus.Type.ApplicationModel 1.0 - -Item { - readonly property bool use_history_list_delete_area: Gov.PlatformConstants.use_history_list_delete_area - - readonly property color background_color: "#dcebf6" - readonly property color blue: "#659bcd" +PlatformConstants { readonly property color green: "#a3cb7f" readonly property color red: "#cc0000" readonly property color grey: "#8e8e93" @@ -19,45 +12,5 @@ Item { readonly property color white: "#ffffff" readonly property color black: "#000000" - readonly property color primary_text: Gov.PlatformConstants.primary_text - readonly property color secondary_text: Gov.PlatformConstants.secondary_text - readonly property color accent_color: Gov.PlatformConstants.accent_color - readonly property color second_accent_color: Gov.PlatformConstants.second_accent_color - - readonly property int header_font_size: thresholdReduce(22) - readonly property int normal_font_size: thresholdReduce(16) - readonly property int label_font_size: Utils.sp(14) - readonly property int small_font_size: Utils.sp(12) - - readonly property int titlebar_height: Utils.dp(48) - readonly property int titlebar_padding: Utils.dp(12) - readonly property int titlebar_spacing: Utils.dp(18) - readonly property int titlebar_font_size: Gov.PlatformConstants.titlebar_font_size - - readonly property int menubar_width: Utils.dp(60) - - readonly property int searchbar_height: Utils.dp(48) - - readonly property int provider_section_height: Gov.PlatformConstants.provider_section_height - - readonly property int history_section_height: Gov.PlatformConstants.history_section_height - readonly property int history_delegate_spacing: Gov.PlatformConstants.history_delegate_spacing - readonly property color history_delegate_address_color: Gov.PlatformConstants.history_delegate_address_color - - readonly property int button_height: Gov.PlatformConstants.button_height - - readonly property int tabbar_height: Utils.dp(48) - - readonly property int component_spacing: Utils.dp(20) - readonly property int pane_padding: Utils.dp(20) - readonly property int pane_spacing: Utils.dp(20) - readonly property int groupbox_spacing: Utils.dp(10) - - function thresholdReduce(value) { - var w = Screen.width - if (w > 415) { - return Utils.sp(value) - } - return Utils.sp(value * w / 415) - } + readonly property double scrolling_speed: 7500.0 } diff --git a/resources/qml/Governikus/Global/GButton.qml b/resources/qml/Governikus/Global/GButton.qml deleted file mode 100644 index 4b11736..0000000 --- a/resources/qml/Governikus/Global/GButton.qml +++ /dev/null @@ -1,3 +0,0 @@ -Item { - id: dummy -} diff --git a/resources/qml/Governikus/Global/GCheckBox.qml b/resources/qml/Governikus/Global/GCheckBox.qml deleted file mode 100644 index 4b11736..0000000 --- a/resources/qml/Governikus/Global/GCheckBox.qml +++ /dev/null @@ -1,3 +0,0 @@ -Item { - id: dummy -} diff --git a/resources/qml/Governikus/Global/GSwitch.qml b/resources/qml/Governikus/Global/GSwitch.qml deleted file mode 100644 index ed44bc8..0000000 --- a/resources/qml/Governikus/Global/GSwitch.qml +++ /dev/null @@ -1,74 +0,0 @@ -import QtQuick 2.7 - -import "Utils.js" as Utils -import "." as Gov - -MouseArea { - property alias isOn: toggleswitch.isOn - width: Utils.dp(60) - height: Utils.dp(48) - - onClicked: toggleswitch.toggle() - - Item { - id: toggleswitch - anchors.fill: parent - anchors.topMargin: Utils.dp(12) - anchors.bottomMargin: Utils.dp(12) - - property bool isOn: false - onIsOnChanged: state = (isOn ? "on" : "off") - - readonly property int dragMax: width - height - - function toggle() { - isOn = !isOn - } - - function releaseSwitch() { - isOn = (button.x > dragMax / 2) - button.x = (isOn ? dragMax : 0) - } - - Rectangle { - id: background - anchors.fill: parent - anchors.margins: parent.height / 4 - radius: height / 2 - color: isOn ? Qt.lighter(Gov.Constants.blue, 1.55) : "lightgray" - } - - Rectangle { - id: button - height: parent.height - width: height - radius: width - color: isOn ? Gov.Constants.blue : "darkgray" - - MouseArea { - anchors.fill: parent - drag.target: button - drag.axis: Drag.XAxis - drag.minimumX: 0 - drag.maximumX: toggleswitch.dragMax - onClicked: toggleswitch.toggle() - onReleased: toggleswitch.releaseSwitch() - } - } - - states: [ - State { - name: "on" - PropertyChanges { target: button; x: toggleswitch.dragMax } - }, - State { - name: "off" - PropertyChanges { target: button; x: 0 } - } - ] - - transitions: Transition { - NumberAnimation { properties: "x"; easing.type: Easing.InOutQuad; duration: 200 } - } - } -} diff --git a/resources/qml/Governikus/Global/GTextField.qml b/resources/qml/Governikus/Global/GTextField.qml deleted file mode 100644 index bbf26b0..0000000 --- a/resources/qml/Governikus/Global/GTextField.qml +++ /dev/null @@ -1,40 +0,0 @@ -import QtQuick 2.7 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 - -import "Utils.js" as Utils -import "." as Gov - -Item { - property alias text: field.text - property alias displayText: field.displayText - property alias echoMode: field.echoMode - signal accepted - - property bool valid: true - - height: Utils.dp(24) - width: Utils.dp(240) - - Rectangle { - radius: Utils.dp(6) - anchors.fill: parent - border.color: Gov.Constants.red - color: enabled ? "white" : Gov.Constants.grey - border.width: valid ? 0 : Utils.dp(2) - } - - TextField { - id: field - anchors.fill: parent - anchors.leftMargin: Utils.dp(6) - anchors.rightMargin: Utils.dp(6) - font.pixelSize: Gov.Constants.normal_font_size - onAccepted: parent.accepted() - style: TextFieldStyle { - background: Rectangle {} - } - } - - onActiveFocusChanged: if (focus) field.forceActiveFocus() -} diff --git a/resources/qml/Governikus/Global/LabeledText.qml b/resources/qml/Governikus/Global/LabeledText.qml deleted file mode 100644 index 4b11736..0000000 --- a/resources/qml/Governikus/Global/LabeledText.qml +++ /dev/null @@ -1,3 +0,0 @@ -Item { - id: dummy -} diff --git a/resources/qml/Governikus/Global/LocationButton.qml b/resources/qml/Governikus/Global/LocationButton.qml deleted file mode 100644 index 85adf18..0000000 --- a/resources/qml/Governikus/Global/LocationButton.qml +++ /dev/null @@ -1,49 +0,0 @@ -import QtQuick 2.7 - -import "Utils.js" as Utils -import "." as Gov - -MouseArea { - property string language - property string name - property string image - - height: Utils.dp(35) - width: height - - onClicked: { - settingsModel.language = language - if (typeof(navigationController) !== "undefined") { - navigationController.close() - } - } - - Rectangle { - opacity: 0.1 - border.color: "black" - border.width: settingsModel.language === language ? 0 : Utils.dp(1) - color: settingsModel.language === language ? "black" : Gov.Constants.background_color - anchors.fill: parent - radius: Utils.dp(3) - } - - Text { - text: name - color: Gov.Constants.secondary_text - - anchors.margins: Utils.dp(2) - anchors.top: parent.top - anchors.horizontalCenter: parent.horizontalCenter - font.pixelSize: Gov.Constants.small_font_size - } - - Image { - source: image - fillMode: Image.PreserveAspectFit - - anchors.margins: Utils.dp(4) - anchors.left: parent.left - anchors.bottom: parent.bottom - anchors.right: parent.right - } -} diff --git a/resources/qml/Governikus/Global/Pane.qml b/resources/qml/Governikus/Global/Pane.qml deleted file mode 100644 index 4b11736..0000000 --- a/resources/qml/Governikus/Global/Pane.qml +++ /dev/null @@ -1,3 +0,0 @@ -Item { - id: dummy -} diff --git a/resources/qml/Governikus/Global/PaneTitle.qml b/resources/qml/Governikus/Global/PaneTitle.qml new file mode 100644 index 0000000..4087927 --- /dev/null +++ b/resources/qml/Governikus/Global/PaneTitle.qml @@ -0,0 +1,11 @@ +import QtQuick 2.10 + + +Text { + anchors.left: parent.left + anchors.right: parent.right + visible: text !== "" + font.pixelSize: Constants.pane_title_font_size + color: Constants.blue + elide: Text.ElideRight +} diff --git a/resources/qml/Governikus/Global/PlatformConstants.qml b/resources/qml/Governikus/Global/PlatformConstants.qml deleted file mode 100644 index 4b11736..0000000 --- a/resources/qml/Governikus/Global/PlatformConstants.qml +++ /dev/null @@ -1,3 +0,0 @@ -Item { - id: dummy -} diff --git a/resources/qml/Governikus/Global/SectionPage.qml b/resources/qml/Governikus/Global/SectionPage.qml deleted file mode 100644 index d95f24c..0000000 --- a/resources/qml/Governikus/Global/SectionPage.qml +++ /dev/null @@ -1,58 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Layouts 1.2 - -import Governikus.Global 1.0 -import Governikus.TitleBar 1.0 - -Item { - signal firePush(var sectionPage, var properties) - signal firePop() - signal firePopAll() - - property bool topLevelPage: false - - property var leftTitleBarAction: TitleBarAction {} - property var headerTitleBarAction: TitleBarAction {} - property var rightTitleBarAction: Item {} - property var subTitleBarAction: Item {} - - property color titleBarColor: Constants.blue - /* if the header component has a property named titleBarOpacity, use it, otherwise use default value*/ - readonly property real titleBarOpacity: headerLoader.item && typeof(headerLoader.item.titleBarOpacity) != "undefined" ? headerLoader.item.titleBarOpacity : 1 - - property alias header: headerLoader.sourceComponent - property alias content: contentLoader.sourceComponent - property alias contentY: flickable.contentY - property bool disableFlicking: false - - Flickable { - property real startContentY: 0 - id: flickable - clip: true - flickableDirection: Flickable.VerticalFlick - contentWidth: flickableContent.width - contentHeight: flickableContent.height - anchors.bottom: parent.bottom - width: parent.width - /* if a header is set, it is shown as background of the TitleBar, so we need to expand the height*/ - height: headerLoader.item ? parent.height + Constants.titlebar_height : parent.height - onMovementStarted: { - startContentY = contentY - } - onContentYChanged: { - if (disableFlicking || contentY < 0) { contentY = 0 /* prevent flicking over the top */} - } - Column { - id: flickableContent - Loader { - id: headerLoader - readonly property alias contentY: flickable.contentY - } - Loader { - id: contentLoader - readonly property alias contentY: flickable.contentY - } - } - } -} diff --git a/resources/qml/Governikus/Global/StatusIcon.qml b/resources/qml/Governikus/Global/StatusIcon.qml new file mode 100644 index 0000000..c8878b7 --- /dev/null +++ b/resources/qml/Governikus/Global/StatusIcon.qml @@ -0,0 +1,51 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Style 1.0 + + +Rectangle { + property alias busy: busyIndicator.visible + property alias source: image.source + property alias text: text.text + property alias font: text.font + + width: height + radius: height / 2 + border.width: height / 40; + border.color: Constants.white + color: "transparent" + + BusyIndicator { + id: busyIndicator + anchors.fill: parent + visible: false + running: parent.visible + contentItem: NpaBusyIndicatorStyle { factor: 1 } + } + + Rectangle { + radius: height / 2 + anchors.fill: parent + anchors.margins: parent.height / 8; + color: Constants.white + + Image { + id: image + anchors.fill: parent + anchors.margins: parent.height / 8 + sourceSize.height: Constants.is_desktop ? height : undefined + fillMode: Image.PreserveAspectFit + visible: source.toString().length > 0 + } + + Text { + id: text + anchors.centerIn: parent + color: Constants.blue + font.bold: true + font.pixelSize: Constants.header_font_size + visible: text !== "" + } + } +} diff --git a/resources/qml/Governikus/Global/Utils.js b/resources/qml/Governikus/Global/Utils.js index fa448c5..f161cac 100644 --- a/resources/qml/Governikus/Global/Utils.js +++ b/resources/qml/Governikus/Global/Utils.js @@ -1,3 +1,7 @@ +function noTest() +{ + return typeof(plugin) !== 'undefined' +} function escapeHtml(str) { @@ -37,20 +41,13 @@ function getRandomInt(min, max) return Math.floor(Math.random() * (max - min)) + min; } -var contentScaleFactor = screenDpi / 160 +var contentScaleFactor = noTest() ? screenDpiScale : 1 function dp(value) { return value * contentScaleFactor } -function sp(value) -{ - var textScale = 1 - return dp(value) * textScale -} - - function providerIconSource(baseName) { var platform = plugin.platformStyle.indexOf("tablet") !== -1 ? "+tablet/" : "" diff --git a/resources/qml/Governikus/Global/qmldir b/resources/qml/Governikus/Global/qmldir index afca3a9..5f9b951 100644 --- a/resources/qml/Governikus/Global/qmldir +++ b/resources/qml/Governikus/Global/qmldir @@ -1,13 +1,24 @@ module Global + singleton Constants 1.0 Constants.qml -singleton PlatformConstants 1.0 PlatformConstants.qml +internal PlatformConstants PlatformConstants.qml +internal BrandConstants BrandConstants.qml +internal DeviceConstants DeviceConstants.qml + Utils 1.0 Utils.js Category 1.0 Category.js -LabeledText 1.0 LabeledText.qml -Pane 1.0 Pane.qml -GCheckBox 1.0 GCheckBox.qml -GTextField 1.0 GTextField.qml + +ConfirmationPopup 1.0 ConfirmationPopup.qml +ContinueButton 1.0 ContinueButton.qml GButton 1.0 GButton.qml -LocationButton 1.0 LocationButton.qml -SectionPage 1.0 SectionPage.qml +GCheckBox 1.0 GCheckBox.qml +GComboBox 1.0 GComboBox.qml +GRadioButton 1.0 GRadioButton.qml GSwitch 1.0 GSwitch.qml +GText 1.0 GText.qml +GTextField 1.0 GTextField.qml +LabeledText 1.0 LabeledText.qml +LocationButton 1.0 LocationButton.qml +Pane 1.0 Pane.qml +PaneTitle 1.0 PaneTitle.qml +StatusIcon 1.0 StatusIcon.qml diff --git a/resources/qml/Governikus/HistoryView/+android/+phone/HistoryView.qml b/resources/qml/Governikus/HistoryView/+android/+phone/HistoryView.qml new file mode 100644 index 0000000..b3045a6 --- /dev/null +++ b/resources/qml/Governikus/HistoryView/+android/+phone/HistoryView.qml @@ -0,0 +1,61 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.2 + +import Governikus.Global 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 + +SectionPage { + id: rootPage + property alias listViewModel: listView.model + property var selectedIndices: [] + + headerTitleBarAction: TitleBarAction { text: qsTr("History") + settingsModel.translationTrigger; font.bold: true } + rightTitleBarAction: HistoryViewTitleBarControls { + id: historyControls + deleteHistoryConfirmationPopup: deleteHistoryConfirmationPopup + } + + HistoryViewConfirmationPopup { + id: deleteHistoryConfirmationPopup + baseItem: rootPage + } + + Text { + color: Constants.secondary_text + anchors.centerIn: parent + text: qsTr("Currently there are no history entries.") + settingsModel.translationTrigger + wrapMode: Text.WordWrap + font.pixelSize: Constants.normal_font_size + visible: listView.count === 0 + } + + ListView { + id: listView + anchors.fill: parent + model: historyModel + onContentYChanged: { + if (contentY < 0) { + // prevent flicking over the top + contentY = 0 + } + } + + delegate: + HistoryListViewDelegate { + id: historyDelegate + anchors.left: parent.left + anchors.right: parent.right + height: Utils.dp(120) + listModel: historyModel + property var historyModelItem: model + showDetail: false + } + } + + HistoryViewDetails { + id: detailsHistoryView + visible: false + } +} diff --git a/resources/qml/Governikus/HistoryView/+android/+tablet/HistoryView.qml b/resources/qml/Governikus/HistoryView/+android/+tablet/HistoryView.qml index 18c9d82..fe88448 100644 --- a/resources/qml/Governikus/HistoryView/+android/+tablet/HistoryView.qml +++ b/resources/qml/Governikus/HistoryView/+android/+tablet/HistoryView.qml @@ -1,11 +1,11 @@ -import QtQuick 2.5 -import QtQuick.Controls 2.2 -import QtQuick.Controls.Styles 1.4 +import QtQuick 2.10 +import QtQuick.Controls 2.3 import QtQuick.Layouts 1.2 import Governikus.Global 1.0 import Governikus.TitleBar 1.0 import Governikus.Provider 1.0 +import Governikus.View 1.0 SectionPage { id: rootPage @@ -13,10 +13,13 @@ SectionPage { property var selectedIndices: [] headerTitleBarAction: TitleBarAction { text: qsTr("History") + settingsModel.translationTrigger; font.bold: true } - rightTitleBarAction: HistoryContextMenu { historyReminderPopup: deleteHistoryConfirmationDialog } + rightTitleBarAction: HistoryViewTitleBarControls { + id: historyControls + deleteHistoryConfirmationPopup: deleteHistoryConfirmationPopup + } HistoryViewConfirmationPopup { - id: deleteHistoryConfirmationDialog + id: deleteHistoryConfirmationPopup baseItem: rootPage } @@ -52,7 +55,7 @@ SectionPage { } } - ProviderDetailView_tablet { + ProviderDetailView { id: providerHistoryView visible: false } diff --git a/resources/qml/Governikus/HistoryView/+android/CustomSwipeBar.qml b/resources/qml/Governikus/HistoryView/+android/CustomSwipeBar.qml deleted file mode 100644 index 814a0ed..0000000 --- a/resources/qml/Governikus/HistoryView/+android/CustomSwipeBar.qml +++ /dev/null @@ -1,55 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Layouts 1.2 -import QtQuick.Controls 2.0 as QtControls - -import Governikus.Global 1.0 - -QtControls.TabBar { - id: rootItem - - height: firstButton.height - width: parent.width - - QtControls.TabButton { - id: firstButton - padding: Utils.dp(10) - // TODO: Workaround, use contentItem when switching to Qt 5.7.1 - // See https://bugreports.qt.io/browse/QTBUG-50992 - text: qsTr("CONTACT") + settingsModel.translationTrigger - -/* - contentItem: Text { - text: qsTr("Contact") + settingsModel.translationTrigger - font.pixelSize: Constants.normal_font_size - font.capitalization: Font.AllUppercase - elide: Text.ElideRight - opacity: enabled ? 1 : 0.3 - color: !parent.checked ? "black" : parent.pressed ? "black" : Constants.blue - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - } -*/ - } - - - QtControls.TabButton { - padding: Utils.dp(10) - // TODO: Workaround, use contentItem when switching to Qt 5.7.1 - // See https://bugreports.qt.io/browse/QTBUG-50992 - text: qsTr("HISTORY") + settingsModel.translationTrigger - -/* - contentItem: Text { - text: qsTr("History") + settingsModel.translationTrigger - font.capitalization: Font.AllUppercase - font.pixelSize: Constants.normal_font_size - elide: Text.ElideRight - opacity: enabled ? 1 : 0.3 - color: !parent.checked ? "black" : parent.pressed ? "black" : Constants.blue - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - } -*/ - } -} diff --git a/resources/qml/Governikus/HistoryView/+android/HistoryItemImage.qml b/resources/qml/Governikus/HistoryView/+android/HistoryItemImage.qml index 1a1ceac..e5ac63a 100644 --- a/resources/qml/Governikus/HistoryView/+android/HistoryItemImage.qml +++ b/resources/qml/Governikus/HistoryView/+android/HistoryItemImage.qml @@ -1,4 +1,4 @@ -import QtQuick 2.5 +import QtQuick 2.10 import Governikus.Global 1.0 diff --git a/resources/qml/Governikus/HistoryView/+android/HistoryListViewDelegate.qml b/resources/qml/Governikus/HistoryView/+android/HistoryListViewDelegate.qml index bca8aa3..257523c 100644 --- a/resources/qml/Governikus/HistoryView/+android/HistoryListViewDelegate.qml +++ b/resources/qml/Governikus/HistoryView/+android/HistoryListViewDelegate.qml @@ -1,4 +1,4 @@ -import QtQuick 2.7 +import QtQuick 2.10 import Governikus.Global 1.0 diff --git a/resources/qml/Governikus/HistoryView/+android/HistoryView.qml b/resources/qml/Governikus/HistoryView/+android/HistoryView.qml deleted file mode 100644 index 07a00db..0000000 --- a/resources/qml/Governikus/HistoryView/+android/HistoryView.qml +++ /dev/null @@ -1,64 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Controls 2.2 -import QtQuick.Controls.Styles 1.4 -import QtQuick.Layouts 1.2 -import QtQuick.Dialogs 1.2 - -import Governikus.Global 1.0 -import Governikus.TitleBar 1.0 - -SectionPage { - id: rootPage - property alias listViewModel: listView.model - property var selectedIndices: [] - - headerTitleBarAction: TitleBarAction { text: qsTr("History") + settingsModel.translationTrigger; font.bold: true } - rightTitleBarAction: HistoryContextMenu { historyReminderPopup: deleteHistoryConfirmationDialog } - - HistoryViewConfirmationPopup { - id: deleteHistoryConfirmationDialog - baseItem: rootPage - } - - Text { - color: Constants.secondary_text - anchors.centerIn: parent - text: qsTr("Currently there are no history entries.") + settingsModel.translationTrigger - wrapMode: Text.WordWrap - font.pixelSize: Constants.normal_font_size - visible: listView.count === 0 - } - - ListView { - id: listView - anchors.fill: parent - model: historyModel - onContentYChanged: { - if (contentY < 0) { - // prevent flicking over the top - contentY = 0 - } - } - - delegate: - HistoryListViewDelegate { - id: historyDelegate - anchors.left: parent.left - anchors.right: parent.right - height: Utils.dp(120) - listModel: historyModel - property var historyModelItem: model - showDetail: false - } - } - - HistoryViewPage { - id: providerHistoryView - visible: false - } - - HistoryViewDetails { - id: detailsHistoryView - visible: false - } -} diff --git a/resources/qml/Governikus/HistoryView/+ios/+phone/HistoryView.qml b/resources/qml/Governikus/HistoryView/+ios/+phone/HistoryView.qml new file mode 100644 index 0000000..c194efa --- /dev/null +++ b/resources/qml/Governikus/HistoryView/+ios/+phone/HistoryView.qml @@ -0,0 +1,101 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 + +SectionPage { + id: baseItem + + leftTitleBarAction: TitleBarAction { + id: leftAction + + state: "edit" + states: [ + State { name: "edit"; when: !historyListView.editMode }, + State { name: "cancel"; when: historyListView.editMode } + ] + onClicked: { + if (state === "edit") { + state = "cancel" + historyListView.editMode = true + } + else { + state = "edit" + historyListView.editMode = false + } + } + } + + headerTitleBarAction: Text { text: qsTr("History") + settingsModel.translationTrigger; font.bold: true } + + rightTitleBarAction: TitleBarAction { + id: rightAction + + states: [ + State { + name: "none" + when: leftAction.state === "edit" + PropertyChanges { target: rightAction; text: "" } + }, + State { + name: "deleteAll" + when: historyListView.editMode + PropertyChanges { target: rightAction; text: qsTr("Delete all") + settingsModel.translationTrigger } + } + ] + onClicked: { + if (historyListView.editMode){ + deleteHistoryConfirmationPopup.setValues("ALL_HISTORY", qsTr("Please confirm that you want to delete your complete history.")) + deleteHistoryConfirmationPopup.open() + } + historyListView.editMode = false + } + } + + HistoryViewConfirmationPopup { + id: deleteHistoryConfirmationPopup + baseItem: baseItem + } + + Text { + color: Constants.secondary_text + anchors.centerIn: parent + text: qsTr("Currently there are no history entries.") + settingsModel.translationTrigger + wrapMode: Text.WordWrap + font.pixelSize: Constants.normal_font_size + visible: historyListView.count === 0 + } + + ListView { + id: historyListView + anchors.fill: parent + focus: true + spacing: Utils.dp(5) + + property bool editMode: false + + model: historyModel + delegate: HistoryListViewDelegate { + historyModelItem: model + + onClicked: { + detailsHistoryView.historyModelItem = historyModelItem + firePush(detailsHistoryView) + } + + Connections { + target: historyListView + onEditModeChanged: { + reactToEditModeChanged(historyListView.editMode) + } + } + } + } + + HistoryViewDetails { + id: detailsHistoryView + visible: false + } +} diff --git a/resources/qml/Governikus/HistoryView/+ios/+tablet/HistoryView.qml b/resources/qml/Governikus/HistoryView/+ios/+tablet/HistoryView.qml index 25e700f..c194efa 100644 --- a/resources/qml/Governikus/HistoryView/+ios/+tablet/HistoryView.qml +++ b/resources/qml/Governikus/HistoryView/+ios/+tablet/HistoryView.qml @@ -1,30 +1,29 @@ -import QtQml.Models 2.2 -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 +import QtQuick 2.10 +import QtQuick.Controls 2.3 import Governikus.Global 1.0 import Governikus.TitleBar 1.0 -import Governikus.Provider 1.0 +import Governikus.View 1.0 SectionPage { + id: baseItem + leftTitleBarAction: TitleBarAction { id: leftAction state: "edit" states: [ - State { name: "edit"; when: !historyListView.selectable }, - State { name: "cancel"; when: historyListView.selectable } + State { name: "edit"; when: !historyListView.editMode }, + State { name: "cancel"; when: historyListView.editMode } ] onClicked: { if (state === "edit") { state = "cancel" - historyListView.selectable = true + historyListView.editMode = true } else { state = "edit" - historyListView.selectable = false - historyListView.cancelDeletion() + historyListView.editMode = false } } } @@ -37,28 +36,27 @@ SectionPage { states: [ State { name: "none" - when: leftAction.state == "edit" + when: leftAction.state === "edit" PropertyChanges { target: rightAction; text: "" } }, - State { - name: "delete" - when: historyListView.selectable && historyListView.selectedIndices.length !== 0 - PropertyChanges { target: rightAction; text: qsTr("Delete") + settingsModel.translationTrigger } - }, State { name: "deleteAll" - when: historyListView.selectable && historyListView.selectedIndices.length === 0 + when: historyListView.editMode PropertyChanges { target: rightAction; text: qsTr("Delete all") + settingsModel.translationTrigger } } ] onClicked: { - historyListView.performDeletion() - historyListView.selectable = false + if (historyListView.editMode){ + deleteHistoryConfirmationPopup.setValues("ALL_HISTORY", qsTr("Please confirm that you want to delete your complete history.")) + deleteHistoryConfirmationPopup.open() + } + historyListView.editMode = false } } - HistoryViewBackground { - visible: historyListView.count !== 0 + HistoryViewConfirmationPopup { + id: deleteHistoryConfirmationPopup + baseItem: baseItem } Text { @@ -70,28 +68,32 @@ SectionPage { visible: historyListView.count === 0 } - HistoryListView { + ListView { id: historyListView anchors.fill: parent + focus: true + spacing: Utils.dp(5) - listViewModel: historyModel + property bool editMode: false + + model: historyModel delegate: HistoryListViewDelegate { - id: historyDelegate - showDetail: false - } + historyModelItem: model - onSelectedIndicesChanged: { - if (!historyListView.selectable) { - leftAction.state = historyListView.selectedIndices.length !== 0 ? "cancel" : "edit" + onClicked: { + detailsHistoryView.historyModelItem = historyModelItem + firePush(detailsHistoryView) + } + + Connections { + target: historyListView + onEditModeChanged: { + reactToEditModeChanged(historyListView.editMode) + } } } } - ProviderDetailView_tablet { - id: providerHistoryView - visible: false - } - HistoryViewDetails { id: detailsHistoryView visible: false diff --git a/resources/qml/Governikus/HistoryView/+ios/CustomSwipeBar.qml b/resources/qml/Governikus/HistoryView/+ios/CustomSwipeBar.qml deleted file mode 100644 index b961d2e..0000000 --- a/resources/qml/Governikus/HistoryView/+ios/CustomSwipeBar.qml +++ /dev/null @@ -1,58 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Layouts 1.2 - -import Governikus.Global 1.0 - -Rectangle { - id: rootItem - property int currentIndex - - anchors.horizontalCenter: parent.horizontalCenter - width: descriptionTab.width + contactTab.width + 2 * border.width - height: descriptionTab.height + 2 * border.width - - border.color: Constants.blue - border.width: Utils.dp(1) - radius: Utils.dp(3) - clip: true - - Row { - id: row - readonly property int maxContentWidth: Math.max(descriptionText.contentWidth, contactText.contentWidth) - - anchors.centerIn: parent - Rectangle { - id: descriptionTab - width: row.maxContentWidth + Utils.dp(6) - height: descriptionText.contentHeight + Utils.dp(6) - color: rootItem.currentIndex === 0 ? Constants.blue : "white" - Text { - id: descriptionText - anchors.centerIn: parent - color: rootItem.currentIndex === 0 ? "white" : Constants.blue - text: qsTr("Contact") + settingsModel.translationTrigger - } - MouseArea { - anchors.fill: parent - onClicked: rootItem.currentIndex = 0 - } - } - Rectangle { - id: contactTab - width: row.maxContentWidth + Utils.dp(6) - height: contactText.contentHeight + Utils.dp(6) - color: rootItem.currentIndex === 1 ? Constants.blue : "white" - Text { - id: contactText - anchors.centerIn: parent - color: rootItem.currentIndex === 1 ? "white" : Constants.blue - text: qsTr("History") + settingsModel.translationTrigger - } - MouseArea { - anchors.fill: parent - onClicked: rootItem.currentIndex = 1 - } - } - } -} diff --git a/resources/qml/Governikus/HistoryView/+ios/HistoryDetails.qml b/resources/qml/Governikus/HistoryView/+ios/HistoryDetails.qml index 3e0c923..aa43a18 100644 --- a/resources/qml/Governikus/HistoryView/+ios/HistoryDetails.qml +++ b/resources/qml/Governikus/HistoryView/+ios/HistoryDetails.qml @@ -1,4 +1,4 @@ -import QtQuick 2.5 +import QtQuick 2.10 import Governikus.Global 1.0 diff --git a/resources/qml/Governikus/HistoryView/+ios/HistoryItemImage.qml b/resources/qml/Governikus/HistoryView/+ios/HistoryItemImage.qml index 77de8c6..ed59fe0 100644 --- a/resources/qml/Governikus/HistoryView/+ios/HistoryItemImage.qml +++ b/resources/qml/Governikus/HistoryView/+ios/HistoryItemImage.qml @@ -1,4 +1,4 @@ -import QtQuick 2.5 +import QtQuick 2.10 import Governikus.Global 1.0 diff --git a/resources/qml/Governikus/HistoryView/+ios/HistoryListViewDelegate.qml b/resources/qml/Governikus/HistoryView/+ios/HistoryListViewDelegate.qml index e84ff81..e02f246 100644 --- a/resources/qml/Governikus/HistoryView/+ios/HistoryListViewDelegate.qml +++ b/resources/qml/Governikus/HistoryView/+ios/HistoryListViewDelegate.qml @@ -1,21 +1,133 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 +import QtQuick 2.10 +import QtQuick.Controls 2.3 import Governikus.Global 1.0 Item { - property bool showDetail: false - - property var listModel - id: baseItem + property var historyModelItem: historyModel - HistoryListViewDelegateContent { - showDetail: baseItem.showDetail - listModel: baseItem.listModel + height: swipeComponent.height + width: parent.width - width: parent.width - height: parent.height + signal clicked() + + function reactToEditModeChanged(pNewValue){ + if (pNewValue){ + swipeComponent.swipe.open(SwipeDelegate.Right) + } else { + swipeComponent.swipe.close() + } + } + + SwipeDelegate { + id: swipeComponent + height: Constants.history_section_height + anchors.left: parent.left + anchors.right: parent.right + + onClicked: { + baseItem.clicked() + } + + contentItem: Rectangle { + id: background + color: "white" + anchors.top: parent.top + anchors.bottom: parent.bottom + + HistoryItemImage { + id: categoryImage + imageUrl: providerIcon + visible: true + } + + Rectangle { + id: purposeObject + anchors.verticalCenter: parent.verticalCenter + anchors.left: categoryImage.right + anchors.leftMargin: Utils.dp(15) + anchors.right: parent.right + + Column { + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.right: parent.right + + spacing: Constants.history_delegate_spacing + + Text { + id: dateTimeText + + verticalAlignment: Text.AlignVCenter + font.pixelSize: Constants.label_font_size + font.capitalization: Font.AllUppercase + color: Constants.blue + text: (Utils.isToday(dateTime) ? qsTr("today") : + Utils.isYesterday(dateTime) ? qsTr("yesterday") : + Utils.isThisWeek(dateTime) ? dateTime.toLocaleString(Qt.locale(), qsTr("dddd")) : + dateTime.toLocaleString(Qt.locale(), qsTr("dd.MM.yyyy")) + ) + settingsModel.translationTrigger + } + Text { + id: subjectText + + anchors.left: parent.left + anchors.right: parent.right + verticalAlignment: Text.AlignVCenter + font.pixelSize: Constants.label_font_size + wrapMode: Text.WordWrap + text: subject + } + Text { + id: purposeText + + anchors.left: parent.left + anchors.right: parent.right + verticalAlignment: Text.AlignVCenter + font.pixelSize: Constants.small_font_size + color: Constants.history_delegate_address_color + wrapMode: Text.WordWrap + text: !!purpose ? historyModelItem.purpose : qsTr("Tap for more details") + settingsModel.translationTrigger + } + } + } + + Image { + height: Utils.dp(35) + source: "qrc:///images/arrowRight.svg" + anchors.right: parent.right + anchors.verticalCenter: purposeObject.verticalCenter + anchors.rightMargin: Utils.dp(2) + fillMode: Image.PreserveAspectFit + } + } + + swipe.right: Rectangle { + id: confirmDeletionRect + height: parent.height + width: parent.width / 4 + anchors.right: parent.right + anchors.top: parent.top + + color: Constants.red + border.width: Constants.groupbox_spacing + border.color: Constants.white + + MouseArea { + anchors.fill: parent + onClicked: { + historyModel.removeRows(index, 1) + } + } + + Image { + anchors.fill: parent + anchors.margins: Utils.dp(20) + source: "qrc:///images/trash_icon_white.svg" + fillMode: Image.PreserveAspectFit + } + } } } + diff --git a/resources/qml/Governikus/HistoryView/+ios/HistoryView.qml b/resources/qml/Governikus/HistoryView/+ios/HistoryView.qml deleted file mode 100644 index ed4f32d..0000000 --- a/resources/qml/Governikus/HistoryView/+ios/HistoryView.qml +++ /dev/null @@ -1,98 +0,0 @@ -import QtQml.Models 2.2 -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 - -import Governikus.Global 1.0 -import Governikus.TitleBar 1.0 - -SectionPage { - leftTitleBarAction: TitleBarAction { - id: leftAction - - state: "edit" - states: [ - State { name: "edit"; when: !historyListView.selectable }, - State { name: "cancel"; when: historyListView.selectable } - ] - onClicked: { - if (state === "edit") { - state = "cancel" - historyListView.selectable = true - } - else { - state = "edit" - historyListView.selectable = false - historyListView.cancelDeletion() - } - } - } - - headerTitleBarAction: Text { text: qsTr("History") + settingsModel.translationTrigger; font.bold: true } - - rightTitleBarAction: TitleBarAction { - id: rightAction - - states: [ - State { - name: "none" - when: leftAction.state == "edit" - PropertyChanges { target: rightAction; text: "" } - }, - State { - name: "delete" - when: historyListView.selectable && historyListView.selectedIndices.length !== 0 - PropertyChanges { target: rightAction; text: qsTr("Delete") + settingsModel.translationTrigger } - }, - State { - name: "deleteAll" - when: historyListView.selectable && historyListView.selectedIndices.length === 0 - PropertyChanges { target: rightAction; text: qsTr("Delete all") + settingsModel.translationTrigger } - } - ] - onClicked: { - historyListView.performDeletion() - historyListView.selectable = false - } - } - - HistoryViewBackground { - visible: historyListView.count !== 0 - } - - Text { - color: Constants.secondary_text - anchors.centerIn: parent - text: qsTr("Currently there are no history entries.") + settingsModel.translationTrigger - wrapMode: Text.WordWrap - font.pixelSize: Constants.normal_font_size - visible: historyListView.count === 0 - } - - HistoryListView { - id: historyListView - anchors.fill: parent - - listViewModel: historyModel - delegate: HistoryListViewDelegate { - id: historyDelegate - showDetail: false - } - - onSelectedIndicesChanged: { - if (!historyListView.selectable) { - leftAction.state = historyListView.selectedIndices.length !== 0 ? "cancel" : "edit" - } - } - } - - HistoryViewPage { - id: providerHistoryView - visible: false - } - - HistoryViewDetails { - id: detailsHistoryView - visible: false - } -} diff --git a/resources/qml/Governikus/HistoryView/ContextMenuEntry.qml b/resources/qml/Governikus/HistoryView/ContextMenuEntry.qml deleted file mode 100644 index 0a0a9f6..0000000 --- a/resources/qml/Governikus/HistoryView/ContextMenuEntry.qml +++ /dev/null @@ -1,58 +0,0 @@ -import QtQuick 2.5 -import QtGraphicalEffects 1.0 - -import Governikus.Global 1.0 - -MouseArea { - id: mouseArea - property alias text: textItem.text - property color buttonColor: Constants.white - readonly property int touchPadding: Utils.dp(3); - - height: textItem.height + 2 * touchPadding - width: Math.max(textItem.implicitWidth + 2 * touchPadding, parent.width) - - state: "normal" - states: [ - State { name: "normal"; when: !mouseArea.pressed - PropertyChanges { target: darkLayer; width: 0 } - }, - State { name: "pressed"; when: mouseArea.pressed - PropertyChanges { target: darkLayer; width: 2 * rect.width } - } - ] - transitions: [ - Transition { - from: "normal"; to: "pressed"; reversible: false - PropertyAnimation { target: darkLayer; property: "width"} - } - ] - - Rectangle { - id: rect - anchors.fill: parent - color: buttonColor - - Item { - anchors.fill: parent - clip: true - Rectangle { - id: darkLayer - x: mouseArea.containsMouse ? mouseArea.mouseX - width * 0.5 : 0 - height: parent.height - color: Constants.black - opacity: 0.2 - } - } - } - - Text { - id: textItem - color: Constants.secondary_text - anchors.left: rect.left - anchors.leftMargin: touchPadding - anchors.verticalCenter: rect.verticalCenter - font.pixelSize: Constants.titlebar_font_size - } -} - diff --git a/resources/qml/Governikus/HistoryView/CustomSwipeBar.qml b/resources/qml/Governikus/HistoryView/CustomSwipeBar.qml deleted file mode 100644 index 4b11736..0000000 --- a/resources/qml/Governikus/HistoryView/CustomSwipeBar.qml +++ /dev/null @@ -1,3 +0,0 @@ -Item { - id: dummy -} diff --git a/resources/qml/Governikus/HistoryView/HistoryContextMenu.qml b/resources/qml/Governikus/HistoryView/HistoryContextMenu.qml deleted file mode 100644 index 4b50422..0000000 --- a/resources/qml/Governikus/HistoryView/HistoryContextMenu.qml +++ /dev/null @@ -1,103 +0,0 @@ -import QtQuick 2.7 -import QtQuick.Controls 2.2 - -import Governikus.Global 1.0 - -MouseArea { - property var historyReminderPopup: HistoryViewConfirmationPopup { } - readonly property alias contentWidth: contextMenuButton.width - - id: contextMenuButton - height: Constants.titlebar_height - width: height - anchors.right: parent ? parent.right : undefined - anchors.top: parent ? parent.top : undefined - anchors.verticalCenter: parent ? parent.verticalCenter : undefined - - Image { - id: contextMenuIcon - height: Constants.titlebar_height - 2 * Constants.titlebar_padding - width: height - anchors.topMargin: Constants.titlebar_padding - anchors.right: parent.right - anchors.bottomMargin: Constants.titlebar_padding - source: "qrc:///images/android/navigation/versionsinformation.svg" - anchors.verticalCenter: parent.verticalCenter - } - - onClicked: contextMenu.visible ? contextMenu.close() : contextMenu.open() - - Popup { - id: contextMenu - x: -(width - contextMenuButton.width) - y: Constants.titlebar_height + Constants.titlebar_padding - closePolicy: Popup.CloseOnPressOutsideParent | Popup.CloseOnEscape - height: contentColumn.height + Constants.titlebar_padding - - Column { - id: contentColumn - width: childrenRect.width - spacing: Constants.titlebar_padding - - ContextMenuEntry { - text: (settingsModel.historyEnabled ? qsTr("Disable history") : qsTr("Enable history")) + settingsModel.translationTrigger - onClicked: { - settingsModel.historyEnabled = !settingsModel.historyEnabled - qmlExtension.showFeedback((settingsModel.historyEnabled ? qsTr("History enabled") : qsTr("History disabled")) + settingsModel.translationTrigger) - contextMenu.close() - } - } - - Rectangle { - width: parent.width - color: Constants.grey - height: Utils.dp(1) - } - - ContextMenuEntry { - text: qsTr("Delete all") + settingsModel.translationTrigger - onClicked: { - historyReminderPopup.setValues("ALL_HISTORY", qsTr("Please confirm that you want to delete your complete history.")) - historyReminderPopup.open() - contextMenu.close() - } - } - - ContextMenuEntry { - text: qsTr("Delete last 4 weeks") + settingsModel.translationTrigger - onClicked: { - historyReminderPopup.setValues("LAST_FOUR_WEEKS", qsTr("Please confirm that you want to delete your history from the last four weeks.")) - historyReminderPopup.open() - contextMenu.close() - } - } - - ContextMenuEntry { - text: qsTr("Delete last week") + settingsModel.translationTrigger - onClicked: { - historyReminderPopup.setValues("PAST_WEEK", qsTr("Please confirm that you want to delete your history from the last week.")) - historyReminderPopup.open() - contextMenu.close() - } - } - - ContextMenuEntry { - text: qsTr("Delete last day") + settingsModel.translationTrigger - onClicked: { - historyReminderPopup.setValues("PAST_DAY", qsTr("Please confirm that you want to delete your history from the last day.")) - historyReminderPopup.open() - contextMenu.close() - } - } - - ContextMenuEntry { - text: qsTr("Delete last hour") + settingsModel.translationTrigger - onClicked: { - historyReminderPopup.setValues("PAST_HOUR", qsTr("Please confirm that you want to delete your history from the last hour.")) - historyReminderPopup.open() - contextMenu.close() - } - } - } - } -} diff --git a/resources/qml/Governikus/HistoryView/HistoryDetails.qml b/resources/qml/Governikus/HistoryView/HistoryDetails.qml deleted file mode 100644 index 4b11736..0000000 --- a/resources/qml/Governikus/HistoryView/HistoryDetails.qml +++ /dev/null @@ -1,3 +0,0 @@ -Item { - id: dummy -} diff --git a/resources/qml/Governikus/HistoryView/HistoryItemImage.qml b/resources/qml/Governikus/HistoryView/HistoryItemImage.qml deleted file mode 100644 index 4b11736..0000000 --- a/resources/qml/Governikus/HistoryView/HistoryItemImage.qml +++ /dev/null @@ -1,3 +0,0 @@ -Item { - id: dummy -} diff --git a/resources/qml/Governikus/HistoryView/HistoryListView.qml b/resources/qml/Governikus/HistoryView/HistoryListView.qml deleted file mode 100644 index 20c68e8..0000000 --- a/resources/qml/Governikus/HistoryView/HistoryListView.qml +++ /dev/null @@ -1,221 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 - -import Governikus.Global 1.0 - -Item { - id: baseItem - - property alias listViewModel: listView.model - property alias interactive: listView.interactive - readonly property int count: listView.count - property bool deletable: true - property bool selectable: false - property var selectedIndices: [] - property Component delegate - - readonly property int contentHeight: listView.contentHeight - - function requestDeletion(index) { - selectedIndices.push(index) - selectedIndices = selectedIndices //to have the change signal emitted - } - - - function cancelDeletion(cancelIndex) { - if (arguments.length === 1) { - var reducedSelection = []; - selectedIndices.map(function (index) { - if (index !== cancelIndex) { - reducedSelection.push(index) - } - }) - selectedIndices = reducedSelection - } - else { - selectedIndices = [] - } - } - - - function performDeletion() { - if (selectedIndices.length === 0) { - listViewModel.removeRows(0, historyModel.rowCount()) - } - else { - // sort in descending order, so that the indices don't change after partial deletion - selectedIndices.sort(function(a,b){return b-a}) - selectedIndices.map(function (index) { - listViewModel.removeRows(index, 1) - }) - selectedIndices = [] //to have the change signal emitted - } - } - - - ListView { - id: listView - anchors.fill: parent - focus: true - spacing: Utils.dp(5) - - delegate: Item { - id: delegateItem - width: parent.width - height: delegateLoader.height - - Connections { - target: baseItem - onSelectedIndicesChanged: { - if (baseItem.selectedIndices.indexOf(model.index) === -1) { - flickable.state = "hideDeleteConfirmation" - checkBox.checked = false - } - } - } - - Rectangle { - id: confirmDeletionRect - height: parent.height - width: parent.width / 4 - anchors.right: parent.right - anchors.rightMargin: Utils.dp(10) - anchors.top: parent.top - anchors.topMargin: Utils.dp(8) - anchors.bottom: parent.bottom - anchors.bottomMargin: Utils.dp(8) - - color: "red" - - Text { - anchors.centerIn: parent - text: qsTr("Delete") + settingsModel.translationTrigger - color: "white" - } - } - - Flickable { - id: flickable - anchors.fill: parent - contentWidth: parent.width + confirmDeletionRect.width - contentHeight: parent.height - flickableDirection: Flickable.HorizontalFlick - interactive: baseItem.deletable && !baseItem.selectable && Constants.use_history_list_delete_area - - Item { - id: delegateContent - width: delegateItem.width - height: delegateItem.height - - CheckBox { - id: checkBox - width: Utils.dp(40) - height: parent.height - - style: IosCheckBoxStyle {} - onClicked: checked ? requestDeletion(model.index) : cancelDeletion(model.index) - } - Loader { - id: delegateLoader - anchors.left: checkBox.right - anchors.right: parent.right - height: Constants.history_section_height - sourceComponent: baseItem.delegate - property var historyModelItem: model - } - - states: [ - State { - name: "notSelectable" - when: !baseItem.selectable - PropertyChanges { target: checkBox; opacity: 0 } - AnchorChanges { target: checkBox; anchors.left: undefined} - AnchorChanges { target: checkBox; anchors.right: delegateContent.left} - }, - State { - name: "selectable" - when: baseItem.selectable - PropertyChanges { target: checkBox; opacity: 1 } - AnchorChanges { target: checkBox; anchors.left: delegateContent.left} - AnchorChanges { target: checkBox; anchors.right: undefined} - } - ] - state: "notSelectable" - - transitions: [ - Transition { - from: "notSelectable" - to: "selectable" - reversible: true - animations: - [ - AnchorAnimation { duration: 200 }, - PropertyAnimation { target: checkBox; properties: "opacity"; duration: 200 } - ] - } - ] - } - - onMovementStarted: { - delegateItem.ListView.view.currentIndex = index - state = "moving" - requestDeletion(model.index) - } - onMovementEnded: { - var ratio = contentX / confirmDeletionRect.width - if (ratio > 0.4) { - state = "showDeleteConfirmation" - } - else { - state = "hideDeleteConfirmation" - cancelDeletion(model.index) - } - } - - state: "hideDeleteConfirmation" - states: [ - State { - name: "moving" - }, - State { - name: "showDeleteConfirmation" - PropertyChanges { target: flickable; contentX: confirmDeletionRect.width } - PropertyChanges { target: cancelDeletionMouseArea; enabled: true } - PropertyChanges { target: confirmDeletionMouseArea; enabled: true } - }, - State { - name: "hideDeleteConfirmation" - PropertyChanges { target: flickable; contentX: 0 } - PropertyChanges { target: cancelDeletionMouseArea; enabled: false } - PropertyChanges { target: confirmDeletionMouseArea; enabled: false } - } - ] - Behavior on contentX { - NumberAnimation { duration: 500; easing.type: Easing.OutQuart } - } - } - } - - removeDisplaced: Transition { - NumberAnimation { properties: "y"; duration: 500; easing.type: Easing.OutQuart } - } - } - - MouseArea { - id: cancelDeletionMouseArea - enabled: false - anchors.fill: parent - onClicked: cancelDeletion() - } - - MouseArea { - id: confirmDeletionMouseArea - enabled: false - width: listView.currentItem ? listView.currentItem.width / 4 : 0 - height: listView.currentItem ? listView.currentItem.height : 0 - x: listView.currentItem ? listView.currentItem.width - width : 0 - y: listView.currentItem ? listView.currentItem.y - listView.contentY : 0 - onClicked: performDeletion() - } -} diff --git a/resources/qml/Governikus/HistoryView/HistoryListViewDelegate.qml b/resources/qml/Governikus/HistoryView/HistoryListViewDelegate.qml deleted file mode 100644 index 4b11736..0000000 --- a/resources/qml/Governikus/HistoryView/HistoryListViewDelegate.qml +++ /dev/null @@ -1,3 +0,0 @@ -Item { - id: dummy -} diff --git a/resources/qml/Governikus/HistoryView/HistoryListViewDelegateContent.qml b/resources/qml/Governikus/HistoryView/HistoryListViewDelegateContent.qml index e18bd8d..214354e 100644 --- a/resources/qml/Governikus/HistoryView/HistoryListViewDelegateContent.qml +++ b/resources/qml/Governikus/HistoryView/HistoryListViewDelegateContent.qml @@ -1,6 +1,4 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 +import QtQuick 2.10 import Governikus.Global 1.0 @@ -95,7 +93,7 @@ Item { Image { height: Utils.dp(35) - source: "qrc:///images/android/arrowRight.svg" + source: "qrc:///images/arrowRight.svg" anchors.right: parent.right anchors.verticalCenter: purposeObject.verticalCenter anchors.rightMargin: Utils.dp(2) @@ -104,7 +102,10 @@ Item { MouseArea { anchors.fill: parent - onClicked: firePush(detailsHistoryView, { historyModelItem: historyModelItem }) + onClicked: { + detailsHistoryView.historyModelItem = historyModelItem + firePush(detailsHistoryView) + } } MouseArea { @@ -114,7 +115,7 @@ Item { anchors.topMargin: Utils.dp(5) height: parent.height * 0.3 width: height - visible: PlatformConstants.is_layout_ios ? false : true + visible: Constants.is_layout_ios ? false : true onClicked: { if (typeof(listModel) === "object") { diff --git a/resources/qml/Governikus/HistoryView/HistoryView.qml b/resources/qml/Governikus/HistoryView/HistoryView.qml deleted file mode 100644 index 4b11736..0000000 --- a/resources/qml/Governikus/HistoryView/HistoryView.qml +++ /dev/null @@ -1,3 +0,0 @@ -Item { - id: dummy -} diff --git a/resources/qml/Governikus/HistoryView/HistoryViewBackground.qml b/resources/qml/Governikus/HistoryView/HistoryViewBackground.qml deleted file mode 100644 index 8c98b2d..0000000 --- a/resources/qml/Governikus/HistoryView/HistoryViewBackground.qml +++ /dev/null @@ -1,15 +0,0 @@ -import QtQuick 2.5 - -import Governikus.Global 1.0 - -Rectangle { - id: redLine - height: parent.height - width: Utils.dp(2) - - anchors.left: parent.left - anchors.leftMargin: Utils.dp(30) - - color: "red" - opacity: 0.05 -} diff --git a/resources/qml/Governikus/HistoryView/HistoryViewConfirmationPopup.qml b/resources/qml/Governikus/HistoryView/HistoryViewConfirmationPopup.qml index 73b96b7..c779d11 100644 --- a/resources/qml/Governikus/HistoryView/HistoryViewConfirmationPopup.qml +++ b/resources/qml/Governikus/HistoryView/HistoryViewConfirmationPopup.qml @@ -1,5 +1,5 @@ -import QtQuick 2.7 -import QtQuick.Controls 2.2 +import QtQuick 2.10 +import QtQuick.Controls 2.3 import Governikus.Global 1.0 @@ -81,7 +81,7 @@ Popup { } onClicked: { - var removedItems = settingsModel.removeHistory(deleteHistoryConfirmationDialog.timePeriod); + var removedItems = settingsModel.removeHistory(popupObject.timePeriod); qmlExtension.showFeedback(qsTr("Removed %1 entries from the history.").arg(removedItems)) popupObject.close() } diff --git a/resources/qml/Governikus/HistoryView/HistoryViewDetails.qml b/resources/qml/Governikus/HistoryView/HistoryViewDetails.qml index 886a819..08a75ae 100644 --- a/resources/qml/Governikus/HistoryView/HistoryViewDetails.qml +++ b/resources/qml/Governikus/HistoryView/HistoryViewDetails.qml @@ -1,9 +1,10 @@ -import QtQuick 2.7 -import QtQuick.Controls 2.0 +import QtQuick 2.10 +import QtQuick.Controls 2.3 import QtQuick.Layouts 1.2 import Governikus.Global 1.0 import Governikus.TitleBar 1.0 +import Governikus.View 1.0 SectionPage { id: root diff --git a/resources/qml/Governikus/HistoryView/HistoryViewPage.qml b/resources/qml/Governikus/HistoryView/HistoryViewPage.qml deleted file mode 100644 index 468b25c..0000000 --- a/resources/qml/Governikus/HistoryView/HistoryViewPage.qml +++ /dev/null @@ -1,84 +0,0 @@ -import QtQuick 2.6 -import QtQuick.Layouts 1.2 -import QtQuick.Controls 2.0 - -import Governikus.Global 1.0 -import Governikus.Provider 1.0 -import Governikus.TitleBar 1.0 - -SectionPage { - id: baseItem - property var historyModelItem - readonly property real headerHeight: Utils.dp(200) - ProviderModelItem { - id: provider - modelItem: baseItem.historyModelItem - } - - leftTitleBarAction: TitleBarAction { state: "back"; onClicked: firePop() } - headerTitleBarAction: TitleBarAction { text: historyModelItem ? historyModelItem.subject : ""; font.bold: true } - titleBarColor: !!provider.category ? Category.displayColor(provider.category) : "" - - - header: ProviderHeader { - id: providerHeader - width: baseItem.width - selectedProvider: provider - } - - content: Item { - height: swipeBar.height + swipeViewBackground.height + Constants.component_spacing - width: baseItem.width - - CustomSwipeBar { - id: swipeBar - currentIndex: swipeView.currentIndex - anchors.top: parent.top - anchors.topMargin: Utils.dp(20) - } - - Rectangle { - id: swipeViewBackground - anchors.top: swipeBar.bottom - anchors.horizontalCenter: swipeBar.horizontalCenter - height: swipeView.height + 2 * Constants.component_spacing - width: parent.width - - SwipeView { - id: swipeView - height: Math.max(providerInfo.contentHeight, detailsHistoryListView.contentHeight) - anchors.margins: Constants.component_spacing - anchors.left: parent.left - anchors.top: parent.top - anchors.right: parent.right - - currentIndex: swipeBar.currentIndex - clip: true - - ProviderContactTab { - id: providerInfo - contactModel: provider.contactModel - } - - HistoryListView { - id: detailsHistoryListView - interactive: false - deletable: false - - onVisibleChanged: { - if (visible) { - historyModel.filter.setFilterFixedString(historyModelItem.subject) - } - } - - listViewModel: historyModel.filter - delegate: HistoryListViewDelegate { - id: detailsHistoryDelegate - showDetail: true - listModel: historyModel.filter - } - } - } - } - } -} diff --git a/resources/qml/Governikus/HistoryView/HistoryViewTitleBarControls.qml b/resources/qml/Governikus/HistoryView/HistoryViewTitleBarControls.qml new file mode 100644 index 0000000..92813e1 --- /dev/null +++ b/resources/qml/Governikus/HistoryView/HistoryViewTitleBarControls.qml @@ -0,0 +1,45 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 + +Item { + id: historyControls + property var deleteHistoryConfirmationPopup: HistoryViewConfirmationPopup { } + anchors.verticalCenter: parent ? parent.verticalCenter : undefined + + GSwitch { + id: enableHistorySwitch + anchors.verticalCenter: parent.verticalCenter + anchors.rightMargin: Utils.dp(20) + anchors.right: deleteEntriesButton.left + + color: Constants.green + initialState: settingsModel.historyEnabled + onSwitched: { + settingsModel.historyEnabled = enableHistorySwitch.isOn + qmlExtension.showFeedback((settingsModel.historyEnabled ? qsTr("History enabled") : qsTr("History disabled")) + settingsModel.translationTrigger) + } + } + + MouseArea { + id: deleteEntriesButton + width: deleteEntriesButtonImage.width + height: deleteEntriesButtonImage.height + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + anchors.rightMargin: Utils.dp(10) + + onClicked: { + deleteHistoryConfirmationPopup.setValues("ALL_HISTORY", qsTr("Please confirm that you want to delete your complete history.")) + deleteHistoryConfirmationPopup.open() + } + + Image { + id: deleteEntriesButtonImage + height: Constants.titlebar_font_size * 1.5 + fillMode: Image.PreserveAspectFit + source: "qrc:///images/trash_icon_white.svg" + } + } + } diff --git a/resources/qml/Governikus/HistoryView/IosCheckBoxStyle.qml b/resources/qml/Governikus/HistoryView/IosCheckBoxStyle.qml deleted file mode 100644 index d873418..0000000 --- a/resources/qml/Governikus/HistoryView/IosCheckBoxStyle.qml +++ /dev/null @@ -1,37 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 - -import Governikus.Global 1.0 - -CheckBoxStyle { - id: style - spacing: 0 - - indicator: Component { - Rectangle { - id: rectangle - implicitWidth: control.width - implicitHeight: control.height - color: Constants.background_color - - Rectangle { - anchors.centerIn: parent - height: Utils.dp(radius * 2 + border.width * 2) - width: height - radius: 8 - border.color: control.activeFocus ? Constants.blue : Constants.grey - border.width: 1 - color: style.control.parent.color ? style.control.parent.color : "white" - - Rectangle { - visible: control.checked - color: Constants.blue - border.color: "#333" - radius: parent.radius - anchors.fill: parent - } - } - } - } -} diff --git a/resources/qml/Governikus/HistoryView/qmldir b/resources/qml/Governikus/HistoryView/qmldir index 47e86b6..f90cead 100644 --- a/resources/qml/Governikus/HistoryView/qmldir +++ b/resources/qml/Governikus/HistoryView/qmldir @@ -1,2 +1,10 @@ module HistoryView + +internal HistoryDetails HistoryDetails.qml +internal HistoryItemImage HistoryItemImage.qml +internal HistoryListViewDelegateContent HistoryListViewDelegateContent.qml +internal HistoryListViewDelegate HistoryListViewDelegate.qml +internal HistoryViewDetails HistoryViewDetails.qml +internal HistoryViewTitleBarControls HistoryViewTitleBarControls.qml + HistoryView 1.0 HistoryView.qml diff --git a/resources/qml/Governikus/IdentifyView/+android/+tablet/IdentifyViewContent.qml b/resources/qml/Governikus/IdentifyView/+android/+tablet/IdentifyViewContent.qml deleted file mode 100644 index a768e3c..0000000 --- a/resources/qml/Governikus/IdentifyView/+android/+tablet/IdentifyViewContent.qml +++ /dev/null @@ -1,159 +0,0 @@ -import QtQuick 2.7 - -import Governikus.Global 1.0 -import Governikus.Provider 1.0 -import Governikus.TitleBar 1.0 - -SectionPage { - id: root - - leftTitleBarAction: TitleBarAction { - state: "cancel" - onClicked: authModel.cancelWorkflow() - } - headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger; font.bold: true } - - content: Column { - width: baseItem.width - padding: Constants.pane_padding - - Column { - width: parent.width - 2 * Constants.pane_padding - spacing: Constants.pane_spacing - - Text { - color: Constants.secondary_text - font.pixelSize: Constants.normal_font_size - width: parent.width - wrapMode: Text.WordWrap - text: qsTr("You are about to identify yourself towards the following service provider:") + settingsModel.translationTrigger - } - - Pane { - - Row { - height: providerEntries.height - width: parent.width - spacing: Constants.pane_spacing - - Item { - height: providerEntries.height - width: (parent.width - Constants.pane_spacing) / 2 - - Column { - id: providerEntries - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - spacing: Constants.pane_spacing - - ProviderInfoSection { - imageSource: "qrc:///images/provider/information.svg" - title: qsTr("Service provider") + settingsModel.translationTrigger - name: certificateDescriptionModel.subjectName - } - ProviderInfoSection { - imageSource: "qrc:///images/provider/purpose.svg" - title: qsTr("Purpose for reading out requested data") + settingsModel.translationTrigger - name: certificateDescriptionModel.purpose - } - } - - MouseArea { - anchors.fill: parent - onClicked: firePush(certificateDescriptionPage, {}) - } - - CertificateDescriptionPage { - id: certificateDescriptionPage - name: certificateDescriptionModel.subjectName - visible: false - } - } - - - Item { - height: parent.height - width: (parent.width - Constants.pane_spacing) / 2 - - GButton { - id: button - iconSource: "qrc:///images/npa.svg" - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter - text: qsTr("Identify now") + settingsModel.translationTrigger - onClicked: { - chatModel.transferAccessRights() - numberModel.continueWorkflow() - } - } - } - } - } - - Text { - color: Constants.secondary_text - font.pixelSize: Constants.normal_font_size - width: parent.width - wrapMode: Text.WordWrap - text: qsTr("The following data will be transferred to the service provider when you enter the PIN:") + settingsModel.translationTrigger - } - - Pane { - Column { - height: childrenRect.height - width: parent.width - spacing: Utils.dp(30) - - Column { - id: transactionInfo - - width: parent.width - visible: !!transactionInfoText.text - - Text { - height: implicitHeight * 1.5 - verticalAlignment: Text.AlignTop - text: qsTr("Transactional information") + settingsModel.translationTrigger - color: Constants.blue - font.pixelSize: Constants.header_font_size - elide: Text.ElideRight - } - - Text { - id: transactionInfoText - color: Constants.secondary_text - - width: parent.width - font.pixelSize: Constants.normal_font_size - text: authModel.transactionInfo - wrapMode: Text.WordWrap - } - } - - Row { - width: parent.width - spacing: Constants.pane_spacing - - DataGroup { - id: requiredData - width: optionalData.visible ? parent.width * 0.63 : parent.width - - title: qsTr("Required Data") + settingsModel.translationTrigger - columns: optionalData.visible ? 2 : 3 - chat: chatModel.required - } - - DataGroup { - id: optionalData - width: parent.width * 0.37 - Constants.pane_spacing - - title: qsTr("Optional Data") + settingsModel.translationTrigger - chat: chatModel.optional - } - } - } - } - } - } -} diff --git a/resources/qml/Governikus/IdentifyView/+android/DataGroup.qml b/resources/qml/Governikus/IdentifyView/+android/DataGroup.qml deleted file mode 100644 index f3f9f62..0000000 --- a/resources/qml/Governikus/IdentifyView/+android/DataGroup.qml +++ /dev/null @@ -1,116 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Layouts 1.2 -import QtQuick.Controls.Styles 1.4 - -import Governikus.Global 1.0 - - -Rectangle { - id: root - property string title; - property int columns: 1 - property var chat - - width: parent.width - height: column.height - visible: repeater.count > 0 - - Column { - id: column - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - - Text { - height: implicitHeight * 1.5 - verticalAlignment: Text.AlignTop - text: title - color: Constants.blue - font.pixelSize: Constants.header_font_size - elide: Text.ElideRight - } - - Rectangle { - width: parent.width - height: Utils.dp(40) - visible: repeater.count < 1 - Text { - id: emptyText - color: Constants.secondary_text - anchors.verticalCenter: parent.verticalCenter - width: parent.width - font.pixelSize: Constants.normal_font_size - text: qsTr("No data requested") + settingsModel.translationTrigger - } - Rectangle { - anchors.top: parent.bottom - anchors.topMargin: -height - height: 1 - width: parent.width - color: Constants.grey - } - } - - Grid { - id: grid - columns: root.columns - columnSpacing: Constants.pane_spacing - width: parent.width - verticalItemAlignment: Grid.AlignBottom - - Repeater { - id: repeater - model: chat - visible: repeater.count > 0 - - Rectangle { - width: (grid.width - ((grid.columns - 1) * grid.columnSpacing)) / grid.columns - height: Utils.dp(40) - color: "white" - - Text { - id: text - color: Constants.secondary_text - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.right: checkBox.left - font.pixelSize: Constants.normal_font_size - text: name - wrapMode: Text.WordWrap - } - - Rectangle { - anchors.top: parent.bottom - anchors.topMargin: -height - height: 1 - width: parent.width - color: Constants.grey - } - - GCheckBox { - id: checkBox - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - visible: optional - checked: selected - } - - MouseArea { - anchors.fill: parent - enabled: optional - onClicked: selected = !selected - Rectangle { - anchors.centerIn: parent - width: root.width - height: parent.height - color: Constants.accent_color - opacity: parent.pressed ? 0.5 : 0 - Behavior on opacity { NumberAnimation { duration: 100 } } - } - } - } - } - } - } -} diff --git a/resources/qml/Governikus/IdentifyView/+android/IdentifyViewContent.qml b/resources/qml/Governikus/IdentifyView/+android/IdentifyViewContent.qml deleted file mode 100644 index 252d563..0000000 --- a/resources/qml/Governikus/IdentifyView/+android/IdentifyViewContent.qml +++ /dev/null @@ -1,139 +0,0 @@ -import QtQuick 2.7 - -import Governikus.Global 1.0 -import Governikus.Provider 1.0 -import Governikus.TitleBar 1.0 - -SectionPage { - id: baseItem - - leftTitleBarAction: TitleBarAction { - state: "cancel" - onClicked: authModel.cancelWorkflow() - } - headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger; font.bold: true } - - content: Column { - width: baseItem.width - padding: Constants.pane_padding - - Column { - width: parent.width - 2 * Constants.pane_padding - spacing: Constants.component_spacing - - Text { - color: Constants.secondary_text - font.pixelSize: Constants.normal_font_size - width: parent.width - wrapMode: Text.WordWrap - text: qsTr("You are about to identify yourself towards the following service provider:") + settingsModel.translationTrigger - } - - Pane { - - Item { - width: parent.width - height: providerEntries.height - - Column { - id: providerEntries - anchors.top: parent.top - anchors.left: parent.left - anchors.right: forwardAction.left - spacing: Constants.pane_spacing - - ProviderInfoSection { - imageSource: "qrc:///images/provider/information.svg" - title: qsTr("Service provider") + settingsModel.translationTrigger - name: certificateDescriptionModel.subjectName - } - ProviderInfoSection { - imageSource: "qrc:///images/provider/purpose.svg" - title: qsTr("Purpose for reading out requested data") + settingsModel.translationTrigger - name: certificateDescriptionModel.purpose - } - } - - Text { - id: forwardAction - anchors.right: parent.right - anchors.verticalCenter: providerEntries.verticalCenter - - text: ">" - font.pixelSize: Utils.sp(22) - color: Constants.grey - } - - MouseArea { - anchors.fill: parent - onClicked: firePush(certificateDescriptionPage, {}) - } - - CertificateDescriptionPage { - id: certificateDescriptionPage - name: certificateDescriptionModel.subjectName - visible: false - } - } - } - - GButton { - iconSource: "qrc:///images/npa.svg" - anchors.horizontalCenter: parent.horizontalCenter - text: qsTr("Identify now") + settingsModel.translationTrigger; - onClicked: { - chatModel.transferAccessRights() - numberModel.continueWorkflow() - } - } - - Text { - color: Constants.secondary_text - font.pixelSize: Constants.normal_font_size - width: parent.width - wrapMode: Text.WordWrap - text: qsTr("The following data will be transferred to the service provider when you enter the PIN:") + settingsModel.translationTrigger - } - - Pane { - width: parent.width - - Column { - id: transactionInfo - - width: parent.width - visible: !!transactionInfoText.text - - Text { - height: implicitHeight * 1.5 - verticalAlignment: Text.AlignTop - text: qsTr("Transactional information") + settingsModel.translationTrigger - color: Constants.blue - font.pixelSize: Constants.header_font_size - elide: Text.ElideRight - } - - Text { - id: transactionInfoText - color: Constants.secondary_text - - width: parent.width - font.pixelSize: Constants.normal_font_size - text: authModel.transactionInfo - wrapMode: Text.WordWrap - } - } - - DataGroup { - title: qsTr("Required Data") + settingsModel.translationTrigger - chat: chatModel.required - } - - DataGroup { - title: qsTr("Optional Data") + settingsModel.translationTrigger - chat: chatModel.optional - } - } - } - } -} diff --git a/resources/qml/Governikus/IdentifyView/+desktop/CertificateDescriptionPage.qml b/resources/qml/Governikus/IdentifyView/+desktop/CertificateDescriptionPage.qml new file mode 100644 index 0000000..b3b47dd --- /dev/null +++ b/resources/qml/Governikus/IdentifyView/+desktop/CertificateDescriptionPage.qml @@ -0,0 +1,45 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 + +SectionPage +{ + signal exit() + + titleBarAction: TitleBarAction { + text: qsTr("Provider Information") + showSettings: false + showHelp: false + } + + Pane { + id: pane + anchors.top: parent.top + anchors.margins: Constants.pane_padding + title: qsTr("Provider Information") + settingsModel.translationTrigger + + Repeater { + id: listView + model: certificateDescriptionModel + + LabeledText { + id: delegate + label: model.label + text: model.text + textFormat: Text.PlainText + width: parent.width + } + } + } + + GButton { + id: button + anchors.right: pane.right + anchors.bottom: pane.bottom + anchors.margins: Constants.pane_padding + text: qsTr("Close") + onClicked: parent.exit() + } +} diff --git a/resources/qml/Governikus/IdentifyView/+desktop/DataGroup.qml b/resources/qml/Governikus/IdentifyView/+desktop/DataGroup.qml new file mode 100644 index 0000000..9e0eda3 --- /dev/null +++ b/resources/qml/Governikus/IdentifyView/+desktop/DataGroup.qml @@ -0,0 +1,103 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 +import Governikus.Type.ApplicationModel 1.0 + + +Column { + id: column + visible: count > 0 + spacing: Constants.pane_spacing + + property alias title: dataTitle.text + property alias columns: grid.columns + property alias chat: repeater.model + readonly property alias count: repeater.count + + PaneTitle { + id: dataTitle + } + + Item { + width: parent.width + height: noDataText * 1.5 + visible: count < 1 + + Text { + id: noDataText + color: Constants.black + anchors.verticalCenter: parent.verticalCenter + width: parent.width + font.pixelSize: Constants.normal_font_size + wrapMode: Text.WordWrap + text: qsTr("No data requested") + settingsModel.translationTrigger + } + + Rectangle { + anchors.bottom: parent.bottom + height: Math.max(ApplicationModel.scaleFactor * 1, 1) + width: parent.width + color: Constants.grey + } + } + + Grid { + id: grid + columnSpacing: Constants.pane_spacing + width: parent.width + verticalItemAlignment: Grid.AlignBottom + + Repeater { + id: repeater + visible: count > 0 + + Item { + width: (grid.width - ((grid.columns - 1) * grid.columnSpacing)) / grid.columns + height: dataText.height * 1.5 + + Text { + id: dataText + color: Constants.black + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: checkBox.visible ? checkBox.width + Constants.pane_spacing : 0 + font.pixelSize: Constants.normal_font_size + text: name + wrapMode: Text.WordWrap + } + + Rectangle { + anchors.bottom: parent.bottom + height: Math.max(ApplicationModel.scaleFactor * 1, 1) + width: parent.width + color: Constants.grey + } + + GCheckBox { + id: checkBox + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + height: parent.height / 2 + width: height + visible: optional + checked: selected + } + + MouseArea { + anchors.fill: parent + enabled: optional + onClicked: selected = !selected + + Rectangle { + anchors.fill: parent + color: Constants.accent_color + opacity: parent.pressed ? 0.5 : 0 + Behavior on opacity { NumberAnimation { duration: 100 } } + } + } + } + } + } +} diff --git a/resources/qml/Governikus/IdentifyView/+desktop/EditRights.qml b/resources/qml/Governikus/IdentifyView/+desktop/EditRights.qml new file mode 100644 index 0000000..f0f6a92 --- /dev/null +++ b/resources/qml/Governikus/IdentifyView/+desktop/EditRights.qml @@ -0,0 +1,181 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 +import Governikus.Provider 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 +import Governikus.Type.AuthModel 1.0 +import Governikus.Type.ApplicationModel 1.0 + + +SectionPage { + id: root + + property bool detailView: false + + function showProviderInformation(show) { + detailView = show + ApplicationWindow.menuBar.updateActions() + } + + Column { + visible: !root.detailView + + anchors.left: parent.left + anchors.right: parent.right + spacing: Constants.pane_spacing + topPadding: providerRect.height + + Rectangle { + id: providerRect + height: providerColumn.height + anchors.left: parent.left + anchors.right: parent.right + color: Constants.white + + Column { + id: providerColumn + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: Constants.pane_padding + topPadding: Constants.pane_padding + bottomPadding: Constants.pane_padding + spacing: Constants.pane_spacing + + Item { + height: providerText.height + width: parent.width + + Image { + id: providerImage + source: "qrc:///images/npa.svg" + sourceSize.height: providerText.height * 4 + anchors.left: parent.left + anchors.bottom: parent.bottom + } + + Text { + id: providerText + anchors.left: providerImage.right + anchors.leftMargin: Constants.pane_spacing + anchors.right: parent.right + color: Constants.black + font.pixelSize: Constants.normal_font_size + wrapMode: Text.WordWrap + text: qsTr("You are about to identify yourself towards the following service provider:") + settingsModel.translationTrigger + } + } + + Item { + height: providerRow.height + width: parent.width + + Row { + id: providerRow + spacing: Constants.component_spacing + + ProviderInfoSection { + image: "qrc:///images/provider/purpose.svg" + title: qsTr("Purpose for reading out requested data") + settingsModel.translationTrigger + name: certificateDescriptionModel.purpose + } + + ProviderInfoSection { + image: "qrc:///images/provider/information.svg" + title: qsTr("Service provider") + settingsModel.translationTrigger + name: certificateDescriptionModel.subjectName + } + } + + GButton { + anchors.right: parent.right + anchors.bottom: parent.bottom + text: qsTr("more...") + settingsModel.translationTrigger + onClicked: showProviderInformation(true) + } + } + } + } + + Text { + color: Constants.black + font.pixelSize: Constants.normal_font_size + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: Constants.pane_padding + wrapMode: Text.WordWrap + text: qsTr("The following data will be transferred to the service provider when you enter the PIN:") + settingsModel.translationTrigger + } + + Pane { + anchors.margins: Constants.pane_padding + + Column { + id: transactionInfo + width: parent.width + visible: !!transactionInfoText.text + spacing: Constants.pane_spacing + + Text { + width: parent.width + text: qsTr("Transactional information") + settingsModel.translationTrigger + color: Constants.blue + font.pixelSize: Constants.pane_title_font_size + wrapMode: Text.WordWrap + } + + Text { + id: transactionInfoText + width: parent.width + text: AuthModel.transactionInfo + color: Constants.black + font.pixelSize: Constants.normal_font_size + wrapMode: Text.WordWrap + } + } + + Row { + spacing: Constants.pane_spacing + + readonly property int columnWidth: (parent.width - 2 * Constants.pane_spacing) / 3 + + DataGroup { + id: requiredData + width: columns * parent.columnWidth + ((columns - 1) * Constants.pane_spacing) + + title: qsTr("Required Data") + settingsModel.translationTrigger + columns: !optionalData.visible ? 3 + : count > optionalData.count ? 2 + : 1 + chat: chatModel.required + } + + DataGroup { + id: optionalData + width: columns * parent.columnWidth + ((columns - 1) * Constants.pane_spacing) + + title: qsTr("Optional Data") + settingsModel.translationTrigger + columns: 3 - (requiredData.visible ? requiredData.columns : 0) + chat: chatModel.optional + } + } + + GButton { + icon.source: "qrc:///images/npa.svg" + anchors.right: parent.right + text: qsTr("Identify now") + settingsModel.translationTrigger + onClicked: { + chatModel.transferAccessRights() + AuthModel.continueWorkflow() + } + } + } + } + + CertificateDescriptionPage { + id: certificateDescriptionPage + visible: root.detailView + onExit: showProviderInformation(false) + } +} diff --git a/resources/qml/Governikus/IdentifyView/+desktop/IdentifyController.qml b/resources/qml/Governikus/IdentifyView/+desktop/IdentifyController.qml new file mode 100644 index 0000000..26b796e --- /dev/null +++ b/resources/qml/Governikus/IdentifyView/+desktop/IdentifyController.qml @@ -0,0 +1,151 @@ +import QtQuick 2.10 + +import Governikus.View 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.AuthModel 1.0 + +SectionPage { + enum WorkflowStates { + Initial, + Reader, + Card, + Update, + Can, + Pin, + Processing + } + + id: controller + readonly property string currentState: AuthModel.currentState + + property bool connectedToCard: false + property int workflowState: 0 + property int workflowProgressValue: 0 + property bool workflowProgressVisible: false + + states: [ + State { + when: AuthModel.currentState === "StateGetTcToken" && !connectivityManager.networkInterfaceActive + StateChangeScript { + script: controller.nextView(IdentifyView.SubViews.Connectivity) + } + }, + State { + when: AuthModel.currentState === "StateGetTcToken" && connectivityManager.networkInterfaceActive + StateChangeScript { + script: { + controller.nextView(IdentifyView.SubViews.Progress) + AuthModel.continueWorkflow() + } + } + } + ] + + Component.onCompleted: if (AuthModel.currentState === "StateProcessing") processStateChange() + + Connections { + target: AuthModel + onFireCurrentStateChanged: processStateChange() + // This is necessary because onCurrentStateChanged is not + // working, when we need to process a state a second time + } + + function showRemoveCardFeedback() { + if (controller.connectedToCard) { + controller.connectedToCard = false + qmlExtension.showFeedback(qsTr("You may now remove your ID card from the device.")) + } + } + + function processStateChange() { + switch (AuthModel.currentState) { + case "": + break; + case "StateGetTcToken": + // enterPinView.state = "INITIAL" + controller.workflowState = IdentifyController.WorkflowStates.Initial + break + case "StateEditAccessRights": + controller.nextView(IdentifyView.SubViews.AccessRights) + AuthModel.setInitialPluginType() + break + case "StateSelectReader": + controller.nextView(IdentifyView.SubViews.Workflow) + setIdentifyWorkflowStateAndContinue(IdentifyController.WorkflowStates.Reader) + break + case "StateConnectCard": + setIdentifyWorkflowStateAndContinue(IdentifyController.WorkflowStates.Card) + break + case "StateHandleRetryCounter": + controller.nextView(IdentifyView.SubViews.Progress) + setIdentifyWorkflowStateAndContinue(IdentifyController.WorkflowStates.Update) + break + case "StateEstablishPaceCan": + setIdentifyWorkflowStateAndRequestInput(IdentifyController.WorkflowStates.Can, "CAN") + break + case "StateEstablishPacePin": + setIdentifyWorkflowStateAndRequestInput(IdentifyController.WorkflowStates.Pin, "PIN") + break + case "StateDidAuthenticateEac1": + controller.workflowProgressVisible = true + controller.workflowProgressValue = 1 + setIdentifyWorkflowStateAndContinue(IdentifyController.WorkflowStates.Processing) + break + case "StateEstablishPacePuk": + AuthModel.cancelWorkflowOnPinBlocked() + break + case "StateDidAuthenticateEac2": + controller.workflowProgressValue = 2 + AuthModel.continueWorkflow() + break + case "StateTransmit": + controller.workflowProgressValue = 3 + AuthModel.continueWorkflow() + break + case "StateCleanUpReaderManager": + controller.connectedToCard = AuthModel.selectedReaderHasCard() && !AuthModel.isError; + AuthModel.continueWorkflow() + break; + case "StateCheckRefreshAddress": + controller.workflowProgressValue = 4 + AuthModel.continueWorkflow() + break + case "StateWriteHistory": + showRemoveCardFeedback() + controller.workflowProgressValue = 5 + AuthModel.continueWorkflow() + break + case "StateShowSelfInfo": + controller.nextView(IdentifyView.SubViews.Data) + break + case "FinalState": + if (AuthModel.error) { + showRemoveCardFeedback() + controller.nextView(IdentifyView.SubViews.Result) + } else { + AuthModel.continueWorkflow() + controller.nextView(IdentifyView.SubViews.ReturnToMain) + } + controller.workflowProgressVisible = false + controller.workflowProgressValue = 0 + break + default: + AuthModel.continueWorkflow() + } + } + + function setIdentifyWorkflowStateAndContinue(pState) { + controller.workflowState = pState + AuthModel.continueWorkflow() + } + + function setIdentifyWorkflowStateAndRequestInput(pState, pInput) { + controller.workflowState = pState + if (AuthModel.isBasicReader) { + // enterPinView.state = pInput + controller.nextView(IdentifyView.SubViews.Password) + } else { + AuthModel.continueWorkflow() + } + } +} diff --git a/resources/qml/Governikus/IdentifyView/+desktop/IdentifyView.qml b/resources/qml/Governikus/IdentifyView/+desktop/IdentifyView.qml new file mode 100644 index 0000000..c9680fa --- /dev/null +++ b/resources/qml/Governikus/IdentifyView/+desktop/IdentifyView.qml @@ -0,0 +1,166 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.EnterPinView 1.0 +import Governikus.Global 1.0 +import Governikus.TitleBar 1.0 +import Governikus.ProgressView 1.0 +import Governikus.ResultView 1.0 +import Governikus.View 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.AuthModel 1.0 +import Governikus.Type.NumberModel 1.0 + + +SectionPage +{ + enum SubViews { + Connectivity = 1, + Progress, + AccessRights, + Workflow, + Password, + Data, + Result, + ReturnToMain + } + + id: identifyView + + KeyNavigation.tab: visibleChildren.length > 1 ? visibleChildren[1] : navSuccessor + + Accessible.role: Accessible.Grouping + Accessible.name: qsTr("Identify view") + settingsModel.translationTrigger + Accessible.description: qsTr("This is the identify view of the AusweisApp2.") + settingsModel.translationTrigger + + readonly property var tabTarget: visibleChildren.length > 1 ? visibleChildren[1] : identifyView + + property int activeView: 0 + + titleBarAction: TitleBarAction { + text: qsTr("Identify") + showSettings: false + showHelp: false + + onClicked: editRights.showProviderInformation(false) + + customSubAction: CancelAction { + onClicked: { + if (identifyResult.visible) { + AuthModel.continueWorkflow() + identifyView.nextView(SectionPage.Views.Main) + } else { + AuthModel.cancelWorkflow() + } + } + } + } +/* + content: IdentifyViewInfo { + id: identifyViewInfo + width: identifyEditChatView.width + height: identifyEditChatView.height + } +*/ + IdentifyController { + id: identifyController + + onNextView: { + if (pName < 1) { + console.warn("Unknown view requested: " + pName) + return; + } + + if (pName === IdentifyView.SubViews.ReturnToMain) { + identifyView.nextView(SectionPage.Views.Main) + return; + } + + identifyView.activeView = pName + } + } + + EditRights { + id: editRights + visible: identifyView.activeView === IdentifyView.SubViews.AccessRights + } + + SelfAuthenticationData { + visible: identifyView.activeView === IdentifyView.SubViews.Data + onVisibleChanged: ApplicationWindow.menuBar.updateActions() + } +/* + IdentifyWorkflow { + id: identifyWorkflow + visible: false + } + + EnterPinView { + id: enterPinView + leftTitleBarAction: TitleBarAction { state: "cancel"; onClicked: AuthModel.cancelWorkflow() } + headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger } + visible: false + + onPinEntered: { + firePop() + AuthModel.continueWorkflow() + } + } +*/ + + ProgressView { + id: identifyProgressView + + readonly property bool inProgress: identifyController.workflowState !== IdentifyController.WorkflowStates.Initial + + visible: identifyView.activeView === IdentifyView.SubViews.Progress + text: (inProgress ? qsTr("Authentication in progress") : qsTr("Acquiring provider certificate")) + settingsModel.translationTrigger + subText: { + settingsModel.translationTrigger; + if (!visible) { + return "" + } + if (AuthModel.isBasicReader) { + return qsTr("Please wait a moment...") + } + if (!!NumberModel.inputError) { + return NumberModel.inputError + } + if (NumberModel.pinDeactivated) { + return qsTr("The online identification function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the online identification function.") + } + return qsTr("Please wait a moment...") + } + subTextColor: !AuthModel.isBasicReader && (NumberModel.inputError || NumberModel.pinDeactivated) ? "red" : Constants.secondary_text + progressValue: identifyController.workflowProgressValue + progressText: (progressValue === 0 ? "" : + progressValue === 1 ? qsTr("Service provider is being verified") : + progressValue === 2 ? qsTr("Card is being verified") : + progressValue === 3 ? qsTr("Reading data") : + progressValue === 4 ? qsTr("Sending data to service provider") : + progressValue === 5 ? qsTr("Preparing results") : + "") + settingsModel.translationTrigger + progressBarVisible: identifyController.workflowProgressVisible + } + + ProgressView { + id: checkConnectivityView + visible: identifyView.activeView === IdentifyView.SubViews.Connectivity + text: qsTr("No network connectivity") + settingsModel.translationTrigger + subText: qsTr("Please enable the network interface or cancel the workflow.") + settingsModel.translationTrigger + subTextColor: Constants.red + } + + ResultView { + id: identifyResult + navSuccessor: identifyView.navSuccessor + + isError: AuthModel.resultString + text: AuthModel.resultString + onNextView: { + AuthModel.continueWorkflow() + identifyView.nextView(pName) + } + visible: identifyView.activeView === IdentifyView.SubViews.Result + } +} diff --git a/resources/qml/Governikus/IdentifyView/+desktop/SelfAuthenticationData.qml b/resources/qml/Governikus/IdentifyView/+desktop/SelfAuthenticationData.qml new file mode 100644 index 0000000..bf57822 --- /dev/null +++ b/resources/qml/Governikus/IdentifyView/+desktop/SelfAuthenticationData.qml @@ -0,0 +1,73 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 +import Governikus.Type.AuthModel 1.0 + +import Governikus.Type.ApplicationModel 1.0 + + +SectionPage +{ + titleBarAction: TitleBarAction { + text: qsTr("Read data") + showSettings: false + showHelp: false + } + + Row { + id: statusRow + + anchors.top: parent.top + anchors.topMargin: Constants.component_spacing + anchors.horizontalCenter: parent.horizontalCenter + height: parent.height / 4 + spacing: Constants.component_spacing + + StatusIcon { + height: ApplicationModel.scaleFactor * 200 + anchors.verticalCenter: parent.verticalCenter + source: "qrc:///images/status_ok.svg" + } + + Text { + id: successText + anchors.verticalCenter: parent.verticalCenter + text: qsTr("Successfull reading data") + settingsModel.translationTrigger + font.pixelSize: Constants.header_font_size + color: Constants.white + } + } + + Pane { + id: pane + anchors.top: statusRow.bottom + anchors.margins: Constants.pane_padding + title: qsTr("Read data") + settingsModel.translationTrigger + + Grid { + id: grid + width: parent.width + columns: 3 + spacing: Utils.dp(15) + verticalItemAlignment: Grid.AlignTop + Repeater { + model: selfAuthModel + + LabeledText { + label: name + text: value === "" ? "---" : value + width: (pane.width - 2 * Constants.pane_padding - (grid.columns - 1) * grid.spacing) / grid.columns + } + } + } + + GButton { + id: okButton + anchors.right: parent.right + text: qsTr("OK") + settingsModel.translationTrigger + onClicked: AuthModel.continueWorkflow() + } + } +} diff --git a/resources/qml/Governikus/IdentifyView/+ios/+tablet/DataGroup.qml b/resources/qml/Governikus/IdentifyView/+ios/+tablet/DataGroup.qml deleted file mode 100644 index 38cc8d6..0000000 --- a/resources/qml/Governikus/IdentifyView/+ios/+tablet/DataGroup.qml +++ /dev/null @@ -1,116 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Layouts 1.2 -import QtQuick.Controls.Styles 1.4 - -import Governikus.Global 1.0 - - -Rectangle { - id: root - property string title; - property int columns: 1 - property var chat - - width: parent.width - height: column.height - visible: repeater.count > 0 - - Column { - id: column - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - - Text { - height: implicitHeight * 1.5 - verticalAlignment: Text.AlignTop - text: title - color: Constants.blue - font.pixelSize: Constants.header_font_size - elide: Text.ElideRight - } - - Rectangle { - width: parent.width - height: Utils.dp(40) - visible: repeater.count < 1 - Text { - color: Constants.secondary_text - id: emptyText - anchors.verticalCenter: parent.verticalCenter - width: parent.width - font.pixelSize: Constants.normal_font_size - text: qsTr("No data requested") + settingsModel.translationTrigger - } - Rectangle { - anchors.top: parent.bottom - anchors.topMargin: -height - height: 1 - width: parent.width - color: Constants.grey - } - } - - Grid { - id: grid - columns: root.columns - columnSpacing: Constants.pane_spacing - width: parent.width - verticalItemAlignment: Grid.AlignBottom - - Repeater { - id: repeater - model: chat - visible: repeater.count > 0 - - Rectangle { - width: (grid.width - ((grid.columns - 1) * grid.columnSpacing)) / grid.columns - height: Utils.dp(40) - color: "white" - - Text { - id: text - color: Constants.secondary_text - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.right: checkBox.left - font.pixelSize: Constants.normal_font_size - text: name - wrapMode: Text.WordWrap - } - - Rectangle { - anchors.top: parent.bottom - anchors.topMargin: -height - height: 1 - width: parent.width - color: Constants.grey - } - - GCheckBox { - id: checkBox - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - visible: optional - checked: selected - } - - MouseArea { - anchors.fill: parent - enabled: optional - onClicked: selected = !selected - Rectangle { - anchors.centerIn: parent - width: root.width - height: parent.height - color: Constants.accent_color - opacity: parent.pressed ? 0.5 : 0 - Behavior on opacity { NumberAnimation { duration: 100 } } - } - } - } - } - } - } -} diff --git a/resources/qml/Governikus/IdentifyView/+ios/+tablet/IdentifyViewContent.qml b/resources/qml/Governikus/IdentifyView/+ios/+tablet/IdentifyViewContent.qml deleted file mode 100644 index 55fad1b..0000000 --- a/resources/qml/Governikus/IdentifyView/+ios/+tablet/IdentifyViewContent.qml +++ /dev/null @@ -1,155 +0,0 @@ -import QtQuick 2.7 - -import Governikus.Global 1.0 -import Governikus.Provider 1.0 -import Governikus.TitleBar 1.0 - -SectionPage { - id: baseItem - - leftTitleBarAction: TitleBarAction { - state: "cancel" - onClicked: authModel.cancelWorkflow() - } - headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger; font.bold: true } - - content: Column { - width: baseItem.width - padding: Constants.pane_padding - - Column { - width: parent.width - 2 * Constants.pane_padding - spacing: Constants.component_spacing - - Text { - color: Constants.secondary_text - font.pixelSize: Constants.normal_font_size - width: parent.width - wrapMode: Text.WordWrap - text: qsTr("You are about to identify yourself towards the following service provider:") + settingsModel.translationTrigger - } - - Pane { - - Item { - width: parent.width - height: providerEntries.height - - Column { - id: providerEntries - anchors.top: parent.top - anchors.left: parent.left - anchors.right: forwardAction.left - spacing: Constants.pane_spacing - - ProviderInfoSection { - imageSource: "qrc:///images/provider/information.svg" - title: qsTr("Service provider") + settingsModel.translationTrigger - name: certificateDescriptionModel.subjectName - } - ProviderInfoSection { - imageSource: "qrc:///images/provider/purpose.svg" - title: qsTr("Purpose for reading out requested data") + settingsModel.translationTrigger - name: certificateDescriptionModel.purpose - } - } - - Text { - id: forwardAction - anchors.right: parent.right - anchors.verticalCenter: providerEntries.verticalCenter - - text: ">" - font.pixelSize: Utils.sp(22) - color: Constants.grey - } - - MouseArea { - anchors.fill: parent - onClicked: firePush(certificateDescriptionPage, {}) - } - - CertificateDescriptionPage { - id: certificateDescriptionPage - name: certificateDescriptionModel.subjectName - visible: false - } - } - } - - GButton { - iconSource: "qrc:///images/npa.svg" - anchors.horizontalCenter: parent.horizontalCenter - text: qsTr("Identify now") + settingsModel.translationTrigger; - onClicked: { - chatModel.transferAccessRights() - numberModel.continueWorkflow() - } - } - - Text { - color: Constants.secondary_text - font.pixelSize: Constants.normal_font_size - width: parent.width - wrapMode: Text.WordWrap - text: qsTr("The following data will be transferred to the service provider when you enter the PIN:") + settingsModel.translationTrigger - } - - Pane { - Column { - height: childrenRect.height - width: parent.width - spacing: Utils.dp(30) - - Column { - id: transactionInfo - - width: parent.width - visible: !!transactionInfoText.text - - Text { - height: implicitHeight * 1.5 - verticalAlignment: Text.AlignTop - text: qsTr("Transactional information") + settingsModel.translationTrigger - color: Constants.blue - font.pixelSize: Constants.header_font_size - elide: Text.ElideRight - } - - Text { - id: transactionInfoText - color: Constants.secondary_text - - width: parent.width - font.pixelSize: Constants.normal_font_size - text: authModel.transactionInfo - wrapMode: Text.WordWrap - } - } - - Row { - width: parent.width - spacing: Constants.pane_spacing - - DataGroup { - id: requiredData - width: optionalData.visible ? parent.width * 0.63 : parent.width - - title: qsTr("Required Data") + settingsModel.translationTrigger - columns: optionalData.visible ? 2 : 3 - chat: chatModel.required - } - - DataGroup { - id: optionalData - width: parent.width * 0.37 - Constants.pane_spacing - - title: qsTr("Optional Data") + settingsModel.translationTrigger - chat: chatModel.optional - } - } - } - } - } - } -} diff --git a/resources/qml/Governikus/IdentifyView/+ios/DataGroup.qml b/resources/qml/Governikus/IdentifyView/+ios/DataGroup.qml deleted file mode 100644 index 3eae86b..0000000 --- a/resources/qml/Governikus/IdentifyView/+ios/DataGroup.qml +++ /dev/null @@ -1,64 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Layouts 1.2 -import QtQuick.Controls.Styles 1.4 - -import Governikus.Global 1.0 - - -Pane { - property alias chat: repeater.model - - id: pane - spacing: 0 - visible: repeater.count > 0 - - Repeater { - id: repeater - - Rectangle { - width: parent.width - height: Utils.dp(40) - radius: 3 - color: "white" - Text { - id: dataGroup - color: Constants.secondary_text - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - anchors.right: checkBox.left - width: parent.width - font.pixelSize: Constants.normal_font_size - wrapMode: Text.WordWrap - text: name - } - Rectangle { - anchors.top: parent.bottom - anchors.topMargin: -height - height: 1 - anchors.left: dataGroup.left - anchors.right: dataGroup.right - color: Constants.grey - } - GCheckBox { - id: checkBox - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - visible: optional - checked: selected - } - - MouseArea { - anchors.fill: parent - enabled: optional - onClicked: selected = !selected - Rectangle { - anchors.fill: parent - color: Constants.grey - opacity: parent.pressed ? 0.5 : 0 - Behavior on opacity { NumberAnimation { duration: 100 } } - } - } - } - } -} diff --git a/resources/qml/Governikus/IdentifyView/+ios/IdentifyViewContent.qml b/resources/qml/Governikus/IdentifyView/+ios/IdentifyViewContent.qml deleted file mode 100644 index e3233e3..0000000 --- a/resources/qml/Governikus/IdentifyView/+ios/IdentifyViewContent.qml +++ /dev/null @@ -1,126 +0,0 @@ -import QtQuick 2.7 - -import Governikus.Global 1.0 -import Governikus.Provider 1.0 -import Governikus.TitleBar 1.0 - -SectionPage { - id: baseItem - - leftTitleBarAction: TitleBarAction { - state: "cancel" - onClicked: authModel.cancelWorkflow() - } - headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger; font.bold: true } - - content: Column { - width: baseItem.width - padding: Constants.pane_padding - - Column { - width: parent.width - 2 * Constants.pane_padding - - spacing: Constants.component_spacing - - Text { - color: Constants.secondary_text - font.pixelSize: Constants.normal_font_size - width: parent.width - wrapMode: Text.WordWrap - text: qsTr("You are about to identify yourself towards the following service provider:") + settingsModel.translationTrigger - } - - Pane { - - Item { - width: parent.width - height: providerEntries.height - - Column { - id: providerEntries - anchors.top: parent.top - anchors.left: parent.left - anchors.right: forwardAction.left - spacing: Constants.pane_spacing - - ProviderInfoSection { - imageSource: "qrc:///images/provider/information.svg" - title: qsTr("Service provider") + settingsModel.translationTrigger - name: certificateDescriptionModel.subjectName - } - ProviderInfoSection { - imageSource: "qrc:///images/provider/purpose.svg" - title: qsTr("Purpose for reading out requested data") + settingsModel.translationTrigger - name: certificateDescriptionModel.purpose - } - } - - Text { - id: forwardAction - anchors.right: parent.right - anchors.verticalCenter: providerEntries.verticalCenter - - text: ">" - font.pixelSize: Utils.sp(22) - color: Constants.grey - } - - MouseArea { - anchors.fill: parent - onClicked: firePush(certificateDescriptionPage, {}) - } - - CertificateDescriptionPage { - id: certificateDescriptionPage - name: certificateDescriptionModel.subjectName - visible: false - } - } - } - - GButton { - iconSource: "qrc:///images/npa.svg" - anchors.horizontalCenter: parent.horizontalCenter - text: qsTr("Identify now") + settingsModel.translationTrigger; - onClicked: { - chatModel.transferAccessRights() - numberModel.continueWorkflow() - } - } - - Text { - color: Constants.secondary_text - font.pixelSize: Constants.normal_font_size - width: parent.width - wrapMode: Text.WordWrap - text: qsTr("The following data will be transferred to the service provider when you enter the PIN:") + settingsModel.translationTrigger - } - - Pane { - id: transactionInfo - title: qsTr("Transactional information") + settingsModel.translationTrigger - visible: !!transactionInfoText.text - - Text { - id: transactionInfoText - color: Constants.secondary_text - - width: parent.width - font.pixelSize: Constants.normal_font_size - text: authModel.transactionInfo - wrapMode: Text.WordWrap - } - } - - DataGroup { - title: qsTr("Required Data") + settingsModel.translationTrigger - chat: chatModel.required - } - - DataGroup { - title: qsTr("Optional Data") + settingsModel.translationTrigger - chat: chatModel.optional - } - } - } -} diff --git a/resources/qml/Governikus/IdentifyView/+mobile/+android/+phone/EditRights.qml b/resources/qml/Governikus/IdentifyView/+mobile/+android/+phone/EditRights.qml new file mode 100644 index 0000000..39a0479 --- /dev/null +++ b/resources/qml/Governikus/IdentifyView/+mobile/+android/+phone/EditRights.qml @@ -0,0 +1,138 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.Provider 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 +import Governikus.Type.AuthModel 1.0 + +SectionPage { + id: baseItem + + leftTitleBarAction: TitleBarAction { + state: "cancel" + onClicked: AuthModel.cancelWorkflow() + } + headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger; font.bold: true } + + content: Column { + width: baseItem.width + padding: Constants.pane_padding + + Column { + width: parent.width - 2 * Constants.pane_padding + spacing: Constants.component_spacing + + Text { + color: Constants.secondary_text + font.pixelSize: Constants.normal_font_size + width: parent.width + wrapMode: Text.WordWrap + text: qsTr("You are about to identify yourself towards the following service provider:") + settingsModel.translationTrigger + } + + Pane { + + Item { + width: parent.width + height: providerEntries.height + + Column { + id: providerEntries + anchors.top: parent.top + anchors.left: parent.left + anchors.right: forwardAction.left + spacing: Constants.pane_spacing + + ProviderInfoSection { + imageSource: "qrc:///images/provider/information.svg" + title: qsTr("Service provider") + settingsModel.translationTrigger + name: certificateDescriptionModel.subjectName + } + ProviderInfoSection { + imageSource: "qrc:///images/provider/purpose.svg" + title: qsTr("Purpose for reading out requested data") + settingsModel.translationTrigger + name: certificateDescriptionModel.purpose + } + } + + Text { + id: forwardAction + anchors.right: parent.right + anchors.verticalCenter: providerEntries.verticalCenter + + text: ">" + font.pixelSize: Utils.dp(22) + color: Constants.grey + } + + MouseArea { + anchors.fill: parent + onClicked: firePush(certificateDescriptionPage) + } + + CertificateDescriptionPage { + id: certificateDescriptionPage + name: certificateDescriptionModel.subjectName + visible: false + } + } + } + + GButton { + iconSource: "qrc:///images/npa.svg" + anchors.horizontalCenter: parent.horizontalCenter + text: qsTr("Identify now") + settingsModel.translationTrigger; + onClicked: { + chatModel.transferAccessRights() + AuthModel.continueWorkflow() + } + } + + Text { + color: Constants.secondary_text + font.pixelSize: Constants.normal_font_size + width: parent.width + wrapMode: Text.WordWrap + text: qsTr("The following data will be transferred to the service provider when you enter the PIN:") + settingsModel.translationTrigger + } + + Pane { + width: parent.width + + Column { + id: transactionInfo + + width: parent.width + visible: !!transactionInfoText.text + + PaneTitle { + height: implicitHeight * 1.5 + verticalAlignment: Text.AlignTop + text: qsTr("Transactional information") + settingsModel.translationTrigger + } + + Text { + id: transactionInfoText + color: Constants.secondary_text + + width: parent.width + font.pixelSize: Constants.normal_font_size + text: AuthModel.transactionInfo + wrapMode: Text.WordWrap + } + } + + DataGroup { + title: qsTr("Required Data") + settingsModel.translationTrigger + chat: chatModel.required + } + + DataGroup { + title: qsTr("Optional Data") + settingsModel.translationTrigger + chat: chatModel.optional + } + } + } + } +} diff --git a/resources/qml/Governikus/IdentifyView/+mobile/+android/+tablet/EditRights.qml b/resources/qml/Governikus/IdentifyView/+mobile/+android/+tablet/EditRights.qml new file mode 100644 index 0000000..c11317d --- /dev/null +++ b/resources/qml/Governikus/IdentifyView/+mobile/+android/+tablet/EditRights.qml @@ -0,0 +1,158 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.Provider 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 +import Governikus.Type.AuthModel 1.0 + +SectionPage { + id: root + + leftTitleBarAction: TitleBarAction { + state: "cancel" + onClicked: AuthModel.cancelWorkflow() + } + headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger; font.bold: true } + + content: Column { + width: baseItem.width + padding: Constants.pane_padding + + Column { + width: parent.width - 2 * Constants.pane_padding + spacing: Constants.pane_spacing + + Text { + color: Constants.secondary_text + font.pixelSize: Constants.normal_font_size + width: parent.width + wrapMode: Text.WordWrap + text: qsTr("You are about to identify yourself towards the following service provider:") + settingsModel.translationTrigger + } + + Pane { + + Row { + height: providerEntries.height + width: parent.width + spacing: Constants.pane_spacing + + Item { + height: providerEntries.height + width: (parent.width - Constants.pane_spacing) / 2 + + Column { + id: providerEntries + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + spacing: Constants.pane_spacing + + ProviderInfoSection { + imageSource: "qrc:///images/provider/information.svg" + title: qsTr("Service provider") + settingsModel.translationTrigger + name: certificateDescriptionModel.subjectName + } + ProviderInfoSection { + imageSource: "qrc:///images/provider/purpose.svg" + title: qsTr("Purpose for reading out requested data") + settingsModel.translationTrigger + name: certificateDescriptionModel.purpose + } + } + + MouseArea { + anchors.fill: parent + onClicked: firePush(certificateDescriptionPage) + } + + CertificateDescriptionPage { + id: certificateDescriptionPage + name: certificateDescriptionModel.subjectName + visible: false + } + } + + + Item { + height: parent.height + width: (parent.width - Constants.pane_spacing) / 2 + + GButton { + id: button + iconSource: "qrc:///images/npa.svg" + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + text: qsTr("Identify now") + settingsModel.translationTrigger + onClicked: { + chatModel.transferAccessRights() + AuthModel.continueWorkflow() + } + } + } + } + } + + Text { + color: Constants.secondary_text + font.pixelSize: Constants.normal_font_size + width: parent.width + wrapMode: Text.WordWrap + text: qsTr("The following data will be transferred to the service provider when you enter the PIN:") + settingsModel.translationTrigger + } + + Pane { + Column { + height: childrenRect.height + width: parent.width + spacing: Utils.dp(30) + + Column { + id: transactionInfo + + width: parent.width + visible: !!transactionInfoText.text + + PaneTitle { + height: implicitHeight * 1.5 + verticalAlignment: Text.AlignTop + text: qsTr("Transactional information") + settingsModel.translationTrigger + } + + Text { + id: transactionInfoText + color: Constants.secondary_text + + width: parent.width + font.pixelSize: Constants.normal_font_size + text: AuthModel.transactionInfo + wrapMode: Text.WordWrap + } + } + + Row { + width: parent.width + spacing: Constants.pane_spacing + + DataGroup { + id: requiredData + width: optionalData.visible ? parent.width * 0.63 : parent.width + + title: qsTr("Required Data") + settingsModel.translationTrigger + columns: optionalData.visible ? 2 : 3 + chat: chatModel.required + } + + DataGroup { + id: optionalData + width: parent.width * 0.37 - Constants.pane_spacing + + title: qsTr("Optional Data") + settingsModel.translationTrigger + chat: chatModel.optional + } + } + } + } + } + } +} diff --git a/resources/qml/Governikus/IdentifyView/+mobile/+android/DataGroup.qml b/resources/qml/Governikus/IdentifyView/+mobile/+android/DataGroup.qml new file mode 100644 index 0000000..54f74b0 --- /dev/null +++ b/resources/qml/Governikus/IdentifyView/+mobile/+android/DataGroup.qml @@ -0,0 +1,111 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 + + +Rectangle { + id: root + property string title; + property int columns: 1 + property var chat + + width: parent.width + height: column.height + visible: repeater.count > 0 + + Column { + id: column + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + PaneTitle { + height: implicitHeight * 1.5 + verticalAlignment: Text.AlignTop + text: title + } + + Rectangle { + width: parent.width + height: Utils.dp(40) + visible: repeater.count < 1 + Text { + id: emptyText + color: Constants.secondary_text + anchors.verticalCenter: parent.verticalCenter + width: parent.width + font.pixelSize: Constants.normal_font_size + text: qsTr("No data requested") + settingsModel.translationTrigger + } + Rectangle { + anchors.top: parent.bottom + anchors.topMargin: -height + height: 1 + width: parent.width + color: Constants.grey + } + } + + Grid { + id: grid + columns: root.columns + columnSpacing: Constants.pane_spacing + width: parent.width + verticalItemAlignment: Grid.AlignBottom + + Repeater { + id: repeater + model: chat + visible: repeater.count > 0 + + Rectangle { + width: (grid.width - ((grid.columns - 1) * grid.columnSpacing)) / grid.columns + height: Utils.dp(40) + color: "white" + + Text { + id: text + color: Constants.secondary_text + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.right: checkBox.left + font.pixelSize: Constants.normal_font_size + text: name + wrapMode: Text.WordWrap + } + + Rectangle { + anchors.top: parent.bottom + anchors.topMargin: -height + height: 1 + width: parent.width + color: Constants.grey + } + + GCheckBox { + id: checkBox + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + visible: optional + checked: selected + } + + MouseArea { + anchors.fill: parent + enabled: optional + onClicked: selected = !selected + Rectangle { + anchors.centerIn: parent + width: root.width + height: parent.height + color: Constants.accent_color + opacity: parent.pressed ? 0.5 : 0 + Behavior on opacity { NumberAnimation { duration: 100 } } + } + } + } + } + } + } +} diff --git a/resources/qml/Governikus/IdentifyView/+mobile/+ios/+phone/DataGroup.qml b/resources/qml/Governikus/IdentifyView/+mobile/+ios/+phone/DataGroup.qml new file mode 100644 index 0000000..5005d4a --- /dev/null +++ b/resources/qml/Governikus/IdentifyView/+mobile/+ios/+phone/DataGroup.qml @@ -0,0 +1,62 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 + + +Pane { + property alias chat: repeater.model + + id: pane + spacing: 0 + visible: repeater.count > 0 + + Repeater { + id: repeater + + Rectangle { + width: parent.width + height: Utils.dp(40) + radius: 3 + color: "white" + Text { + id: dataGroup + color: Constants.secondary_text + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + anchors.right: checkBox.left + width: parent.width + font.pixelSize: Constants.normal_font_size + wrapMode: Text.WordWrap + text: name + } + Rectangle { + anchors.top: parent.bottom + anchors.topMargin: -height + height: 1 + anchors.left: dataGroup.left + anchors.right: dataGroup.right + color: Constants.grey + } + GCheckBox { + id: checkBox + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + visible: optional + checked: selected + } + + MouseArea { + anchors.fill: parent + enabled: optional + onClicked: selected = !selected + Rectangle { + anchors.fill: parent + color: Constants.grey + opacity: parent.pressed ? 0.5 : 0 + Behavior on opacity { NumberAnimation { duration: 100 } } + } + } + } + } +} diff --git a/resources/qml/Governikus/IdentifyView/+mobile/+ios/+phone/EditRights.qml b/resources/qml/Governikus/IdentifyView/+mobile/+ios/+phone/EditRights.qml new file mode 100644 index 0000000..e6870f5 --- /dev/null +++ b/resources/qml/Governikus/IdentifyView/+mobile/+ios/+phone/EditRights.qml @@ -0,0 +1,128 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.Provider 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 +import Governikus.Type.AuthModel 1.0 + +SectionPage { + id: baseItem + + leftTitleBarAction: TitleBarAction { + state: "cancel" + onClicked: AuthModel.cancelWorkflow() + } + headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger; font.bold: true } + + content: Column { + width: baseItem.width + padding: Constants.pane_padding + + Column { + width: parent.width - 2 * Constants.pane_padding + + spacing: Constants.component_spacing + + Text { + color: Constants.secondary_text + font.pixelSize: Constants.normal_font_size + width: parent.width + wrapMode: Text.WordWrap + text: qsTr("You are about to identify yourself towards the following service provider:") + settingsModel.translationTrigger + } + + Pane { + + Item { + width: parent.width + height: providerEntries.height + + Column { + id: providerEntries + anchors.top: parent.top + anchors.left: parent.left + anchors.right: forwardAction.left + spacing: Constants.pane_spacing + + ProviderInfoSection { + imageSource: "qrc:///images/provider/information.svg" + title: qsTr("Service provider") + settingsModel.translationTrigger + name: certificateDescriptionModel.subjectName + } + ProviderInfoSection { + imageSource: "qrc:///images/provider/purpose.svg" + title: qsTr("Purpose for reading out requested data") + settingsModel.translationTrigger + name: certificateDescriptionModel.purpose + } + } + + Text { + id: forwardAction + anchors.right: parent.right + anchors.verticalCenter: providerEntries.verticalCenter + + text: ">" + font.pixelSize: Utils.dp(22) + color: Constants.grey + } + + MouseArea { + anchors.fill: parent + onClicked: firePush(certificateDescriptionPage) + } + + CertificateDescriptionPage { + id: certificateDescriptionPage + name: certificateDescriptionModel.subjectName + visible: false + } + } + } + + GButton { + iconSource: "qrc:///images/npa.svg" + anchors.horizontalCenter: parent.horizontalCenter + text: qsTr("Identify now") + settingsModel.translationTrigger; + onClicked: { + chatModel.transferAccessRights() + AuthModel.continueWorkflow() + } + } + + Text { + color: Constants.secondary_text + font.pixelSize: Constants.normal_font_size + width: parent.width + wrapMode: Text.WordWrap + text: qsTr("The following data will be transferred to the service provider when you enter the PIN:") + settingsModel.translationTrigger + } + + Pane { + id: transactionInfo + title: qsTr("Transactional information") + settingsModel.translationTrigger + visible: !!transactionInfoText.text + + Text { + id: transactionInfoText + color: Constants.secondary_text + + width: parent.width + font.pixelSize: Constants.normal_font_size + text: AuthModel.transactionInfo + wrapMode: Text.WordWrap + } + } + + DataGroup { + title: qsTr("Required Data") + settingsModel.translationTrigger + chat: chatModel.required + } + + DataGroup { + title: qsTr("Optional Data") + settingsModel.translationTrigger + chat: chatModel.optional + } + } + } +} diff --git a/resources/qml/Governikus/IdentifyView/+mobile/+ios/+tablet/DataGroup.qml b/resources/qml/Governikus/IdentifyView/+mobile/+ios/+tablet/DataGroup.qml new file mode 100644 index 0000000..98c7ac7 --- /dev/null +++ b/resources/qml/Governikus/IdentifyView/+mobile/+ios/+tablet/DataGroup.qml @@ -0,0 +1,111 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 + + +Rectangle { + id: root + property string title; + property int columns: 1 + property var chat + + width: parent.width + height: column.height + visible: repeater.count > 0 + + Column { + id: column + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + PaneTitle { + height: implicitHeight * 1.5 + verticalAlignment: Text.AlignTop + text: title + } + + Rectangle { + width: parent.width + height: Utils.dp(40) + visible: repeater.count < 1 + Text { + color: Constants.secondary_text + id: emptyText + anchors.verticalCenter: parent.verticalCenter + width: parent.width + font.pixelSize: Constants.normal_font_size + text: qsTr("No data requested") + settingsModel.translationTrigger + } + Rectangle { + anchors.top: parent.bottom + anchors.topMargin: -height + height: 1 + width: parent.width + color: Constants.grey + } + } + + Grid { + id: grid + columns: root.columns + columnSpacing: Constants.pane_spacing + width: parent.width + verticalItemAlignment: Grid.AlignBottom + + Repeater { + id: repeater + model: chat + visible: repeater.count > 0 + + Rectangle { + width: (grid.width - ((grid.columns - 1) * grid.columnSpacing)) / grid.columns + height: Utils.dp(40) + color: "white" + + Text { + id: text + color: Constants.secondary_text + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.right: checkBox.left + font.pixelSize: Constants.normal_font_size + text: name + wrapMode: Text.WordWrap + } + + Rectangle { + anchors.top: parent.bottom + anchors.topMargin: -height + height: 1 + width: parent.width + color: Constants.grey + } + + GCheckBox { + id: checkBox + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + visible: optional + checked: selected + } + + MouseArea { + anchors.fill: parent + enabled: optional + onClicked: selected = !selected + Rectangle { + anchors.centerIn: parent + width: root.width + height: parent.height + color: Constants.accent_color + opacity: parent.pressed ? 0.5 : 0 + Behavior on opacity { NumberAnimation { duration: 100 } } + } + } + } + } + } + } +} diff --git a/resources/qml/Governikus/IdentifyView/+mobile/+ios/+tablet/EditRights.qml b/resources/qml/Governikus/IdentifyView/+mobile/+ios/+tablet/EditRights.qml new file mode 100644 index 0000000..d28d8ca --- /dev/null +++ b/resources/qml/Governikus/IdentifyView/+mobile/+ios/+tablet/EditRights.qml @@ -0,0 +1,154 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.Provider 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 +import Governikus.Type.AuthModel 1.0 + +SectionPage { + id: baseItem + + leftTitleBarAction: TitleBarAction { + state: "cancel" + onClicked: AuthModel.cancelWorkflow() + } + headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger; font.bold: true } + + content: Column { + width: baseItem.width + padding: Constants.pane_padding + + Column { + width: parent.width - 2 * Constants.pane_padding + spacing: Constants.component_spacing + + Text { + color: Constants.secondary_text + font.pixelSize: Constants.normal_font_size + width: parent.width + wrapMode: Text.WordWrap + text: qsTr("You are about to identify yourself towards the following service provider:") + settingsModel.translationTrigger + } + + Pane { + + Item { + width: parent.width + height: providerEntries.height + + Column { + id: providerEntries + anchors.top: parent.top + anchors.left: parent.left + anchors.right: forwardAction.left + spacing: Constants.pane_spacing + + ProviderInfoSection { + imageSource: "qrc:///images/provider/information.svg" + title: qsTr("Service provider") + settingsModel.translationTrigger + name: certificateDescriptionModel.subjectName + } + ProviderInfoSection { + imageSource: "qrc:///images/provider/purpose.svg" + title: qsTr("Purpose for reading out requested data") + settingsModel.translationTrigger + name: certificateDescriptionModel.purpose + } + } + + Text { + id: forwardAction + anchors.right: parent.right + anchors.verticalCenter: providerEntries.verticalCenter + + text: ">" + font.pixelSize: Utils.dp(22) + color: Constants.grey + } + + MouseArea { + anchors.fill: parent + onClicked: firePush(certificateDescriptionPage) + } + + CertificateDescriptionPage { + id: certificateDescriptionPage + name: certificateDescriptionModel.subjectName + visible: false + } + } + } + + GButton { + iconSource: "qrc:///images/npa.svg" + anchors.horizontalCenter: parent.horizontalCenter + text: qsTr("Identify now") + settingsModel.translationTrigger; + onClicked: { + chatModel.transferAccessRights() + AuthModel.continueWorkflow() + } + } + + Text { + color: Constants.secondary_text + font.pixelSize: Constants.normal_font_size + width: parent.width + wrapMode: Text.WordWrap + text: qsTr("The following data will be transferred to the service provider when you enter the PIN:") + settingsModel.translationTrigger + } + + Pane { + Column { + height: childrenRect.height + width: parent.width + spacing: Utils.dp(30) + + Column { + id: transactionInfo + + width: parent.width + visible: !!transactionInfoText.text + + PaneTitle { + height: implicitHeight * 1.5 + verticalAlignment: Text.AlignTop + text: qsTr("Transactional information") + settingsModel.translationTrigger + } + + Text { + id: transactionInfoText + color: Constants.secondary_text + + width: parent.width + font.pixelSize: Constants.normal_font_size + text: AuthModel.transactionInfo + wrapMode: Text.WordWrap + } + } + + Row { + width: parent.width + spacing: Constants.pane_spacing + + DataGroup { + id: requiredData + width: optionalData.visible ? parent.width * 0.63 : parent.width + + title: qsTr("Required Data") + settingsModel.translationTrigger + columns: optionalData.visible ? 2 : 3 + chat: chatModel.required + } + + DataGroup { + id: optionalData + width: parent.width * 0.37 - Constants.pane_spacing + + title: qsTr("Optional Data") + settingsModel.translationTrigger + chat: chatModel.optional + } + } + } + } + } + } +} diff --git a/resources/qml/Governikus/IdentifyView/+mobile/CertificateDescriptionPage.qml b/resources/qml/Governikus/IdentifyView/+mobile/CertificateDescriptionPage.qml new file mode 100644 index 0000000..de7446d --- /dev/null +++ b/resources/qml/Governikus/IdentifyView/+mobile/CertificateDescriptionPage.qml @@ -0,0 +1,44 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 + +SectionPage +{ + id: root + leftTitleBarAction: TitleBarAction { state: "back"; onClicked: firePop() } + headerTitleBarAction: TitleBarAction { text: name; font.bold: true } + + property string name + + content: Item + { + height: pane.height + 2 * Constants.component_spacing + width: root.width + + Column + { + anchors.fill: parent + anchors.margins: Constants.component_spacing + + Pane { + id: pane + title: qsTr("Provider Information") + settingsModel.translationTrigger + + Repeater { + id: listView + model: certificateDescriptionModel + + LabeledText { + id: delegate + label: model.label + text: model.text + textFormat: Text.PlainText + width: parent.width + } + } + } + } + } +} diff --git a/resources/qml/Governikus/IdentifyView/+mobile/IdentifyController.qml b/resources/qml/Governikus/IdentifyView/+mobile/IdentifyController.qml new file mode 100644 index 0000000..892f6ef --- /dev/null +++ b/resources/qml/Governikus/IdentifyView/+mobile/IdentifyController.qml @@ -0,0 +1,206 @@ +import QtQuick 2.10 + +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.AuthModel 1.0 +import Governikus.Type.NumberModel 1.0 +import Governikus.Type.ReaderPlugIn 1.0 +import Governikus.Type.PacePasswordId 1.0 + +Item { + enum WorkflowStates { + Initial, + Reader, + Card, + Update, + Can, + Pin, + Processing + } + + QtObject { + id: d + readonly property int readerPlugInType: AuthModel.readerPlugInType + } + + id: controller + readonly property string currentState: AuthModel.currentState + readonly property bool bluetoothEnabled: ApplicationModel.bluetoothEnabled + + property bool connectedToCard: false + property int workflowState: 0 + property int workflowProgressValue: 0 + property bool workflowProgressVisible: false + + function sufficientBluetoothRights() { + return ApplicationModel.bluetoothEnabled && (!ApplicationModel.locationPermissionRequired || locationPermissionConfirmed) + } + + property bool locationPermissionConfirmed: false + onLocationPermissionConfirmedChanged: { + // If the user has given location permission: continue Bluetooth workflow. + if (d.readerPlugInType === ReaderPlugIn.BLUETOOTH && sufficientBluetoothRights()) { + AuthModel.continueWorkflow() + } + } + + onBluetoothEnabledChanged: { + // If the user has activated bluetooth and no location permission is needed: continue Bluetooth workflow. + if (d.readerPlugInType === ReaderPlugIn.BLUETOOTH && sufficientBluetoothRights()) { + AuthModel.continueWorkflow() + } + } + + states: [ + State { + when: AuthModel.currentState === "StateGetTcToken" && !connectivityManager.networkInterfaceActive + StateChangeScript { + script: firePush(checkConnectivityView) + } + }, + State { + when: AuthModel.currentState === "StateGetTcToken" && connectivityManager.networkInterfaceActive + StateChangeScript { + script: { + firePush(identifyProgressView) + AuthModel.continueWorkflow() + } + } + } + ] + + Component.onCompleted: if (AuthModel.currentState === "StateProcessing") processStateChange() + + Connections { + target: AuthModel + onFireCurrentStateChanged: processStateChange() + // This is necessary because onCurrentStateChanged is not + // working, when we need to process a state a second time + } + + function showRemoveCardFeedback() { + if (controller.connectedToCard) { + controller.connectedToCard = false + qmlExtension.showFeedback(qsTr("You may now remove your ID card from the device.")) + } + } + + function processStateChange() { + switch (AuthModel.currentState) { + case "": + break; + case "StateGetTcToken": + enterPinView.state = "INITIAL" + controller.workflowState = IdentifyController.WorkflowStates.Initial + navBar.lockedAndHidden = true + navBar.state = "identify" + navBar.currentIndex = 0 + break + case "StateEditAccessRights": + fireReplace(editRights) + AuthModel.setInitialPluginType() + break + case "StateSelectReader": + fireReplace(identifyWorkflow) + if (d.readerPlugInType === ReaderPlugIn.BLUETOOTH && !sufficientBluetoothRights()) { + // Stop the workflow here until the user has enabled bluetooth and confirmed the location permission. + controller.workflowState = IdentifyController.WorkflowStates.Reader + } + else + { + setIdentifyWorkflowStateAndContinue(IdentifyController.WorkflowStates.Reader) + } + break + case "StateConnectCard": + setIdentifyWorkflowStateAndContinue(IdentifyController.WorkflowStates.Card) + break + case "StatePreparePace": + fireReplace(identifyProgressView) + setIdentifyWorkflowStateAndContinue(IdentifyController.WorkflowStates.Update) + break + case "StateEnterPacePassword": + if (NumberModel.establishPaceChannelType == PacePasswordId.PACE_PIN) { + setIdentifyWorkflowStateAndRequestInput(IdentifyController.WorkflowStates.Pin, "PIN") + } + else if (NumberModel.establishPaceChannelType == PacePasswordId.PACE_CAN) { + setIdentifyWorkflowStateAndRequestInput(IdentifyController.WorkflowStates.Can, "CAN") + } + else if (NumberModel.establishPaceChannelType == PacePasswordId.PACE_PUK) { + AuthModel.cancelWorkflowOnPinBlocked() + } + break + case "StateUnfortunateCardPosition": + firePush(cardPositionView) + break + case "StateDidAuthenticateEac1": + controller.workflowProgressVisible = true + controller.workflowProgressValue = 1 + setIdentifyWorkflowStateAndContinue(IdentifyController.WorkflowStates.Processing) + break + case "StateSendDIDAuthenticateResponseEAC1": + fireReplace(identifyProgressView) + AuthModel.continueWorkflow() + break + case "StateDidAuthenticateEac2": + controller.workflowProgressValue = 2 + AuthModel.continueWorkflow() + break + case "StateTransmit": + controller.workflowProgressValue = 3 + AuthModel.continueWorkflow() + break + case "StateCleanUpReaderManager": + controller.connectedToCard = AuthModel.selectedReaderHasCard() && !AuthModel.isError; + AuthModel.continueWorkflow() + break; + case "StateCheckRefreshAddress": + controller.workflowProgressValue = 4 + AuthModel.continueWorkflow() + break + case "StateWriteHistory": + showRemoveCardFeedback() + controller.workflowProgressValue = 5 + AuthModel.continueWorkflow() + break + case "StateShowSelfInfo": + firePush(selfAuthenticationData) + break + case "StateSendWhitelistSurvey": + if (settingsModel.askForDeviceSurvey() && !AuthModel.error && d.readerPlugInType === ReaderPlugIn.NFC) { + firePush(whiteListSurveyView) + } else { + AuthModel.continueWorkflow() + } + break + case "FinalState": + navBar.lockedAndHidden = true + if (AuthModel.error) { + showRemoveCardFeedback() + firePush(identifyResult) + } else { + AuthModel.continueWorkflow() + firePopAll() + navBar.lockedAndHidden = false + } + controller.workflowProgressVisible = false + controller.workflowProgressValue = 0 + break + default: + AuthModel.continueWorkflow() + } + } + + function setIdentifyWorkflowStateAndContinue(pState) { + controller.workflowState = pState; + AuthModel.continueWorkflow() + } + + function setIdentifyWorkflowStateAndRequestInput(pState, pInput) { + controller.workflowState = pState + if (AuthModel.isBasicReader) { + enterPinView.state = pInput + firePush(enterPinView) + } else { + AuthModel.continueWorkflow() + } + } +} diff --git a/resources/qml/Governikus/IdentifyView/+mobile/IdentifyView.qml b/resources/qml/Governikus/IdentifyView/+mobile/IdentifyView.qml new file mode 100644 index 0000000..9fc8d2b --- /dev/null +++ b/resources/qml/Governikus/IdentifyView/+mobile/IdentifyView.qml @@ -0,0 +1,215 @@ +import QtQuick 2.10 + +import Governikus.MainView 1.0 +import Governikus.EnterPinView 1.0 +import Governikus.Global 1.0 +import Governikus.TitleBar 1.0 +import Governikus.ProgressView 1.0 +import Governikus.ResultView 1.0 +import Governikus.WhiteListClient 1.0 +import Governikus.View 1.0 +import Governikus.Workflow 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.AuthModel 1.0 +import Governikus.Type.NumberModel 1.0 +import Governikus.Type.ChangePinModel 1.0 + + +SectionPage +{ + id: identifyEditChatView + leftTitleBarAction: TitleBarAction { + state: ApplicationModel.currentWorkflow === "authentication" ? "cancel" : "" + onClicked: AuthModel.cancelWorkflow() + } + headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger; font.bold: true } + + // Workaround for "cold start" case, IdentifyController onCompleted fires too early, TabBarView + // for this View is not ready with loading the initial stack item. firePush of the ProgressView + // is therefore not caught anywhere. This workaround buffers firePush messages until the base + // SectionPage is loaded by its parent TabBarView. + property var pendingSignals: new Array() + + onFirePush: { + if (!pushed) { + pendingSignals.push(pSectionPage) + } + } + + onPushedChanged: { + if (pushed) { + pendingSignals.reverse(); + while (pendingSignals.length > 0) { + firePush(pendingSignals.pop()) + } + } + } + + content: MainView { + width: identifyEditChatView.width + height: identifyEditChatView.height + } + + IdentifyController { + id: identifyController + } + + EditRights { + id: editRights + visible: false + } + + SelfAuthenticationData { + id: selfAuthenticationData + visible: false + + onDone: { + firePop() + AuthModel.continueWorkflow() + } + } + + WhiteListSurveyView { + id: whiteListSurveyView + visible: false + + onDone: { + settingsModel.setDeviceSurveyPending(pUserAccepted) + firePop() + AuthModel.continueWorkflow() + } + } + + GeneralWorkflow { + id: identifyWorkflow + visible: false + + controller: identifyController + workflowModel: AuthModel + workflowTitle: qsTr("Identify") + settingsModel.translationTrigger + + waitingFor: switch (identifyController.workflowState) { + case IdentifyController.WorkflowStates.Reader: + return Workflow.WaitingFor.Reader + case IdentifyController.WorkflowStates.Card: + return Workflow.WaitingFor.Card + case IdentifyController.WorkflowStates.Can: + case IdentifyController.WorkflowStates.Pin: + return Workflow.WaitingFor.Password + default: + return Workflow.WaitingFor.None + } + } + + EnterPinView { + id: enterPinView + leftTitleBarAction: TitleBarAction { state: "cancel"; onClicked: AuthModel.cancelWorkflow() } + headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger } + visible: false + + onPinEntered: { + firePop() + AuthModel.continueWorkflow() + } + + onChangePinLength: { + firePush(changeToTransportPinView) + } + } + + ProgressView { + id: identifyProgressView + leftTitleBarAction: TitleBarAction { state: AuthModel.isBasicReader || identifyController.workflowProgressVisible ? "cancel" : "hidden"; onClicked: AuthModel.cancelWorkflow() } + headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger; font.bold: true } + visible: false + text: (AuthModel.error ? qsTr("Cancel authentication process") : + identifyController.workflowState === IdentifyController.WorkflowStates.Initial ? qsTr("Acquiring provider certificate") : + qsTr("Authentication in progress")) + settingsModel.translationTrigger + subText: { + settingsModel.translationTrigger; + if (!visible) { + return "" + } + if (AuthModel.isBasicReader) { + return qsTr("Please wait a moment...") + } + if (!!NumberModel.inputError) { + return NumberModel.inputError + } + if (NumberModel.pinDeactivated) { + return qsTr("The online identification function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the online identification function.") + } + if (identifyController.workflowState === IdentifyController.WorkflowStates.Update || identifyController.workflowState === IdentifyController.WorkflowStates.Pin) { + return qsTr("Please observe the display of your card reader.") + } + if (identifyController.workflowState === IdentifyController.WorkflowStates.Can) { + return qsTr("You have entered the wrong PIN twice. Prior to a third attempt, you have to enter your six-digit card access number first. You can find your card access number on the front of your ID card.") + } + return qsTr("Please wait a moment...") + } + subTextColor: !AuthModel.isBasicReader && (NumberModel.inputError + || NumberModel.pinDeactivated + || identifyController.workflowState === IdentifyController.WorkflowStates.Can) + ? "red" : Constants.secondary_text + progressValue: identifyController.workflowProgressValue + progressText: (progressValue === 0 ? "" : + progressValue === 1 ? qsTr("Service provider is being verified") : + progressValue === 2 ? qsTr("Card is being verified") : + progressValue === 3 ? qsTr("Reading data") : + progressValue === 4 ? qsTr("Sending data to service provider") : + progressValue === 5 ? qsTr("Preparing results") : + "") + settingsModel.translationTrigger + progressBarVisible: identifyController.workflowProgressVisible + } + + ProgressView { + id: checkConnectivityView + leftTitleBarAction: TitleBarAction { state: "cancel"; onClicked: AuthModel.cancelWorkflow() } + headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger; font.bold: true } + visible: false + text: qsTr("No network connectivity") + settingsModel.translationTrigger + subText: qsTr("Please enable the network interface or cancel the workflow.") + settingsModel.translationTrigger + subTextColor: Constants.red + } + + ResultView { + id: changeToTransportPinView + leftTitleBarAction: TitleBarAction { state: "back"; onClicked: firePop() } + headerTitleBarAction: TitleBarAction { text: qsTr("Change transport PIN") + settingsModel.translationTrigger; font.bold: true } + resultType: ResultView.Type.IsInfo + buttonText: qsTr("Change PIN") + settingsModel.translationTrigger + text: qsTr("You leave the process and are forwarded to the PIN management. Please restart the desired process after the PIN has been changed.") + settingsModel.translationTrigger + onClicked: { + ChangePinModel.startWorkflow() + AuthModel.cancelWorkflowToChangePin() + } + visible: false + } + + ResultView { + id: cardPositionView + headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger; font.bold: true } + resultType: ResultView.Type.IsInfo + buttonText: qsTr("Retry") + settingsModel.translationTrigger + text: qsTr("Weak NFC signal. Please reposition your card.") + settingsModel.translationTrigger + onClicked: { + firePop() + AuthModel.continueWorkflow() + } + visible: false + } + + ResultView { + id: identifyResult + headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger; font.bold: true } + resultType: AuthModel.resultString ? ResultView.Type.IsError : ResultView.Type.IsSuccess + showMailButton: AuthModel.errorIsMasked + text: AuthModel.resultString + onClicked: { + AuthModel.continueWorkflow() + firePopAll() + navBar.lockedAndHidden = false + } + visible: false + } +} diff --git a/resources/qml/Governikus/IdentifyView/+mobile/SelfAuthenticationData.qml b/resources/qml/Governikus/IdentifyView/+mobile/SelfAuthenticationData.qml new file mode 100644 index 0000000..5ff099a --- /dev/null +++ b/resources/qml/Governikus/IdentifyView/+mobile/SelfAuthenticationData.qml @@ -0,0 +1,83 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 +import Governikus.Type.AuthModel 1.0 + +SectionPage { + id: root + leftTitleBarAction: TitleBarAction { state: "cancel"; onClicked: root.done() } + headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger; font.bold: true } + + signal done() + + content: Item { + height: content.height + okButton.height + Constants.component_spacing + width: root.width + + Column { + id: content + width: parent.width + padding: Constants.component_spacing + spacing: Constants.component_spacing + + Item { + id: message + height: Utils.dp(60) + width: resultIcon.width + Constants.component_spacing + successText.width + anchors.horizontalCenter: parent.horizontalCenter + + StatusIcon { + id: resultIcon + height: parent.height + anchors.top: parent.top + anchors.verticalCenter: parent.verticalCenter + source: "qrc:///images/status_ok.svg" + } + + Text { + id: successText + anchors.left: resultIcon.right + anchors.leftMargin: Constants.component_spacing + anchors.verticalCenter: resultIcon.verticalCenter + text: qsTr("Successfull reading data") + settingsModel.translationTrigger + font.pixelSize: Constants.is_tablet ? Constants.header_font_size : Constants.normal_font_size + color: Constants.blue + } + } + + Pane { + id: pane + anchors.leftMargin: Constants.component_spacing + anchors.rightMargin: Constants.component_spacing + + Grid { + id: grid + width: parent.width + columns: Constants.is_tablet ? 3 : 1 + spacing: Utils.dp(15) + verticalItemAlignment: Grid.AlignBottom + Repeater { + model: selfAuthModel + + LabeledText { + label: name + text: value === "" ? "---" : value + width: (pane.width - 2 * Constants.pane_padding - (grid.columns - 1) * grid.spacing) / grid.columns + } + } + } + } + } + } + + GButton { + id: okButton + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + anchors.bottomMargin: Constants.component_spacing + text: qsTr("OK") + settingsModel.translationTrigger + onClicked: root.done() + } +} diff --git a/resources/qml/Governikus/IdentifyView/CertificateDescriptionPage.qml b/resources/qml/Governikus/IdentifyView/CertificateDescriptionPage.qml deleted file mode 100644 index d8e89a8..0000000 --- a/resources/qml/Governikus/IdentifyView/CertificateDescriptionPage.qml +++ /dev/null @@ -1,54 +0,0 @@ -import QtQml.Models 2.2 -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Layouts 1.2 - -import Governikus.Global 1.0 -import Governikus.TitleBar 1.0 - -SectionPage -{ - id: root - leftTitleBarAction: TitleBarAction { state: "back"; onClicked: firePop() } - headerTitleBarAction: TitleBarAction { text: name; font.bold: true } - - property string name - - content: Item - { - height: pane.height + 2 * Constants.component_spacing - width: root.width - - Column - { - anchors.fill: parent - anchors.margins: Constants.component_spacing - - Pane { - id: pane - - Text { - height: implicitHeight * 2 - verticalAlignment: Text.AlignVCenter - text: qsTr("Provider Information") + settingsModel.translationTrigger - color: Constants.blue - font.pixelSize: Constants.header_font_size - elide: Text.ElideRight - } - - Repeater { - id: listView - model: certificateDescriptionModel - - LabeledText { - id: delegate - label: model.label - text: model.text - textFormat: Text.PlainText - width: parent.width - } - } - } - } - } -} diff --git a/resources/qml/Governikus/IdentifyView/DataGroup.qml b/resources/qml/Governikus/IdentifyView/DataGroup.qml deleted file mode 100644 index 4b11736..0000000 --- a/resources/qml/Governikus/IdentifyView/DataGroup.qml +++ /dev/null @@ -1,3 +0,0 @@ -Item { - id: dummy -} diff --git a/resources/qml/Governikus/IdentifyView/IdentifyController.qml b/resources/qml/Governikus/IdentifyView/IdentifyController.qml deleted file mode 100644 index ed9d47b..0000000 --- a/resources/qml/Governikus/IdentifyView/IdentifyController.qml +++ /dev/null @@ -1,161 +0,0 @@ -import QtQuick 2.5 - - -Item { - id: controller - readonly property string currentState: authModel.currentState - property bool showRemoveCardFeedback: false - - property bool locationPermissionConfirmed: false - onLocationPermissionConfirmedChanged: { - // If the user has given location permission: continue Bluetooth workflow. - if (authModel.readerPlugInType === "BLUETOOTH" && applicationModel.bluetoothEnabled && applicationModel.locationPermissionRequired && locationPermissionConfirmed) { - numberModel.continueWorkflow() - } - } - - states: [ - State { - when: authModel.currentState === "StateGetTcToken" && !connectivityManager.networkInterfaceActive - StateChangeScript { - script: firePush(checkConnectivityView, {}) - } - }, - State { - when: authModel.currentState === "StateGetTcToken" && connectivityManager.networkInterfaceActive - StateChangeScript { - script: { - firePush(identifyProgressView, {}) - numberModel.continueWorkflow() - } - } - } - ] - - Component.onCompleted: if (authModel.currentState === "StateProcessing") processStateChange() - - Connections { - target: authModel - onFireCurrentStateChanged: processStateChange() - // This is necessary because onCurrentStateChanged is not - // working, when we need to process a state a second time - } - - function processStateChange() { - switch (authModel.currentState) { - case "": - break; - case "StateGetTcToken": - enterPinView.state = "INITIAL" - identifyWorkflow.state = "initial" - navBar.lockedAndHidden = true - navBar.state = "identify" - navBar.currentIndex = 0 - break - case "StateEditAccessRights": - firePush(identifyViewContent, {}) - - authModel.setInitialPluginType() - break - case "StateSelectReader": - firePush(identifyWorkflow, {}) - if (authModel.readerPlugInType === "BLUETOOTH" && applicationModel.bluetoothEnabled && applicationModel.locationPermissionRequired && !locationPermissionConfirmed) { - // Stop the workflow here until the user has confirmed the location permission. - setIdentifyWorkflowState("reader") - } - else - { - setIdentifyWorkflowStateAndContinue("reader") - } - break - case "StateConnectCard": - setIdentifyWorkflowStateAndContinue("card") - break - case "StateHandleRetryCounter": - if (!authModel.isBasicReader) { - firePush(identifyProgressView, {}) - } - setIdentifyWorkflowStateAndContinue("updateretrycounter") - break - case "StateEstablishPaceCan": - setIdentifyWorkflowStateAndRequestInput("identify_entercan", "CAN") - break - case "StateEstablishPacePin": - setIdentifyWorkflowStateAndRequestInput("identify_enterpin", "PIN") - break - case "StateDidAuthenticateEac1": - identifyProgressView.progressBarVisible = true - setIdentifyProgressViewValue(1) - setIdentifyWorkflowStateAndContinue("identify_processing") - break - case "StateEstablishPacePuk": - authModel.cancelWorkflowOnPinBlocked() - break - case "StateDidAuthenticateEac2": - setIdentifyProgressViewValue(2) - numberModel.continueWorkflow() - break - case "StateTransmit": - setIdentifyProgressViewValue(3) - numberModel.continueWorkflow() - break - case "StateCleanUpReaderManager": - controller.showRemoveCardFeedback = numberModel.cardConnected && !authModel.isError; - numberModel.continueWorkflow() - break; - case "StateCheckRefreshAddress": - setIdentifyProgressViewValue(4) - numberModel.continueWorkflow() - break - case "StateWriteHistory": - setIdentifyProgressViewValue(5) - numberModel.continueWorkflow() - break - case "FinalState": - navBar.lockedAndHidden = true - if (controller.showRemoveCardFeedback) { - controller.showRemoveCardFeedback = false - qmlExtension.showFeedback(qsTr("You may now remove your ID card from the device.")) - } - - if (authModel.error) - firePush(identifyResult, {}) - else { - if (applicationModel.currentWorkflow === "selfauthentication") { - firePush(selfAuthenticationData, {}) - } else { - numberModel.continueWorkflow() - firePopAll() - navBar.lockedAndHidden = false - } - } - identifyProgressView.progressBarVisible = false - setIdentifyProgressViewValue(0) - break - default: - numberModel.continueWorkflow() - } - } - - function setIdentifyProgressViewValue(value){ - identifyProgressView.progressValue = value - } - - function setIdentifyWorkflowState(pState) { - identifyWorkflow.state = pState - } - - function setIdentifyWorkflowStateAndContinue(pState) { - setIdentifyWorkflowState(pState); - numberModel.continueWorkflow() - } - - function setIdentifyWorkflowStateAndRequestInput(pState, pInput) { - identifyWorkflow.state = pState - if (authModel.isBasicReader) { - firePush(enterPinView, {state: pInput}) - } else { - numberModel.continueWorkflow() - } - } -} diff --git a/resources/qml/Governikus/IdentifyView/IdentifyView.qml b/resources/qml/Governikus/IdentifyView/IdentifyView.qml deleted file mode 100644 index 7a07fa6..0000000 --- a/resources/qml/Governikus/IdentifyView/IdentifyView.qml +++ /dev/null @@ -1,114 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Layouts 1.2 -import QtQuick.Controls.Styles 1.4 - -import Governikus.EnterPinView 1.0 -import Governikus.Global 1.0 -import Governikus.TitleBar 1.0 -import Governikus.ProgressView 1.0 -import Governikus.ResultView 1.0 - -SectionPage -{ - id: identifyEditChatView - leftTitleBarAction: TitleBarAction { - state: applicationModel.currentWorkflow === "authentication" ? "cancel" : "" - onClicked: authModel.cancelWorkflow() - } - headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger; font.bold: true } - - content: IdentifyViewInfo { - id: identifyViewInfo - width: identifyEditChatView.width - height: identifyEditChatView.height - } - - IdentifyController { - id: identifyController - } - - IdentifyViewContent { - id: identifyViewContent - visible: false - } - - SelfAuthenticationData { - id: selfAuthenticationData - visible: false - } - - IdentifyWorkflow { - id: identifyWorkflow - visible: false - } - - EnterPinView { - id: enterPinView - leftTitleBarAction: TitleBarAction { state: "cancel"; onClicked: authModel.cancelWorkflow() } - headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger } - visible: false - - onPinEntered: { - numberModel.continueWorkflow() - firePush(identifyProgressView, {}) - } - } - - ProgressView { - id: identifyProgressView - state: identifyWorkflow.state - leftTitleBarAction: TitleBarAction { state: authModel.isBasicReader ? "cancel" : "hidden"; onClicked: authModel.cancelWorkflow() } - headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger; font.bold: true } - visible: false - text: qsTr("Authentication in progress") + settingsModel.translationTrigger - subText: (!visible ? "" : - authModel.isBasicReader ? - qsTr("Please wait a moment...") : - !!numberModel.inputError ? - numberModel.inputError : - numberModel.pinDeactivated ? - qsTr("The online identification function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the online identification function.") : - (state === "updateretrycounter" || state === "identify_enterpin") ? - qsTr("Please observe the display of your card reader.") : - (state === "identify_entercan") ? - qsTr("You have entered the wrong PIN twice. Prior to a third attempt, you have to enter your six-digit card access number first. You can find your card access number on the front of your ID card.") : - (state === "enterpuk") ? - qsTr("You have entered a wrong PIN three times. Your PIN is now blocked. You have to enter the PUK now for unblocking.") : - qsTr("Please wait a moment...") - ) + settingsModel.translationTrigger - subTextColor: !authModel.isBasicReader && (numberModel.inputError || numberModel.pinDeactivated || state === "identify_entercan" || state === "enterpuk") ? "red" : Constants.secondary_text - progressValue: 0 - progressText: (progressValue == 0 ? "" : - progressValue == 1 ? qsTr("Service provider is being verified") : - progressValue == 2 ? qsTr("Card is being verified") : - progressValue == 3 ? qsTr("Reading data") : - progressValue == 4 ? qsTr("Sending data to service provider") : - progressValue == 5 ? qsTr("Preparing results") : - "") + settingsModel.translationTrigger - progressBarVisible: false - } - - ProgressView { - id: checkConnectivityView - leftTitleBarAction: TitleBarAction { state: "cancel"; onClicked: authModel.cancelWorkflow() } - headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger; font.bold: true } - visible: false - text: qsTr("No network connectivity") + settingsModel.translationTrigger - subText: qsTr("Please enable the network interface or cancel the workflow.") + settingsModel.translationTrigger - subTextColor: Constants.red - } - - ResultView { - id: identifyResult - headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger; font.bold: true } - isError: authModel.resultString - text: authModel.resultString - onClicked: { - numberModel.continueWorkflow() - firePopAll() - navBar.lockedAndHidden = false - } - visible: false - } -} diff --git a/resources/qml/Governikus/IdentifyView/IdentifyViewContent.qml b/resources/qml/Governikus/IdentifyView/IdentifyViewContent.qml deleted file mode 100644 index 4b11736..0000000 --- a/resources/qml/Governikus/IdentifyView/IdentifyViewContent.qml +++ /dev/null @@ -1,3 +0,0 @@ -Item { - id: dummy -} diff --git a/resources/qml/Governikus/IdentifyView/IdentifyViewInfo.qml b/resources/qml/Governikus/IdentifyView/IdentifyViewInfo.qml deleted file mode 100644 index c41483c..0000000 --- a/resources/qml/Governikus/IdentifyView/IdentifyViewInfo.qml +++ /dev/null @@ -1,75 +0,0 @@ -import QtQuick 2.7 - -import Governikus.Global 1.0 -import Governikus.TitleBar 1.0 - - -Item { - id: baseItem - Column { - readonly property int maxWidth: width - 2 * Constants.pane_padding - width: baseItem.width - - id: root - spacing: Constants.component_spacing - padding: Constants.pane_padding - - Item { - height: childrenRect.height - width: root.maxWidth - Image { - id: useNpa - anchors.left: parent.left - width: parent.width * 0.4 - fillMode: Image.PreserveAspectFit - source: "qrc:///images/siteWithLogo.png" - } - - Text { - color: Constants.secondary_text - font.pixelSize: Constants.normal_font_size - anchors.verticalCenter: useNpa.verticalCenter - anchors.leftMargin: Constants.component_spacing - wrapMode: Text.WordWrap - anchors.left: useNpa.right - anchors.right: parent.right - text: qsTr("You can use your ID card anywhere you see this logo.") + settingsModel.translationTrigger - } - } - - Pane { - anchors.leftMargin: Constants.pane_padding - anchors.rightMargin: Constants.pane_padding - Text { - id: info - color: Constants.secondary_text - font.pixelSize: Constants.normal_font_size - wrapMode: Text.WordWrap - anchors.left: parent.left - anchors.right: parent.right - text: qsTr("Use the button 'See my personal data' to display the data stored on your ID card." - + " An Internet connection is required to display the data.") - + "

" - + qsTr("Your personal data is neither saved nor processed in any way. Please see our %1 for details on how your personal data is processed.") - .arg('' + qsTr("data privacy statement") + '') - + "" - + settingsModel.translationTrigger - onLinkActivated: Qt.openUrlExternally(link) - } - } - } - - GButton { - iconSource: "qrc:///images/npa.svg" - anchors.bottom: baseItem.bottom - anchors.bottomMargin: Constants.pane_padding - anchors.horizontalCenter: parent.horizontalCenter - buttonColor: settingsModel.useSelfauthenticationTestUri ? Constants.red : Constants.blue - text: qsTr("See my personal data") + settingsModel.translationTrigger - - enabled: applicationModel.currentWorkflow !== "authentication" - onClicked: { - selfAuthenticationModel.startWorkflow() - } - } -} diff --git a/resources/qml/Governikus/IdentifyView/IdentifyWorkflow.qml b/resources/qml/Governikus/IdentifyView/IdentifyWorkflow.qml deleted file mode 100644 index fe5dda9..0000000 --- a/resources/qml/Governikus/IdentifyView/IdentifyWorkflow.qml +++ /dev/null @@ -1,43 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 - -import Governikus.Global 1.0 -import Governikus.TitleBar 1.0 -import Governikus.Workflow 1.0 - -SectionPage -{ - id: baseItem - - leftTitleBarAction: TitleBarAction { - enabled: !(baseItem.state === "identify_enterpin" || - baseItem.state === "identify_entercan") - state: enabled ? "cancel" : "hidden" - onClicked: authModel.cancelWorkflow() - } - headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger; font.bold: true } - - NfcWorkflow - { - anchors.fill: parent - state: parent.state - visible: authModel.readerPlugInType === "NFC" - onRequestPluginType: authModel.readerPlugInType = pReaderPlugInType; - } - - RemoteWorkflow - { - anchors.fill: parent - state: parent.state - visible: authModel.readerPlugInType === "REMOTE" || authModel.readerPlugInType === "PCSC" - onRequestPluginType: authModel.readerPlugInType = pReaderPlugInType; - } - - BluetoothWorkflow - { - anchors.fill: parent - state: parent.state - visible: authModel.readerPlugInType === "BLUETOOTH" - onRequestPluginType: authModel.readerPlugInType = pReaderPlugInType; - } -} diff --git a/resources/qml/Governikus/IdentifyView/SelfAuthenticationData.qml b/resources/qml/Governikus/IdentifyView/SelfAuthenticationData.qml deleted file mode 100644 index d1ac5dd..0000000 --- a/resources/qml/Governikus/IdentifyView/SelfAuthenticationData.qml +++ /dev/null @@ -1,87 +0,0 @@ -import QtQuick 2.7 - -import Governikus.Global 1.0 -import Governikus.TitleBar 1.0 - -SectionPage { - id: root - leftTitleBarAction: TitleBarAction { state: "cancel"; onClicked: root.close() } - headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger; font.bold: true } - - function close() { - numberModel.continueWorkflow() - firePopAll() - navBar.lockedAndHidden = false - } - - content: Item { - height: content.height + okButton.height + Constants.component_spacing - width: root.width - - Column { - id: content - width: parent.width - padding: Constants.component_spacing - spacing: Constants.component_spacing - - Item { - id: message - height: Utils.dp(60) - width: resultImage.width + Constants.component_spacing + successText.width - anchors.horizontalCenter: parent.horizontalCenter - - Image { - id: resultImage - anchors.top: parent.top - anchors.verticalCenter: parent.verticalCenter - height: parent.height - width: height - fillMode: Image.PreserveAspectFit - source: "qrc:///images/gruener_Haken.svg" - } - - Text { - id: successText - anchors.left: resultImage.right - anchors.leftMargin: Constants.component_spacing - anchors.verticalCenter: resultImage.verticalCenter - text: qsTr("Successfull reading data") + settingsModel.translationTrigger - font.pixelSize: PlatformConstants.is_tablet ? Constants.header_font_size : Constants.normal_font_size - color: Constants.blue - } - } - - Pane { - id: pane - anchors.leftMargin: Constants.component_spacing - anchors.rightMargin: Constants.component_spacing - - Grid { - id: grid - width: parent.width - columns: PlatformConstants.is_tablet ? 3 : 1 - spacing: Utils.dp(15) - verticalItemAlignment: Grid.AlignBottom - Repeater { - model: selfAuthenticationModel - - LabeledText { - label: name - text: value === "" ? "---" : value - width: (pane.width - 2 * Constants.pane_padding - (grid.columns - 1) * grid.spacing) / grid.columns - } - } - } - } - } - } - - GButton { - id: okButton - anchors.horizontalCenter: parent.horizontalCenter - anchors.bottom: parent.bottom - anchors.bottomMargin: Constants.component_spacing - text: qsTr("OK") + settingsModel.translationTrigger - onClicked: root.close() - } -} diff --git a/resources/qml/Governikus/IdentifyView/qmldir b/resources/qml/Governikus/IdentifyView/qmldir index 603a937..8bb337b 100644 --- a/resources/qml/Governikus/IdentifyView/qmldir +++ b/resources/qml/Governikus/IdentifyView/qmldir @@ -1,2 +1,10 @@ module IdentifyView + +internal CertificateDescriptionPage CertificateDescriptionPage.qml +internal DataGroup DataGroup.qml +internal EditRights EditRights.qml +internal IdentifyController IdentifyController.qml +internal IdentifyViewContent IdentifyViewContent.qml +internal SelfAuthenticationData SelfAuthenticationData.qml + IdentifyView 1.0 IdentifyView.qml diff --git a/resources/qml/Governikus/InformationView/Information.qml b/resources/qml/Governikus/InformationView/Information.qml index 4ee95de..162e7a3 100644 --- a/resources/qml/Governikus/InformationView/Information.qml +++ b/resources/qml/Governikus/InformationView/Information.qml @@ -1,11 +1,10 @@ -import QtQuick 2.7 +import QtQuick 2.10 import QtQuick.Layouts 1.2 -import QtQuick.Controls 2.0 +import QtQuick.Controls 2.3 import Governikus.Global 1.0 import Governikus.TitleBar 1.0 -import Governikus.VersionInformationView 1.0 - +import Governikus.View 1.0 SectionPage { id: root @@ -29,7 +28,7 @@ SectionPage { spacing: Constants.component_spacing Text { width: parent.width - font.pixelSize: Utils.sp(18) + font.pixelSize: Utils.dp(18) color: Constants.blue wrapMode: Text.WordWrap text: titleText @@ -64,30 +63,11 @@ SectionPage { spacing: Constants.component_spacing padding: Constants.component_spacing - Text { - id: title - anchors.left: parent.left - anchors.right: parent.right - text: qsTr("You need help?") + settingsModel.translationTrigger - font.pixelSize: Constants.header_font_size - color: Constants.blue - wrapMode: Text.WordWrap - } - Text { - id: subtitle - color: Constants.secondary_text - anchors.left: parent.left - anchors.right: parent.right - text: qsTr("Here you are in the right place.") + settingsModel.translationTrigger - font.pixelSize: Constants.normal_font_size - wrapMode: Text.WordWrap - } - Pane { Loader { readonly property string titleText: qsTr("Version information") + settingsModel.translationTrigger readonly property string descriptionText: qsTr("Here you can see detailed information about AusweisApp2.") + settingsModel.translationTrigger - function onClickFunction() { firePush(versionInformationPage, {}) } + function onClickFunction() { firePush(versionInformationPage) } width: parent.width sourceComponent: subMenu } diff --git a/resources/qml/Governikus/InformationView/VersionInformation.qml b/resources/qml/Governikus/InformationView/VersionInformation.qml new file mode 100644 index 0000000..97d3157 --- /dev/null +++ b/resources/qml/Governikus/InformationView/VersionInformation.qml @@ -0,0 +1,40 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 + + +SectionPage +{ + id: root + leftTitleBarAction: TitleBarAction { state: "back"; onClicked: firePop() } + headerTitleBarAction: TitleBarAction { text: qsTr("Version Information") + settingsModel.translationTrigger; font.bold: true } + + content: Item + { + height: pane.height + 2 * Constants.component_spacing + width: root.width + + Column + { + anchors.fill: parent + anchors.margins: Constants.component_spacing + + Pane { + id: pane + + Repeater { + model: versionInformationModel + + LabeledText { + id: delegate + label: model.label + text: model.text + width: pane.width + } + } + } + } + } +} diff --git a/resources/qml/Governikus/InformationView/qmldir b/resources/qml/Governikus/InformationView/qmldir index 00cf3c7..0b05560 100644 --- a/resources/qml/Governikus/InformationView/qmldir +++ b/resources/qml/Governikus/InformationView/qmldir @@ -1,2 +1,4 @@ module InformationView + Information 1.0 Information.qml +VersionInformation 1.0 VersionInformation.qml diff --git a/resources/qml/Governikus/MainView/+desktop/MainView.qml b/resources/qml/Governikus/MainView/+desktop/MainView.qml new file mode 100644 index 0000000..51fdf2d --- /dev/null +++ b/resources/qml/Governikus/MainView/+desktop/MainView.qml @@ -0,0 +1,147 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.View 1.0 +import Governikus.Type.ApplicationModel 1.0 + +SectionPage { + id: sectionPage + + KeyNavigation.tab: tileIdentify + Accessible.role: Accessible.Grouping + Accessible.name: qsTr("Main view") + Accessible.description: qsTr("This is the main view of the AusweisApp2.") + + Item { + id: view + anchors.fill: parent + + readonly property int separatorLineWidth: Math.max(1, ApplicationModel.scaleFactor * 4) + readonly property int horizontalItemSpace: (width - 2 * separatorLineWidth) / 3 + readonly property int verticalItemSpace: (height - separatorLineWidth) / 2 + + Row { + height: parent.verticalItemSpace + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + + Tile { + id: tileIdentify + + height: parent.height + width: view.horizontalItemSpace + + title: qsTr("Identify") + image: "qrc:/images/desktop/main_identify.svg" + + onClicked: selfAuthModel.startWorkflow() + + KeyNavigation.tab: tileProvider + } + + Rectangle { + height: view.verticalItemSpace * 2/3 + width: view.separatorLineWidth + anchors.verticalCenter: parent.verticalCenter + color: Constants.grey_light + } + + Tile { + id: tileProvider + + height: view.verticalItemSpace + width: view.horizontalItemSpace + + title: qsTr("Provider") + image: "qrc:/images/desktop/main_provider.svg" + + onClicked: sectionPage.nextView(SectionPage.Views.Provider) + + KeyNavigation.tab: tileHistory + } + + Rectangle { + height: view.verticalItemSpace * 2/3 + width: view.separatorLineWidth + anchors.verticalCenter: parent.verticalCenter + color: Constants.grey_light + } + + Tile { + id: tileHistory + + height: view.verticalItemSpace + width: view.horizontalItemSpace + + title: qsTr("History") + image: "qrc:/images/desktop/main_history.svg" + + KeyNavigation.tab: tileSettings + } + } + + Rectangle { + height: view.separatorLineWidth + width: parent.width - view.horizontalItemSpace / 3 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + color: Constants.grey_light + } + + Row { + height: parent.verticalItemSpace + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + + Tile { + id: tileSettings + + height: view.verticalItemSpace + width: view.horizontalItemSpace + + title: qsTr("Settings") + image: "qrc:/images/desktop/settings_icon.svg" + + KeyNavigation.tab: tilePin + } + + Rectangle { + height: view.verticalItemSpace * 2/3 + width: view.separatorLineWidth + anchors.verticalCenter: parent.verticalCenter + color: Constants.grey_light + } + + Tile { + id: tilePin + + height: view.verticalItemSpace + width: view.horizontalItemSpace + + title: qsTr("PIN management") + image: "qrc:/images/desktop/main_pin.svg" + + KeyNavigation.tab: tileHelp + } + + Rectangle { + height: view.verticalItemSpace * 2/3 + width: view.separatorLineWidth + anchors.verticalCenter: parent.verticalCenter + color: Constants.grey_light + } + + Tile { + id: tileHelp + + height: view.verticalItemSpace + width: view.horizontalItemSpace + + title: qsTr("Help") + image: "qrc:/images/desktop/help_icon.svg" + + KeyNavigation.tab: sectionPage.navSuccessor + } + } + } +} diff --git a/resources/qml/Governikus/MainView/+desktop/Tile.qml b/resources/qml/Governikus/MainView/+desktop/Tile.qml new file mode 100644 index 0000000..e02bd4e --- /dev/null +++ b/resources/qml/Governikus/MainView/+desktop/Tile.qml @@ -0,0 +1,47 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.View 1.0 + +FocusScope { + id: tile + + property alias title: text.text + property alias image: image.source + + signal clicked() + + Accessible.role: Accessible.Button + Accessible.name: title + + Keys.onSpacePressed: tile.clicked() + + MouseArea { + anchors.fill: parent + onPressed: tile.focus = true + onClicked: tile.clicked() + } + + FocusFrame { + marginFactor: -2 + } + + Column { + anchors.centerIn: parent + spacing: text.height / 2 + + Image { + id: image + sourceSize.height: text.height * 4 + anchors.horizontalCenter: parent.horizontalCenter + } + + GText { + id: text + anchors.horizontalCenter: parent.horizontalCenter + color: Constants.white + font.pixelSize: Constants.header_font_size + font.weight: Font.Bold + } + } +} diff --git a/resources/qml/Governikus/MainView/+mobile/MainView.qml b/resources/qml/Governikus/MainView/+mobile/MainView.qml new file mode 100644 index 0000000..1a0e999 --- /dev/null +++ b/resources/qml/Governikus/MainView/+mobile/MainView.qml @@ -0,0 +1,84 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.TitleBar 1.0 +import Governikus.Type.ApplicationModel 1.0 + + +Item { + id: baseItem + + Column { + readonly property int maxWidth: width - 2 * Constants.pane_padding + width: baseItem.width + + id: root + spacing: Constants.component_spacing + padding: Constants.pane_padding + + Item { + height: childrenRect.height + width: root.maxWidth + + Image { + readonly property double ratio: 1080 / 815 + readonly property int maxHeight: baseItem.height - 4 * Constants.component_spacing - textPane.height - selfAuthButton.height + + id: useNpa + anchors.left: parent.left + width: Math.min(maxHeight * ratio, parent.width * 0.4) + fillMode: Image.PreserveAspectFit + source: "qrc:///images/siteWithLogo.png" + } + + Text { + color: Constants.secondary_text + font.pixelSize: Constants.normal_font_size + anchors.verticalCenter: useNpa.verticalCenter + anchors.leftMargin: Constants.component_spacing + wrapMode: Text.WordWrap + anchors.left: useNpa.right + anchors.right: parent.right + text: qsTr("You can use your ID card anywhere you see this logo.") + settingsModel.translationTrigger + } + } + + Pane { + id: textPane + anchors.leftMargin: Constants.pane_padding + anchors.rightMargin: Constants.pane_padding + + Text { + id: info + color: Constants.secondary_text + font.pixelSize: Constants.normal_font_size + wrapMode: Text.WordWrap + anchors.left: parent.left + anchors.right: parent.right + text: qsTr("Use the button 'See my personal data' to display the data stored on your ID card." + + " An Internet connection is required to display the data.") + + "

" + + qsTr("Your personal data is neither saved nor processed in any way. Please see our %1 for details on how your personal data is processed.") + .arg('' + qsTr("data privacy statement") + '') + + "" + + settingsModel.translationTrigger + onLinkActivated: Qt.openUrlExternally(link) + } + } + } + + GButton { + id: selfAuthButton + iconSource: "qrc:///images/npa.svg" + anchors.bottom: baseItem.bottom + anchors.bottomMargin: Constants.pane_padding + anchors.horizontalCenter: parent.horizontalCenter + buttonColor: settingsModel.useSelfauthenticationTestUri ? Constants.red : Constants.blue + text: qsTr("See my personal data") + settingsModel.translationTrigger + + enabled: ApplicationModel.currentWorkflow !== "authentication" + onClicked: { + selfAuthModel.startWorkflow() + } + } +} diff --git a/resources/qml/Governikus/MainView/qmldir b/resources/qml/Governikus/MainView/qmldir new file mode 100644 index 0000000..91f4e63 --- /dev/null +++ b/resources/qml/Governikus/MainView/qmldir @@ -0,0 +1,5 @@ +module MainView + +internal Tile Tile.qml + +MainView 1.0 MainView.qml diff --git a/resources/qml/Governikus/MoreView/MoreView.qml b/resources/qml/Governikus/MoreView/MoreView.qml index f8e8371..bff8f86 100644 --- a/resources/qml/Governikus/MoreView/MoreView.qml +++ b/resources/qml/Governikus/MoreView/MoreView.qml @@ -1,28 +1,24 @@ -import QtQuick 2.5 +import QtQuick 2.10 import QtQuick.Layouts 1.2 import Governikus.Global 1.0 import Governikus.TitleBar 1.0 -import Governikus.VersionInformationView 1.0 +import Governikus.FeedbackView 1.0 +import Governikus.InformationView 1.0 import Governikus.RemoteServiceView 1.0 import Governikus.DeveloperView 1.0 +import Governikus.TutorialView 1.0 +import Governikus.View 1.0 +import Governikus.Type.RemoteServiceModel 1.0 SectionPage { headerTitleBarAction: TitleBarAction { text: qsTr("More") + settingsModel.translationTrigger; font.bold: true } - Rectangle { anchors.fill: menu color: "white" } - - onVisibleChanged: { - if (visible) { - remoteServiceModel.detectRemoteDevices = false - } - } - Column { id: menu width: parent.width @@ -33,7 +29,14 @@ SectionPage { text: qsTr("Version information") + settingsModel.translationTrigger imageSource: "qrc:///images/npa.svg" showRightArrow: true - onClicked: firePush(versionInformationPage, {}) + onClicked: firePush(versionInformationPage) + } + + MoreViewMenuItem { + text: qsTr("Tutorial") + settingsModel.translationTrigger + imageSource: "qrc:///images/iOS/more/icon_mehr_tutorial.svg" + showRightArrow: true + onClicked: firePush(tutorialView) } MoreViewMenuItem { @@ -79,21 +82,27 @@ SectionPage { } MoreViewMenuItem { - imageSource: "qrc:///images/android/navigation/remotesettings.svg" + imageSource: "qrc:///images/iOS/more/icon_mehr_remotereader" text: qsTr("Configure remote service") + settingsModel.translationTrigger showRightArrow: true onClicked: { - remoteServiceModel.detectRemoteDevices = true - firePush(remoteServiceSettings, {}) + firePush(remoteServiceView) } } + MoreViewMenuItem { + text: qsTr("Show log") + settingsModel.translationTrigger + imageSource: "qrc:///images/iOS/more/icon_mehr_log.svg" + showRightArrow: true + onClicked: firePush(logView) + } + MoreViewMenuItem { visible: plugin.developerBuild text: qsTr("Developer options") + settingsModel.translationTrigger imageSource: "qrc:///images/zahnraeder.svg" showRightArrow: true - onClicked: firePush(developerView, {}) + onClicked: firePush(developerView) } } @@ -143,8 +152,8 @@ SectionPage { visible: false } - RemoteServiceSettings { - id: remoteServiceSettings + RemoteServiceView { + id: remoteServiceView visible: false } @@ -152,4 +161,14 @@ SectionPage { id: developerView visible: false } + + Log { + id: logView + visible: false + } + + TutorialView { + id: tutorialView + visible: false + } } diff --git a/resources/qml/Governikus/MoreView/MoreViewMenuItem.qml b/resources/qml/Governikus/MoreView/MoreViewMenuItem.qml index d6f4740..6238d51 100644 --- a/resources/qml/Governikus/MoreView/MoreViewMenuItem.qml +++ b/resources/qml/Governikus/MoreView/MoreViewMenuItem.qml @@ -1,6 +1,4 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Layouts 1.2 +import QtQuick 2.10 import Governikus.Global 1.0 @@ -57,6 +55,5 @@ Item { height: 1 color: Constants.grey } - } diff --git a/resources/qml/Governikus/MoreView/qmldir b/resources/qml/Governikus/MoreView/qmldir index 5a542d2..81c2505 100644 --- a/resources/qml/Governikus/MoreView/qmldir +++ b/resources/qml/Governikus/MoreView/qmldir @@ -1,2 +1,5 @@ module MoreView + +internal MoreViewMenuItem MoreViewMenuItem.qml + MoreView 1.0 MoreView.qml diff --git a/resources/qml/Governikus/Navigation/+android/Navigation.qml b/resources/qml/Governikus/Navigation/+android/Navigation.qml index 1a2e118..6e0ebcb 100644 --- a/resources/qml/Governikus/Navigation/+android/Navigation.qml +++ b/resources/qml/Governikus/Navigation/+android/Navigation.qml @@ -1,17 +1,22 @@ -import QtQuick 2.5 -import QtQuick.Controls 2.0 +import QtQuick 2.10 +import QtQuick.Controls 2.3 import Governikus.Global 1.0 Item { + property int leftOverlayMargin: 0 id: navigation - state: "identify" - width: !PlatformConstants.is_tablet || lockedAndHidden ? 0 : Constants.menubar_width + width: !Constants.is_tablet || lockedAndHidden ? 0 : Constants.menubar_width enabled: !lockedAndHidden property bool lockedAndHidden: true property bool isOpen: drawer.position > 0 - property int currentIndex: 0 + property int currentIndex + + Component.onCompleted: { + state = settingsModel.showTutorialOnStart ? "tutorial" : "identify" + currentIndex = settingsModel.showTutorialOnStart ? 5 : 0 + } onLockedAndHiddenChanged: { if (lockedAndHidden) { @@ -19,6 +24,10 @@ Item { } } + onLeftOverlayMarginChanged: { + Overlay.overlay.x = leftOverlayMargin + } + function open() { if (!lockedAndHidden) { drawer.open() @@ -30,11 +39,11 @@ Item { } Rectangle { - visible: PlatformConstants.is_tablet + visible: Constants.is_tablet anchors.top: parent.top anchors.left: parent.left anchors.bottom: parent.bottom - width: Math.max(parent.width, appWindow.leftOverlayMargin); + width: Math.max(parent.width, navigation.leftOverlayMargin); color: Constants.background_color clip: true @@ -48,38 +57,35 @@ Item { anchors.top: parent.top anchors.bottom: parent.bottom width: Utils.dp(2) - color: PlatformConstants.grey_border + color: Constants.grey_border } } Drawer { id: drawer - topPadding: Constants.titlebar_height - height: appWindow.height + x: Constants.is_tablet ? Constants.menubar_width : 0 + y: Constants.titlebar_height + height: navigation.height width: Utils.dp(250) - opacity: PlatformConstants.is_tablet ? 0 : 1 - background: Item { - opacity: 0 - visible: !lockedAndHidden && drawer.position > 0 - - MouseArea { - anchors.fill: parent - onClicked: drawer.close() - } - } - + opacity: Constants.is_tablet ? 0 : 1 dragMargin: lockedAndHidden ? 0 : Utils.dp(Qt.styleHints.startDragDistance) onPositionChanged: { - if (PlatformConstants.is_tablet && position > 0) { - appWindow.leftOverlayMargin = Constants.menubar_width + (width - Constants.menubar_width) * position + if (Constants.is_tablet && position > 0) { + navigation.leftOverlayMargin = Constants.menubar_width + (width - Constants.menubar_width) * position } else { - appWindow.leftOverlayMargin = 0 + navigation.leftOverlayMargin = 0 } } contentItem: NavigationView { navigationController: navigation + + MouseArea { + enabled: Constants.is_tablet + anchors.fill: parent + onClicked: drawer.close() + } } } } diff --git a/resources/qml/Governikus/Navigation/+android/NavigationItem.qml b/resources/qml/Governikus/Navigation/+android/NavigationItem.qml index 0c30c52..69400bb 100644 --- a/resources/qml/Governikus/Navigation/+android/NavigationItem.qml +++ b/resources/qml/Governikus/Navigation/+android/NavigationItem.qml @@ -1,4 +1,4 @@ -import QtQuick 2.5 +import QtQuick 2.10 import Governikus.Global 1.0 diff --git a/resources/qml/Governikus/Navigation/+android/NavigationView.qml b/resources/qml/Governikus/Navigation/+android/NavigationView.qml index de6c63b..94f16c5 100644 --- a/resources/qml/Governikus/Navigation/+android/NavigationView.qml +++ b/resources/qml/Governikus/Navigation/+android/NavigationView.qml @@ -1,5 +1,5 @@ -import QtQuick 2.5 -import QtQuick.Controls 2.0 +import QtQuick 2.10 +import QtQuick.Controls 2.3 import Governikus.Global 1.0 @@ -44,6 +44,12 @@ Rectangle { condition: "remoteservice" } + ListElement { + image: "qrc:///images/android/navigation/tutorial.svg" + desc: QT_TR_NOOP("Tutorial") + condition: "tutorial" + } + ListElement { image: "qrc:///images/android/navigation/support.svg" desc: QT_TR_NOOP("Help & Feedback") diff --git a/resources/qml/Governikus/Navigation/+ios/Navigation.qml b/resources/qml/Governikus/Navigation/+ios/Navigation.qml index e5111ce..27b22bb 100644 --- a/resources/qml/Governikus/Navigation/+ios/Navigation.qml +++ b/resources/qml/Governikus/Navigation/+ios/Navigation.qml @@ -1,4 +1,4 @@ -import QtQuick 2.5 +import QtQuick 2.10 import Governikus.Global 1.0 diff --git a/resources/qml/Governikus/Navigation/+ios/NavigationItem.qml b/resources/qml/Governikus/Navigation/+ios/NavigationItem.qml index d512667..d196a6a 100644 --- a/resources/qml/Governikus/Navigation/+ios/NavigationItem.qml +++ b/resources/qml/Governikus/Navigation/+ios/NavigationItem.qml @@ -1,4 +1,4 @@ -import QtQuick 2.5 +import QtQuick 2.10 import Governikus.Global 1.0 diff --git a/resources/qml/Governikus/Navigation/+ios/NavigationView.qml b/resources/qml/Governikus/Navigation/+ios/NavigationView.qml index d1467fa..7c0c84e 100644 --- a/resources/qml/Governikus/Navigation/+ios/NavigationView.qml +++ b/resources/qml/Governikus/Navigation/+ios/NavigationView.qml @@ -1,4 +1,4 @@ -import QtQuick 2.5 +import QtQuick 2.10 import Governikus.Global 1.0 diff --git a/resources/qml/Governikus/Navigation/Navigation.qml b/resources/qml/Governikus/Navigation/Navigation.qml deleted file mode 100644 index 4b11736..0000000 --- a/resources/qml/Governikus/Navigation/Navigation.qml +++ /dev/null @@ -1,3 +0,0 @@ -Item { - id: dummy -} diff --git a/resources/qml/Governikus/Navigation/NavigationItem.qml b/resources/qml/Governikus/Navigation/NavigationItem.qml deleted file mode 100644 index 4b11736..0000000 --- a/resources/qml/Governikus/Navigation/NavigationItem.qml +++ /dev/null @@ -1,3 +0,0 @@ -Item { - id: dummy -} diff --git a/resources/qml/Governikus/Navigation/NavigationView.qml b/resources/qml/Governikus/Navigation/NavigationView.qml deleted file mode 100644 index 4b11736..0000000 --- a/resources/qml/Governikus/Navigation/NavigationView.qml +++ /dev/null @@ -1,3 +0,0 @@ -Item { - id: dummy -} diff --git a/resources/qml/Governikus/Navigation/qmldir b/resources/qml/Governikus/Navigation/qmldir index 5be0692..6503c13 100644 --- a/resources/qml/Governikus/Navigation/qmldir +++ b/resources/qml/Governikus/Navigation/qmldir @@ -1,2 +1,6 @@ module Navigation + +internal NavigationItem NavigationItem.qml +internal NavigationView NavigationView.qml + Navigation 1.0 Navigation.qml diff --git a/resources/qml/Governikus/PinView/+android/PinViewContent.qml b/resources/qml/Governikus/PinView/+android/PinViewContent.qml deleted file mode 100644 index 024e9bc..0000000 --- a/resources/qml/Governikus/PinView/+android/PinViewContent.qml +++ /dev/null @@ -1,63 +0,0 @@ -import QtQuick 2.5 - -import Governikus.Global 1.0 - - -Item { - readonly property int spacing: (height - pinIcon.height - pinHeader.height - pinDesc.height - govButton.height - Utils.dp(40)) / 3 - - Image { - id: pinIcon - - height: parent.height * 0.25 - width: height - - anchors.top: parent.top - anchors.topMargin: spacing - anchors.horizontalCenter: parent.horizontalCenter - - fillMode: Image.PreserveAspectFit - smooth: true - source: "qrc:///images/icon_Pin.svg" - } - - Text { - id: pinHeader - - text: qsTr("PIN Management") + settingsModel.translationTrigger - - anchors.top: pinIcon.bottom - anchors.topMargin: spacing - anchors.horizontalCenter: parent.horizontalCenter - - font.pixelSize: Constants.header_font_size - color: Constants.blue - } - - Text { - id: pinDesc - color: Constants.secondary_text - - anchors.margins: Utils.dp(10) - anchors.top: pinHeader.bottom - anchors.topMargin: Utils.dp(10) - anchors.horizontalCenter: parent.horizontalCenter - - text: qsTr("You have the opportunity to change your transport PIN into a personal PIN. You can also change the PIN at any time or unblock the PIN using the personal unblocking key (PUK). The transport PIN and the PUK can be found in the letter sent to you by your competent authority.") + settingsModel.translationTrigger - font.pixelSize: Constants.normal_font_size - horizontalAlignment: Text.AlignHCenter - width: parent.width - Utils.dp(60) - wrapMode: Text.WordWrap - } - - GButton { - id: govButton - - anchors.bottom: parent.bottom - anchors.horizontalCenter: parent.horizontalCenter - anchors.bottomMargin: Utils.dp(30) - - text: qsTr("Change PIN now") + settingsModel.translationTrigger - onClicked: changePinModel.startWorkflow() - } -} diff --git a/resources/qml/Governikus/PinView/+ios/PinViewContent.qml b/resources/qml/Governikus/PinView/+ios/PinViewContent.qml deleted file mode 100644 index f3ce948..0000000 --- a/resources/qml/Governikus/PinView/+ios/PinViewContent.qml +++ /dev/null @@ -1,63 +0,0 @@ -import QtQuick 2.5 - -import Governikus.Global 1.0 - - -Item { - readonly property int spacing: (height - pinHeader.height - pinDesc.height - pinIcon.height - govButton.height - Utils.dp(40)) / 3 - - Text { - id: pinHeader - - text: qsTr("PIN Management") + settingsModel.translationTrigger - - anchors.top: parent.top - anchors.topMargin: spacing - anchors.horizontalCenter: parent.horizontalCenter - - font.pixelSize: Constants.header_font_size - color: Constants.blue - } - - Text { - id: pinDesc - color: Constants.secondary_text - - width: parent.width * 0.9 - - text: qsTr("You have the opportunity to change your transport PIN into a personal PIN. You can also change the PIN at any time or unblock the PIN using the personal unblocking key (PUK). The transport PIN and the PUK can be found in the letter sent to you by your competent authority.") + settingsModel.translationTrigger - font.pixelSize: Constants.normal_font_size - horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap - - anchors.top: pinHeader.bottom - anchors.topMargin: Utils.dp(10) - anchors.horizontalCenter: parent.horizontalCenter - } - - Image { - id: pinIcon - - height: parent.height * 0.25 - width: height - - anchors.top: pinDesc.bottom - anchors.topMargin: spacing - anchors.horizontalCenter: parent.horizontalCenter - - fillMode: Image.PreserveAspectFit - smooth: true - source: "qrc:///images/icon_Pin.svg" - } - - GButton { - id: govButton - - anchors.bottom: parent.bottom - anchors.horizontalCenter: parent.horizontalCenter - anchors.bottomMargin: Utils.dp(30) - - text: qsTr("Change PIN now") + settingsModel.translationTrigger - onClicked: changePinModel.startWorkflow() - } -} diff --git a/resources/qml/Governikus/PinView/ChangePinController.qml b/resources/qml/Governikus/PinView/ChangePinController.qml deleted file mode 100644 index d41c24d..0000000 --- a/resources/qml/Governikus/PinView/ChangePinController.qml +++ /dev/null @@ -1,108 +0,0 @@ -import QtQuick 2.5 - -Item { - id: controller - readonly property string currentState: changePinModel.currentState - property bool showRemoveCardFeedback: false - - property bool locationPermissionConfirmed: false - onLocationPermissionConfirmedChanged: { - // If the user has given location permission: continue Bluetooth workflow. - if (changePinModel.readerPlugInType === "BLUETOOTH" && applicationModel.bluetoothEnabled && applicationModel.locationPermissionRequired && locationPermissionConfirmed) { - numberModel.continueWorkflow() - } - } - - Connections { - target: changePinModel - - onFireNewContextSet: { - if (pinView.stack.currentItem !== pinWorkflow) - { - baseItem.firePopAll() - baseItem.firePush(pinWorkflow, {}) - - changePinModel.setInitialPluginType() - } - - navBar.lockedAndHidden = true - enterPinView.state = "INITIAL" - setPinWorkflowState("initial") - } - - onFireCurrentStateChanged: processStateChange() - // This is necessary because onCurrentStateChanged is not - // working, when we need to process a state a second time - } - - function processStateChange() { - switch (changePinModel.currentState) { - case "": - break - case "StateSelectReader": - firePush(pinWorkflow, {}) - if (changePinModel.readerPlugInType === "BLUETOOTH" && applicationModel.bluetoothEnabled && applicationModel.locationPermissionRequired && !locationPermissionConfirmed) { - // Stop the workflow here until the user has confirmed the location permission. - setPinWorkflowState("reader") - } - else - { - setPinWorkflowStateAndContinue("reader") - } - break - case "StateConnectCard": - setPinWorkflowStateAndContinue("card") - break - case "StateHandleRetryCounter": - if (!changePinModel.isBasicReader) { - firePush(pinProgressView, {}) - } - setPinWorkflowStateAndContinue("updateretrycounter") - break - case "StateEstablishPacePuk": - setPinWorkflowStateAndRequestInput("enterpuk", "PUK") - break - case "StateEstablishPaceCan": - setPinWorkflowStateAndRequestInput("changepin_entercan", "CAN") - break - case "StateEstablishPacePin": - setPinWorkflowStateAndRequestInput("changepin_enterpin", "PIN_OR_TRANSPORT_PIN") - break - case "StateChangePin": - setPinWorkflowStateAndRequestInput("enternewpin", "PIN_NEW") - break - case "StateCleanUpReaderManager": - controller.showRemoveCardFeedback = numberModel.cardConnected && !changePinModel.error; - numberModel.continueWorkflow() - break; - case "FinalState": - if (controller.showRemoveCardFeedback) { - controller.showRemoveCardFeedback = false - qmlExtension.showFeedback(qsTr("You may now remove your ID card from the device.")) - } - baseItem.firePush(pinResult, {}) - navBar.lockedAndHidden = true - break - default: - numberModel.continueWorkflow() - } - } - - function setPinWorkflowState(pState) { - pinWorkflow.state = pState - } - - function setPinWorkflowStateAndContinue(pState) { - setPinWorkflowState(pState) - numberModel.continueWorkflow() - } - - function setPinWorkflowStateAndRequestInput(pState, pInput) { - pinWorkflow.state = pState - if (changePinModel.isBasicReader) { - baseItem.firePush(enterPinView, {state: pInput}) - } else { - numberModel.continueWorkflow() - } - } -} diff --git a/resources/qml/Governikus/PinView/PinView.qml b/resources/qml/Governikus/PinView/PinView.qml deleted file mode 100644 index bdf4a77..0000000 --- a/resources/qml/Governikus/PinView/PinView.qml +++ /dev/null @@ -1,83 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Layouts 1.2 -import QtQuick.Controls.Styles 1.4 - -import QtQml.StateMachine 1.0 as DSM - -import Governikus.EnterPinView 1.0 -import Governikus.Global 1.0 -import Governikus.TitleBar 1.0 -import Governikus.ProgressView 1.0 -import Governikus.ResultView 1.0 - -SectionPage { - id: baseItem - - disableFlicking: true - headerTitleBarAction: TitleBarAction { text: qsTr("PIN Management") + settingsModel.translationTrigger; font.bold: true } - - ChangePinController { - id: changePinController - } - - content: PinViewContent { - height: baseItem.height - width: baseItem.width - } - - PinWorkflow { - id: pinWorkflow - visible: false - } - - ResultView { - id: pinResult - headerTitleBarAction: TitleBarAction { text: qsTr("PIN Management") + settingsModel.translationTrigger; font.bold: true } - isError: changePinModel.error - text: changePinModel.resultString - onClicked: { - numberModel.continueWorkflow() - firePopAll() - navBar.lockedAndHidden = false - } - visible: false - } - - EnterPinView { - id: enterPinView - leftTitleBarAction: TitleBarAction { state: "cancel"; onClicked: changePinModel.cancelWorkflow() } - headerTitleBarAction: TitleBarAction { text: qsTr("Change PIN") + settingsModel.translationTrigger } - visible: false - - onPinEntered: { - numberModel.continueWorkflow() - firePush(pinProgressView, {}) - } - } - - ProgressView { - id: pinProgressView - state: pinWorkflow.state - leftTitleBarAction: TitleBarAction { state: changePinModel.isBasicReader ? "cancel" : "hidden"; onClicked: changePinModel.cancelWorkflow() } - headerTitleBarAction: TitleBarAction { text: qsTr("PIN Management") + settingsModel.translationTrigger; font.bold: true } - visible: false - text: qsTr("Change PIN") + settingsModel.translationTrigger - subText: (!visible ? "" : - changePinModel.isBasicReader ? - qsTr("Please wait a moment...") : - !!numberModel.inputError ? - numberModel.inputError : - numberModel.pinDeactivated ? - qsTr("The online identification function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the online identification function.") : - (state === "updateretrycounter" || state === "changepin_enterpin" || state === "enternewpin") ? - qsTr("Please observe the display of your card reader.") : - (state === "changepin_entercan") ? - qsTr("You have entered the wrong PIN twice. Prior to a third attempt, you have to enter your six-digit card access number first. You can find your card access number on the front of your ID card.") : - (state === "enterpuk") ? - qsTr("You have entered a wrong PIN three times. Your PIN is now blocked. You have to enter the PUK now for unblocking.") : - qsTr("Please wait a moment...") - ) + settingsModel.translationTrigger - subTextColor: !changePinModel.isBasicReader && (numberModel.inputError || numberModel.pinDeactivated || state === "changepin_entercan" || state === "enterpuk") ? "red" : Constants.secondary_text - } -} diff --git a/resources/qml/Governikus/PinView/PinViewContent.qml b/resources/qml/Governikus/PinView/PinViewContent.qml deleted file mode 100644 index 4b11736..0000000 --- a/resources/qml/Governikus/PinView/PinViewContent.qml +++ /dev/null @@ -1,3 +0,0 @@ -Item { - id: dummy -} diff --git a/resources/qml/Governikus/PinView/PinWorkflow.qml b/resources/qml/Governikus/PinView/PinWorkflow.qml deleted file mode 100644 index c62f8b9..0000000 --- a/resources/qml/Governikus/PinView/PinWorkflow.qml +++ /dev/null @@ -1,43 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 - -import Governikus.Global 1.0 -import Governikus.TitleBar 1.0 -import Governikus.Workflow 1.0 - -SectionPage -{ - id: baseItem - - leftTitleBarAction: TitleBarAction { - enabled: !(baseItem.state === "changepin_enterpin" || - baseItem.state === "changepin_entercan") - state: enabled ? "cancel" : "hidden" - onClicked: changePinModel.cancelWorkflow() - } - headerTitleBarAction: TitleBarAction { text: qsTr("PIN Management") + settingsModel.translationTrigger; font.bold: true } - - NfcWorkflow - { - anchors.fill: parent - state: parent.state - visible: changePinModel.readerPlugInType === "NFC" - onRequestPluginType: changePinModel.readerPlugInType = pReaderPlugInType; - } - - RemoteWorkflow - { - anchors.fill: parent - state: parent.state - visible: changePinModel.readerPlugInType === "REMOTE" || changePinModel.readerPlugInType === "PCSC" - onRequestPluginType: changePinModel.readerPlugInType = pReaderPlugInType; - } - - BluetoothWorkflow - { - anchors.fill: parent - state: parent.state - visible: changePinModel.readerPlugInType === "BLUETOOTH" - onRequestPluginType: changePinModel.readerPlugInType = pReaderPlugInType; - } -} diff --git a/resources/qml/Governikus/PinView/qmldir b/resources/qml/Governikus/PinView/qmldir deleted file mode 100644 index 15b9514..0000000 --- a/resources/qml/Governikus/PinView/qmldir +++ /dev/null @@ -1,2 +0,0 @@ -module PinView -PinView 1.0 PinView.qml diff --git a/resources/qml/Governikus/ProgressView/+desktop/ProgressView.qml b/resources/qml/Governikus/ProgressView/+desktop/ProgressView.qml new file mode 100644 index 0000000..4540fdd --- /dev/null +++ b/resources/qml/Governikus/ProgressView/+desktop/ProgressView.qml @@ -0,0 +1,95 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.2 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.View 1.0 +import Governikus.Type.ApplicationModel 1.0 + + +SectionPage +{ + id: baseItem + property alias text: text.text + property alias subText: subText.text + property alias subTextColor: subText.color + property alias progressText: progressText.text + property int progressValue + property alias progressBarVisible: progressBar.visible + + StatusIcon { + id: circle + height: ApplicationModel.scaleFactor * 400 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.top + anchors.verticalCenterOffset: baseItem.height / 4 + busy: true + source: "qrc:///images/desktop/sandglass.svg" + } + + Text { + id: text + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.verticalCenter + width: parent.width - (2 * Constants.pane_padding) + + font.pixelSize: Constants.header_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + color: Constants.white + onLinkActivated: Qt.openUrlExternally(link) + } + + Text { + id: subText + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: text.bottom + width: parent.width - (2 * Constants.pane_padding) + + font.pixelSize: Constants.header_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + color: Constants.secondary_text + onLinkActivated: Qt.openUrlExternally(link) + } + + Text { + id: progressText + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Constants.normal_font_size + anchors.bottom: progressBar.top + anchors.bottomMargin: ApplicationModel.scaleFactor * 20 + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width - (2 * Constants.pane_padding) + wrapMode: Text.WordWrap + color: Constants.white + } + + ProgressBar { + id: progressBar + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: ApplicationModel.scaleFactor * 80 + height: ApplicationModel.scaleFactor * 40 + from: 0 + to: 5 + visible: false + value: progressValue + + background: Rectangle { + radius: ApplicationModel.scaleFactor * 8 + color: Constants.lightgrey + } + + contentItem: Item { + Rectangle { + width: progressBar.visualPosition * parent.width + height: parent.height + radius: ApplicationModel.scaleFactor * 8 + color: Constants.green + } + } + } +} diff --git a/resources/qml/Governikus/ProgressView/+mobile/ProgressView.qml b/resources/qml/Governikus/ProgressView/+mobile/ProgressView.qml new file mode 100644 index 0000000..7a35846 --- /dev/null +++ b/resources/qml/Governikus/ProgressView/+mobile/ProgressView.qml @@ -0,0 +1,95 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.2 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.View 1.0 + +SectionPage +{ + id: baseItem + property alias text: text.text + property alias subText: subText.text + property alias subTextColor: subText.color + property alias progressText: progressText.text + property int progressValue + property alias progressBarVisible: progressBar.visible + + BusyIndicator { + id: busyIndicator + anchors.fill: circle + running: baseItem.visible + contentItem: NpaBusyIndicatorStyle { factor: 1.2 } + } + Rectangle { + id: circle + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.top + anchors.verticalCenterOffset: Constants.is_tablet ? parent.height / 4 : parent.width / 2 + width: parent.height / 4 + height: width + radius: width / 2 + color: Constants.blue + } + Text { + id: text + anchors.top: circle.bottom + anchors.topMargin: Utils.dp(50) + anchors.horizontalCenter: parent.horizontalCenter + font.pixelSize: Constants.header_font_size + font.weight: Font.Bold + color: Constants.blue + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + width: baseItem.width * 0.8 + } + Text { + id: subText + color: Constants.secondary_text + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Constants.normal_font_size + anchors.top: text.bottom + anchors.topMargin: Utils.dp(10) + anchors.horizontalCenter: parent.horizontalCenter + width: baseItem.width * 0.8 + wrapMode: Text.WordWrap + } + Text { + id: progressText + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Constants.normal_font_size + anchors.top: subText.bottom + anchors.topMargin: Utils.dp(20) + anchors.horizontalCenter: parent.horizontalCenter + width: baseItem.width * 0.8 + wrapMode: Text.WordWrap + color: Constants.grey + } + ProgressBar { + id: progressBar + anchors.top: progressText.bottom + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: Utils.dp(10) + width: baseItem.width * 0.6 + height: Utils.dp(24) + from: 0 + to: 5 + visible: false + value: progressValue + + background: Rectangle { + radius: Utils.dp(2) + color: Constants.lightgrey + } + + contentItem: Item { + Rectangle { + width: progressBar.visualPosition * parent.width + height: parent.height + radius: Utils.dp(2) + color: Constants.green + } + } + } +} diff --git a/resources/qml/Governikus/ProgressView/ProgressView.qml b/resources/qml/Governikus/ProgressView/ProgressView.qml deleted file mode 100644 index 394fbf7..0000000 --- a/resources/qml/Governikus/ProgressView/ProgressView.qml +++ /dev/null @@ -1,91 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 -import QtQuick.Layouts 1.2 - -import Governikus.Global 1.0 -import Governikus.Style 1.0 - -SectionPage -{ - id: baseItem - property alias text: text.text - property alias subText: subText.text - property alias subTextColor: subText.color - property alias progressText: progressText.text - property int progressValue - property alias progressBarVisible: progressBar.visible - - BusyIndicator { - id: busyIndicator - anchors.fill: circle - running: baseItem.visible - style: NpaBusyIndicatorStyle { factor: 1.2 } - } - Rectangle { - id: circle - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.top - anchors.verticalCenterOffset: PlatformConstants.is_tablet ? parent.height / 4 : parent.width / 2 - width: parent.height / 4 - height: width - radius: width / 2 - color: Constants.blue - } - Text { - id: text - anchors.top: circle.bottom - anchors.topMargin: Utils.dp(50) - anchors.horizontalCenter: parent.horizontalCenter - font.pixelSize: Constants.header_font_size - font.weight: Font.Bold - color: Constants.blue - } - Text { - id: subText - color: Constants.secondary_text - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - font.pixelSize: Constants.normal_font_size - anchors.top: text.bottom - anchors.topMargin: Utils.dp(10) - anchors.horizontalCenter: parent.horizontalCenter - width: baseItem.width * 0.8 - wrapMode: Text.WordWrap - } - Text { - id: progressText - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - font.pixelSize: Constants.normal_font_size - anchors.top: subText.bottom - anchors.topMargin: Utils.dp(20) - anchors.horizontalCenter: parent.horizontalCenter - width: baseItem.width * 0.8 - wrapMode: Text.WordWrap - color: Constants.grey - } - ProgressBar { - id: progressBar - anchors.top: progressText.bottom - anchors.horizontalCenter: parent.horizontalCenter - anchors.topMargin: Utils.dp(10) - width: baseItem.width * 0.6 - minimumValue: 0 - maximumValue: 5 - visible: false - value: progressValue - style: ProgressBarStyle { - background: Rectangle { - radius: Utils.dp(2) - color: Constants.lightgrey - implicitWidth: parent.width - implicitHeight: parent.width/12.0 - } - progress: Rectangle { - radius: Utils.dp(2) - color: Constants.green - } - } - } -} diff --git a/resources/qml/Governikus/ProgressView/qmldir b/resources/qml/Governikus/ProgressView/qmldir index 8ad9938..098fb3f 100644 --- a/resources/qml/Governikus/ProgressView/qmldir +++ b/resources/qml/Governikus/ProgressView/qmldir @@ -1,2 +1,3 @@ module ProgressView + ProgressView 1.0 ProgressView.qml diff --git a/resources/qml/Governikus/Provider/+android/ProviderDetailView.qml b/resources/qml/Governikus/Provider/+android/ProviderDetailView.qml deleted file mode 100644 index ef92f35..0000000 --- a/resources/qml/Governikus/Provider/+android/ProviderDetailView.qml +++ /dev/null @@ -1,116 +0,0 @@ -import QtQuick 2.6 -import QtQuick.Layouts 1.2 -import QtQuick.Controls 2.0 - -import Governikus.Global 1.0 -import Governikus.Provider 1.0 -import Governikus.TitleBar 1.0 - -SectionPage { - id: baseItem - - leftTitleBarAction: TitleBarAction { state: "back"; onClicked: firePop() } - headerTitleBarAction: TitleBarAction { text: provider.shortName } - titleBarColor: Category.displayColor(provider.category) - - property alias providerModelItem: provider.modelItem - ProviderModelItem { - id: provider - } - - - header: ProviderHeader { - id: ownHeader - width: baseItem.width - selectedProvider: provider - } - - content: Item { - height: swipeBar.height + swipeViewBackground.height + Constants.component_spacing - width: baseItem.width - - TabBar { - id: swipeBar - height: firstButton.implicitHeight - anchors.top: parent.top - anchors.topMargin: Constants.component_spacing - anchors.left: parent.left - anchors.right: parent.right - - currentIndex: swipeView.currentIndex - - TabButton { - id: firstButton - padding: Utils.dp(10) - // TODO: Workaround, use contentItem when switching to Qt 5.7.1 - // See https://bugreports.qt.io/browse/QTBUG-50992 - text: qsTr("DESCRIPTION") + settingsModel.translationTrigger - -/* - contentItem: Text { - text: qsTr("DESCRIPTION") + settingsModel.translationTrigger - font.pixelSize: Constants.normal_font_size - elide: Text.ElideRight - opacity: enabled ? 1 : 0.3 - color: !parent.checked ? "black" : parent.pressed ? "black" : Constants.blue - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - } -*/ - } - - TabButton { - padding: Utils.dp(10) - // TODO: Workaround, use contentItem when switching to Qt 5.7.1 - // See https://bugreports.qt.io/browse/QTBUG-50992 - text: qsTr("CONTACT") + settingsModel.translationTrigger - -/* - contentItem: Text { - text: qsTr("CONTACT") + settingsModel.translationTrigger - font.pixelSize: Constants.normal_font_size - elide: Text.ElideRight - opacity: enabled ? 1 : 0.3 - color: !parent.checked ? "black" : parent.pressed ? "black" : Constants.blue - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - } -*/ - } - } - - Rectangle { - id: swipeViewBackground - anchors.top: swipeBar.bottom - anchors.horizontalCenter: swipeBar.horizontalCenter - height: swipeView.height + 2 * Constants.component_spacing - width: parent.width - - SwipeView { - id: swipeView - height: Math.max(providerText.contentHeight, providerInfo.contentHeight) - anchors.margins: Constants.component_spacing - anchors.left: parent.left - anchors.top: parent.top - anchors.right: parent.right - - currentIndex: swipeBar.currentIndex - clip: true - - Text { - id: providerText - color: Constants.secondary_text - text: (!!provider.longDescription ? provider.longDescription : qsTr("Description not available")) + settingsModel.translationTrigger - horizontalAlignment: Text.AlignLeft - wrapMode: Text.WordWrap - font.pixelSize: Constants.normal_font_size - } - - ProviderContactTab { - id: providerInfo - contactModel: provider.contactModel - } - } - } - } -} diff --git a/resources/qml/Governikus/Provider/+android/ProviderViewDelegate.qml b/resources/qml/Governikus/Provider/+android/ProviderViewDelegate.qml deleted file mode 100644 index e32f158..0000000 --- a/resources/qml/Governikus/Provider/+android/ProviderViewDelegate.qml +++ /dev/null @@ -1,133 +0,0 @@ -import QtQml.Models 2.2 -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Layouts 1.2 - -import Governikus.Global 1.0 -import Governikus.Style 1.0 - -Rectangle { - - ProviderStyle { - id: providerStyle - } - - id: baseItem - width: parent.width - height: Constants.provider_section_height - color: Constants.background_color - clip: true - - Rectangle { - id: background - color: "white" - anchors.left: parent.left - anchors.right: parent.right - anchors.rightMargin: providerStyle.providerListItemRightMargin - anchors.top: parent.top - anchors.topMargin: providerStyle.providerListItemTopMargin - anchors.bottom: parent.bottom - anchors.bottomMargin: providerStyle.providerListItemBottomMargin - - Item { - property int childrenHeight: subjectText.height + addressText.height - - id: contentItem - height: Constants.provider_section_height - anchors.verticalCenter: parent.verticalCenter - - anchors.left: parent.left - anchors.right: parent.right - anchors.leftMargin: providerStyle.itemLeftMargin - anchors.rightMargin: Utils.dp(15) - - property int detailsLinkWidth: height / providerStyle.infoItemWidthFactor - - Text { - id: subjectText - - anchors.top: parent.top - anchors.left: parent.left - anchors.right: detailsLink.left - - anchors.topMargin: (Constants.provider_section_height - contentItem.childrenHeight) / 3 - anchors.rightMargin: Utils.dp(5) - - verticalAlignment: Text.AlignVCenter - font.pixelSize: Constants.normal_font_size - color: providerStyle.subjectTextColor - font.bold: providerStyle.subjectTextFontBold - elide: Text.ElideRight - text: display - } - - Text { - id: addressText - - anchors.top: subjectText.bottom - anchors.left: parent.left - anchors.right: detailsLink.left - - anchors.topMargin: (Constants.provider_section_height - contentItem.childrenHeight) / 3 - anchors.bottomMargin: (Constants.provider_section_height - contentItem.childrenHeight) / 3 - - verticalAlignment: Text.AlignVCenter - font.pixelSize: providerStyle.addressTextFontSize - color: providerStyle.addressTextColor - elide: Text.ElideRight - text: providerAddressDomain - } - - Item { - id: detailsLink - anchors.right: parent.right - anchors.verticalCenter: providerStyle.providerListDetailsLinkPosition === "top" ? - subjectText.verticalCenter : - parent.verticalCenter - - anchors.margins: Utils.dp(5) - height: parent.height - width: contentItem.detailsLinkWidth - - Rectangle { - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - height: width - - border.color: providerStyle.providerListDetailsLinkBorder - border.width: 1 - radius: width - - color: providerStyle.providerListDetailsLinkBackground - - Text { - anchors.centerIn: parent - text: "i" - font.bold: providerStyle.providerListDetailsLinkBold - color: providerStyle.providerListDetailsLinkColor - } - } - } - } - } - - MouseArea { - anchors.fill: parent - onClicked: { - firePush(providerDetailView, {providerModelItem: model}) - } - } - - Rectangle { - width: parent.width - anchors.top: parent.bottom - anchors.topMargin: -height - anchors.leftMargin: providerStyle.itemLeftMargin - anchors.left: parent.left - anchors.right: parent.right - visible: providerStyle.providerListItemsHaveBorder - height: visible ? 1 : 0 - color: Constants.grey - } -} diff --git a/resources/qml/Governikus/Provider/+desktop/ProviderDetailView.qml b/resources/qml/Governikus/Provider/+desktop/ProviderDetailView.qml new file mode 100644 index 0000000..08d62f0 --- /dev/null +++ b/resources/qml/Governikus/Provider/+desktop/ProviderDetailView.qml @@ -0,0 +1,22 @@ +import QtQuick 2.10 + +import Governikus.View 1.0 +import Governikus.TitleBar 1.0 +import Governikus.Provider 1.0 + +SectionPage { + titleBarAction: TitleBarAction { + text: qsTr("Provider details") + showSettings: false + helpTopic: "providerPage" + } + + Rectangle { + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + height: parent.height / 2 + width: parent.width / 2 + + color: "mediumaquamarine" + } +} diff --git a/resources/qml/Governikus/Provider/+desktop/ProviderInfoSection.qml b/resources/qml/Governikus/Provider/+desktop/ProviderInfoSection.qml new file mode 100644 index 0000000..d90b346 --- /dev/null +++ b/resources/qml/Governikus/Provider/+desktop/ProviderInfoSection.qml @@ -0,0 +1,25 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.Type.ApplicationModel 1.0 + + +Row { + property alias image: icon.source + property alias title: text.label + property string name: "" + + height: text.height + spacing: Constants.groupbox_spacing + + Image { + id: icon + sourceSize.height: ApplicationModel.scaleFactor * 40 + anchors.verticalCenter: text.verticalCenter + } + + LabeledText { + id: text + text: name.length > 0 ? name : qsTr("See details under \"more...\"") + settingsModel.translationTrigger + } +} diff --git a/resources/qml/Governikus/Provider/+ios/ProviderDetailView.qml b/resources/qml/Governikus/Provider/+ios/ProviderDetailView.qml deleted file mode 100644 index 70a59d4..0000000 --- a/resources/qml/Governikus/Provider/+ios/ProviderDetailView.qml +++ /dev/null @@ -1,119 +0,0 @@ -import QtQuick 2.6 -import QtQuick.Layouts 1.2 -import QtQuick.Controls 2.0 - -import Governikus.Global 1.0 -import Governikus.Provider 1.0 -import Governikus.TitleBar 1.0 - -SectionPage { - id: baseItem - leftTitleBarAction: TitleBarAction { state: "back"; onClicked: firePop() } - headerTitleBarAction: TitleBarAction { text: provider.shortName } - titleBarColor: Category.displayColor(provider.category) - - property alias providerModelItem: provider.modelItem - ProviderModelItem { - id: provider - } - - - header: ProviderHeader { - id: ownHeader - width: baseItem.width - selectedProvider: provider - } - - content: Item { - height: swipeBar.height + swipeViewBackground.height + Constants.component_spacing - width: baseItem.width - - Rectangle { - id: swipeBar - anchors.top: header.bottom - anchors.topMargin: Utils.dp(20) - anchors.horizontalCenter: parent.horizontalCenter - - width: descriptionTab.width + contactTab.width + 2 * border.width - height: descriptionTab.height + 2 * border.width - - border.color: Constants.blue - border.width: Utils.dp(1) - radius: Utils.dp(3) - clip: true - - Row { - id: row - readonly property int maxContentWidth: Math.max(descriptionText.contentWidth, contactText.contentWidth) - - anchors.centerIn: parent - Rectangle { - id: descriptionTab - width: row.maxContentWidth + Utils.dp(6) - height: descriptionText.contentHeight + Utils.dp(6) - color: swipeView.currentIndex === 0 ? Constants.blue : descriptiontMouseArea.pressed ? PlatformConstants.blue_light : "transparent" - Text { - id: descriptionText - anchors.centerIn: parent - color: swipeView.currentIndex === 0 ? "white" : Constants.blue - text: qsTr("Description") + settingsModel.translationTrigger - } - MouseArea { - id: descriptiontMouseArea - anchors.fill: parent - onClicked: swipeView.currentIndex = 0 - } - } - Rectangle { - id: contactTab - width: row.maxContentWidth + Utils.dp(6) - height: contactText.contentHeight + Utils.dp(6) - color: swipeView.currentIndex === 1 ? Constants.blue : contactMouseArea.pressed ? PlatformConstants.blue_light : "transparent" - Text { - id: contactText - anchors.centerIn: parent - color: swipeView.currentIndex === 1 ? "white" : Constants.blue - text: qsTr("Contact") + settingsModel.translationTrigger - } - MouseArea { - id: contactMouseArea - anchors.fill: parent - onClicked: swipeView.currentIndex = 1 - } - } - } - } - - Rectangle { - id: swipeViewBackground - anchors.top: swipeBar.bottom - anchors.horizontalCenter: swipeBar.horizontalCenter - height: swipeView.height + 2 * Constants.component_spacing - width: parent.width - - SwipeView { - id: swipeView - height: Math.max(providerText.contentHeight, providerInfo.contentHeight) - anchors.margins: Constants.component_spacing - anchors.left: parent.left - anchors.top: parent.top - anchors.right: parent.right - clip: true - - Text { - id: providerText - color: Constants.secondary_text - text: (!!provider.longDescription ? provider.longDescription : qsTr("Description not available")) + settingsModel.translationTrigger - horizontalAlignment: Text.AlignLeft - wrapMode: Text.WordWrap - font.pixelSize: Constants.normal_font_size - } - - ProviderContactTab { - id: providerInfo - contactModel: provider.contactModel - } - } - } - } -} diff --git a/resources/qml/Governikus/Provider/+ios/ProviderViewDelegate.qml b/resources/qml/Governikus/Provider/+ios/ProviderViewDelegate.qml deleted file mode 100644 index dde60e6..0000000 --- a/resources/qml/Governikus/Provider/+ios/ProviderViewDelegate.qml +++ /dev/null @@ -1,86 +0,0 @@ -import QtQml.Models 2.2 -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Layouts 1.2 - -import Governikus.Global 1.0 - -Rectangle { - id: baseItem - width: parent.width - height: Constants.provider_section_height - color: Constants.background_color - clip: true - - Rectangle { - id: background - color: "white" - anchors.left: parent.left - anchors.right: parent.right - anchors.rightMargin: Utils.dp(5) - anchors.top: parent.top - anchors.topMargin: Utils.dp(2) - anchors.bottom: parent.bottom - anchors.bottomMargin: Utils.dp(5) - - Item { - height: subjectText.height + addressText.height - anchors.verticalCenter: parent.verticalCenter - - anchors.left: parent.left - anchors.leftMargin: Utils.dp(5) - anchors.right: detailsLink.left - anchors.rightMargin: Utils.dp(15) - - Text { - id: subjectText - color: Constants.secondary_text - width: parent.width - verticalAlignment: Text.AlignVCenter - font.pixelSize: Constants.normal_font_size - elide: Text.ElideRight - text: display - } - Text { - id: addressText - anchors.top: subjectText.bottom - width: parent.width - - verticalAlignment: Text.AlignVCenter - font.pixelSize: Constants.small_font_size - color: PlatformConstants.blue_dark - elide: Text.ElideRight - text: providerAddressDomain - } - } - Item { - id: detailsLink - anchors.right: parent.right - anchors.margins: Utils.dp(5) - height: parent.height - width: parent.height / 2 - - Rectangle { - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - height: width - - border.color: PlatformConstants.blue_dark - border.width: 1 - radius: width - - Text { - anchors.centerIn: parent - text: qsTr("i") + settingsModel.translationTrigger - color: parent.border.color - } - } - } - } - - MouseArea { - anchors.fill: parent - onClicked: firePush(providerDetailView, {providerModelItem: model}) - } -} diff --git a/resources/qml/Governikus/Provider/+mobile/+android/ProviderDetailTab.qml b/resources/qml/Governikus/Provider/+mobile/+android/ProviderDetailTab.qml new file mode 100644 index 0000000..66237b2 --- /dev/null +++ b/resources/qml/Governikus/Provider/+mobile/+android/ProviderDetailTab.qml @@ -0,0 +1,33 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 + +TabButton { + id: button + padding: Utils.dp(10) + + contentItem: Text { + text: button.text + font.pixelSize: Constants.small_font_size + elide: Text.ElideRight + color: button.checked || button.pressed ? Constants.blue : Constants.black + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + background: Rectangle { + color: Constants.background_color + clip: true + + Rectangle { + height: parent.height + radius + width: parent.width + + radius: Utils.dp(3) + color: button.checked ? Constants.white : Constants.grey + border.color: Constants.grey + border.width: Utils.dp(1) + } + } +} diff --git a/resources/qml/Governikus/Provider/+mobile/+android/ProviderViewDelegate.qml b/resources/qml/Governikus/Provider/+mobile/+android/ProviderViewDelegate.qml new file mode 100644 index 0000000..c06b088 --- /dev/null +++ b/resources/qml/Governikus/Provider/+mobile/+android/ProviderViewDelegate.qml @@ -0,0 +1,126 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 + +Rectangle { + id: baseItem + width: parent.width + height: Constants.provider_section_height + color: Constants.background_color + clip: true + + Rectangle { + id: background + color: "white" + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: ProviderStyle.providerListItemRightMargin + anchors.top: parent.top + anchors.topMargin: ProviderStyle.providerListItemTopMargin + anchors.bottom: parent.bottom + anchors.bottomMargin: ProviderStyle.providerListItemBottomMargin + + Item { + property int childrenHeight: subjectText.height + addressText.height + + id: contentItem + height: Constants.provider_section_height + anchors.verticalCenter: parent.verticalCenter + + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: ProviderStyle.itemLeftMargin + anchors.rightMargin: Utils.dp(15) + + property int detailsLinkWidth: height / ProviderStyle.infoItemWidthFactor + + Text { + id: subjectText + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: detailsLink.left + + anchors.topMargin: (Constants.provider_section_height - contentItem.childrenHeight) / 3 + anchors.rightMargin: Utils.dp(5) + + verticalAlignment: Text.AlignVCenter + font.pixelSize: Constants.normal_font_size + color: ProviderStyle.subjectTextColor + font.bold: ProviderStyle.subjectTextFontBold + elide: Text.ElideRight + text: display + } + + Text { + id: addressText + + anchors.top: subjectText.bottom + anchors.left: parent.left + anchors.right: detailsLink.left + + anchors.topMargin: (Constants.provider_section_height - contentItem.childrenHeight) / 3 + anchors.bottomMargin: (Constants.provider_section_height - contentItem.childrenHeight) / 3 + + verticalAlignment: Text.AlignVCenter + font.pixelSize: ProviderStyle.addressTextFontSize + color: ProviderStyle.addressTextColor + elide: Text.ElideRight + text: providerAddressDomain + } + + Item { + id: detailsLink + anchors.right: parent.right + anchors.verticalCenter: ProviderStyle.providerListDetailsLinkPosition === "top" ? + subjectText.verticalCenter : + parent.verticalCenter + + anchors.margins: Utils.dp(5) + height: parent.height + width: contentItem.detailsLinkWidth + + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + height: width + + border.color: ProviderStyle.providerListDetailsLinkBorder + border.width: 1 + radius: width + + color: ProviderStyle.providerListDetailsLinkBackground + + Text { + anchors.centerIn: parent + text: "i" + font.bold: ProviderStyle.providerListDetailsLinkBold + color: ProviderStyle.providerListDetailsLinkColor + } + } + } + } + } + + MouseArea { + anchors.fill: parent + onClicked: { + providerDetailView.providerModelItem = model + firePush(providerDetailView) + } + } + + Rectangle { + width: parent.width + anchors.top: parent.bottom + anchors.topMargin: -height + anchors.leftMargin: ProviderStyle.itemLeftMargin + anchors.left: parent.left + anchors.right: parent.right + visible: ProviderStyle.providerListItemsHaveBorder + height: visible ? 1 : 0 + color: Constants.grey + } +} diff --git a/resources/qml/Governikus/Provider/+mobile/+ios/ProviderDetailTab.qml b/resources/qml/Governikus/Provider/+mobile/+ios/ProviderDetailTab.qml new file mode 100644 index 0000000..379f205 --- /dev/null +++ b/resources/qml/Governikus/Provider/+mobile/+ios/ProviderDetailTab.qml @@ -0,0 +1,34 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 + +TabButton { + id: button + + contentItem: Text { + text: button.text + font.pixelSize: Constants.small_font_size + elide: Text.ElideRight + color: button.checked ? Constants.white : Constants.blue + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + background: Rectangle { + color: Constants.background_color + clip: true + + Rectangle { + height: parent.height + width: parent.width + radius + anchors.left: parent.left + anchors.leftMargin: button.TabBar.index === 0 ? 0 : -radius + + radius: Utils.dp(3) + color: button.checked ? Constants.blue : button.pressed ? Constants.blue_light : Constants.white + border.color: Constants.blue + border.width: Utils.dp(1) + } + } +} diff --git a/resources/qml/Governikus/Provider/+mobile/+ios/ProviderViewDelegate.qml b/resources/qml/Governikus/Provider/+mobile/+ios/ProviderViewDelegate.qml new file mode 100644 index 0000000..436e4b1 --- /dev/null +++ b/resources/qml/Governikus/Provider/+mobile/+ios/ProviderViewDelegate.qml @@ -0,0 +1,86 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 + +Rectangle { + id: baseItem + width: parent.width + height: Constants.provider_section_height + color: Constants.background_color + clip: true + + Rectangle { + id: background + color: "white" + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: Utils.dp(5) + anchors.top: parent.top + anchors.topMargin: Utils.dp(2) + anchors.bottom: parent.bottom + anchors.bottomMargin: Utils.dp(5) + + Item { + height: subjectText.height + addressText.height + anchors.verticalCenter: parent.verticalCenter + + anchors.left: parent.left + anchors.leftMargin: Utils.dp(5) + anchors.right: detailsLink.left + anchors.rightMargin: Utils.dp(15) + + Text { + id: subjectText + color: Constants.secondary_text + width: parent.width + verticalAlignment: Text.AlignVCenter + font.pixelSize: Constants.normal_font_size + elide: Text.ElideRight + text: display + } + Text { + id: addressText + anchors.top: subjectText.bottom + width: parent.width + + verticalAlignment: Text.AlignVCenter + font.pixelSize: Constants.small_font_size + color: Constants.blue_dark + elide: Text.ElideRight + text: providerAddressDomain + } + } + Item { + id: detailsLink + anchors.right: parent.right + anchors.margins: Utils.dp(5) + height: parent.height + width: parent.height / 2 + + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + height: width + + border.color: Constants.blue_dark + border.width: 1 + radius: width + + Text { + anchors.centerIn: parent + text: qsTr("i") + settingsModel.translationTrigger + color: parent.border.color + } + } + } + } + + MouseArea { + anchors.fill: parent + onClicked: { + providerDetailView.providerModelItem = model + firePush(providerDetailView) + } + } +} diff --git a/resources/qml/Governikus/Provider/+mobile/+phone/ProviderDetailView.qml b/resources/qml/Governikus/Provider/+mobile/+phone/ProviderDetailView.qml new file mode 100644 index 0000000..3852621 --- /dev/null +++ b/resources/qml/Governikus/Provider/+mobile/+phone/ProviderDetailView.qml @@ -0,0 +1,86 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 +import Governikus.Provider 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 + +SectionPage { + id: baseItem + + leftTitleBarAction: TitleBarAction { state: "back"; onClicked: firePop() } + headerTitleBarAction: TitleBarAction { text: provider.shortName } + titleBarColor: Category.displayColor(provider.category) + + property alias providerModelItem: provider.modelItem + ProviderModelItem { + id: provider + } + + header: ProviderHeader { + id: ownHeader + width: baseItem.width + selectedProvider: provider + } + + content: Item { + height: swipeBar.height + swipeViewBackground.height + Constants.component_spacing + width: baseItem.width + + TabBar { + id: swipeBar + spacing: 0 + width: Constants.is_layout_android ? parent.width : Utils.dp(220) + height: descriptionButton.implicitHeight + anchors.top: parent.top + anchors.topMargin: Constants.component_spacing + anchors.horizontalCenter: parent.horizontalCenter + + currentIndex: swipeView.currentIndex + + ProviderDetailTab { + id: descriptionButton + text: qsTr("DESCRIPTION") + settingsModel.translationTrigger + } + + ProviderDetailTab { + id: contactButton + text: qsTr("CONTACT") + settingsModel.translationTrigger + } + } + + Rectangle { + id: swipeViewBackground + anchors.top: swipeBar.bottom + anchors.horizontalCenter: swipeBar.horizontalCenter + height: swipeView.height + 2 * Constants.component_spacing + width: parent.width + + SwipeView { + id: swipeView + height: Math.max(providerText.contentHeight, providerInfo.contentHeight) + anchors.margins: Constants.component_spacing + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + + currentIndex: swipeBar.currentIndex + clip: true + + Text { + id: providerText + text: (!!provider.longDescription ? provider.longDescription : qsTr("Description not available")) + settingsModel.translationTrigger + horizontalAlignment: Text.AlignLeft + wrapMode: Text.WordWrap + font.pixelSize: Constants.normal_font_size + } + + ProviderContactTab { + id: providerInfo + contactModel: provider.contactModel + } + } + } + } +} diff --git a/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderContactInfo.qml b/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderContactInfo.qml new file mode 100644 index 0000000..3e14007 --- /dev/null +++ b/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderContactInfo.qml @@ -0,0 +1,66 @@ +import QtQuick 2.10 +import QtQuick.Layouts 1.2 + +import Governikus.Global 1.0 + +Rectangle { + id: baseItem + property alias contactModel: contactListView.model + + onWidthChanged: info.resetSize() + onHeightChanged: info.resetSize() + + Column { + id: info + width: parent.width + + property int sizeRecudctor: 0 + function resetSize() { + if (childrenRect.height < baseItem.height) { + sizeRecudctor = 0 + } + } + function approximateSize() { + if (childrenRect.height > baseItem.height) { + sizeRecudctor = sizeRecudctor +1 + } + } + + onWidthChanged: info.approximateSize() + onHeightChanged: info.approximateSize() + onVisibleChanged: { info.resetSize(); info.approximateSize() } + onChildrenRectChanged: info.approximateSize() + + Text { + text: qsTr("Contact") + settingsModel.translationTrigger + padding: Constants.component_spacing + font.pixelSize: Constants.header_font_size + color: "white" + } + Rectangle { + // The delegates have space in between which is + // therefore the color of this very Rectangle. + anchors.left: parent.left + anchors.right: parent.right + height: contactListView.height + color: "white" + + ListView { + id: contactListView + width: parent.width + height: contentHeight + interactive: false + spacing: 2 + delegate: ProviderContactInfoItem { + anchors.left: parent.left + width: contactListView.width + color: baseItem.color + imageSource: Qt.resolvedUrl(model.iconSource) + itemText: (!!model.text ? model.text : qsTr("Unknown")) + settingsModel.translationTrigger + link: model.link + sizeRecudctor: info.sizeRecudctor + } + } + } + } +} diff --git a/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderContactInfoItem.qml b/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderContactInfoItem.qml new file mode 100644 index 0000000..5a40b98 --- /dev/null +++ b/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderContactInfoItem.qml @@ -0,0 +1,42 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 + + +Rectangle { + id: baseItem + property alias imageSource: image.source + property alias itemText: text.text + property url link + property int sizeRecudctor: 0 + height: text.height + Utils.dp(20) + + + Image { + id: image + width: Utils.dp(25) - baseItem.sizeRecudctor + height: width + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + fillMode: Image.PreserveAspectFit + } + + Text { + id: text + anchors.left: image.right + anchors.leftMargin: Utils.dp(10) + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + font.pixelSize: Constants.normal_font_size - baseItem.sizeRecudctor + color: "white" + linkColor: color + elide: Text.ElideRight + wrapMode: Text.WordWrap + } + + MouseArea { + anchors.fill: parent + enabled: !!baseItem.link + onClicked: Qt.openUrlExternally(baseItem.link) + } +} diff --git a/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailButtonBar.qml b/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailButtonBar.qml new file mode 100644 index 0000000..ff2d20d --- /dev/null +++ b/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailButtonBar.qml @@ -0,0 +1,44 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 + +Item { + id: baseItem + + height: button.height + Constants.component_spacing + width: parent.width + + property string selectedCategory: "" + property string providerIcon: "" + property string address: "" + property color titleBarColor + + Image { + id: icon + source: baseItem.providerIcon + asynchronous: true + height: 2 * baseItem.height + width: height + fillMode: Image.PreserveAspectFit + anchors.left: parent.left + anchors.leftMargin: Constants.component_spacing + anchors.verticalCenter: baseItem.top + } + + GButton { + id: button + text: qsTr("ONLINE APPLICATION") + settingsModel.translationTrigger + buttonColor: baseItem.titleBarColor + maxWidth: parent.width - icon.width - 3 * Constants.component_spacing + anchors.left: icon.right + anchors.leftMargin: Constants.component_spacing + anchors.bottom: icon.bottom + enabled: baseItem.address !== "" + + onClicked: { + if (baseItem.address !== "") { + Qt.openUrlExternally(baseItem.address) + } + } + } +} diff --git a/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailDescription.qml b/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailDescription.qml new file mode 100644 index 0000000..f73c135 --- /dev/null +++ b/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailDescription.qml @@ -0,0 +1,26 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 + + +Column { + id: baseItem + spacing: Constants.pane_spacing + + property string description: "" + + Text { + font.pixelSize: Constants.header_font_size + color: Constants.blue + text: qsTr("Description") + settingsModel.translationTrigger + } + + Text { + color: Constants.secondary_text + font.pixelSize: Constants.normal_font_size + horizontalAlignment: Text.AlignLeft + text: baseItem.description + width: parent.width + wrapMode: Text.Wrap + } +} diff --git a/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailHistory.qml b/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailHistory.qml new file mode 100644 index 0000000..071989b --- /dev/null +++ b/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailHistory.qml @@ -0,0 +1,44 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.Provider 1.0 + +Column { + id: baseItem + spacing: Constants.pane_spacing + + property var openHistoryInfoFunc: function() { + } + + readonly property int historyItemHeight: Utils.dp(66) + readonly property int historyItemMargin: Utils.dp(8) + + Text { + id: headerText + + font.pixelSize: Constants.header_font_size + color: Constants.blue + text: qsTr("History") + settingsModel.translationTrigger + } + + Repeater { + model: historyModel.nameFilter + + ProviderDetailHistoryItem { + itemHeight: baseItem.historyItemHeight + itemMargin: baseItem.historyItemMargin + + width: parent.width + + providerName: subject + providerPostalAddress: providerPostalAddress + dateTime: model.dateTime + infoText: qsTr("Purpose for reading out requested data") + settingsModel.translationTrigger + purposeText: purpose + requestedDataText: requestedData + termsOfUsageText: termsOfUsage + + openInfoFunction: baseItem.openHistoryInfoFunc + } + } +} diff --git a/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailHistoryInfo.qml b/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailHistoryInfo.qml new file mode 100644 index 0000000..910f959 --- /dev/null +++ b/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailHistoryInfo.qml @@ -0,0 +1,145 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.Provider 1.0 + +Item { + id: baseItem + + property string providerName: "" + property string providerPostalAddress: "" + property string purposeText: "" + property string requestedDataText: "" + property string termsOfUsageText: "" + property string internalState: "off" + + Rectangle { + anchors.fill: baseItem + + color: "black" + opacity: 0.4 + } + + Flickable { + anchors.fill: baseItem + anchors.margins: Constants.component_spacing + contentHeight: infoRow.height + + onContentYChanged: { + if (contentY < 0) { contentY = 0 /* prevent flicking over the top */} + } + + Row { + id: infoRow + height: Math.max(leftColumn.height, rightColumn.height) + 2 * Constants.pane_padding + spacing: Constants.component_spacing + + Item { + height: 1 + width: baseItem.width / 3 + + Pane { + id: leftPane + height: infoRow.height + } + + Column { + id: leftColumn + anchors.margins: Constants.pane_padding + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + spacing: Constants.pane_spacing + + ProviderInfoSection { + imageSource: "qrc:///images/provider/information.svg" + title: qsTr("Service provider") + settingsModel.translationTrigger + name: baseItem.providerName + } + + ProviderInfoSection { + imageSource: "qrc:///images/provider/purpose.svg" + title: qsTr("Purpose for reading out requested data") + settingsModel.translationTrigger + name: baseItem.purposeText + } + + Text { + id: readDataTitle + width: parent.width + font.pixelSize: Constants.header_font_size + color: Constants.blue + text: qsTr("Read data") + settingsModel.translationTrigger + } + + Column { + id: infoTable + + width: parent.width + spacing: 1 + + Repeater { + model: baseItem.requestedDataText.split(",") + + Item { + id: textItem + + height: Utils.dp(32) + width: infoTable.width + + Rectangle { + anchors.fill: textItem + color: "white" + } + + Text { + color: Constants.secondary_text + text: modelData.trim() + + anchors.verticalCenter: parent.verticalCenter + font.pixelSize: Constants.normal_font_size + } + } + } + } + } + } + + Item { + height: 1 + width: baseItem.width / 3 * 2 - 3 * Constants.component_spacing + + Pane { + id: rightPane + height: infoRow.height + } + + Column { + id: rightColumn + anchors.margins: Constants.pane_padding + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + spacing: Constants.pane_spacing + + Text { + id: termsOfUsageTitle + text: qsTr("Terms of usage") + settingsModel.translationTrigger + font.pixelSize: Constants.header_font_size + color: Constants.blue + } + + Text { + id: termsOfUsageTextItem + color: Constants.secondary_text + + text: baseItem.termsOfUsageText + width: parent.width + elide: Text.ElideRight + wrapMode: Text.Wrap + font.pixelSize: Constants.normal_font_size + } + } + } + } + } +} diff --git a/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailHistoryItem.qml b/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailHistoryItem.qml new file mode 100644 index 0000000..b443c93 --- /dev/null +++ b/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailHistoryItem.qml @@ -0,0 +1,104 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 + +Item { + id: baseItem + + readonly property color backgroundColor: Constants.blue + readonly property int iconWidth: Utils.dp(18) + + property string providerName: "" + property string providerPostalAddress: "" + property var dateTime: "" + property string infoText: "" + property string purposeText: "" + property string requestedDataText: "" + property string termsOfUsageText: "" + property int itemHeight: 0 + property int itemMargin: 0 + property int lineHeight: itemHeight / 3 + property var openInfoFunction: function () {} + + height: itemHeight + 2 * itemMargin + + Rectangle { + anchors.fill: baseItem + color: "white" + } + + Column { + id: textColumn + + height: baseItem.itemHeight + width: baseItem.width - baseItem.iconWidth + + anchors.left: baseItem.left + anchors.top: baseItem.top + anchors.topMargin: baseItem.itemMargin + + Text { + height: baseItem.lineHeight + verticalAlignment: Text.AlignVCenter + font.pixelSize: Constants.label_font_size + font.capitalization: Font.AllUppercase + color: Constants.blue + text: (!new Date(dateTime) ? "" : + Utils.isToday(dateTime) ? qsTr("today") : + Utils.isYesterday(dateTime) ? qsTr("yesterday") : + Utils.isThisWeek(dateTime) ? dateTime.toLocaleString(Qt.locale(), qsTr("dddd")) : + dateTime.toLocaleString(Qt.locale(), qsTr("dd.MM.yyyy")) + ) + settingsModel.translationTrigger + } + + LabeledText { + label: !!purposeText ? purposeText : "" + text: (!!providerName ? providerName : qsTr("Touch for more details")) + settingsModel.translationTrigger + width: parent.width + height: baseItem.lineHeight + } + } + + Item { + id: detailsLink + + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + + height: width + width: iconWidth + + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + + height: width + + border.color: ProviderStyle.providerListDetailsLinkBorder + border.width: 1 + radius: width + + color: ProviderStyle.providerListDetailsLinkBackground + Text { + anchors.centerIn: parent + + text: "i" + font.bold: ProviderStyle.providerListDetailsLinkBold + color: ProviderStyle.providerListDetailsLinkColor + } + } + } + + MouseArea { + anchors.fill: baseItem + onClicked: baseItem.openInfoFunction({ + providerName: baseItem.providerName, + providerPostalAddress: baseItem.providerPostalAddress, + purposeText: baseItem.purposeText, + requestedDataText: baseItem.requestedDataText, + termsOfUsageText: baseItem.termsOfUsageText + }) + } +} diff --git a/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailView.qml b/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailView.qml new file mode 100644 index 0000000..9218779 --- /dev/null +++ b/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailView.qml @@ -0,0 +1,175 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.Provider 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 + +SectionPage { + id: baseItem + readonly property TitleBarAction leftTitleBarAction: TitleBarAction { + state: "back" + onClicked: { + if (providerDetailsHistoryInfo.visible) { + providerDetailsHistoryInfo.visible = false + } + else { + firePop() + } + } + } + readonly property TitleBarAction headerTitleBarAction: TitleBarAction { + text: historyModelItem && historyModelItem.subject ? historyModelItem.subject : provider.shortName + font.bold: true + } + readonly property Item rightTitleBarAction: Item {} + readonly property color titleBarColor: Category.displayColor(provider.category) + readonly property real titleBarOpacity: 1 + + property alias historyModelItem: provider.modelItem + property alias providerModelItem: provider.modelItem + ProviderModelItem { + id: provider + } + + + content: Column { + id: mainContent + height: childrenRect.height + Constants.component_spacing + width: baseItem.width + + Row { + height: baseItem.height / 2 + width: parent.width + + Item { + height: parent.height + width: baseItem.width * 2 / 3 + anchors.top: parent.top + + Image { + id: image + source: provider.image + asynchronous: true + height: parent.height + fillMode: Image.PreserveAspectFit + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + } + + Image { + height: parent.height + width: height / 2 + anchors.right: image.right + anchors.top: parent.top + fillMode: Image.Stretch + source: Category.gradientImageSource(provider.category) + } + + Rectangle { + anchors.left: image.right + anchors.right: parent.right + anchors.top: parent.top + height: parent.height + color: baseItem.titleBarColor + } + } + + Rectangle { + height: parent.height + width: baseItem.width / 3 + color: baseItem.titleBarColor + + ProviderContactInfo { + color: baseItem.titleBarColor + height: parent.height + width: baseItem.width / 3 - Constants.component_spacing + + contactModel: provider.contactModel + } + } + } + + Row { + id: lowerRow + height: Math.max(buttonBar.height + leftColumn.height, rightColumn.height) + 3 * Constants.pane_padding + width: parent.width + + Item { + height: 1 + width: lowerRow.width * 2 / 3 + + ProviderDetailButtonBar { + id: buttonBar + selectedCategory: provider.category + providerIcon: provider.icon + address: provider.address + titleBarColor: baseItem.titleBarColor + } + + Pane { + id: leftPane + anchors.margins: Constants.component_spacing + anchors.top: buttonBar.bottom + height: lowerRow.height - (buttonBar.height + Constants.pane_padding) + } + + ProviderDetailDescription { + id: leftColumn + anchors.margins: 2 * Constants.pane_padding + anchors.top: buttonBar.bottom + anchors.left: parent.left + anchors.right: parent.right + + description: provider.longDescription + } + } + + Item { + height: 1 + width: lowerRow.width / 3 - Constants.component_spacing + + Pane { + id: rightPane + anchors.topMargin: Constants.component_spacing + anchors.top: parent.top + height: lowerRow.height - Constants.pane_padding + } + + ProviderDetailHistory { + id: rightColumn + anchors.topMargin: 2 * Constants.pane_padding + anchors.leftMargin: Constants.pane_padding + anchors.rightMargin: Constants.pane_padding + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + openHistoryInfoFunc: baseItem.openHistoryInfoFunc + } + } + } + } + + property var openHistoryInfoFunc: function(entryInfo) { + providerDetailsHistoryInfo.visible = true + + providerDetailsHistoryInfo.providerName = entryInfo['providerName'] + providerDetailsHistoryInfo.providerPostalAddress = entryInfo['providerPostalAddress'] + providerDetailsHistoryInfo.purposeText = entryInfo['purposeText'] + providerDetailsHistoryInfo.requestedDataText = entryInfo['requestedDataText'] + providerDetailsHistoryInfo.termsOfUsageText = entryInfo['termsOfUsageText'] + } + + ProviderDetailHistoryInfo { + id: providerDetailsHistoryInfo + + height: parent.height + width: parent.width + + anchors.top: baseItem.top + anchors.left: baseItem.left + + visible: false + } +} diff --git a/resources/qml/Governikus/Provider/+mobile/ProviderContactTab.qml b/resources/qml/Governikus/Provider/+mobile/ProviderContactTab.qml new file mode 100644 index 0000000..e2b7745 --- /dev/null +++ b/resources/qml/Governikus/Provider/+mobile/ProviderContactTab.qml @@ -0,0 +1,63 @@ +import QtQuick 2.10 +import QtQuick.Layouts 1.2 + +import Governikus.Global 1.0 + +Item { + id: baseItem + property alias contactModel: infoList.model + readonly property int contentHeight: infoList.contentHeight + + + ListView { + id: infoList + anchors.fill: parent + interactive: false + + delegate: Item { + id: delegateItem + width: parent.width + height: Math.max(textItem.height, Utils.dp(50)) + + Image { + id: imageItem + fillMode: Image.PreserveAspectFit + height: Utils.dp(24) + width: Utils.dp(24) + anchors.left: parent.left + anchors.leftMargin: Utils.dp(15) + anchors.verticalCenter: parent.verticalCenter + source: Qt.resolvedUrl(model.iconSource) + } + + Text { + id: textItem + color: Constants.secondary_text + text: !!model.text ? model.text : qsTr("Unknown") + settingsModel.translationTrigger + verticalAlignment: Text.AlignVCenter + anchors.left: imageItem.right + anchors.leftMargin: Utils.dp(20) + anchors.right: parent.right + anchors.rightMargin: Utils.dp(10) + anchors.verticalCenter: parent.verticalCenter + font.pixelSize: Utils.dp(16) + wrapMode: Text.WordWrap + textFormat: Text.RichText + } + + MouseArea { + anchors.fill: delegateItem + enabled: !!model.link + onClicked: Qt.openUrlExternally(model.link) + } + + Rectangle { + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + height: 1 + color: Constants.grey + } + } + } +} diff --git a/resources/qml/Governikus/Provider/+mobile/ProviderHeader.qml b/resources/qml/Governikus/Provider/+mobile/ProviderHeader.qml new file mode 100644 index 0000000..f10ce5a --- /dev/null +++ b/resources/qml/Governikus/Provider/+mobile/ProviderHeader.qml @@ -0,0 +1,177 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.Type.ProviderCategoryFilterModel 1.0 + +Rectangle { + id: baseItem + + // Properties that are set by ProviderView or ProviderDetailView + property string selectedCategory: selectedProvider ? selectedProvider.category : "" + property var selectedProvider + + // This is interpreted by the SectionPage component + readonly property real titleBarOpacity: shadow.opacity === 1 ? 1 : (customProviderImage ? Math.max(0, 0.5 - shadow.opacity) : 0) + + // Internal vars + readonly property color shadowColor: Category.displayColor(selectedCategory) + readonly property bool customProviderImage: !!selectedProvider && !!selectedProvider.image + readonly property string backgroundImage: customProviderImage ? selectedProvider.image : Category.backgroundImageSource(selectedCategory) + readonly property string categoryIcon: selectedProvider || selectedCategory !== "all" ? "" : Category.imageSource(selectedCategory) + readonly property bool withButtons: selectedCategory === "" && !selectedProvider + + readonly property double iconHeightRatio: 0.3 + readonly property double iconVerticalMarginRatio: 0.2 + + property int maxContentY: if (withButtons && parent != null) { + return parent.height * (iconHeightRatio + iconVerticalMarginRatio) + } else { + return height / 2 + } + + height: backgroundImage.height + providerInfo.height + + function definedContentY() { + // For some reason contentY is sometimes set to undefined. + return typeof(contentY) === "undefined" ? 0 : contentY + } + + function currentMargin() { + // Height of button icons. + var H = height * iconHeightRatio + + // Initial inferior margin for button icons. + var M = height * iconVerticalMarginRatio + + var y = definedContentY() + + return -2 * y * (M + H) / (3 * M + 2 * H) + M + } + + Image { + id: backgroundImage + + source: baseItem.backgroundImage + height: width / 1.80 + width: parent.width + anchors.left: parent.left + anchors.top: parent.top + fillMode: Image.PreserveAspectCrop + + function transition() { + return Math.min(1, contentY / (height - Constants.titlebar_height)) + } + + Image { + id: categoryIcon + source: baseItem.categoryIcon + asynchronous: true + height: parent.height * 0.5 + width: height + fillMode: Image.PreserveAspectFit + anchors.horizontalCenter: backgroundImage.horizontalCenter + anchors.bottom: backgroundImage.bottom + anchors.bottomMargin: Constants.component_spacing + + visible: baseItem.categoryIcon !== "" + + opacity: baseItem.definedContentY() <= maxContentY ? 1 : 0 + Behavior on opacity { + NumberAnimation {} + } + } + + Image { + source: selectedProvider ? selectedProvider.icon : "" + asynchronous: true + height: Utils.dp(70) + width: height + fillMode: Image.PreserveAspectFit + anchors.margins: Constants.component_spacing + anchors.left: parent.left + anchors.bottom: parent.bottom + visible: !!selectedProvider + } + + Rectangle { + id: shadow + anchors.fill: parent + color: baseItem.shadowColor + opacity: parent.transition() + } + } + + Row { + id: iconsRow + + height: backgroundImage.height * iconHeightRatio + width: backgroundImage.width * 0.9 + + visible: withButtons + + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: backgroundImage.bottom + anchors.bottomMargin: baseItem.currentMargin() + + Repeater { + model: ["citizen", "finance", "insurance", "other"] + + Rectangle { + height: parent.height + width: parent.width * 0.25 + color: "transparent" + + Image { + source: Category.buttonImageSource(modelData) + asynchronous: true + anchors.fill: parent + fillMode: Image.PreserveAspectFit + } + + MouseArea { + anchors.fill: parent + onClicked: ProviderCategoryFilterModel.setCategorySelection(modelData) + } + } + } + } + + Rectangle { + id: providerInfo + height: visible ? column.height + 2 * Constants.pane_padding : 0 + width: parent.width + anchors.left: parent.left + anchors.top: backgroundImage.bottom + + visible: !!selectedProvider + + Column { + id: column + anchors.margins: Constants.pane_padding + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + spacing: Constants.pane_spacing + + Text { + id: providerText + color: Constants.secondary_text + width: parent.width + text: selectedProvider ? selectedProvider.shortDescription : "" + font.pixelSize: Constants.normal_font_size + wrapMode: Text.WordWrap + visible: text.length > 0 + } + + GButton { + id: providerButton + anchors.right: parent.right + buttonColor: shadowColor + text: qsTr("To service provider") + settingsModel.translationTrigger + onClicked: { + Qt.openUrlExternally(selectedProvider ? selectedProvider.address : "") + } + } + } + } +} diff --git a/resources/qml/Governikus/Provider/+mobile/ProviderInfoSection.qml b/resources/qml/Governikus/Provider/+mobile/ProviderInfoSection.qml new file mode 100644 index 0000000..c25f45f --- /dev/null +++ b/resources/qml/Governikus/Provider/+mobile/ProviderInfoSection.qml @@ -0,0 +1,40 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 + + +Rectangle { + + property string imageSource; + property string title; + property string name; + + width: parent.width + height: Math.max(image.height, providerTitle.height) + + color: "white" + clip: true + + Image { + id: image + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + + height: Utils.dp(40) + width: Utils.dp(40) + + source: imageSource + fillMode: Image.PreserveAspectFit + } + + LabeledText { + id: providerTitle + anchors.verticalCenter: image.verticalCenter + anchors.left: image.right + anchors.leftMargin: Utils.dp(10) + anchors.right: parent.right + + label: title + text: name.length > 0 ? name : qsTr("Touch for more details") + settingsModel.translationTrigger + } +} diff --git a/resources/qml/Governikus/Provider/+mobile/ProviderModelItem.qml b/resources/qml/Governikus/Provider/+mobile/ProviderModelItem.qml new file mode 100644 index 0000000..9d7f2cf --- /dev/null +++ b/resources/qml/Governikus/Provider/+mobile/ProviderModelItem.qml @@ -0,0 +1,106 @@ +import QtQuick 2.10 + + +/* + * Convenience utility to access properties of a ProviderModel item + * This ensures having always a defined string, i.e. a non-null string object. + */ +Item { + id: baseItem + property var modelItem + + readonly property string category: !!modelItem && !!modelItem.providerCategory ? modelItem.providerCategory : "" + readonly property string shortName: !!modelItem && !!modelItem.providerShortName ? modelItem.providerShortName : "" + readonly property string longName: !!modelItem && !!modelItem.providerLongName ? modelItem.providerLongName : "" + readonly property string shortDescription: !!modelItem && !!modelItem.providerShortDescription ? modelItem.providerShortDescription : "" + readonly property string longDescription: !!modelItem && !!modelItem.providerLongDescription ? modelItem.providerLongDescription : "" + readonly property string address: !!modelItem && !!modelItem.providerAddress ? modelItem.providerAddress : "" + readonly property string addressDomain: !!modelItem && !!modelItem.providerAddressDomain ? modelItem.providerAddressDomain : "" + readonly property string homepage: !!modelItem && !!modelItem.providerHomepage ? modelItem.providerHomepage : "" + readonly property string homepageBase: !!modelItem && !!modelItem.providerHomepageBase ? modelItem.providerHomepageBase : "" + readonly property string phone: !!modelItem && !!modelItem.providerPhone ? modelItem.providerPhone : "" + readonly property string phoneCost: !!modelItem && !!modelItem.providerPhoneCost ? modelItem.providerPhoneCost : "" + readonly property string email: !!modelItem && !!modelItem.providerEmail ? modelItem.providerEmail : "" + readonly property string postalAddress: !!modelItem && !!modelItem.providerPostalAddress ? modelItem.providerPostalAddress : "" + readonly property string icon: !!modelItem && !!modelItem.providerIcon ? modelItem.providerIcon : "" + readonly property string image: !!modelItem && !!modelItem.providerImage ? modelItem.providerImage : "" + + readonly property ListModel contactModel: ListModel { + readonly property alias homepage: baseItem.homepage + readonly property alias email: baseItem.email + readonly property alias phone: baseItem.phone + readonly property alias phoneCost: baseItem.phoneCost + readonly property string phoneDisplayString: { + var s = "" + if (!!phone) { + s = '' + phone + "" + if (!!phoneCost) { + s += "
" + phoneCost + } + } + return s + } + readonly property alias postalAddress: baseItem.postalAddress + + function removeHtml(htmlString) { + return htmlString.replace(/<\/?[a-z][a-z0-9]*[^>]*>/ig, " "); + } + + onHomepageChanged: { + setProperty(0, "text", !!homepage ? '' + homepage + "" : "") + setProperty(0, "link", homepage) + } + onEmailChanged: { + setProperty(1, "text", !!email ? '' + email + "" : "") + setProperty(1, "link", !!email ? "mailto:" + email : "") + } + onPhoneDisplayStringChanged: { + setProperty(2, "text", phoneDisplayString) + setProperty(2, "link", !!phone? "tel:" + phone : "") + } + onPostalAddressChanged: { + var dest + if (Qt.platform.os === "android") { + dest = 'geo:0,0?q=' + } + else if (Qt.platform.os === "ios") { + dest = 'maps:0,0?q=' + } + else { + dest = 'https://www.google.com/maps?q=' + } + + dest = !!postalAddress ? dest + encodeURIComponent(postalAddress.replace(//gi,' ')) : "" + setProperty(3, "text", !!postalAddress ? '' + postalAddress + "" : "") + setProperty(3, "link", dest) + } + + ListElement { + iconSource: "qrc:///images/provider/url.png" + label: QT_TR_NOOP("Homepage") + text: "" + link: "" + } + + ListElement { + iconSource: "qrc:///images/provider/mail.png" + label: QT_TR_NOOP("E-Mail") + text: "" + link: "" + } + + ListElement { + iconSource: "qrc:///images/provider/telefon.png" + label: QT_TR_NOOP("Phone") + text: "" + link: "" + } + + ListElement { + iconSource: "qrc:///images/provider/adresse.png" + label: QT_TR_NOOP("Contact") + text: "" + link: "" + } + } +} diff --git a/resources/qml/Governikus/Provider/ProviderContactInfoItem_tablet.qml b/resources/qml/Governikus/Provider/ProviderContactInfoItem_tablet.qml deleted file mode 100644 index 731ad82..0000000 --- a/resources/qml/Governikus/Provider/ProviderContactInfoItem_tablet.qml +++ /dev/null @@ -1,42 +0,0 @@ -import QtQuick 2.6 - -import Governikus.Global 1.0 - - -Rectangle { - id: baseItem - property alias imageSource: image.source - property alias itemText: text.text - property url link - property int sizeRecudctor: 0 - height: text.height + Utils.dp(20) - - - Image { - id: image - width: Utils.dp(25) - baseItem.sizeRecudctor - height: width - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - fillMode: Image.PreserveAspectFit - } - - Text { - id: text - anchors.left: image.right - anchors.leftMargin: Utils.dp(10) - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - font.pixelSize: Constants.normal_font_size - baseItem.sizeRecudctor - color: "white" - linkColor: color - elide: Text.ElideRight - wrapMode: Text.WordWrap - } - - MouseArea { - anchors.fill: parent - enabled: !!baseItem.link - onClicked: Qt.openUrlExternally(baseItem.link) - } -} diff --git a/resources/qml/Governikus/Provider/ProviderContactInfo_tablet.qml b/resources/qml/Governikus/Provider/ProviderContactInfo_tablet.qml deleted file mode 100644 index f004cf8..0000000 --- a/resources/qml/Governikus/Provider/ProviderContactInfo_tablet.qml +++ /dev/null @@ -1,66 +0,0 @@ -import QtQuick 2.6 -import QtQuick.Layouts 1.2 - -import Governikus.Global 1.0 - -Rectangle { - id: baseItem - property alias contactModel: contactListView.model - - onWidthChanged: info.resetSize() - onHeightChanged: info.resetSize() - - Column { - id: info - width: parent.width - - property int sizeRecudctor: 0 - function resetSize() { - if (childrenRect.height < baseItem.height) { - sizeRecudctor = 0 - } - } - function approximateSize() { - if (childrenRect.height > baseItem.height) { - sizeRecudctor = sizeRecudctor +1 - } - } - - onWidthChanged: info.approximateSize() - onHeightChanged: info.approximateSize() - onVisibleChanged: { info.resetSize(); info.approximateSize() } - onChildrenRectChanged: info.approximateSize() - - Text { - text: qsTr("Contact") + settingsModel.translationTrigger - padding: Constants.component_spacing - font.pixelSize: Constants.header_font_size - color: "white" - } - Rectangle { - // The delegates have space in between which is - // therefore the color of this very Rectangle. - anchors.left: parent.left - anchors.right: parent.right - height: contactListView.height - color: "white" - - ListView { - id: contactListView - width: parent.width - height: contentHeight - interactive: false - spacing: 2 - delegate: ProviderContactInfoItem_tablet { - anchors.left: parent.left - width: contactListView.width - color: baseItem.color - imageSource: Qt.resolvedUrl(model.iconSource) - itemText: (!!model.text ? model.text : qsTr("Unknown")) + settingsModel.translationTrigger - link: model.link - sizeRecudctor: info.sizeRecudctor - } - } - } - } -} diff --git a/resources/qml/Governikus/Provider/ProviderContactTab.qml b/resources/qml/Governikus/Provider/ProviderContactTab.qml deleted file mode 100644 index 1aa1d4b..0000000 --- a/resources/qml/Governikus/Provider/ProviderContactTab.qml +++ /dev/null @@ -1,63 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Layouts 1.2 - -import Governikus.Global 1.0 - -Item { - id: baseItem - property alias contactModel: infoList.model - readonly property int contentHeight: infoList.contentHeight - - - ListView { - id: infoList - anchors.fill: parent - interactive: false - - delegate: Item { - id: delegateItem - width: parent.width - height: Math.max(textItem.height, Utils.dp(50)) - - Image { - id: imageItem - fillMode: Image.PreserveAspectFit - height: Utils.dp(24) - width: Utils.dp(24) - anchors.left: parent.left - anchors.leftMargin: Utils.dp(15) - anchors.verticalCenter: parent.verticalCenter - source: Qt.resolvedUrl(model.iconSource) - } - - Text { - id: textItem - color: Constants.secondary_text - text: !!model.text ? model.text : qsTr("Unknown") + settingsModel.translationTrigger - verticalAlignment: Text.AlignVCenter - anchors.left: imageItem.right - anchors.leftMargin: Utils.dp(20) - anchors.right: parent.right - anchors.rightMargin: Utils.dp(10) - anchors.verticalCenter: parent.verticalCenter - font.pixelSize: Utils.dp(16) - wrapMode: Text.WordWrap - textFormat: Text.RichText - } - - MouseArea { - anchors.fill: delegateItem - enabled: !!model.link - onClicked: Qt.openUrlExternally(model.link) - } - - Rectangle { - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.right: parent.right - height: 1 - color: Constants.grey - } - } - } -} diff --git a/resources/qml/Governikus/Provider/ProviderDetailButtonBar_tablet.qml b/resources/qml/Governikus/Provider/ProviderDetailButtonBar_tablet.qml deleted file mode 100644 index 932e8eb..0000000 --- a/resources/qml/Governikus/Provider/ProviderDetailButtonBar_tablet.qml +++ /dev/null @@ -1,44 +0,0 @@ -import QtQuick 2.6 - -import Governikus.Global 1.0 - -Item { - id: baseItem - - height: button.height + Constants.component_spacing - width: parent.width - - property string selectedCategory: "" - property string providerIcon: "" - property string address: "" - property color titleBarColor - - Image { - id: icon - source: baseItem.providerIcon - asynchronous: true - height: 2 * baseItem.height - width: height - fillMode: Image.PreserveAspectFit - anchors.left: parent.left - anchors.leftMargin: Constants.component_spacing - anchors.verticalCenter: baseItem.top - } - - GButton { - id: button - text: qsTr("ONLINE APPLICATION") + settingsModel.translationTrigger - buttonColor: baseItem.titleBarColor - maxWidth: parent.width - icon.width - 3 * Constants.component_spacing - anchors.left: icon.right - anchors.leftMargin: Constants.component_spacing - anchors.bottom: icon.bottom - enabled: baseItem.address !== "" - - onClicked: { - if (baseItem.address !== "") { - Qt.openUrlExternally(baseItem.address) - } - } - } -} diff --git a/resources/qml/Governikus/Provider/ProviderDetailDescription_tablet.qml b/resources/qml/Governikus/Provider/ProviderDetailDescription_tablet.qml deleted file mode 100644 index 5483d52..0000000 --- a/resources/qml/Governikus/Provider/ProviderDetailDescription_tablet.qml +++ /dev/null @@ -1,26 +0,0 @@ -import QtQuick 2.6 - -import Governikus.Global 1.0 - - -Column { - id: baseItem - spacing: Constants.pane_spacing - - property string description: "" - - Text { - font.pixelSize: Constants.header_font_size - color: Constants.blue - text: qsTr("Description") + settingsModel.translationTrigger - } - - Text { - color: Constants.secondary_text - font.pixelSize: Constants.normal_font_size - horizontalAlignment: Text.AlignLeft - text: baseItem.description - width: parent.width - wrapMode: Text.Wrap - } -} diff --git a/resources/qml/Governikus/Provider/ProviderDetailHistoryInfo_tablet.qml b/resources/qml/Governikus/Provider/ProviderDetailHistoryInfo_tablet.qml deleted file mode 100644 index 25a980a..0000000 --- a/resources/qml/Governikus/Provider/ProviderDetailHistoryInfo_tablet.qml +++ /dev/null @@ -1,145 +0,0 @@ -import QtQuick 2.6 - -import Governikus.Global 1.0 -import Governikus.Provider 1.0 - -Item { - id: baseItem - - property string providerName: "" - property string providerPostalAddress: "" - property string purposeText: "" - property string requestedDataText: "" - property string termsOfUsageText: "" - property string internalState: "off" - - Rectangle { - anchors.fill: baseItem - - color: "black" - opacity: 0.4 - } - - Flickable { - anchors.fill: baseItem - anchors.margins: Constants.component_spacing - contentHeight: infoRow.height - - onContentYChanged: { - if (contentY < 0) { contentY = 0 /* prevent flicking over the top */} - } - - Row { - id: infoRow - height: Math.max(leftColumn.height, rightColumn.height) + 2 * Constants.pane_padding - spacing: Constants.component_spacing - - Item { - height: 1 - width: baseItem.width / 3 - - Pane { - id: leftPane - height: infoRow.height - } - - Column { - id: leftColumn - anchors.margins: Constants.pane_padding - anchors.left: parent.left - anchors.top: parent.top - anchors.right: parent.right - spacing: Constants.pane_spacing - - ProviderInfoSection { - imageSource: "qrc:///images/provider/information.svg" - title: qsTr("Service provider") + settingsModel.translationTrigger - name: baseItem.providerName - } - - ProviderInfoSection { - imageSource: "qrc:///images/provider/purpose.svg" - title: qsTr("Purpose for reading out requested data") + settingsModel.translationTrigger - name: baseItem.purposeText - } - - Text { - id: readDataTitle - width: parent.width - font.pixelSize: Constants.header_font_size - color: Constants.blue - text: qsTr("Read data") + settingsModel.translationTrigger - } - - Column { - id: infoTable - - width: parent.width - spacing: 1 - - Repeater { - model: baseItem.requestedDataText.split(",") - - Item { - id: textItem - - height: Utils.dp(32) - width: infoTable.width - - Rectangle { - anchors.fill: textItem - color: "white" - } - - Text { - color: Constants.secondary_text - text: modelData.trim() - - anchors.verticalCenter: parent.verticalCenter - font.pixelSize: Constants.normal_font_size - } - } - } - } - } - } - - Item { - height: 1 - width: baseItem.width / 3 * 2 - 3 * Constants.component_spacing - - Pane { - id: rightPane - height: infoRow.height - } - - Column { - id: rightColumn - anchors.margins: Constants.pane_padding - anchors.left: parent.left - anchors.top: parent.top - anchors.right: parent.right - spacing: Constants.pane_spacing - - Text { - id: termsOfUsageTitle - text: qsTr("Terms of usage") + settingsModel.translationTrigger - font.pixelSize: Constants.header_font_size - color: Constants.blue - } - - Text { - id: termsOfUsageTextItem - color: Constants.secondary_text - - text: baseItem.termsOfUsageText - width: parent.width - elide: Text.ElideRight - wrapMode: Text.Wrap - font.pixelSize: Constants.normal_font_size - } - } - } - } - } -} diff --git a/resources/qml/Governikus/Provider/ProviderDetailHistoryItem_tablet.qml b/resources/qml/Governikus/Provider/ProviderDetailHistoryItem_tablet.qml deleted file mode 100644 index 0b7d5ae..0000000 --- a/resources/qml/Governikus/Provider/ProviderDetailHistoryItem_tablet.qml +++ /dev/null @@ -1,110 +0,0 @@ -import QtQuick 2.6 - -import Governikus.Global 1.0 -import Governikus.Style 1.0 - -Item { - id: baseItem - - readonly property color backgroundColor: Constants.blue - readonly property int iconWidth: Utils.dp(18) - - property string providerName: "" - property string providerPostalAddress: "" - property var dateTime: "" - property string infoText: "" - property string purposeText: "" - property string requestedDataText: "" - property string termsOfUsageText: "" - property int itemHeight: 0 - property int itemMargin: 0 - property int lineHeight: itemHeight / 3 - property var openInfoFunction: function () {} - - height: itemHeight + 2 * itemMargin - - ProviderStyle { - id: providerStyle - - visible: false - } - - Rectangle { - anchors.fill: baseItem - color: "white" - } - - Column { - id: textColumn - - height: baseItem.itemHeight - width: baseItem.width - baseItem.iconWidth - - anchors.left: baseItem.left - anchors.top: baseItem.top - anchors.topMargin: baseItem.itemMargin - - Text { - height: baseItem.lineHeight - verticalAlignment: Text.AlignVCenter - font.pixelSize: Constants.label_font_size - font.capitalization: Font.AllUppercase - color: Constants.blue - text: (!new Date(dateTime) ? "" : - Utils.isToday(dateTime) ? qsTr("today") : - Utils.isYesterday(dateTime) ? qsTr("yesterday") : - Utils.isThisWeek(dateTime) ? dateTime.toLocaleString(Qt.locale(), qsTr("dddd")) : - dateTime.toLocaleString(Qt.locale(), qsTr("dd.MM.yyyy")) - ) + settingsModel.translationTrigger - } - - LabeledText { - label: !!purposeText ? purposeText : "" - text: (!!providerName ? providerName : qsTr("Touch for more details")) + settingsModel.translationTrigger - width: parent.width - height: baseItem.lineHeight - } - } - - Item { - id: detailsLink - - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - - height: width - width: iconWidth - - Rectangle { - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - - height: width - - border.color: providerStyle.providerListDetailsLinkBorder - border.width: 1 - radius: width - - color: providerStyle.providerListDetailsLinkBackground - Text { - anchors.centerIn: parent - - text: "i" - font.bold: providerStyle.providerListDetailsLinkBold - color: providerStyle.providerListDetailsLinkColor - } - } - } - - MouseArea { - anchors.fill: baseItem - onClicked: baseItem.openInfoFunction({ - providerName: baseItem.providerName, - providerPostalAddress: baseItem.providerPostalAddress, - purposeText: baseItem.purposeText, - requestedDataText: baseItem.requestedDataText, - termsOfUsageText: baseItem.termsOfUsageText - }) - } -} diff --git a/resources/qml/Governikus/Provider/ProviderDetailHistory_tablet.qml b/resources/qml/Governikus/Provider/ProviderDetailHistory_tablet.qml deleted file mode 100644 index dd93d9e..0000000 --- a/resources/qml/Governikus/Provider/ProviderDetailHistory_tablet.qml +++ /dev/null @@ -1,44 +0,0 @@ -import QtQuick 2.6 - -import Governikus.Global 1.0 -import Governikus.Provider 1.0 - -Column { - id: baseItem - spacing: Constants.pane_spacing - - property var openHistoryInfoFunc: function() { - } - - readonly property int historyItemHeight: Utils.dp(66) - readonly property int historyItemMargin: Utils.dp(8) - - Text { - id: headerText - - font.pixelSize: Constants.header_font_size - color: Constants.blue - text: qsTr("History") + settingsModel.translationTrigger - } - - Repeater { - model: historyModel.nameFilter - - ProviderDetailHistoryItem_tablet { - itemHeight: baseItem.historyItemHeight - itemMargin: baseItem.historyItemMargin - - width: parent.width - - providerName: subject - providerPostalAddress: providerPostalAddress - dateTime: model.dateTime - infoText: qsTr("Purpose for reading out requested data") + settingsModel.translationTrigger - purposeText: purpose - requestedDataText: requestedData - termsOfUsageText: termsOfUsage - - openInfoFunction: baseItem.openHistoryInfoFunc - } - } -} diff --git a/resources/qml/Governikus/Provider/ProviderDetailView.qml b/resources/qml/Governikus/Provider/ProviderDetailView.qml deleted file mode 100644 index 4b11736..0000000 --- a/resources/qml/Governikus/Provider/ProviderDetailView.qml +++ /dev/null @@ -1,3 +0,0 @@ -Item { - id: dummy -} diff --git a/resources/qml/Governikus/Provider/ProviderDetailView_tablet.qml b/resources/qml/Governikus/Provider/ProviderDetailView_tablet.qml deleted file mode 100644 index b9d1382..0000000 --- a/resources/qml/Governikus/Provider/ProviderDetailView_tablet.qml +++ /dev/null @@ -1,175 +0,0 @@ -import QtQuick 2.6 - -import Governikus.Global 1.0 -import Governikus.Provider 1.0 -import Governikus.TitleBar 1.0 - -SectionPage { - id: baseItem - readonly property TitleBarAction leftTitleBarAction: TitleBarAction { - state: "back" - onClicked: { - if (providerDetailsHistoryInfo.visible) { - providerDetailsHistoryInfo.visible = false - } - else { - firePop() - } - } - } - readonly property TitleBarAction headerTitleBarAction: TitleBarAction { - text: historyModelItem && historyModelItem.subject ? historyModelItem.subject : provider.shortName - font.bold: true - } - readonly property Item rightTitleBarAction: Item {} - readonly property color titleBarColor: Category.displayColor(provider.category) - readonly property real titleBarOpacity: 1 - - property alias historyModelItem: provider.modelItem - property alias providerModelItem: provider.modelItem - ProviderModelItem { - id: provider - } - - - content: Column { - id: mainContent - height: childrenRect.height + Constants.component_spacing - width: baseItem.width - - Row { - height: baseItem.height / 2 - width: parent.width - - Item { - height: parent.height - width: baseItem.width * 2 / 3 - anchors.top: parent.top - - Image { - id: image - source: provider.image - asynchronous: true - height: parent.height - fillMode: Image.PreserveAspectFit - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - } - - Image { - height: parent.height - width: height / 2 - anchors.right: image.right - anchors.top: parent.top - fillMode: Image.Stretch - source: Category.gradientImageSource(provider.category) - - } - - Rectangle { - anchors.left: image.right - anchors.right: parent.right - anchors.top: parent.top - height: parent.height - color: baseItem.titleBarColor - } - } - - Rectangle { - height: parent.height - width: baseItem.width / 3 - color: baseItem.titleBarColor - - ProviderContactInfo_tablet { - color: baseItem.titleBarColor - height: parent.height - width: baseItem.width / 3 - Constants.component_spacing - - contactModel: provider.contactModel - } - } - } - - Row { - id: lowerRow - height: Math.max(buttonBar.height + leftColumn.height, rightColumn.height) + 3 * Constants.pane_padding - width: parent.width - - Item { - height: 1 - width: lowerRow.width * 2 / 3 - - ProviderDetailButtonBar_tablet { - id: buttonBar - selectedCategory: provider.category - providerIcon: provider.icon - address: provider.address - titleBarColor: baseItem.titleBarColor - } - - Pane { - id: leftPane - anchors.margins: Constants.component_spacing - anchors.top: buttonBar.bottom - height: lowerRow.height - (buttonBar.height + Constants.pane_padding) - } - - ProviderDetailDescription_tablet { - id: leftColumn - anchors.margins: 2 * Constants.pane_padding - anchors.top: buttonBar.bottom - anchors.left: parent.left - anchors.right: parent.right - - description: provider.longDescription - } - } - - Item { - height: 1 - width: lowerRow.width / 3 - Constants.component_spacing - - Pane { - id: rightPane - anchors.topMargin: Constants.component_spacing - anchors.top: parent.top - height: lowerRow.height - Constants.pane_padding - } - - ProviderDetailHistory_tablet { - id: rightColumn - anchors.topMargin: 2 * Constants.pane_padding - anchors.leftMargin: Constants.pane_padding - anchors.rightMargin: Constants.pane_padding - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - - openHistoryInfoFunc: baseItem.openHistoryInfoFunc - } - } - } - } - - property var openHistoryInfoFunc: function(entryInfo) { - providerDetailsHistoryInfo.visible = true - - providerDetailsHistoryInfo.providerName = entryInfo['providerName'] - providerDetailsHistoryInfo.providerPostalAddress = entryInfo['providerPostalAddress'] - providerDetailsHistoryInfo.purposeText = entryInfo['purposeText'] - providerDetailsHistoryInfo.requestedDataText = entryInfo['requestedDataText'] - providerDetailsHistoryInfo.termsOfUsageText = entryInfo['termsOfUsageText'] - } - - ProviderDetailHistoryInfo_tablet { - id: providerDetailsHistoryInfo - - height: parent.height - width: parent.width - - anchors.top: baseItem.top - anchors.left: baseItem.left - - visible: false - } -} diff --git a/resources/qml/Governikus/Provider/ProviderHeader.qml b/resources/qml/Governikus/Provider/ProviderHeader.qml deleted file mode 100644 index abc0dec..0000000 --- a/resources/qml/Governikus/Provider/ProviderHeader.qml +++ /dev/null @@ -1,173 +0,0 @@ -import QtQuick 2.5 - -import Governikus.Global 1.0 - -Rectangle { - id: baseItem - - // Properties that are set by ProviderView or ProviderDetailView - property string selectedCategory: selectedProvider ? selectedProvider.category : "" - property var selectedProvider - - // This is interpreted by the SectionPage component - readonly property real titleBarOpacity: shadow.opacity === 1 ? 1 : (customProviderImage ? Math.max(0, 0.5 - shadow.opacity) : 0) - - // Internal vars - readonly property color shadowColor: Category.displayColor(selectedCategory) - readonly property bool customProviderImage: !!selectedProvider && !!selectedProvider.image - readonly property string backgroundImage: customProviderImage ? selectedProvider.image : Category.backgroundImageSource(selectedCategory) - readonly property string categoryIcon: selectedProvider || selectedCategory !== "all" ? "" : Category.imageSource(selectedCategory) - readonly property bool withButtons: selectedCategory === "" && !selectedProvider - - readonly property double iconHeightRatio: 0.3 - readonly property double iconVerticalMarginRatio: 0.2 - - property int maxContentY: withButtons ? parent.height * (iconHeightRatio + iconVerticalMarginRatio) : - height / 2 - - height: backgroundImage.height + providerInfo.height - - function definedContentY() { - // For some reason contentY is sometimes set to undefined. - return typeof(contentY) === "undefined" ? 0 : contentY - } - - function currentMargin() { - // Height of button icons. - var H = height * iconHeightRatio - - // Initial inferior margin for button icons. - var M = height * iconVerticalMarginRatio - - var y = definedContentY() - - return -2 * y * (M + H) / (3 * M + 2 * H) + M - } - - Image { - id: backgroundImage - - source: baseItem.backgroundImage - height: width / 1.80 - width: parent.width - anchors.left: parent.left - anchors.top: parent.top - fillMode: Image.PreserveAspectCrop - - function transition() { - return Math.min(1, contentY / (height - Constants.titlebar_height)) - } - - Image { - id: categoryIcon - source: baseItem.categoryIcon - asynchronous: true - height: parent.height * 0.5 - width: height - fillMode: Image.PreserveAspectFit - anchors.horizontalCenter: backgroundImage.horizontalCenter - anchors.bottom: backgroundImage.bottom - anchors.bottomMargin: Constants.component_spacing - - visible: baseItem.categoryIcon !== "" - - opacity: baseItem.definedContentY() <= maxContentY ? 1 : 0 - Behavior on opacity { - NumberAnimation {} - } - } - - Image { - source: selectedProvider ? selectedProvider.icon : "" - asynchronous: true - height: Utils.dp(70) - width: height - fillMode: Image.PreserveAspectFit - anchors.margins: Constants.component_spacing - anchors.left: parent.left - anchors.bottom: parent.bottom - visible: !!selectedProvider - } - - Rectangle { - id: shadow - anchors.fill: parent - color: baseItem.shadowColor - opacity: parent.transition() - } - } - - Row { - id: iconsRow - - height: backgroundImage.height * iconHeightRatio - width: backgroundImage.width * 0.9 - - visible: withButtons - - anchors.horizontalCenter: parent.horizontalCenter - anchors.bottom: backgroundImage.bottom - anchors.bottomMargin: baseItem.currentMargin() - - Repeater { - model: ["citizen", "finance", "insurance", "other"] - - Rectangle { - height: parent.height - width: parent.width * 0.25 - color: "transparent" - - Image { - source: Category.buttonImageSource(modelData) - asynchronous: true - anchors.fill: parent - fillMode: Image.PreserveAspectFit - } - - MouseArea { - anchors.fill: parent - onClicked: providerModel.setCategorySelection(modelData) - } - } - } - } - - Rectangle { - id: providerInfo - height: visible ? column.height + 2 * Constants.pane_padding : 0 - width: parent.width - anchors.left: parent.left - anchors.top: backgroundImage.bottom - - visible: !!selectedProvider - - Column { - id: column - anchors.margins: Constants.pane_padding - anchors.left: parent.left - anchors.top: parent.top - anchors.right: parent.right - spacing: Constants.pane_spacing - - Text { - id: providerText - color: Constants.secondary_text - width: parent.width - text: selectedProvider ? selectedProvider.shortDescription : "" - font.pixelSize: Constants.normal_font_size - wrapMode: Text.WordWrap - visible: text.length > 0 - } - - GButton { - id: providerButton - anchors.right: parent.right - buttonColor: shadowColor - text: qsTr("To service provider") + settingsModel.translationTrigger - onClicked: { - Qt.openUrlExternally(selectedProvider ? selectedProvider.address : "") - } - } - } - } -} diff --git a/resources/qml/Governikus/Provider/ProviderInfoSection.qml b/resources/qml/Governikus/Provider/ProviderInfoSection.qml deleted file mode 100644 index 763ee5a..0000000 --- a/resources/qml/Governikus/Provider/ProviderInfoSection.qml +++ /dev/null @@ -1,43 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Layouts 1.2 -import QtQuick.Controls.Styles 1.4 - -import Governikus.Global 1.0 - - -Rectangle { - - property string imageSource; - property string title; - property string name; - - width: parent.width - height: Math.max(image.height, providerTitle.height) - - color: "white" - clip: true - - Image { - id: image - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - - height: Utils.dp(40) - width: Utils.dp(40) - - source: imageSource - fillMode: Image.PreserveAspectFit - } - - LabeledText { - id: providerTitle - anchors.verticalCenter: image.verticalCenter - anchors.left: image.right - anchors.leftMargin: Utils.dp(10) - anchors.right: parent.right - - label: title - text: name.length > 0 ? name : qsTr("Touch for more details") + settingsModel.translationTrigger - } -} diff --git a/resources/qml/Governikus/Provider/ProviderModelItem.qml b/resources/qml/Governikus/Provider/ProviderModelItem.qml deleted file mode 100644 index 9cc83e3..0000000 --- a/resources/qml/Governikus/Provider/ProviderModelItem.qml +++ /dev/null @@ -1,106 +0,0 @@ -import QtQuick 2.7 - - -/* - * Convenience utility to access properties of a ProviderModel item - * This ensures having always a defined string, i.e. a non-null string object. - */ -Item { - id: baseItem - property var modelItem - - readonly property string category: !!modelItem && !!modelItem.providerCategory ? modelItem.providerCategory : "" - readonly property string shortName: !!modelItem && !!modelItem.providerShortName ? modelItem.providerShortName : "" - readonly property string longName: !!modelItem && !!modelItem.providerLongName ? modelItem.providerLongName : "" - readonly property string shortDescription: !!modelItem && !!modelItem.providerShortDescription ? modelItem.providerShortDescription : "" - readonly property string longDescription: !!modelItem && !!modelItem.providerLongDescription ? modelItem.providerLongDescription : "" - readonly property string address: !!modelItem && !!modelItem.providerAddress ? modelItem.providerAddress : "" - readonly property string addressDomain: !!modelItem && !!modelItem.providerAddressDomain ? modelItem.providerAddressDomain : "" - readonly property string homepage: !!modelItem && !!modelItem.providerHomepage ? modelItem.providerHomepage : "" - readonly property string homepageBase: !!modelItem && !!modelItem.providerHomepageBase ? modelItem.providerHomepageBase : "" - readonly property string phone: !!modelItem && !!modelItem.providerPhone ? modelItem.providerPhone : "" - readonly property string phoneCost: !!modelItem && !!modelItem.providerPhoneCost ? modelItem.providerPhoneCost : "" - readonly property string email: !!modelItem && !!modelItem.providerEmail ? modelItem.providerEmail : "" - readonly property string postalAddress: !!modelItem && !!modelItem.providerPostalAddress ? modelItem.providerPostalAddress : "" - readonly property string icon: !!modelItem && !!modelItem.providerIcon ? modelItem.providerIcon : "" - readonly property string image: !!modelItem && !!modelItem.providerImage ? modelItem.providerImage : "" - - readonly property ListModel contactModel: ListModel { - readonly property alias homepage: baseItem.homepage - readonly property alias email: baseItem.email - readonly property alias phone: baseItem.phone - readonly property alias phoneCost: baseItem.phoneCost - readonly property string phoneDisplayString: { - var s = "" - if (!!phone) { - s = '' + phone + "" - if (!!phoneCost) { - s += "
" + phoneCost - } - } - return s - } - readonly property alias postalAddress: baseItem.postalAddress - - function removeHtml(htmlString) { - return htmlString.replace(/<\/?[a-z][a-z0-9]*[^>]*>/ig, " "); - } - - onHomepageChanged: { - setProperty(0, "text", !!homepage ? '' + homepage + "" : "") - setProperty(0, "link", homepage) - } - onEmailChanged: { - setProperty(1, "text", !!email ? '' + email + "" : "") - setProperty(1, "link", !!email ? "mailto:" + email : "") - } - onPhoneDisplayStringChanged: { - setProperty(2, "text", phoneDisplayString) - setProperty(2, "link", !!phone? "tel:" + phone : "") - } - onPostalAddressChanged: { - var dest - if (Qt.platform.os === "android") { - dest = 'geo:0,0?q=' - } - else if (Qt.platform.os === "ios") { - dest = 'maps:0,0?q=' - } - else { - dest = 'https://www.google.com/maps?q=' - } - - dest = !!postalAddress ? dest + encodeURIComponent(postalAddress.replace(//gi,' ')) : "" - setProperty(3, "text", !!postalAddress ? '' + postalAddress + "" : "") - setProperty(3, "link", dest) - } - - ListElement { - iconSource: "qrc:///images/provider/url.png" - label: QT_TR_NOOP("Homepage") - text: "" - link: "" - } - - ListElement { - iconSource: "qrc:///images/provider/mail.png" - label: QT_TR_NOOP("E-Mail") - text: "" - link: "" - } - - ListElement { - iconSource: "qrc:///images/provider/telefon.png" - label: QT_TR_NOOP("Phone") - text: "" - link: "" - } - - ListElement { - iconSource: "qrc:///images/provider/adresse.png" - label: QT_TR_NOOP("Contact") - text: "" - link: "" - } - } -} diff --git a/resources/qml/Governikus/Provider/ProviderViewDelegate.qml b/resources/qml/Governikus/Provider/ProviderViewDelegate.qml deleted file mode 100644 index 4b11736..0000000 --- a/resources/qml/Governikus/Provider/ProviderViewDelegate.qml +++ /dev/null @@ -1,3 +0,0 @@ -Item { - id: dummy -} diff --git a/resources/qml/Governikus/Provider/qmldir b/resources/qml/Governikus/Provider/qmldir index 9adfe21..d1c895e 100644 --- a/resources/qml/Governikus/Provider/qmldir +++ b/resources/qml/Governikus/Provider/qmldir @@ -1,8 +1,17 @@ module ProviderInfo -ProviderInfoSection 1.0 ProviderInfoSection.qml -ProviderHeader 1.0 ProviderHeader.qml -ProviderModelItem 1.0 ProviderModelItem.qml + +internal ProviderContactInfoItem ProviderContactInfoItem.qml +internal ProviderContactInfo ProviderContactInfo.qml +internal ProviderDetailButtonBar ProviderDetailButtonBar.qml +internal ProviderDetailDescription ProviderDetailDescription.qml +internal ProviderDetailTab ProviderDetailTab.qml +internal ProviderDetailHistoryInfo ProviderDetailHistoryInfo.qml +internal ProviderDetailHistoryItem ProviderDetailHistoryItem.qml +internal ProviderDetailHistory ProviderDetailHistory.qml + ProviderContactTab 1.0 ProviderContactTab.qml ProviderDetailView 1.0 ProviderDetailView.qml -ProviderDetailView_tablet 1.0 ProviderDetailView_tablet.qml +ProviderHeader 1.0 ProviderHeader.qml +ProviderInfoSection 1.0 ProviderInfoSection.qml +ProviderModelItem 1.0 ProviderModelItem.qml ProviderViewDelegate 1.0 ProviderViewDelegate.qml diff --git a/resources/qml/Governikus/ProviderView/+android/+tablet/ProviderView.qml b/resources/qml/Governikus/ProviderView/+android/+tablet/ProviderView.qml deleted file mode 100644 index 13d5560..0000000 --- a/resources/qml/Governikus/ProviderView/+android/+tablet/ProviderView.qml +++ /dev/null @@ -1,174 +0,0 @@ -import QtQuick 2.6 - -import Governikus.Global 1.0 -import Governikus.Provider 1.0 -import Governikus.TitleBar 1.0 -import Governikus.Provider 1.0 - -SectionPage { - id: baseItem - - leftTitleBarAction: TitleBarAction {} - headerTitleBarAction: TitleBarAction { text: qsTr("Provider") + settingsModel.translationTrigger; font.bold: true } - rightTitleBarAction: SearchBar { - availableWidth: baseItem.width - onSearchTextChanged: providerModel.searchString = searchText - } - - titleBarColor: Constants.blue - - visible: false - property bool wasVisible: false - onVisibleChanged: wasVisible = true - - readonly property int headerHeight: Utils.dp(54) - readonly property int separatorHeight: Utils.dp(2) - - ProviderDetailView_tablet { - id: providerDetailView - visible: false - } - - function pushProviderDetails(model) { - historyModel.nameFilter.setProviderAddress(model.providerAddress) - firePush(providerDetailView, {providerModelItem: model}) - } - - Column { - id: content - - width: parent.width - - Rectangle { - - height: baseItem.headerHeight - width: parent.width - - color: "white" - - Row { - id: checkBoxesItem - - height: parent.height - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter - - padding: Utils.dp(30) - spacing: Utils.dp(30) - - transformOrigin: Item.Center - scale: Math.min(parent.width / width, 1) - - CategoryCheckbox_tablet { - id: checkBoxCitizen - - category: "citizen" - imageSource: Category.imageSource("citizen") - text: qsTr("Citizen services") + settingsModel.translationTrigger - } - - CategoryCheckbox_tablet { - id: checkBoxFinance - - category: "finance" - imageSource: Category.imageSource("finance") - text: qsTr("Financials") + settingsModel.translationTrigger - } - - CategoryCheckbox_tablet { - id: checkBoxInsurance - - category: "insurance" - imageSource: Category.imageSource("insurance") - text: qsTr("Insurances") + settingsModel.translationTrigger - } - - CategoryCheckbox_tablet { - id: checkBoxOther - - category: "other" - imageSource: Category.imageSource("other") - text: qsTr("Other services") + settingsModel.translationTrigger - } - } - } - - Rectangle { - height: baseItem.separatorHeight - width: parent.width - - color: PlatformConstants.grey_border - } - - Rectangle { - id: mainPane - - height: baseItem.height - (baseItem.headerHeight + baseItem.separatorHeight) - width: parent.width - anchors.horizontalCenter: parent.horizontalCenter - color: Constants.background_color - - Text { - id: noResultsText - color: Constants.secondary_text - - anchors.centerIn: mainPane - text: qsTr("No match found") + settingsModel.translationTrigger - - wrapMode: Text.WordWrap - font.pixelSize: Constants.normal_font_size - visible: !flickable.visible - } - - Flickable { - id: flickable - anchors.fill: mainPane - clip: true - flickableDirection: Flickable.VerticalFlick - visible: grid.hasResults - - contentHeight: grid.height - contentWidth: parent.width - - onContentYChanged: { - if (contentY < 0) { contentY = 0 /* prevent flicking over the top */} - } - - Grid { - id: grid - columns: Math.floor((parent.width - Constants.component_spacing) / (Utils.dp(196) + Constants.component_spacing)) - padding: Constants.component_spacing - spacing: Constants.component_spacing - width: parent.width - - property int cardHeight: (flickable.height - Constants.component_spacing) / 2 - property int cardWidth: (flickable.width - (grid.columns + 1) * Constants.component_spacing) / grid.columns - property bool hasResults: gridRepeater.count > 0 || additionalResults.totalHits > 0 - - Repeater { - id: gridRepeater - - model: providerModel - - ProviderCard_tablet { - width: grid.cardWidth - headerHeight: width / 1.80 - textHeight: Utils.dp(64) - footerHeight: Utils.dp(30) - pushFunction: baseItem.pushProviderDetails - providerModelItem: baseItem.wasVisible ? model : undefined - } - } - - AdditionalResultsItem_tablet { - id: additionalResults - width: grid.cardWidth - headerHeight: width / 1.80 - textHeight: Utils.dp(64) - footerHeight: Utils.dp(30) - } - } - } - } - } -} diff --git a/resources/qml/Governikus/ProviderView/+android/ProviderView.qml b/resources/qml/Governikus/ProviderView/+android/ProviderView.qml deleted file mode 100644 index 223cbfd..0000000 --- a/resources/qml/Governikus/ProviderView/+android/ProviderView.qml +++ /dev/null @@ -1,117 +0,0 @@ -import QtQuick 2.5 - -import Governikus.Global 1.0 -import Governikus.Provider 1.0 -import Governikus.TitleBar 1.0 -import Governikus.Provider 1.0 -import Governikus.Style 1.0 - -SectionPage { - id: baseItem - - readonly property var category: providerModel.categories.length === 0 ? "" : providerModel.categories[0] - - Component.onCompleted: providerModel.sortByCategoryFirst(true) - - onCategoryChanged: { - providerModel.sortByCategoryFirst(category === "") - } - - ProviderStyle { - id: providerStyle - } - - leftTitleBarAction: TitleBarAction { - state: category !== "" ? "back" : "" - onClicked: { - if (state === "back") { - providerModel.setCategorySelection("") - } - } - } - - headerTitleBarAction: TitleBarAction { - text: Category.displayString(category) + settingsModel.translationTrigger - font.bold: true - } - - rightTitleBarAction: SearchBar { - availableWidth: baseItem.width - Constants.menubar_width - onSearchTextChanged: providerModel.searchString = searchText - } - - titleBarColor: Category.displayColor(category) - - ProviderDetailView { - id: providerDetailView - visible: false - } - - header: ProviderHeader { - width: baseItem.width - selectedCategory: category - } - - content: Column { - width: baseItem.width - - Rectangle { - height: Utils.dp(200) - width: parent.width - color: Constants.background_color - visible: providerModel.rowCount === 0 && !additionalResults.visible - - Text { - color: Constants.secondary_text - anchors.centerIn: parent - text: qsTr("No match found") + settingsModel.translationTrigger - font.pixelSize: Constants.normal_font_size - } - } - - ProviderSectionDelegate { - id: allSection - sectionName: "all" - visible: providerModel.searchString === "" && providerModel.categories.length === 0 - height: visible ? Constants.provider_section_height : 0 - } - - ListView { - id: providerListMain - height: childrenRect.height - width: baseItem.width - interactive: false - visible: category === "" - - model: providerModel - - delegate: ProviderViewDelegate { - height: visible ? Constants.provider_section_height : 0 - visible: providerModel.searchString !== "" - } - - section.property: "providerCategory" - section.labelPositioning: ViewSection.InlineLabels | ViewSection.CurrentLabelAtStart - section.delegate: ProviderSectionDelegate { - sectionName: section - } - } - - ListView { - id: providerListSection - height: childrenRect.height - width: baseItem.width - interactive: false - visible: !providerListMain.visible - - model: providerModel - - delegate: ProviderViewDelegate {} - } - - AdditionalResultsItem { - id: additionalResults - width: parent.width - } - } -} diff --git a/resources/qml/Governikus/ProviderView/+android/SearchBar.qml b/resources/qml/Governikus/ProviderView/+android/SearchBar.qml deleted file mode 100644 index 1530ba4..0000000 --- a/resources/qml/Governikus/ProviderView/+android/SearchBar.qml +++ /dev/null @@ -1,69 +0,0 @@ -import QtQuick 2.7 -import QtQuick.Controls 2.0 - -import Governikus.Global 1.0 - - -Row { - id: root - - property int availableWidth: 0 - readonly property int contentWidth: root.implicitWidth - readonly property alias searchText: searchField.displayText - - anchors.top: parent ? parent.top : undefined - anchors.topMargin: Constants.titlebar_padding - anchors.right: parent ? parent.right : undefined - anchors.bottom: parent ? parent.bottom : undefined - anchors.bottomMargin: Constants.titlebar_padding - spacing: Constants.titlebar_padding - - GTextField { - id: searchField - height: parent.height - width: root.availableWidth - parent.spacing - iconItem.width - 2 * Constants.titlebar_padding - - visible: false - - onAccepted: { - iconItem.forceActiveFocus(Qt.MouseFocusReason) - } - - Behavior on visible { - PropertyAnimation { - duration: 150 - } - } - } - - Image { - id: iconItem - - height: parent.height - width: height - fillMode: Image.PreserveAspectFit - source: "qrc:///images/android/search_icon.svg" - - MouseArea { - anchors.fill: parent - onClicked: { - // Storage of the new value is needed because the query - // of searchField.visible still delivers the old value. - var searchFieldVisible = !searchField.visible - - if (searchFieldVisible) { - iconItem.source = "qrc:///images/android/search_cancel.svg" - searchField.forceActiveFocus(Qt.MouseFocusReason) - Qt.inputMethod.show() - } else { - iconItem.source = "qrc:///images/android/search_icon.svg" - iconItem.forceActiveFocus(Qt.MouseFocusReason) - searchField.text = "" - Qt.inputMethod.hide() - } - - searchField.visible = searchFieldVisible - } - } - } -} diff --git a/resources/qml/Governikus/ProviderView/+desktop/ProviderView.qml b/resources/qml/Governikus/ProviderView/+desktop/ProviderView.qml new file mode 100644 index 0000000..909cdd6 --- /dev/null +++ b/resources/qml/Governikus/ProviderView/+desktop/ProviderView.qml @@ -0,0 +1,51 @@ +import QtQuick 2.10 + +import Governikus.View 1.0 +import Governikus.TitleBar 1.0 +import Governikus.Provider 1.0 +import Governikus.Type.ApplicationModel 1.0 + + +SectionPage { + id: sectionPage + + KeyNavigation.tab: navSuccessor + Accessible.role: Accessible.Grouping + Accessible.name: qsTr("Provider view") + Accessible.description: qsTr("This is the provider view of the AusweisApp2.") + + titleBarAction: TitleBarAction { + text: qsTr("Provider") + onClicked: { + rect.visible = true + detailView.visible = false + } + customSubAction: SearchBar { + availableWidth: ApplicationModel.scaleFactor * 768 + } + showHelp: false + } + + Rectangle { + id: rect + + anchors.fill: parent + color: "red" + opacity: 0.5 + visible: true + + MouseArea { + anchors.fill: parent + onClicked: { + rect.visible = false + detailView.visible = true + } + } + } + + ProviderDetailView { + id: detailView + visible: false + onNextView: sectionPage.nextView(pName) + } +} diff --git a/resources/qml/Governikus/ProviderView/+desktop/SearchBar.qml b/resources/qml/Governikus/ProviderView/+desktop/SearchBar.qml new file mode 100644 index 0000000..17f272f --- /dev/null +++ b/resources/qml/Governikus/ProviderView/+desktop/SearchBar.qml @@ -0,0 +1,63 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 + + +Row { + id: root + + property int availableWidth: 0 + readonly property int contentWidth: root.implicitWidth + readonly property alias searchText: searchField.displayText + + spacing: Constants.titlebar_padding + height: searchField.height + + GTextField { + id: searchField + width: root.availableWidth - parent.spacing - iconItem.width - 2 * Constants.titlebar_padding + font.pixelSize: Constants.small_font_size + + visible: false + + onAccepted: { + iconItem.forceActiveFocus(Qt.MouseFocusReason) + } + + Behavior on visible { + PropertyAnimation { + duration: 150 + } + } + } + + Image { + id: iconItem + + sourceSize.height: parent.height + source: "qrc:///images/search.svg" + + MouseArea { + anchors.fill: parent + onClicked: { + // Storage of the new value is needed because the query + // of searchField.visible still delivers the old value. + var searchFieldVisible = !searchField.visible + + if (searchFieldVisible) { + iconItem.source = "qrc:///images/cancel.svg" + searchField.forceActiveFocus(Qt.MouseFocusReason) + Qt.inputMethod.show() + } else { + iconItem.source = "qrc:///images/search.svg" + iconItem.forceActiveFocus(Qt.MouseFocusReason) + searchField.text = "" + Qt.inputMethod.hide() + } + + searchField.visible = searchFieldVisible + } + } + } +} diff --git a/resources/qml/Governikus/ProviderView/+ios/+tablet/ProviderView.qml b/resources/qml/Governikus/ProviderView/+ios/+tablet/ProviderView.qml deleted file mode 100644 index 3f42785..0000000 --- a/resources/qml/Governikus/ProviderView/+ios/+tablet/ProviderView.qml +++ /dev/null @@ -1,175 +0,0 @@ -import QtQuick 2.6 - -import Governikus.Global 1.0 -import Governikus.Provider 1.0 -import Governikus.TitleBar 1.0 -import Governikus.Provider 1.0 - -SectionPage { - id: baseItem - - headerTitleBarAction: TitleBarAction { text: qsTr("Provider") + settingsModel.translationTrigger; font.bold: true } - subTitleBarAction: SearchBar { - width: baseItem.width - onSearchTextChanged: providerModel.searchString = searchText - } - - titleBarColor: Constants.blue - - visible: false - property bool wasVisible: false - onVisibleChanged: wasVisible = true - - readonly property var category: providerModel.categories.length === 0 ? "" : providerModel.categories[0] - - readonly property int headerHeight: Utils.dp(54) - readonly property int separatorHeight: Utils.dp(2) - - ProviderDetailView_tablet { - id: providerDetailView - visible: false - } - - function pushProviderDetails(model) { - historyModel.nameFilter.setProviderAddress(model.providerAddress) - firePush(providerDetailView, {providerModelItem: model}) - } - - Column { - id: content - - width: parent.width - - Rectangle { - - height: baseItem.headerHeight - width: parent.width - - color: "white" - - Row { - id: checkBoxesItem - - height: parent.height - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter - - padding: Utils.dp(30) - spacing: Utils.dp(30) - - transformOrigin: Item.Center - scale: Math.min(parent.width / width, 1) - - CategoryCheckbox_tablet { - id: checkBoxCitizen - - category: "citizen" - imageSource: Category.imageSource("citizen") - text: qsTr("Citizen services") + settingsModel.translationTrigger - } - - CategoryCheckbox_tablet { - id: checkBoxFinance - - category: "finance" - imageSource: Category.imageSource("finance") - text: qsTr("Financials") + settingsModel.translationTrigger - } - - CategoryCheckbox_tablet { - id: checkBoxInsurance - - category: "insurance" - imageSource: Category.imageSource("insurance") - text: qsTr("Insurances") + settingsModel.translationTrigger - } - - CategoryCheckbox_tablet { - id: checkBoxOther - - category: "other" - imageSource: Category.imageSource("other") - text: qsTr("Other services") + settingsModel.translationTrigger - } - } - } - - Rectangle { - height: baseItem.separatorHeight - width: parent.width - - color: PlatformConstants.grey_border - } - - Rectangle { - id: mainPane - - height: baseItem.height - (baseItem.headerHeight + baseItem.separatorHeight) - width: parent.width - anchors.horizontalCenter: parent.horizontalCenter - color: Constants.background_color - - Text { - id: noResultsText - color: Constants.secondary_text - - anchors.centerIn: mainPane - text: qsTr("No match found") + settingsModel.translationTrigger - - wrapMode: Text.WordWrap - font.pixelSize: Constants.normal_font_size - visible: !flickable.visible - } - - Flickable { - id: flickable - anchors.fill: mainPane - clip: true - flickableDirection: Flickable.VerticalFlick - visible: grid.hasResults - - contentHeight: grid.height - contentWidth: parent.width - - onContentYChanged: { - if (contentY < 0) { contentY = 0 /* prevent flicking over the top */} - } - - Grid { - id: grid - columns: Math.floor((parent.width - Constants.component_spacing) / (Utils.dp(196) + Constants.component_spacing)) - padding: Constants.component_spacing - spacing: Constants.component_spacing - width: parent.width - - property int cardHeight: (flickable.height - Constants.component_spacing) / 2 - property int cardWidth: (flickable.width - (grid.columns + 1) * Constants.component_spacing) / grid.columns - property bool hasResults: gridRepeater.count > 0 || additionalResults.totalHits > 0 - - Repeater { - id: gridRepeater - - model: providerModel - - ProviderCard_tablet { - width: grid.cardWidth - headerHeight: width / 1.80 - textHeight: Utils.dp(64) - footerHeight: Utils.dp(30) - pushFunction: baseItem.pushProviderDetails - providerModelItem: baseItem.wasVisible ? model : undefined - } - } - - AdditionalResultsItem_tablet { - id: additionalResults - width: grid.cardWidth - headerHeight: width / 1.80 - textHeight: Utils.dp(64) - footerHeight: Utils.dp(30) - } - } - } - } - } -} diff --git a/resources/qml/Governikus/ProviderView/+ios/ProviderView.qml b/resources/qml/Governikus/ProviderView/+ios/ProviderView.qml deleted file mode 100644 index 538a0ba..0000000 --- a/resources/qml/Governikus/ProviderView/+ios/ProviderView.qml +++ /dev/null @@ -1,111 +0,0 @@ -import QtQuick 2.5 - -import Governikus.Global 1.0 -import Governikus.Provider 1.0 -import Governikus.TitleBar 1.0 -import Governikus.Style 1.0 - -SectionPage { - id: baseItem - - readonly property var category: providerModel.categories.length === 0 ? "" : providerModel.categories[0] - - Component.onCompleted: providerModel.sortByCategoryFirst(true) - - onCategoryChanged: { - providerModel.sortByCategoryFirst(category === "") - } - - ProviderStyle { - id: providerStyle - } - - leftTitleBarAction: TitleBarAction { - state: category !== "" ? "back" : "" - onClicked: { - if (state === "back") { - providerModel.setCategorySelection("") - } - } - } - - headerTitleBarAction: TitleBarAction { - text: Category.displayString(category) + settingsModel.translationTrigger - font.bold: true - } - - subTitleBarAction: SearchBar { - width: baseItem.width - onSearchTextChanged: providerModel.searchString = searchText - } - - titleBarColor: Category.displayColor(category) - - ProviderDetailView { - id: providerDetailView - visible: false - } - - content: Column { - width: baseItem.width - - Rectangle { - height: Utils.dp(200) - width: parent.width - color: Constants.background_color - visible: providerModel.rowCount === 0 && !additionalResults.visible - - Text { - color: Constants.secondary_text - anchors.centerIn: parent - text: qsTr("No match found") + settingsModel.translationTrigger - font.pixelSize: Constants.normal_font_size - } - } - - ProviderSectionDelegate { - id: allSection - sectionName: "all" - visible: providerModel.searchString === "" && providerModel.categories.length === 0 - height: visible ? Constants.provider_section_height : 0 - } - - ListView { - id: providerListMain - height: childrenRect.height - width: baseItem.width - interactive: false - visible: category === "" - - model: providerModel - - delegate: ProviderViewDelegate { - height: visible ? Constants.provider_section_height : 0 - visible: providerModel.searchString !== "" - } - - section.property: "providerCategory" - section.labelPositioning: ViewSection.InlineLabels | ViewSection.CurrentLabelAtStart - section.delegate: ProviderSectionDelegate { - sectionName: section - } - } - - ListView { - id: providerListSection - height: childrenRect.height - width: baseItem.width - interactive: false - visible: !providerListMain.visible - - model: providerModel - - delegate: ProviderViewDelegate {} - } - - AdditionalResultsItem { - id: additionalResults - width: parent.width - } - } -} diff --git a/resources/qml/Governikus/ProviderView/+ios/SearchBar.qml b/resources/qml/Governikus/ProviderView/+ios/SearchBar.qml deleted file mode 100644 index 84b20e6..0000000 --- a/resources/qml/Governikus/ProviderView/+ios/SearchBar.qml +++ /dev/null @@ -1,150 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 - -import Governikus.Global 1.0 - - -Item { - readonly property alias searchText: searchField.text - - id: baseItem - height: Constants.searchbar_height - - MouseArea { - id: pageArea - onClicked: pageArea.focus = true - - height: Constants.searchbar_height - width: parent.width - anchors.bottom: parent.bottom - - Rectangle { - anchors.left: parent.left - anchors.right: cancelButton.left - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.leftMargin: Utils.dp(8) - anchors.rightMargin: Utils.dp(10) - anchors.topMargin: Utils.dp(6) - anchors.bottomMargin: Utils.dp(8) - radius: Utils.dp(6) - color: "white" - - Image { - id: glassIcon - height: parent.height - 2 * anchors.leftMargin - width: height - anchors.left: parent.left - anchors.leftMargin: Utils.dp(4) - anchors.verticalCenter: parent.verticalCenter - source: "qrc:///images/iOS/search_icon.svg" - } - - TextField { - id: searchField - anchors.margins: Utils.dp(8) - anchors.verticalCenter: parent.verticalCenter - anchors.left: glassIcon.right - anchors.right: textEditX.left - horizontalAlignment: Text.AlignLeft - - style: TextFieldStyle { - background: Rectangle {} - } - - onVisibleChanged: { - if (visible === false){ - Qt.inputMethod.hide() - } - } - } - - Label { - id: searchLabel - anchors.left: searchField.left - anchors.leftMargin: Utils.dp(8) - anchors.verticalCenter: searchField.verticalCenter - text: qsTr("Search") + settingsModel.translationTrigger - color: Constants.grey - font.pixelSize: Constants.normal_font_size - visible: searchField.text.trim().length === 0 - } - - MouseArea { - id: textEditX - height: parent.height - width: height - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - enabled: searchField.text.length > 0 - - onClicked: { - searchField.text = "" - } - - Image { - anchors.margins: Utils.dp(4) - anchors.fill: parent - source: "qrc:///images/iOS/search_cancel.svg" - visible: parent.enabled - } - } - } - - DimmableTextButton { - id: cancelButton - - anchors.right: parent.right - anchors.rightMargin: visible ? Utils.dp(8) : 0 - anchors.verticalCenter: parent.verticalCenter - clip: true - visible: false - width: visible ? cancelButton.implicitWidth : 0 - font.family: "Helvetica" - font.pixelSize: Constants.normal_font_size - color: "white" - text: qsTr("Cancel") + settingsModel.translationTrigger - onClicked: { - searchField.text = "" - pageArea.clicked(null) - } - } - } - - states: [ - State { - name: "searchBarActive" - when: searchField.activeFocus || searchField.text.trim().length !== 0 - - PropertyChanges { - target: cancelButton - visible: true - } - }, - State { - name: "" - StateChangeScript { - script: { - Qt.inputMethod.hide() - } - } - - PropertyChanges { - target: cancelButton - visible: false - } - } - ] - - transitions: [ - Transition { - from: "*" - to: "*" - AnchorAnimation { - duration: 200; - easing.type: Easing.InOutQuad - } - } - ] -} diff --git a/resources/qml/Governikus/ProviderView/+mobile/+android/+phone/ProviderView.qml b/resources/qml/Governikus/ProviderView/+mobile/+android/+phone/ProviderView.qml new file mode 100644 index 0000000..84f1b6f --- /dev/null +++ b/resources/qml/Governikus/ProviderView/+mobile/+android/+phone/ProviderView.qml @@ -0,0 +1,114 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.Provider 1.0 +import Governikus.TitleBar 1.0 +import Governikus.Provider 1.0 +import Governikus.View 1.0 +import Governikus.Type.ProviderCategoryFilterModel 1.0 + +SectionPage { + id: baseItem + + readonly property var category: ProviderCategoryFilterModel.categories.length === 0 ? "" : ProviderCategoryFilterModel.categories[0] + + Component.onCompleted: ProviderCategoryFilterModel.sortByCategoryFirst(true) + + onCategoryChanged: { + ProviderCategoryFilterModel.sortByCategoryFirst(category === "") + } + + leftTitleBarAction: TitleBarAction { + state: category !== "" ? "back" : "" + onClicked: { + if (state === "back") { + ProviderCategoryFilterModel.setCategorySelection("") + } + } + } + + headerTitleBarAction: TitleBarAction { + text: Category.displayString(category) + settingsModel.translationTrigger + font.bold: true + } + + rightTitleBarAction: SearchBar { + availableWidth: baseItem.width - Constants.menubar_width + onSearchTextChanged: ProviderCategoryFilterModel.searchString = searchText + } + + titleBarColor: Category.displayColor(category) + + ProviderDetailView { + id: providerDetailView + visible: false + } + + header: ProviderHeader { + width: baseItem.width + selectedCategory: category + } + + content: Column { + width: baseItem.width + + Rectangle { + height: Utils.dp(200) + width: parent.width + color: Constants.background_color + visible: ProviderCategoryFilterModel.rowCount === 0 && !additionalResults.visible + + Text { + color: Constants.secondary_text + anchors.centerIn: parent + text: qsTr("No match found") + settingsModel.translationTrigger + font.pixelSize: Constants.normal_font_size + } + } + + ProviderSectionDelegate { + id: allSection + sectionName: "all" + visible: ProviderCategoryFilterModel.searchString === "" && ProviderCategoryFilterModel.categories.length === 0 + height: visible ? Constants.provider_section_height : 0 + } + + ListView { + id: providerListMain + height: childrenRect.height + width: baseItem.width + interactive: false + visible: category === "" + + model: ProviderCategoryFilterModel + + delegate: ProviderViewDelegate { + height: visible ? Constants.provider_section_height : 0 + visible: ProviderCategoryFilterModel.searchString !== "" + } + + section.property: "providerCategory" + section.labelPositioning: ViewSection.InlineLabels | ViewSection.CurrentLabelAtStart + section.delegate: ProviderSectionDelegate { + sectionName: section + } + } + + ListView { + id: providerListSection + height: childrenRect.height + width: baseItem.width + interactive: false + visible: !providerListMain.visible + + model: ProviderCategoryFilterModel + + delegate: ProviderViewDelegate {} + } + + AdditionalResultsItem { + id: additionalResults + width: parent.width + } + } +} diff --git a/resources/qml/Governikus/ProviderView/+mobile/+android/+tablet/ProviderView.qml b/resources/qml/Governikus/ProviderView/+mobile/+android/+tablet/ProviderView.qml new file mode 100644 index 0000000..badf8af --- /dev/null +++ b/resources/qml/Governikus/ProviderView/+mobile/+android/+tablet/ProviderView.qml @@ -0,0 +1,176 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.Provider 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 +import Governikus.Type.ProviderCategoryFilterModel 1.0 + +SectionPage { + id: baseItem + + leftTitleBarAction: TitleBarAction {} + headerTitleBarAction: TitleBarAction { text: qsTr("Provider") + settingsModel.translationTrigger; font.bold: true } + rightTitleBarAction: SearchBar { + availableWidth: baseItem.width + onSearchTextChanged: ProviderCategoryFilterModel.searchString = searchText + } + + titleBarColor: Constants.blue + + visible: false + property bool wasVisible: false + onVisibleChanged: wasVisible = true + + readonly property int headerHeight: Utils.dp(54) + readonly property int separatorHeight: Utils.dp(2) + + ProviderDetailView { + id: providerDetailView + visible: false + } + + function pushProviderDetails(pModel) { + historyModel.nameFilter.setProviderAddress(pModel.providerAddress) + providerDetailView.providerModelItem = pModel + firePush(providerDetailView) + } + + Column { + id: content + + width: parent.width + + Rectangle { + + height: baseItem.headerHeight + width: parent.width + + color: "white" + + Row { + id: checkBoxesItem + + height: parent.height + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + + padding: Utils.dp(30) + spacing: Utils.dp(30) + + transformOrigin: Item.Center + scale: Math.min(parent.width / width, 1) + + CategoryCheckbox { + id: checkBoxCitizen + + category: "citizen" + imageSource: Category.imageSource("citizen") + text: qsTr("Citizen services") + settingsModel.translationTrigger + } + + CategoryCheckbox { + id: checkBoxFinance + + category: "finance" + imageSource: Category.imageSource("finance") + text: qsTr("Financials") + settingsModel.translationTrigger + } + + CategoryCheckbox { + id: checkBoxInsurance + + category: "insurance" + imageSource: Category.imageSource("insurance") + text: qsTr("Insurances") + settingsModel.translationTrigger + } + + CategoryCheckbox { + id: checkBoxOther + + category: "other" + imageSource: Category.imageSource("other") + text: qsTr("Other services") + settingsModel.translationTrigger + } + } + } + + Rectangle { + height: baseItem.separatorHeight + width: parent.width + + color: Constants.grey_border + } + + Rectangle { + id: mainPane + + height: baseItem.height - (baseItem.headerHeight + baseItem.separatorHeight) + width: parent.width + anchors.horizontalCenter: parent.horizontalCenter + color: Constants.background_color + + Text { + id: noResultsText + color: Constants.secondary_text + + anchors.centerIn: mainPane + text: qsTr("No match found") + settingsModel.translationTrigger + + wrapMode: Text.WordWrap + font.pixelSize: Constants.normal_font_size + visible: !flickable.visible + } + + Flickable { + id: flickable + anchors.fill: mainPane + clip: true + flickableDirection: Flickable.VerticalFlick + visible: grid.hasResults + + contentHeight: grid.height + contentWidth: parent.width + + onContentYChanged: { + if (contentY < 0) { contentY = 0 /* prevent flicking over the top */} + } + + Grid { + id: grid + columns: Math.floor((parent.width - Constants.component_spacing) / (Utils.dp(196) + Constants.component_spacing)) + padding: Constants.component_spacing + spacing: Constants.component_spacing + width: parent.width + + property int cardHeight: (flickable.height - Constants.component_spacing) / 2 + property int cardWidth: (flickable.width - (grid.columns + 1) * Constants.component_spacing) / grid.columns + property bool hasResults: gridRepeater.count > 0 || additionalResults.totalHits > 0 + + Repeater { + id: gridRepeater + + model: ProviderCategoryFilterModel + + ProviderCard { + width: grid.cardWidth + headerHeight: width / 1.80 + textHeight: Utils.dp(64) + footerHeight: Utils.dp(30) + pushFunction: baseItem.pushProviderDetails + providerModelItem: baseItem.wasVisible ? model : undefined + } + } + + AdditionalResultsItem { + id: additionalResults + width: grid.cardWidth + headerHeight: width / 1.80 + textHeight: Utils.dp(64) + footerHeight: Utils.dp(30) + } + } + } + } + } +} diff --git a/resources/qml/Governikus/ProviderView/+mobile/+android/SearchBar.qml b/resources/qml/Governikus/ProviderView/+mobile/+android/SearchBar.qml new file mode 100644 index 0000000..4de7099 --- /dev/null +++ b/resources/qml/Governikus/ProviderView/+mobile/+android/SearchBar.qml @@ -0,0 +1,69 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 + + +Row { + id: root + + property int availableWidth: 0 + readonly property int contentWidth: root.implicitWidth + readonly property alias searchText: searchField.displayText + + anchors.top: parent ? parent.top : undefined + anchors.topMargin: Constants.titlebar_padding + anchors.right: parent ? parent.right : undefined + anchors.bottom: parent ? parent.bottom : undefined + anchors.bottomMargin: Constants.titlebar_padding + spacing: Constants.titlebar_padding + + GTextField { + id: searchField + height: parent.height + width: root.availableWidth - parent.spacing - iconItem.width - 2 * Constants.titlebar_padding + + visible: false + + onAccepted: { + iconItem.forceActiveFocus(Qt.MouseFocusReason) + } + + Behavior on visible { + PropertyAnimation { + duration: 150 + } + } + } + + Image { + id: iconItem + + height: parent.height + width: height + fillMode: Image.PreserveAspectFit + source: "qrc:///images/search.svg" + + MouseArea { + anchors.fill: parent + onClicked: { + // Storage of the new value is needed because the query + // of searchField.visible still delivers the old value. + var searchFieldVisible = !searchField.visible + + if (searchFieldVisible) { + iconItem.source = "qrc:///images/cancel.svg" + searchField.forceActiveFocus(Qt.MouseFocusReason) + Qt.inputMethod.show() + } else { + iconItem.source = "qrc:///images/search.svg" + iconItem.forceActiveFocus(Qt.MouseFocusReason) + searchField.text = "" + Qt.inputMethod.hide() + } + + searchField.visible = searchFieldVisible + } + } + } +} diff --git a/resources/qml/Governikus/ProviderView/+mobile/+ios/+phone/ProviderView.qml b/resources/qml/Governikus/ProviderView/+mobile/+ios/+phone/ProviderView.qml new file mode 100644 index 0000000..d234be9 --- /dev/null +++ b/resources/qml/Governikus/ProviderView/+mobile/+ios/+phone/ProviderView.qml @@ -0,0 +1,108 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.Provider 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 +import Governikus.Type.ProviderCategoryFilterModel 1.0 + +SectionPage { + id: baseItem + + readonly property var category: ProviderCategoryFilterModel.categories.length === 0 ? "" : ProviderCategoryFilterModel.categories[0] + + Component.onCompleted: ProviderCategoryFilterModel.sortByCategoryFirst(true) + + onCategoryChanged: { + ProviderCategoryFilterModel.sortByCategoryFirst(category === "") + } + + leftTitleBarAction: TitleBarAction { + state: category !== "" ? "back" : "" + onClicked: { + if (state === "back") { + ProviderCategoryFilterModel.setCategorySelection("") + } + } + } + + headerTitleBarAction: TitleBarAction { + text: Category.displayString(category) + settingsModel.translationTrigger + font.bold: true + } + + subTitleBarAction: SearchBar { + width: baseItem.width + onSearchTextChanged: ProviderCategoryFilterModel.searchString = searchText + } + + titleBarColor: Category.displayColor(category) + + ProviderDetailView { + id: providerDetailView + visible: false + } + + content: Column { + width: baseItem.width + + Rectangle { + height: Utils.dp(200) + width: parent.width + color: Constants.background_color + visible: ProviderCategoryFilterModel.rowCount === 0 && !additionalResults.visible + + Text { + color: Constants.secondary_text + anchors.centerIn: parent + text: qsTr("No match found") + settingsModel.translationTrigger + font.pixelSize: Constants.normal_font_size + } + } + + ProviderSectionDelegate { + id: allSection + sectionName: "all" + visible: ProviderCategoryFilterModel.searchString === "" && ProviderCategoryFilterModel.categories.length === 0 + height: visible ? Constants.provider_section_height : 0 + } + + ListView { + id: providerListMain + height: childrenRect.height + width: baseItem.width + interactive: false + visible: category === "" + + model: ProviderCategoryFilterModel + + delegate: ProviderViewDelegate { + height: visible ? Constants.provider_section_height : 0 + visible: ProviderCategoryFilterModel.searchString !== "" + } + + section.property: "providerCategory" + section.labelPositioning: ViewSection.InlineLabels | ViewSection.CurrentLabelAtStart + section.delegate: ProviderSectionDelegate { + sectionName: section + } + } + + ListView { + id: providerListSection + height: childrenRect.height + width: baseItem.width + interactive: false + visible: !providerListMain.visible + + model: ProviderCategoryFilterModel + + delegate: ProviderViewDelegate {} + } + + AdditionalResultsItem { + id: additionalResults + width: parent.width + } + } +} diff --git a/resources/qml/Governikus/ProviderView/+mobile/+ios/+tablet/ProviderView.qml b/resources/qml/Governikus/ProviderView/+mobile/+ios/+tablet/ProviderView.qml new file mode 100644 index 0000000..4c36a1f --- /dev/null +++ b/resources/qml/Governikus/ProviderView/+mobile/+ios/+tablet/ProviderView.qml @@ -0,0 +1,177 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.Provider 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 +import Governikus.Type.ProviderCategoryFilterModel 1.0 + +SectionPage { + id: baseItem + + headerTitleBarAction: TitleBarAction { text: qsTr("Provider") + settingsModel.translationTrigger; font.bold: true } + subTitleBarAction: SearchBar { + width: baseItem.width + onSearchTextChanged: ProviderCategoryFilterModel.searchString = searchText + } + + titleBarColor: Constants.blue + + visible: false + property bool wasVisible: false + onVisibleChanged: wasVisible = true + + readonly property var category: ProviderCategoryFilterModel.categories.length === 0 ? "" : ProviderCategoryFilterModel.categories[0] + + readonly property int headerHeight: Utils.dp(54) + readonly property int separatorHeight: Utils.dp(2) + + ProviderDetailView { + id: providerDetailView + visible: false + } + + function pushProviderDetails(pModel) { + historyModel.nameFilter.setProviderAddress(pModel.providerAddress) + providerDetailView.providerModelItem = pModel + firePush(providerDetailView) + } + + Column { + id: content + + width: parent.width + + Rectangle { + + height: baseItem.headerHeight + width: parent.width + + color: "white" + + Row { + id: checkBoxesItem + + height: parent.height + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + + padding: Utils.dp(30) + spacing: Utils.dp(30) + + transformOrigin: Item.Center + scale: Math.min(parent.width / width, 1) + + CategoryCheckbox { + id: checkBoxCitizen + + category: "citizen" + imageSource: Category.imageSource("citizen") + text: qsTr("Citizen services") + settingsModel.translationTrigger + } + + CategoryCheckbox { + id: checkBoxFinance + + category: "finance" + imageSource: Category.imageSource("finance") + text: qsTr("Financials") + settingsModel.translationTrigger + } + + CategoryCheckbox { + id: checkBoxInsurance + + category: "insurance" + imageSource: Category.imageSource("insurance") + text: qsTr("Insurances") + settingsModel.translationTrigger + } + + CategoryCheckbox { + id: checkBoxOther + + category: "other" + imageSource: Category.imageSource("other") + text: qsTr("Other services") + settingsModel.translationTrigger + } + } + } + + Rectangle { + height: baseItem.separatorHeight + width: parent.width + + color: Constants.grey_border + } + + Rectangle { + id: mainPane + + height: baseItem.height - (baseItem.headerHeight + baseItem.separatorHeight) + width: parent.width + anchors.horizontalCenter: parent.horizontalCenter + color: Constants.background_color + + Text { + id: noResultsText + color: Constants.secondary_text + + anchors.centerIn: mainPane + text: qsTr("No match found") + settingsModel.translationTrigger + + wrapMode: Text.WordWrap + font.pixelSize: Constants.normal_font_size + visible: !flickable.visible + } + + Flickable { + id: flickable + anchors.fill: mainPane + clip: true + flickableDirection: Flickable.VerticalFlick + visible: grid.hasResults + + contentHeight: grid.height + contentWidth: parent.width + + onContentYChanged: { + if (contentY < 0) { contentY = 0 /* prevent flicking over the top */} + } + + Grid { + id: grid + columns: Math.floor((parent.width - Constants.component_spacing) / (Utils.dp(196) + Constants.component_spacing)) + padding: Constants.component_spacing + spacing: Constants.component_spacing + width: parent.width + + property int cardHeight: (flickable.height - Constants.component_spacing) / 2 + property int cardWidth: (flickable.width - (grid.columns + 1) * Constants.component_spacing) / grid.columns + property bool hasResults: gridRepeater.count > 0 || additionalResults.totalHits > 0 + + Repeater { + id: gridRepeater + + model: ProviderCategoryFilterModel + + ProviderCard { + width: grid.cardWidth + headerHeight: width / 1.80 + textHeight: Utils.dp(64) + footerHeight: Utils.dp(30) + pushFunction: baseItem.pushProviderDetails + providerModelItem: baseItem.wasVisible ? model : undefined + } + } + + AdditionalResultsItem { + id: additionalResults + width: grid.cardWidth + headerHeight: width / 1.80 + textHeight: Utils.dp(64) + footerHeight: Utils.dp(30) + } + } + } + } + } +} diff --git a/resources/qml/Governikus/ProviderView/+mobile/+ios/SearchBar.qml b/resources/qml/Governikus/ProviderView/+mobile/+ios/SearchBar.qml new file mode 100644 index 0000000..05a454a --- /dev/null +++ b/resources/qml/Governikus/ProviderView/+mobile/+ios/SearchBar.qml @@ -0,0 +1,146 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 + + +Item { + readonly property alias searchText: searchField.text + + id: baseItem + height: Constants.searchbar_height + + MouseArea { + id: pageArea + onClicked: pageArea.focus = true + + height: Constants.searchbar_height + width: parent.width + anchors.bottom: parent.bottom + + Rectangle { + anchors.left: parent.left + anchors.right: cancelButton.left + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.leftMargin: Utils.dp(8) + anchors.rightMargin: Utils.dp(10) + anchors.topMargin: Utils.dp(6) + anchors.bottomMargin: Utils.dp(8) + radius: Utils.dp(6) + color: "white" + + Image { + id: glassIcon + height: parent.height - 2 * anchors.leftMargin + width: height + anchors.left: parent.left + anchors.leftMargin: Utils.dp(4) + anchors.verticalCenter: parent.verticalCenter + source: "qrc:///images/iOS/search_icon.svg" + } + + TextField { + id: searchField + anchors.margins: Utils.dp(8) + anchors.verticalCenter: parent.verticalCenter + anchors.left: glassIcon.right + anchors.right: textEditX.left + horizontalAlignment: Text.AlignLeft + background: Item {} + + onVisibleChanged: { + if (visible === false){ + Qt.inputMethod.hide() + } + } + } + + Label { + id: searchLabel + anchors.left: searchField.left + anchors.leftMargin: Utils.dp(8) + anchors.verticalCenter: searchField.verticalCenter + text: qsTr("Search") + settingsModel.translationTrigger + color: Constants.grey + font.pixelSize: Constants.normal_font_size + visible: searchField.text.trim().length === 0 + } + + MouseArea { + id: textEditX + height: parent.height + width: height + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + enabled: searchField.text.length > 0 + + onClicked: { + searchField.text = "" + } + + Image { + anchors.margins: Utils.dp(4) + anchors.fill: parent + source: "qrc:///images/iOS/search_cancel.svg" + visible: parent.enabled + } + } + } + + DimmableTextButton { + id: cancelButton + + anchors.right: parent.right + anchors.rightMargin: visible ? Utils.dp(8) : 0 + anchors.verticalCenter: parent.verticalCenter + clip: true + visible: false + width: visible ? cancelButton.implicitWidth : 0 + font.family: "Helvetica" + font.pixelSize: Constants.normal_font_size + color: "white" + text: qsTr("Cancel") + settingsModel.translationTrigger + onClicked: { + searchField.text = "" + pageArea.clicked(null) + } + } + } + + states: [ + State { + name: "searchBarActive" + when: searchField.activeFocus || searchField.text.trim().length !== 0 + + PropertyChanges { + target: cancelButton + visible: true + } + }, + State { + name: "" + StateChangeScript { + script: { + Qt.inputMethod.hide() + } + } + + PropertyChanges { + target: cancelButton + visible: false + } + } + ] + + transitions: [ + Transition { + from: "*" + to: "*" + AnchorAnimation { + duration: 200; + easing.type: Easing.InOutQuad + } + } + ] +} diff --git a/resources/qml/Governikus/ProviderView/+mobile/+phone/AdditionalResultsItem.qml b/resources/qml/Governikus/ProviderView/+mobile/+phone/AdditionalResultsItem.qml new file mode 100644 index 0000000..9472f3a --- /dev/null +++ b/resources/qml/Governikus/ProviderView/+mobile/+phone/AdditionalResultsItem.qml @@ -0,0 +1,60 @@ +import QtQuick 2.10 +import QtQuick.Layouts 1.2 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.ProviderCategoryFilterModel 1.0 + +Rectangle { + id: baseItem + height: Constants.provider_section_height + + property int totalHits: ProviderCategoryFilterModel.additionalResultCount + + visible: totalHits > 0 && ProviderCategoryFilterModel.categories.length > 0 && ProviderCategoryFilterModel.categories.indexOf("all") === -1 + + Item { + anchors.fill: parent + anchors.topMargin: Utils.dp(5) + anchors.bottomMargin: Utils.dp(5) + + Image { + id: allImage + source: Category.imageSource("all") + asynchronous: true + height: parent.height + width: parent.width * 0.15 + fillMode: Image.PreserveAspectFit + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: ProviderStyle.leftIconMargin + } + + Text { + anchors.verticalCenter: parent.verticalCenter + anchors.left: allImage.right + anchors.leftMargin: ProviderStyle.leftProviderListMargin + color: ProviderStyle.categoryColor + font.pixelSize: ProviderStyle.categoryFontPixelSize + font.bold: ProviderStyle.categoryFontBold + elide: Text.ElideRight + text: '' + qsTr("Additional results:") + " " + baseItem.totalHits + '' + settingsModel.translationTrigger + } + + Text { + anchors.right: parent.right + anchors.rightMargin: Utils.dp(5) + anchors.verticalCenter: parent.verticalCenter + + text: ">" + color: Constants.grey + font.pixelSize: Constants.normal_font_size + visible: ProviderStyle.showCategoryRightArrow + } + + MouseArea { + anchors.fill: parent + onClicked: ProviderCategoryFilterModel.setCategorySelection("") + } + } +} diff --git a/resources/qml/Governikus/ProviderView/+mobile/+tablet/AdditionalResultsItem.qml b/resources/qml/Governikus/ProviderView/+mobile/+tablet/AdditionalResultsItem.qml new file mode 100644 index 0000000..ee45079 --- /dev/null +++ b/resources/qml/Governikus/ProviderView/+mobile/+tablet/AdditionalResultsItem.qml @@ -0,0 +1,71 @@ +import QtQuick 2.10 +import QtQuick.Layouts 1.2 + +import Governikus.Global 1.0 +import Governikus.Type.ProviderCategoryFilterModel 1.0 + +Rectangle { + id: baseItem + height: column.height + + property int headerHeight: 0 + property int textHeight: 0 + property int footerHeight: 0 + property int totalHits: ProviderCategoryFilterModel.additionalResultCount + + visible: totalHits > 0 + + Column { + id: column + width: baseItem.width + + Image { + id: backgroundImage + source: Category.backgroundImageSource("all") + asynchronous: true + height: baseItem.headerHeight + width: parent.width + fillMode: Image.PreserveAspectCrop + anchors.horizontalCenter: parent.horizontalCenter + + Image { + id: icon + source: Category.imageSource("all") + asynchronous: true + height: backgroundImage.height * 0.5 + width: height + fillMode: Image.PreserveAspectFit + anchors.horizontalCenter: backgroundImage.horizontalCenter + anchors.bottom: backgroundImage.bottom + anchors.bottomMargin: Utils.dp(20) + } + } + + Rectangle { + id: textRectangle + height: baseItem.textHeight + width: parent.width + + Text { + text: '' + qsTr("Additional results:") + " " + baseItem.totalHits + '' + settingsModel.translationTrigger + + anchors.centerIn: parent + + font.bold: true + font.pixelSize: Constants.normal_font_size + color: Constants.secondary_text + } + } + + Rectangle { + height: baseItem.footerHeight + width: parent.width + color: Constants.blue + } + } + + MouseArea { + anchors.fill: parent + onClicked: ProviderCategoryFilterModel.addAdditionalResultCategories() + } +} diff --git a/resources/qml/Governikus/ProviderView/+mobile/+tablet/ProviderCard.qml b/resources/qml/Governikus/ProviderView/+mobile/+tablet/ProviderCard.qml new file mode 100644 index 0000000..f57644a --- /dev/null +++ b/resources/qml/Governikus/ProviderView/+mobile/+tablet/ProviderCard.qml @@ -0,0 +1,70 @@ +import QtQuick 2.10 +import QtQuick.Layouts 1.2 + +import Governikus.Global 1.0 +import Governikus.Provider 1.0 + +Rectangle { + id: baseItem + height: column.height + color: Category.displayColor(provider.category) + + property int headerHeight: 0 + property int textHeight: 0 + property int footerHeight: 0 + + property alias providerModelItem: provider.modelItem + property var pushFunction: function(model) {} + + ProviderModelItem { + id: provider + } + + Column { + id: column + width: baseItem.width + + Image { + source: provider.image + asynchronous: true + height: baseItem.headerHeight + width: parent.width + fillMode: Image.PreserveAspectCrop + anchors.horizontalCenter: parent.horizontalCenter + } + + ProviderCardNameRow { + height: baseItem.textHeight + providerName: provider.longName !== "" ? provider.longName : provider.shortName + headerIcon: provider.icon + providerCategory: provider.category + } + + Rectangle { + color: Category.displayColor(provider.category) + height: baseItem.footerHeight + width: parent.width + + Text { + text: provider.homepageBase + + anchors.centerIn: parent + + leftPadding: Constants.pane_padding + rightPadding: Constants.pane_padding + elide: Text.ElideRight + maximumLineCount: 1 + + font.pixelSize: Constants.normal_font_size + color: "white" + + scale: Math.min(1, parent.width / (contentWidth + leftPadding + rightPadding)) + } + } + } + + MouseArea { + anchors.fill: parent + onClicked: baseItem.pushFunction(providerModelItem) + } +} diff --git a/resources/qml/Governikus/ProviderView/+mobile/+tablet/ProviderCardNameRow.qml b/resources/qml/Governikus/ProviderView/+mobile/+tablet/ProviderCardNameRow.qml new file mode 100644 index 0000000..f5ddb1d --- /dev/null +++ b/resources/qml/Governikus/ProviderView/+mobile/+tablet/ProviderCardNameRow.qml @@ -0,0 +1,48 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 + + +Rectangle { + readonly property int padding: Constants.pane_padding / 2 + + property string providerName + property string headerIcon + property int nameHeight + property string providerCategory + + width: parent.width + + Image { + id: image + source: parent.headerIcon !== "" ? + parent.headerIcon : + Category.buttonImageSource(parent.providerCategory) + asynchronous: true + height: parent.height + width: height + fillMode: Image.PreserveAspectFit + anchors.top: parent.top + anchors.topMargin: -parent.padding + anchors.left: parent.left + anchors.leftMargin: parent.padding + } + + Text { + text: '' + providerName + '' + anchors.left: image.right + anchors.leftMargin: parent.padding + anchors.top: parent.top + anchors.topMargin: parent.height * 0.05 + anchors.right: parent.right + anchors.rightMargin: parent.padding + elide: Text.ElideRight + maximumLineCount: 4 + wrapMode: Text.Wrap + lineHeightMode: Text.FixedHeight + lineHeight: parent.height * 0.90 / 4 + font.bold: true + font.pixelSize: Constants.small_font_size + color: Constants.secondary_text + } +} diff --git a/resources/qml/Governikus/ProviderView/+mobile/CategoryCheckbox.qml b/resources/qml/Governikus/ProviderView/+mobile/CategoryCheckbox.qml new file mode 100644 index 0000000..3a0bf23 --- /dev/null +++ b/resources/qml/Governikus/ProviderView/+mobile/CategoryCheckbox.qml @@ -0,0 +1,51 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.2 + +import Governikus.Global 1.0 +import Governikus.Type.ProviderCategoryFilterModel 1.0 + +Item { + id: baseItem + height: parent.height + width: mainContent.width + anchors.verticalCenter: parent.verticalCenter + + property string category: "" + property alias imageSource: icon.source + property alias text: label.text + + Row { + id: mainContent + height: parent.height + spacing: Utils.dp(5) + anchors.verticalCenter: parent.verticalCenter + + Image { + id: icon + height: baseItem.height * 0.7 + width: height + fillMode: Image.PreserveAspectFit + anchors.verticalCenter: parent.verticalCenter + } + + Text { + id: label + color: Constants.secondary_text + font.pixelSize: Constants.normal_font_size + anchors.verticalCenter: parent.verticalCenter + } + + GCheckBox { + id: checkBox + anchors.verticalCenter: parent.verticalCenter + visible: true + checked: ProviderCategoryFilterModel.categories.indexOf(baseItem.category) !== -1 + } + } + + MouseArea { + anchors.fill: parent + onClicked: ProviderCategoryFilterModel.updateCategorySelection(category, !checkBox.checked) + } +} diff --git a/resources/qml/Governikus/ProviderView/+mobile/DimmableTextButton.qml b/resources/qml/Governikus/ProviderView/+mobile/DimmableTextButton.qml new file mode 100644 index 0000000..7430099 --- /dev/null +++ b/resources/qml/Governikus/ProviderView/+mobile/DimmableTextButton.qml @@ -0,0 +1,33 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 + +Label { + id: label + signal clicked + + color: white + + MouseArea { + id: buttonArea + anchors.fill: parent + onClicked: { + parent.clicked() + } + } + + states: [ + State { + name: "pressed" + when: buttonArea.pressed + + PropertyChanges { + target: label + color: Constants.grey + } + } + ] + transitions: [ + ] +} diff --git a/resources/qml/Governikus/ProviderView/+mobile/ProviderDelegateModel.qml b/resources/qml/Governikus/ProviderView/+mobile/ProviderDelegateModel.qml new file mode 100644 index 0000000..2e44518 --- /dev/null +++ b/resources/qml/Governikus/ProviderView/+mobile/ProviderDelegateModel.qml @@ -0,0 +1,45 @@ +import QtQml.Models 2.2 +import QtQuick 2.10 + +DelegateModel { + id: root + property string searchText + + items { + includeByDefault: true + onChanged: filterBy(root.searchText) + } + + groups: DelegateModelGroup { + name: "filteredItems" + includeByDefault: false + } + + filterOnGroup: "filteredItems" + + onSearchTextChanged: { + filterBy(searchText); + } + + function match(entry, filterString) { + var validFilterString = filterString ? filterString : "" + + return entry.model.display.toLowerCase().search(validFilterString.toLowerCase()) !== -1 || + entry.model.providerAddress.toLowerCase().search(validFilterString.toLowerCase()) !== -1 + } + + function filterBy(filterString) { + for (var i = 0; i < items.count; i++ ) { + var entry = items.get(i) + + if (match(entry, filterString)) { + entry.groups = ["filteredItems", "items"] + // it's now visible, because we filter on group "filteredItems" + } + else { + entry.groups = ["items"] + } + } + } +} + diff --git a/resources/qml/Governikus/ProviderView/+mobile/ProviderSectionDelegate.qml b/resources/qml/Governikus/ProviderView/+mobile/ProviderSectionDelegate.qml new file mode 100644 index 0000000..d1b769f --- /dev/null +++ b/resources/qml/Governikus/ProviderView/+mobile/ProviderSectionDelegate.qml @@ -0,0 +1,69 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.ProviderCategoryFilterModel 1.0 + +Rectangle { + property string sectionName: "" + + width: parent.width + height: Constants.provider_section_height + clip: true + + Item { + anchors.fill: parent + anchors.topMargin: Utils.dp(5) + anchors.bottomMargin: Utils.dp(5) + + Image { + id: sectionImage + source: Category.imageSource(sectionName) + asynchronous: true + height: parent.height + width: parent.width * 0.15 + fillMode: Image.PreserveAspectFit + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: ProviderStyle.leftIconMargin + } + + Text { + anchors.verticalCenter: parent.verticalCenter + anchors.left: sectionImage.right + anchors.leftMargin: ProviderStyle.leftProviderListMargin + color: ProviderStyle.categoryColor + font.pixelSize: ProviderStyle.categoryFontPixelSize + font.bold: ProviderStyle.categoryFontBold + elide: Text.ElideRight + text: Category.displayString(sectionName) + settingsModel.translationTrigger + } + + Text { + anchors.right: parent.right + anchors.rightMargin: Utils.dp(5) + anchors.verticalCenter: parent.verticalCenter + + text: ">" + color: Constants.grey + font.pixelSize: Constants.normal_font_size + visible: ProviderStyle.showCategoryRightArrow + } + + MouseArea { + anchors.fill: parent + onClicked: { + ProviderCategoryFilterModel.setCategorySelection(sectionName) + } + } + } + + Rectangle { + width: parent.width * 0.85 + anchors.top: parent.bottom + anchors.topMargin: -height + anchors.right: parent.right + height: 1 + color: Constants.grey + } +} diff --git a/resources/qml/Governikus/ProviderView/AdditionalResultsItem.qml b/resources/qml/Governikus/ProviderView/AdditionalResultsItem.qml deleted file mode 100644 index 6eb6686..0000000 --- a/resources/qml/Governikus/ProviderView/AdditionalResultsItem.qml +++ /dev/null @@ -1,58 +0,0 @@ -import QtQuick 2.6 -import QtQuick.Layouts 1.2 - -import Governikus.Global 1.0 - -Rectangle { - id: baseItem - height: Constants.provider_section_height - - property int totalHits: providerModel.additionalResultCount - - visible: totalHits > 0 && providerModel.categories.length > 0 && providerModel.categories.indexOf("all") === -1 - - Item { - anchors.fill: parent - anchors.topMargin: Utils.dp(5) - anchors.bottomMargin: Utils.dp(5) - - Image { - id: allImage - source: Category.imageSource("all") - asynchronous: true - height: parent.height - width: parent.width * 0.15 - fillMode: Image.PreserveAspectFit - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: providerStyle.leftIconMargin - } - - Text { - anchors.verticalCenter: parent.verticalCenter - anchors.left: allImage.right - anchors.leftMargin: providerStyle.leftProviderListMargin - color: providerStyle.categoryColor - font.pixelSize: providerStyle.categoryFontPixelSize - font.bold: providerStyle.categoryFontBold - elide: Text.ElideRight - text: '' + qsTr("Additional results:") + " " + baseItem.totalHits + '' + settingsModel.translationTrigger - } - - Text { - anchors.right: parent.right - anchors.rightMargin: Utils.dp(5) - anchors.verticalCenter: parent.verticalCenter - - text: ">" - color: Constants.grey - font.pixelSize: Constants.normal_font_size - visible: providerStyle.showCategoryRightArrow - } - - MouseArea { - anchors.fill: parent - onClicked: providerModel.setCategorySelection("") - } - } -} diff --git a/resources/qml/Governikus/ProviderView/AdditionalResultsItem_tablet.qml b/resources/qml/Governikus/ProviderView/AdditionalResultsItem_tablet.qml deleted file mode 100644 index e329dea..0000000 --- a/resources/qml/Governikus/ProviderView/AdditionalResultsItem_tablet.qml +++ /dev/null @@ -1,70 +0,0 @@ -import QtQuick 2.6 -import QtQuick.Layouts 1.2 - -import Governikus.Global 1.0 - -Rectangle { - id: baseItem - height: column.height - - property int headerHeight: 0 - property int textHeight: 0 - property int footerHeight: 0 - property int totalHits: providerModel.additionalResultCount - - visible: totalHits > 0 - - Column { - id: column - width: baseItem.width - - Image { - id: backgroundImage - source: Category.backgroundImageSource("all") - asynchronous: true - height: baseItem.headerHeight - width: parent.width - fillMode: Image.PreserveAspectCrop - anchors.horizontalCenter: parent.horizontalCenter - - Image { - id: icon - source: Category.imageSource("all") - asynchronous: true - height: backgroundImage.height * 0.5 - width: height - fillMode: Image.PreserveAspectFit - anchors.horizontalCenter: backgroundImage.horizontalCenter - anchors.bottom: backgroundImage.bottom - anchors.bottomMargin: Utils.dp(20) - } - } - - Rectangle { - id: textRectangle - height: baseItem.textHeight - width: parent.width - - Text { - text: '' + qsTr("Additional results:") + " " + baseItem.totalHits + '' + settingsModel.translationTrigger - - anchors.centerIn: parent - - font.bold: true - font.pixelSize: Constants.normal_font_size - color: PlatformConstants.secondary_text - } - } - - Rectangle { - height: baseItem.footerHeight - width: parent.width - color: Constants.blue - } - } - - MouseArea { - anchors.fill: parent - onClicked: providerModel.addAdditionalResultCategories() - } -} diff --git a/resources/qml/Governikus/ProviderView/CategoryCheckbox_tablet.qml b/resources/qml/Governikus/ProviderView/CategoryCheckbox_tablet.qml deleted file mode 100644 index 86cad75..0000000 --- a/resources/qml/Governikus/ProviderView/CategoryCheckbox_tablet.qml +++ /dev/null @@ -1,49 +0,0 @@ -import QtQuick 2.6 -import QtQuick.Layouts 1.2 - -import Governikus.Global 1.0 - -Item { - id: baseItem - height: parent.height - width: mainContent.width - anchors.verticalCenter: parent.verticalCenter - - property string category: "" - property alias imageSource: icon.source - property alias text: label.text - - Row { - id: mainContent - height: parent.height - spacing: Utils.dp(5) - anchors.verticalCenter: parent.verticalCenter - - Image { - id: icon - height: baseItem.height * 0.7 - width: height - fillMode: Image.PreserveAspectFit - anchors.verticalCenter: parent.verticalCenter - } - - Text { - id: label - color: Constants.secondary_text - font.pixelSize: Constants.normal_font_size - anchors.verticalCenter: parent.verticalCenter - } - - GCheckBox { - id: checkbox - anchors.verticalCenter: parent.verticalCenter - visible: true - checked: providerModel.categories.indexOf(baseItem.category) !== -1 - } - } - - MouseArea { - anchors.fill: parent - onClicked: providerModel.updateCategorySelection(category, !checkbox.checked) - } -} diff --git a/resources/qml/Governikus/ProviderView/DimmableTextButton.qml b/resources/qml/Governikus/ProviderView/DimmableTextButton.qml deleted file mode 100644 index 0857e16..0000000 --- a/resources/qml/Governikus/ProviderView/DimmableTextButton.qml +++ /dev/null @@ -1,33 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 - -import Governikus.Global 1.0 - -Label { - id: label - signal clicked - - color: white - - MouseArea { - id: buttonArea - anchors.fill: parent - onClicked: { - parent.clicked() - } - } - - states: [ - State { - name: "pressed" - when: buttonArea.pressed - - PropertyChanges { - target: label - color: Constants.grey - } - } - ] - transitions: [ - ] -} diff --git a/resources/qml/Governikus/ProviderView/ProviderCardNameRow_tablet.qml b/resources/qml/Governikus/ProviderView/ProviderCardNameRow_tablet.qml deleted file mode 100644 index b297c85..0000000 --- a/resources/qml/Governikus/ProviderView/ProviderCardNameRow_tablet.qml +++ /dev/null @@ -1,48 +0,0 @@ -import QtQuick 2.6 - -import Governikus.Global 1.0 - - -Rectangle { - readonly property int padding: Constants.pane_padding / 2 - - property string providerName - property string headerIcon - property int nameHeight - property string providerCategory - - width: parent.width - - Image { - id: image - source: parent.headerIcon !== "" ? - parent.headerIcon : - Category.buttonImageSource(parent.providerCategory) - asynchronous: true - height: parent.height - width: height - fillMode: Image.PreserveAspectFit - anchors.top: parent.top - anchors.topMargin: -parent.padding - anchors.left: parent.left - anchors.leftMargin: parent.padding - } - - Text { - text: '' + providerName + '' - anchors.left: image.right - anchors.leftMargin: parent.padding - anchors.top: parent.top - anchors.topMargin: parent.height * 0.05 - anchors.right: parent.right - anchors.rightMargin: parent.padding - elide: Text.ElideRight - maximumLineCount: 4 - wrapMode: Text.Wrap - lineHeightMode: Text.FixedHeight - lineHeight: parent.height * 0.90 / 4 - font.bold: true - font.pixelSize: Constants.small_font_size - color: PlatformConstants.secondary_text - } -} diff --git a/resources/qml/Governikus/ProviderView/ProviderCard_tablet.qml b/resources/qml/Governikus/ProviderView/ProviderCard_tablet.qml deleted file mode 100644 index 19ca431..0000000 --- a/resources/qml/Governikus/ProviderView/ProviderCard_tablet.qml +++ /dev/null @@ -1,70 +0,0 @@ -import QtQuick 2.6 -import QtQuick.Layouts 1.2 - -import Governikus.Global 1.0 -import Governikus.Provider 1.0 - -Rectangle { - id: baseItem - height: column.height - color: Category.displayColor(provider.category) - - property int headerHeight: 0 - property int textHeight: 0 - property int footerHeight: 0 - - property alias providerModelItem: provider.modelItem - property var pushFunction: function(model) {} - - ProviderModelItem { - id: provider - } - - Column { - id: column - width: baseItem.width - - Image { - source: provider.image - asynchronous: true - height: baseItem.headerHeight - width: parent.width - fillMode: Image.PreserveAspectCrop - anchors.horizontalCenter: parent.horizontalCenter - } - - ProviderCardNameRow_tablet { - height: baseItem.textHeight - providerName: provider.longName !== "" ? provider.longName : provider.shortName - headerIcon: provider.icon - providerCategory: provider.category - } - - Rectangle { - color: Category.displayColor(provider.category) - height: baseItem.footerHeight - width: parent.width - - Text { - text: provider.homepageBase - - anchors.centerIn: parent - - leftPadding: Constants.pane_padding - rightPadding: Constants.pane_padding - elide: Text.ElideRight - maximumLineCount: 1 - - font.pixelSize: Constants.normal_font_size - color: "white" - - scale: Math.min(1, parent.width / (contentWidth + leftPadding + rightPadding)) - } - } - } - - MouseArea { - anchors.fill: parent - onClicked: baseItem.pushFunction(providerModelItem) - } -} diff --git a/resources/qml/Governikus/ProviderView/ProviderContactInfoItem.qml b/resources/qml/Governikus/ProviderView/ProviderContactInfoItem.qml deleted file mode 100644 index 4b11736..0000000 --- a/resources/qml/Governikus/ProviderView/ProviderContactInfoItem.qml +++ /dev/null @@ -1,3 +0,0 @@ -Item { - id: dummy -} diff --git a/resources/qml/Governikus/ProviderView/ProviderDelegateModel.qml b/resources/qml/Governikus/ProviderView/ProviderDelegateModel.qml deleted file mode 100644 index 86d8d81..0000000 --- a/resources/qml/Governikus/ProviderView/ProviderDelegateModel.qml +++ /dev/null @@ -1,46 +0,0 @@ -import QtQml.Models 2.2 -import QtQuick 2.5 - -DelegateModel { - id: root - property string searchText - - items { - includeByDefault: true - onChanged: filterBy(root.searchText) - } - - groups: DelegateModelGroup { - name: "filteredItems" - includeByDefault: false - } - - filterOnGroup: "filteredItems" - - onSearchTextChanged: { - filterBy(searchText); - } - - function match(entry, filterString) { - var validFilterString = filterString ? filterString : "" - - return entry.model.display.toLowerCase().search(validFilterString.toLowerCase()) !== -1 || - entry.model.providerAddress.toLowerCase().search(validFilterString.toLowerCase()) !== -1 - } - - function filterBy(filterString) { - for (var i = 0; i < items.count; i++ ) { - var entry = items.get(i) - - if (match(entry, filterString)) { - entry.groups = ["filteredItems", "items"] - // it's now visible, because we filter on group "filteredItems" - } - else { - entry.groups = ["items"] - } - } - } - -} - diff --git a/resources/qml/Governikus/ProviderView/ProviderSectionDelegate.qml b/resources/qml/Governikus/ProviderView/ProviderSectionDelegate.qml deleted file mode 100644 index 981a152..0000000 --- a/resources/qml/Governikus/ProviderView/ProviderSectionDelegate.qml +++ /dev/null @@ -1,67 +0,0 @@ -import QtQuick 2.7 - -import Governikus.Global 1.0 - -Rectangle { - property string sectionName: "" - - width: parent.width - height: Constants.provider_section_height - clip: true - - Item { - anchors.fill: parent - anchors.topMargin: Utils.dp(5) - anchors.bottomMargin: Utils.dp(5) - - Image { - id: sectionImage - source: Category.imageSource(sectionName) - asynchronous: true - height: parent.height - width: parent.width * 0.15 - fillMode: Image.PreserveAspectFit - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: providerStyle.leftIconMargin - } - - Text { - anchors.verticalCenter: parent.verticalCenter - anchors.left: sectionImage.right - anchors.leftMargin: providerStyle.leftProviderListMargin - color: providerStyle.categoryColor - font.pixelSize: providerStyle.categoryFontPixelSize - font.bold: providerStyle.categoryFontBold - elide: Text.ElideRight - text: Category.displayString(sectionName) + settingsModel.translationTrigger - } - - Text { - anchors.right: parent.right - anchors.rightMargin: Utils.dp(5) - anchors.verticalCenter: parent.verticalCenter - - text: ">" - color: Constants.grey - font.pixelSize: Constants.normal_font_size - visible: providerStyle.showCategoryRightArrow - } - - MouseArea { - anchors.fill: parent - onClicked: { - providerModel.setCategorySelection(sectionName) - } - } - } - - Rectangle { - width: parent.width * 0.85 - anchors.top: parent.bottom - anchors.topMargin: -height - anchors.right: parent.right - height: 1 - color: Constants.grey - } -} diff --git a/resources/qml/Governikus/ProviderView/ProviderView.qml b/resources/qml/Governikus/ProviderView/ProviderView.qml deleted file mode 100644 index 4b11736..0000000 --- a/resources/qml/Governikus/ProviderView/ProviderView.qml +++ /dev/null @@ -1,3 +0,0 @@ -Item { - id: dummy -} diff --git a/resources/qml/Governikus/ProviderView/SearchBar.qml b/resources/qml/Governikus/ProviderView/SearchBar.qml deleted file mode 100644 index 4b11736..0000000 --- a/resources/qml/Governikus/ProviderView/SearchBar.qml +++ /dev/null @@ -1,3 +0,0 @@ -Item { - id: dummy -} diff --git a/resources/qml/Governikus/ProviderView/qmldir b/resources/qml/Governikus/ProviderView/qmldir index 8b489f7..a0c3c96 100644 --- a/resources/qml/Governikus/ProviderView/qmldir +++ b/resources/qml/Governikus/ProviderView/qmldir @@ -1,2 +1,13 @@ module ProviderView + +internal AdditionalResultsItem AdditionalResultsItem.qml +internal CategoryCheckbox CategoryCheckbox.qml +internal DimmableTextButton DimmableTextButton.qml +internal ProviderCardNameRow ProviderCardNameRow.qml +internal ProviderCard ProviderCard.qml +internal ProviderContactInfoItem ProviderContactInfoItem.qml +internal ProviderDelegateModel ProviderDelegateModel.qml +internal ProviderSectionDelegate ProviderSectionDelegate.qml +internal SearchBar SearchBar.qml + ProviderView 1.0 ProviderView.qml diff --git a/resources/qml/Governikus/RemoteServiceView/AvailableDevicesListDelegate.qml b/resources/qml/Governikus/RemoteServiceView/AvailableDevicesListDelegate.qml index d5db823..9222cf6 100644 --- a/resources/qml/Governikus/RemoteServiceView/AvailableDevicesListDelegate.qml +++ b/resources/qml/Governikus/RemoteServiceView/AvailableDevicesListDelegate.qml @@ -1,5 +1,5 @@ -import QtQuick 2.7 -import QtQuick.Controls 2.2 +import QtQuick 2.10 +import QtQuick.Controls 2.3 import QtQuick.Layouts 1.1 import Governikus.Global 1.0 @@ -17,7 +17,7 @@ MouseArea { id: nameText color: Constants.secondary_text width: parent.width - font.pixelSize: Utils.sp(16) + font.pixelSize: Utils.dp(16) anchors.verticalCenter: parent.verticalCenter opacity: 0.87 text: { diff --git a/resources/qml/Governikus/RemoteServiceView/KnownDevicesListDelegate.qml b/resources/qml/Governikus/RemoteServiceView/KnownDevicesListDelegate.qml index 9cbe067..4f47b70 100644 --- a/resources/qml/Governikus/RemoteServiceView/KnownDevicesListDelegate.qml +++ b/resources/qml/Governikus/RemoteServiceView/KnownDevicesListDelegate.qml @@ -1,7 +1,8 @@ -import QtQuick 2.7 +import QtQuick 2.10 import QtQuick.Layouts 1.1 import Governikus.Global 1.0 +import Governikus.Type.RemoteServiceModel 1.0 Item { id: root @@ -16,7 +17,7 @@ Item { Text { id: nameText color: Constants.secondary_text - font.pixelSize: Utils.sp(16) + font.pixelSize: Utils.dp(16) opacity: 0.87 text: { settingsModel.translationTrigger @@ -36,7 +37,7 @@ Item { color: Constants.secondary_text anchors.top: nameText.bottom anchors.topMargin: Utils.dp(2) - font.pixelSize: Utils.sp(14) + font.pixelSize: Utils.dp(14) opacity: 0.38 text: qsTr("Last connection:") + " " + lastConnected + settingsModel.translationTrigger } @@ -59,7 +60,7 @@ Item { } onClicked: { - remoteServiceModel.forgetDevice(deviceId) + RemoteServiceModel.forgetDevice(deviceId) } } diff --git a/resources/qml/Governikus/RemoteServiceView/RemoteServiceController.qml b/resources/qml/Governikus/RemoteServiceView/RemoteServiceController.qml index f406611..52ac682 100644 --- a/resources/qml/Governikus/RemoteServiceView/RemoteServiceController.qml +++ b/resources/qml/Governikus/RemoteServiceView/RemoteServiceController.qml @@ -1,40 +1,55 @@ -import QtQuick 2.5 +import QtQuick 2.10 + +import Governikus.Type.RemoteServiceModel 1.0 +import Governikus.EnterPinView 1.0 +import Governikus.TitleBar 1.0 +import Governikus.Type.ReaderPlugIn 1.0 +import Governikus.Type.NumberModel 1.0 Item { id: controller Connections { - target: remoteServiceModel + target: RemoteServiceModel onFireCurrentStateChanged: { - switch (remoteServiceModel.currentState) { + switch (RemoteServiceModel.currentState) { case "": break case "StateStartRemoteService": navBar.lockedAndHidden = true if (Qt.platform.os === "android") { - remoteServiceModel.readerPlugInType = "NFC"; + RemoteServiceModel.readerPlugInType = ReaderPlugIn.NFC; } else { - remoteServiceModel.readerPlugInType = "PCSC"; + RemoteServiceModel.readerPlugInType = ReaderPlugIn.PCSC; } setWorkflowStateAndContinue("startRemoteService") + break case "StateProcessRemoteMessages": firePopAll() - numberModel.continueWorkflow() + RemoteServiceModel.continueWorkflow() break - case "StateEstablishPaceChannel": - enterPinView.state = "INITIAL" - setWorkflowStateAndRequestInput("establishPaceChannel", remoteServiceModel.getPacePasswordId()) + case "StateEnterPacePasswordRemote": + if (RemoteServiceModel.isBasicReader) { + enterPinView.state = "INITIAL" + setWorkflowStateAndRequestInput("establishPaceChannelRemote", RemoteServiceModel.getPacePasswordId()) + } else { + RemoteServiceModel.continueWorkflow() + } break - case "StateChangePinRemote": - enterPinView.state = "INITIAL" - setWorkflowStateAndRequestInput("changePinRemote", "PIN_NEW") + case "StateEnterNewPacePinRemote": + if (RemoteServiceModel.isBasicReader) { + enterPinView.state = "INITIAL" + setWorkflowStateAndRequestInput("changePinRemote", "PIN_NEW") + } else { + RemoteServiceModel.continueWorkflow() + } break case "FinalState": - numberModel.continueWorkflow() + RemoteServiceModel.continueWorkflow() navBar.lockedAndHidden = false break default: - numberModel.continueWorkflow() + RemoteServiceModel.continueWorkflow() } } } @@ -45,13 +60,35 @@ Item { function setWorkflowStateAndContinue(pState) { setWorkflowState(pState) - numberModel.continueWorkflow() + RemoteServiceModel.continueWorkflow() } function setWorkflowStateAndRequestInput(pState, pEnterPinState) { setWorkflowState(pState) - if (remoteServiceModel.pinPadModeOn()) { - firePush(enterPinView, {state: pEnterPinState}) + if (RemoteServiceModel.pinPadModeOn()) { + enterPinView.state = pEnterPinState + firePush(enterPinView) } } + + EnterPinView { + id: enterPinView + visible: false + enableTransportPinLink: RemoteServiceModel.isSaCPinChangeWorkflow + + leftTitleBarAction: TitleBarAction { + state: "cancel" + onClicked: { + firePop() + RemoteServiceModel.cancelPasswordRequest() + } + } + + onPinEntered: { + firePop() + RemoteServiceModel.continueWorkflow() + } + + onChangePinLength: NumberModel.requestTransportPin = !NumberModel.requestTransportPin + } } diff --git a/resources/qml/Governikus/RemoteServiceView/RemoteServicePairingPopup.qml b/resources/qml/Governikus/RemoteServiceView/RemoteServicePairingPopup.qml index 7084ed8..2183be0 100644 --- a/resources/qml/Governikus/RemoteServiceView/RemoteServicePairingPopup.qml +++ b/resources/qml/Governikus/RemoteServiceView/RemoteServicePairingPopup.qml @@ -1,8 +1,9 @@ -import QtQuick 2.7 -import QtQuick.Controls 2.2 +import QtQuick 2.10 +import QtQuick.Controls 2.3 import QtQuick.Layouts 1.3 import Governikus.Global 1.0 +import Governikus.Type.RemoteServiceModel 1.0 Popup { property bool requestInput: false @@ -18,7 +19,7 @@ Popup { padding: Constants.pane_padding Connections { - target: remoteServiceModel + target: RemoteServiceModel onFireEnvironmentChanged: close() } @@ -53,13 +54,13 @@ Popup { width: parent.width horizontalAlignment: Text.AlignHCenter font.letterSpacing: Utils.dp(5) - font.pixelSize: Utils.sp(50) + font.pixelSize: Utils.dp(50) font.bold: true readOnly: !requestInput inputMethodHints: Qt.ImhDigitsOnly validator: RegExpValidator { regExp: /\d\d\d\d/ } onAccepted: { - remoteServiceModel.connectToServer(deviceId, name.getText(0,4)) + RemoteServiceModel.connectToServer(deviceId, name.getText(0,4)) close() } } @@ -70,7 +71,7 @@ Popup { visible: requestInput onClicked: { - remoteServiceModel.connectToServer(deviceId, name.getText(0,4)) + RemoteServiceModel.connectToServer(deviceId, name.getText(0,4)) close() } } diff --git a/resources/qml/Governikus/RemoteServiceView/RemoteServiceSettings.qml b/resources/qml/Governikus/RemoteServiceView/RemoteServiceSettings.qml index 2cf574f..062000e 100644 --- a/resources/qml/Governikus/RemoteServiceView/RemoteServiceSettings.qml +++ b/resources/qml/Governikus/RemoteServiceView/RemoteServiceSettings.qml @@ -1,233 +1,19 @@ -import QtQuick 2.7 -import QtQuick.Controls 2.2 +import QtQuick 2.10 +import QtQuick.Controls 2.3 import QtQuick.Layouts 1.1 -import Governikus.EnterPinView 1.0 import Governikus.Global 1.0 import Governikus.TitleBar 1.0 +import Governikus.View 1.0 SectionPage { id: rootPage leftTitleBarAction: TitleBarAction { state: "back"; onClicked: firePop() } headerTitleBarAction: TitleBarAction { text: qsTr("Configure remote service") + settingsModel.translationTrigger; font.bold: true } - Connections { - target: remoteServiceModel - onFirePairingFailed: qmlExtension.showFeedback(qsTr("Pairing failed. Please try again to activate pairing on your other device and enter the shown pairing code.")) - } - - content: Column { - id: mainColumn + content: RemoteServiceViewRemote { width: rootPage.width - padding: Constants.pane_padding - spacing: Constants.component_spacing - - readonly property int usableWidth: width - 2 * padding - - Text { - id: errorMsg - width: parent.usableWidth - text: "" - color: Constants.red - font.pixelSize: Constants.normal_font_size - wrapMode: Text.WordWrap - verticalAlignment: Text.AlignVCenter - visible: text !== "" - } - - Column { - id: nameContainer - width: parent.usableWidth - - Text { - text: qsTr("Device name") + settingsModel.translationTrigger - font.pixelSize: Constants.normal_font_size - font.bold: true - color: Constants.blue - } - - Text { - color: Constants.secondary_text - text: qsTr("Set device name:") + settingsModel.translationTrigger - width: parent.width - font.pixelSize: Constants.normal_font_size - wrapMode: Text.WordWrap - } - - Item { - id: spacing - height: Utils.dp(5) - width: height - } - - GTextField { - id: serverName123 - width: parent.width - onAccepted: { - settingsModel.serverName = text - text = settingsModel.serverName - } - onVisibleChanged: if (visible) text = settingsModel.serverName - } - } - - Item { - id: pinPadModeContainer - visible: Qt.platform.os !== "ios" - width: parent.usableWidth - height: Math.max(pinPadModeText.height, pinPadModeSwitch.height) - - Item { - id: pinPadModeText - height: nameText.height + dateText.height - anchors.left: pinPadModeContainer.left - anchors.right: pinPadModeSwitch.left - anchors.rightMargin: Constants.component_spacing - anchors.verticalCenter: pinPadModeContainer.verticalCenter - Text { - id: nameText - anchors.bottomMargin: Utils.dp(2) - font.pixelSize: Constants.normal_font_size - color: Constants.blue - font.bold: true - text: qsTr("PIN pad mode") + settingsModel.translationTrigger - } - - Text { - id: dateText - width: parent.width - anchors.top: nameText.bottom - font.pixelSize: Constants.normal_font_size - color: Constants.secondary_text - text: qsTr("Enter PIN on this device") + settingsModel.translationTrigger - wrapMode: Text.WordWrap - } - } - - GSwitch { - id: pinPadModeSwitch - anchors.right: pinPadModeContainer.right - anchors.verticalCenter: pinPadModeContainer.verticalCenter - onIsOnChanged: settingsModel.pinPadMode = pinPadModeSwitch.isOn - onVisibleChanged: if (visible) isOn = settingsModel.pinPadMode - } - } - - Column { - width: parent.usableWidth - - Text { - text: qsTr("Paired devices") + settingsModel.translationTrigger - font.pixelSize: Constants.normal_font_size - font.bold: true - color: Constants.blue - } - - Text { - color: Constants.secondary_text - text: qsTr("No device is paired.") + settingsModel.translationTrigger - width: parent.width - visible: !knownDeviceList.visible - font.pixelSize: Constants.normal_font_size - wrapMode: Text.WordWrap - } - - ListView { - id: knownDeviceList - width: parent.width - height: childrenRect.height - model: remoteServiceModel.knownDevices - delegate: KnownDevicesListDelegate { - width: knownDeviceList.width - } - visible: count > 0 - interactive: false - } - } - - Column { - width: parent.usableWidth - - Text { - text: qsTr("Available devices") + settingsModel.translationTrigger - font.pixelSize: Constants.normal_font_size - font.bold: true - color: Constants.blue - } - - Text { - color: Constants.secondary_text - text: qsTr("No new remote reader was found on your network. Make sure that the remote reader functionality in AusweisApp2 on your other device is activated and that your devices are connected to the same network.") + settingsModel.translationTrigger - width: parent.width - visible: !searchDeviceList.visible - font.pixelSize: Constants.normal_font_size - wrapMode: Text.WordWrap - } - - ListView { - id: searchDeviceList - width: parent.width - height: childrenRect.height - model: remoteServiceModel.availableRemoteDevices - delegate: AvailableDevicesListDelegate { - width: searchDeviceList.width - onRequestPairing: { - enterPinView.remoteDeviceId = pDeviceId - informationPairingPopup.open() - } - } - visible: count > 0 - interactive: false - } - } - } - - Popup { - id: informationPairingPopup - x: (rootPage.width - width) / 2 - y: (rootPage.height - height) / 2 - width: Utils.dp(250) - height: contentColumn.height + 2 * Constants.pane_padding - modal: true - focus: true - padding: Constants.pane_padding - closePolicy: Popup.CloseOnPressOutside | Popup.CloseOnEscape - - Column { - id: contentColumn - width: parent.width - spacing: Constants.pane_spacing - - Text { - id: info - color: Constants.secondary_text - width: parent.width - wrapMode: Text.WordWrap - font.pixelSize: Constants.normal_font_size - text: qsTr("Please start pairing mode first.") + settingsModel.translationTrigger - } - - GButton { - text: qsTr("OK") + settingsModel.translationTrigger - width: parent.width - - onClicked: { - informationPairingPopup.close() - firePush(enterPinView, {}) - } - } - } - } - - EnterPinView { - id: enterPinView - state: "REMOTE_PIN" - leftTitleBarAction: TitleBarAction { state: "cancel"; onClicked: firePop() } - headerTitleBarAction: TitleBarAction { text: qsTr("Pairing code") + settingsModel.translationTrigger } - visible: false - - onPinEntered: { - firePop() - } + visible: true + parentSectionPage: rootPage } } diff --git a/resources/qml/Governikus/RemoteServiceView/RemoteServiceView.qml b/resources/qml/Governikus/RemoteServiceView/RemoteServiceView.qml index 1ec628f..089da46 100644 --- a/resources/qml/Governikus/RemoteServiceView/RemoteServiceView.qml +++ b/resources/qml/Governikus/RemoteServiceView/RemoteServiceView.qml @@ -1,255 +1,82 @@ -import QtQuick 2.7 -import QtQuick.Controls 1.4 +import QtQuick 2.10 -import Governikus.EnterPinView 1.0 import Governikus.Global 1.0 -import Governikus.Style 1.0 import Governikus.TitleBar 1.0 -import Governikus.TechnologyInfo 1.0 +import Governikus.View 1.0 +import Governikus.Type.RemoteServiceModel 1.0 SectionPage { - leftTitleBarAction: TitleBarAction { - state: remoteServiceModel.running ? "cancel" : "" - onClicked: startButton.clicked() + id: baseItem + leftTitleBarAction: Constants.is_layout_ios ? iosTitleBarAction : androidTitleBarAction + headerTitleBarAction: TitleBarAction { + text: sectionSwitch.selectedSection === "LOCAL" ? qsTr("Configure local settings") + settingsModel.translationTrigger + : sectionSwitch.selectedSection === "REMOTE" ? qsTr("Pair remote devices")+ settingsModel.translationTrigger + : qsTr("Remote service") + settingsModel.translationTrigger + font.bold: true } + + property var iosTitleBarAction: TitleBarAction { state: "back"; onClicked: firePop() } + property var androidTitleBarAction: TitleBarAction { + state: RemoteServiceModel.running ? "cancel" : "" + onClicked: RemoteServiceModel.running = !RemoteServiceModel.running } - headerTitleBarAction: TitleBarAction { - text: qsTr("Smartphone as card reader") + settingsModel.translationTrigger; - font.bold: true + Connections { + target: RemoteServiceModel + onFirePairingFailed: qmlExtension.showFeedback(qsTr("Pairing failed. Please try again to activate pairing on your other device and enter the shown pairing code.")) } onVisibleChanged: { - if (visible) { - remoteServiceModel.detectRemoteDevices = false + if (visible && sectionSwitch.selectedSection === "REMOTE" && !RemoteServiceModel.detectRemoteDevices) { + RemoteServiceModel.detectRemoteDevices = true + } + if (!visible && RemoteServiceModel.detectRemoteDevices && !remoteSettingsView.pinEntryInProgress) { + RemoteServiceModel.detectRemoteDevices = false } } - readonly property int maxWidth: Math.min(width - 2 * Constants.component_spacing, Utils.dp(500)) - id: root + content: Column { + width: baseItem.width - RemoteServiceSettings { - id: remoteSettings - visible: false + RemoteServiceViewLocal + { + width: parent.width + visible: sectionSwitch.selectedSection === "LOCAL" + } + + RemoteServiceViewRemote + { + id: remoteSettingsView + width: parent.width + visible: sectionSwitch.selectedSection === "REMOTE" + parentSectionPage: baseItem + } + + RemoteServiceViewStartStop + { + width: parent.width + height: baseItem.height + visible: Constants.is_layout_android && sectionSwitch.selectedSection === "STARTSTOP" + } + } + + SectionSwitch { + id: sectionSwitch + visible: !RemoteServiceModel.running + showStartStopButton: Constants.is_layout_android + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + + onSelectedSectionChanged: { + if (selectedSection === "REMOTE" && !RemoteServiceModel.detectRemoteDevices){ + RemoteServiceModel.detectRemoteDevices = true + } else { + RemoteServiceModel.detectRemoteDevices = false + } + } } RemoteServiceController { - id: controller - } - - RemoteServicePairingPopup { - id: popup - x: (root.width - width) / 2 - y: (root.height - height) / 2 - - pin: remoteServiceModel.psk.toString() - onPinChanged: { - if (pin === "") { - close() - } - } - onVisibleChanged: remoteServiceModel.setPairing(visible) + id: controller } - - Image { - id: image - source: "qrc:///images/phone_to_pc.svg" - anchors.top: parent.top - anchors.margins: Constants.component_spacing - anchors.horizontalCenter: parent.horizontalCenter - height: parent.height * 0.2 - width: parent.maxWidth - fillMode: Image.PreserveAspectFit - } - - Text { - id: text - color: Constants.secondary_text - - width: parent.maxWidth - anchors.top: image.bottom - anchors.margins: Constants.component_spacing - anchors.horizontalCenter: parent.horizontalCenter - - text: qsTr("Please start the remote service in order to use your smartphone as a card reader with AusweisApp2." - + " Please note: Both your devices have to be connected to the same WiFi.") - + settingsModel.translationTrigger - font.pixelSize: Constants.normal_font_size - wrapMode: Text.WordWrap - } - - - GButton { - readonly property bool running: remoteServiceModel.running - readonly property bool canEnableNfc: remoteServiceModel.canEnableNfc - id: startButton - buttonColor: running ? "red" : "green" - anchors.top: text.bottom - anchors.horizontalCenter: parent.horizontalCenter - anchors.margins: Constants.component_spacing - enabled: canEnableNfc || remoteServiceModel.runnable || running - onClicked: { - if (canEnableNfc) { - qmlExtension.showSettings("android.settings.NFC_SETTINGS") - } else { - var newRunning = !running; - remoteServiceModel.running = newRunning - } - } - text: { - settingsModel.translationTrigger; // Bind this evaluation to the trigger. - - if (canEnableNfc) { - return qsTr("Enable NFC"); - } else if (running) { - return qsTr("Stop remote service"); - } else { - return qsTr("Start remote service"); - } - } - onRunningChanged: { - navBar.lockedAndHidden = running - } - } - - GButton { - id: pairingButton - anchors.top: startButton.bottom - anchors.horizontalCenter: parent.horizontalCenter - anchors.margins: Constants.component_spacing - text: qsTr("Start pairing") + settingsModel.translationTrigger - opacity: 0 - enabled: opacity === 1 - onClicked: popup.open() - } - - Item { - id: statusText - - anchors.top: startButton.bottom - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.right: parent.right - anchors.margins: Constants.component_spacing - anchors.topMargin: Constants.component_spacing * 2 - - Text { - id: error - width: text.width - - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.horizontalCenter: parent.horizontalCenter - horizontalAlignment: Text.AlignHCenter - font.pixelSize: Utils.dp(16) - font.bold: true - color: "red" - wrapMode: Text.WordWrap - visible: !remoteServiceModel.runnable - text: remoteServiceModel.errorMessage; - } - - Item { - id: connectedText - anchors.fill: parent - opacity: 0 - - Text { - id: headText - anchors.top: connectedText.top - anchors.horizontalCenter: parent.horizontalCenter - font.pixelSize: Constants.header_font_size - font.weight: Font.Bold - color: Constants.blue - - text: qsTr("Card access in progress") + settingsModel.translationTrigger; - } - Text { - id: subText - color: Constants.secondary_text - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - font.pixelSize: Constants.normal_font_size - anchors.top: headText.bottom - anchors.topMargin: Utils.dp(10) - anchors.horizontalCenter: parent.horizontalCenter - width: connectedText.width * 0.8 - wrapMode: Text.WordWrap - - text: qsTr("Please pay attention to the display on your other device %1.").arg("\"" + remoteServiceModel.connectedClientDeviceName + "\"") + settingsModel.translationTrigger; - } - - states: [ - State { name: "UNCONNECTED"; when: remoteServiceModel.running && !remoteServiceModel.connected - PropertyChanges { target: connectedText; opacity: 0 } - PropertyChanges { target: pairingButton; opacity: 1 } - }, - State { name: "CONNECTED"; when: remoteServiceModel.running && remoteServiceModel.connected - PropertyChanges { target: connectedText; opacity: 1 } - PropertyChanges { target: pairingButton; opacity: 0 } - } - ] - transitions: [ - Transition { - NumberAnimation { - property: "opacity" - duration: 500 - easing.type: Easing.InOutQuad - } - } - ] - } - } - - EnterPinView { - id: enterPinView - visible: false - - leftTitleBarAction: TitleBarAction { - state: "cancel" - onClicked: remoteServiceModel.cancelPasswordRequest() - } - - onPinEntered: { - numberModel.continueWorkflow() - } - } - - TechnologySwitchButton { - id: switchTo - anchors.horizontalCenter: parent.horizontalCenter - anchors.bottom: parent.bottom - onClicked: { - remoteServiceModel.detectRemoteDevices = true - firePush(remoteSettings, {}) - } - imageSource: "qrc:///images/android/navigation/remotesettings.svg" - text: qsTr("Settings") + settingsModel.translationTrigger - opacity: 1 - enabled: opacity === 1 - } - - states: [ - State { name: "OFF"; when: !remoteServiceModel.running - PropertyChanges { target: pairingButton; opacity: 0 } - PropertyChanges { target: switchTo; opacity: 1 } - AnchorChanges { target: statusText; anchors.top: startButton.bottom } - }, - State { name: "ON"; when: remoteServiceModel.running - PropertyChanges { target: pairingButton; opacity: 1 } - PropertyChanges { target: switchTo; opacity: 0 } - AnchorChanges { target: statusText; anchors.top: pairingButton.bottom } - } - ] - transitions: [ - Transition { - NumberAnimation { - property: "opacity" - duration: 500 - easing.type: Easing.InOutQuad - } - AnchorAnimation { - duration: 500 - } - } - ] } diff --git a/resources/qml/Governikus/RemoteServiceView/RemoteServiceViewLocal.qml b/resources/qml/Governikus/RemoteServiceView/RemoteServiceViewLocal.qml new file mode 100644 index 0000000..0ea5962 --- /dev/null +++ b/resources/qml/Governikus/RemoteServiceView/RemoteServiceViewLocal.qml @@ -0,0 +1,111 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.Type.RemoteServiceModel 1.0 + +Item { + id: baseItem + height: mainColumn.height + + Column { + id: mainColumn + width: baseItem.width + padding: Constants.pane_padding + spacing: Constants.component_spacing + + readonly property int usableWidth: width - 2 * padding + + Text { + id: errorMsg + width: parent.usableWidth + text: "" + color: Constants.red + font.pixelSize: Constants.normal_font_size + wrapMode: Text.WordWrap + verticalAlignment: Text.AlignVCenter + visible: text !== "" + } + + Column { + id: nameContainer + width: parent.usableWidth + + Text { + text: qsTr("Device name") + settingsModel.translationTrigger + font.pixelSize: Constants.normal_font_size + font.bold: true + color: Constants.blue + } + + Text { + color: Constants.secondary_text + text: qsTr("Set device name:") + settingsModel.translationTrigger + width: parent.width + font.pixelSize: Constants.normal_font_size + wrapMode: Text.WordWrap + } + + Item { + id: spacing + height: Utils.dp(5) + width: height + } + + GTextField { + id: serverName + width: parent.width + onAccepted: { + settingsModel.serverName = text + text = settingsModel.serverName + nameContainer.forceActiveFocus(Qt.MouseFocusReason) + } + onVisibleChanged: { + nameContainer.forceActiveFocus(Qt.MouseFocusReason) + if (visible) text = settingsModel.serverName + } + } + } + + Item { + id: pinPadModeContainer + visible: Qt.platform.os !== "ios" + width: parent.usableWidth + height: Math.max(pinPadModeText.height, pinPadModeSwitch.height) + + Item { + id: pinPadModeText + height: nameText.height + dateText.height + anchors.left: pinPadModeContainer.left + anchors.right: pinPadModeSwitch.left + anchors.rightMargin: Constants.component_spacing + anchors.verticalCenter: pinPadModeContainer.verticalCenter + Text { + id: nameText + anchors.bottomMargin: Utils.dp(2) + font.pixelSize: Constants.normal_font_size + color: Constants.blue + font.bold: true + text: qsTr("PIN pad mode") + settingsModel.translationTrigger + } + + Text { + id: dateText + width: parent.width + anchors.top: nameText.bottom + font.pixelSize: Constants.normal_font_size + color: Constants.secondary_text + text: qsTr("Enter PIN on this device") + settingsModel.translationTrigger + wrapMode: Text.WordWrap + } + } + + GSwitch { + id: pinPadModeSwitch + anchors.right: pinPadModeContainer.right + anchors.verticalCenter: pinPadModeContainer.verticalCenter + initialState: settingsModel.pinPadMode + onSwitched: settingsModel.pinPadMode = pinPadModeSwitch.isOn + } + } + } +} diff --git a/resources/qml/Governikus/RemoteServiceView/RemoteServiceViewRemote.qml b/resources/qml/Governikus/RemoteServiceView/RemoteServiceViewRemote.qml new file mode 100644 index 0000000..d4cea51 --- /dev/null +++ b/resources/qml/Governikus/RemoteServiceView/RemoteServiceViewRemote.qml @@ -0,0 +1,143 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.EnterPinView 1.0 +import Governikus.Global 1.0 +import Governikus.TitleBar 1.0 +import Governikus.Type.RemoteServiceModel 1.0 + +Item { + id: baseItem + height: mainColumn.height + + property var parentSectionPage: undefined + + property bool pinEntryInProgress: false + + Column { + id: mainColumn + width: baseItem.width + padding: Constants.pane_padding + spacing: Constants.component_spacing + + readonly property int usableWidth: width - 2 * padding + Column { + width: parent.usableWidth + + Text { + text: qsTr("Paired devices") + settingsModel.translationTrigger + font.pixelSize: Constants.normal_font_size + font.bold: true + color: Constants.blue + } + + Text { + color: Constants.secondary_text + text: qsTr("No device is paired.") + settingsModel.translationTrigger + width: parent.width + visible: !knownDeviceList.visible + font.pixelSize: Constants.normal_font_size + wrapMode: Text.WordWrap + } + + ListView { + id: knownDeviceList + width: parent.width + height: childrenRect.height + model: RemoteServiceModel.knownDevices + delegate: KnownDevicesListDelegate { + width: knownDeviceList.width + } + visible: count > 0 + interactive: false + } + } + + Column { + width: parent.usableWidth + + Text { + text: qsTr("Available devices") + settingsModel.translationTrigger + font.pixelSize: Constants.normal_font_size + font.bold: true + color: Constants.blue + } + + Text { + color: Constants.secondary_text + text: qsTr("No new remote reader was found on your network. Make sure that the remote reader functionality in AusweisApp2 on your other device is activated and that your devices are connected to the same network.") + settingsModel.translationTrigger + width: parent.width + visible: !searchDeviceList.visible + font.pixelSize: Constants.normal_font_size + wrapMode: Text.WordWrap + } + + ListView { + id: searchDeviceList + width: parent.width + height: childrenRect.height + model: RemoteServiceModel.availableRemoteDevices + delegate: AvailableDevicesListDelegate { + width: searchDeviceList.width + onRequestPairing: { + enterPinView.remoteDeviceId = pDeviceId + informationPairingPopup.open() + } + } + visible: count > 0 + interactive: false + } + } + } + + Popup { + id: informationPairingPopup + x: (parentSectionPage.width - width) / 2 + y: (parentSectionPage.height - height) / 2 + width: Utils.dp(250) + height: contentColumn.height + 2 * Constants.pane_padding + modal: true + focus: true + padding: Constants.pane_padding + closePolicy: Popup.CloseOnPressOutside | Popup.CloseOnEscape + + Column { + id: contentColumn + width: parent.width + spacing: Constants.pane_spacing + + Text { + id: info + color: Constants.secondary_text + width: parent.width + wrapMode: Text.WordWrap + font.pixelSize: Constants.normal_font_size + text: qsTr("Please start pairing mode first.") + settingsModel.translationTrigger + } + + GButton { + text: qsTr("OK") + settingsModel.translationTrigger + width: parent.width + + onClicked: { + informationPairingPopup.close() + pinEntryInProgress = true + firePush(enterPinView) + } + } + } + } + + EnterPinView { + id: enterPinView + state: "REMOTE_PIN" + leftTitleBarAction: TitleBarAction { state: "cancel"; onClicked: { firePop(); pinEntryInProgress = false } } + headerTitleBarAction: TitleBarAction { text: qsTr("Pairing code") + settingsModel.translationTrigger } + visible: false + + onPinEntered: { + firePop() + pinEntryInProgress = false + } + } +} diff --git a/resources/qml/Governikus/RemoteServiceView/RemoteServiceViewStartStop.qml b/resources/qml/Governikus/RemoteServiceView/RemoteServiceViewStartStop.qml new file mode 100644 index 0000000..2bf8de6 --- /dev/null +++ b/resources/qml/Governikus/RemoteServiceView/RemoteServiceViewStartStop.qml @@ -0,0 +1,199 @@ +import QtQuick 2.10 + +import Governikus.EnterPinView 1.0 +import Governikus.Global 1.0 +import Governikus.Type.RemoteServiceModel 1.0 +import Governikus.TitleBar 1.0 + +Item { + id: baseItem + + readonly property int maxWidth: Math.min(width - 2 * Constants.component_spacing, Utils.dp(500)) + + RemoteServicePairingPopup { + id: popup + x: (baseItem.width - width) / 2 + y: (baseItem.height - height) / 2 + + pin: RemoteServiceModel.psk.toString() + onPinChanged: { + if (pin === "") { + close() + } + } + onVisibleChanged: RemoteServiceModel.setPairing(visible) + } + + Image { + id: image + source: "qrc:///images/phone_to_pc.svg" + anchors.top: parent.top + anchors.margins: Constants.component_spacing + anchors.horizontalCenter: parent.horizontalCenter + height: parent.height * 0.2 + width: parent.maxWidth + fillMode: Image.PreserveAspectFit + } + + Text { + id: text + color: Constants.secondary_text + + width: parent.maxWidth + anchors.top: image.bottom + anchors.margins: Constants.component_spacing + anchors.horizontalCenter: parent.horizontalCenter + + text: qsTr("Please start the remote service in order to use your smartphone as a card reader with AusweisApp2." + + " Please note: Both your devices have to be connected to the same WiFi.") + + settingsModel.translationTrigger + font.pixelSize: Constants.normal_font_size + wrapMode: Text.WordWrap + } + + + GButton { + readonly property bool running: RemoteServiceModel.running + readonly property bool canEnableNfc: RemoteServiceModel.canEnableNfc + id: startButton + buttonColor: running ? "red" : "green" + anchors.top: text.bottom + anchors.horizontalCenter: parent.horizontalCenter + anchors.margins: Constants.component_spacing + enabled: canEnableNfc || RemoteServiceModel.runnable || running + onClicked: { + if (canEnableNfc) { + qmlExtension.showSettings("android.settings.NFC_SETTINGS") + } else { + var newRunning = !running; + RemoteServiceModel.running = newRunning + } + } + text: { + settingsModel.translationTrigger; // Bind this evaluation to the trigger. + + if (canEnableNfc) { + return qsTr("Enable NFC"); + } else if (running) { + return qsTr("Stop remote service"); + } else { + return qsTr("Start remote service"); + } + } + onRunningChanged: { + navBar.lockedAndHidden = running + } + } + + GButton { + id: pairingButton + anchors.top: startButton.bottom + anchors.horizontalCenter: parent.horizontalCenter + anchors.margins: Constants.component_spacing + text: qsTr("Start pairing") + settingsModel.translationTrigger + opacity: 0 + enabled: opacity === 1 + onClicked: popup.open() + } + + Item { + id: statusText + + anchors.top: startButton.bottom + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: Constants.component_spacing + anchors.topMargin: Constants.component_spacing * 2 + + Text { + id: error + width: text.width + + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Utils.dp(16) + font.bold: true + color: "red" + wrapMode: Text.WordWrap + visible: !RemoteServiceModel.runnable + text: RemoteServiceModel.errorMessage; + } + + Item { + id: connectedText + anchors.fill: parent + opacity: 0 + + Text { + id: headText + anchors.top: connectedText.top + anchors.horizontalCenter: parent.horizontalCenter + font.pixelSize: Constants.header_font_size + font.weight: Font.Bold + color: Constants.blue + + text: qsTr("Card access in progress") + settingsModel.translationTrigger; + } + Text { + id: subText + color: Constants.secondary_text + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Constants.normal_font_size + anchors.top: headText.bottom + anchors.topMargin: Utils.dp(10) + anchors.horizontalCenter: parent.horizontalCenter + width: connectedText.width * 0.8 + wrapMode: Text.WordWrap + + text: qsTr("Please pay attention to the display on your other device %1.").arg("\"" + RemoteServiceModel.connectedClientDeviceName + "\"") + settingsModel.translationTrigger; + } + + states: [ + State { name: "UNCONNECTED"; when: RemoteServiceModel.running && !RemoteServiceModel.connected + PropertyChanges { target: connectedText; opacity: 0 } + PropertyChanges { target: pairingButton; opacity: 1 } + }, + State { name: "CONNECTED"; when: RemoteServiceModel.running && RemoteServiceModel.connected + PropertyChanges { target: connectedText; opacity: 1 } + PropertyChanges { target: pairingButton; opacity: 0 } + } + ] + transitions: [ + Transition { + NumberAnimation { + property: "opacity" + duration: 500 + easing.type: Easing.InOutQuad + } + } + ] + } + } + + states: [ + State { name: "OFF"; when: !RemoteServiceModel.running + PropertyChanges { target: pairingButton; opacity: 0 } + AnchorChanges { target: statusText; anchors.top: startButton.bottom } + }, + State { name: "ON"; when: RemoteServiceModel.running + PropertyChanges { target: pairingButton; opacity: 1 } + AnchorChanges { target: statusText; anchors.top: pairingButton.bottom } + } + ] + transitions: [ + Transition { + NumberAnimation { + property: "opacity" + duration: 500 + easing.type: Easing.InOutQuad + } + AnchorAnimation { + duration: 500 + } + } + ] +} diff --git a/resources/qml/Governikus/RemoteServiceView/SectionSwitch.qml b/resources/qml/Governikus/RemoteServiceView/SectionSwitch.qml new file mode 100644 index 0000000..f74babc --- /dev/null +++ b/resources/qml/Governikus/RemoteServiceView/SectionSwitch.qml @@ -0,0 +1,43 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.TechnologyInfo 1.0 +import Governikus.Type.RemoteServiceModel 1.0 + +Rectangle { + id: baseItem + height: sectionRow.height + color: Constants.background_color + + property bool showStartStopButton: true + property string selectedSection: showStartStopButton ? "STARTSTOP" : "REMOTE" + + Row { + id: sectionRow + spacing: Utils.dp(30) + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + + TechnologySwitchButton { + visible: showStartStopButton + buttonActive: selectedSection !== "STARTSTOP" + onClicked: selectedSection = "STARTSTOP" + imageSource: "qrc:///images/icon_remote.svg" + text: qsTr("Service") + settingsModel.translationTrigger + } + + TechnologySwitchButton { + buttonActive: selectedSection !== "REMOTE" + onClicked: selectedSection = "REMOTE" + imageSource: "qrc:///images/icon_pair.svg" + text: qsTr("Pairing") + settingsModel.translationTrigger + } + + TechnologySwitchButton { + buttonActive: selectedSection !== "LOCAL" + onClicked: selectedSection = "LOCAL" + imageSource: "qrc:///images/icon_settings.svg" + text: qsTr("Settings") + settingsModel.translationTrigger + } + } +} diff --git a/resources/qml/Governikus/RemoteServiceView/qmldir b/resources/qml/Governikus/RemoteServiceView/qmldir index f134ced..f54cc0d 100644 --- a/resources/qml/Governikus/RemoteServiceView/qmldir +++ b/resources/qml/Governikus/RemoteServiceView/qmldir @@ -1,4 +1,13 @@ -module RemoteServiceView -RemoteServiceView 1.0 RemoteServiceView.qml +module RemoteServiceView + +internal AvailableDevicesListDelegate AvailableDevicesListDelegate.qml +internal KnownDevicesListDelegate KnownDevicesListDelegate.qml +internal RemoteServiceController RemoteServiceController.qml +internal RemoteServicePairingPopup RemoteServicePairingPopup.qml +internal RemoteServiceViewLocal RemoteServiceViewLocal.qml +internal RemoteServiceViewRemote RemoteServiceViewRemote.qml +internal RemoteServiceViewStartStop RemoteServiceViewStartStop.qml +internal SectionSwitch SectionSwitch.qml + RemoteServiceSettings 1.0 RemoteServiceSettings.qml -RemoteServicePairingView 1.0 RemoteServicePairingView.qml +RemoteServiceView 1.0 RemoteServiceView.qml diff --git a/resources/qml/Governikus/ResultView/+android/ResultView.qml b/resources/qml/Governikus/ResultView/+android/ResultView.qml deleted file mode 100644 index f78cf2e..0000000 --- a/resources/qml/Governikus/ResultView/+android/ResultView.qml +++ /dev/null @@ -1,59 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Window 2.2 - -import Governikus.Global 1.0 -import Governikus.TitleBar 1.0 - -SectionPage { - id: baseItem - leftTitleBarAction: TitleBarAction { state: "cancel"; onClicked: baseItem.clicked() } - - property alias text: resultText.text - property bool isError: false - signal clicked - - Rectangle { - anchors.fill: parent - color: Constants.background_color - } - - // See: http://tagasks.com/in_qtqml_how_to_load_different_images_for_different_device_densities_android - property int ppi: Screen.pixelDensity * 25.4 - - Image { - id: resultImage - source: isError ? "qrc:///images/rotes_X.svg" : "qrc:///images/gruener_Haken.svg" - height: Utils.dp(100) - width: height - anchors.horizontalCenter: parent.horizontalCenter - anchors.top: parent.top - anchors.topMargin: Utils.dp(60) - fillMode: Image.PreserveAspectFit - } - - Text { - id: resultText - width: parent.width * 0.9 - anchors.horizontalCenter: parent.horizontalCenter - anchors.top: resultImage.bottom - anchors.bottom: button.top - - font.pixelSize: Constants.header_font_size - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - wrapMode: Text.WordWrap - color: isError ? Constants.red : Constants.blue - onLinkActivated: Qt.openUrlExternally(link) - } - - GButton { - id: button - width: Utils.dp(90) - anchors.bottom: parent.bottom - anchors.horizontalCenter: parent.horizontalCenter - anchors.bottomMargin: Utils.dp(30) - - text: qsTr("OK") + settingsModel.translationTrigger - onClicked: baseItem.clicked() - } -} diff --git a/resources/qml/Governikus/ResultView/+desktop/ResultView.qml b/resources/qml/Governikus/ResultView/+desktop/ResultView.qml new file mode 100644 index 0000000..2b32f84 --- /dev/null +++ b/resources/qml/Governikus/ResultView/+desktop/ResultView.qml @@ -0,0 +1,52 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 +import Governikus.Type.ApplicationModel 1.0 + +SectionPage { + id: baseItem + + property alias text: resultText.text + property bool isError: false + + KeyNavigation.tab: button + Accessible.role: Accessible.Grouping + Accessible.name: qsTr("Result view") + settingsModel.translationTrigger + Accessible.description: qsTr("This is the result of an authentication.") + settingsModel.translationTrigger + + StatusIcon { + height: ApplicationModel.scaleFactor * 400 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.top + anchors.verticalCenterOffset: baseItem.height / 4 + source: isError ? "qrc:///images/status_error.svg" : "qrc:///images/status_ok.svg" + } + + Text { + id: resultText + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.verticalCenter + width: parent.width - (2 * Constants.pane_padding) + + font.pixelSize: Constants.header_font_size + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + wrapMode: Text.WordWrap + color: Constants.white + onLinkActivated: Qt.openUrlExternally(link) + } + + ContinueButton { + id: button + height: ApplicationModel.scaleFactor * 160 + anchors.verticalCenter: parent.bottom + anchors.verticalCenterOffset: -baseItem.height / 6 + anchors.horizontalCenter: parent.horizontalCenter + onClicked: baseItem.nextView(SectionPage.Views.Main) + + KeyNavigation.tab: baseItem.navSuccessor + } +} diff --git a/resources/qml/Governikus/ResultView/+ios/ResultView.qml b/resources/qml/Governikus/ResultView/+ios/ResultView.qml deleted file mode 100644 index 7e167df..0000000 --- a/resources/qml/Governikus/ResultView/+ios/ResultView.qml +++ /dev/null @@ -1,47 +0,0 @@ -import QtQuick 2.5 - -import Governikus.Global 1.0 -import Governikus.TitleBar 1.0 - -SectionPage { - id: baseItem - leftTitleBarAction: TitleBarAction { state: "hidden" } - - property alias text: resultText.text - property bool isError: false - signal clicked - - Rectangle { - anchors.fill: parent - color: Constants.background_color - } - - - Image { - id: resultImage - anchors.horizontalCenter: parent.horizontalCenter - anchors.bottom: parent.verticalCenter - fillMode: Image.PreserveAspectFit - source: isError ? "qrc:///images/rotes_X.svg" : "qrc:///images/gruener_Haken.svg" - width: Utils.dp(160) - } - Text { - id: resultText - anchors.horizontalCenter: parent.horizontalCenter - anchors.top: resultImage.bottom - width: parent.width * 0.9 - font.pixelSize: Constants.header_font_size - horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap - color: isError ? Constants.red : Constants.blue - onLinkActivated: Qt.openUrlExternally(link) - } - - GButton { - anchors.horizontalCenter: parent.horizontalCenter - anchors.bottom: parent.bottom - anchors.bottomMargin: Utils.dp(30) - text: qsTr("OK") + settingsModel.translationTrigger - onClicked: baseItem.clicked() - } -} diff --git a/resources/qml/Governikus/ResultView/+mobile/ResultView.qml b/resources/qml/Governikus/ResultView/+mobile/ResultView.qml new file mode 100644 index 0000000..bd65f53 --- /dev/null +++ b/resources/qml/Governikus/ResultView/+mobile/ResultView.qml @@ -0,0 +1,93 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 +import Governikus.Type.LogModel 1.0 + +SectionPage { + id: baseItem + leftTitleBarAction: TitleBarAction { state: "cancel"; onClicked: baseItem.clicked() } + + enum Type { + IsSuccess, + IsError, + IsInfo + } + + property alias text: resultText.text + property alias buttonText: buttonLeft.text + property alias showMailButton: buttonRight.visible + property int resultType: Type.IsSuccess + signal clicked + + Rectangle { + anchors.fill: parent + color: Constants.background_color + } + + StatusIcon { + id: resultIcon + height: Utils.dp(100) + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: Utils.dp(60) + source: { + switch (resultType) { + case ResultView.Type.IsSuccess: + return "qrc:///images/status_ok.svg" + case ResultView.Type.IsInfo: + return "qrc:///images/status_info.svg" + case ResultView.Type.IsError: + return "qrc:///images/status_error.svg" + } + } + } + + Text { + id: resultText + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: resultIcon.bottom + anchors.bottom: buttonRow.top + width: parent.width - (2 * Constants.pane_padding) + + font.pixelSize: Constants.header_font_size + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + wrapMode: Text.WordWrap + color: { + switch (resultType) { + case ResultView.Type.IsSuccess: + case ResultView.Type.IsInfo: + return Constants.blue + case ResultView.Type.IsError: + return Constants.red + } + } + onLinkActivated: Qt.openUrlExternally(link) + } + + Row { + id: buttonRow + + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottomMargin: Utils.dp(30) + spacing: Constants.component_spacing + + GButton { + id: buttonLeft + + text: qsTr("OK") + settingsModel.translationTrigger + onClicked: baseItem.clicked() + } + + GButton { + id: buttonRight + visible: false + + text: qsTr("Send log file") + settingsModel.translationTrigger + onClicked: LogModel.mailLog() + } + } +} diff --git a/resources/qml/Governikus/ResultView/ResultView.qml b/resources/qml/Governikus/ResultView/ResultView.qml deleted file mode 100644 index 4b11736..0000000 --- a/resources/qml/Governikus/ResultView/ResultView.qml +++ /dev/null @@ -1,3 +0,0 @@ -Item { - id: dummy -} diff --git a/resources/qml/Governikus/ResultView/qmldir b/resources/qml/Governikus/ResultView/qmldir index b2eec08..2e1266d 100644 --- a/resources/qml/Governikus/ResultView/qmldir +++ b/resources/qml/Governikus/ResultView/qmldir @@ -1,2 +1,3 @@ module ResultView + ResultView 1.0 ResultView.qml diff --git a/resources/qml/Governikus/SplashScreen/SplashScreen.qml b/resources/qml/Governikus/SplashScreen/SplashScreen.qml index af43f10..2772b5b 100644 --- a/resources/qml/Governikus/SplashScreen/SplashScreen.qml +++ b/resources/qml/Governikus/SplashScreen/SplashScreen.qml @@ -1,5 +1,4 @@ -import QtQuick 2.7 -import QtQuick.Controls 1.4 +import QtQuick 2.10 Rectangle { id: splashScreen @@ -26,7 +25,7 @@ Rectangle { var TIMEOUT = 1000; var remaining = startTime + TIMEOUT - new Date().getTime(); - var timer = Qt.createQmlObject("import QtQuick 2.0; Timer {}", splashScreen); + var timer = Qt.createQmlObject("import QtQuick 2.10; Timer {}", splashScreen); timer.interval = remaining > 0 ? remaining : 0; timer.repeat = false; timer.triggered.connect(function(){ diff --git a/resources/qml/Governikus/SplashScreen/qmldir b/resources/qml/Governikus/SplashScreen/qmldir index c62cfae..298e3b8 100644 --- a/resources/qml/Governikus/SplashScreen/qmldir +++ b/resources/qml/Governikus/SplashScreen/qmldir @@ -1,2 +1,3 @@ module SplashScreen + SplashScreen 1.0 SplashScreen.qml diff --git a/resources/qml/Governikus/Style/+android/ProviderStyle.qml b/resources/qml/Governikus/Style/+android/ProviderStyle.qml deleted file mode 100644 index db35540..0000000 --- a/resources/qml/Governikus/Style/+android/ProviderStyle.qml +++ /dev/null @@ -1,33 +0,0 @@ -import QtQuick 2.5 - -import Governikus.Global 1.0 - -Item { - // Provider category list properties - readonly property int categoryFontPixelSize: Constants.titlebar_font_size - readonly property bool categoryFontBold: true - readonly property color categoryColor: Constants.accent_color - readonly property int leftIconMargin: Utils.dp(10) - readonly property int leftProviderListMargin: Utils.dp(20) - readonly property bool showCategoryRightArrow: false - - // Provider list item properties - readonly property int itemLeftMargin: Utils.dp(15) - readonly property color subjectTextColor: Constants.secondary_text - readonly property bool subjectTextFontBold: true - readonly property int addressTextFontSize: Utils.dp(12) - readonly property color addressTextColor: Constants.accent_color - readonly property double infoItemWidthFactor: 4.0 - - readonly property int providerListItemTopMargin: Utils.dp(0) - readonly property int providerListItemRightMargin: Utils.dp(0) - readonly property int providerListItemBottomMargin: Utils.dp(0) - - readonly property bool providerListItemsHaveBorder: true - - readonly property bool providerListDetailsLinkBold: true - readonly property color providerListDetailsLinkBorder: PlatformConstants.grey_light - readonly property color providerListDetailsLinkColor: Constants.primary_text - readonly property color providerListDetailsLinkBackground: PlatformConstants.grey_light - readonly property string providerListDetailsLinkPosition: "top" -} diff --git a/resources/qml/Governikus/Style/+desktop/ProviderStyle.qml b/resources/qml/Governikus/Style/+desktop/ProviderStyle.qml new file mode 100644 index 0000000..fa889c8 --- /dev/null +++ b/resources/qml/Governikus/Style/+desktop/ProviderStyle.qml @@ -0,0 +1,7 @@ +pragma Singleton + +import QtQuick 2.10 + + +Item { +} diff --git a/resources/qml/Governikus/Style/+ios/ProviderStyle.qml b/resources/qml/Governikus/Style/+ios/ProviderStyle.qml deleted file mode 100644 index 93572dc..0000000 --- a/resources/qml/Governikus/Style/+ios/ProviderStyle.qml +++ /dev/null @@ -1,33 +0,0 @@ -import QtQuick 2.5 - -import Governikus.Global 1.0 - -Item { - // Provider category list properties - readonly property int categoryFontPixelSize: Constants.normal_font_size - readonly property bool categoryFontBold: false - readonly property color categoryColor: Constants.accent_color - readonly property int leftIconMargin: Utils.dp(0) - readonly property int leftProviderListMargin: Utils.dp(0) - readonly property bool showCategoryRightArrow: true - - // Provider list item properties - readonly property int itemLeftMargin: Utils.dp(5) - readonly property color subjectTextColor: "#000000" // default color, which is? - readonly property bool subjectTextFontBold: false - readonly property int addressTextFontSize: Utils.dp(11) - readonly property color addressTextColor: PlatformConstants.blue_dark - readonly property double infoItemWidthFactor: 2.0 - - readonly property int providerListItemTopMargin: Utils.dp(2) - readonly property int providerListItemRightMargin: Utils.dp(5) - readonly property int providerListItemBottomMargin: Utils.dp(5) - - readonly property bool providerListItemsHaveBorder: true - - readonly property bool providerListDetailsLinkBold: false - readonly property color providerListDetailsLinkBorder: PlatformConstants.blue_dark - readonly property color providerListDetailsLinkColor: PlatformConstants.blue_dark - readonly property color providerListDetailsLinkBackground: "#ffffff" - readonly property string providerListDetailsLinkPosition: "middle" -} diff --git a/resources/qml/Governikus/Style/+mobile/+android/ProviderStyle.qml b/resources/qml/Governikus/Style/+mobile/+android/ProviderStyle.qml new file mode 100644 index 0000000..5876377 --- /dev/null +++ b/resources/qml/Governikus/Style/+mobile/+android/ProviderStyle.qml @@ -0,0 +1,35 @@ +pragma Singleton + +import QtQuick 2.10 + +import Governikus.Global 1.0 + +Item { + // Provider category list properties + readonly property int categoryFontPixelSize: Constants.titlebar_font_size + readonly property bool categoryFontBold: true + readonly property color categoryColor: Constants.accent_color + readonly property int leftIconMargin: Utils.dp(10) + readonly property int leftProviderListMargin: Utils.dp(20) + readonly property bool showCategoryRightArrow: false + + // Provider list item properties + readonly property int itemLeftMargin: Utils.dp(15) + readonly property color subjectTextColor: Constants.secondary_text + readonly property bool subjectTextFontBold: true + readonly property int addressTextFontSize: Utils.dp(12) + readonly property color addressTextColor: Constants.accent_color + readonly property double infoItemWidthFactor: 4.0 + + readonly property int providerListItemTopMargin: Utils.dp(0) + readonly property int providerListItemRightMargin: Utils.dp(0) + readonly property int providerListItemBottomMargin: Utils.dp(0) + + readonly property bool providerListItemsHaveBorder: true + + readonly property bool providerListDetailsLinkBold: true + readonly property color providerListDetailsLinkBorder: Constants.grey_light + readonly property color providerListDetailsLinkColor: Constants.primary_text + readonly property color providerListDetailsLinkBackground: Constants.grey_light + readonly property string providerListDetailsLinkPosition: "top" +} diff --git a/resources/qml/Governikus/Style/+mobile/+ios/ProviderStyle.qml b/resources/qml/Governikus/Style/+mobile/+ios/ProviderStyle.qml new file mode 100644 index 0000000..1e256ef --- /dev/null +++ b/resources/qml/Governikus/Style/+mobile/+ios/ProviderStyle.qml @@ -0,0 +1,35 @@ +pragma Singleton + +import QtQuick 2.10 + +import Governikus.Global 1.0 + +Item { + // Provider category list properties + readonly property int categoryFontPixelSize: Constants.normal_font_size + readonly property bool categoryFontBold: false + readonly property color categoryColor: Constants.accent_color + readonly property int leftIconMargin: Utils.dp(0) + readonly property int leftProviderListMargin: Utils.dp(0) + readonly property bool showCategoryRightArrow: true + + // Provider list item properties + readonly property int itemLeftMargin: Utils.dp(5) + readonly property color subjectTextColor: "#000000" // default color, which is? + readonly property bool subjectTextFontBold: false + readonly property int addressTextFontSize: Utils.dp(11) + readonly property color addressTextColor: Constants.blue_dark + readonly property double infoItemWidthFactor: 2.0 + + readonly property int providerListItemTopMargin: Utils.dp(2) + readonly property int providerListItemRightMargin: Utils.dp(5) + readonly property int providerListItemBottomMargin: Utils.dp(5) + + readonly property bool providerListItemsHaveBorder: true + + readonly property bool providerListDetailsLinkBold: false + readonly property color providerListDetailsLinkBorder: Constants.blue_dark + readonly property color providerListDetailsLinkColor: Constants.blue_dark + readonly property color providerListDetailsLinkBackground: "#ffffff" + readonly property string providerListDetailsLinkPosition: "middle" +} diff --git a/resources/qml/Governikus/Style/NpaBusyIndicatorStyle.qml b/resources/qml/Governikus/Style/NpaBusyIndicatorStyle.qml index e62b0fc..69a6ded 100644 --- a/resources/qml/Governikus/Style/NpaBusyIndicatorStyle.qml +++ b/resources/qml/Governikus/Style/NpaBusyIndicatorStyle.qml @@ -1,106 +1,101 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 +import QtQuick 2.10 +import QtQml 2.2 import QtGraphicalEffects 1.0 import Governikus.Global 1.0 -BusyIndicatorStyle -{ + Item { property real factor: 1.1 - indicator: Item - { - id: busyIndicator + + id: busyIndicator + anchors.centerIn: parent + + state: parent.running ? "running" : "notrunning" + states: [ + State{ name: "running" }, + State{ name: "notrunning" } + ] + + transitions: [ + Transition { from: "notrunning"; to: "running" + SequentialAnimation { + PropertyAnimation { target: timer; property: "running"; to: true } + PropertyAction { target: busyIndicator; property: "rotation"; value: 0 } + PropertyAction { target: green; property: "rotation"; value: 0 } + PropertyAction { target: blue; property: "rotation"; value: 0 } + PropertyAnimation { target: rect; property: "opacity"; to: 1 } + } + }, + Transition { from: "running"; to: "notrunning" + SequentialAnimation { + PropertyAction { target: timer; property: "running"; value: false } + PropertyAction { target: rect; property: "opacity"; value: 0 } + } + } + ] + + + Behavior on rotation { + NumberAnimation { duration: timer.interval; easing.type: Easing.Linear } + } + Rectangle { + id: rect anchors.centerIn: parent + height: parent.height * factor + width: height + radius: width / 2 + color: Constants.background_color + opacity: 0 - state: control.running ? "running" : "notrunning" - states: [ - State{ name: "running" }, - State{ name: "notrunning" } - ] - - transitions: [ - Transition { from: "notrunning"; to: "running" - SequentialAnimation { - PropertyAnimation { target: timer; property: "running"; to: true } - PropertyAction { target: busyIndicator; property: "rotation"; value: 0 } - PropertyAction { target: green; property: "rotation"; value: 0 } - PropertyAction { target: blue; property: "rotation"; value: 0 } - PropertyAnimation { target: rect; property: "opacity"; to: 1 } - } - }, - Transition { from: "running"; to: "notrunning" - SequentialAnimation { - PropertyAction { target: timer; property: "running"; value: false } - PropertyAction { target: rect; property: "opacity"; value: 0 } - } - } - ] - - - Behavior on rotation { - NumberAnimation { duration: timer.interval; easing.type: Easing.Linear } - } - Rectangle { - id: rect - anchors.centerIn: parent - height: control.height * factor - width: height - radius: width / 2 - color: Constants.background_color - opacity: 0 - - Behavior on opacity { - NumberAnimation { duration: 200 } - } - } - - Timer { - id: timer - interval: 1000; repeat: true - onTriggered: { - green.rotation = green.rotation + Utils.getRandomInt(0, 135) - blue.rotation = blue.rotation + Utils.getRandomInt(0, 195) - busyIndicator.rotation = busyIndicator.rotation + 100 - } - } - - ConicalGradient { - id: green - anchors.fill: rect - source: rect - angle: 0.0 - cached: true - opacity: rect.opacity - gradient: Gradient - { - GradientStop {color: Constants.green; position: 0.0} - GradientStop {color: Constants.green; position: 0.50} - GradientStop {color: "transparent"; position: 0.5000000000000001} - GradientStop {color: "transparent"; position: 1.0} - } - Behavior on rotation { - NumberAnimation { duration: timer.interval; easing.type: Easing.InOutQuad } - } - } - ConicalGradient { - id: blue - anchors.fill: rect - source: rect - angle: 0.0 - cached: true - opacity: rect.opacity - gradient: Gradient - { - GradientStop {color: "transparent"; position: 0.0} - GradientStop {color: "transparent"; position: 0.50} - GradientStop {color: rect.color; position: 0.5000000000000001} - GradientStop {color: Constants.blue; position: 1.0} - } - Behavior on rotation { - NumberAnimation { duration: timer.interval; easing.type: Easing.InOutQuad } - } + Behavior on opacity { + NumberAnimation { duration: 200 } } } + Timer { + id: timer + interval: 1000; repeat: true + onTriggered: { + green.rotation = green.rotation + Utils.getRandomInt(0, 135) + blue.rotation = blue.rotation + Utils.getRandomInt(0, 195) + busyIndicator.rotation = busyIndicator.rotation + 100 + } + } + + ConicalGradient { + id: green + anchors.fill: rect + source: rect + angle: 0.0 + cached: true + opacity: rect.opacity + gradient: Gradient + { + GradientStop {color: Constants.green; position: 0.0} + GradientStop {color: Constants.green; position: 0.50} + GradientStop {color: "transparent"; position: 0.5000000000000001} + GradientStop {color: "transparent"; position: 1.0} + } + Behavior on rotation { + NumberAnimation { duration: timer.interval; easing.type: Easing.InOutQuad } + } + } + ConicalGradient { + id: blue + anchors.fill: rect + source: rect + angle: 0.0 + cached: true + opacity: rect.opacity + gradient: Gradient + { + GradientStop {color: "transparent"; position: 0.0} + GradientStop {color: "transparent"; position: 0.50} + GradientStop {color: rect.color; position: 0.5000000000000001} + GradientStop {color: Constants.blue; position: 1.0} + } + Behavior on rotation { + NumberAnimation { duration: timer.interval; easing.type: Easing.InOutQuad } + } + } } diff --git a/resources/qml/Governikus/Style/ProviderStyle.qml b/resources/qml/Governikus/Style/ProviderStyle.qml deleted file mode 100644 index 4b11736..0000000 --- a/resources/qml/Governikus/Style/ProviderStyle.qml +++ /dev/null @@ -1,3 +0,0 @@ -Item { - id: dummy -} diff --git a/resources/qml/Governikus/Style/qmldir b/resources/qml/Governikus/Style/qmldir index ddcb96e..4deea53 100644 --- a/resources/qml/Governikus/Style/qmldir +++ b/resources/qml/Governikus/Style/qmldir @@ -1,3 +1,5 @@ module Style + +singleton ProviderStyle 1.0 ProviderStyle.qml + NpaBusyIndicatorStyle 1.0 NpaBusyIndicatorStyle.qml -ProviderStyle 1.0 ProviderStyle.qml diff --git a/resources/qml/Governikus/TechnologyInfo/+android/TechnologySwitch.qml b/resources/qml/Governikus/TechnologyInfo/+android/TechnologySwitch.qml index cca07ac..a62f492 100644 --- a/resources/qml/Governikus/TechnologyInfo/+android/TechnologySwitch.qml +++ b/resources/qml/Governikus/TechnologyInfo/+android/TechnologySwitch.qml @@ -1,15 +1,16 @@ -import QtQuick 2.7 +import QtQuick 2.10 import Governikus.Global 1.0 -import "." as Gov +import Governikus.Type.ReaderPlugIn 1.0 -Item { +Rectangle { id: baseItem height: technologyRow.height + color: Constants.background_color - signal requestPluginType(string pReaderPlugInType) + signal requestPluginType(int pReaderPlugInType) - property string selectedTechnology + property int selectedTechnology Row { id: technologyRow @@ -17,23 +18,23 @@ Item { anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter - Gov.TechnologySwitchButton { - visible: selectedTechnology !== "NFC" - onClicked: baseItem.requestPluginType("NFC") + TechnologySwitchButton { + buttonActive: selectedTechnology !== ReaderPlugIn.NFC + onClicked: baseItem.requestPluginType(ReaderPlugIn.NFC) imageSource: "qrc:///images/icon_nfc.svg" text: qsTr("NFC") + settingsModel.translationTrigger } - Gov.TechnologySwitchButton { - visible: selectedTechnology !== "REMOTE" - onClicked: baseItem.requestPluginType("REMOTE") + TechnologySwitchButton { + buttonActive: selectedTechnology !== ReaderPlugIn.REMOTE + onClicked: baseItem.requestPluginType(ReaderPlugIn.REMOTE) imageSource: "qrc:///images/icon_remote.svg" text: qsTr("WiFi") + settingsModel.translationTrigger } - Gov.TechnologySwitchButton { - visible: selectedTechnology !== "BLUETOOTH" - onClicked: baseItem.requestPluginType("BLUETOOTH") + TechnologySwitchButton { + buttonActive: selectedTechnology !== ReaderPlugIn.BLUETOOTH + onClicked: baseItem.requestPluginType(ReaderPlugIn.BLUETOOTH) imageSource: "qrc:///images/icon_bluetooth.svg" text: qsTr("Bluetooth") + settingsModel.translationTrigger } diff --git a/resources/qml/Governikus/TechnologyInfo/+ios/TechnologySwitch.qml b/resources/qml/Governikus/TechnologyInfo/+ios/TechnologySwitch.qml index b6ab0fa..98aaddd 100644 --- a/resources/qml/Governikus/TechnologyInfo/+ios/TechnologySwitch.qml +++ b/resources/qml/Governikus/TechnologyInfo/+ios/TechnologySwitch.qml @@ -1,15 +1,16 @@ -import QtQuick 2.7 +import QtQuick 2.10 import Governikus.Global 1.0 -import "." as Gov +import Governikus.Type.ReaderPlugIn 1.0 -Item { +Rectangle { id: baseItem height: technologyRow.height + color: Constants.background_color - signal requestPluginType(string pReaderPlugInType) + signal requestPluginType(int pReaderPlugInType) - property string selectedTechnology + property int selectedTechnology Row { id: technologyRow @@ -17,17 +18,16 @@ Item { anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter - Gov.TechnologySwitchButton { - visible: selectedTechnology !== "REMOTE" - onClicked: baseItem.requestPluginType("REMOTE") + TechnologySwitchButton { + buttonActive: selectedTechnology !== ReaderPlugIn.REMOTE + onClicked: baseItem.requestPluginType(ReaderPlugIn.REMOTE) imageSource: "qrc:///images/icon_remote.svg" text: qsTr("Use WiFi card reader instead
of Bluetooth card reader") + settingsModel.translationTrigger - } - Gov.TechnologySwitchButton { - visible: selectedTechnology !== "BLUETOOTH" - onClicked: baseItem.requestPluginType("BLUETOOTH") + TechnologySwitchButton { + buttonActive: selectedTechnology !== ReaderPlugIn.BLUETOOTH + onClicked: baseItem.requestPluginType(ReaderPlugIn.BLUETOOTH) imageSource: "qrc:///images/icon_bluetooth.svg" text: qsTr("Use Bluetooth card reader instead
of remote card reader") + settingsModel.translationTrigger } diff --git a/resources/qml/Governikus/TechnologyInfo/TechnologyInfo.qml b/resources/qml/Governikus/TechnologyInfo/TechnologyInfo.qml index 0745ea7..98597f0 100644 --- a/resources/qml/Governikus/TechnologyInfo/TechnologyInfo.qml +++ b/resources/qml/Governikus/TechnologyInfo/TechnologyInfo.qml @@ -1,6 +1,8 @@ -import QtQuick 2.5 +import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Type.NumberModel 1.0 + Item { id: baseItem @@ -8,7 +10,7 @@ Item { property alias enableButtonText: enableButton.text property alias enableButtonVisible: enableButton.visible property alias titleText: title.text - property string subTitleText: "" + property alias subTitleText: subTitle.text signal enableClicked() @@ -25,10 +27,9 @@ Item { wrapMode: Text.WordWrap visible: !!text - Behavior on text { SequentialAnimation { - PropertyAnimation { target: enableInfo; property: "opacity"; to: 0; duration: 500} + PropertyAction { target: enableInfo; property: "opacity"; value: 0.0 } PropertyAction { target: enableInfo; property: "text" } PropertyAnimation { target: enableInfo; property: "opacity"; to: 1.0; duration: 500} } @@ -70,41 +71,26 @@ Item { } } - onSubTitleTextChanged: { - subTitleTextColor.value = numberModel.hasError ? Constants.red : Constants.secondary_text - subTitle.text = subTitleText - } - - Item { + Text { + id: subTitle anchors.left: parent.left - anchors.top: parent.verticalCenter + anchors.top: title.bottom anchors.right: parent.right - anchors.bottom: parent.bottom - clip: true + anchors.bottom: enableButton.top + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignTop + font.pixelSize: Constants.normal_font_size + color: Constants.secondary_text + wrapMode: Text.WordWrap + visible: !enableInfo.visible && !enableButton.visible - Text { - id: subTitle - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - horizontalAlignment: Text.AlignHCenter - font.pixelSize: Constants.normal_font_size - color: Constants.secondary_text - wrapMode: Text.WordWrap - visible: !enableInfo.visible && !enableButton.visible - Behavior on text { - SequentialAnimation { - PropertyAnimation { target: subTitle; property: "anchors.topMargin"; to: baseItem.height/2; duration: 500} - PropertyAction { target: subTitle; property: "text" } - PropertyAction { - id: subTitleTextColor - target: subTitle; - property: "color"; - value: Constants.secondary_text - } - PropertyAnimation { target: subTitle; property: "anchors.topMargin"; to: 0; duration: 500 } - } + Behavior on text { + SequentialAnimation { + PropertyAnimation { target: subTitle; property: "opacity"; to: 0; duration: 500} + PropertyAction { target: subTitle; property: "text" } + PropertyAnimation { target: subTitle; property: "opacity"; to: 1.0; duration: 500 } + PropertyAction { target: subTitle; property: "color"; value: NumberModel.hasError ? Constants.red : Constants.secondary_text } } } } diff --git a/resources/qml/Governikus/TechnologyInfo/TechnologySwitch.qml b/resources/qml/Governikus/TechnologyInfo/TechnologySwitch.qml deleted file mode 100644 index 4b11736..0000000 --- a/resources/qml/Governikus/TechnologyInfo/TechnologySwitch.qml +++ /dev/null @@ -1,3 +0,0 @@ -Item { - id: dummy -} diff --git a/resources/qml/Governikus/TechnologyInfo/TechnologySwitchButton.qml b/resources/qml/Governikus/TechnologyInfo/TechnologySwitchButton.qml index 9b900ce..fa33b93 100644 --- a/resources/qml/Governikus/TechnologyInfo/TechnologySwitchButton.qml +++ b/resources/qml/Governikus/TechnologyInfo/TechnologySwitchButton.qml @@ -1,4 +1,4 @@ -import QtQuick 2.7 +import QtQuick 2.10 import QtGraphicalEffects 1.0 import Governikus.Global 1.0 @@ -6,13 +6,14 @@ import Governikus.Global 1.0 MouseArea { property alias imageSource: img.source property alias text: infoText.text + property bool buttonActive - height: img.height + 2 * img.anchors.topMargin - width: img.width + infoText.anchors.leftMargin + infoText.width + height: img.height + infoText.height + 2 * img.anchors.topMargin + infoText.anchors.topMargin + width: img.width + 2 * Utils.dp(10) Rectangle { height: 1 - width: parent.width * 1.2 + width: parent.width * 1.5 anchors.horizontalCenter: parent.horizontalCenter color: Constants.grey } @@ -20,15 +21,16 @@ MouseArea { id: img anchors.top: parent.top anchors.topMargin: Utils.dp(20) + anchors.horizontalCenter: parent.horizontalCenter height: Utils.dp(50) fillMode: Image.PreserveAspectFit smooth: true } Text { id: infoText - anchors.left: img.right - anchors.leftMargin: Utils.dp(10) - anchors.verticalCenter: img.verticalCenter + anchors.top: img.bottom + anchors.topMargin: Utils.dp(10) + anchors.horizontalCenter: img.horizontalCenter font.pixelSize: Constants.normal_font_size color: Constants.blue } @@ -40,7 +42,7 @@ MouseArea { hue: 0 lightness: 0.3 cached: true - visible: !parent.enabled + visible: !parent.enabled || buttonActive } Colorize { id: grayLevel2 @@ -50,6 +52,6 @@ MouseArea { hue: 0 lightness: 0.3 cached: true - visible: !parent.enabled + visible: !parent.enabled || buttonActive } } diff --git a/resources/qml/Governikus/TechnologyInfo/qmldir b/resources/qml/Governikus/TechnologyInfo/qmldir index b78c29f..10c6144 100644 --- a/resources/qml/Governikus/TechnologyInfo/qmldir +++ b/resources/qml/Governikus/TechnologyInfo/qmldir @@ -1,4 +1,5 @@ module TechnologyInfo + TechnologyInfo 1.0 TechnologyInfo.qml TechnologySwitch 1.0 TechnologySwitch.qml TechnologySwitchButton 1.0 TechnologySwitchButton.qml diff --git a/resources/qml/Governikus/TitleBar/+android/TitleBar.qml b/resources/qml/Governikus/TitleBar/+android/TitleBar.qml deleted file mode 100644 index e948227..0000000 --- a/resources/qml/Governikus/TitleBar/+android/TitleBar.qml +++ /dev/null @@ -1,130 +0,0 @@ -import QtQuick 2.7 -import QtQuick.Controls 1.4 - -import Governikus.Global 1.0 - -Item -{ - property int duration: 300 - property alias titleBarOpacity: background.opacity - id: titleBar - height: Constants.titlebar_height - - readonly property TitleBarAction defaultLeftAction: TitleBarAction {} - readonly property TitleBarAction defaultTitle: TitleBarAction {} - readonly property Item defaultRightAction: Item {} - - readonly property TitleBarAction activeleftAction: leftAction ? leftAction : defaultLeftAction - readonly property TitleBarAction activeTitleItem: titleItem ? titleItem : defaultTitle - readonly property Item activeRightAction: rightAction ? rightAction : defaultRightAction - - property var leftAction - property var titleItem - property var rightAction - property var subTitleBarAction - property var color - - - Rectangle { - id: background - color: titleBar.color ? titleBar.color : Constants.blue - anchors.top: parent.top - anchors.left: PlatformConstants.is_tablet ? hamburgerFrame.right : parent.left - anchors.bottom: parent.bottom - anchors.right: parent.right - - onColorChanged: statusBarUtil.setStatusBarColor(String(color)) - Behavior on color { ColorAnimation { duration: titleBar.duration } } - } - - Rectangle { - id: hamburgerFrame - height: parent.height - width: Constants.menubar_width - anchors.left: parent.left - color: background.color - opacity: PlatformConstants.is_tablet ? 1 : 0 - } - - Hamburger { - id: burger - anchors.horizontalCenter: hamburgerFrame.horizontalCenter - height: parent.height - width: height - state: navBar.isOpen ? "back" : activeleftAction.state - - MouseArea { - anchors.fill: parent - onClicked: { - switch (burger.state) { - case "": - navBar.open() - break - case "back": - if (navBar.isOpen) { - navBar.close() - } else { - activeleftAction.clicked(mouse) - } - break - case "hidden": - break - default: - activeleftAction.clicked(mouse) - } - } - } - } - - Item { - id: titleText - property string text: activeTitleItem.text - property bool bold: activeTitleItem.font.bold - - anchors.left: burger.right - anchors.leftMargin: Constants.titlebar_spacing - anchors.right: parent.right - anchors.rightMargin: Constants.titlebar_padding + (rightActionStack.width > 0 ? rightAction.width + Constants.titlebar_spacing : 0) - height: parent.height - clip: true - - TitleBarText { - id: oldTitle - text: parent.text - Component.onCompleted: font.bold = parent.bold - opacity: 0 - - Behavior on text { - SequentialAnimation { - PropertyAnimation { target: oldTitle; property: "opacity"; from: 1; to: 0; duration: titleBar.duration } - PropertyAction { target: oldTitle; property: "font.bold"; value: titleText.bold } - PropertyAction { target: oldTitle; property: "text" } - } - } - } - - TitleBarText { - id: newTitle - text: parent.text - font.bold: parent.bold - - Behavior on text { - ParallelAnimation { - PropertyAnimation { target: newTitle; property: "opacity"; from: 0; to: 1; duration: titleBar.duration } - PropertyAnimation { target: newTitle; property: "x"; from: width; to: 0; duration: titleBar.duration } - } - } - } - - } - - Item { - id: rightActionStack - anchors.rightMargin: Constants.titlebar_padding - anchors.top: parent.top - anchors.right: parent.right - anchors.bottom: parent.bottom - width: rightAction ? activeRightAction.contentWidth : 0 - data: activeRightAction - } -} diff --git a/resources/qml/Governikus/TitleBar/+desktop/CancelAction.qml b/resources/qml/Governikus/TitleBar/+desktop/CancelAction.qml new file mode 100644 index 0000000..4e29099 --- /dev/null +++ b/resources/qml/Governikus/TitleBar/+desktop/CancelAction.qml @@ -0,0 +1,53 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 +import Governikus.View 1.0 +import Governikus.Type.ApplicationModel 1.0 + + +Button { + id: button + visible: ApplicationModel.currentWorkflow !== "" + + Accessible.role: Accessible.Button + Accessible.name: qsTr("Cancel") + + background: Row { + id: row + spacing: ApplicationModel.scaleFactor * 20 + + Rectangle { + height: text.height + width: Math.max(ApplicationModel.scaleFactor * 2, 1) + } + + Image { + sourceSize.height: text.height + source: "qrc:///images/cancel.svg" + } + + Item { + id: text + height: sizeBase.height + width: sizeBase.width + readonly property Text sizeBase: Text { + font.weight: Font.Bold + font.pixelSize: Constants.titlebar_font_size + text: qsTr("Cancel") + settingsModel.translationTrigger + } + + FocusFrame { + scope: button + } + + Text { + anchors.centerIn: parent + color: Constants.white + font.weight: Font.Bold + font.pixelSize: Constants.titlebar_font_size * (button.down ? 0.9 : 1) + text: text.sizeBase.text + } + } + } +} diff --git a/resources/qml/Governikus/TitleBar/+desktop/TitleBar.qml b/resources/qml/Governikus/TitleBar/+desktop/TitleBar.qml new file mode 100644 index 0000000..31332a0 --- /dev/null +++ b/resources/qml/Governikus/TitleBar/+desktop/TitleBar.qml @@ -0,0 +1,123 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.View 1.0 + + +Item { + id: titleBar + height: actionRow.height + 2 * Constants.titlebar_padding + + focus: true + KeyNavigation.tab: rootAction + + Accessible.role: Accessible.Grouping + Accessible.name: qsTr("Titlebar") + Accessible.description: qsTr("This bar represents the navigation tree of the AusweisApp2.") + + property Item navSuccessor: null + + property var contentRoot + signal rootClicked(); + + property var rightMostAction: actionRow.lastAction + + function updateActions() { + actionRow.children = [rootAction] + rootAction.KeyNavigation.tab = rightTitleBarActions.children[0] + addRecursive(contentRoot) + } + + function addRecursive(root) { + for (var i in root.children) { + var child = root.children[i] + if (child.sectionPageTypeMarker && child.visible) { + if (child.titleBarAction) { + addAction(child.titleBarAction) + } + addRecursive(child) + } + } + } + + function addAction(newAction) { + rightMostAction.KeyNavigation.tab = newAction + + actionRow.children.push(newAction) + newAction.customSubAction.KeyNavigation.tab = settingsButton + newAction.KeyNavigation.tab = rightTitleBarActions.children[0] + } + + Rectangle + { + anchors.fill: parent + color: Constants.blue + + Row { + id: actionRow + + height: rootAction.height + anchors.left: parent.left + anchors.leftMargin: Constants.titlebar_padding + anchors.verticalCenter: parent.verticalCenter + + spacing: Constants.titlebar_spacing + + readonly property Item lastAction: children && children.length > 0 ? children[children.length - 1] : rootAction + + TitleBarAction { + id: rootAction + showArrow: false + text: qsTr("Start") + onClicked: titleBar.rootClicked() + + Component.onCompleted: { + rootAction.customSubAction.KeyNavigation.tab = settingsButton + rootAction.KeyNavigation.tab = rightTitleBarActions.children[0] + } + } + } + + Row { + id: rightTitleBarActions + + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.margins: Constants.titlebar_padding + spacing: Constants.titlebar_spacing + + children: [ + rightMostAction.customSubAction, + settingsButton, + helpButton + ] + + TitleBarButton { + id: settingsButton + + height: rightTitleBarActions.height + source: "qrc:///images/desktop/settings_icon.svg" + visible: rightMostAction.showSettings + + KeyNavigation.tab: helpButton + Accessible.name: qsTr("Settings") + + onClicked: rightMostAction.settingsHandler() + } + + TitleBarButton { + id: helpButton + + height: rightTitleBarActions.height + source: "qrc:///images/desktop/help_icon.svg" + visible: rightMostAction.showHelp + + KeyNavigation.tab: titleBar.navSuccessor + Accessible.name: qsTr("Help") + + onClicked: qmlExtension.openOnlineHelp(rightMostAction.helpTopic) + } + } + } +} diff --git a/resources/qml/Governikus/TitleBar/+desktop/TitleBarAction.qml b/resources/qml/Governikus/TitleBar/+desktop/TitleBarAction.qml new file mode 100644 index 0000000..930b168 --- /dev/null +++ b/resources/qml/Governikus/TitleBar/+desktop/TitleBarAction.qml @@ -0,0 +1,65 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.View 1.0 + +FocusScope { + id: scope + + height: row.height + width: row.width + + signal clicked() + + property alias showArrow: arrow.visible + property alias text: text.text + + property Item customSubAction: Item { visible: false } + + property bool showSettings: true + property var customSettingsHandler + readonly property var settingsHandler: customSettingsHandler ? customSettingsHandler : function() { + // TODO open settings + } + + property bool showHelp: true + property string helpTopic: "applicationPage" + + Accessible.role: Accessible.Button + Accessible.name: text.text + + Keys.onSpacePressed: scope.clicked() + + Row { + id: row + + height: text.height + spacing: Constants.titlebar_spacing + + Image { + id: arrow + sourceSize.height: text.height / 2.5 + anchors.verticalCenter: text.verticalCenter + anchors.verticalCenterOffset: height / 6 + source: "qrc:///images/desktop/titlebar_arrow.svg" + } + + TitleBarText { + id: text + + Accessible.role: Accessible.Button + Accessible.name: text.text + + FocusFrame { + scope: scope + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onPressed: scope.focus = true + onClicked: scope.clicked() + } + } + } +} diff --git a/resources/qml/Governikus/TitleBar/+desktop/TitleBarButton.qml b/resources/qml/Governikus/TitleBar/+desktop/TitleBarButton.qml new file mode 100644 index 0000000..6b15c1a --- /dev/null +++ b/resources/qml/Governikus/TitleBar/+desktop/TitleBarButton.qml @@ -0,0 +1,22 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.View 1.0 + + +Button { + id: button + width: height + enabled: visible + property alias source: image.source + + Accessible.role: Accessible.Button + + FocusFrame {} + + background: Image { + id: image + anchors.centerIn: parent + sourceSize.height: button.height * (button.down ? 0.9 : 1) + } +} diff --git a/resources/qml/Governikus/TitleBar/+desktop/TitleBarText.qml b/resources/qml/Governikus/TitleBar/+desktop/TitleBarText.qml new file mode 100644 index 0000000..c062662 --- /dev/null +++ b/resources/qml/Governikus/TitleBar/+desktop/TitleBarText.qml @@ -0,0 +1,9 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 + +GText { + color: Constants.white + font.weight: Font.Bold + font.pixelSize: Constants.titlebar_font_size +} diff --git a/resources/qml/Governikus/TitleBar/+ios/TitleBar.qml b/resources/qml/Governikus/TitleBar/+ios/TitleBar.qml deleted file mode 100644 index 6793708..0000000 --- a/resources/qml/Governikus/TitleBar/+ios/TitleBar.qml +++ /dev/null @@ -1,113 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Controls 2.2 - -import Governikus.Global 1.0 - -Item { - property int duration: 300 - property alias titleBarOpacity: background.opacity - id: titleBar - height: Constants.titlebar_height + activeSubTitleBarAction.height - - readonly property var defaultLeftAction: Text {} - readonly property var defaultTitle: Text {} - readonly property var defaultRightAction: Text {} - readonly property var defaultSubTitleBarAction: Item {} - - property var activeleftAction: leftAction ? leftAction : defaultLeftAction - property var activeTitleItem: titleItem ? titleItem : defaultTitle - property var activeRightAction: rightAction ? rightAction : defaultRightAction - property var activeSubTitleBarAction: subTitleBarAction ? subTitleBarAction : defaultSubTitleBarAction - - property var leftAction - property var titleItem - property var rightAction - property var subTitleBarAction - property var color - - - Rectangle { - id: background - anchors.fill: parent - color: titleBar.color ? titleBar.color : Constants.blue - - Behavior on color { ColorAnimation { duration: titleBar.duration } } - } - - Item { - id: firstLine - - height: Constants.titlebar_height - width: parent.width - - Item { - id: leftActionStack - data: activeleftAction - width: activeleftAction.width - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.leftMargin: Constants.titlebar_padding - } - - Item { - id: titleText - property string text: activeTitleItem.text - property bool bold: activeTitleItem.font.bold - - anchors.left: parent.left - anchors.leftMargin: leftActionStack.width > 0 ? leftAction.width + Constants.titlebar_spacing : 0 - anchors.right: parent.right - anchors.rightMargin: rightActionStack.width > 0 ? rightAction.width + Constants.titlebar_spacing : 0 - height: parent.height - clip: true - - TitleBarText { - id: oldTitle - text: parent.text - width: parent.width - horizontalAlignment: Text.AlignHCenter - Component.onCompleted: font.bold = parent.bold - opacity: 0 - - Behavior on text { - SequentialAnimation { - PropertyAnimation { target: oldTitle; property: "opacity"; from: 1; to: 0; duration: titleBar.duration } - PropertyAction { target: oldTitle; property: "font.bold"; value: titleText.bold } - PropertyAction { target: oldTitle; property: "text" } - } - } - } - - TitleBarText { - id: newTitle - text: parent.text - width: parent.width - font.bold: parent.bold - horizontalAlignment: Text.AlignHCenter - - Behavior on text { - ParallelAnimation { - PropertyAnimation { target: newTitle; property: "opacity"; from: 0; to: 1; duration: titleBar.duration } - PropertyAnimation { target: newTitle; property: "x"; from: width; to: 0; duration: titleBar.duration } - } - } - } - } - - Item { - id: rightActionStack - data: activeRightAction - width: activeRightAction.width - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - anchors.rightMargin: Constants.titlebar_padding - } - } - - Item { - data: activeSubTitleBarAction - width: parent.width - anchors.top: firstLine.bottom - anchors.horizontalCenter: parent.horizontalCenter - } -} diff --git a/resources/qml/Governikus/TitleBar/+mobile/+android/Hamburger.qml b/resources/qml/Governikus/TitleBar/+mobile/+android/Hamburger.qml new file mode 100644 index 0000000..0009768 --- /dev/null +++ b/resources/qml/Governikus/TitleBar/+mobile/+android/Hamburger.qml @@ -0,0 +1,127 @@ +import Governikus.Global 1.0 + +import QtQuick 2.10 + + +Item { + id: baseItem + width: height + visible: state !== "hidden" + + Item { + id: content + anchors.centerIn: parent + height: Math.min(parent.height, parent.width) / 2 + width: height + + readonly property double lineHeight: height / 8 + + Rectangle { + id: r0 + transformOrigin: Item.TopLeft + x: 0 + y: content.height / 2 - 2.5 * content.lineHeight + width: content.width + height: content.lineHeight + antialiasing: true + color: Constants.white + } + + Rectangle { + id: r1 + transformOrigin: Item.TopLeft + x: 0 + y: content.height / 2 - 0.5 * content.lineHeight + width: content.width + height: content.lineHeight + antialiasing: true + color: Constants.white + } + + Rectangle { + id: r2 + transformOrigin: Item.TopLeft + x: 0 + y: content.height / 2 + 1.5 * content.lineHeight + width: content.width + height: content.lineHeight + antialiasing: true + color: Constants.white + } + } + + states: [ + State { + id: backState + name: "back" + + readonly property double itemArrowWidth: 0.7 * content.width + readonly property double delta0: Math.sqrt(Math.pow(itemArrowWidth, 2) / 2) + readonly property double delta1: Math.sqrt(Math.pow(content.lineHeight, 2) / 2) + + PropertyChanges { target: content; rotation: 180 } + + PropertyChanges { + target: r0 + rotation: 45 + x: content.width - backState.delta0 + y: content.height / 2 - backState.delta0 + width: backState.itemArrowWidth + } + PropertyChanges { + target: r1 + rotation: 0 + x: 0 + y: (content.height - content.lineHeight) / 2 + width: content.width - content.lineHeight + } + PropertyChanges { + target: r2 + rotation: -45 + x: content.width - backState.delta0 - backState.delta1 + y: content.height / 2 + backState.delta0 - backState.delta1 + width: backState.itemArrowWidth + } + }, + + State { + id: cancelState + name: "cancel" + + readonly property double delta0: Math.sqrt(Math.pow(content.lineHeight, 2) / 2) + readonly property double delta1: Math.sqrt(2 * Math.pow(content.width, 2)) - content.lineHeight + + PropertyChanges { target: content; rotation: 180 } + + PropertyChanges { + target: r0 + rotation: 45 + x: cancelState.delta0 + y: 0 + width: cancelState.delta1 + } + PropertyChanges { + target: r1 + rotation: 0 + x: content.width / 2 + y: (content.height - content.lineHeight) / 2 + width: 0 + } + PropertyChanges { + target: r2 + rotation: -45 + x: 0 + y: content.height - cancelState.delta0 + width: cancelState.delta1 + } + } + ] + + transitions: [ + Transition { from: "hidden" }, + Transition { + RotationAnimation { target: content; direction: RotationAnimation.Clockwise; duration: 300; easing.type: Easing.InOutQuad } + PropertyAnimation { targets: [r0, r1, r2]; properties: "rotation, x, y, width"; duration: 300; easing.type: Easing.InOutQuad } + } + ] +} diff --git a/resources/qml/Governikus/TitleBar/+mobile/+android/TitleBar.qml b/resources/qml/Governikus/TitleBar/+mobile/+android/TitleBar.qml new file mode 100644 index 0000000..42ebdfa --- /dev/null +++ b/resources/qml/Governikus/TitleBar/+mobile/+android/TitleBar.qml @@ -0,0 +1,131 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.Type.StatusBarUtil 1.0 + + +Item +{ + property int duration: 300 + property alias titleBarOpacity: background.opacity + id: titleBar + height: Constants.titlebar_height + + readonly property TitleBarAction defaultLeftAction: TitleBarAction {} + readonly property TitleBarAction defaultTitle: TitleBarAction {} + readonly property Item defaultRightAction: Item {} + + readonly property TitleBarAction activeleftAction: leftAction ? leftAction : defaultLeftAction + readonly property TitleBarAction activeTitleItem: titleItem ? titleItem : defaultTitle + readonly property Item activeRightAction: rightAction ? rightAction : defaultRightAction + + property var leftAction + property var titleItem + property var rightAction + property var subTitleBarAction + property var color + + + Rectangle { + id: background + color: titleBar.color ? titleBar.color : Constants.blue + anchors.top: parent.top + anchors.left: Constants.is_tablet ? hamburgerFrame.right : parent.left + anchors.bottom: parent.bottom + anchors.right: parent.right + + onColorChanged: StatusBarUtil.setStatusBarColor(String(color)) + Behavior on color { ColorAnimation { duration: titleBar.duration } } + } + + Rectangle { + id: hamburgerFrame + height: parent.height + width: Constants.menubar_width + anchors.left: parent.left + color: background.color + opacity: Constants.is_tablet ? 1 : 0 + } + + Hamburger { + id: burger + anchors.horizontalCenter: hamburgerFrame.horizontalCenter + height: parent.height + state: navBar.isOpen ? "back" : activeleftAction.state + + MouseArea { + anchors.fill: parent + onClicked: { + switch (burger.state) { + case "": + navBar.open() + break + case "back": + if (navBar.isOpen) { + navBar.close() + } else { + activeleftAction.clicked(mouse) + } + break + case "hidden": + break + default: + activeleftAction.clicked(mouse) + } + } + } + } + + Item { + id: titleText + property string text: activeTitleItem.text + property bool bold: activeTitleItem.font.bold + + anchors.left: burger.right + anchors.leftMargin: Constants.titlebar_spacing + anchors.right: parent.right + anchors.rightMargin: Constants.titlebar_padding + (rightActionStack.width > 0 ? rightAction.width + Constants.titlebar_spacing : 0) + height: parent.height + clip: true + + TitleBarText { + id: oldTitle + width: parent.width + text: parent.text + Component.onCompleted: font.bold = parent.bold + opacity: 0 + + Behavior on text { + SequentialAnimation { + PropertyAnimation { target: oldTitle; property: "opacity"; from: 1; to: 0; duration: titleBar.duration } + PropertyAction { target: oldTitle; property: "font.bold"; value: titleText.bold } + PropertyAction { target: oldTitle; property: "text" } + } + } + } + + TitleBarText { + id: newTitle + width: parent.width + text: parent.text + font.bold: parent.bold + + Behavior on text { + ParallelAnimation { + PropertyAnimation { target: newTitle; property: "opacity"; from: 0; to: 1; duration: titleBar.duration } + PropertyAnimation { target: newTitle; property: "x"; from: width; to: 0; duration: titleBar.duration } + } + } + } + } + + Item { + id: rightActionStack + anchors.rightMargin: Constants.titlebar_padding + anchors.top: parent.top + anchors.right: parent.right + anchors.bottom: parent.bottom + width: rightAction ? activeRightAction.contentWidth : 0 + data: activeRightAction + } +} diff --git a/resources/qml/Governikus/TitleBar/+mobile/+ios/TitleBar.qml b/resources/qml/Governikus/TitleBar/+mobile/+ios/TitleBar.qml new file mode 100644 index 0000000..691b08a --- /dev/null +++ b/resources/qml/Governikus/TitleBar/+mobile/+ios/TitleBar.qml @@ -0,0 +1,113 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 + +Item { + property int duration: 300 + property alias titleBarOpacity: background.opacity + id: titleBar + height: Constants.titlebar_height + activeSubTitleBarAction.height + + readonly property var defaultLeftAction: Text {} + readonly property var defaultTitle: Text {} + readonly property var defaultRightAction: Text {} + readonly property var defaultSubTitleBarAction: Item {} + + property var activeleftAction: leftAction ? leftAction : defaultLeftAction + property var activeTitleItem: titleItem ? titleItem : defaultTitle + property var activeRightAction: rightAction ? rightAction : defaultRightAction + property var activeSubTitleBarAction: subTitleBarAction ? subTitleBarAction : defaultSubTitleBarAction + + property var leftAction + property var titleItem + property var rightAction + property var subTitleBarAction + property var color + + + Rectangle { + id: background + anchors.fill: parent + color: titleBar.color ? titleBar.color : Constants.blue + + Behavior on color { ColorAnimation { duration: titleBar.duration } } + } + + Item { + id: firstLine + + height: Constants.titlebar_height + width: parent.width + + Item { + id: leftActionStack + data: activeleftAction + width: activeleftAction.width + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: Constants.titlebar_padding + } + + Item { + id: titleText + property string text: activeTitleItem.text + property bool bold: activeTitleItem.font.bold + + anchors.left: parent.left + anchors.leftMargin: leftActionStack.width > 0 ? leftAction.width + Constants.titlebar_spacing : 0 + anchors.right: parent.right + anchors.rightMargin: rightActionStack.width > 0 ? rightAction.width + Constants.titlebar_spacing : 0 + height: parent.height + clip: true + + TitleBarText { + id: oldTitle + text: parent.text + width: parent.width + horizontalAlignment: Text.AlignHCenter + Component.onCompleted: font.bold = parent.bold + opacity: 0 + + Behavior on text { + SequentialAnimation { + PropertyAnimation { target: oldTitle; property: "opacity"; from: 1; to: 0; duration: titleBar.duration } + PropertyAction { target: oldTitle; property: "font.bold"; value: titleText.bold } + PropertyAction { target: oldTitle; property: "text" } + } + } + } + + TitleBarText { + id: newTitle + text: parent.text + width: parent.width + font.bold: parent.bold + horizontalAlignment: Text.AlignHCenter + + Behavior on text { + ParallelAnimation { + PropertyAnimation { target: newTitle; property: "opacity"; from: 0; to: 1; duration: titleBar.duration } + PropertyAnimation { target: newTitle; property: "x"; from: width; to: 0; duration: titleBar.duration } + } + } + } + } + + Item { + id: rightActionStack + data: activeRightAction + width: activeRightAction.width + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + anchors.rightMargin: Constants.titlebar_padding + } + } + + Item { + data: activeSubTitleBarAction + width: parent.width + anchors.top: firstLine.bottom + anchors.horizontalCenter: parent.horizontalCenter + } +} diff --git a/resources/qml/Governikus/TitleBar/+mobile/TitleBarAction.qml b/resources/qml/Governikus/TitleBar/+mobile/TitleBarAction.qml new file mode 100644 index 0000000..8810bb2 --- /dev/null +++ b/resources/qml/Governikus/TitleBar/+mobile/TitleBarAction.qml @@ -0,0 +1,24 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 + +MouseArea { + property string text: "" + property alias font: titleBarText.font + + height: Constants.titlebar_height + width: titleBarText.width + anchors.centerIn: parent + + TitleBarText { + id: titleBarText + anchors.centerIn: parent + + text: ( parent.text !== "" ? parent.text : + parent.state === "cancel" ? qsTr("Cancel") : + parent.state === "edit" ? qsTr("Edit") : + parent.state === "back" ? qsTr("< back") : + parent.state === "hidden" ? "" : + "") + settingsModel.translationTrigger + } +} diff --git a/resources/qml/Governikus/TitleBar/+mobile/TitleBarText.qml b/resources/qml/Governikus/TitleBar/+mobile/TitleBarText.qml new file mode 100644 index 0000000..51147f5 --- /dev/null +++ b/resources/qml/Governikus/TitleBar/+mobile/TitleBarText.qml @@ -0,0 +1,12 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 + +Text { + id: textItem + anchors.verticalCenter: parent.verticalCenter + color: "white" + font.pixelSize: Constants.titlebar_font_size + maximumLineCount: 1 + elide: Text.ElideRight +} diff --git a/resources/qml/Governikus/TitleBar/Hamburger.qml b/resources/qml/Governikus/TitleBar/Hamburger.qml deleted file mode 100644 index 297ed5b..0000000 --- a/resources/qml/Governikus/TitleBar/Hamburger.qml +++ /dev/null @@ -1,170 +0,0 @@ -import QtQuick 2.5 - -Item { - id: baseItem - property color color: "white" - visible: state !== "hidden" - - Item { - id: content - width: parent.width * 0.7 - height: parent.height * 0.7 - anchors.centerIn: parent - - property double itemWidth: width * 0.7 - property double itemHeight: content.height / 12 - property double itemArrowWidth: itemWidth * 0.6 - property double itemArrowMiddleWidth: itemWidth * 0.85 - property double itemArrowWidthDelta: itemArrowWidth / (2 * Math.SQRT2) - property double itemWidthDelta: itemWidth / (2 * Math.SQRT2) - property double itemHeightDelta: itemHeight / (2 * Math.SQRT2) - - Rectangle { - id: r0 - x: (content.width - content.itemWidth) * 0.5 - y: (2+0) / 6 * content.height - content.itemHeight * 0.5 - width: content.itemWidth - height: content.itemHeight - antialiasing: true - opacity: 1 - color: baseItem.color - } - - Rectangle { - id: r1 - x: (content.width - content.itemWidth) * 0.5 - y: (2+1) / 6 * content.height - content.itemHeight * 0.5 - width: content.itemWidth - height: content.itemHeight - antialiasing: true - opacity: (baseItem.state === "cancel") ? 0 : 1 - color: baseItem.color - } - - Rectangle { - id: r2 - x: (content.width - content.itemWidth) * 0.5 - y: (2+2) / 6 * content.height - content.itemHeight * 0.5 - width: content.itemWidth - height: content.itemHeight - antialiasing: true - opacity: 1 - color: baseItem.color - } - } - - states: [ - State { - name: "back" - PropertyChanges { target: content; rotation: 180 } - - PropertyChanges { - target: r0 - explicit: true - transformOrigin: Item.Right - rotation: 45 - opacity: 1 - x: content.itemWidth * 0.5 - y: (2+1) / 6 * content.height - content.itemHeight * 0.25 - width: content.itemArrowWidth - } - PropertyChanges { - target: r1 - explicit: true - opacity: 1 - x: (content.width - content.itemArrowMiddleWidth - content.itemHeightDelta) * 0.5 - y: (2+1) / 6 * content.height - content.itemHeight * 0.5 - width: content.itemArrowMiddleWidth - } - PropertyChanges { - target: r2 - explicit: true - transformOrigin: Item.Right - opacity: 1 - rotation: -45 - x: content.itemWidth * 0.5 - y: (2+1) / 6 * content.height - content.itemHeight * 0.75 - width: content.itemArrowWidth - } - }, - - State { - name: "cancel" - PropertyChanges { target: content; rotation: 180 } - - PropertyChanges { - target: r0 - explicit: true - transformOrigin: Item.Center - rotation: 45 - opacity: 1 - x: content.width * 0.5 - content.itemWidthDelta - y: (2+1) / 6 * content.height - content.itemHeight * 0.5 - width: content.itemWidth - } - PropertyChanges { - target: r1 - explicit: true - transformOrigin: Item.Center - rotation: 0 - opacity: 0 - width: content.itemWidth - x: (content.width - content.itemWidth) * 0.5 - y: (2+1) / 6 * content.height - content.itemHeight * 0.5 - } - PropertyChanges { - target: r2 - explicit: true - transformOrigin: Item.Center - rotation: -45 - opacity: 1 - x: content.width * 0.5 - content.itemWidthDelta - y: (2+1) / 6 * content.height - content.itemHeight * 0.5 - width: content.itemWidth - } - }, - - State { - name: "" - PropertyChanges { target: content; rotation: 0 } - - PropertyChanges { - target: r0 - explicit: true - transformOrigin: Item.Right - rotation: 0 - opacity: 1 - x: (content.width - content.itemWidth) * 0.5 - y: (2+0) / 6 * content.height - content.itemHeight * 0.5 - width: content.itemWidth - } - PropertyChanges { - target: r1 - explicit: true - transformOrigin: Item.Right - rotation: 0 - opacity: 1 - x: (content.width - content.itemWidth) * 0.5 - y: (2+1) / 6 * content.height - content.itemHeight * 0.5 - width: content.itemWidth - } - PropertyChanges { - target: r2 - explicit: true - transformOrigin: Item.Right - rotation: 0 - opacity: 1 - x: (content.width - content.itemWidth) * 0.5 - y: (2+2) / 6 * content.height - content.itemHeight * 0.5 - width: content.itemWidth - } - } - ] - - transitions: [ - Transition { - RotationAnimation { target: content; direction: RotationAnimation.Clockwise; duration: 300; easing.type: Easing.InOutQuad } - PropertyAnimation { targets: [r0, r1, r2]; properties: "opacity, width, rotation, y, x"; duration: 300; easing.type: Easing.InOutQuad } - } - ] -} diff --git a/resources/qml/Governikus/TitleBar/TitleBar.qml b/resources/qml/Governikus/TitleBar/TitleBar.qml deleted file mode 100644 index 4b11736..0000000 --- a/resources/qml/Governikus/TitleBar/TitleBar.qml +++ /dev/null @@ -1,3 +0,0 @@ -Item { - id: dummy -} diff --git a/resources/qml/Governikus/TitleBar/TitleBarAction.qml b/resources/qml/Governikus/TitleBar/TitleBarAction.qml deleted file mode 100644 index 4ca362c..0000000 --- a/resources/qml/Governikus/TitleBar/TitleBarAction.qml +++ /dev/null @@ -1,24 +0,0 @@ -import QtQuick 2.7 - -import Governikus.Global 1.0 - -MouseArea { - property string text: "" - property alias font: titleBarText.font - - height: Constants.titlebar_height - width: titleBarText.width - anchors.centerIn: parent - - TitleBarText { - id: titleBarText - anchors.centerIn: parent - - text: ( parent.text !== "" ? parent.text : - parent.state === "cancel" ? qsTr("Cancel") : - parent.state === "edit" ? qsTr("Edit") : - parent.state === "back" ? qsTr("< back") : - parent.state === "hidden" ? "" : - "") + settingsModel.translationTrigger - } -} diff --git a/resources/qml/Governikus/TitleBar/TitleBarText.qml b/resources/qml/Governikus/TitleBar/TitleBarText.qml deleted file mode 100644 index 5cbe0a6..0000000 --- a/resources/qml/Governikus/TitleBar/TitleBarText.qml +++ /dev/null @@ -1,12 +0,0 @@ -import QtQuick 2.7 - -import Governikus.Global 1.0 - -Text { - id: textItem - anchors.verticalCenter: parent.verticalCenter - color: "white" - font.pixelSize: Constants.titlebar_font_size - maximumLineCount: 1 - elide: Text.ElideRight -} diff --git a/resources/qml/Governikus/TitleBar/qmldir b/resources/qml/Governikus/TitleBar/qmldir index 50eeff3..6db473e 100644 --- a/resources/qml/Governikus/TitleBar/qmldir +++ b/resources/qml/Governikus/TitleBar/qmldir @@ -1,5 +1,9 @@ module TitleBar + +internal TitleBarText TitleBarText.qml + +CancelAction 1.0 CancelAction.qml +Hamburger 1.0 Hamburger.qml TitleBar 1.0 TitleBar.qml TitleBarAction 1.0 TitleBarAction.qml -TitleBarText 1.0 TitleBarText.qml -Hamburger 1.0 Hamburger.qml +TitleBarButton 1.0 TitleBarButton.qml diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialCollapseAnimation.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialCollapseAnimation.qml new file mode 100644 index 0000000..d56db49 --- /dev/null +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialCollapseAnimation.qml @@ -0,0 +1,23 @@ +import QtQuick 2.10 + +SequentialAnimation { + id: collapseAnimation + property real duration: 500 + property var targetContent + + ParallelAnimation { + NumberAnimation { + target: collapseAnimation.targetContent + property: "height" + easing.type: Easing.InOutQuad + to: 0 + duration: collapseAnimation.duration + } + } + + PropertyAction { + target: collapseAnimation.targetContent + property: "visible" + value: false + } +} diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialContent.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialContent.qml new file mode 100644 index 0000000..2c2ccc1 --- /dev/null +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialContent.qml @@ -0,0 +1,24 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 + +Rectangle { + id: contentBackground + visible: false + width: parent.width + height: 0 + color: Constants.white + + default property alias columnChildren: contentColumn.children + readonly property alias contentHeight: contentColumn.height + + Column { + id: contentColumn + + width: parent.width + anchors.top: parent.top + topPadding: parent.width * 0.15 + bottomPadding: Constants.component_spacing + spacing: Constants.tutorial_component_spacing + } +} diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialExpandAnimation.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialExpandAnimation.qml new file mode 100644 index 0000000..5d85c53 --- /dev/null +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialExpandAnimation.qml @@ -0,0 +1,33 @@ +import QtQuick 2.10 + +SequentialAnimation { + id: expandAnimation + property real duration: 500 + property var targetContent + property var targetHeader + + PropertyAction { + target: expandAnimation.targetContent + property: "visible" + value: true + } + + ParallelAnimation { + NumberAnimation { + target: flickable + property: "contentY" + easing.type: Easing.InOutQuad + to: expandAnimation.targetHeader.initY + duration: expandAnimation.duration + } + + NumberAnimation { + target: expandAnimation.targetContent + property: "height" + easing.type: Easing.InOutQuad + from: 0 + to: expandAnimation.targetContent.contentHeight + duration: expandAnimation.duration + } + } +} diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialFooter.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialFooter.qml new file mode 100644 index 0000000..d0f3c56 --- /dev/null +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialFooter.qml @@ -0,0 +1,101 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 + +Rectangle { + id: baseItem + height: Math.max(backToMenu.height, quitTutorial.height) + + property alias backText: menuText.text + + signal menuClicked() + signal quitTutorialClicked() + + Item { + id: backToMenu + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + height: menuRow.height + 2 * Constants.component_spacing + width: menuRow.width + + MouseArea { + anchors.fill: parent + preventStealing: true + onClicked: baseItem.menuClicked() + } + + Row { + id: menuRow + height: menuText.height + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + padding: Constants.component_spacing + spacing: Constants.component_spacing + + Image { + source: "qrc:///images/tutorial/arrows.svg" + rotation: -90 + anchors.verticalCenter: parent.verticalCenter + + height: parent.height + width: height * (sourceSize.width / sourceSize.height) + fillMode: Image.PreserveAspectFit + } + + Text { + id: menuText + anchors.verticalCenter: parent.verticalCenter + text: qsTr("Fold in") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_font_size + color: Constants.white + } + } + + + } + + Item { + id: quitTutorial + height: quitRow.height + 2 * Constants.component_spacing + width: quitRow.width + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + + MouseArea { + anchors.fill: parent + preventStealing: true + onClicked: { + baseItem.quitTutorialClicked() + } + } + + Row { + id: quitRow + height: quitText.height + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + padding: Constants.component_spacing + spacing: Constants.component_spacing + + Text { + id: quitText + anchors.verticalCenter: parent.verticalCenter + text: qsTr("Quit tutorial") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_font_size + color: Constants.white + } + + Image { + anchors.verticalCenter: parent.verticalCenter + source: "qrc:///images/tutorial/arrows.svg" + + height: parent.height + width: height * (sourceSize.width / sourceSize.height) + fillMode: Image.PreserveAspectFit + } + } + } +} + diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialHeader.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialHeader.qml new file mode 100644 index 0000000..df66c3e --- /dev/null +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialHeader.qml @@ -0,0 +1,42 @@ +import QtQuick 2.10 +import QtGraphicalEffects 1.0 + +import Governikus.Global 1.0 + +Item { + id: baseItem + width: parent.width + + property alias headerImageSource: headerImage.source + property alias titleText: title.text + property bool overlapping: true + property real initY + signal clicked() + + Image{ + id: headerImage + width: parent.width + height: overlapping ? parent.height * (4.0/3.0) : parent.height + fillMode: Image.Stretch + + MouseArea{ + anchors.fill: parent + onClicked: baseItem.clicked() + } + + Text { + id: title + anchors.centerIn: parent + font.bold: true + font.pixelSize: Constants.tutorial_header_font_size + layer.enabled: true + layer.effect: DropShadow { + verticalOffset: Utils.dp(3) + horizontalOffset: Utils.dp(3) + color: Constants.white + radius: Utils.dp(1) + samples: 3 + } + } + } +} diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialHow.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialHow.qml new file mode 100644 index 0000000..48de0fc --- /dev/null +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialHow.qml @@ -0,0 +1,620 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 + +TutorialContent { + id: baseItem + + signal firePush(var pSectionPage) + signal quitTutorialClicked() + + TutorialReaderMethodNfc { + id: readerMethodNfc + visible: false + onQuitTutorialClicked: baseItem.quitTutorialClicked() + } + + TutorialReaderMethodSacMobile { + id: readerMethodSacMobile + visible: false + onQuitTutorialClicked: baseItem.quitTutorialClicked() + } + + TutorialReaderMethodSacDesktop { + id: readerMethodSacDesktop + visible: false + onQuitTutorialClicked: baseItem.quitTutorialClicked() + } + + TutorialReaderMethodBluetooth { + id: readerMethodBluetooth + visible: false + onQuitTutorialClicked: baseItem.quitTutorialClicked() + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("How can I use the AusweisApp2 on my smartphone?") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + font.italic: true + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Image { + anchors.horizontalCenter: parent.horizontalCenter + source: "qrc:///images/tutorial/generated/how_questions_everywhere.svg" + width: parent.width * 0.9 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Column { + width: parent.width + spacing: Constants.component_spacing + + Item { + width: parent.width + height: noticeImage.height + + TutorialImage { + id: noticeImage + source: "qrc:///images/tutorial/hint.svg" + + width: parent.width * 0.2 + centerY: 0.5 + centerX: 0.2 + } + + Text { + width: parent.width * 0.6 + text: qsTr("Many Android devices can access the id card via the NFC interface.") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_font_size + font.bold: true + horizontalAlignment: Text.AlignLeft + wrapMode: Text.WordWrap + + x: (parent.width * 0.65) - (width / 2) + y: (parent.height * 0.5) - (height / 2) + } + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("You can find a list of compatible NFC-capable smartphones here:") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h2_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: "%1".arg(qsTr("https://www.ausweisapp.bund.de/mobile-geraete/")) + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + onLinkActivated: Qt.openUrlExternally(link) + } + + Image { + source: "qrc:///images/tutorial/generated/how_device_lineup.svg" + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("The AusweisApp2 offers the following options to access your id card:") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + font.italic: true + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Item { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width + height: methodNfcSection.height + + MouseArea { + anchors.fill: parent + onClicked: firePush(readerMethodNfc) + } + + Column { + id: methodNfcSection + width: parent.width + spacing: Constants.component_spacing + padding: Utils.dp(10) + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("Direct connection via NFC chip") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h2_font_size + font.bold: true + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + Rectangle { + anchors.horizontalCenter: parent.horizontalCenter + height: radius * 2 + width: radius * 2 + radius: numberOne.height + border.width: Utils.dp(3) + border.color: Constants.tutorial_blue + + Text { + id: numberOne + anchors.centerIn: parent + text: "1" + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h2_font_size + font.bold: true + horizontalAlignment: Text.AlignHCenter + } + } + + Image { + source: "qrc:///images/tutorial/generated/how_method_nfc.svg" + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.8 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.6 + text: qsTr("App on Android smartphone with NFC chip as card reader") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + Image { + source: "qrc:///images/tutorial/arrow_blue.svg" + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.2 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Item { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width + height: methodSacDesktopSection.height + + MouseArea { + anchors.fill: parent + onClicked: firePush(readerMethodSacDesktop) + } + + Column { + id: methodSacDesktopSection + width: parent.width + spacing: Constants.component_spacing + padding: Utils.dp(10) + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("Smartphone as card reader") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h2_font_size + font.bold: true + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + Rectangle { + anchors.horizontalCenter: parent.horizontalCenter + height: radius * 2 + width: radius * 2 + radius: numberTwo.height + border.width: Utils.dp(3) + border.color: Constants.tutorial_blue + + Text { + id: numberTwo + anchors.centerIn: parent + text: "2" + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h2_font_size + font.bold: true + horizontalAlignment: Text.AlignHCenter + } + } + + Item { + width: parent.width + height: sacStationaryImage.height + + Rectangle { + anchors.horizontalCenter: parent.horizontalCenter + height: radius * 2 + width: radius * 2 + radius: parent.width * 0.06 + border.width: Utils.dp(3) + border.color: Constants.tutorial_blue + + y: (parent.height * 0.4) - (height / 2) + x: (parent.width * 0.5) - (width / 2) + + TutorialImage { + source: "qrc:///images/tutorial/wifi.svg" + width: parent.width * 0.8 + + centerX: 0.5 + centerY: 0.5 + } + } + + TutorialImage { + id: sacStationaryImage + source: "qrc:///images/tutorial/generated/how_method_sac_desktop.svg" + width: parent.width + + centerX: 0.5 + centerY: 0.5 + } + + Text { + width: parent.width * 0.4 + text: qsTr("App on computer without NFC chip") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + + x: (parent.width * 0.25) - (width / 2) + y: (parent.height * 0.95) - (height / 2) + } + + Text { + width: parent.width * 0.4 + text: qsTr("Android smartphone with NFC chip as card reader") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + + x: (parent.width * 0.75) - (width / 2) + y: (parent.height * 0.95) - (height / 2) + } + } + + Image { + source: "qrc:///images/tutorial/arrow_blue.svg" + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.2 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Item { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width + height: methodSacMobileSection.height + + MouseArea { + anchors.fill: parent + onClicked: firePush(readerMethodSacMobile) + } + + Column { + id: methodSacMobileSection + width: parent.width + spacing: Constants.component_spacing + padding: Utils.dp(10) + + Rectangle { + anchors.horizontalCenter: parent.horizontalCenter + height: radius * 2 + width: radius * 2 + radius: numberTwo.height + border.width: Utils.dp(3) + border.color: Constants.tutorial_blue + + Text { + id: numberThree + anchors.centerIn: parent + text: "3" + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h2_font_size + font.bold: true + horizontalAlignment: Text.AlignHCenter + } + } + + Item { + width: parent.width + height: sacMobileImage.height + + Rectangle { + anchors.horizontalCenter: parent.horizontalCenter + height: radius * 2 + width: radius * 2 + radius: parent.width * 0.06 + border.width: Utils.dp(3) + border.color: Constants.tutorial_blue + + y: (parent.height * 0.4) - (height / 2) + x: (parent.width * 0.5) - (width / 2) + + TutorialImage { + source: "qrc:///images/tutorial/wifi.svg" + width: parent.width * 0.8 + + centerX: 0.5 + centerY: 0.5 + } + } + + TutorialImage { + id: sacMobileImage + source: "qrc:///images/tutorial/generated/how_method_sac_mobile.svg" + width: parent.width + + centerX: 0.5 + centerY: 0.5 + } + + Text { + width: parent.width * 0.4 + text: qsTr("App on tablet or smartphone without NFC chip") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + + x: (parent.width * 0.25) - (width / 2) + y: (parent.height * 0.95) - (height / 2) + } + + Text { + width: parent.width * 0.4 + text: qsTr("Android smartphone with NFC chip as card reader") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + + x: (parent.width * 0.75) - (width / 2) + y: (parent.height * 0.95) - (height / 2) + } + } + + Image { + source: "qrc:///images/tutorial/arrow_blue.svg" + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.2 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Item { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width + height: methodBluetoothSection.height + + MouseArea { + anchors.fill: parent + onClicked: firePush(readerMethodBluetooth) + } + + Column { + id: methodBluetoothSection + width: parent.width + spacing: Constants.component_spacing + padding: Utils.dp(10) + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("Using a bluetooth card reader") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h2_font_size + font.bold: true + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + Rectangle { + anchors.horizontalCenter: parent.horizontalCenter + height: radius * 2 + width: radius * 2 + radius: numberFour.height + border.width: Utils.dp(3) + border.color: Constants.tutorial_blue + + Text { + id: numberFour + anchors.centerIn: parent + text: "4" + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h2_font_size + font.bold: true + horizontalAlignment: Text.AlignHCenter + } + } + + Item { + width: parent.width + height: bluetoothImage.height + + Rectangle { + anchors.horizontalCenter: parent.horizontalCenter + height: radius * 2 + width: radius * 2 + radius: parent.width * 0.06 + border.width: Utils.dp(3) + border.color: Constants.tutorial_blue + + y: (parent.height * 0.4) - (height / 2) + x: (parent.width * 0.5) - (width / 2) + + TutorialImage { + source: "qrc:///images/tutorial/bluetooth.svg" + width: parent.width * 0.8 + + centerX: 0.5 + centerY: 0.5 + } + } + + TutorialImage { + id: bluetoothImage + source: "qrc:///images/tutorial/generated/how_method_bluetooth.svg" + width: parent.width + + centerX: 0.5 + centerY: 0.5 + } + + Text { + width: parent.width * 0.4 + text: qsTr("App on smartphone or tablet") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + + x: (parent.width * 0.25) - (width / 2) + y: (parent.height * 0.95) - (height / 2) + } + + Text { + width: parent.width * 0.4 + text: qsTr("Bluetooth card reader") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + + x: (parent.width * 0.75) - (width / 2) + y: (parent.height * 0.95) - (height / 2) + } + } + + Image { + source: "qrc:///images/tutorial/arrow_blue.svg" + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.2 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + } + } + + Rectangle { + property alias text: textContent.text + height: textContent.height + 2 * Constants.component_spacing + width: parent.width + color: Constants.tutorial_blue + + Text { + id: textContent + anchors.centerIn: parent + color: Constants.white + width: parent.width * 0.8 + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + text: qsTr("Another tip") + settingsModel.translationTrigger + } + } + + + Column { + width: parent.width + spacing: Constants.component_spacing + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("For lenghty forms, e.g. a BAf\u00F6G application, we recommend you to use the AusweisApp2 on a computer...") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h2_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + Item { + width: parent.width + height: formImage.height + + TutorialImage { + id: formImage + source: "qrc:///images/tutorial/generated/how_form_no_fun.svg" + width: parent.width * 0.6 + + centerX: 0.4 + centerY: 0.5 + } + + Text { + width: parent.width * 0.5 + text: qsTr("Filling long forms is no fun on a smartphone!") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignLeft + wrapMode: Text.WordWrap + color: Constants.tutorial_blue + + x: parent.width * 0.5 + y: (parent.height * 0.5) - (height / 2) + } + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("... and to use a smartphone to communicate with your ID card. A USB reader is of course also an alternative.") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h2_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + } + + Image { + anchors.horizontalCenter: parent.horizontalCenter + source: "qrc:///images/tutorial/generated/how_desktop.svg" + width: parent.width + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } +} diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialImage.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialImage.qml new file mode 100644 index 0000000..72b9a38 --- /dev/null +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialImage.qml @@ -0,0 +1,14 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 + +Image { + property real centerX: 0.5 + property real centerY: 0.5 + + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + + y: (parent.height * centerY) - (height / 2) + x: (parent.width * centerX) - (width / 2) +} diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialImportant.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialImportant.qml new file mode 100644 index 0000000..7a05388 --- /dev/null +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialImportant.qml @@ -0,0 +1,314 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.Type.NumberModel 1.0 +import Governikus.Type.ChangePinModel 1.0 + +TutorialContent { + id: baseItem + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: (settingsModel.language === "en" ? qsTr("Please exchange your") : qsTr("Before you use the online ID function please change the")) + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_important.svg" + } + + Column { + width: parent.width + spacing: Constants.component_spacing + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("5 digits long") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("transport PIN") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.bold: true + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + TutorialImage { + width: parent.width * 0.8 + source: "qrc:///images/tutorial/generated/important_pin5.svg" + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_important.svg" + } + + Column { + width: parent.width + spacing: Constants.component_spacing + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("with a personal") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("6 digits long PIN") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.bold: true + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + TutorialImage { + width: parent.width * 0.8 + source: "qrc:///images/tutorial/generated/important_pin6.svg" + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: (settingsModel.language === "en" ? qsTr("before you use the online ID function!") : qsTr("change!")) + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("The transport PIN is send to you by the Bundesdruckerei via mail.") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_important.svg" + } + + Column { + width: parent.width + spacing: Constants.component_spacing + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("Choose for this purpose the menu entry PIN management") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h2_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + Item { + height: parent.width * 0.6 + width: parent.width + + Text { + width: parent.width * 0.35 + text: qsTr("Later you can also change your personal PIN here") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_font_size + horizontalAlignment: Text.AlignLeft + wrapMode: Text.WordWrap + + x: (parent.width * 0.2) - (width / 2) + y: (parent.height * 0.5) - (height / 2) + } + + TutorialImage { + id: screenshot + source: qsTr("qrc:///images/tutorial/screenshot_pin_management_menu_en.png") + settingsModel.translationTrigger + z: 3 + + readonly property real rightX: x + width + + width: parent.height * 0.6 + centerX: 0.6 + centerY: 0.5 + } + + Image { + source: "qrc:///images/tutorial/zoom_triangle.svg" + z: 4 + + width: small_smartphone.centerXValue - screenshot.rightX + height: screenshot.height + x: screenshot.rightX + y: (parent.height * 0.5) - (height / 2) + } + + TutorialImage { + id: small_smartphone + source: "qrc:///images/tutorial/phone.svg" + z: 1 + + readonly property real centerXValue: x + (width / 2) + + width: parent.height * 0.2 + centerY: 0.5 + centerX: 0.9 + } + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("... or click this button to change your PIN right now:") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h2_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + GButton { + anchors.horizontalCenter: parent.horizontalCenter + anchors.margins: Constants.component_spacing + text: qsTr("Change PIN") + settingsModel.translationTrigger + onClicked: { + NumberModel.requestTransportPin = true + ChangePinModel.startWorkflow() + } + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_important.svg" + } + + Column { + width: parent.width + spacing: Constants.component_spacing + + Image { + anchors.horizontalCenter: parent.horizontalCenter + source: "qrc:///images/tutorial/play_movie.png" + width: parent.width * 0.7 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + + MouseArea { + anchors.fill: parent + + onClicked: Qt.openUrlExternally(qsTr("https://www.youtube.com/watch?v=wZglRda5Y60&index=4&list=PLLB5ERhVkn25qQXgMHQr-1KgyZsJKoSAm")) + } + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("Learn more about this in the YouTube video") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h2_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + Item { + width: parent.width + height: letsGoImage.height + + TutorialImage { + id: letsGoImage + width: parent.width + source: "qrc:///images/tutorial/generated/important_lets_go.svg" + + centerX: 0.5 + centerY: 0.5 + } + + Text { + width: parent.width + text: qsTr("Let's go") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + color: Constants.white + + x: (parent.width * 0.5) - (width / 2) + y: (parent.height * 0.6) - (height / 2) + } + } + + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("Do you still have questions?") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + Image { + source: "qrc:///images/tutorial/generated/important_space_questionmark.svg" + anchors.horizontalCenter: parent.horizontalCenter + + width: parent.width * 0.4 + height: width + fillMode: Image.PreserveAspectFit + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("You can read our FAQs or write to us...") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: "%1
%2
%3".arg(qsTr("www.ausweisapp.bund.de")).arg(qsTr("or")).arg(qsTr("www.personalausweisportal.de")) + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h2_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.Wrap + onLinkActivated: Qt.openUrlExternally(link) + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_important.svg" + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("You can always access this tutorial again from the side bar.") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } +} diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodBluetooth.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodBluetooth.qml new file mode 100644 index 0000000..2925613 --- /dev/null +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodBluetooth.qml @@ -0,0 +1,507 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 + +SectionPage { + id: baseItem + leftTitleBarAction: TitleBarAction { state: "back"; onClicked: firePop() } + headerTitleBarAction: TitleBarAction { text: qsTr("Tutorial: Bluetooth") + settingsModel.translationTrigger; font.bold: true } + + signal quitTutorialClicked() + + Rectangle { + anchors.fill: parent + color: Constants.white + z: -1 + } + + content: Item { + width: baseItem.width + height: content.contentHeight + + TutorialContent { + id: content + visible: true + width: Constants.is_tablet ? baseItem.width * 0.5 : baseItem.width + height: content.contentHeight + anchors.horizontalCenter: parent.horizontalCenter + + + Column { + width: parent.width + spacing: Constants.component_spacing + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("Using a bluetooth card reader") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h2_font_size + font.bold: true + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + Rectangle { + anchors.horizontalCenter: parent.horizontalCenter + height: radius * 2 + width: radius * 2 + radius: numberFour.height + border.width: Utils.dp(3) + border.color: Constants.tutorial_blue + + Text { + id: numberFour + anchors.centerIn: parent + text: "4" + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h2_font_size + font.bold: true + horizontalAlignment: Text.AlignHCenter + } + } + + Item { + width: parent.width + height: bluetoothImage.height + + Rectangle { + anchors.horizontalCenter: parent.horizontalCenter + height: radius * 2 + width: radius * 2 + radius: parent.width * 0.06 + border.width: Utils.dp(3) + border.color: Constants.tutorial_blue + + y: (parent.height * 0.4) - (height / 2) + x: (parent.width * 0.5) - (width / 2) + + TutorialImage { + source: "qrc:///images/tutorial/bluetooth.svg" + width: parent.width * 0.8 + + centerX: 0.5 + centerY: 0.5 + } + } + + TutorialImage { + id: bluetoothImage + source: "qrc:///images/tutorial/generated/how_method_bluetooth.svg" + width: parent.width + + centerX: 0.5 + centerY: 0.5 + } + + Text { + width: parent.width * 0.4 + text: qsTr("App on smartphone or tablet") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + + x: (parent.width * 0.25) - (width / 2) + y: (parent.height * 0.95) - (height / 2) + } + + Text { + width: parent.width * 0.4 + text: qsTr("Bluetooth card reader") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + + x: (parent.width * 0.75) - (width / 2) + y: (parent.height * 0.95) - (height / 2) + } + + } + } + + Image { + source: "qrc:///images/tutorial/arrow_blue.svg" + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.2 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + rotation: 90 + } + + Column { + width: parent.width + spacing: Constants.component_spacing + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.8 + text: qsTr("You need a suitable card reader if you want to use the bluetooth connection.") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Constants.tutorial_content_header_h1_font_size + font.family: "Noto Serif" + wrapMode: Text.WordWrap + } + + Image { + anchors.horizontalCenter: parent.horizontalCenter + source: "qrc:///images/tutorial/generated/reader_bluetooth_connection.svg" + width: parent.width * 0.9 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + + Item { + width: parent.width + height: Math.max(leftText.height, rightText.height) + + Text { + id: leftText + width: parent.width * 0.4 + anchors.left: parent.left + leftPadding: Utils.dp(30) + text: qsTr("Set the card reader visible first...") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Constants.tutorial_content_header_h2_font_size + font.family: "Noto Serif" + wrapMode: Text.WordWrap + } + + Text { + id: rightText + width: parent.width * 0.4 + anchors.right: parent.right + rightPadding: Utils.dp(30) + text: qsTr("... and then pair it with your device.") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Constants.tutorial_content_header_h2_font_size + font.family: "Noto Serif" + wrapMode: Text.WordWrap + } + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Column { + width: parent.width + + Image { + anchors.horizontalCenter: parent.horizontalCenter + source: "qrc:///images/tutorial/generated/reader_sac_no_nfc_provider.svg" + width: parent.width * 0.5 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.6 + text: qsTr("Click the link on the website of the service provider.") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Constants.tutorial_content_header_h2_font_size + font.family: "Noto Serif" + wrapMode: Text.WordWrap + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Item{ + width: parent.width + height: userdataExample.height + textAccessWhoWhat.height + textOpenAutomatic.height + 3 * Constants.component_spacing + + TutorialImage { + source: "qrc:///images/tutorial/tablet-no-nfc.svg" + width: parent.width * 0.15 + + centerX: 0.2 + centerY: 0.2 + } + + TutorialImage { + id: userdataExample + source: qsTr("qrc:///images/tutorial/generated/reader_nfc_userdata_example_en.svg") + settingsModel.translationTrigger + width: parent.width * 0.8 + + centerX: 0.5 + centerY: 0.75 + } + + Text { + id: textOpenAutomatic + width: parent.width * 0.6 + text: qsTr("The App opens automatically.") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Constants.tutorial_content_font_size + font.family: "Noto Serif" + wrapMode: Text.WordWrap + + x: (parent.width * 0.7) - (width / 2) + y: (parent.height * 0) - (height / 2) + } + + Text { + id: textAccessWhoWhat + width: parent.width * 0.6 + text: qsTr("The AusweisApp2 will display who wants to access which data.") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Constants.tutorial_content_header_h2_font_size + font.family: "Noto Serif" + wrapMode: Text.WordWrap + + x: (parent.width * 0.7) - (width / 2) + y: (parent.height * 0.2) - (height / 2) + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Column { + width: parent.width + spacing: Constants.component_spacing + + Text { + id: startProcessText + anchors.horizontalCenter: parent.horizontalCenter + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h2_font_size + text: qsTr("Start the process with a click on:") + settingsModel.translationTrigger + } + + Row { + anchors.horizontalCenter: parent.horizontalCenter + height: identifyArrow.height + spacing: Constants.component_spacing + + Image { + id: identifyArrow + source: "qrc:///images/tutorial/arrow_blue.svg" + width: parent.width * 0.2 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + + GButton { + id: identifyButton + iconSource: "qrc:///images/npa.svg" + text: qsTr("Identify now") + settingsModel.translationTrigger; + } + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Column { + width: parent.width + spacing: Constants.component_spacing + + Item { + width: parent.width + height: screenshotIdentify.height + + TutorialImage { + id: screenshotIdentify + source: qsTr("qrc:///images/tutorial/screenshot_choose_reader_en.png") + settingsModel.translationTrigger + width: parent.width * 0.5 + + centerX: 0.5 + centerY: 0.4 + } + + TutorialImage { + id: pointerImage + source: "qrc:///images/tutorial/hand.svg" + width: parent.width * 0.1 + centerX: 0.53 + centerY: 0.87 + } + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("Tap on Bluetooth") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Constants.tutorial_content_header_h1_font_size + font.family: "Noto Serif" + wrapMode: Text.WordWrap + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Column { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width + spacing: Constants.component_spacing + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("Insert card into card reader") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Constants.tutorial_content_header_h1_font_size + font.family: "Noto Serif" + wrapMode: Text.WordWrap + } + + Image { + anchors.horizontalCenter: parent.horizontalCenter + source: "qrc:///images/tutorial/generated/reader_bluetooth_card_inserted.svg" + width: parent.width * 0.3 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("... and confirm the displayed information.") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Constants.tutorial_content_header_h1_font_size + font.family: "Noto Serif" + wrapMode: Text.WordWrap + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Column { + width: parent.width + spacing: Constants.component_spacing + + Item { + width: parent.width + height: pin6Image.height + Constants.component_spacing * 2 + + Text { + width: parent.width * 0.8 + text: (settingsModel.language === "en" ? qsTr("Enter") : qsTr("Now")) + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + + x: (parent.width * 0.5) - (width / 2) + y: (parent.height * 0.05) - (height / 2) + } + + TutorialImage { + id: pin6Image + source: "qrc:///images/tutorial/generated/reader_nfc_pin6.svg" + z: 1 + + width: parent.width * 0.8 + centerY: 0.5 + centerX: 0.5 + } + + Text { + width: parent.width + text: qsTr("6 digits long PIN") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.bold: true + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + z: 2 + + x: (parent.width * 0.5) - (width / 2) + y: (parent.height * 0.2) - (height / 2) + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.8 + text: (settingsModel.language === "en" ? qsTr("now on the card reader!") : qsTr("enter on the card reader!")) + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + + x: (parent.width * 0.5) - (width / 2) + y: (parent.height * 0.9) - (height / 2) + } + } + + Item { + width: parent.width + height: Math.max(noticeImage2.height, noticeText.height) + + TutorialImage { + id: noticeImage2 + source: "qrc:///images/tutorial/hint.svg" + + width: parent.width * 0.2 + centerY: 0.5 + centerX: 0.2 + } + + Text { + id: noticeText + width: parent.width * 0.6 + text: qsTr("This is only possible if you have exchanged the 5 digits long transport PIN with a 6 digits long personal PIN beforehand.") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_font_size + font.bold: true + horizontalAlignment: Text.AlignLeft + wrapMode: Text.WordWrap + + x: (parent.width * 0.65) - (width / 2) + y: (parent.height * 0.5) - (height / 2) + } + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + MouseArea { + width: parent.width + height: finishedButton.height + + Image { + id: finishedButton + source: "qrc:///images/tutorial/generated/reader_nfc_finished.svg" + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.8 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + + onClicked: firePop() + } + + Item { + id: footerSpacer + width: parent.width + height: footer.height + } + } + } + + TutorialReaderMethodFooter { + id: footer + width: baseItem.width + + onMenuClicked: firePop() + onQuitTutorialClicked: { + firePop() + baseItem.quitTutorialClicked() + } + } +} + diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodFooter.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodFooter.qml new file mode 100644 index 0000000..93825d0 --- /dev/null +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodFooter.qml @@ -0,0 +1,13 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 + +TutorialFooter { + id: footer + width: baseItem.width + anchors.horizontalCenter: parent.horizontalCenter + color: Constants.tutorial_blue + anchors.bottom: parent.bottom + + backText: qsTr("Back") + settingsModel.translationTrigger +} diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodNfc.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodNfc.qml new file mode 100644 index 0000000..85b3357 --- /dev/null +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodNfc.qml @@ -0,0 +1,435 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 + +SectionPage { + id: baseItem + leftTitleBarAction: TitleBarAction { state: "back"; onClicked: firePop() } + headerTitleBarAction: TitleBarAction { text: qsTr("Tutorial: NFC") + settingsModel.translationTrigger; font.bold: true } + + signal quitTutorialClicked() + + Rectangle { + anchors.fill: parent + color: Constants.white + z: -1 + } + + content: Item { + width: baseItem.width + height: content.contentHeight + + TutorialContent { + id: content + visible: true + width: Constants.is_tablet ? baseItem.width * 0.5 : baseItem.width + height: content.contentHeight + anchors.horizontalCenter: parent.horizontalCenter + + Column { + width: parent.width + spacing: Constants.component_spacing + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("Direct connection via NFC chip") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h2_font_size + font.bold: true + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + Rectangle { + anchors.horizontalCenter: parent.horizontalCenter + height: radius * 2 + width: radius * 2 + radius: numberOne.height + border.width: Utils.dp(3) + border.color: Constants.tutorial_blue + + Text { + id: numberOne + anchors.centerIn: parent + text: "1" + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h2_font_size + font.bold: true + horizontalAlignment: Text.AlignHCenter + } + } + + Image { + source: "qrc:///images/tutorial/generated/how_method_nfc.svg" + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.8 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.6 + text: qsTr("App on Android smartphone with NFC chip as card reader") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + } + + Image { + source: "qrc:///images/tutorial/arrow_blue.svg" + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.2 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + rotation: 90 + } + + Item { + width: parent.width + height: providerOnSmartphone.height + + TutorialImage { + id: providerOnSmartphone + source: "qrc:///images/tutorial/generated/reader_nfc_provider_on_smartphone.svg" + width: parent.width + } + + Text { + width: parent.width * 0.6 + text: qsTr("Click link on the website of the service provider.") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Constants.tutorial_content_header_h2_font_size + font.family: "Noto Serif" + wrapMode: Text.WordWrap + + x: (parent.width * 0.5) - (width / 2) + y: (parent.height * 0.9) - (height / 2) + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Item{ + width: parent.width + height: userdataExample.height + textAccessWhoWhat.height + textOpenAutomatic.height + 3 * Constants.component_spacing + + TutorialImage { + source: "qrc:///images/tutorial/generated/reader_nfc_npa_on_smartphone.svg" + width: parent.width * 0.3 + + centerX: 0.2 + centerY: 0.2 + } + + TutorialImage { + id: userdataExample + source: qsTr("qrc:///images/tutorial/generated/reader_nfc_userdata_example_en.svg") + settingsModel.translationTrigger + width: parent.width * 0.8 + + centerX: 0.5 + centerY: 0.75 + } + + Text { + id: textOpenAutomatic + width: parent.width * 0.6 + text: qsTr("The App opens automatically.") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Constants.tutorial_content_font_size + font.family: "Noto Serif" + wrapMode: Text.WordWrap + + x: (parent.width * 0.7) - (width / 2) + y: (parent.height * 0) - (height / 2) + } + + Text { + id: textAccessWhoWhat + width: parent.width * 0.6 + text: qsTr("The AusweisApp2 will display who wants to access which data.") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Constants.tutorial_content_header_h2_font_size + font.family: "Noto Serif" + wrapMode: Text.WordWrap + + x: (parent.width * 0.7) - (width / 2) + y: (parent.height * 0.2) - (height / 2) + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Column { + width: parent.width + spacing: Constants.component_spacing + + Text { + id: startProcessText + anchors.horizontalCenter: parent.horizontalCenter + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h2_font_size + text: qsTr("Start the process with a click on:") + settingsModel.translationTrigger + } + + Row { + anchors.horizontalCenter: parent.horizontalCenter + height: identifyArrow.height + spacing: Constants.component_spacing + + Image { + id: identifyArrow + source: "qrc:///images/tutorial/arrow_blue.svg" + width: parent.width * 0.2 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + + GButton { + id: identifyButton + iconSource: "qrc:///images/npa.svg" + text: qsTr("Identify now") + settingsModel.translationTrigger; + } + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Column { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width + spacing: Constants.component_spacing + + Image { + anchors.horizontalCenter: parent.horizontalCenter + source: "qrc:///images/tutorial/generated/where_lay_down_id.svg" + width: parent.width * 0.8 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("... and place the id card flat onto the NFC interface.") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Constants.tutorial_content_header_h2_font_size + font.family: "Noto Serif" + wrapMode: Text.WordWrap + } + + Item { + width: parent.width + height: noticeImage.height + + TutorialImage { + id: noticeImage + source: "qrc:///images/tutorial/hint.svg" + + width: parent.width * 0.2 + centerY: 0.5 + centerX: 0.2 + } + + Text { + width: parent.width * 0.6 + text: qsTr("Do not move device or id card!") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_font_size + font.bold: true + horizontalAlignment: Text.AlignLeft + wrapMode: Text.WordWrap + + x: (parent.width * 0.65) - (width / 2) + y: (parent.height * 0.5) - (height / 2) + } + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Item { + width: parent.width + height: nfcPosition.height + 2 * Constants.component_spacing + + TutorialImage { + id: nfcPosition + source: "qrc:///images/tutorial/generated/reader_nfc_smartphone_nfc_position.svg" + + width: parent.width * 0.7 + centerY: 0.5 + centerX: 0.35 + } + + Text { + width: parent.width * 0.5 + text: qsTr("The correct position is specific for your device...") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_font_size + font.bold: true + horizontalAlignment: Text.AlignLeft + wrapMode: Text.WordWrap + + x: (parent.width * 0.75) - (width / 2) + y: (parent.height * 0.4) - (height / 2) + } + + Text { + width: parent.width * 0.5 + text: "%2".arg(qsTr("https://www.ausweisapp.bund.de/mobile-geraete/")).arg(qsTr("To mobile devices")) + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h2_font_size + font.bold: true + font.underline: true + horizontalAlignment: Text.AlignLeft + wrapMode: Text.WordWrap + onLinkActivated: Qt.openUrlExternally(link) + + x: (parent.width * 0.75) - (width / 2) + y: (parent.height * 0.7) - (height / 2) + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Column { + width: parent.width + spacing: Constants.component_spacing + + Item { + width: parent.width + height: pin6Image.height + Constants.component_spacing * 2 + + Text { + width: parent.width * 0.8 + text: (settingsModel.language === "en" ? qsTr("Enter") : qsTr("Now")) + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + + x: (parent.width * 0.5) - (width / 2) + y: (parent.height * 0.05) - (height / 2) + } + + TutorialImage { + id: pin6Image + source: "qrc:///images/tutorial/generated/reader_nfc_pin6.svg" + z: 1 + + width: parent.width * 0.8 + centerY: 0.5 + centerX: 0.5 + } + + Text { + width: parent.width + text: qsTr("6 digits long PIN") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.bold: true + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + z: 2 + + x: (parent.width * 0.5) - (width / 2) + y: (parent.height * 0.2) - (height / 2) + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.8 + text: (settingsModel.language === "en" ? qsTr("now!") : qsTr("enter!")) + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + + x: (parent.width * 0.5) - (width / 2) + y: (parent.height * 0.9) - (height / 2) + } + } + + Item { + width: parent.width + height: Math.max(noticeImage2.height, noticeText2.height) + + TutorialImage { + id: noticeImage2 + source: "qrc:///images/tutorial/hint.svg" + + width: parent.width * 0.2 + centerY: 0.5 + centerX: 0.2 + } + + Text { + id: noticeText2 + width: parent.width * 0.6 + text: qsTr("This is only possible if you have exchanged the 5 digits long transport PIN with a 6 digits long personal PIN beforehand.") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_font_size + font.bold: true + horizontalAlignment: Text.AlignLeft + wrapMode: Text.WordWrap + + x: (parent.width * 0.65) - (width / 2) + y: (parent.height * 0.5) - (height / 2) + } + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + MouseArea { + width: parent.width + height: finishedButton.height + + Image { + id: finishedButton + source: "qrc:///images/tutorial/generated/reader_nfc_finished.svg" + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.8 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + + onClicked: firePop() + } + + Item { + id: footerSpacer + width: parent.width + height: footer.height + } + } + } + + TutorialReaderMethodFooter { + id: footer + width: baseItem.width + + onMenuClicked: firePop() + onQuitTutorialClicked: { + firePop() + baseItem.quitTutorialClicked() + } + } +} diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodSacDesktop.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodSacDesktop.qml new file mode 100644 index 0000000..65d2f59 --- /dev/null +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodSacDesktop.qml @@ -0,0 +1,751 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 + +SectionPage { + id: baseItem + leftTitleBarAction: TitleBarAction { state: "back"; onClicked: firePop() } + headerTitleBarAction: TitleBarAction { text: qsTr("Tutorial: Smartphone as card reader") + settingsModel.translationTrigger; font.bold: true } + + signal quitTutorialClicked() + + Rectangle { + anchors.fill: parent + color: Constants.white + z: -1 + } + + content: Item { + width: baseItem.width + height: content.contentHeight + + TutorialContent { + id: content + visible: true + width: Constants.is_tablet ? baseItem.width * 0.5 : baseItem.width + height: content.contentHeight + anchors.horizontalCenter: parent.horizontalCenter + + Column { + width: parent.width + spacing: Constants.component_spacing + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("Smartphone as card reader") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h2_font_size + font.bold: true + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + Rectangle { + anchors.horizontalCenter: parent.horizontalCenter + height: radius * 2 + width: radius * 2 + radius: numberTwo.height + border.width: Utils.dp(3) + border.color: Constants.tutorial_blue + + Text { + id: numberTwo + anchors.centerIn: parent + text: "2" + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h2_font_size + font.bold: true + horizontalAlignment: Text.AlignHCenter + } + } + + Item { + width: parent.width + height: sacStationaryImage.height + + Rectangle { + anchors.horizontalCenter: parent.horizontalCenter + height: radius * 2 + width: radius * 2 + radius: parent.width * 0.06 + border.width: Utils.dp(3) + border.color: Constants.tutorial_blue + + y: (parent.height * 0.4) - (height / 2) + x: (parent.width * 0.5) - (width / 2) + + TutorialImage { + source: "qrc:///images/tutorial/wifi.svg" + width: parent.width * 0.8 + + centerX: 0.5 + centerY: 0.5 + } + } + + TutorialImage { + id: sacStationaryImage + source: "qrc:///images/tutorial/generated/how_method_sac_desktop.svg" + width: parent.width + + centerX: 0.5 + centerY: 0.5 + } + + Text { + width: parent.width * 0.4 + text: qsTr("App on computer without NFC chip") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + + x: (parent.width * 0.25) - (width / 2) + y: (parent.height * 0.95) - (height / 2) + } + + Text { + width: parent.width * 0.4 + text: qsTr("Android smartphone with NFC chip as card reader") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + + x: (parent.width * 0.75) - (width / 2) + y: (parent.height * 0.95) - (height / 2) + } + } + } + + Image { + source: "qrc:///images/tutorial/arrow_blue.svg" + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.2 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + rotation: 90 + } + + Column { + width: parent.width + spacing: Constants.component_spacing + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("Install AusweisApp2 on both your computer and your android smartphone with NFC ability.") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + Image { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.8 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + source: "qrc:///images/tutorial/generated/reader_sac_aa2_ok.svg" + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Column { + width: parent.width + spacing: Constants.component_spacing + + Image { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.3 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + source: "qrc:///images/tutorial/hint.svg" + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("Both devices have to be connected to the same wifi network") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + font.bold: true + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + Image { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.3 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + source: "qrc:///images/tutorial/wifi.svg" + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Column { + width: parent.width + spacing: Constants.component_spacing + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("Now enter \"Smartphone as card reader\" in the app on your android smartphone...") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + Image { + source: qsTr("qrc:///images/tutorial/generated/reader_sac_menu_en.svg") + settingsModel.translationTrigger + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Column { + width: parent.width + spacing: Constants.component_spacing + + Text { + anchors.horizontalCenter: parent.horizontalCenter + text: qsTr("Now") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignRight + wrapMode: Text.WordWrap + } + + GButton { + id: remoteButton + buttonColor: "green" + anchors.horizontalCenter: parent.horizontalCenter + text: qsTr("Start remote service") + settingsModel.translationTrigger + } + + Image { + source: "qrc:///images/tutorial/up_icon.svg" + anchors.horizontalCenter: parent.horizontalCenter + rotation: 180 + width: parent.width * 0.1 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + text: qsTr("Next") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignRight + wrapMode: Text.WordWrap + } + + GButton { + id: pairingButton + anchors.horizontalCenter: parent.horizontalCenter + text: qsTr("Start pairing") + settingsModel.translationTrigger + } + + Image { + anchors.horizontalCenter: parent.horizontalCenter + source: "qrc:///images/tutorial/up_icon.svg" + rotation: 180 + width: parent.width * 0.1 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + + Item { + anchors.horizontalCenter: parent.horizontalCenter + width: pairingCodeText.width + height: greyBackgroundRect.height + + Rectangle { + id: greyBackgroundRect + anchors.horizontalCenter: parent.horizontalCenter + width: pairingCodeText.width - Utils.dp(40) + height: width + color: Constants.tutorial_very_light_grey + } + + Text { + id: pairingCodeText + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: greyBackgroundRect.top + topPadding: Utils.dp(30) + text: qsTr("Pairing code") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + font.bold: true + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + Text { + id: appearingText + anchors.bottom: greyBackgroundRect.bottom + bottomPadding: Utils.dp(30) + anchors.horizontalCenter: parent.horizontalCenter + text: qsTr("appears!") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Column { + width: parent.width + spacing: Constants.component_spacing + + Image { + anchors.horizontalCenter: parent.horizontalCenter + source: "qrc:///images/tutorial/generated/reader_sac_npa_on_laptop.svg" + width: parent.width * 0.3 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.8 + text: qsTr("Start the App now on your computer and enter the settings.") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.8 + text: qsTr("Select the Card Readers tab.") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Column { + width: parent.width + spacing: Constants.component_spacing + + Item { + width: parent.width + height: desktopPairing.height + + TutorialImage { + id: desktopPairing + source: qsTr("qrc:///images/tutorial/screenshot_pairing_en.png") + settingsModel.translationTrigger + + width: parent.width * 0.6 + } + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.8 + text: qsTr("Select smartphone from list and click \"pair\"") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + Item { + width: parent.width + height: noticeImage3.height + + TutorialImage { + id: noticeImage3 + source: "qrc:///images/tutorial/hint.svg" + + width: parent.width * 0.2 + centerY: 0.5 + centerX: 0.2 + } + + Text { + width: parent.width * 0.6 + text: qsTr("Enter pairing code next.") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_font_size + font.bold: true + horizontalAlignment: Text.AlignLeft + wrapMode: Text.WordWrap + + x: (parent.width * 0.65) - (width / 2) + y: (parent.height * 0.5) - (height / 2) + } + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Column { + width: parent.width + + Image { + anchors.horizontalCenter: parent.horizontalCenter + source: "qrc:///images/tutorial/generated/reader_sac_provider_on_laptop.svg" + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + width: parent.width * 0.5 + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.6 + text: qsTr("Click link on the website of the service provider.") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Constants.tutorial_content_header_h2_font_size + font.family: "Noto Serif" + wrapMode: Text.WordWrap + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Item{ + width: parent.width + height: userdataExample.height + textAccessWhoWhat.height + textOpenAutomatic.height + 3 * Constants.component_spacing + + TutorialImage { + source: "qrc:///images/tutorial/generated/reader_sac_npa_on_laptop.svg" + width: parent.width * 0.3 + + centerX: 0.2 + centerY: 0.2 + } + + TutorialImage { + id: userdataExample + source: qsTr("qrc:///images/tutorial/generated/reader_nfc_userdata_example_en.svg") + settingsModel.translationTrigger + width: parent.width * 0.8 + + centerX: 0.5 + centerY: 0.75 + } + + Text { + id: textOpenAutomatic + width: parent.width * 0.6 + text: qsTr("The App opens automatically.") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Constants.tutorial_content_font_size + font.family: "Noto Serif" + wrapMode: Text.WordWrap + + x: (parent.width * 0.7) - (width / 2) + y: (parent.height * 0) - (height / 2) + } + + Text { + id: textAccessWhoWhat + width: parent.width * 0.6 + text: qsTr("The AusweisApp2 will display who wants to access which data.") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Constants.tutorial_content_header_h2_font_size + font.family: "Noto Serif" + wrapMode: Text.WordWrap + + x: (parent.width * 0.7) - (width / 2) + y: (parent.height * 0.2) - (height / 2) + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Column { + width: parent.width + spacing: Constants.component_spacing + + Text { + id: startProcessText + anchors.horizontalCenter: parent.horizontalCenter + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h2_font_size + text: qsTr("Start the process with a click on:") + settingsModel.translationTrigger + } + + Row { + anchors.horizontalCenter: parent.horizontalCenter + height: identifyArrow.height + spacing: Constants.component_spacing + + Image { + id: identifyArrow + source: "qrc:///images/tutorial/arrow_blue.svg" + width: parent.width * 0.2 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + + GButton { + id: identifyButton + iconSource: "qrc:///images/npa.svg" + text: qsTr("Identify now") + settingsModel.translationTrigger; + } + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Column { + width: parent.width + spacing: Constants.component_spacing + + Image { + anchors.horizontalCenter: parent.horizontalCenter + source: qsTr( "qrc:///images/tutorial/generated/where_lay_down_id.svg") + settingsModel.translationTrigger + width: parent.width * 0.8 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("... and place the id card flat onto the NFC interface.") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Constants.tutorial_content_header_h2_font_size + font.family: "Noto Serif" + wrapMode: Text.WordWrap + } + + Item { + width: parent.width + height: noticeImage.height + + TutorialImage { + id: noticeImage + source: "qrc:///images/tutorial/hint.svg" + + width: parent.width * 0.2 + centerY: 0.5 + centerX: 0.2 + } + + Text { + width: parent.width * 0.6 + text: qsTr("Do not move device or id card!") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_font_size + font.bold: true + horizontalAlignment: Text.AlignLeft + wrapMode: Text.WordWrap + + x: (parent.width * 0.65) - (width / 2) + y: (parent.height * 0.5) - (height / 2) + } + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Item { + width: parent.width + height: nfcPosition.height + 2 * Constants.component_spacing + + TutorialImage { + id: nfcPosition + source: "qrc:///images/tutorial/generated/reader_nfc_smartphone_nfc_position.svg" + + width: parent.width * 0.7 + centerY: 0.5 + centerX: 0.35 + } + + Text { + width: parent.width * 0.5 + text: qsTr("The correct position is specific for your device...") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_font_size + font.bold: true + horizontalAlignment: Text.AlignLeft + wrapMode: Text.WordWrap + + x: (parent.width * 0.75) - (width / 2) + y: (parent.height * 0.4) - (height / 2) + } + + Text { + width: parent.width * 0.5 + text: "%2".arg(qsTr("https://www.ausweisapp.bund.de/mobile-geraete/")).arg(qsTr("To mobile devices")) + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h2_font_size + font.bold: true + font.underline: true + horizontalAlignment: Text.AlignLeft + wrapMode: Text.WordWrap + onLinkActivated: Qt.openUrlExternally(link) + + x: (parent.width * 0.75) - (width / 2) + y: (parent.height * 0.7) - (height / 2) + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Column { + width: parent.width + spacing: Constants.component_spacing + + Item { + width: parent.width + height: pin6Image.height + Constants.component_spacing * 2 + + Text { + width: parent.width * 0.8 + text: (settingsModel.language === "en" ? qsTr("Enter") : qsTr("Now")) + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + + x: (parent.width * 0.5) - (width / 2) + y: (parent.height * 0.05) - (height / 2) + } + + TutorialImage { + id: pin6Image + source: "qrc:///images/tutorial/generated/reader_nfc_pin6.svg" + z: 1 + + width: parent.width * 0.8 + centerY: 0.5 + centerX: 0.5 + } + + Text { + width: parent.width + text: qsTr("6 digits long PIN") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.bold: true + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + z: 2 + + x: (parent.width * 0.5) - (width / 2) + y: (parent.height * 0.2) - (height / 2) + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.8 + text: (settingsModel.language === "en" ? qsTr("now!") : qsTr("enter!")) + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + + x: (parent.width * 0.5) - (width / 2) + y: (parent.height * 0.9) - (height / 2) + } + } + + Item { + width: parent.width + height: Math.max(noticeImage2.height, noticeText2.height) + + TutorialImage { + id: noticeImage2 + source: "qrc:///images/tutorial/hint.svg" + + width: parent.width * 0.2 + centerY: 0.5 + centerX: 0.2 + } + + Text { + id: noticeText2 + width: parent.width * 0.6 + text: qsTr("This is only possible if you have exchanged the 5 digits long transport PIN with a 6 digits long personal PIN beforehand.") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_font_size + font.bold: true + horizontalAlignment: Text.AlignLeft + wrapMode: Text.WordWrap + + x: (parent.width * 0.65) - (width / 2) + y: (parent.height * 0.5) - (height / 2) + } + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + MouseArea { + width: parent.width + height: finishedButton.height + + Image { + id: finishedButton + source: "qrc:///images/tutorial/generated/reader_nfc_finished.svg" + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.8 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + + onClicked: firePop() + } + + Item { + id: footerSpacer + width: parent.width + height: footer.height + } + } + } + + TutorialReaderMethodFooter { + id: footer + width: baseItem.width + + onMenuClicked: firePop() + onQuitTutorialClicked: { + firePop() + baseItem.quitTutorialClicked() + } + } +} diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodSacMobile.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodSacMobile.qml new file mode 100644 index 0000000..5af1f67 --- /dev/null +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodSacMobile.qml @@ -0,0 +1,792 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 + +SectionPage { + id: baseItem + leftTitleBarAction: TitleBarAction { state: "back"; onClicked: firePop() } + headerTitleBarAction: TitleBarAction { text: qsTr("Tutorial: Smartphone as card reader") + settingsModel.translationTrigger; font.bold: true } + + signal quitTutorialClicked() + + Rectangle { + anchors.fill: parent + color: Constants.white + z: -1 + } + + content: Item { + width: baseItem.width + height: content.contentHeight + + TutorialContent { + id: content + visible: true + width: Constants.is_tablet ? baseItem.width * 0.5 : baseItem.width + height: content.contentHeight + anchors.horizontalCenter: parent.horizontalCenter + + Column { + width: parent.width + spacing: Constants.component_spacing + + Rectangle { + anchors.horizontalCenter: parent.horizontalCenter + height: radius * 2 + width: radius * 2 + radius: numberTwo.height + border.width: Utils.dp(3) + border.color: Constants.tutorial_blue + + Text { + id: numberThree + anchors.centerIn: parent + text: "3" + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h2_font_size + font.bold: true + horizontalAlignment: Text.AlignHCenter + } + } + + Item { + width: parent.width + height: sacMobileImage.height + + Rectangle { + anchors.horizontalCenter: parent.horizontalCenter + height: radius * 2 + width: radius * 2 + radius: parent.width * 0.06 + border.width: Utils.dp(3) + border.color: Constants.tutorial_blue + + y: (parent.height * 0.4) - (height / 2) + x: (parent.width * 0.5) - (width / 2) + + TutorialImage { + source: "qrc:///images/tutorial/wifi.svg" + width: parent.width * 0.8 + + centerX: 0.5 + centerY: 0.5 + } + } + + TutorialImage { + id: sacMobileImage + source: "qrc:///images/tutorial/generated/how_method_sac_mobile.svg" + width: parent.width + + centerX: 0.5 + centerY: 0.5 + } + + Text { + width: parent.width * 0.4 + text: qsTr("App on tablet or smartphone without NFC chip") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + + x: (parent.width * 0.25) - (width / 2) + y: (parent.height * 0.95) - (height / 2) + } + + Text { + width: parent.width * 0.4 + text: qsTr("Android smartphone with NFC chip as card reader") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + + x: (parent.width * 0.75) - (width / 2) + y: (parent.height * 0.95) - (height / 2) + } + } + } + + Image { + source: "qrc:///images/tutorial/arrow_blue.svg" + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.2 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + rotation: 90 + } + + Column { + width: parent.width + spacing: Constants.component_spacing + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("Install AusweisApp2 on both your device without NFC and your android smartphone with NFC ability.") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + Image { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.8 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + source: "qrc:///images/tutorial/generated/reader_sac_aa2_ok.svg" + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Column { + width: parent.width + spacing: Constants.component_spacing + + Image { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.3 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + source: "qrc:///images/tutorial/hint.svg" + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("Both devices have to be connected to the same wifi network") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + font.bold: true + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + Image { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.3 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + source: "qrc:///images/tutorial/wifi.svg" + } + } + + Image { + source: "qrc:///images/tutorial/arrow_blue.svg" + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.2 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + rotation: 90 + } + + Column { + width: parent.width + spacing: Constants.component_spacing + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("Now enter \"Smartphone as card reader\" in the app on your android smartphone with...") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + Image { + source: qsTr("qrc:///images/tutorial/generated/reader_sac_menu_en.svg") + settingsModel.translationTrigger + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Column { + width: parent.width + spacing: Constants.component_spacing + + Text { + anchors.horizontalCenter: parent.horizontalCenter + text: qsTr("Now") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignRight + wrapMode: Text.WordWrap + } + + GButton { + id: remoteButton + buttonColor: "green" + anchors.horizontalCenter: parent.horizontalCenter + text: qsTr("Start remote service") + settingsModel.translationTrigger + } + + Image { + source: "qrc:///images/tutorial/up_icon.svg" + anchors.horizontalCenter: parent.horizontalCenter + rotation: 180 + width: parent.width * 0.1 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + text: qsTr("Next") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignRight + wrapMode: Text.WordWrap + } + + GButton { + id: pairingButton + anchors.horizontalCenter: parent.horizontalCenter + text: qsTr("Start pairing") + settingsModel.translationTrigger + } + + Image { + anchors.horizontalCenter: parent.horizontalCenter + source: "qrc:///images/tutorial/up_icon.svg" + rotation: 180 + width: parent.width * 0.1 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + + Item { + anchors.horizontalCenter: parent.horizontalCenter + width: pairingCodeText.width + height: greyBackgroundRect.height + + Rectangle { + id: greyBackgroundRect + anchors.horizontalCenter: parent.horizontalCenter + width: pairingCodeText.width - Utils.dp(40) + height: width + color: Constants.tutorial_very_light_grey + } + + Text { + id: pairingCodeText + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: greyBackgroundRect.top + topPadding: Utils.dp(30) + text: qsTr("Pairing code") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + font.bold: true + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + Text { + id: appearingText + anchors.bottom: greyBackgroundRect.bottom + bottomPadding: Utils.dp(30) + anchors.horizontalCenter: parent.horizontalCenter + text: qsTr("appears!") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + } + } + + Image { + source: "qrc:///images/tutorial/arrow_blue.svg" + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.2 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + rotation: 90 + } + + Column { + width: parent.width + spacing: Constants.component_spacing + + Image { + source: "qrc:///images/tutorial/generated/reader_sac_no_nfc_devices.svg" + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.5 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("Now open the app on your device without NFC and select Smartphone as card reader.") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("Now select Settings.") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Column { + width: parent.width + spacing: Constants.component_spacing + + Image { + source: "qrc:///images/tutorial/phone_list.svg" + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.5 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.7 + text: qsTr("Choose smartphone from list") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + Item { + width: parent.width + height: noticeImage4.height + + TutorialImage { + id: noticeImage4 + source: "qrc:///images/tutorial/hint.svg" + + width: parent.width * 0.2 + centerY: 0.5 + centerX: 0.2 + } + + Text { + width: parent.width * 0.6 + text: qsTr("Enter pairing code next.") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_font_size + font.bold: true + horizontalAlignment: Text.AlignLeft + wrapMode: Text.WordWrap + + x: (parent.width * 0.65) - (width / 2) + y: (parent.height * 0.5) - (height / 2) + } + } + } + + Image { + source: "qrc:///images/tutorial/arrow_blue.svg" + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.2 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + rotation: 90 + } + + Column { + width: parent.width + + Image { + anchors.horizontalCenter: parent.horizontalCenter + source: "qrc:///images/tutorial/generated/reader_sac_no_nfc_provider.svg" + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + width: parent.width * 0.5 + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.6 + text: qsTr("Click link on the website of the service provider on the device without NFC.") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Constants.tutorial_content_header_h2_font_size + font.family: "Noto Serif" + wrapMode: Text.WordWrap + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Item{ + width: parent.width + height: userdataExample.height + textAccessWhoWhat.height + textOpenAutomatic.height + 3 * Constants.component_spacing + + TutorialImage { + source: "qrc:///images/tutorial/tablet-no-nfc.svg" + width: parent.width * 0.15 + + centerX: 0.2 + centerY: 0.2 + } + + TutorialImage { + id: userdataExample + source: qsTr("qrc:///images/tutorial/generated/reader_nfc_userdata_example_en.svg") + settingsModel.translationTrigger + width: parent.width * 0.8 + + centerX: 0.5 + centerY: 0.75 + } + + Text { + id: textOpenAutomatic + width: parent.width * 0.6 + text: qsTr("The App opens automatically.") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Constants.tutorial_content_font_size + font.family: "Noto Serif" + wrapMode: Text.WordWrap + + x: (parent.width * 0.7) - (width / 2) + y: (parent.height * 0) - (height / 2) + } + + Text { + id: textAccessWhoWhat + width: parent.width * 0.6 + text: qsTr("The AusweisApp2 will display who wants to access which data.") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Constants.tutorial_content_header_h2_font_size + font.family: "Noto Serif" + wrapMode: Text.WordWrap + + x: (parent.width * 0.7) - (width / 2) + y: (parent.height * 0.2) - (height / 2) + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Column { + width: parent.width + spacing: Constants.component_spacing + + Text { + id: startProcessText + anchors.horizontalCenter: parent.horizontalCenter + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h2_font_size + text: qsTr("Start the process with a click on:") + settingsModel.translationTrigger + } + + Row { + anchors.horizontalCenter: parent.horizontalCenter + height: identifyArrow.height + spacing: Constants.component_spacing + + Image { + id: identifyArrow + source: "qrc:///images/tutorial/arrow_blue.svg" + width: parent.width * 0.2 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + + GButton { + id: identifyButton + iconSource: "qrc:///images/npa.svg" + text: qsTr("Identify now") + settingsModel.translationTrigger; + } + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Column { + width: parent.width + spacing: Constants.component_spacing + + Item { + width: parent.width + height: screenshotIdentify.height + + TutorialImage { + id: screenshotIdentify + source: qsTr("qrc:///images/tutorial/screenshot_choose_reader_en.png") + settingsModel.translationTrigger + width: parent.width * 0.5 + + centerX: 0.5 + centerY: 0.4 + } + + TutorialImage { + id: pointerImage + source: "qrc:///images/tutorial/hand.svg" + width: parent.width * 0.1 + centerX: 0.43 + centerY: 0.87 + } + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("Tap on Wifi") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Constants.tutorial_content_header_h1_font_size + font.family: "Noto Serif" + wrapMode: Text.WordWrap + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Column { + width: parent.width + spacing: Constants.component_spacing + + Image { + anchors.horizontalCenter: parent.horizontalCenter + source: qsTr( "qrc:///images/tutorial/generated/where_lay_down_id.svg") + settingsModel.translationTrigger + width: parent.width * 0.8 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("... and place the id card flat onto the NFC interface.") + settingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Constants.tutorial_content_header_h2_font_size + font.family: "Noto Serif" + wrapMode: Text.WordWrap + } + + Item { + width: parent.width + height: noticeImage.height + + TutorialImage { + id: noticeImage + source: "qrc:///images/tutorial/hint.svg" + + width: parent.width * 0.2 + centerY: 0.5 + centerX: 0.2 + } + + Text { + width: parent.width * 0.6 + text: qsTr("Do not move device or id card!") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_font_size + font.bold: true + horizontalAlignment: Text.AlignLeft + wrapMode: Text.WordWrap + + x: (parent.width * 0.65) - (width / 2) + y: (parent.height * 0.5) - (height / 2) + } + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Item { + width: parent.width + height: nfcPosition.height + 2 * Constants.component_spacing + + TutorialImage { + id: nfcPosition + source: "qrc:///images/tutorial/generated/reader_nfc_smartphone_nfc_position.svg" + + width: parent.width * 0.7 + centerY: 0.5 + centerX: 0.35 + } + + Text { + width: parent.width * 0.5 + text: qsTr("The correct position is specific for your device...") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_font_size + font.bold: true + horizontalAlignment: Text.AlignLeft + wrapMode: Text.WordWrap + + x: (parent.width * 0.75) - (width / 2) + y: (parent.height * 0.4) - (height / 2) + } + + Text { + width: parent.width * 0.5 + text: "%2".arg(qsTr("https://www.ausweisapp.bund.de/mobile-geraete/")).arg(qsTr("To mobile devices")) + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h2_font_size + font.bold: true + font.underline: true + horizontalAlignment: Text.AlignLeft + wrapMode: Text.WordWrap + onLinkActivated: Qt.openUrlExternally(link) + + x: (parent.width * 0.75) - (width / 2) + y: (parent.height * 0.7) - (height / 2) + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + Column { + width: parent.width + spacing: Constants.component_spacing + + Item { + width: parent.width + height: pin6Image.height + Constants.component_spacing * 2 + + Text { + width: parent.width * 0.8 + text: (settingsModel.language === "en" ? qsTr("Enter") : qsTr("Now")) + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + + x: (parent.width * 0.5) - (width / 2) + y: (parent.height * 0.05) - (height / 2) + } + + TutorialImage { + id: pin6Image + source: "qrc:///images/tutorial/generated/reader_nfc_pin6.svg" + z: 1 + + width: parent.width * 0.8 + centerY: 0.5 + centerX: 0.5 + } + + Text { + width: parent.width + text: qsTr("6 digits long PIN") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.bold: true + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + z: 2 + + x: (parent.width * 0.5) - (width / 2) + y: (parent.height * 0.2) - (height / 2) + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.8 + text: (settingsModel.language === "en" ? qsTr("now!") : qsTr("enter!")) + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + + x: (parent.width * 0.5) - (width / 2) + y: (parent.height * 0.9) - (height / 2) + } + } + + Item { + width: parent.width + height: Math.max(noticeImage2.height, noticeText2.height) + + TutorialImage { + id: noticeImage2 + source: "qrc:///images/tutorial/hint.svg" + + width: parent.width * 0.2 + centerY: 0.5 + centerX: 0.2 + } + + Text { + id: noticeText2 + width: parent.width * 0.6 + text: qsTr("This is only possible if you have exchanged the 5 digits long transport PIN with a 6 digits long personal PIN beforehand.") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_font_size + font.bold: true + horizontalAlignment: Text.AlignLeft + wrapMode: Text.WordWrap + + x: (parent.width * 0.65) - (width / 2) + y: (parent.height * 0.5) - (height / 2) + } + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_how.svg" + } + + MouseArea { + width: parent.width + height: finishedButton.height + + Image { + id: finishedButton + source: "qrc:///images/tutorial/generated/reader_nfc_finished.svg" + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.8 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + + onClicked: firePop() + } + + Item { + id: footerSpacer + width: parent.width + height: footer.height + } + } + } + + TutorialReaderMethodFooter { + id: footer + width: baseItem.width + + onMenuClicked: firePop() + onQuitTutorialClicked: { + firePop() + baseItem.quitTutorialClicked() + } + } +} diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialSeperator.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialSeperator.qml new file mode 100644 index 0000000..7a7c9ca --- /dev/null +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialSeperator.qml @@ -0,0 +1,17 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 + +Column { + width: parent.width + + property alias source: image.source + + TutorialImage { + id: image + width: parent.width * 0.03 + + centerX: 0.5 + centerY: 0.5 + } +} diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialSpacer.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialSpacer.qml new file mode 100644 index 0000000..bf5fb01 --- /dev/null +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialSpacer.qml @@ -0,0 +1,19 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 + +Rectangle { + property alias text: textContent.text + height: textContent.height + 2 * Constants.component_spacing + + Text { + id: textContent + anchors.centerIn: parent + color: Constants.white + width: parent.width * 0.8 + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } +} diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialView.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialView.qml new file mode 100644 index 0000000..cbcfd41 --- /dev/null +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialView.qml @@ -0,0 +1,298 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.2 +import QtQml.Models 2.10 +import QtQml 2.10 + +import Governikus.Global 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 + +SectionPage { + id: root + + property int lastYPosition: 0 + property var lastVisibleItem + property int contentWidth: Constants.is_tablet ? root.width * 0.5 : root.width + + leftTitleBarAction: TitleBarAction { state: !topLevelPage ? "back" : ""; onClicked: root.state = "" } + headerTitleBarAction: TitleBarAction { id: header; text: qsTr("Tutorial") + settingsModel.translationTrigger; font.bold: true } + + onVisibleChanged: { + if (visible) { + flickable.contentY = lastYPosition + navBar.lockedAndHidden = true + } else { + lastYPosition = flickable.contentY + } + } + + Component.onCompleted: { + if (visible) { + navBar.lockedAndHidden = true + } + } + + states: [ + State { + name: "what" + }, + State { + name: "where" + }, + State { + name: "how" + }, + State { + name: "important" + } + ] + + transitions: [ + Transition { + to: "what" + TutorialExpandAnimation { + targetContent: whatContent + targetHeader: whatHeader + } + TutorialCollapseAnimation { targetContent: whereContent } + TutorialCollapseAnimation { targetContent: howContent } + TutorialCollapseAnimation { targetContent: importantContent } + }, + Transition { + to: "where" + TutorialExpandAnimation { + targetContent: whereContent + targetHeader: whereHeader + } + TutorialCollapseAnimation { targetContent: whatContent } + TutorialCollapseAnimation { targetContent: howContent } + TutorialCollapseAnimation { targetContent: importantContent } + }, + Transition { + to: "how" + TutorialExpandAnimation { + targetContent: howContent + targetHeader: howHeader + } + TutorialCollapseAnimation { targetContent: whatContent } + TutorialCollapseAnimation { targetContent: whereContent } + TutorialCollapseAnimation { targetContent: importantContent } + }, + Transition { + to: "important" + TutorialExpandAnimation { + targetContent: importantContent + targetHeader: importantHeader + } + TutorialCollapseAnimation { targetContent: whatContent } + TutorialCollapseAnimation { targetContent: whereContent } + TutorialCollapseAnimation { targetContent: howContent } + }, + Transition { + to: "" + NumberAnimation { + target: flickable + property: "contentY" + easing.type: Easing.InOutQuad + to: 0 + duration: 500 + } + TutorialCollapseAnimation { targetContent: whatContent } + TutorialCollapseAnimation { targetContent: whereContent } + TutorialCollapseAnimation { targetContent: howContent } + TutorialCollapseAnimation { targetContent: importantContent } + } + ] + + function leaveView() { + flickable.contentY = 0 + state = "" + collapseAllAnimation.start() + navBar.lockedAndHidden = false + if (Constants.is_layout_ios){ + firePop() + } else { + navBar.state = "identify" + navBar.currentIndex = 0 + } + } + + SequentialAnimation { + id: collapseAllAnimation + + NumberAnimation { + target: flickable + property: "contentY" + easing.type: Easing.InOutQuad + to: 0 + duration: 500 + } + + PropertyAction { + target: whatContent + property: "visible" + value: false + } + + PropertyAction { + target: whereContent + property: "visible" + value: false + } + + PropertyAction { + target: howContent + property: "visible" + value: false + } + + PropertyAction { + target: importantContent + property: "visible" + value: false + } + } + + Rectangle { + anchors.fill: parent + color: Constants.white + } + + Flickable { + id: flickable + height: parent.height - footer.height + width: root.width + contentWidth: flickableContent.width + contentHeight: flickableContent.height + maximumFlickVelocity: Constants.scrolling_speed + flickableDirection: Flickable.VerticalFlick + + Item { + width: root.width + + Column { + id: flickableContent + width: root.width + anchors.horizontalCenter: parent.horizontalCenter + + + TutorialHeader { + id: whatHeader + width: root.width + height: (flickable.height / 13.0 ) * 3.0 + headerImageSource: "qrc:///images/tutorial/main_menu_what_caret.svg" + titleText: qsTr("What?") + settingsModel.translationTrigger + initY: 0 + z: 40 + + onClicked: { + if (!whatContent.visible){ + root.state = "what" + } else { + root.state = "" + } + } + } + + TutorialWhat { + id: whatContent + width: root.contentWidth + anchors.horizontalCenter: parent.horizontalCenter + } + + TutorialHeader { + id: whereHeader + width: root.width + height: (flickable.height / 13.0 ) * 3.0 + headerImageSource: "qrc:///images/tutorial/main_menu_where_caret.svg" + titleText: qsTr("Where?") + settingsModel.translationTrigger + initY: whatHeader.height + z: 30 + + onClicked: { + if (!whereContent.visible){ + root.state = "where" + } else { + root.state = "" + } + } + } + + TutorialWhere { + id: whereContent + width: root.contentWidth + anchors.horizontalCenter: parent.horizontalCenter + } + + TutorialHeader { + id: howHeader + width: root.width + height: (flickable.height / 13.0 ) * 3.0 + headerImageSource: "qrc:///images/tutorial/main_menu_how_caret.svg" + titleText: qsTr("How?") + settingsModel.translationTrigger + initY: whatHeader.height + whereHeader.height + z: 20 + + onClicked: { + if (!howContent.visible){ + root.state = "how" + } else { + root.state = "" + } + } + } + + TutorialHow { + id: howContent + width: root.contentWidth + anchors.horizontalCenter: parent.horizontalCenter + + onFirePush: { + root.firePush(pSectionPage) + } + onQuitTutorialClicked: leaveView() + } + + TutorialHeader { + id: importantHeader + width: root.width + height: (flickable.height / 13.0 ) * 4.0 + overlapping: false + headerImageSource: "qrc:///images/tutorial/main_menu_important_caret.svg" + titleText: qsTr("Important!") + settingsModel.translationTrigger + initY: whatHeader.height + whereHeader.height + howHeader.height + z: 10 + + onClicked: { + if (!importantContent.visible){ + root.state = "important" + } else { + root.state = "" + } + } + } + + TutorialImportant { + id: importantContent + width: root.contentWidth + anchors.horizontalCenter: parent.horizontalCenter + } + } + } + } + + TutorialFooter { + id: footer + width: root.width + anchors.horizontalCenter: parent.horizontalCenter + color: importantContent.visible && flickable.contentY > importantHeader.y - 1? Constants.tutorial_red + : howContent.visible && flickable.contentY > howHeader.y - 1? Constants.tutorial_blue + : whereContent.visible && flickable.contentY > whereHeader.y - 1? Constants.tutorial_green + : Constants.tutorial_orange + anchors.bottom: parent.bottom + + onMenuClicked: root.state = "" + onQuitTutorialClicked: leaveView() + } +} diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialWhat.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialWhat.qml new file mode 100644 index 0000000..5a14465 --- /dev/null +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialWhat.qml @@ -0,0 +1,473 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 + +TutorialContent { + id: baseItem + + Column { + width: parent.width * 0.9 + anchors.horizontalCenter: parent.horizontalCenter + + Text { + width: parent.width + text: qsTr("What is the online ID function?") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + font.italic: true + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + Text { + width: parent.width + text: qsTr("You can authenticate yourself in the internet") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_what.svg" + } + + Column { + width: parent.width + spacing: Constants.component_spacing + + Item { + height: parent.width * 0.6 + width: parent.width + + Rectangle { + id: circle + width: parent.width * 0.6 + height: width + radius: width * 0.5 + color: Constants.tutorial_very_light_grey + + x: (parent.width * 0.5) - (width / 2) + } + + TutorialImage { + id: stylisedEpa + source: "qrc:///images/tutorial/idcard.svg" + z: 2 + + width: parent.height * 0.6 + centerY: 0.5 + centerX: 0.8 + } + + TutorialImage { + id: smartPhone + source: "qrc:///images/tutorial/phone.svg" + z: 3 + + width: parent.height * 0.4 + centerY: 0.5 + centerX: 0.2 + } + + TutorialImage { + id: shield + source: "qrc:///images/tutorial/save.svg" + z: 4 + + width: parent.height * 0.3 + centerY: 0.5 + centerX: 0.5 + } + } + + Text { + width: parent.width * 0.9 + anchors.horizontalCenter: parent.horizontalCenter + text: qsTr("and use it to deal with administrative paperwork and business matters electronically!") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + } + + TutorialSpacer { + width: parent.width + text: qsTr("Alright, but is it secure?") + settingsModel.translationTrigger + color: Constants.tutorial_orange + } + + Column { + width: parent.width * 0.9 + anchors.horizontalCenter: parent.horizontalCenter + + Text { + width: parent.width + text: qsTr("Of course, because we use a so called") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + Text { + width: parent.width + text: qsTr("Mutual authentication") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + font.bold: true + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_what.svg" + } + + Column { + width: parent.width + spacing: Constants.component_spacing + + Item { + height: parent.width * 0.6 + width: parent.width + + TutorialImage { + source: "qrc:///images/tutorial/user-tine@3x.png" + z: 2 + + width: parent.height * 0.3 + centerY: 0.5 + centerX: 0.25 + } + + TutorialImage { + source: "qrc:///images/npa.svg" + z: 3 + + width: parent.height * 0.15 + centerY: 0.65 + centerX: 0.2 + } + + TutorialImage { + source: "qrc:///images/tutorial/circle-lock.svg" + z: 1 + + width: parent.height * 0.7 + centerY: 0.75 + centerX: 0.45 + } + + TutorialImage { + source: "qrc:///images/tutorial/circle-lock-2.svg" + z: 1 + + width: parent.height * 0.7 + centerY: 0.2 + centerX: 0.55 + } + + TutorialImage { + source: "qrc:///images/tutorial/providericons.png" + z: 2 + + width: parent.height * 0.8 + centerY: 0.5 + centerX: 0.7 + } + + TutorialImage { + source: "qrc:///images/tutorial/provider_home.svg" + z: 3 + + width: parent.height * 0.18 + centerY: 0.48 + centerX: 0.7 + } + } + + Text { + width: parent.width * 0.9 + anchors.horizontalCenter: parent.horizontalCenter + text: qsTr("... it establishes a secure connection between ID document and service provider.") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h2_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_what.svg" + } + + + Column { + width: parent.width + spacing: Constants.component_spacing * 2 + + Item { + height: parent.width * 0.6 + width: parent.width + + Text { + width: parent.width * 0.35 + text: qsTr("On every authentication you get displayed who wants to access which data") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_font_size + horizontalAlignment: Text.AlignLeft + wrapMode: Text.WordWrap + + x: (parent.width * 0.2) - (width / 2) + y: (parent.height * 0.5) - (height / 2) + } + + TutorialImage { + id: screenshot + source: qsTr("qrc:///images/tutorial/screenshot_cert_en.png") + settingsModel.translationTrigger + z: 3 + + readonly property real rightX: x + width + + width: parent.height * 0.6 + centerX: 0.6 + centerY: 0.5 + } + + Image { + source: "qrc:///images/tutorial/zoom_triangle.svg" + z: 4 + + width: small_smartphone.centerXValue - screenshot.rightX + height: screenshot.height + x: screenshot.rightX + y: (parent.height * 0.5) - (height / 2) + } + + TutorialImage { + id: small_smartphone + source: "qrc:///images/tutorial/phone.svg" + z: 1 + + readonly property real centerXValue: x + (width / 2) + + width: parent.height * 0.2 + centerY: 0.5 + centerX: 0.9 + } + } + + Text { + width: parent.width * 0.95 + anchors.horizontalCenter: parent.horizontalCenter + text: qsTr("and you consent to the request with your personal PIN.") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + Item { + width: parent.width + height: parent.width * 0.3 + z: 5 + + TutorialImage { + source: "qrc:///images/tutorial/pin-6@2x.png" + + width: parent.height + centerX: 0.6 + centerY: 0.5 + } + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_what.svg" + } + + + Column { + width: parent.width + spacing: Constants.component_spacing + + Item { + width: parent.width + height: width * 0.6 + + Rectangle { + width: parent.width * 0.6 + height: width + radius: width * 0.5 + color: Constants.tutorial_very_light_grey + z: 1 + + x: (parent.width * 0.5) - (width / 2) + } + + Text { + width: parent.width + text: qsTr("... is the provider authorized for this?") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + font.bold: true + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + z: 2 + + x: (parent.width * 0.5) - (width / 2) + y: (parent.height * 0.3) - (height / 2) + } + + Text { + width: parent.width + text: qsTr("The provider needs an authorization of the Federal Office of Administration.") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + z: 2 + + x: (parent.width * 0.5) - (width / 2) + y: (parent.height * 0.5) - (height / 2) + } + } + + Item { + width: parent.width + height: parent.width * 0.3 + + Text { + width: parent.width + text: qsTr("Certificate") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + font.bold: true + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + z: 1 + + x: (parent.width * 0.2) - (width / 2) + y: (parent.height * 0.5) - (height / 2) + } + + TutorialImage { + source: "qrc:///images/tutorial/provider_home.svg" + z: 1 + + width: parent.height * 0.7 + centerY: 0.5 + centerX: 0.5 + } + + TutorialImage { + source: "qrc:///images/tutorial/bva.svg" + z: 1 + + width: parent.height + centerY: 0.5 + centerX: 0.8 + } + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_what.svg" + } + + Column { + width: parent.width + spacing: Constants.component_spacing + + Item { + width: parent.width + height: parent.width * 0.3 + + TutorialImage { + source: "qrc:///images/tutorial/user-tine@3x.png" + z: 1 + + width: parent.height * 0.5 + centerY: 0.4 + centerX: 0.15 + } + + TutorialImage { + source: "qrc:///images/tutorial/pin-6@2x.png" + z: 2 + + width: parent.height + centerY: 0.5 + centerX: 0.25 + } + + TutorialImage { + source: "qrc:///images/tutorial/provider_home.svg" + z: 1 + + width: parent.height * 0.6 + centerY: 0.25 + centerX: 0.7 + } + + TutorialImage { + source: "qrc:///images/tutorial/bva.svg" + z: 2 + + width: parent.height * 0.8 + centerY: 0.5 + centerX: 0.8 + } + } + + Text { + width: parent.width * 0.9 + anchors.horizontalCenter: parent.horizontalCenter + text: qsTr("Everytime both participants authenticate each other...") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + font.bold: true + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + z: 1 + } + } + + TutorialSpacer { + width: parent.width + text: qsTr("... and therefore your data is protected and securely transfered.") + settingsModel.translationTrigger + color: Constants.tutorial_orange + } + + Column { + width: parent.width + spacing: Constants.component_spacing + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.8 + text: qsTr("You can also watch a video on YouTube on this topic") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h2_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + z: 2 + } + + Image { + anchors.horizontalCenter: parent.horizontalCenter + source: "qrc:///images/tutorial/play_movie.png" + width: parent.width * 0.7 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + + MouseArea { + anchors.fill: parent + + onClicked: Qt.openUrlExternally(qsTr("https://www.youtube.com/watch?v=fzbUZmHaZp4&index=5&list=PLLB5ERhVkn25qQXgMHQr-1KgyZsJKoSAm")) + } + } + } +} diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialWhere.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialWhere.qml new file mode 100644 index 0000000..725da4e --- /dev/null +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialWhere.qml @@ -0,0 +1,241 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 + +TutorialContent { + id: baseItem + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("Where can I use the online ID function?") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + font.italic: true + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_where.svg" + } + + Image { + anchors.horizontalCenter: parent.horizontalCenter + source: "qrc:///images/tutorial/generated/where_overview_question.svg" + width: parent.width * 0.75 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_where.svg" + } + + Text { + width: parent.width * 0.9 + anchors.horizontalCenter: parent.horizontalCenter + text: qsTr("On every website of a service provider where you see this icon:") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h2_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + Image { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.2 + source: "qrc:///images/npa.svg" + + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + + + Text { + width: parent.width * 0.8 + anchors.horizontalCenter: parent.horizontalCenter + text: qsTr("By the way, you can find many services directly in the AusweisApp2 provider list.") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_where.svg" + } + + Image { + source: qsTr("qrc:///images/tutorial/generated/where_providerlist_screenshot_en.svg") + settingsModel.translationTrigger + width: parent.width + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_where.svg" + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.9 + text: qsTr("The integrated self-disclosure is a special service to view the data saved on your ID card.") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_where.svg" + } + + Text { + width: parent.width * 0.8 + anchors.horizontalCenter: parent.horizontalCenter + text: qsTr("You can access it by clicking \"See my personal data\" on the AusweisApp2 start page, followed by \"Identify now\"") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h2_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + Image { + anchors.horizontalCenter: parent.horizontalCenter + source: qsTr("qrc:///images/tutorial/generated/where_identify_now_en.svg") + settingsModel.translationTrigger + width: parent.width + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + + TutorialSpacer { + width: parent.width + text: qsTr("And this is how it works") + settingsModel.translationTrigger + color: Constants.tutorial_green + } + + Text { + width: parent.width * 0.9 + anchors.horizontalCenter: parent.horizontalCenter + text: qsTr("The AusweisApp2 will always display who wants to access which of your data.") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + Image { + anchors.horizontalCenter: parent.horizontalCenter + source: qsTr("qrc:///images/tutorial/generated/where_userdata_example_en.svg") + settingsModel.translationTrigger + width: parent.width * 0.8 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_where.svg" + } + + Image { + anchors.horizontalCenter: parent.horizontalCenter + source: "qrc:///images/tutorial/generated/where_lay_down_id.svg" + width: parent.width * 0.7 + height: width * (sourceSize.height / sourceSize.width) + fillMode: Image.PreserveAspectFit + } + + Text { + width: parent.width * 0.8 + anchors.horizontalCenter: parent.horizontalCenter + text: qsTr("Now lay down your ID card and place your device on the ID card.") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + Item { + width: parent.width + height: noticeImage.height + + TutorialImage { + id: noticeImage + source: "qrc:///images/tutorial/hint.svg" + + width: parent.width * 0.2 + centerY: 0.5 + centerX: 0.2 + } + + Text { + width: parent.width * 0.6 + text: qsTr("Don't move your device during the procedure!") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_font_size + horizontalAlignment: Text.AlignLeft + wrapMode: Text.WordWrap + + x: (parent.width * 0.75) - (width / 2) + y: (parent.height * 0.5) - (height / 2) + } + } + + TutorialSeperator { + source: "qrc:///images/tutorial/section_seperator_where.svg" + } + + Item { + width: parent.width + height: pin6Image.height + Constants.component_spacing * 2 + + Text { + width: parent.width * 0.8 + text: (settingsModel.language === "en" ? qsTr("Enter") : qsTr("Now")) + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + + x: (parent.width * 0.5) - (width / 2) + y: (parent.height * 0.05) - (height / 2) + } + + TutorialImage { + id: pin6Image + source: "qrc:///images/tutorial/generated/where_pin6.svg" + z: 1 + + width: parent.width * 0.8 + centerY: 0.5 + centerX: 0.5 + } + + Text { + width: parent.width + text: qsTr("6 digits long PIN") + settingsModel.translationTrigger + font.family: "Noto Serif" + font.bold: true + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + z: 2 + + x: (parent.width * 0.5) - (width / 2) + y: (parent.height * 0.2) - (height / 2) + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.8 + text: (settingsModel.language === "en" ? qsTr("now!") : qsTr("enter!")) + settingsModel.translationTrigger + font.family: "Noto Serif" + font.pixelSize: Constants.tutorial_content_header_h1_font_size + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + + x: (parent.width * 0.5) - (width / 2) + y: (parent.height * 0.9) - (height / 2) + } + } +} diff --git a/resources/qml/Governikus/TutorialView/qmldir b/resources/qml/Governikus/TutorialView/qmldir new file mode 100644 index 0000000..9e53a1f --- /dev/null +++ b/resources/qml/Governikus/TutorialView/qmldir @@ -0,0 +1,20 @@ +module TutorialView + +internal TutorialHeader TutorialHeader.qml +internal TutorialContent TutorialContent.qml +internal TutorialWhat TutorialWhat.qml +internal TutorialWhere TutorialWhere.qml +internal TutorialHow TutorialHow.qml +internal TutorialImportant TutorialImportant.qml +internal TutorialImage TutorialImage.qml +internal TutorialFooter TutorialFooter.qml +internal TutorialSeperator TutorialSeperator.qml +internal TutorialSpacer TutorialSpacer.qml +internal TutorialReaderMethodNfc TutorialReaderMethodNfc.qml +internal TutorialReaderMethodSacMobile TutorialReaderMethodSacMobile.qml +internal TutorialReaderMethodSacDesktop TutorialReaderMethodSacDesktop.qml +internal TutorialReaderMethodBluetooth TutorialReaderMethodBluetooth.qml +internal TutorialReaderMethodFooter TutorialReaderMethodFooter.qml +internal TutorialExpandAnimation TutorialExpandAnimation.qml +internal TutorialCollapseAnimation TutorialCollapseAnimation.qml +TutorialView 1.0 TutorialView.qml diff --git a/resources/qml/Governikus/VersionInformationView/VersionInformation.qml b/resources/qml/Governikus/VersionInformationView/VersionInformation.qml deleted file mode 100644 index a074255..0000000 --- a/resources/qml/Governikus/VersionInformationView/VersionInformation.qml +++ /dev/null @@ -1,41 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Layouts 1.2 - -import Governikus.Global 1.0 -import Governikus.TitleBar 1.0 - - -SectionPage -{ - id: root - leftTitleBarAction: TitleBarAction { state: "back"; onClicked: firePop() } - headerTitleBarAction: TitleBarAction { text: qsTr("Version Information") + settingsModel.translationTrigger; font.bold: true } - - content: Item - { - height: pane.height + 2 * Constants.component_spacing - width: root.width - - Column - { - anchors.fill: parent - anchors.margins: Constants.component_spacing - - Pane { - id: pane - - Repeater { - model: versionInformationModel - - LabeledText { - id: delegate - label: model.label - text: model.text - width: pane.width - } - } - } - } - } -} diff --git a/resources/qml/Governikus/VersionInformationView/qmldir b/resources/qml/Governikus/VersionInformationView/qmldir deleted file mode 100644 index 652ae10..0000000 --- a/resources/qml/Governikus/VersionInformationView/qmldir +++ /dev/null @@ -1,2 +0,0 @@ -module VersionInformationView -VersionInformation 1.0 VersionInformation.qml diff --git a/resources/qml/Governikus/View/+desktop/FocusFrame.qml b/resources/qml/Governikus/View/+desktop/FocusFrame.qml new file mode 100644 index 0000000..f22e01d --- /dev/null +++ b/resources/qml/Governikus/View/+desktop/FocusFrame.qml @@ -0,0 +1,21 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 + + +Rectangle { + property Item framee: parent + property Item scope: parent + property real marginFactor: 1 + + readonly property real size: Math.max(Math.min(framee.width, framee.height) / 32, 1) + + id: border + anchors.fill: framee + anchors.margins: marginFactor * -size * 2 + radius: size * 2 + border.width: scope.focus ? size : 0; + border.color: Constants.white + opacity: 0.5 + color: "transparent" +} diff --git a/resources/qml/Governikus/View/+desktop/SectionPage.qml b/resources/qml/Governikus/View/+desktop/SectionPage.qml new file mode 100644 index 0000000..1ce8016 --- /dev/null +++ b/resources/qml/Governikus/View/+desktop/SectionPage.qml @@ -0,0 +1,20 @@ +import QtQuick 2.10 + +import Governikus.TitleBar 1.0 + +Item { + enum Views { + Main = 1, + Identify, + Provider + } + + anchors.fill: parent + + signal nextView(int pName) + + readonly property bool sectionPageTypeMarker: true + + property TitleBarAction titleBarAction: null + property Item navSuccessor: null +} diff --git a/resources/qml/Governikus/View/+mobile/+android/ContentArea.qml b/resources/qml/Governikus/View/+mobile/+android/ContentArea.qml new file mode 100644 index 0000000..14ef4ce --- /dev/null +++ b/resources/qml/Governikus/View/+mobile/+android/ContentArea.qml @@ -0,0 +1,86 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.ChangePinView 1.0 +import Governikus.InformationView 1.0 +import Governikus.RemoteServiceView 1.0 +import Governikus.TutorialView 1.0 +import Governikus.FeedbackView 1.0 +import Governikus.DeveloperView 1.0 +import Governikus.IdentifyView 1.0 +import Governikus.ProviderView 1.0 +import Governikus.HistoryView 1.0 +import Governikus.View 1.0 + +Item { + id: baseItem + property bool ready: settingsModel.showTutorialOnStart ? tutorialView.ready : identifyView.ready + readonly property var visibleItem: visibleChildren[0] + readonly property var currentSectionPage: if (visibleItem) visibleItem.currentSectionPage + + TabBarView { + id: identifyView + anchors.fill: parent + visible: baseItem.state === "identify" + prefetch: baseItem.ready + sourceComponent: IdentifyView {} + } + + TabBarView { + anchors.fill: parent + visible: baseItem.state === "provider" + prefetch: baseItem.ready + sourceComponent: ProviderView {} + } + + TabBarView { + anchors.fill: parent + visible: baseItem.state === "history" + prefetch: baseItem.ready + sourceComponent: HistoryView {} + } + + TabBarView { + id: pinView + anchors.fill: parent + visible: baseItem.state === "pin" + prefetch: baseItem.ready + sourceComponent: ChangePinView {} + } + + TabBarView { + anchors.fill: parent + visible: baseItem.state === "remoteservice" + prefetch: baseItem.ready + sourceComponent: RemoteServiceView {} + } + + TabBarView { + id: tutorialView + anchors.fill: parent + visible: baseItem.state === "tutorial" + prefetch: baseItem.ready + sourceComponent: TutorialView {} + } + + TabBarView { + anchors.fill: parent + visible: baseItem.state === "feedback" + prefetch: baseItem.ready + sourceComponent: Feedback {} + } + + TabBarView { + anchors.fill: parent + visible: baseItem.state === "information" + prefetch: baseItem.ready + sourceComponent: Information {} + } + + TabBarView { + anchors.fill: parent + visible: baseItem.state === "developeroptions" + prefetch: baseItem.ready + sourceComponent: DeveloperView {} + } +} diff --git a/resources/qml/Governikus/View/+mobile/+ios/ContentArea.qml b/resources/qml/Governikus/View/+mobile/+ios/ContentArea.qml new file mode 100644 index 0000000..7c06c0f --- /dev/null +++ b/resources/qml/Governikus/View/+mobile/+ios/ContentArea.qml @@ -0,0 +1,52 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.ChangePinView 1.0 +import Governikus.MoreView 1.0 +import Governikus.IdentifyView 1.0 +import Governikus.ProviderView 1.0 +import Governikus.HistoryView 1.0 +import Governikus.View 1.0 + +Item { + id: baseItem + property alias ready: identifyView.ready + readonly property var visibleItem: visibleChildren[0] + readonly property var currentSectionPage: if (visibleItem) visibleItem.currentSectionPage + + TabBarView { + id: identifyView + anchors.fill: parent + visible: baseItem.state === "identify" + sourceComponent: IdentifyView {} + } + + TabBarView { + anchors.fill: parent + visible: baseItem.state === "provider" + prefetch: identifyView.ready + sourceComponent: ProviderView {} + } + + TabBarView { + anchors.fill: parent + visible: baseItem.state === "history" + prefetch: identifyView.ready + sourceComponent: HistoryView {} + } + + TabBarView { + id: pinView + anchors.fill: parent + visible: baseItem.state === "pin" + prefetch: identifyView.ready + sourceComponent: ChangePinView {} + } + + TabBarView { + anchors.fill: parent + visible: baseItem.state === "more" + prefetch: identifyView.ready + sourceComponent: MoreView {} + } +} diff --git a/resources/qml/Governikus/View/+mobile/ContentAreaLoader.qml b/resources/qml/Governikus/View/+mobile/ContentAreaLoader.qml new file mode 100644 index 0000000..0918ff8 --- /dev/null +++ b/resources/qml/Governikus/View/+mobile/ContentAreaLoader.qml @@ -0,0 +1,28 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 + +// This proxy Component interrupts the QML parsing process by using a Loader. +// The parsing of the included file is continued in a background +// process by the loader. This way the splash screen is shown while the Loader +// parses the given source. +Item { + id: baseItem + property bool ready: false + readonly property var visibleItem: if (loader.item) loader.item.visibleChildren[0] + readonly property var currentSectionPage: if (visibleItem) visibleItem.currentSectionPage + + Loader { + id: loader + anchors.fill: parent + + asynchronous: true + sourceComponent: ContentArea {} + onStatusChanged: { + if (status === Loader.Ready){ + baseItem.ready = Qt.binding(function() {return loader.item.ready}) + item.state = Qt.binding(function() {return baseItem.state}) + } + } + } +} diff --git a/resources/qml/Governikus/View/+mobile/SectionPage.qml b/resources/qml/Governikus/View/+mobile/SectionPage.qml new file mode 100644 index 0000000..61e0f57 --- /dev/null +++ b/resources/qml/Governikus/View/+mobile/SectionPage.qml @@ -0,0 +1,54 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.TitleBar 1.0 + +Item { + signal firePush(var pSectionPage) + signal fireReplace(var pSectionPage) + signal firePop() + signal firePopAll() + + property bool pushed: false + + property bool topLevelPage: false + + property var leftTitleBarAction: TitleBarAction {} + property var headerTitleBarAction: TitleBarAction {} + property var rightTitleBarAction: Item {} + property var subTitleBarAction: Item {} + + property color titleBarColor: Constants.blue + /* if the header component has a property named titleBarOpacity, use it, otherwise use default value*/ + readonly property real titleBarOpacity: header !== null && typeof(header.titleBarOpacity) != "undefined" ? header.titleBarOpacity : 1 + + property QtObject header: null + property QtObject content: null + property alias contentY: flickable.contentY + property bool disableFlicking: false + + Flickable { + property real startContentY: 0 + id: flickable + clip: true + flickableDirection: Flickable.VerticalFlick + contentWidth: flickableContent.width + contentHeight: flickableContent.height + anchors.bottom: parent.bottom + width: parent.width + maximumFlickVelocity: Constants.scrolling_speed + /* if a header is set, it is shown as background of the TitleBar, so we need to expand the height*/ + height: header !== null ? parent.height + Constants.titlebar_height : parent.height + onMovementStarted: { + startContentY = contentY + } + onContentYChanged: { + if (disableFlicking || contentY < 0) { contentY = 0 /* prevent flicking over the top */} + } + Column { + id: flickableContent + + children: [header, content] + } + } +} diff --git a/resources/qml/Governikus/View/+mobile/TabBarView.qml b/resources/qml/Governikus/View/+mobile/TabBarView.qml new file mode 100644 index 0000000..8e24e9b --- /dev/null +++ b/resources/qml/Governikus/View/+mobile/TabBarView.qml @@ -0,0 +1,158 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.2 + +import Governikus.Global 1.0 + +Item { + id: baseItem + + property Component sourceComponent + readonly property bool ready: loader.status === Loader.Ready + property bool prefetch: false + readonly property var currentSectionPage: stack.currentItem + + property alias stack: stack + + // Item provided by loader has been pushed + property bool pushed: false + + // Items that should be pushed after the main item has been loaded and pushed + property var pendingItems: new Array() + + QtObject { + id: d + + property var pendingSignals: [] + + function push(pSectionPage) { + if (stack.currentItem === pSectionPage) { + return + } + + if (baseItem.pushed) { + pSectionPage.firePush.connect(baseItem.push) + pSectionPage.fireReplace.connect(baseItem.replace) + pSectionPage.firePop.connect(baseItem.pop) + pSectionPage.firePopAll.connect(baseItem.popAll) + stack.push(pSectionPage) + pSectionPage.pushed = true + } + // Main item has not been loaded yet, delay push. + else { + baseItem.pendingItems.push({ "item": pSectionPage }) + } + } + + + function replace(pSectionPage) { + if (stack.currentItem === pSectionPage) { + return + } + + if (stack.depth <= 1) { + d.push(pSectionPage) + return + } + + pSectionPage.pushed = false + var item = stack.currentItem + item.firePush.disconnect(baseItem.push) + item.fireReplace.disconnect(baseItem.replace) + item.firePop.disconnect(baseItem.pop) + item.firePopAll.disconnect(baseItem.popAll) + + var page = stack.replace(pSectionPage) + page.firePush.connect(baseItem.push) + page.fireReplace.connect(baseItem.replace) + page.firePop.connect(baseItem.pop) + page.firePopAll.connect(baseItem.popAll) + } + + + function pop() { + var sectionPage = stack.pop() + sectionPage.pushed = false + sectionPage.firePush.disconnect(baseItem.push) + sectionPage.fireReplace.disconnect(baseItem.replace) + sectionPage.firePop.disconnect(baseItem.pop) + sectionPage.firePopAll.disconnect(baseItem.popAll) + } + + + function popAll() { + while (stack.depth > 1) { + d.pop() + } + } + + // Workaround for QTBUG-57267 + function handlePendingSignals() { + while (pendingSignals.length > 0) { + if (stack.busy) { + pendingSignalsTimer.start() + return + } + + console.log("Executing queued stack action.") + pendingSignals.shift()() + } + } + } + + + Loader { + id: loader + asynchronous: true + onLoaded: { + baseItem.pushed = true + item.topLevelPage = true + push(item) + item.pushed = true + + pendingItems.reverse(); + while (pendingItems.length > 0) { + var pendingItem = pendingItems.pop() + d.push(pendingItem["item"], pendingItem["properties"]) + } + } + + sourceComponent: parent.visible || baseItem.prefetch ? parent.sourceComponent : loader.sourceComponent + } + + + StackView { + id: stack + anchors.fill: parent + } + + Timer { + id: pendingSignalsTimer + interval: 100 + repeat: false + onTriggered: d.handlePendingSignals() + } + + function push(pSectionPage) { + d.pendingSignals.push(function() {d.push(pSectionPage)}) + d.handlePendingSignals() + } + + + function replace(pSectionPage) { + d.pendingSignals.push(function() {d.replace(pSectionPage)}) + d.handlePendingSignals() + } + + + function pop() { + d.pendingSignals.push(function() {d.pop()}) + d.handlePendingSignals() + } + + + function popAll() { + d.pendingSignals.push(function() {d.popAll()}) + d.handlePendingSignals() + } +} diff --git a/resources/qml/Governikus/View/qmldir b/resources/qml/Governikus/View/qmldir new file mode 100644 index 0000000..f119d43 --- /dev/null +++ b/resources/qml/Governikus/View/qmldir @@ -0,0 +1,7 @@ +module View + +ContentArea 1.0 ContentArea.qml +ContentAreaLoader 1.0 ContentAreaLoader.qml +SectionPage 1.0 SectionPage.qml +TabBarView 1.0 TabBarView.qml +FocusFrame 1.0 FocusFrame.qml diff --git a/resources/qml/Governikus/WhiteListClient/+mobile/WhiteListSurveyView.qml b/resources/qml/Governikus/WhiteListClient/+mobile/WhiteListSurveyView.qml new file mode 100644 index 0000000..c5e8920 --- /dev/null +++ b/resources/qml/Governikus/WhiteListClient/+mobile/WhiteListSurveyView.qml @@ -0,0 +1,154 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 +import Governikus.Type.ApplicationModel 1.0 + +SectionPage { + id: root + leftTitleBarAction: TitleBarAction { state: "cancel"; onClicked: root.done(false) } + headerTitleBarAction: TitleBarAction { text: qsTr("Feedback") + settingsModel.translationTrigger; font.bold: true } + + signal done(bool pUserAccepted) + + content: Column { + width: root.width + padding: Constants.pane_padding + spacing: Constants.pane_spacing + + Pane { + id: whitePane + anchors.margins: Constants.pane_padding + title: qsTr("Send device data?") + settingsModel.translationTrigger + + Text { + anchors.left: parent.left + anchors.right: parent.right + text: qsTr("Would you like to help us to improve the AusweisApp2?") + settingsModel.translationTrigger + font.pixelSize: Constants.normal_font_size + color: Constants.secondary_text + wrapMode: Text.WordWrap + } + Text { + anchors.left: parent.left + anchors.right: parent.right + text: qsTr("Supplying your device characteristics helps us to gather reliable information about the compatibility of your device.") + settingsModel.translationTrigger + font.pixelSize: Constants.normal_font_size + color: Constants.secondary_text + wrapMode: Text.WordWrap + } + Text { + anchors.left: parent.left + anchors.right: parent.right + text: qsTr("The transmission is anonymous. No personal data is collected or transmitted!") + settingsModel.translationTrigger + font.pixelSize: Constants.normal_font_size + color: Constants.secondary_text + wrapMode: Text.WordWrap + } + Text { + anchors.left: parent.left + anchors.right: parent.right + text: qsTr("The following information will be transmitted, if you decide so:") + settingsModel.translationTrigger + font.pixelSize: Constants.normal_font_size + color: Constants.secondary_text + wrapMode: Text.WordWrap + } + + Item { + anchors.left: parent.left + anchors.right: parent.right + height: Math.max(column1.height, column2.height) + + ListModel { + id: leftInformationModel + ListElement { + entry: QT_TR_NOOP("Vendor") + } + ListElement { + entry: QT_TR_NOOP("Model number") + } + ListElement { + entry: QT_TR_NOOP("Model name") + } + ListElement { + entry: QT_TR_NOOP("Collection date") + } + ListElement { + entry: QT_TR_NOOP("AusweisApp2 version") + } + } + + ListModel { + id: rightInformationModel + ListElement { + entry: QT_TR_NOOP("ROM build number") + } + ListElement { + entry: QT_TR_NOOP("Android version") + } + ListElement { + entry: QT_TR_NOOP("Kernel version") + } + ListElement { + entry: QT_TR_NOOP("Max. NFC packet length") + } + } + + Column { + id: column1 + width: parent.width / 2 + anchors.left: parent.left + + Repeater { + model: leftInformationModel + delegate: BulletPointDelegate { + text: qsTr(entry) + settingsModel.translationTrigger + } + } + } + + Column { + id: column2 + width: parent.width / 2 + anchors.right: parent.right + + Repeater { + model: rightInformationModel + delegate: BulletPointDelegate { + text: qsTr(entry) + settingsModel.translationTrigger + } + } + } + } + + Text { + anchors.left: parent.left + anchors.right: parent.right + text: qsTr("Thank you for your assistance!") + settingsModel.translationTrigger + font.pixelSize: Constants.normal_font_size + font.bold: true + color: Constants.secondary_text + wrapMode: Text.WordWrap + } + } + + + Row { + spacing: Constants.component_spacing + height: childrenRect.height + anchors.horizontalCenter: parent.horizontalCenter + + GButton { + text: qsTr("Cancel") + settingsModel.translationTrigger + onClicked: root.done(false) + } + + GButton { + text: qsTr("Transmit") + settingsModel.translationTrigger + onClicked: root.done(true) + } + } + } +} diff --git a/resources/qml/Governikus/WhiteListClient/BulletPointDelegate.qml b/resources/qml/Governikus/WhiteListClient/BulletPointDelegate.qml new file mode 100644 index 0000000..e673bf9 --- /dev/null +++ b/resources/qml/Governikus/WhiteListClient/BulletPointDelegate.qml @@ -0,0 +1,31 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 + +Item { + width: parent.width + height: content.height + property alias text: content.text + + Text { + id: bullet + anchors.top: parent.top + anchors.left: parent.left + text: "\u2022" + font.pixelSize: Constants.normal_font_size + color: Constants.secondary_text + } + + Text { + id: content + anchors.top: parent.top + anchors.left: bullet.right + anchors.leftMargin: Constants.groupbox_spacing + anchors.right: parent.right + anchors.rightMargin: Constants.groupbox_spacing + + font.pixelSize: Constants.normal_font_size + color: Constants.secondary_text + wrapMode: Text.WordWrap + } +} diff --git a/resources/qml/Governikus/WhiteListClient/qmldir b/resources/qml/Governikus/WhiteListClient/qmldir new file mode 100644 index 0000000..0ece239 --- /dev/null +++ b/resources/qml/Governikus/WhiteListClient/qmldir @@ -0,0 +1,5 @@ +module WhiteListClient + +internal BulletPointDelegate BulletPointDelegate.qml + +WhiteListSurveyView 1.0 WhiteListSurveyView.qml diff --git a/resources/qml/Governikus/Workflow/+desktop/ProgressCircle.qml b/resources/qml/Governikus/Workflow/+desktop/ProgressCircle.qml new file mode 100644 index 0000000..8ab3f2b --- /dev/null +++ b/resources/qml/Governikus/Workflow/+desktop/ProgressCircle.qml @@ -0,0 +1,92 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.Type.ApplicationModel 1.0 + + +Item { + QtObject { + id: d + readonly property int stepWidth: ApplicationModel.scaleFactor * 250 + } + + height: selector.height + width: d.stepWidth * 4 + height + + state: "1" + states: [ + State { + name: "1" + PropertyChanges { target: circle1; enabled: true } + PropertyChanges { target: circle2; enabled: false } + PropertyChanges { target: circle3; enabled: false } + PropertyChanges { target: line; anchors.leftMargin: 0 } + }, + State { + name: "2" + PropertyChanges { target: circle1; enabled: false } + PropertyChanges { target: circle2; enabled: true } + PropertyChanges { target: circle3; enabled: false } + PropertyChanges { target: line; anchors.leftMargin: -d.stepWidth } + }, + State { + name: "3" + PropertyChanges { target: circle1; enabled: false } + PropertyChanges { target: circle2; enabled: false } + PropertyChanges { target: circle3; enabled: true } + PropertyChanges { target: line; anchors.leftMargin: 2 * -d.stepWidth } + } + ] + transitions: [ + Transition { + PropertyAnimation { target: line; property: "anchors.leftMargin"; duration: 500; easing.type: Easing.InOutCubic } + SequentialAnimation { + PauseAnimation { duration: 200 } + PropertyAction { target: circle1; property: "enabled" } + PropertyAction { target: circle2; property: "enabled" } + PropertyAction { target: circle3; property: "enabled" } + } + } + ] + + Rectangle { + id: line + anchors.left: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + width: d.stepWidth * 2 + height: ApplicationModel.scaleFactor * 8 + color: Constants.grey_light + } + + TextCircle { + id: circle1 + anchors.verticalCenter: line.verticalCenter + anchors.horizontalCenter: line.left + text: "1" + } + + TextCircle { + id: circle2 + anchors.verticalCenter: line.verticalCenter + anchors.horizontalCenter: line.horizontalCenter + text: "2" + } + + TextCircle { + id: circle3 + anchors.verticalCenter: line.verticalCenter + anchors.horizontalCenter: line.right + text: "3" + } + + Rectangle { + id: selector + height: circle1.height + ApplicationModel.scaleFactor * 40 + width: height + anchors.centerIn: parent + color: "transparent" + radius: height / 2 + border.color: Constants.white + border.width: ApplicationModel.scaleFactor * 6 + } +} diff --git a/resources/qml/Governikus/Workflow/+desktop/TextCircle.qml b/resources/qml/Governikus/Workflow/+desktop/TextCircle.qml new file mode 100644 index 0000000..fc03d10 --- /dev/null +++ b/resources/qml/Governikus/Workflow/+desktop/TextCircle.qml @@ -0,0 +1,23 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 + + +Rectangle { + property alias text: number.text + + height: number.height * 2 + width: height + enabled: false + radius: width * 0.5 + color: enabled ? Constants.blue : Constants.white + + Text { + id: number + anchors.centerIn: parent + font.bold: true + font.pixelSize: Constants.header_font_size + color: parent.enabled ? Constants.white : Constants.blue + } +} diff --git a/resources/qml/Governikus/Workflow/+mobile/BluetoothWorkflow.qml b/resources/qml/Governikus/Workflow/+mobile/BluetoothWorkflow.qml new file mode 100644 index 0000000..75436e5 --- /dev/null +++ b/resources/qml/Governikus/Workflow/+mobile/BluetoothWorkflow.qml @@ -0,0 +1,91 @@ +import QtQuick 2.10 +import QtQuick.Layouts 1.1 + +import Governikus.Global 1.0 +import Governikus.TechnologyInfo 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.ReaderPlugIn 1.0 +import Governikus.Type.NumberModel 1.0 + + +Item { + id: baseItem + signal continueWorkflow() + signal requestPluginType(int pReaderPlugInType) + + property int waitingFor: 0 + + property bool locationPermissionInfoConfirmed: false + readonly property bool showLocationPermissionInfo: ApplicationModel.locationPermissionRequired && !locationPermissionInfoConfirmed + + onStateChanged: { + if (baseItem.waitingFor === Workflow.WaitingFor.Reader) { + locationPermissionInfoConfirmed = false + } + } + + onShowLocationPermissionInfoChanged: { + if (ApplicationModel.bluetoothEnabled && !showLocationPermissionInfo) { + continueWorkflow() + } + } + + ProgressIndicator { + id: progressIndicator + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + height: parent.height / 2 + imageIconSource: "qrc:///images/icon_bluetooth.svg" + imagePhoneSource: "qrc:///images/phone_bluetooth.svg" + state: (!ApplicationModel.bluetoothAvailable || !ApplicationModel.bluetoothEnabled || parent.showLocationPermissionInfo) ? "off" + : (baseItem.waitingFor === Workflow.WaitingFor.Reader) ? "one" + : (baseItem.waitingFor === Workflow.WaitingFor.Card) ? "two" + : "" + } + + TechnologyInfo { + id: techInfo + anchors.left: parent.left + anchors.leftMargin: Utils.dp(5) + anchors.right: parent.right + anchors.rightMargin: anchors.leftMargin + anchors.top: progressIndicator.bottom + anchors.bottom: technologySwitch.top + + enableButtonVisible: ApplicationModel.bluetoothAvailable && (!ApplicationModel.bluetoothEnabled || parent.showLocationPermissionInfo) + enableButtonText: (!ApplicationModel.bluetoothEnabled ? qsTr("Enable Bluetooth") : qsTr("Continue")) + settingsModel.translationTrigger + onEnableClicked: { + if (!ApplicationModel.bluetoothEnabled) { + ApplicationModel.bluetoothEnabled = true + } else { + parent.locationPermissionInfoConfirmed = true + } + } + enableText: (!visible ? "" + : !ApplicationModel.bluetoothAvailable ? qsTr("Bluetooth is not supported by your device.") + "
" + qsTr("Please try NFC.") + : !ApplicationModel.bluetoothEnabled ? qsTr("Bluetooth is switched off.") + "
" + qsTr("Please enable Bluetooth.") + : parent.showLocationPermissionInfo ? qsTr("No paired and activated Bluetooth device was detected. The AusweisApp2 needs access to your location in order to discover available devices. You can grant this permission after clicking the continue button.") + : !ApplicationModel.bluetoothResponding ? qsTr("An error occured while connecting to your bluetooth device. Try to pair your device in the system settings and restart the app.") + : "") + settingsModel.translationTrigger + + titleText: ((baseItem.waitingFor === Workflow.WaitingFor.Reader) ? qsTr("Establish connection") + : (baseItem.waitingFor === Workflow.WaitingFor.Card) ? qsTr("Determine card") + : "") + settingsModel.translationTrigger + + subTitleText: (!visible ? "" + : !!NumberModel.inputError ? NumberModel.inputError + : (baseItem.waitingFor === Workflow.WaitingFor.Reader) ? qsTr("Search card reader...") + : (baseItem.waitingFor === Workflow.WaitingFor.Card) ? qsTr("Please insert your ID card.") + : "") + settingsModel.translationTrigger + } + + TechnologySwitch { + id: technologySwitch + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + selectedTechnology: ReaderPlugIn.BLUETOOTH + onRequestPluginType: parent.requestPluginType(pReaderPlugInType) + } +} diff --git a/resources/qml/Governikus/Workflow/+mobile/BusyImageIndicator.qml b/resources/qml/Governikus/Workflow/+mobile/BusyImageIndicator.qml new file mode 100644 index 0000000..a4b49c3 --- /dev/null +++ b/resources/qml/Governikus/Workflow/+mobile/BusyImageIndicator.qml @@ -0,0 +1,27 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Style 1.0 + +Item { + property string image + property alias running: busyIndicator.running + + BusyIndicator { + id: busyIndicator + anchors.centerIn: parent + height: img.height + width: height + running: false + contentItem: NpaBusyIndicatorStyle {factor: 1.2} + } + + Image { + id: img + anchors.centerIn: parent + width: parent.width + fillMode: Image.PreserveAspectFit + smooth: true + source: image + } +} diff --git a/resources/qml/Governikus/Workflow/+mobile/CardReader.qml b/resources/qml/Governikus/Workflow/+mobile/CardReader.qml new file mode 100644 index 0000000..4a2d465 --- /dev/null +++ b/resources/qml/Governikus/Workflow/+mobile/CardReader.qml @@ -0,0 +1,213 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 + +Item { + property bool pinFieldAnimation: true + property bool cardAnimation: true + + id: baseItem + width: height * 3 / 7 + + Timer { + property int index + + id: onTimer + interval: 1500 + running: pinFieldAnimation || cardAnimation + triggeredOnStart: true + + onTriggered: { + onTimer.triggeredOnStart = false + if (pinFieldAnimation) + { + onTimer.index = Utils.getRandomInt(0, 9) + repeater.itemAt(onTimer.index).state = "on" + } + + if (cardAnimation) + { + card.state = "out" + } + + offTimer.restart() + } + } + + Timer { + id: offTimer + interval: 500 + onTriggered: { + if (onTimer.index != undefined) + { + repeater.itemAt(onTimer.index).state = "off" + } + + card.state = "in" + + onTimer.restart() + } + } + + + + Rectangle { + id: reader + color: "#92cef9" + radius: height * 0.05 + anchors.bottom: parent.bottom + width: parent.width + height: parent.height * 5 / 7 + + Rectangle { + id: slot + color: "white" + radius: 10 + height: reader.height * 0.05 + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: parent.height * 0.05 + anchors.leftMargin: parent.width * 0.2 + anchors.rightMargin: parent.width * 0.2 + } + + Rectangle { + id: card + color: Constants.blue + radius: height * 0.05 + anchors.horizontalCenter: parent.horizontalCenter + height: baseItem.height * 1.5 / 7 + width: baseItem.width * 0.5 + + onVisibleChanged: y = reader.mapFromItem(baseItem, baseItem.x, card.y).y + + Rectangle { + id: cardStripe1 + color: "white" + radius: 10 + height: parent.height * 0.1 + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: parent.height * 0.1 + anchors.leftMargin: parent.width * 0.1 + anchors.rightMargin: parent.width * 0.1 + } + + Rectangle { + id: cardStripe2 + color: "white" + radius: cardStripe1.radius + height: parent.height * 0.1 + anchors.top: cardStripe1.top + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: parent.height * 0.2 + anchors.leftMargin: parent.width * 0.1 + anchors.rightMargin: parent.width * 0.1 + } + + MouseArea { + anchors.fill: parent + onClicked: card.state = card.state === "out" ? "in" : "out" + } + + states: [ + State { + name: "out" + AnchorChanges {target: card; anchors.bottom: reader.top} + }, + State { + name: "in" + AnchorChanges {target: card; anchors.bottom: slot.verticalCenter} + } + ] + + transitions: [ + Transition { + AnchorAnimation {duration: 500} + } + ] + } + + Rectangle { + readonly property int margin: parent.width * 0.1 + id: display + color: "white" + radius: height * 0.2 + height: reader.height * 0.2 + anchors.bottom: pinGrid.top + anchors.margins: margin + anchors.left: parent.left + anchors.right: parent.right + } + + Grid { + id: pinGrid + anchors.bottom: reader.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: display.margin + + columns: 3 + spacing: anchors.margins + height: width + + Repeater { + id: repeater + model: 9 + + Item { + readonly property int _size: (reader.width - 4 * pinGrid.spacing) / 3 + property alias state: pinButtonCircle.state + + id: pinButton + width: _size + height: width + + Rectangle { + id: pinButtonCircle + anchors.centerIn: parent + width: pinButton._size + height: width + radius: width * 0.5 + + Behavior on color { + ColorAnimation { + duration: 250 + } + } + + Behavior on width { + NumberAnimation { + duration: 1000 + easing.type: Easing.OutElastic + } + } + + MouseArea { + anchors.fill: parent + onClicked: pinButton.state = pinButton.state === "off" ? "on" : "off" + } + + state: "off" + + states: [ + State { + name: "off" + PropertyChanges {target: pinButtonCircle; color: "white"} + PropertyChanges {target: pinButtonCircle; width: pinButton._size} + }, + State { + name: "on" + PropertyChanges {target: pinButtonCircle; color: Constants.blue} + PropertyChanges {target: pinButtonCircle; width: pinButton._size + pinButton._size * 0.3} + } + ] + } + } + } + } + } +} diff --git a/resources/qml/Governikus/Workflow/+mobile/GeneralWorkflow.qml b/resources/qml/Governikus/Workflow/+mobile/GeneralWorkflow.qml new file mode 100644 index 0000000..5998e93 --- /dev/null +++ b/resources/qml/Governikus/Workflow/+mobile/GeneralWorkflow.qml @@ -0,0 +1,54 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.TitleBar 1.0 +import Governikus.Workflow 1.0 +import Governikus.View 1.0 +import Governikus.Type.ReaderPlugIn 1.0 + +SectionPage +{ + QtObject { + id: d + readonly property int readerPlugInType: workflowModel.readerPlugInType + } + + id: baseItem + + property var controller + property var workflowModel + property string workflowTitle + + property int waitingFor: 0 + + leftTitleBarAction: TitleBarAction { + enabled: baseItem.waitingFor !== Workflow.WaitingFor.Password + state: enabled ? "cancel" : "hidden" + onClicked: workflowModel.cancelWorkflow() + } + headerTitleBarAction: TitleBarAction { text: workflowTitle; font.bold: true } + + NfcWorkflow + { + anchors.fill: parent + visible: d.readerPlugInType === ReaderPlugIn.NFC + onRequestPluginType: workflowModel.readerPlugInType = pReaderPlugInType; + } + + RemoteWorkflow + { + anchors.fill: parent + visible: d.readerPlugInType === ReaderPlugIn.REMOTE || d.readerPlugInType === ReaderPlugIn.PCSC + onRequestPluginType: workflowModel.readerPlugInType = pReaderPlugInType; + } + + BluetoothWorkflow + { + anchors.fill: parent + waitingFor: baseItem.waitingFor + visible: d.readerPlugInType === ReaderPlugIn.BLUETOOTH + onLocationPermissionInfoConfirmedChanged: controller.locationPermissionConfirmed = locationPermissionInfoConfirmed + onContinueWorkflow: workflowModel.continueWorkflow() + onRequestPluginType: workflowModel.readerPlugInType = pReaderPlugInType; + } +} diff --git a/resources/qml/Governikus/Workflow/+mobile/NfcProgressIndicator.qml b/resources/qml/Governikus/Workflow/+mobile/NfcProgressIndicator.qml new file mode 100644 index 0000000..6f22a6e --- /dev/null +++ b/resources/qml/Governikus/Workflow/+mobile/NfcProgressIndicator.qml @@ -0,0 +1,92 @@ +import QtQuick 2.10 +import QtGraphicalEffects 1.0 + +import Governikus.Global 1.0 + + +Item { + id: baseItem + + SequentialAnimation { + id: shaking + loops: Animation.Infinite + readonly property int delta: Utils.dp(4) + readonly property int deltaDuration: 300 + + ParallelAnimation { + SequentialAnimation { + PropertyAnimation { target: phone.anchors; property: "horizontalCenterOffset"; to: -shaking.delta; duration: shaking.deltaDuration } + PropertyAnimation { target: phone.anchors; property: "horizontalCenterOffset"; to: shaking.delta; duration: 2*shaking.deltaDuration } + PropertyAnimation { target: phone.anchors; property: "horizontalCenterOffset"; to: 0; duration: shaking.deltaDuration } + } + SequentialAnimation { + PropertyAnimation { target: phone.anchors; property: "verticalCenterOffset"; to: -shaking.delta; duration: 2*shaking.deltaDuration } + PropertyAnimation { target: phone.anchors; property: "verticalCenterOffset"; to: 0; duration: 2*shaking.deltaDuration } + } + } + ParallelAnimation { + SequentialAnimation { + PropertyAnimation { target: phone.anchors; property: "horizontalCenterOffset"; to: -shaking.delta; duration: shaking.deltaDuration } + PropertyAnimation { target: phone.anchors; property: "horizontalCenterOffset"; to: shaking.delta; duration: 2*shaking.deltaDuration } + PropertyAnimation { target: phone.anchors; property: "horizontalCenterOffset"; to: 0; duration: shaking.deltaDuration } + } + SequentialAnimation { + PropertyAnimation { target: phone.anchors; property: "verticalCenterOffset"; to: shaking.delta; duration: 2*shaking.deltaDuration } + PropertyAnimation { target: phone.anchors; property: "verticalCenterOffset"; to: 0; duration: 2*shaking.deltaDuration } + } + } + } + + states: [ + State { + name: "off" + PropertyChanges { target: grayLevel; visible: true } + PropertyChanges { target: card; x: baseItem.width; y: baseItem.height / 2; rotation: -180 } + PropertyChanges { target: shaking; running: false } + }, + State { + name: "on" + PropertyChanges { target: grayLevel; visible: false } + PropertyChanges { target: card; x: (baseItem.width + phone.width - card.width)/2; y: baseItem.height / 4; rotation: 0 } + PropertyChanges { target: shaking; running: true } + } + ] + + transitions: + Transition { + from: "off"; to: "on"; reversible: false + SequentialAnimation + { + PauseAnimation { duration: 500 } + PropertyAnimation{ targets: card; properties: "x,y,rotation"; duration: 500; easing.type: Easing.OutQuad } + PropertyAnimation{ targets: phone; property: "x"; duration: 500; easing.type: Easing.OutQuart } + PropertyAction { target: shaking; property: "running" } + } + } + + Image { + id: card + source: "qrc:///images/ausweis.png" + height: phone.height + width: height + fillMode: Image.PreserveAspectFit + } + + Image { + id: phone + source: "qrc:///images/phone_nfc.svg" + height: parent.height * 0.5 + width: height + anchors.centerIn: parent + fillMode: Image.PreserveAspectFit + } + Colorize { + id: grayLevel + source: phone + anchors.fill: phone + saturation: 0 + hue: 0 + lightness: 0.3 + cached: true + } +} diff --git a/resources/qml/Governikus/Workflow/+mobile/NfcWorkflow.qml b/resources/qml/Governikus/Workflow/+mobile/NfcWorkflow.qml new file mode 100644 index 0000000..ec57ea8 --- /dev/null +++ b/resources/qml/Governikus/Workflow/+mobile/NfcWorkflow.qml @@ -0,0 +1,70 @@ +import QtQuick 2.10 +import QtQuick.Layouts 1.1 + +import Governikus.Global 1.0 +import Governikus.TechnologyInfo 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.ReaderPlugIn 1.0 +import Governikus.Type.NumberModel 1.0 + + +Item { + id: baseItem + signal requestPluginType(int pReaderPlugInType) + clip: true + + Item { + id: progressIndicatorWrapper + height: 2 * baseItem.height / 5 + anchors.left: parent.left + anchors.right: parent.right + + NfcProgressIndicator { + id: progressIndicator + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + height: baseItem.height / 2 + state: visible && ApplicationModel.nfcAvailable && ApplicationModel.nfcEnabled ? "on" : "off" + } + } + + + TechnologyInfo { + id: technologyInfo + + anchors.left: parent.left + anchors.leftMargin: Utils.dp(5) + anchors.right: parent.right + anchors.rightMargin: anchors.leftMargin + anchors.top: progressIndicatorWrapper.bottom + anchors.bottom: technologySwitch.top + clip: true + + enableButtonVisible: ApplicationModel.nfcAvailable && !ApplicationModel.nfcEnabled + enableButtonText: qsTr("Go to NFC settings") + settingsModel.translationTrigger + onEnableClicked: qmlExtension.showSettings("android.settings.NFC_SETTINGS") + enableText: (!visible ? "" : + !ApplicationModel.nfcAvailable ? qsTr("NFC is not supported by your device.") + "
" + qsTr("Please try Bluetooth.") : + !ApplicationModel.nfcEnabled ? qsTr("NFC is switched off.") + "
" + qsTr("Please enable NFC in your system settings.") : "" + ) + settingsModel.translationTrigger + + titleText: qsTr("Establish connection") + settingsModel.translationTrigger + + subTitleText: (!visible ? "" : + ApplicationModel.extendedLengthApdusUnsupported ? qsTr("Your device does not meet the technical requirements (Extended Length not supported). You require an additional 'Bluetooth card reader' or an additional 'smartphone as card reader' to use the online identification function with this device.") : + NumberModel.pinDeactivated ? qsTr("The online identification function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the online identification function.") : + qsTr("Please place your device
on your ID card.") + ) + settingsModel.translationTrigger + } + + + TechnologySwitch { + id: technologySwitch + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + selectedTechnology: ReaderPlugIn.NFC + onRequestPluginType: parent.requestPluginType(pReaderPlugInType) + } +} diff --git a/resources/qml/Governikus/Workflow/+mobile/ProgressCircle.qml b/resources/qml/Governikus/Workflow/+mobile/ProgressCircle.qml new file mode 100644 index 0000000..030095f --- /dev/null +++ b/resources/qml/Governikus/Workflow/+mobile/ProgressCircle.qml @@ -0,0 +1,99 @@ +import QtQuick 2.10 + +import Governikus.Global 1.0 + +Item { + id: baseItem + height: Utils.dp(70) + readonly property int _stepWidth: Utils.dp(60) + + Rectangle { + id: rec1 + anchors.left: baseItem.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + width: _stepWidth + height: Utils.dp(2) + color: Constants.blue + } + + Rectangle { + id: tCircle1 + anchors.verticalCenter: rec1.verticalCenter + anchors.horizontalCenter: rec1.left + width: state === "active" ? Utils.dp(70) : Utils.dp(25) + border.width + height: width + radius: width / 2 + color: Constants.background_color + border.color: "white" + border.width: state === "active" ? 2 : 0 + + state: "inactive" + states: [ + State { name: "active" + PropertyChanges { target: innerDisc; width: Utils.dp(50) } + }, + State { name: "inactive" + PropertyChanges { target: innerDisc; width: Utils.dp(25) } + } + ] + + transitions: [ + Transition { to: "active" + PropertyAnimation { target: innerDisc; property: "width"; duration: 1500; easing.type: Easing.OutBounce } + } + ] + + Rectangle { + id: innerDisc + anchors.centerIn: parent + color: tCircle1.state === "active" ? Constants.blue : "white" + radius: width / 2 + height: width + border.color: Constants.blue + border.width: 1 + Text { + anchors.centerIn: parent + text: "1" + font.bold: true + font.pixelSize: parent.height / 3 + color: tCircle1.state === "active" ? "white" : Constants.blue + } + } + } + + TextCircle { + id: tCircle2 + anchors.verticalCenter: rec1.verticalCenter + anchors.horizontalCenter: rec1.right + text: "2" + } + + state: "inactive" + states: [ + State { + name: "inactive" + PropertyChanges { target: tCircle1; state: "inactive" } + PropertyChanges { target: tCircle2; state: "inactive" } + PropertyChanges { target: rec1; anchors.leftMargin: 0 } + }, + State { + name: "one" + PropertyChanges { target: tCircle1; state: "active" } + PropertyChanges { target: tCircle2; state: "inactive" } + PropertyChanges { target: rec1; anchors.leftMargin: 0 } + }, + State { + name: "two" + PropertyChanges { target: tCircle1; state: "inactive" } + PropertyChanges { target: tCircle2; state: "active" } + PropertyChanges { target: rec1; anchors.leftMargin: -_stepWidth } + } + ] + + transitions: [ + Transition { + to: "two" + PropertyAnimation { target: rec1; property: "anchors.leftMargin"; duration: 500; easing.type: Easing.InOutCubic } + } + ] +} diff --git a/resources/qml/Governikus/Workflow/+mobile/ProgressIndicator.qml b/resources/qml/Governikus/Workflow/+mobile/ProgressIndicator.qml new file mode 100644 index 0000000..7ace838 --- /dev/null +++ b/resources/qml/Governikus/Workflow/+mobile/ProgressIndicator.qml @@ -0,0 +1,65 @@ +import QtQuick 2.10 +import QtGraphicalEffects 1.0 + +import Governikus.Global 1.0 + + +Item { + id: baseItem + property alias imageIconSource: busyIcon.image + property alias imagePhoneSource: phone.source + + Image { + id: phone + height: parent.height * 0.5 + width: height + anchors.centerIn: parent + fillMode: Image.PreserveAspectFit + visible: baseItem.state === "off" + } + Colorize { + id: grayLevel + source: phone + anchors.fill: phone + saturation: 0 + hue: 0 + lightness: 0.3 + cached: true + visible: baseItem.state === "off" + } + + Item { + id: currentAction + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: pCircle.top + anchors.margins: Constants.component_spacing + + BusyImageIndicator { + id: busyIcon + anchors.centerIn: parent + width: height + height: Math.min(parent.height - Utils.dp(40), 2 * pCircle.height) + running: visible + visible: baseItem.state === "one" + } + + CardReader { + anchors.centerIn: parent + height: parent.height + visible: baseItem.state === "two" + cardAnimation: baseItem.state === "two" + pinFieldAnimation: false + } + } + + ProgressCircle { + id: pCircle + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + state: baseItem.state + visible: baseItem.state === "one" || baseItem.state === "two" + } +} diff --git a/resources/qml/Governikus/Workflow/+mobile/RemoteWorkflow.qml b/resources/qml/Governikus/Workflow/+mobile/RemoteWorkflow.qml new file mode 100644 index 0000000..f4ffaaf --- /dev/null +++ b/resources/qml/Governikus/Workflow/+mobile/RemoteWorkflow.qml @@ -0,0 +1,121 @@ +import QtQuick 2.10 +import QtQuick.Layouts 1.1 + +import Governikus.Global 1.0 +import Governikus.RemoteServiceView 1.0 +import Governikus.TechnologyInfo 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.RemoteServiceModel 1.0 +import Governikus.Type.ReaderPlugIn 1.0 +import Governikus.Type.NumberModel 1.0 + + +Item { + id: baseItem + signal requestPluginType(int pReaderPlugInType) + + property bool settingsPushed: remoteServiceSettings.visible + property bool wifiEnabled: ApplicationModel.wifiEnabled + property bool foundSelectedReader: ApplicationModel.foundSelectedReader + + Connections { + target: ApplicationModel + onFireCertificateRemoved: { + qmlExtension.showFeedback(qsTr("The device %1 was unpaired because it does not react to connection attempts. Retry the pairing process if you want to use this device to authenticate yourself.").arg(pDeviceName)) + } + } + + onFoundSelectedReaderChanged: { + if (baseItem.settingsPushed && foundSelectedReader) { + remoteServiceSettings.firePop() + } + } + + ProgressIndicator { + id: progressIndicator + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + height: parent.height / 2 + imageIconSource: "qrc:///images/icon_remote.svg" + imagePhoneSource: "qrc:///images/phone_remote.svg" + state: foundSelectedReader ? "two" : "off" + } + + TechnologyInfo { + id: techInfo + anchors.left: parent.left + anchors.leftMargin: Utils.dp(5) + anchors.right: parent.right + anchors.rightMargin: anchors.leftMargin + anchors.top: progressIndicator.bottom + anchors.bottom: switchToNfcAction.top + + enableButtonVisible: !wifiEnabled || !foundSelectedReader + enableButtonText: { + settingsModel.translationTrigger + + if (!wifiEnabled) { + return qsTr("Enable Wifi"); + } else if (!foundSelectedReader) { + return qsTr("Pair device"); + } else { + return qsTr("Continue") + } + } + + onEnableClicked: { + if (!wifiEnabled) { + ApplicationModel.enableWifi() + } else if (!baseItem.settingsPushed) { + firePush(remoteServiceSettings) + } + } + enableText: { + settingsModel.translationTrigger + + if (!wifiEnabled) { + return qsTr("To use the remote service WiFi has to be activated. Please activate WiFi in your device settings."); + } else if (!foundSelectedReader) { + return qsTr("No paired and activated remote device was detected. Make sure that you have started remote service on you remote device."); + } else { + return ""; + } + } + + titleText: (foundSelectedReader ? + qsTr("Determine card") : + qsTr("Establish connection") + ) + settingsModel.translationTrigger + + subTitleText: { + settingsModel.translationTrigger + + if (!visible) { + return ""; + } else if (!!NumberModel.inputError) { + return NumberModel.inputError; + } else if (ApplicationModel.extendedLengthApdusUnsupported) { + qsTr("Your remote device does not meet the technical requirements (Extended Length not supported)."); + } else if (NumberModel.pinDeactivated) { + return qsTr("The online identification function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the online identification function."); + } else { + return qsTr("Connected to %1. Please insert your ID card.").arg(RemoteServiceModel.connectedServerDeviceNames); + } + } + } + + TechnologySwitch { + id: switchToNfcAction + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + selectedTechnology: ReaderPlugIn.REMOTE + onRequestPluginType: parent.requestPluginType(pReaderPlugInType) + } + + RemoteServiceSettings { + id: remoteServiceSettings + visible: false + } +} diff --git a/resources/qml/Governikus/Workflow/+mobile/TextCircle.qml b/resources/qml/Governikus/Workflow/+mobile/TextCircle.qml new file mode 100644 index 0000000..3b86cb0 --- /dev/null +++ b/resources/qml/Governikus/Workflow/+mobile/TextCircle.qml @@ -0,0 +1,67 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 + +Item { + property alias text: t.text + height: Utils.dp(50) + width: height + + BusyIndicator { + id: busy + anchors.centerIn: parent + height: rec.height * 1.1 + width: height + running: false + contentItem: NpaBusyIndicatorStyle {} + } + + Rectangle { + id: rec + border.width: 1 + border.color: Constants.blue + color: parent.state === "active" ? Constants.blue : "white" + height: parent.state === "active" ? parent.height : parent.height / 2 + width: height + radius: width * 0.5 + anchors.centerIn: parent + } + + Text { + id: t + anchors.centerIn: rec + font.bold: parent.state === "active" + font.pixelSize: rec.height / 3 + color: parent.state === "active" ? "white" : Constants.blue + } + + state:"inactive" + states: [ + State { name: "active" }, + State { name: "inactive" } + ] + + transitions: [ + Transition { + from: "inactive" + to: "active" + SequentialAnimation { + ParallelAnimation { + NumberAnimation {target: t; property: "font.pixelSize"; duration: 1000; easing.type: Easing.OutElastic} + ColorAnimation {duration: 100} + } + PropertyAnimation {target: busy; property: "running"; to: true} + } + }, + Transition { + from: "active" + to: "inactive" + SequentialAnimation { + PropertyAction {target: busy; property: "running"; value: false} + NumberAnimation {target: t; property: "font.pixelSize"; duration: 200} + } + } + ] +} diff --git a/resources/qml/Governikus/Workflow/+mobile/Workflow.qml b/resources/qml/Governikus/Workflow/+mobile/Workflow.qml new file mode 100644 index 0000000..cc3d0ed --- /dev/null +++ b/resources/qml/Governikus/Workflow/+mobile/Workflow.qml @@ -0,0 +1,10 @@ +import QtQuick 2.10 + +QtObject { + enum WaitingFor { + None, + Card, + Reader, + Password + } +} diff --git a/resources/qml/Governikus/Workflow/BluetoothWorkflow.qml b/resources/qml/Governikus/Workflow/BluetoothWorkflow.qml deleted file mode 100644 index ba7dad2..0000000 --- a/resources/qml/Governikus/Workflow/BluetoothWorkflow.qml +++ /dev/null @@ -1,91 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Layouts 1.1 - -import Governikus.Global 1.0 -import Governikus.TechnologyInfo 1.0 - -Item { - id: baseItem - signal requestPluginType(string pReaderPlugInType) - - property bool locationPermissionInfoConfirmed: false - onLocationPermissionInfoConfirmedChanged: { - if (identifyController) identifyController.locationPermissionConfirmed = locationPermissionInfoConfirmed - if (changePinController) changePinController.locationPermissionConfirmed = locationPermissionInfoConfirmed - } - - readonly property bool showLocationPermissionInfo: applicationModel.locationPermissionRequired && !locationPermissionInfoConfirmed - - onStateChanged: { - if (state === "reader") { - locationPermissionInfoConfirmed = false - } - } - - onShowLocationPermissionInfoChanged: { - if (applicationModel.bluetoothEnabled && !showLocationPermissionInfo) { - numberModel.continueWorkflow() - } - } - - ProgressIndicator { - id: progressIndicator - anchors.left: parent.left - anchors.top: parent.top - anchors.right: parent.right - height: parent.height / 2 - imageIconSource: "qrc:///images/icon_bluetooth.svg" - imagePhoneSource: "qrc:///images/phone_bluetooth.svg" - state: (!applicationModel.bluetoothAvailable || !applicationModel.bluetoothEnabled || parent.showLocationPermissionInfo) ? "off" : - (baseItem.state === "reader") ? "one" : - (baseItem.state === "card") ? "two" : "" - } - - TechnologyInfo { - id: techInfo - anchors.left: parent.left - anchors.leftMargin: Utils.dp(5) - anchors.right: parent.right - anchors.rightMargin: anchors.leftMargin - anchors.top: progressIndicator.bottom - anchors.bottom: technologySwitch.top - state: parent.state - - enableButtonVisible: applicationModel.bluetoothAvailable && (!applicationModel.bluetoothEnabled || parent.showLocationPermissionInfo) - enableButtonText: (!applicationModel.bluetoothEnabled ? qsTr("Enable Bluetooth") : qsTr("Continue")) + settingsModel.translationTrigger - onEnableClicked: { - if (!applicationModel.bluetoothEnabled) { - applicationModel.bluetoothEnabled = true - } else { - parent.locationPermissionInfoConfirmed = true - } - } - enableText: (!visible ? "" : - !applicationModel.bluetoothAvailable ? qsTr("Bluetooth is not supported by your device.") + "
" + qsTr("Please try NFC.") : - !applicationModel.bluetoothEnabled ? qsTr("Bluetooth is switched off.") + "
" + qsTr("Please enable Bluetooth.") : - parent.showLocationPermissionInfo ? qsTr("No paired and activated Bluetooth device was detected. The AusweisApp2 needs access to your location in order to discover available devices. You can grant this permission after clicking the continue button.") : - !applicationModel.bluetoothResponding ? qsTr("An error occured while connecting to your bluetooth device. Try to pair your device in the system settings and restart the app.") : "" - ) + settingsModel.translationTrigger - - titleText: ((baseItem.state === "reader") ? qsTr("Establish connection") : - (baseItem.state === "card") ? qsTr("Determine card") : "" - ) + settingsModel.translationTrigger - - subTitleText: (!visible ? "" : - !!numberModel.inputError ? numberModel.inputError : - (state === "reader") ? - qsTr("Search card reader...") : - (state === "card") ? - qsTr("Please insert your ID card.") : "" - ) + settingsModel.translationTrigger - } - - TechnologySwitch { - id: technologySwitch - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - selectedTechnology: "BLUETOOTH" - onRequestPluginType: parent.requestPluginType(pReaderPlugInType) - } -} diff --git a/resources/qml/Governikus/Workflow/BusyImageIndicator.qml b/resources/qml/Governikus/Workflow/BusyImageIndicator.qml deleted file mode 100644 index e6e875f..0000000 --- a/resources/qml/Governikus/Workflow/BusyImageIndicator.qml +++ /dev/null @@ -1,28 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 - -import Governikus.Style 1.0 - -Item { - property string image - property alias running: busyIndicator.running - - BusyIndicator { - id: busyIndicator - anchors.centerIn: parent - height: img.height - width: height - running: false - style: NpaBusyIndicatorStyle {factor: 1.2} - } - - Image { - id: img - anchors.centerIn: parent - width: parent.width - fillMode: Image.PreserveAspectFit - smooth: true - source: image - } -} diff --git a/resources/qml/Governikus/Workflow/CardReader.qml b/resources/qml/Governikus/Workflow/CardReader.qml deleted file mode 100644 index 85f9efa..0000000 --- a/resources/qml/Governikus/Workflow/CardReader.qml +++ /dev/null @@ -1,213 +0,0 @@ -import QtQuick 2.5 - -import Governikus.Global 1.0 - -Item { - property bool pinFieldAnimation: true - property bool cardAnimation: true - - id: baseItem - width: height * 3 / 7 - - Timer { - property int index - - id: onTimer - interval: 1500 - running: pinFieldAnimation || cardAnimation - triggeredOnStart: true - - onTriggered: { - onTimer.triggeredOnStart = false - if (pinFieldAnimation) - { - onTimer.index = Utils.getRandomInt(0, 9) - repeater.itemAt(onTimer.index).state = "on" - } - - if (cardAnimation) - { - card.state = "out" - } - - offTimer.restart() - } - } - - Timer { - id: offTimer - interval: 500 - onTriggered: { - if (onTimer.index != undefined) - { - repeater.itemAt(onTimer.index).state = "off" - } - - card.state = "in" - - onTimer.restart() - } - } - - - - Rectangle { - id: reader - color: "#92cef9" - radius: height * 0.05 - anchors.bottom: parent.bottom - width: parent.width - height: parent.height * 5 / 7 - - Rectangle { - id: slot - color: "white" - radius: 10 - height: reader.height * 0.05 - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - anchors.topMargin: parent.height * 0.05 - anchors.leftMargin: parent.width * 0.2 - anchors.rightMargin: parent.width * 0.2 - } - - Rectangle { - id: card - color: Constants.blue - radius: height * 0.05 - anchors.horizontalCenter: parent.horizontalCenter - height: baseItem.height * 1.5 / 7 - width: baseItem.width * 0.5 - - onVisibleChanged: y = reader.mapFromItem(pinView, pinView.x, card.y).y - - Rectangle { - id: cardStripe1 - color: "white" - radius: 10 - height: parent.height * 0.1 - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - anchors.topMargin: parent.height * 0.1 - anchors.leftMargin: parent.width * 0.1 - anchors.rightMargin: parent.width * 0.1 - } - - Rectangle { - id: cardStripe2 - color: "white" - radius: cardStripe1.radius - height: parent.height * 0.1 - anchors.top: cardStripe1.top - anchors.left: parent.left - anchors.right: parent.right - anchors.topMargin: parent.height * 0.2 - anchors.leftMargin: parent.width * 0.1 - anchors.rightMargin: parent.width * 0.1 - } - - MouseArea { - anchors.fill: parent - onClicked: card.state = card.state === "out" ? "in" : "out" - } - - states: [ - State { - name: "out" - AnchorChanges {target: card; anchors.bottom: reader.top} - }, - State { - name: "in" - AnchorChanges {target: card; anchors.bottom: slot.verticalCenter} - } - ] - - transitions: [ - Transition { - AnchorAnimation {duration: 500} - } - ] - } - - Rectangle { - readonly property int margin: parent.width * 0.1 - id: display - color: "white" - radius: height * 0.2 - height: reader.height * 0.2 - anchors.bottom: pinGrid.top - anchors.margins: margin - anchors.left: parent.left - anchors.right: parent.right - } - - Grid { - id: pinGrid - anchors.bottom: reader.bottom - anchors.left: parent.left - anchors.right: parent.right - anchors.margins: display.margin - - columns: 3 - spacing: anchors.margins - height: width - - Repeater { - id: repeater - model: 9 - - Item { - readonly property int _size: (reader.width - 4 * pinGrid.spacing) / 3 - property alias state: pinButtonCircle.state - - id: pinButton - width: _size - height: width - - Rectangle { - id: pinButtonCircle - anchors.centerIn: parent - width: pinButton._size - height: width - radius: width * 0.5 - - Behavior on color { - ColorAnimation { - duration: 250 - } - } - - Behavior on width { - NumberAnimation { - duration: 1000 - easing.type: Easing.OutElastic - } - } - - MouseArea { - anchors.fill: parent - onClicked: pinButton.state = pinButton.state === "off" ? "on" : "off" - } - - state: "off" - - states: [ - State { - name: "off" - PropertyChanges {target: pinButtonCircle; color: "white"} - PropertyChanges {target: pinButtonCircle; width: pinButton._size} - }, - State { - name: "on" - PropertyChanges {target: pinButtonCircle; color: Constants.blue} - PropertyChanges {target: pinButtonCircle; width: pinButton._size + pinButton._size * 0.3} - } - ] - } - } - } - } - } -} diff --git a/resources/qml/Governikus/Workflow/NfcProgressIndicator.qml b/resources/qml/Governikus/Workflow/NfcProgressIndicator.qml deleted file mode 100644 index 6b88e58..0000000 --- a/resources/qml/Governikus/Workflow/NfcProgressIndicator.qml +++ /dev/null @@ -1,92 +0,0 @@ -import QtQuick 2.5 -import QtGraphicalEffects 1.0 - -import Governikus.Global 1.0 - - -Item { - id: baseItem - - SequentialAnimation { - id: shaking - loops: Animation.Infinite - readonly property int delta: Utils.dp(4) - readonly property int deltaDuration: 300 - - ParallelAnimation { - SequentialAnimation { - PropertyAnimation { target: phone.anchors; property: "horizontalCenterOffset"; to: -shaking.delta; duration: shaking.deltaDuration } - PropertyAnimation { target: phone.anchors; property: "horizontalCenterOffset"; to: shaking.delta; duration: 2*shaking.deltaDuration } - PropertyAnimation { target: phone.anchors; property: "horizontalCenterOffset"; to: 0; duration: shaking.deltaDuration } - } - SequentialAnimation { - PropertyAnimation { target: phone.anchors; property: "verticalCenterOffset"; to: -shaking.delta; duration: 2*shaking.deltaDuration } - PropertyAnimation { target: phone.anchors; property: "verticalCenterOffset"; to: 0; duration: 2*shaking.deltaDuration } - } - } - ParallelAnimation { - SequentialAnimation { - PropertyAnimation { target: phone.anchors; property: "horizontalCenterOffset"; to: -shaking.delta; duration: shaking.deltaDuration } - PropertyAnimation { target: phone.anchors; property: "horizontalCenterOffset"; to: shaking.delta; duration: 2*shaking.deltaDuration } - PropertyAnimation { target: phone.anchors; property: "horizontalCenterOffset"; to: 0; duration: shaking.deltaDuration } - } - SequentialAnimation { - PropertyAnimation { target: phone.anchors; property: "verticalCenterOffset"; to: shaking.delta; duration: 2*shaking.deltaDuration } - PropertyAnimation { target: phone.anchors; property: "verticalCenterOffset"; to: 0; duration: 2*shaking.deltaDuration } - } - } - } - - states: [ - State { - name: "off" - PropertyChanges { target: grayLevel; visible: true } - PropertyChanges { target: card; x: baseItem.width; y: baseItem.height / 2; rotation: -180 } - PropertyChanges { target: shaking; running: false } - }, - State { - name: "on" - PropertyChanges { target: grayLevel; visible: false } - PropertyChanges { target: card; x: (baseItem.width + phone.width - card.width)/2; y: baseItem.height / 4; rotation: 0 } - PropertyChanges { target: shaking; running: true } - } - ] - - transitions: - Transition { - from: "off"; to: "on"; reversible: false - SequentialAnimation - { - PauseAnimation { duration: 500 } - PropertyAnimation{ targets: card; properties: "x,y,rotation"; duration: 500; easing.type: Easing.OutQuad } - PropertyAnimation{ targets: phone; property: "x"; duration: 500; easing.type: Easing.OutQuart } - PropertyAction { target: shaking; property: "running" } - } - } - - Image { - id: card - source: "qrc:///images/ausweis.png" - height: phone.height - width: height - fillMode: Image.PreserveAspectFit - } - - Image { - id: phone - source: "qrc:///images/phone_nfc.svg" - height: parent.height * 0.5 - width: height - anchors.centerIn: parent - fillMode: Image.PreserveAspectFit - } - Colorize { - id: grayLevel - source: phone - anchors.fill: phone - saturation: 0 - hue: 0 - lightness: 0.3 - cached: true - } -} diff --git a/resources/qml/Governikus/Workflow/NfcWorkflow.qml b/resources/qml/Governikus/Workflow/NfcWorkflow.qml deleted file mode 100644 index acd9d54..0000000 --- a/resources/qml/Governikus/Workflow/NfcWorkflow.qml +++ /dev/null @@ -1,57 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Layouts 1.1 - -import Governikus.Global 1.0 -import Governikus.TechnologyInfo 1.0 - -Item { - id: baseItem - signal requestPluginType(string pReaderPlugInType) - clip: true - - NfcProgressIndicator { - id: progressIndicator - anchors.left: parent.left - anchors.right: parent.right - height: parent.height / 2 - state: visible && applicationModel.nfcAvailable && applicationModel.nfcEnabled ? "on" : "off" - } - - - TechnologyInfo { - anchors.left: parent.left - anchors.leftMargin: Utils.dp(5) - anchors.right: parent.right - anchors.rightMargin: anchors.leftMargin - anchors.top: progressIndicator.bottom - anchors.bottom: technologySwitch.top - clip: true - state: parent.state - - enableButtonVisible: applicationModel.nfcAvailable && !applicationModel.nfcEnabled - enableButtonText: qsTr("Go to NFC settings") + settingsModel.translationTrigger - onEnableClicked: qmlExtension.showSettings("android.settings.NFC_SETTINGS") - enableText: (!visible ? "" : - !applicationModel.nfcAvailable ? qsTr("NFC is not supported by your device.") + "
" + qsTr("Please try Bluetooth.") : - !applicationModel.nfcEnabled ? qsTr("NFC is switched off.") + "
" + qsTr("Please enable NFC in your system settings.") : "" - ) + settingsModel.translationTrigger - - titleText: qsTr("Establish connection") + settingsModel.translationTrigger - - subTitleText: (!visible ? "" : - numberModel.extendedLengthApdusUnsupported ? qsTr("Your device does not meet the technical requirements (Extended Length not supported). You require an additional 'Bluetooth card reader' or an additional 'smartphone as card reader' to use the online identification function with this device.") : - numberModel.pinDeactivated ? qsTr("The online identification function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the online identification function.") : - qsTr("Please place your device
on your ID card.") - ) + settingsModel.translationTrigger - } - - - TechnologySwitch { - id: technologySwitch - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - selectedTechnology: "NFC" - onRequestPluginType: parent.requestPluginType(pReaderPlugInType) - } -} diff --git a/resources/qml/Governikus/Workflow/ProgressCircle.qml b/resources/qml/Governikus/Workflow/ProgressCircle.qml deleted file mode 100644 index 6296868..0000000 --- a/resources/qml/Governikus/Workflow/ProgressCircle.qml +++ /dev/null @@ -1,100 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 - -import Governikus.Global 1.0 - -Item { - id: baseItem - height: Utils.dp(70) - readonly property int _stepWidth: Utils.dp(60) - - Rectangle { - id: rec1 - anchors.left: baseItem.horizontalCenter - anchors.verticalCenter: parent.verticalCenter - width: _stepWidth - height: Utils.dp(2) - color: Constants.blue - } - - Rectangle { - id: tCircle1 - anchors.verticalCenter: rec1.verticalCenter - anchors.horizontalCenter: rec1.left - width: state === "active" ? Utils.dp(70) : Utils.dp(25) + border.width - height: width - radius: width / 2 - color: Constants.background_color - border.color: "white" - border.width: state === "active" ? 2 : 0 - - state: "inactive" - states: [ - State { name: "active" - PropertyChanges { target: innerDisc; width: Utils.dp(50) } - }, - State { name: "inactive" - PropertyChanges { target: innerDisc; width: Utils.dp(25) } - } - ] - - transitions: [ - Transition { to: "active" - PropertyAnimation { target: innerDisc; property: "width"; duration: 1500; easing.type: Easing.OutBounce } - } - ] - - Rectangle { - id: innerDisc - anchors.centerIn: parent - color: tCircle1.state === "active" ? Constants.blue : "white" - radius: width / 2 - height: width - border.color: Constants.blue - border.width: 1 - Text { - anchors.centerIn: parent - text: "1" - font.bold: true - font.pixelSize: parent.height / 3 - color: tCircle1.state === "active" ? "white" : Constants.blue - } - } - } - - TextCircle { - id: tCircle2 - anchors.verticalCenter: rec1.verticalCenter - anchors.horizontalCenter: rec1.right - text: "2" - } - - state: "inactive" - states: [ - State { - name: "inactive" - PropertyChanges { target: tCircle1; state: "inactive" } - PropertyChanges { target: tCircle2; state: "inactive" } - PropertyChanges { target: rec1; anchors.leftMargin: 0 } - }, - State { - name: "one" - PropertyChanges { target: tCircle1; state: "active" } - PropertyChanges { target: tCircle2; state: "inactive" } - PropertyChanges { target: rec1; anchors.leftMargin: 0 } - }, - State { - name: "two" - PropertyChanges { target: tCircle1; state: "inactive" } - PropertyChanges { target: tCircle2; state: "active" } - PropertyChanges { target: rec1; anchors.leftMargin: -_stepWidth } - } - ] - - transitions: [ - Transition { - to: "two" - PropertyAnimation { target: rec1; property: "anchors.leftMargin"; duration: 500; easing.type: Easing.InOutCubic } - } - ] -} diff --git a/resources/qml/Governikus/Workflow/ProgressIndicator.qml b/resources/qml/Governikus/Workflow/ProgressIndicator.qml deleted file mode 100644 index a400555..0000000 --- a/resources/qml/Governikus/Workflow/ProgressIndicator.qml +++ /dev/null @@ -1,65 +0,0 @@ -import QtQuick 2.5 -import QtGraphicalEffects 1.0 - -import Governikus.Global 1.0 - - -Item { - id: baseItem - property alias imageIconSource: busyIcon.image - property alias imagePhoneSource: phone.source - - Image { - id: phone - height: parent.height * 0.5 - width: height - anchors.centerIn: parent - fillMode: Image.PreserveAspectFit - visible: baseItem.state === "off" - } - Colorize { - id: grayLevel - source: phone - anchors.fill: phone - saturation: 0 - hue: 0 - lightness: 0.3 - cached: true - visible: baseItem.state === "off" - } - - Item { - id: currentAction - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - anchors.bottom: pCircle.top - anchors.margins: Constants.component_spacing - - BusyImageIndicator { - id: busyIcon - anchors.centerIn: parent - width: height - height: parent.height - Utils.dp(40) - running: visible - visible: baseItem.state === "one" - } - - CardReader { - anchors.centerIn: parent - height: parent.height - visible: baseItem.state === "two" - cardAnimation: baseItem.state === "two" - pinFieldAnimation: false - } - } - - ProgressCircle { - id: pCircle - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.right: parent.right - state: baseItem.state - visible: baseItem.state === "one" || baseItem.state === "two" - } -} diff --git a/resources/qml/Governikus/Workflow/RemoteWorkflow.qml b/resources/qml/Governikus/Workflow/RemoteWorkflow.qml deleted file mode 100644 index c7b7258..0000000 --- a/resources/qml/Governikus/Workflow/RemoteWorkflow.qml +++ /dev/null @@ -1,117 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Layouts 1.1 - -import Governikus.Global 1.0 -import Governikus.RemoteServiceView 1.0 -import Governikus.TechnologyInfo 1.0 - -Item { - id: baseItem - signal requestPluginType(string pReaderPlugInType) - - property bool settingsPushed: remoteServiceSettings.visible - property bool wifiEnabled: applicationModel.wifiEnabled - property bool foundSelectedReader: applicationModel.foundSelectedReader - - Connections { - target: applicationModel - onFireCertificateRemoved: { - qmlExtension.showFeedback(qsTr("The device %1 was unpaired because it does not react to connection attempts. Retry the pairing process if you want to use this device to authenticate yourself.").arg(pDeviceName)) - } - } - - onFoundSelectedReaderChanged: { - if (baseItem.settingsPushed && foundSelectedReader) { - remoteServiceSettings.firePop() - } - } - - ProgressIndicator { - id: progressIndicator - anchors.left: parent.left - anchors.top: parent.top - anchors.right: parent.right - height: parent.height / 2 - imageIconSource: "qrc:///images/icon_remote.svg" - imagePhoneSource: "qrc:///images/phone_remote.svg" - state: foundSelectedReader ? "two" : "one" - } - - TechnologyInfo { - id: techInfo - anchors.left: parent.left - anchors.leftMargin: Utils.dp(5) - anchors.right: parent.right - anchors.rightMargin: anchors.leftMargin - anchors.top: progressIndicator.bottom - anchors.bottom: switchToNfcAction.top - state: parent.state - - enableButtonVisible: !wifiEnabled || !foundSelectedReader - enableButtonText: { - settingsModel.translationTrigger - - if (!wifiEnabled) { - return qsTr("Enable Wifi"); - } else if (!foundSelectedReader) { - return qsTr("Pair device"); - } else { - return qsTr("Continue") - } - } - - onEnableClicked: { - if (!wifiEnabled) { - applicationModel.enableWifi() - } else if (!baseItem.settingsPushed) { - firePush(remoteServiceSettings, {}) - } - } - enableText: { - settingsModel.translationTrigger - - if (!wifiEnabled) { - return qsTr("To use the remote service WiFi has to be activated. Please activate WiFi in your device settings."); - } else if (!foundSelectedReader) { - return qsTr("No paired and activated remote device was detected. Make sure that you have started remote service on you remote device."); - } else { - return ""; - } - } - - titleText: (foundSelectedReader ? - qsTr("Determine card") : - qsTr("Establish connection") - ) + settingsModel.translationTrigger - - subTitleText: { - settingsModel.translationTrigger - - if (!visible) { - return ""; - } else if (!!numberModel.inputError) { - return numberModel.inputError; - } else if (numberModel.extendedLengthApdusUnsupported) { - qsTr("Your remote device does not meet the technical requirements (Extended Length not supported)."); - } else if (numberModel.pinDeactivated) { - qsTr("The online identification function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the online identification function."); - } else { - return qsTr("Connected to %1. Please insert your ID card.").arg(remoteServiceModel.connectedServerDeviceNames); - } - } - } - - TechnologySwitch { - id: switchToNfcAction - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - selectedTechnology: "REMOTE" - onRequestPluginType: parent.requestPluginType(pReaderPlugInType) - } - - RemoteServiceSettings { - id: remoteServiceSettings - visible: false - } -} diff --git a/resources/qml/Governikus/Workflow/TextCircle.qml b/resources/qml/Governikus/Workflow/TextCircle.qml deleted file mode 100644 index 5c40837..0000000 --- a/resources/qml/Governikus/Workflow/TextCircle.qml +++ /dev/null @@ -1,68 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 - -import Governikus.Global 1.0 -import Governikus.Style 1.0 - -Item { - property alias text: t.text - height: Utils.dp(50) - width: height - - BusyIndicator { - id: busy - anchors.centerIn: parent - height: rec.height * 1.1 - width: height - running: false - style: NpaBusyIndicatorStyle {} - } - - Rectangle { - id: rec - border.width: 1 - border.color: Constants.blue - color: parent.state === "active" ? Constants.blue : "white" - height: parent.state === "active" ? parent.height : parent.height / 2 - width: height - radius: width * 0.5 - anchors.centerIn: parent - } - - Text { - id: t - anchors.centerIn: rec - font.bold: parent.state === "active" - font.pixelSize: rec.height / 3 - color: parent.state === "active" ? "white" : Constants.blue - } - - state:"inactive" - states: [ - State { name: "active" }, - State { name: "inactive" } - ] - - transitions: [ - Transition { - from: "inactive" - to: "active" - SequentialAnimation { - ParallelAnimation { - NumberAnimation {target: t; property: "font.pixelSize"; duration: 1000; easing.type: Easing.OutElastic} - ColorAnimation {duration: 100} - } - PropertyAnimation {target: busy; property: "running"; to: true} - } - }, - Transition { - from: "active" - to: "inactive" - SequentialAnimation { - PropertyAction {target: busy; property: "running"; value: false} - NumberAnimation {target: t; property: "font.pixelSize"; duration: 200} - } - } - ] -} diff --git a/resources/qml/Governikus/Workflow/qmldir b/resources/qml/Governikus/Workflow/qmldir index 83cd0f9..07e5325 100644 --- a/resources/qml/Governikus/Workflow/qmldir +++ b/resources/qml/Governikus/Workflow/qmldir @@ -1,4 +1,14 @@ module Workflow -NfcWorkflow 1.0 NfcWorkflow.qml -RemoteWorkflow 1.0 RemoteWorkflow.qml -BluetoothWorkflow 1.0 BluetoothWorkflow.qml + +internal BluetoothWorkflow BluetoothWorkflow.qml +internal BusyImageIndicator BusyImageIndicator.qml +internal CardReader CardReader.qml +internal NfcProgressIndicator NfcProgressIndicator.qml +internal NfcWorkflow NfcWorkflow.qml +internal ProgressCircle ProgressCircle.qml +internal ProgressIndicator ProgressIndicator.qml +internal RemoteWorkflow RemoteWorkflow.qml +internal TextCircle TextCircle.qml + +Workflow 1.0 Workflow.qml +GeneralWorkflow 1.0 GeneralWorkflow.qml diff --git a/resources/qml/TabBarView.qml b/resources/qml/TabBarView.qml deleted file mode 100644 index abc744e..0000000 --- a/resources/qml/TabBarView.qml +++ /dev/null @@ -1,77 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Layouts 1.2 - -import Governikus.Global 1.0 - -Item { - id: baseItem - - property Component sourceComponent - readonly property bool ready: loader.status === Loader.Ready - property bool prefetch: false - readonly property var currentSectionPage: stack.currentItem - - property alias stack: stack - - // Item provided by loader has been pushed - property bool pushed: false - - // Items that should be pushed after the main item has been loaded and pushed - property var pendingItems: [] - - Loader { - id: loader - asynchronous: true - onLoaded: { - baseItem.pushed = true - item.topLevelPage = true - push(item) - - for (var i = 0; i < baseItem.pendingItems.length; i++) { - var pendingItem = baseItem.pendingItems[i] - push(pendingItem["item"], pendingItem["properties"]) - } - - baseItem.pendingItems = [] - } - - sourceComponent: parent.visible || baseItem.prefetch ? parent.sourceComponent : loader.sourceComponent - } - - StackView { - id: stack - anchors.fill: parent - } - - function push(sectionPage, properties) { - var dep = stack.depth - if (dep !== 0 && stack.get(dep - 1) === sectionPage) { - return - } - - if (baseItem.pushed) { - sectionPage.firePush.connect(baseItem.push) - sectionPage.firePop.connect(baseItem.pop) - sectionPage.firePopAll.connect(baseItem.popAll) - stack.push(sectionPage, properties) - } - // Main item has not been loaded yet, delay push. - else { - baseItem.pendingItems.push({ "item": sectionPage, "properties": properties }) - } - } - - function pop() { - var sectionPage = stack.pop() - sectionPage.firePush.disconnect(baseItem.push) - sectionPage.firePop.disconnect(baseItem.pop) - sectionPage.firePopAll.disconnect(baseItem.popAll) - } - - function popAll() { - while (stack.depth > 1) { - pop() - } - } -} diff --git a/resources/qml/main.qml b/resources/qml/main.qml index 5e36784..9ec7fb7 100644 --- a/resources/qml/main.qml +++ b/resources/qml/main.qml @@ -1,124 +1,4 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 as QtQuickControls14 -import QtQuick.Controls 2.0 -import QtQuick.Window 2.2 -import QtQuick.Dialogs 1.2 +import "main.qml" -import Governikus.Global 1.0 -import Governikus.TitleBar 1.0 -import Governikus.Navigation 1.0 -import Governikus.SplashScreen 1.0 - -ApplicationWindow { - id: appWindow - visible: true - width: 750 / 2 //Screen.desktopAvailableWidth - height: 1334 / 2 - title: "Governikus AusweisApp2" - color: Constants.background_color - - property var lastCloseInvocation: 0 - property int leftOverlayMargin: 0 - - - QtQuickControls14.Action { - shortcut: "Ctrl+Alt+R" - onTriggered: plugin.developerBuild ? plugin.doRefresh() : "" - } - - header: TitleBar { - id: titleBar - visible: !splashScreen.visible - - titleBarOpacity: contentArea.visibleItem && contentArea.visibleItem.stack.currentItem ? contentArea.visibleItem.stack.currentItem.titleBarOpacity : 1 - - property var currentSectionPage: if (contentArea) contentArea.currentSectionPage - - leftAction: if (currentSectionPage) currentSectionPage.leftTitleBarAction - titleItem: if (currentSectionPage) currentSectionPage.headerTitleBarAction - rightAction: if (currentSectionPage) currentSectionPage.rightTitleBarAction - subTitleBarAction: if (currentSectionPage) currentSectionPage.subTitleBarAction - color: if (currentSectionPage) currentSectionPage.titleBarColor - - - } - - overlay.modal: Item { - anchors.fill: parent - - Rectangle { - anchors.left: parent.left - anchors.leftMargin: appWindow.leftOverlayMargin - anchors.top: parent.top - anchors.topMargin: Constants.titlebar_height - height: appWindow.height - Constants.titlebar_height - width: appWindow.width - anchors.leftMargin - color: "black" - opacity: parent.opacity * 0.5 - } - } - - ContentAreaLoader { - id: contentArea - state: navBar.state - anchors.left: PlatformConstants.leftNavigation ? navBar.right : parent.left - anchors.top: parent.top - anchors.right: parent.right - anchors.bottom: PlatformConstants.bottomNavigation ? navBar.top : parent.bottom - } - - Navigation { - id: navBar - visible: !splashScreen.visible - anchors.left: parent.left - anchors.top: PlatformConstants.leftNavigation ? parent.top : undefined - anchors.right: PlatformConstants.bottomNavigation ? parent.right : undefined - anchors.bottom: parent.bottom - } - - onClosing: { - if (contentArea.visibleItem) - { - var activeStackView = contentArea.visibleItem.stack - - if (activeStackView.depth <= 1 && (!activeStackView.currentItem.leftTitleBarAction || activeStackView.currentItem.leftTitleBarAction.state === "")) { - if (contentArea.state != "identify") { - navBar.state = "identify" - navBar.currentIndex = 0 - } else { - var currentTime = new Date().getTime(); - if( currentTime - lastCloseInvocation < 1000 ) { - plugin.fireQuitApplicationRequest() - return - } - - lastCloseInvocation = currentTime - qmlExtension.showFeedback(qsTr("To close the app, quickly press the back button twice.")) - } - } - else if (activeStackView.currentItem.leftTitleBarAction) { - if (navBar.isOpen) { - navBar.close() - } - else { - activeStackView.currentItem.leftTitleBarAction.clicked(undefined) - } - } - } - - close.accepted = false - } - - SplashScreen { - id: splashScreen - color: appWindow.color - - property alias ready: contentArea.ready - onReadyChanged: { - splashScreen.hide() - if (!applicationModel.currentWorkflow) { - navBar.lockedAndHidden = false - } - } - } +main { } diff --git a/resources/qml_stationary/AusweisApp2/Global/Category.js b/resources/qml_stationary/AusweisApp2/Global/Category.js deleted file mode 100644 index db0071c..0000000 --- a/resources/qml_stationary/AusweisApp2/Global/Category.js +++ /dev/null @@ -1,86 +0,0 @@ -var CATEGORY_COLOR_ALL = "#659bcd" -var CATEGORY_COLOR_CITIZEN = "#aa4079" -var CATEGORY_COLOR_INSURANCE = "#52539f" -var CATEGORY_COLOR_FINANCE = "#ecc758" -var CATEGORY_COLOR_OTHER = "#00868e" - -function displayString(cat) { - if (cat === "all") { - return qsTr("Provider") + settingsModel.translationTrigger - } - if (cat === "citizen") { - return qsTr("Citizen services") + settingsModel.translationTrigger - } - if (cat === "insurance") { - return qsTr("Insurances") + settingsModel.translationTrigger - } - if (cat === "finance") { - return qsTr("Financials") + settingsModel.translationTrigger - } - return qsTr("Other services") + settingsModel.translationTrigger -} - -function displayColor(cat) { - if (cat === "all") { - return CATEGORY_COLOR_ALL - } - if (cat === "citizen") { - return CATEGORY_COLOR_CITIZEN - } - if (cat === "insurance") { - return CATEGORY_COLOR_INSURANCE - } - if (cat === "finance") { - return CATEGORY_COLOR_FINANCE - } - return CATEGORY_COLOR_OTHER -} - - -var CATEGORY_TO_IMAGE_NAME = { - "all": "general", - "citizen": "citizen", - "insurance": "insurance", - "finance": "finance" -} - - -function imageName(cat) { - return cat in CATEGORY_TO_IMAGE_NAME ? CATEGORY_TO_IMAGE_NAME[cat] : "OtherServices" -} - - -function getPlatform() { -// return plugin.platformStyle.indexOf("android") !== -1 ? "+android/" : "" - return "+android/" -} - - -function gradientImageSource(cat) { - if (cat !== "citizen" && cat !== "insurance" && cat !== "finance") { - return "qrc:///images/provider/gradient-other.png" - } - else { - return "qrc:///images/provider/gradient-" + cat + ".png" - } -} - - -function backgroundImageSource(cat) { - return "qrc:///images/provider/categoryIcons/" + getPlatform() + imageName(cat) + "_bg.svg" -} - - -function buttonImageSource(cat) { - return "qrc:///images/provider/categoryIcons/" + getPlatform() + imageName(cat) + "_button.svg" -} - - -function imageSource(cat) { - return "qrc:///images/provider/categoryIcons/" + getPlatform() + imageName(cat) + ".svg" -} - - -function sectionImageSource(cat) { - return "qrc:///images/provider/categoryIcons/" + getPlatform() + imageName(cat) + "_section.svg" -} diff --git a/resources/qml_stationary/AusweisApp2/Global/CheckBox.qml b/resources/qml_stationary/AusweisApp2/Global/CheckBox.qml deleted file mode 100644 index 0b290bd..0000000 --- a/resources/qml_stationary/AusweisApp2/Global/CheckBox.qml +++ /dev/null @@ -1,12 +0,0 @@ -import QtQuick 2.5 - -import "." - -Image { - property bool checked - - source: checked ? "qrc:///images/iOS/CheckedCheckbox.png" : "" - height: Utils.dp(20) - width: height - fillMode: Image.PreserveAspectFit -} diff --git a/resources/qml_stationary/AusweisApp2/Global/Constants.qml b/resources/qml_stationary/AusweisApp2/Global/Constants.qml deleted file mode 100644 index b550b22..0000000 --- a/resources/qml_stationary/AusweisApp2/Global/Constants.qml +++ /dev/null @@ -1,49 +0,0 @@ -pragma Singleton - -import QtQuick 2.5 - -import "." - -Item { - readonly property color all_image_background_color: PlatformConstants.all_image_background_color - readonly property bool use_history_list_delete_area: PlatformConstants.use_history_list_delete_area - - readonly property color background_color: "#dcebf6" - readonly property color blue: PlatformConstants.blue - readonly property color blue_dark: PlatformConstants.blue_dark - readonly property color blue_light: PlatformConstants.blue_light - readonly property color green: "#a3cb7f" - readonly property color red: "#cc0000" - readonly property color grey: "#8e8e93" - - readonly property color primary_text: PlatformConstants.primary_text - readonly property color secondary_text: PlatformConstants.secondary_text - readonly property color accent_color: PlatformConstants.accent_color - readonly property color second_accent_color: PlatformConstants.second_accent_color - - readonly property int header_font_size: Utils.sp(40) - readonly property int normal_font_size: Utils.sp(29) - readonly property int label_font_size: Utils.sp(25) - readonly property int small_font_size: Utils.sp(22) - - readonly property int titlebar_height: Utils.dp(48) - readonly property int titlebar_font_size: PlatformConstants.titlebar_font_size - - readonly property int menubar_width: Utils.dp(60) - - readonly property int searchbar_height: Utils.dp(48) - - readonly property int provider_section_height: PlatformConstants.provider_section_height - - readonly property int history_section_height: PlatformConstants.history_section_height - readonly property int history_delegate_spacing: PlatformConstants.history_delegate_spacing - readonly property color history_delegate_address_color: PlatformConstants.history_delegate_address_color - - readonly property int button_height: PlatformConstants.button_height - - readonly property int tabbar_height: Utils.dp(48) - - readonly property int component_spacing: Utils.dp(20) - readonly property int pane_padding: Utils.dp(20) - readonly property int pane_spacing: Utils.dp(20) -} diff --git a/resources/qml_stationary/AusweisApp2/Global/Pane.qml b/resources/qml_stationary/AusweisApp2/Global/Pane.qml deleted file mode 100644 index 1c7706b..0000000 --- a/resources/qml_stationary/AusweisApp2/Global/Pane.qml +++ /dev/null @@ -1,50 +0,0 @@ -import QtQuick 2.5 -import QtGraphicalEffects 1.0 - -import "." - - -Rectangle { - id: root - property alias title: titleText.text - default property alias paneChildren: paneContent.children - - anchors.left: parent.left - anchors.right: parent.right - height: childrenRect.height - color: "white" - radius: 16 - - Column { - anchors.left: parent.left - anchors.right: parent.right - anchors.leftMargin: Constants.pane_padding - anchors.rightMargin: Constants.pane_padding - - Text { - id: titleText - height: implicitHeight * 2 - width: parent.width - visible: text !== "" - verticalAlignment: Text.AlignVCenter - font.pixelSize: Constants.header_font_size - color: Constants.blue - } - Item { width: parent.width; height: Constants.pane_padding } - Column { - id: paneContent - width: parent.width - spacing: Constants.pane_spacing - } - Item { width: parent.width; height: Constants.pane_padding } - } - - layer.enabled: true - layer.effect: DropShadow { - radius: 8 - samples: 8 - source: root - color: Constants.grey - verticalOffset: 2 - } -} diff --git a/resources/qml_stationary/AusweisApp2/Global/PlatformConstants.qml b/resources/qml_stationary/AusweisApp2/Global/PlatformConstants.qml deleted file mode 100644 index 9295d03..0000000 --- a/resources/qml_stationary/AusweisApp2/Global/PlatformConstants.qml +++ /dev/null @@ -1,37 +0,0 @@ -pragma Singleton - -import QtQuick 2.5 - -import "Utils.js" as Utils - -Item { - // Android-specific constants - readonly property color blue_primary: "#659bcd" - readonly property color blue_primary_dark: "#324d66" - readonly property color lila_accent_color: "#7879b2" - readonly property color green_second_accent_color: "#a3cb7f" - readonly property color white_primary_text: "#ffffff" - readonly property color dark_grey_secondary_text: "#444444" - readonly property color grey_light: "#bbbbbb" - readonly property color grey_border: "lightslategrey" - - readonly property color all_image_background_color: "#dcebf6" - readonly property color blue: blue_primary - readonly property color blue_dark: blue_primary_dark - readonly property color blue_light: blue_primary - readonly property color primary_text: white_primary_text - readonly property color secondary_text: dark_grey_secondary_text - readonly property color accent_color: lila_accent_color - readonly property color second_accent_color: green_second_accent_color - readonly property int titlebar_font_size: Utils.sp(18) - readonly property int provider_section_height: Utils.dp(62) - readonly property int history_section_height: Utils.dp(120) - readonly property int history_delegate_spacing: Utils.dp(10) - readonly property color history_delegate_address_color: lila_accent_color - readonly property int button_height: Utils.dp(36) - readonly property bool use_history_list_delete_area: false - - readonly property bool is_tablet: true - readonly property bool leftNavigation: true - readonly property bool bottomNavigation: false -} diff --git a/resources/qml_stationary/AusweisApp2/Global/ProviderModelItem.qml b/resources/qml_stationary/AusweisApp2/Global/ProviderModelItem.qml deleted file mode 100644 index ac8c99a..0000000 --- a/resources/qml_stationary/AusweisApp2/Global/ProviderModelItem.qml +++ /dev/null @@ -1,92 +0,0 @@ -import QtQuick 2.7 - - -/* - * Convenience utility to access properties of a ProviderModel item - * This ensures having always a defined string, i.e. a non-null string object. - */ -Item { - id: baseItem - property var modelItem - - readonly property string category: !!modelItem && !!modelItem.providerCategory ? modelItem.providerCategory : "" - readonly property string shortName: !!modelItem && !!modelItem.providerShortName ? modelItem.providerShortName : "" - readonly property string longName: !!modelItem && !!modelItem.providerLongName ? modelItem.providerLongName : "" - readonly property string shortDescription: !!modelItem && !!modelItem.providerShortDescription ? modelItem.providerShortDescription : "" - readonly property string longDescription: !!modelItem && !!modelItem.providerLongDescription ? modelItem.providerLongDescription : "" - readonly property string address: !!modelItem && !!modelItem.providerAddress ? modelItem.providerAddress : "" - readonly property string addressDomain: !!modelItem && !!modelItem.providerAddressDomain ? modelItem.providerAddressDomain : "" - readonly property string homepage: !!modelItem && !!modelItem.providerHomepage ? modelItem.providerHomepage : "" - readonly property string homepageBase: !!modelItem && !!modelItem.providerHomepageBase ? modelItem.providerHomepageBase : "" - readonly property string phone: !!modelItem && !!modelItem.providerPhone ? modelItem.providerPhone : "" - readonly property string phoneCost: !!modelItem && !!modelItem.providerPhoneCost ? modelItem.providerPhoneCost : "" - readonly property string email: !!modelItem && !!modelItem.providerEmail ? modelItem.providerEmail : "" - readonly property string postalAddress: !!modelItem && !!modelItem.providerPostalAddress ? modelItem.providerPostalAddress : "" - readonly property string icon: !!modelItem && !!modelItem.providerIcon ? modelItem.providerIcon : "" - readonly property string image: !!modelItem && !!modelItem.providerImage ? modelItem.providerImage : "" - - readonly property ListModel contactModel: ListModel { - readonly property alias homepage: baseItem.homepage - readonly property alias email: baseItem.email - readonly property alias phone: baseItem.phone - readonly property alias phoneCost: baseItem.phoneCost - readonly property string phoneDisplayString: { - var s = "" - if (!!phone) { - s = '' + phone + "" - if (!!phoneCost) { - s += "
" + phoneCost - } - } - return s - } - readonly property alias postalAddress: baseItem.postalAddress - - function removeHtml(htmlString) { - return htmlString.replace(/<\/?[a-z][a-z0-9]*[^>]*>/ig, " "); - } - - onHomepageChanged: { - setProperty(0, "text", !!homepage ? '' + homepage + "" : "") - setProperty(0, "link", !!homepage ? homepage : "") - } - onEmailChanged: { - setProperty(1, "text", !!email ? '' + email + "" : "") - setProperty(1, "link", !!email ? "mailto:" + email : "") - } - onPhoneDisplayStringChanged: { - setProperty(2, "text", phoneDisplayString) - } - onPostalAddressChanged: { - setProperty(3, "text", postalAddress) - } - - ListElement { - iconSource: "qrc:///images/provider/+tablet/url.png" - label: QT_TR_NOOP("Homepage") - text: "" - link: "" - } - - ListElement { - iconSource: "qrc:///images/provider/+tablet/mail.png" - label: QT_TR_NOOP("E-Mail") - text: "" - link: "" - } - - ListElement { - iconSource: "qrc:///images/provider/+tablet/telefon.png" - label: QT_TR_NOOP("Phone") - text: "" - link: "" - } - - ListElement { - iconSource: "qrc:///images/provider/+tablet/adresse.png" - label: QT_TR_NOOP("Contact") - text: "" - link: "" - } - } -} diff --git a/resources/qml_stationary/AusweisApp2/Global/Utils.js b/resources/qml_stationary/AusweisApp2/Global/Utils.js deleted file mode 100644 index 3469eb6..0000000 --- a/resources/qml_stationary/AusweisApp2/Global/Utils.js +++ /dev/null @@ -1,57 +0,0 @@ - -function escapeHtml(str) -{ - return String(str).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); -} - -function isToday(date) -{ - var today = new Date; - return isSameDate(today, date); -} - -function isYesterday(date) -{ - var yesterday = new Date; - yesterday.setDate(yesterday.getDate() - 1); - return isSameDate(yesterday, date); -} - -function isThisWeek(date) -{ - var monday = new Date; - monday.setDate(monday.getDate()-monday.getDay()); - - date.setDate(date.getDate()-date.getDay()); - - return isSameDate(monday, date); -} - -function isSameDate(one, another) -{ - return one.getFullYear() === another.getFullYear() && one.getMonth() === another.getMonth() && one.getDate() === another.getDate(); -} - -function getRandomInt(min, max) -{ - return Math.floor(Math.random() * (max - min)) + min; -} - -var contentScaleFactor = screenDpi / 160 - -function dp(value) -{ - return value * contentScaleFactor -} - -function sp(value) -{ - var textScale = 1 - return dp(value) * textScale -} - - -// TODO: Use proper path for stationary -function providerIconSource(baseName) { - return "qrc:///images/provider/+tablet/" + baseName + ".png" -} diff --git a/resources/qml_stationary/AusweisApp2/Global/qmldir b/resources/qml_stationary/AusweisApp2/Global/qmldir deleted file mode 100644 index 9511f37..0000000 --- a/resources/qml_stationary/AusweisApp2/Global/qmldir +++ /dev/null @@ -1,8 +0,0 @@ -module Global -singleton Constants 1.0 Constants.qml -singleton PlatformConstants 1.0 PlatformConstants.qml -Category 1.0 Category.js -CheckBox 1.0 CheckBox.qml -Pane 1.0 Pane.qml -Utils 1.0 Utils.js -ProviderModelItem 1.0 ProviderModelItem.qml diff --git a/resources/qml_stationary/AusweisApp2/Views/History/HistoryView.qml b/resources/qml_stationary/AusweisApp2/Views/History/HistoryView.qml deleted file mode 100644 index 5a22005..0000000 --- a/resources/qml_stationary/AusweisApp2/Views/History/HistoryView.qml +++ /dev/null @@ -1,181 +0,0 @@ -import AusweisApp2.Global 1.0 - -import QtQml 2.2 -import QtQuick 2.7 -import QtQuick.Controls 1.4 -import QtQuick.Dialogs 1.2 -import QtQuick.Layouts 1.3 - - -Item { - id: baseItem - - Column { - id: header - width: parent.width - spacing: Constants.pane_spacing - - Text { - width: parent.width - wrapMode: Text.WordWrap - text: qsTr("This page displays the history of your successful authentications. " + - "Double-click on a service provider for more information. " + - "You can delete parts or the entire history. " + - "You can also save the history as a PDF file.") - } - - Item { - height: searchField.height - width: parent.width - - Text { - id: searchLabel - anchors.verticalCenter: parent.verticalCenter - text: qsTr("Search:") + settingsModel.translationTrigger - textFormat: Text.StyledText - } - TextField { - id: searchField - anchors.left: searchLabel.right - anchors.leftMargin: Constants.pane_spacing - anchors.right: parent.right - onTextChanged: historyModel.searchFilter.setFilterString(text) - } - } - } - - Item { - anchors.top: header.bottom - anchors.topMargin: header.spacing - anchors.bottom: footer.top - anchors.bottomMargin: header.spacing - width: parent.width - - Text { - anchors.centerIn: parent - text: qsTr("No history entry available") + settingsModel.translationTrigger - wrapMode: Text.WordWrap - font.pixelSize: Constants.normal_font_size - visible: !scrollView.visible - } - - ScrollView { - id: scrollView - width: parent.width - anchors.fill: parent - anchors.topMargin: 1 - anchors.bottomMargin: 1 - clip: true - horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff - verticalScrollBarPolicy: Qt.ScrollBarAlwaysOn - visible: listView.count > 0 - - Rectangle { - width: baseItem.width - height: listView.contentHeight - color: PlatformConstants.grey_border - - } - - ListView { - id: listView - width: parent.width - spacing: 1 - model: historyModel.searchFilter - - delegate: ListViewDelegate { - width: parent.width - height: Utils.dp(120) - historyModelItem: model - onShowHistoryDetails:widgetPlugin.showDetailDialog(model.termsOfUsage) - } - } - } - - Rectangle { - anchors.top: parent.top - width: parent.width - height: 1 - color: PlatformConstants.grey_border - } - Rectangle { - anchors.bottom: parent.bottom - anchors.bottomMargin: height - width: parent.width - height: 1 - color: PlatformConstants.grey_border - } - } - - Rectangle { - id: footer - width: parent.width - height: Utils.dp(50) - anchors.bottom: parent.bottom - - RowLayout { - anchors.fill: parent - spacing: Constants.pane_spacing - - Text { - text: qsTr("History:") + settingsModel.translationTrigger - } - - CheckBox { - checked: historyModel.enabled - onCheckedChanged: historyModel.enabled = checked - } - - Item { - // Quickfix because CheckBox text turns into a black box sometimes - // TODO: Use text property from checkbox - height: childrenRect.height - width: childrenRect.width - - Text { - anchors.left: parent.left - anchors.leftMargin: -Constants.pane_spacing - text: qsTr("save") + settingsModel.translationTrigger - } - } - - Item { - Layout.fillWidth: true - } - - Button { - text: qsTr("Delete History") + settingsModel.translationTrigger - enabled: listView.count > 0 - onClicked: deleteConfirmationDialog.open() - } - - Button { - text: qsTr("Save as PDF") + settingsModel.translationTrigger - enabled: listView.count > 0 - onClicked: fileDialog.open() - } - } - } - - - MessageDialog { - id: deleteConfirmationDialog - icon: StandardIcon.Question - modality: Qt.ApplicationModal - standardButtons: StandardButton.Yes | StandardButton.No - title: qsTr("Delete history") + settingsModel.translationTrigger - text: qsTr("Do you really want to delete the history?") + settingsModel.translationTrigger - onYes: { historyModel.removeRows(0, historyModel.rowCount()) } - } - FileDialog { - id: fileDialog - modality: Qt.ApplicationModal - selectExisting: false - title: Qt.application.name + " - " + qsTr("Save history") + settingsModel.translationTrigger - folder: shortcuts.home - nameFilters: [qsTr("PDF Documents (*.pdf)") + settingsModel.translationTrigger] - onAccepted: { - qmlExtension.exportHistory(fileDialog.fileUrls) - } - } -} diff --git a/resources/qml_stationary/AusweisApp2/Views/History/ListViewDelegate.qml b/resources/qml_stationary/AusweisApp2/Views/History/ListViewDelegate.qml deleted file mode 100644 index c1d7daa..0000000 --- a/resources/qml_stationary/AusweisApp2/Views/History/ListViewDelegate.qml +++ /dev/null @@ -1,55 +0,0 @@ -import AusweisApp2.Global 1.0 - -import QtQuick 2.6 -import QtQuick.Layouts 1.2 - - -Rectangle { - id: baseItem - property var historyModelItem - signal showHistoryDetails - - Item { - id: sectionImage - height: parent.height - width: Utils.dp(80) - - Rectangle { - id: providerImage - anchors.fill: parent - anchors.margins: Utils.dp(10) - color: baseItem.historyModelItem ? Category.displayColor(baseItem.historyModelItem.providerCategory) : Category.displayColor("unknown" ) - visible: !!providerIconSource - readonly property string providerIconSource: baseItem.historyModelItem ? baseItem.historyModelItem.providerIcon : "" - - Image { - source: parent.providerIconSource - anchors.fill: parent - anchors.margins: Utils.dp(10) - asynchronous: true - fillMode: Image.PreserveAspectFit - } - } - Image { - id: categoryImage - anchors.fill: parent - anchors.margins: Utils.dp(10) - source: baseItem.historyModelItem ? Category.sectionImageSource(baseItem.historyModelItem.providerCategory) : Category.sectionImageSource("unknown") - asynchronous: true - clip: true - visible: !providerImage.visible - } - } - - ListViewDelegateContent { - anchors.left: sectionImage.right - anchors.right: parent.right - height: parent.height - historyModelItem: baseItem.historyModelItem - } - - MouseArea { - anchors.fill: parent - onDoubleClicked: baseItem.showHistoryDetails() - } -} diff --git a/resources/qml_stationary/AusweisApp2/Views/History/ListViewDelegateContent.qml b/resources/qml_stationary/AusweisApp2/Views/History/ListViewDelegateContent.qml deleted file mode 100644 index 274b764..0000000 --- a/resources/qml_stationary/AusweisApp2/Views/History/ListViewDelegateContent.qml +++ /dev/null @@ -1,54 +0,0 @@ -import AusweisApp2.Global 1.0 - -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 - -Item { - id: baseItem - property var historyModelItem - readonly property string providerName: !!historyModelItem.providerLongName ? historyModelItem.providerLongName : historyModelItem.providerShortName - - - Text { - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.verticalCenter - anchors.margins: Utils.dp(15) - anchors.bottomMargin: 0 - - verticalAlignment: Text.AlignVCenter - font.capitalization: Font.AllUppercase - elide: Text.ElideRight - text: { - if (!historyModelItem) { - return ""; - } - else if (Utils.isToday(historyModelItem.dateTime)) { - return qsTr("today") + settingsModel.translationTrigger - } - else if (Utils.isYesterday(historyModelItem.dateTime)) { - return qsTr("yesterday") + settingsModel.translationTrigger - } - else if (Utils.isThisWeek(historyModelItem.dateTime)) { - return historyModelItem.dateTime.toLocaleString(Qt.locale(), qsTr("dddd")) + settingsModel.translationTrigger - } - return historyModelItem.dateTime.toLocaleString(Qt.locale(), qsTr("dd.MM.yyyy")) + settingsModel.translationTrigger - } - } - Text { - anchors.top: parent.verticalCenter - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - anchors.margins: Utils.dp(15) - anchors.topMargin: 0 - - verticalAlignment: Text.AlignVCenter - font.pixelSize: Constants.small_font_size - elide: Text.ElideRight - color: Constants.blue_dark - text: !!providerName ? providerName : historyModelItem.subject - } -} diff --git a/resources/qml_stationary/AusweisApp2/Views/History/qmldir b/resources/qml_stationary/AusweisApp2/Views/History/qmldir deleted file mode 100644 index f426001..0000000 --- a/resources/qml_stationary/AusweisApp2/Views/History/qmldir +++ /dev/null @@ -1,2 +0,0 @@ -module AusweisApp2.Views.History -HistoryView 1.0 HistoryView.qml diff --git a/resources/qml_stationary/AusweisApp2/Views/Provider/AdditionalResultsItem.qml b/resources/qml_stationary/AusweisApp2/Views/Provider/AdditionalResultsItem.qml deleted file mode 100644 index c9694f1..0000000 --- a/resources/qml_stationary/AusweisApp2/Views/Provider/AdditionalResultsItem.qml +++ /dev/null @@ -1,46 +0,0 @@ -import AusweisApp2.Global 1.0 - -import QtQuick 2.6 -import QtQuick.Layouts 1.2 - - -Rectangle { - id: baseItem - readonly property int totalHits: providerModel.additionalResultCount - - visible: totalHits > 0 && providerModel.categories.length > 0 && providerModel.categories.indexOf("all") === -1 - - Image { - id: backgroundImage - anchors.margins: Utils.dp(10) - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.left: parent.left - width: Utils.dp(60) - source: Category.backgroundImageSource("all") - asynchronous: true - clip: true - Image { - id: foregroundImage - anchors.fill: parent - anchors.margins: parent.anchors.margins - source: Category.imageSource("all") - asynchronous: true - clip: true - fillMode: Image.PreserveAspectFit - } - } - - Text { - text: qsTr("Additional results in other categories: %1").arg(baseItem.totalHits) + settingsModel.translationTrigger - anchors.verticalCenter: parent.verticalCenter - anchors.left: backgroundImage.right - anchors.margins: Utils.dp(15) - anchors.right: parent.right - } - - MouseArea { - anchors.fill: parent - onDoubleClicked: providerModel.addAdditionalResultCategories() - } -} diff --git a/resources/qml_stationary/AusweisApp2/Views/Provider/CategoryCheckbox.qml b/resources/qml_stationary/AusweisApp2/Views/Provider/CategoryCheckbox.qml deleted file mode 100644 index b43aab8..0000000 --- a/resources/qml_stationary/AusweisApp2/Views/Provider/CategoryCheckbox.qml +++ /dev/null @@ -1,52 +0,0 @@ -import AusweisApp2.Global 1.0 - -import QtQuick 2.6 -import QtQuick.Layouts 1.2 - -Item { - id: baseItem - height: parent.height - width: mainContent.width - anchors.verticalCenter: parent.verticalCenter - - property string category: "" - property alias imageSource: icon.source - property alias text: label.text - - Row { - id: mainContent - height: parent.height - spacing: Utils.dp(5) - anchors.verticalCenter: parent.verticalCenter - - Image { - id: icon - height: baseItem.height * 0.7 - width: height - fillMode: Image.PreserveAspectFit - anchors.verticalCenter: parent.verticalCenter - } - - Text { - id: label - font.pixelSize: Constants.small_font_size - anchors.verticalCenter: parent.verticalCenter - } - - CheckBox { - id: checkbox - anchors.verticalCenter: parent.verticalCenter - visible: true - checked: providerModel.categories.indexOf(baseItem.category) !== -1 - - // TODO: At this time there is no way to select and toggle the checkbox with keyboard. - // Depending on the selection model in future the following line is needed. - // onCheckedChanged: providerModel.updateCategorySelection(category, checked) - } - } - - MouseArea { - anchors.fill: parent - onClicked: providerModel.updateCategorySelection(category, !checkbox.checked) - } -} diff --git a/resources/qml_stationary/AusweisApp2/Views/Provider/ProviderCard.qml b/resources/qml_stationary/AusweisApp2/Views/Provider/ProviderCard.qml deleted file mode 100644 index 5c7da83..0000000 --- a/resources/qml_stationary/AusweisApp2/Views/Provider/ProviderCard.qml +++ /dev/null @@ -1,55 +0,0 @@ -import AusweisApp2.Global 1.0 - -import QtQuick 2.6 -import QtQuick.Layouts 1.2 - - -Rectangle { - id: baseItem - property var providerModelItem: null - signal showProviderDetails - - Item { - id: sectionImage - height: parent.height - width: Utils.dp(80) - - Rectangle { - id: providerImage - anchors.fill: parent - anchors.margins: Utils.dp(10) - color: baseItem.providerModelItem ? Category.displayColor(baseItem.providerModelItem.providerCategory) : Category.displayColor("unknown" ) - visible: !!providerIconSource - readonly property string providerIconSource: baseItem.providerModelItem ? baseItem.providerModelItem.providerIcon : "" - - Image { - source: parent.providerIconSource - anchors.fill: parent - anchors.margins: Utils.dp(10) - asynchronous: true - fillMode: Image.PreserveAspectFit - } - } - Image { - id: categoryImage - anchors.fill: parent - anchors.margins: Utils.dp(10) - source: baseItem.providerModelItem ? Category.sectionImageSource(baseItem.providerModelItem.providerCategory) : Category.sectionImageSource("unknown") - asynchronous: true - clip: true - visible: !providerImage.visible - } - } - ProviderCardInfoItem { - height: parent.height - anchors.left: sectionImage.right - anchors.right: parent.right - providerName: !!providerModelItem.providerLongName ? providerModelItem.providerLongName : providerModelItem.providerShortName - providerAddressDomain: providerModelItem.providerAddressDomain - } - - MouseArea { - anchors.fill: parent - onDoubleClicked: baseItem.showProviderDetails() - } -} diff --git a/resources/qml_stationary/AusweisApp2/Views/Provider/ProviderCardInfoItem.qml b/resources/qml_stationary/AusweisApp2/Views/Provider/ProviderCardInfoItem.qml deleted file mode 100644 index 89e7aff..0000000 --- a/resources/qml_stationary/AusweisApp2/Views/Provider/ProviderCardInfoItem.qml +++ /dev/null @@ -1,40 +0,0 @@ -import AusweisApp2.Global 1.0 - -import QtQuick 2.6 - - -Rectangle { - id: baseItem - - property alias providerName: subjectText.text - property alias providerAddressDomain: addressText.text - - Text { - id: subjectText - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.verticalCenter - anchors.margins: Utils.dp(15) - anchors.bottomMargin: 0 - - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - - Text { - id: addressText - anchors.top: parent.verticalCenter - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - anchors.margins: Utils.dp(15) - anchors.topMargin: 0 - - verticalAlignment: Text.AlignVCenter - font.pixelSize: Constants.small_font_size - elide: Text.ElideRight - color: Constants.blue_dark - } -} - diff --git a/resources/qml_stationary/AusweisApp2/Views/Provider/ProviderView.qml b/resources/qml_stationary/AusweisApp2/Views/Provider/ProviderView.qml deleted file mode 100644 index 4ec814e..0000000 --- a/resources/qml_stationary/AusweisApp2/Views/Provider/ProviderView.qml +++ /dev/null @@ -1,166 +0,0 @@ -import AusweisApp2.Global 1.0 -import AusweisApp2.Views.ProviderDetails 1.0 - -import QtQml 2.2 -import QtQuick 2.6 -import QtQuick.Controls 1.4 -import QtQuick.Dialogs 1.2 -import QtQuick.Layouts 1.3 - - -Rectangle { - id: baseItem - property color titleBarColor: Constants.blue - readonly property real titleBarOpacity: 1 - readonly property int headerHeight: Utils.dp(54) - readonly property int separatorHeight: Utils.dp(2) - color: "white" - - Column { - id: header - width: parent.width - spacing: Constants.pane_spacing - - Text { - width: parent.width - text: "

" + qsTr("This section lists offers of service providers that support online identification. " + - "Click on an entry to go to the provider's web site. This section will be continuously updated with further applications for " + - "the online identification function.") + "

" - wrapMode: Text.Wrap - } - - Item { - height: searchField.height - width: parent.width - - Text { - id: searchLabel - anchors.verticalCenter: parent.verticalCenter - text: qsTr("Search:") + settingsModel.translationTrigger - textFormat: Text.StyledText - } - TextField { - id: searchField - anchors.left: searchLabel.right - anchors.leftMargin: Constants.pane_spacing - anchors.right: parent.right - text: "" - onTextChanged: providerModel.searchString = text - } - } - - Row { - id: checkBoxesItem - height: Utils.dp(40) - anchors.horizontalCenter: parent.horizontalCenter - padding: space - readonly property real space: (parent.width - checkBoxCitizen.width - checkBoxInsurance.width - checkBoxFinance.width - checkBoxOther.width) / 5 - spacing: space - - CategoryCheckbox_tablet { - id: checkBoxCitizen - category: "citizen" - imageSource: Category.imageSource("citizen") - text: qsTr("Citizen services") + settingsModel.translationTrigger - } - - CategoryCheckbox_tablet { - id: checkBoxInsurance - category: "insurance" - imageSource: Category.imageSource("insurance") - text: qsTr("Insurances") + settingsModel.translationTrigger - } - - CategoryCheckbox_tablet { - id: checkBoxFinance - category: "finance" - imageSource: Category.imageSource("finance") - text: qsTr("Financials") + settingsModel.translationTrigger - } - - CategoryCheckbox_tablet { - id: checkBoxOther - category: "other" - imageSource: Category.imageSource("other") - text: qsTr("Other services") + settingsModel.translationTrigger - } - } - } - - Item { - anchors.top: header.bottom - anchors.topMargin: header.spacing - anchors.bottom: parent.bottom - width: parent.width - - Text { - anchors.centerIn: parent - text: qsTr("No match found") + settingsModel.translationTrigger - wrapMode: Text.WordWrap - font.pixelSize: Constants.normal_font_size - visible: !scrollView.visible - } - ScrollView { - id: scrollView - anchors.fill: parent - anchors.topMargin: 1 - anchors.bottomMargin: 1 - clip: true - horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff - verticalScrollBarPolicy: Qt.ScrollBarAlwaysOn - visible: tableRepeater.count > 0 || additionalResults.totalHits > 0 - - Rectangle { - width: baseItem.width - height: table.height - color: PlatformConstants.grey_border - - Column { - id: table - width: parent.width - spacing: 1 - - Repeater { - id: tableRepeater - model: providerModel - - ProviderCard { - height: Utils.dp(120) - width: parent.width - providerModelItem: model - onShowProviderDetails: { - historyModel.nameFilter.setProviderAddress(model.providerAddress) - providerDetailsDialog.providerModelItem = model - providerDetailsDialog.visible = true - } - } - } - AdditionalResultsItem { - id: additionalResults - width: parent.width - height: Utils.dp(120) - } - } - } - } - - Rectangle { - anchors.top: parent.top - width: parent.width - height: 1 - color: PlatformConstants.grey_border - } - Rectangle { - anchors.bottom: parent.bottom - width: parent.width - height: 1 - color: PlatformConstants.grey_border - } - } - - - ProviderDetailsDialog { - id: providerDetailsDialog - visible: false - } -} diff --git a/resources/qml_stationary/AusweisApp2/Views/Provider/qmldir b/resources/qml_stationary/AusweisApp2/Views/Provider/qmldir deleted file mode 100644 index 9601e44..0000000 --- a/resources/qml_stationary/AusweisApp2/Views/Provider/qmldir +++ /dev/null @@ -1,2 +0,0 @@ -module AusweisApp2.Views.Provider -ProviderView 1.0 ProviderView.qml diff --git a/resources/qml_stationary/AusweisApp2/Views/ProviderDetails/ProviderContactInfo.qml b/resources/qml_stationary/AusweisApp2/Views/ProviderDetails/ProviderContactInfo.qml deleted file mode 100644 index 9c61e41..0000000 --- a/resources/qml_stationary/AusweisApp2/Views/ProviderDetails/ProviderContactInfo.qml +++ /dev/null @@ -1,52 +0,0 @@ -import QtQuick 2.6 -import QtQuick.Layouts 1.2 - -import AusweisApp2.Global 1.0 - - -Rectangle { - id: baseItem - - property alias contactModel: contactListView.model - - Column { - id: info - width: baseItem.width / info.scaleFactor // fill whole width - transform: Scale { yScale: info.scaleFactor; xScale: info.scaleFactor } - - readonly property real scaleFactor: { - var unscaledElementsAccumulatedHeight = 3 - var factor = height > baseItem.height ? (baseItem.height - unscaledElementsAccumulatedHeight) / height : 1 - if (factor <= 0) { - return 1 - } - return factor - } - - Text { - text: qsTr("Contact") + settingsModel.translationTrigger - padding: Constants.component_spacing - font.pixelSize: Constants.header_font_size - color: "white" - } - Rectangle { - anchors.left: parent.left - anchors.right: parent.right - height: contactListView.height - ListView { - id: contactListView - width: parent.width - height: contentHeight - interactive: false - spacing: 2 - delegate: ProviderContactInfoItem { - width: contactListView.width - color: baseItem.color - imageSource: Qt.resolvedUrl(model.iconSource) - itemText: (!!model.text ? model.text : qsTr("Unknown")) + settingsModel.translationTrigger - link: model.link - } - } - } - } -} diff --git a/resources/qml_stationary/AusweisApp2/Views/ProviderDetails/ProviderContactInfoItem.qml b/resources/qml_stationary/AusweisApp2/Views/ProviderDetails/ProviderContactInfoItem.qml deleted file mode 100644 index bd15dd8..0000000 --- a/resources/qml_stationary/AusweisApp2/Views/ProviderDetails/ProviderContactInfoItem.qml +++ /dev/null @@ -1,45 +0,0 @@ -import QtQuick 2.6 - -import AusweisApp2.Global 1.0 - - -Rectangle { - id: baseItem - property alias imageSource: image.source - property alias itemText: text.text - property url link - height: Math.max(Utils.dp(60), text.height + Utils.dp(10)) - - Item { - id: iconItem - height: parent.height - width: Math.min(height, Utils.dp(60)) - anchors.left: parent.left - - Image { - id: image - width: parent.width * 0.5 - height: width - anchors.centerIn: parent - fillMode: Image.PreserveAspectFit - } - } - Text { - id: text - anchors.left: iconItem.right - anchors.leftMargin: Utils.dp(10) - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - font.pixelSize: Constants.normal_font_size - color: "white" - linkColor: color - elide: Text.ElideRight - wrapMode: Text.WordWrap - } - - MouseArea { - anchors.fill: parent - enabled: !!baseItem.link - onClicked: Qt.openUrlExternally(baseItem.link) - } -} diff --git a/resources/qml_stationary/AusweisApp2/Views/ProviderDetails/ProviderDetailButtonBar.qml b/resources/qml_stationary/AusweisApp2/Views/ProviderDetails/ProviderDetailButtonBar.qml deleted file mode 100644 index 7e71751..0000000 --- a/resources/qml_stationary/AusweisApp2/Views/ProviderDetails/ProviderDetailButtonBar.qml +++ /dev/null @@ -1,47 +0,0 @@ -import QtQuick 2.6 -import QtQuick.Controls 1.4 - -import AusweisApp2.Global 1.0 - - -Item { - id: baseItem - - height: button.height + Constants.component_spacing - width: parent.width - - property string selectedCategory: "" - property string providerIcon: "" - property string address: "" - property color titleBarColor - - Image { - id: icon - source: baseItem.providerIcon !== "" ? baseItem.providerIcon : - Category.buttonImageSource(baseItem.selectedCategory) - asynchronous: true - height: 2 * baseItem.height - width: height - fillMode: Image.PreserveAspectFit - anchors.left: parent.left - anchors.leftMargin: Constants.component_spacing - anchors.verticalCenter: baseItem.top - } - - Button { - id: button - text: qsTr("ONLINE-APPLICATION") + settingsModel.translationTrigger - // TODO: Use custom button -// buttonColor: baseItem.titleBarColor - anchors.left: icon.right - anchors.leftMargin: Constants.component_spacing - anchors.bottom: icon.bottom - enabled: baseItem.address !== "" - - onClicked: { - if (baseItem.address !== "") { - Qt.openUrlExternally(baseItem.address) - } - } - } -} diff --git a/resources/qml_stationary/AusweisApp2/Views/ProviderDetails/ProviderDetailDescription.qml b/resources/qml_stationary/AusweisApp2/Views/ProviderDetails/ProviderDetailDescription.qml deleted file mode 100644 index 1cf6505..0000000 --- a/resources/qml_stationary/AusweisApp2/Views/ProviderDetails/ProviderDetailDescription.qml +++ /dev/null @@ -1,30 +0,0 @@ -import AusweisApp2.Global 1.0 - -import QtQuick 2.6 -import QtQuick.Controls 1.4 - - -ScrollView { - id: baseItem - property string description: "" - - horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff - verticalScrollBarPolicy: Qt.ScrollBarAlwaysOn - - Column { - spacing: Constants.pane_spacing - - Text { -// font.pixelSize: Constants.header_font_size - color: PlatformConstants.blue_primary - text: qsTr("Description") + settingsModel.translationTrigger - } - - Text { -// font.pixelSize: Constants.normal_font_size - text: baseItem.description - width: baseItem.width - Utils.dp(50) - wrapMode: Text.Wrap - } - } -} diff --git a/resources/qml_stationary/AusweisApp2/Views/ProviderDetails/ProviderDetailView.qml b/resources/qml_stationary/AusweisApp2/Views/ProviderDetails/ProviderDetailView.qml deleted file mode 100644 index 8b6b829..0000000 --- a/resources/qml_stationary/AusweisApp2/Views/ProviderDetails/ProviderDetailView.qml +++ /dev/null @@ -1,140 +0,0 @@ -import AusweisApp2.Global 1.0 - -import QtQuick 2.6 -import QtQuick.Controls 1.4 - - -Item { - id: baseItem - - property alias providerModelItem: provider.modelItem - - ProviderModelItem { - id: provider - } - - function historyInfoIsOpen() { - return providerDetailsHistoryInfo.visible - } - - function closeHistoryInfo() { - providerDetailsHistoryInfo.visible = false - } - - property color titleBarColor: Category.displayColor(provider.category) - - - Rectangle { - id: titleBar - height: Utils.dp(40) - width: baseItem.width - color: baseItem.titleBarColor - - Text { - text: provider.shortName - font.bold: true - color: "white" - - anchors.left: titleBar.left - anchors.leftMargin: Constants.component_spacing - anchors.verticalCenter: titleBar.verticalCenter - } - } - - Row { - id: header - height: Utils.dp(300) - width: parent.width - anchors.top: titleBar.bottom - - Item { - height: parent.height - width: baseItem.width * 2 / 3 - anchors.top: parent.top - - Image { - id: image - source: provider.image !== "" ? provider.image : - Category.backgroundImageSource(provider.category) - asynchronous: true - height: parent.height - fillMode: Image.PreserveAspectFit - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - } - - Image { - height: parent.height - width: height / 2 - anchors.right: image.right - anchors.top: parent.top - fillMode: Image.Stretch - source: Category.gradientImageSource(provider.category) - - } - - Rectangle { - anchors.left: image.right - anchors.right: parent.right - anchors.top: parent.top - height: parent.height - color: baseItem.titleBarColor - } - } - - Rectangle { - height: parent.height - width: baseItem.width / 3 - color: baseItem.titleBarColor - - ProviderContactInfo { - color: baseItem.titleBarColor - height: parent.height - width: baseItem.width / 3 - Constants.component_spacing - contactModel: provider.contactModel - } - } - } - - ProviderDetailButtonBar { - id: buttonBar - selectedCategory: provider.category - providerIcon: provider.icon - address: provider.address - titleBarColor: baseItem.titleBarColor - anchors.top: header.bottom - } - - Pane { - id: leftPane - anchors.margins: Constants.component_spacing - anchors.top: buttonBar.bottom - anchors.bottom: closeButton.top - - ProviderDetailDescription { - id: descriptionData - width: parent.width - height: leftPane.height - 2 * Constants.pane_padding - description: provider.longDescription - } - } - - Button { - id: closeButton - text: "Close" - anchors.right: parent.right - anchors.bottom: parent.bottom - anchors.margins: Constants.component_spacing - onClicked: close() - } - - property var openHistoryInfoFunc: function(entryInfo) { - providerDetailsHistoryInfo.visible = true - - providerDetailsHistoryInfo.providerName = entryInfo['providerName'] - providerDetailsHistoryInfo.providerPostalAddress = entryInfo['providerPostalAddress'] - providerDetailsHistoryInfo.purposeText = entryInfo['purposeText'] - providerDetailsHistoryInfo.requestedDataText = entryInfo['requestedDataText'] - providerDetailsHistoryInfo.termsOfUsageText = entryInfo['termsOfUsageText'] - } -} diff --git a/resources/qml_stationary/AusweisApp2/Views/ProviderDetails/ProviderDetailsDialog.qml b/resources/qml_stationary/AusweisApp2/Views/ProviderDetails/ProviderDetailsDialog.qml deleted file mode 100644 index 019d258..0000000 --- a/resources/qml_stationary/AusweisApp2/Views/ProviderDetails/ProviderDetailsDialog.qml +++ /dev/null @@ -1,31 +0,0 @@ -import QtQuick 2.6 -import QtQuick.Controls 1.4 -import QtQuick.Window 2.2 - -import AusweisApp2.Global 1.0 -import AusweisApp2.Views.Provider 1.0 - -Window { - id: baseItem - title: qsTr("Provider details") + settingsModel.translationTrigger - height: Utils.dp(900) - minimumHeight: Utils.dp(700) - width: Utils.dp(1200) - minimumWidth: Utils.dp(800) - modality: Qt.ApplicationModal - color: Constants.background_color - - property var providerModelItem: null - - ProviderDetailView { - anchors.centerIn: parent - height: parent.height - width: parent.width - providerModelItem: baseItem.providerModelItem - } - - Connections { - target: applicationModel - onCurrentWorkflowChanged: if (!!applicationModel.currentWorkflow) {baseItem.close()} - } -} diff --git a/resources/qml_stationary/AusweisApp2/Views/ProviderDetails/qmldir b/resources/qml_stationary/AusweisApp2/Views/ProviderDetails/qmldir deleted file mode 100644 index 0c43e6d..0000000 --- a/resources/qml_stationary/AusweisApp2/Views/ProviderDetails/qmldir +++ /dev/null @@ -1,7 +0,0 @@ -module AusweisApp2.Views.ProviderDetails -ProviderDetailsDialog 1.0 ProviderDetailsDialog.qml -ProviderContactInfoItem 1.0 ProviderContactInfoItem.qml -ProviderContactInfo 1.0 ProviderContactInfo.qml -ProviderDetailButtonBar 1.0 ProviderDetailButtonBar.qml -ProviderDetailDescription 1.0 ProviderDetailDescription.qml -ProviderDetailView 1.0 ProviderDetailView.qml diff --git a/resources/qml_stationary/HistoryWidgetQmlPlugin.qml b/resources/qml_stationary/HistoryWidgetQmlPlugin.qml deleted file mode 100644 index 2d2c73c..0000000 --- a/resources/qml_stationary/HistoryWidgetQmlPlugin.qml +++ /dev/null @@ -1,3 +0,0 @@ -import AusweisApp2.Views.History 1.0 - -HistoryView {} diff --git a/resources/qml_stationary/ProviderWidgetQmlPlugin.qml b/resources/qml_stationary/ProviderWidgetQmlPlugin.qml deleted file mode 100644 index 90e2c69..0000000 --- a/resources/qml_stationary/ProviderWidgetQmlPlugin.qml +++ /dev/null @@ -1,4 +0,0 @@ -import AusweisApp2.Views.Provider 1.0 - -ProviderView { -} diff --git a/resources/sonar-project.properties.in b/resources/sonar-project.properties.in new file mode 100644 index 0000000..768df57 --- /dev/null +++ b/resources/sonar-project.properties.in @@ -0,0 +1,17 @@ +sonar.host.url=https://sonar.governikus.de + +sonar.projectKey=ausweisapp2:default +sonar.projectName=AusweisApp2 +sonar.projectVersion=@PROJECT_VERSION@ + +sonar.projectBaseDir=@PROJECT_SOURCE_DIR@ +sonar.sources=src + +sonar.sourceEncoding=UTF-8 +sonar.language=cpp +sonar.cpp.file.suffixes=.cpp,.h +sonar.objc.file.suffixes=.m,.mm + +sonar.cfamily.build-wrapper-output=@PROJECT_BINARY_DIR@ +sonar.cfamily.gcov.reportsPath=@PROJECT_BINARY_DIR@/Testing/CoverageInfo +sonar.exclusions=src/external/**,utils/**,**/CMakeFiles/* diff --git a/resources/statemachine.sh.in b/resources/statemachine.sh.in index 3567ffa..568b961 100755 --- a/resources/statemachine.sh.in +++ b/resources/statemachine.sh.in @@ -1,14 +1,17 @@ #!/bin/sh RULE=' -s/([[:alnum:]]+)\.setInitialState\(([[:alnum:]]+)\)\;/[*] --> \2/p -s/setInitialState\(([[:alnum:]]+)\)\;/[*] --> \1/p -s/([[:alnum:]]+)\-.addTransition\(([[:alnum:]]+)\,[[:space:]]+\&([[:alnum:]]+)\:\:fire([[:alnum:]]+)\,[[:space:]]+([[:alnum:]]+)\)\;/\2 --> \5 : \4/p +s/[[:alnum:]]+\.setInitialState\(s([[:alnum:]]+)\)\;/[*] --> \1/p +s/setInitialState\(s([[:alnum:]]+)\)\;/[*] --> \1/p +s/auto[[:space:]]sFinal[[:space:]]=[[:space:]]addState\(\)\;/state Final #DarkSeaGreen/p +s/[[:alnum:]]+\-.addTransition\(s([[:alnum:]]+)\,[[:space:]]+\&[[:alnum:]]+\:\:fire([[:alnum:]]+)\,[[:space:]]+s([[:alnum:]]+)\)\;/\1 --> \3 : \2/p +s/connect\(s([[:alnum:]]+)\,[[:space:]]+\&[[:alnum:]]+\:\:fire([[:alnum:]]+)\,[[:space:]]+this\,[[:space:]]+\&[[:alnum:]]+\:\:fire([[:alnum:]]+)\)\;/state \3 #DarkSeaGreen\n \1 --> \3 : \2/p s/auto[[:space:]]+([[:alnum:]]+)[[:space:]]+=\s+addAndConnectState\(\);/\1 --> [*]/p ' function createImage { echo "@startuml" > $2.uml + echo " hide empty description" >> $2.uml sed -E -n -e "$RULE" < $1 >> $2.uml echo "@enduml" >> $2.uml cat $2.uml @@ -16,8 +19,9 @@ function createImage { rm $2.uml } -createImage @PROJECT_SOURCE_DIR@/src/core/states/CompositeStateSelectCard.cpp @PROJECT_BINARY_DIR@/uml_SelectCard -createImage @PROJECT_SOURCE_DIR@/src/core/states/CompositeStateProcessCvcsAndSetRights.cpp @PROJECT_BINARY_DIR@/uml_ProcessCvcsAndSetRights +createImage @PROJECT_SOURCE_DIR@/src/core/states/CompositeStateSelectCard.cpp @PROJECT_BINARY_DIR@/uml_CompositeStateSelectCard +createImage @PROJECT_SOURCE_DIR@/src/core/states/CompositeStatePace.cpp @PROJECT_BINARY_DIR@/uml_CompositeStatePace +createImage @PROJECT_SOURCE_DIR@/src/core/states/CompositeStateProcessCvcsAndSetRights.cpp @PROJECT_BINARY_DIR@/uml_CompositeStateProcessCvcsAndSetRights createImage @PROJECT_SOURCE_DIR@/src/core/controller/ChangePinController.cpp @PROJECT_BINARY_DIR@/uml_ChangePinController createImage @PROJECT_SOURCE_DIR@/src/core/controller/SelfAuthController.cpp @PROJECT_BINARY_DIR@/uml_SelfAuthController createImage @PROJECT_SOURCE_DIR@/src/core/controller/AuthController.cpp @PROJECT_BINARY_DIR@/uml_AuthController diff --git a/resources/translations/ausweisapp2_de.ts b/resources/translations/ausweisapp2_de.ts index 85fe5d9..51f0c38 100644 --- a/resources/translations/ausweisapp2_de.ts +++ b/resources/translations/ausweisapp2_de.ts @@ -4,43 +4,25 @@ AboutDialog - - AusweisApp2 is a product of Governikus GmbH & Co. KG - on behalf of the Federal Ministry of the Interior, Building and Community. - Die AusweisApp2 ist ein Produkt der Governikus GmbH & Co. KG - im Auftrag des Bundesministeriums des Innern, für Bau und Heimat. - - - Developer mode: Entwicklermodus: - use verwenden - OK OK + + AusweisApp2 is a product of Governikus GmbH & Co. KG - on behalf of the Federal Ministry of the Interior, Building and Community. + Die AusweisApp2 ist ein Produkt der Governikus GmbH & Co. KG - im Auftrag des Bundesministeriums des Innern, für Bau und Heimat. + AdditionalResultsItem - - Additional results: - Weitere Ergebnisse: - - - - Additional results in other categories: %1 - Weitere Ergebnisse in anderen Kategorien: %1 - - - - AdditionalResultsItem_tablet - - Additional results: Weitere Ergebnisse: @@ -48,162 +30,130 @@ AppQtMainWidget - Actions Hauptaktionen - Welcome Startseite - Provider Anbieter - History Verlauf - Identify Ausweisen - Settings Einstellungen - Minimise Minimieren - AusweisApp2 AusweisApp2 - nPA and eAT Logo Logo des nPA und eAT - AusweisApp2 Logo Logo der AusweisApp2 - Fi&le &Datei - &PIN Management &PIN-Verwaltung - &Exit &Beenden - &Identify &Ausweisen - &Settings &Einstellungen - &Online help &Online-Hilfe - &Evaluate &Bewerten - &Report error Fehler &melden - &About AusweisApp2 &Über AusweisApp2 - &Provider A&nbieter - S&how log Protokoll &anzeigen - Save &log Protokoll &speichern - &Questions &Fragen - &History &Verlauf - &Change PIN &PIN ändern - &Setup assistant &Einrichtungsassistent - &Help &Hilfe - &Diagnosis &Diagnose - Switch language to German Die Sprache auf Deutsch umstellen - DE DE - Switch language to English Die Sprache auf Englisch umstellen - EN EN @@ -211,7 +161,6 @@ AvailableDevicesListDelegate - Unsupported Nicht unterstützt @@ -219,62 +168,50 @@ BluetoothWorkflow - Enable Bluetooth Bluetooth aktivieren - Continue Fortsetzen - Bluetooth is not supported by your device. Ihr Gerät unterstützt kein Bluetooth. - Please try NFC. Bitte versuchen Sie NFC. - - An error occured while connecting to your bluetooth device. Try to pair your device in the system settings and restart the app. - Beim Verbindungsaufbau zum Bluetooth-Kartenlesegerät ist ein Fehler aufgetreten. Bitte koppeln Sie das Gerät über die Einstellungen des Systems und starten Sie die App erneut. - - - Bluetooth is switched off. Bluetooth ist deaktiviert. - Please enable Bluetooth. Bitte aktivieren Sie Bluetooth. - No paired and activated Bluetooth device was detected. The AusweisApp2 needs access to your location in order to discover available devices. You can grant this permission after clicking the continue button. Es konnte kein gekoppeltes, eingeschaltetes Bluetooth-Kartenlesegerät erkannt werden. Zur Erkennung benötigt die AusweisApp2 Zugriff auf Ihren Standort. Nach Klicken auf den Fortsetzen-Knopf können Sie die benötigte Freigabe erteilen. - + An error occured while connecting to your bluetooth device. Try to pair your device in the system settings and restart the app. + Beim Verbindungsaufbau zum Bluetooth-Kartenlesegerät ist ein Fehler aufgetreten. Bitte koppeln Sie das Gerät über die Einstellungen des Systems und starten Sie die App erneut. + + Establish connection Verbindung wird hergestellt - Determine card Ermittle Ausweis - Search card reader... Suche Kartenlesegerät... - Please insert your ID card. Bitte legen Sie Ihren Ausweis ein. @@ -282,45 +219,40 @@ BusyOverlay - The process is started... Der Vorgang wird gestartet... + + CancelAction + + Cancel + Abbrechen + + Category - - Provider Anbieter - All Alle - - Citizen services Bürgerdienste - - Insurances Versicherungen - - Financials Finanzen - - Other services Weitere Dienste @@ -328,80 +260,119 @@ CertificateDescriptionPage - Provider Information Anbieterinformationen + + Close + Schließen + ChangePinController - You may now remove your ID card from the device. Sie können nun Ihr Ausweisdokument vom Gerät entfernen. + + ChangePinView + + PIN Management + PIN-Verwaltung + + + Change PIN + PIN ändern + + + Please wait a moment... + Bitte warten Sie einen Moment... + + + The online identification function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the online identification function. + Die Online-Ausweisfunktion Ihres Ausweisdokumentes ist nicht aktiviert. Bitte wenden Sie sich an die Behörde, die Ihr Ausweisdokument ausgegeben hat, um die Online-Ausweisfunktion zu aktivieren. + + + Please observe the display of your card reader. + Bitte beachten Sie die Anzeige Ihres Kartenlesegeräts. + + + You have entered the wrong PIN twice. Prior to a third attempt, you have to enter your six-digit card access number first. You can find your card access number on the front of your ID card. + Sie haben Ihre PIN zweimal falsch eingegeben. Für einen dritten Versuch müssen Sie vorher Ihre 6-stellige Zugangsnummer eingeben. Sie finden Ihre Zugangsnummer auf der Vorderseite Ihres Ausweises. + + + You have entered a wrong PIN three times. Your PIN is now blocked. You have to enter the PUK now for unblocking. + Sie haben Ihre PIN dreimal falsch eingegeben. Ihre PIN ist jetzt gesperrt. Zum Entsperren geben Sie bitte Ihre PUK ein. + + + Weak NFC signal. Please reposition your card. + Schwacher NFC-Empfang. Bitte korrigieren Sie die Position Ihres Ausweises. + + + Retry + Erneut versuchen + + + + ChangePinViewContent + + PIN Management + PIN-Verwaltung + + + You have the opportunity to change your transport PIN into a personal PIN. You can also change the PIN at any time or unblock the PIN using the personal unblocking key (PUK). The transport PIN and the PUK can be found in the letter sent to you by your competent authority. + Hier haben Sie die Möglichkeit, Ihre Transport-PIN in eine persönliche PIN zu ändern. Zudem können Sie jederzeit Ihre persönliche PIN ändern oder eine Blockierung mit Hilfe der Entsperrnummer (PUK) aufheben. Sie finden Ihre Transport-PIN und die PUK in dem Schreiben, das Sie nach Beantragung Ihres Ausweisdokuments von der für die Ausgabe Ihres Ausweisdokuments zuständigen Behörde erhalten haben. + + + Change PIN now + Jetzt PIN ändern + + + + ConfirmationPopup + + Cancel + Abbrechen + + + + ContinueButton + + Continue + Fortsetzen + + CredentialDialog - Proxy security Proxy-Sicherheit - Proxy requires credentials: Es werden Proxy-Anmeldeinformationen benötigt: - Password: Kennwort: - Username: Benutzername: - Proxy credential password Passwort für den Proxy - Proxy credential username Benutzername für den Proxy - - CustomSwipeBar - - - Contact - Kontakt - - - - History - Verlauf - - - - CONTACT - KONTAKT - - - - HISTORY - VERLAUF - - DataGroup - - No data requested Keine Daten erforderlich @@ -409,12 +380,10 @@ DetailDialog - Service provider data Angaben zum Diensteanbieter - close dialog Schließen Dialog @@ -422,12 +391,10 @@ DeveloperModeHistoryWidget - Disable Deaktivieren - Developer Mode: Enabled! Entwicklermodus: Aktiviert! @@ -435,12 +402,10 @@ DeveloperSettingsWidget - Self authentication test URI: Test URI für die Selbstauskunft: - use verwenden @@ -448,343 +413,311 @@ DeveloperView - Developer options Entwickleroptionen + + Test environment + Testumgebung + + + Use the test environment during a selfauthentication + Benutze die Test-Umgebung während der Selbstauskunft + + + Developer Mode + Entwicklermodus + + + Use a more tolerant mode + Benutze einen toleranten Modus + + + Change the layout style + Ändere den Style des Layouts + + + iOS + iOS + + + Android + Android + + + Tablet, Android + Tablet, Android + DiagnosisDialog - Diagnosis Diagnose - Save as... Speichern als... - Close Schließen + + EditRights + + You are about to identify yourself towards the following service provider: + Sie möchten sich bei folgendem Diensteanbieter ausweisen: + + + Purpose for reading out requested data + Zweck des Auslesevorgangs + + + Service provider + Diensteanbieter + + + more... + mehr... + + + The following data will be transferred to the service provider when you enter the PIN: + Folgende Daten Ihres Ausweises werden nach Eingabe der PIN ausgelesen und an den Diensteanbieter übermittelt: + + + Transactional information + Transaktionsinformationen + + + Required Data + Erforderliche Daten + + + Optional Data + Optionale Daten + + + Identify now + Jetzt ausweisen + + + Identify + Ausweisen + + EnterPinView - The entered PIN does not match the new PIN. Please correct your input. Die eingegebene PIN stimmt nicht mit Ihrer neuen PIN überein. Bitte korrigieren Sie Ihre PIN-Eingabe. - Please enter the six-digit card access number. You can find the card access number on the front of the ID card. Bitte geben Sie Ihre 6-stellige Zugangsnummer ein. Sie finden Ihre Zugangsnummer auf der Vorderseite Ihres Ausweises. - You have entered the wrong PIN twice. Prior to a third attempt, you have to enter your six-digit card access number first. You can find your card access number on the front of your ID card. Sie haben Ihre PIN zweimal falsch eingegeben. Für einen dritten Versuch müssen Sie vorher Ihre 6-stellige Zugangsnummer eingeben. Sie finden Ihre Zugangsnummer auf der Vorderseite Ihres Ausweises. - You have entered a wrong PIN three times. Your PIN is now blocked. You have to enter the PUK now for unblocking. Sie haben Ihre PIN dreimal falsch eingegeben. Ihre PIN ist jetzt gesperrt. Zum Entsperren geben Sie bitte Ihre PUK ein. - Please enter a new 6-digit PIN of your choice. Geben Sie nun bitte eine neue 6-stellige PIN Ihrer Wahl ein. - Please enter your new 6-digit PIN again. Wiederholen Sie bitte Ihre neue 6-stellige PIN. - Enter the pairing code shown on your other device to use it as a card reader. Geben Sie den Kopplungscode, der auf Ihrem anderen Gerät angezeigt wird, ein, um dieses als Kartenlesegerät verwenden zu können. - - Please enter your current PIN or your initial transport PIN first. - Geben Sie bitte zunächst Ihre aktuelle PIN bzw. die Transport-PIN ein. - - - Please enter your personal PIN. Geben Sie bitte Ihre persönliche PIN ein. + + Please enter your transport PIN. + Geben Sie bitte Ihre Transport-PIN ein. + + + Your PIN has 6 digits? + Ihre PIN ist 6-stellig? + + + Your PIN has 5 digits? + Ihre PIN ist 5-stellig? + Feedback - - Your opinion matters - Ihre Meinung zählt - - - - We are happy about every feedback on our software. - Wir freuen uns über Ihre Rückmeldung zu unserem Programm. - - - Rate AusweisApp2 Bewerten Sie die AusweisApp2 - Help & Feedback Hilfe & Feedback - FAQ FAQ - Do you have questions how to use AusweisApp2? Haben Sie Fragen zur Nutzung der AusweisApp2? - https://www.ausweisapp.bund.de/en/questions-and-answers/frequently-asked-questions/ https://www.ausweisapp.bund.de/fragen-und-antworten/haeufig-gestellte-fragen/ - Support Support - You need further help? Benötigen Sie weitere Hilfe? - https://www.ausweisapp.bund.de/en/questions-and-answers/support/ https://www.ausweisapp.bund.de/fragen-und-antworten/support/ - Please rate us in the Google Play Store. Bewerten Sie die AusweisApp2. - Report error Melden Sie einen Fehler - You found a bug? Please tell us, so we can fix it. Sie haben einen Fehler gefunden? Teilen Sie ihn uns mit, damit wir ihn beheben können. - - Android log file - Android Protokolle + Show log + Protokoll anzeigen - - <Please describe the error> - <Bitte beschreiben Sie den Fehler> + You can view the logs of the AusweisApp2 here. + Hier können Sie das Protokoll der AusweisApp2 einsehen. GeneralSettingsWidget - Software update: Softwareaktualisierungen: - History: Verlauf: - check on program start beim Programmstart prüfen - search for updates nach Aktualisierungen suchen - On screen password: Bildschirmtastatur: - use verwenden - Start AusweisApp2 automatically: AusweisApp2 automatisch starten: - on system start beim Systemstart - Close AusweisApp2 window automatically: Fenster der AusweisApp2 automatisch schließen: - after successful identification nach erfolgreichem Ausweisen - save speichern - check software update on program start Beim Start des Programms auf Software-Aktualisierung prüfen - save history Verlauf speichern - Start AusweisApp2 automatically on system startup AusweisApp2 automatisch beim Systemstart starten - Close AusweisApp2 window automatically after successful identification AusweisApp2 automatisch nach erfolgreicher Authentisierung schließen - use on screen password Bildschirmtastatur verwenden - HistoryContextMenu + HistoryListViewDelegate - - Disable history - Verlauf deaktivieren + today + heute - - Enable history - Verlauf aktivieren + yesterday + gestern - - History enabled - Verlauf aktiviert + dddd + dddd - - History disabled - Verlauf deaktiviert + dd.MM.yyyy + dd.MM.yyyy - - Delete all - Lösche alle - - - - Please confirm that you want to delete your complete history. - Bitte bestätigen Sie, dass der komplette Verlauf gelöscht werden soll. - - - - Delete last 4 weeks - Lösche die letzten 4 Wochen - - - - Please confirm that you want to delete your history from the last four weeks. - Bitte bestätigen Sie, dass der Verlauf der letzten vier Wochen gelöscht werden soll. - - - - Delete last week - Lösche letzte Woche - - - - Please confirm that you want to delete your history from the last week. - Bitte bestätigen Sie, dass der Verlauf der letzten Woche gelöscht werden soll. - - - - Delete last day - Lösche letzten Tag - - - - Please confirm that you want to delete your history from the last day. - Bitte bestätigen Sie, dass der Verlauf des letzten Tages gelöscht werden soll. - - - - Delete last hour - Lösche letzte Stunde - - - - Please confirm that you want to delete your history from the last hour. - Bitte bestätigen Sie, dass der Verlauf der letzten Stunde gelöscht werden soll. - - - - HistoryListView - - - Delete - Löschen + Tap for more details + Berühren Sie hier für mehr Details HistoryListViewDelegateContent - today heute - yesterday gestern - dddd dddd - dd.MM.yyyy dd.MM.yyyy - Tap for more details Berühren Sie hier für mehr Details @@ -792,108 +725,37 @@ HistoryView - - - - History Verlauf - - - - Currently there are no history entries. Derzeit gibt es keine Einträge im Verlauf. - - - Delete - Löschen - - - - Delete all Lösche alle - - This page displays the history of your successful authentications. Double-click on a service provider for more information. You can delete parts or the entire history. You can also save the history as a PDF file. - Auf dieser Seite sehen Sie den Verlauf Ihrer erfolgreichen Authentisierungen. Mit einem Doppelklick erhalten Sie weitere Informationen zum ausgewählten Diensteanbieter. Den Verlauf können Sie in Teilen oder auch komplett löschen. Es ist darüber hinaus auch möglich, den Verlauf zu speichern. - - - - Search: - Suche: - - - - No history entry available - Kein Eintrag vorhanden - - - - History: - Verlauf: - - - - save - speichern - - - - Delete History - Verlauf löschen - - - - Save as PDF - Speichern als PDF - - - - Delete history - Verlauf löschen - - - - Do you really want to delete the history? - Möchten Sie den Verlauf wirklich löschen? - - - - Save history - Verlauf speichern - - - - PDF Documents (*.pdf) - PDF Dokumente (*.pdf) + Please confirm that you want to delete your complete history. + Bitte bestätigen Sie, dass der komplette Verlauf gelöscht werden soll. HistoryViewConfirmationPopup - Delete history Verlauf löschen - Cancel Abbrechen - Delete Löschen - Removed %1 entries from the history. Es wurden %1 Einträge aus dem Verlauf entfernt. @@ -901,95 +763,92 @@ HistoryViewDetails - Provider Information Anbieterinformationen - Provider name Anbieter - Purpose Zweck - Date Datum - dd.MM.yyyy dd.MM.yyyy - Requested data Angeforderte Daten - Terms of usage Nutzungsbedingungen + + HistoryViewTitleBarControls + + History enabled + Verlauf aktiviert + + + History disabled + Verlauf deaktiviert + + + Please confirm that you want to delete your complete history. + Bitte bestätigen Sie, dass der komplette Verlauf gelöscht werden soll. + + HistoryWidget - This page displays the history of your successful authentications. Double-click on a service provider for more information. You can delete parts or the entire history. You can also save the history as a PDF file. Auf dieser Seite sehen Sie den Verlauf Ihrer erfolgreichen Authentisierungen. Mit einem Doppelklick erhalten Sie weitere Informationen zum ausgewählten Diensteanbieter. Es ist darüber hinaus auch möglich, den Verlauf zu speichern. - No matching history entries were found. Please modify your search criteria. Es wurden keine Verlaufseinträge gefunden. Bitte ändern Sie Ihr Suchkriterium. - Delete history... Verlauf löschen... - Save as PDF... Als PDF speichern... - History: Verlauf: - save speichern - Search: Suche: - Please enter your search Bitte geben Sie Ihre Suche ein - save history: Verlauf speichern: - save history Verlauf speichern - save history as PDF Verlauf als PDF speichern @@ -997,7 +856,6 @@ IdentifyController - You may now remove your ID card from the device. Sie können nun Ihr Ausweisdokument vom Gerät entfernen. @@ -1005,246 +863,125 @@ IdentifyView - - - - - Identify Ausweisen - Authentication in progress Authentisierung wird durchgeführt - - + Acquiring provider certificate + Lade Berechtigungszertifikat herunter + + Please wait a moment... Bitte warten Sie einen Moment... - The online identification function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the online identification function. Die Online-Ausweisfunktion Ihres Ausweisdokumentes ist nicht aktiviert. Bitte wenden Sie sich an die Behörde, die Ihr Ausweisdokument ausgegeben hat, um die Online-Ausweisfunktion zu aktivieren. - Please observe the display of your card reader. Bitte beachten Sie die Anzeige Ihres Kartenlesegeräts. - You have entered the wrong PIN twice. Prior to a third attempt, you have to enter your six-digit card access number first. You can find your card access number on the front of your ID card. Sie haben Ihre PIN zweimal falsch eingegeben. Für einen dritten Versuch müssen Sie vorher Ihre 6-stellige Zugangsnummer eingeben. Sie finden Ihre Zugangsnummer auf der Vorderseite Ihres Ausweises. - - You have entered a wrong PIN three times. Your PIN is now blocked. You have to enter the PUK now for unblocking. - Sie haben Ihre PIN dreimal falsch eingegeben. Ihre PIN ist jetzt gesperrt. Zum Entsperren geben Sie bitte Ihre PUK ein. - - - Service provider is being verified Diensteanbieter wird geprüft - Card is being verified Karte wird geprüft - Reading data Daten werden gelesen - Sending data to service provider Sende Daten an den Dienstanbieter - Preparing results Bereite Ergebnisse vor - No network connectivity Keine Netzwerkverbindung - Please enable the network interface or cancel the workflow. Bitte stellen Sie eine Internetverbindung her oder beenden Sie den Vorgang. - - - IdentifyViewContent - - - - - Service provider - Diensteanbieter + Identify view + Ausweisen-Ansicht - - - - - Purpose for reading out requested data - Zweck des Auslesevorgangs + This is the identify view of the AusweisApp2. + Dies ist die Ausweisen-Ansicht der AusweisApp2. - - - - - Identify now - Jetzt ausweisen + Weak NFC signal. Please reposition your card. + Schwacher NFC-Empfang. Bitte korrigieren Sie die Position Ihres Ausweises. - - - - - Transactional information - Transaktionsinformationen + Change transport PIN + Transport-PIN ändern - - - - - Required Data - Erforderliche Daten + Change PIN + PIN ändern - - - - - Optional Data - Optionale Daten + You leave the process and are forwarded to the PIN management. Please restart the desired process after the PIN has been changed. + Sie verlassen den Vorgang und werden zur PIN-Verwaltung weitergeleitet. Starten Sie den gewünschten Vorgang bitte nach der PIN-Änderung erneut. - - - - - You are about to identify yourself towards the following service provider: - Sie möchten sich bei folgendem Diensteanbieter ausweisen: + Retry + Erneut versuchen - - - - - Identify - Ausweisen - - - - - - - The following data will be transferred to the service provider when you enter the PIN: - Folgende Daten Ihres Ausweises werden nach Eingabe der PIN ausgelesen und an den Diensteanbieter übermittelt: - - - - IdentifyViewInfo - - - You can use your ID card anywhere you see this logo. - Überall wo Sie dieses Logo sehen, können Sie Ihr Ausweisdokument einsetzen. - - - - Use the button 'See my personal data' to display the data stored on your ID card. An Internet connection is required to display the data. - Über die Schaltfläche 'Meine Daten einsehen' können Sie sich die im Chip Ihres Ausweisdokuments gespeicherten Daten anzeigen lassen. Um die Daten anzeigen zu können, benötigt diese Anwendung eine Internetverbindung. - - - - Your personal data is neither saved nor processed in any way. Please see our %1 for details on how your personal data is processed. - Es erfolgt keine Speicherung oder Weiterverarbeitung Ihrer persönlichen Daten. Näheres dazu erfahren Sie in unserer %1. - - - - https://www.ausweisapp.bund.de/datenschutz/ - https://www.ausweisapp.bund.de/datenschutz/ - - - - data privacy statement - Datenschutzerklärung - - - - See my personal data - Meine Daten einsehen - - - - IdentifyWorkflow - - - Identify - Ausweisen + Cancel authentication process + Beende Ausweisvorgang Information - - You need help? - Benötigen Sie Hilfe? - - - - Here you are in the right place. - Dann sind Sie hier richtig. - - - Information Information - Version information Versionsinformationen - Here you can see detailed information about AusweisApp2. Hier finden Sie detaillierte Informationen zur AusweisApp2. - Software license Softwarelizenz - Read the software license text on the application homepage. Lesen Sie die Softwarelizenz auf der Internetseite der Anwendung. - https://www.ausweisapp.bund.de/en/download/android/ https://www.ausweisapp.bund.de/download/android/ - https://www.ausweisapp.bund.de/en/download/ios/ https://www.ausweisapp.bund.de/download/ios/ - https://www.ausweisapp.bund.de/en/download/windows-and-mac/ https://www.ausweisapp.bund.de/download/windows-und-mac/ @@ -1252,241 +989,272 @@ KnownDevicesListDelegate - Available Verfügbar - Available, but unsupported Verfügbar, aber nicht unterstützt - Last connection: Letzte Verbindung: - ListViewDelegateContent + Log - - today - heute + Log + Protokoll - - yesterday - gestern + Delete + Löschen - - dddd - dddd + Log: + Protokoll: - - dd.MM.yyyy - dd.MM.yyyy + Please confirm that you want to delete your old logfiles. + Bitte bestätigen Sie, dass alle alten Protokolle gelöscht werden sollen. + + + Delete all + Lösche alle + + + Please confirm that you want to delete the logfile. + Bitte bestätigen Sie, dass das Protokoll gelöscht werden soll. LogFilesDialog - Log files Protokolle - File: Datei: - Save... Speichern... - Delete old files... Alte Dateien löschen... - Close Schließen + + MainView + + Main view + Hauptseite + + + This is the main view of the AusweisApp2. + Dies ist die Hauptseite der AusweisApp2. + + + Identify + Ausweisen + + + Provider + Anbieter + + + History + Verlauf + + + PIN management + PIN-Verwaltung + + + Settings + Einstellungen + + + Help + Hilfe + + + You can use your ID card anywhere you see this logo. + Überall wo Sie dieses Logo sehen, können Sie Ihr Ausweisdokument einsetzen. + + + Use the button 'See my personal data' to display the data stored on your ID card. An Internet connection is required to display the data. + Über die Schaltfläche 'Meine Daten einsehen' können Sie sich die im Chip Ihres Ausweisdokuments gespeicherten Daten anzeigen lassen. Um die Daten anzeigen zu können, benötigt diese Anwendung eine Internetverbindung. + + + Your personal data is neither saved nor processed in any way. Please see our %1 for details on how your personal data is processed. + Es erfolgt keine Speicherung oder Weiterverarbeitung Ihrer persönlichen Daten. Näheres dazu erfahren Sie in unserer %1. + + + https://www.ausweisapp.bund.de/datenschutz/ + https://www.ausweisapp.bund.de/datenschutz/ + + + data privacy statement + Datenschutzerklärung + + + See my personal data + Meine Daten einsehen + + MoreView - More Mehr - Version information Versionsinformationen - FAQ FAQ - https://www.ausweisapp.bund.de/en/questions-and-answers/frequently-asked-questions/ https://www.ausweisapp.bund.de/fragen-und-antworten/haeufig-gestellte-fragen/ - Support Support - https://www.ausweisapp.bund.de/en/questions-and-answers/support/ https://www.ausweisapp.bund.de/fragen-und-antworten/support/ - Rate app Bewerten - https://www.ausweisapp.bund.de/en/questions-and-answers/evaluate-us/ https://www.ausweisapp.bund.de/fragen-und-antworten/bewerten-sie-uns/ - Software license Softwarelizenz - https://www.ausweisapp.bund.de/en/download/android/ https://www.ausweisapp.bund.de/download/android/ - https://www.ausweisapp.bund.de/en/download/ios/ https://www.ausweisapp.bund.de/download/ios/ - https://www.ausweisapp.bund.de/en/download/windows-and-mac/ https://www.ausweisapp.bund.de/download/windows-und-mac/ - Configure remote service Fernzugriff konfigurieren - Developer options Entwickleroptionen + + Show log + Protokoll anzeigen + + + Tutorial + Tutorial + NavigationView - - Identify Ausweisen - - Provider Anbieter - - History Verlauf - - PIN Management PIN-Verwaltung - Help & Feedback Hilfe & Feedback - Information Information - Smartphone as card reader Smartphone als Kartenlesegerät - Developer options Entwickleroptionen - More Mehr + + Tutorial + Tutorial + NfcWorkflow - NFC is not supported by your device. Ihr Gerät unterstützt kein NFC. - Please try Bluetooth. Bitte versuchen Sie Bluetooth. - NFC is switched off. NFC ist nicht aktiv. - Go to NFC settings Zu den NFC Einstellungen - Please enable NFC in your system settings. Bitte aktivieren Sie NFC in Ihren Systemeinstellungen. - Establish connection Verbindung wird hergestellt - Your device does not meet the technical requirements (Extended Length not supported). You require an additional 'Bluetooth card reader' or an additional 'smartphone as card reader' to use the online identification function with this device. - Ihr Gerät erfüllt leider nicht die technischen Voraussetzungen (Extended Length). Sie können die Online-Ausweisfunktion daher mit diesem Gerät nur über einen separaten Bluetooth-Leser oder mit einem separaten Smartphone als Kartenleser nutzen. + Ihr Gerät erfüllt leider nicht die technischen Voraussetzungen (Extended Length). Sie können die Online-Ausweisfunktion daher mit diesem Gerät nur über einen separaten Bluetooth-Leser oder mit einem separaten Smartphone als Kartenlesegerät nutzen. - The online identification function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the online identification function. Die Online-Ausweisfunktion Ihres Ausweisdokumentes ist nicht aktiviert. Bitte wenden Sie sich an die Behörde, die Ihr Ausweisdokument ausgegeben hat, um die Online-Ausweisfunktion zu aktivieren. - Please place your device<br/>on your ID card. Bitte platzieren Sie Ihr Gerät<br/>über Ihrem Personalausweis. @@ -1494,22 +1262,14 @@ PinSettingsWidget - - open on screen keyboard - öffne bildschirmtastatur - - - Please pay attention to the display of your card reader. Bitte beachten Sie die Anzeige auf Ihrem Kartenlesegerät. - <h4>PIN successfully changed</h4> <h4>Änderung der PIN erfolgreich</h4> - Click on "Change PIN" if you want to change your PIN again. If not, you can now remove your ID card form the card reader. @@ -1518,7 +1278,6 @@ If not, you can now remove your ID card form the card reader. Anderenfalls können Sie nun Ihr Ausweisdokument vom Kartenlesegerät entfernen. - <html> <h4>No card reader detected. Please make sure that a card reader is connected.</h4> <p>If you need help or have problems with your card reader click on the "Diagnosis" button for further information. @@ -1530,13 +1289,12 @@ However, you can change your PIN on your smartphone directly as long as the remo <html> <h4>Es wurde kein Kartenlesegerät gefunden. Bitte stellen Sie sicher, dass ein Kartenlesegerät angeschlossen ist.</h4> <p>Wenn Sie Hilfe bei der Einrichtung Ihres Kartenlesegerät benötigen, klicken Sie auf "Diagnose".</p> -<p>Beachten Sie: Es ist zurzeit noch nicht möglich, Ihre PIN mithilfe der "Smartphone als Kartenleser"-Funktionalität zu ändern. +<p>Beachten Sie: Es ist zurzeit noch nicht möglich, Ihre PIN mithilfe der "Smartphone als Kartenlesegerät"-Funktionalität zu ändern. Sie können jedoch Ihre PIN direkt am Smartphone ändern solange der Fernzugriff deaktiviert ist. </p> </html> - <html> <h4>Please place your ID card on the card reader.</h4> <p>If you have already placed an ID card on the card reader but it is not displayed here, please click on "Diagnosis".</p> @@ -1547,7 +1305,6 @@ Sie können jedoch Ihre PIN direkt am Smartphone ändern solange der Fernzugriff </html> - <html> <h4>Several ID cards detected</h4> <p>Please place just one ID card on the card reader.</p> @@ -1558,12 +1315,10 @@ Sie können jedoch Ihre PIN direkt am Smartphone ändern solange der Fernzugriff </html> - Please make sure that only one card reader with an ID card on it is connected to your computer. Bitte stellen Sie sicher, dass an Ihrem Computer nur ein Kartenlesegerät mit aufliegendem Ausweisdokument angeschlossen ist. - <html> <h4>eID feature deactivated</h4> <p>The eID function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the eID function.</p> @@ -1574,7 +1329,6 @@ Sie können jedoch Ihre PIN direkt am Smartphone ändern solange der Fernzugriff </html> - Select a secure PIN that consists of six digits. Do not select a number that can be guessed easily, such as "123456", your date of birth or any other number that is printed on your ID card. When you change your PIN for the first time, please enter your five-digit transport PIN in the field "Current PIN / Transport PIN". You received your transport PIN with the letter sent to you by your competent authority. @@ -1585,97 +1339,78 @@ Please note that the PIN may only consist of digits (0-9). Bei der erstmaligen PIN-Änderung geben Sie bitte in das Feld "Aktuelle PIN / Transport-PIN" Ihre 5-stellige Transport-PIN ein. Sie finden die Transport-PIN in dem Schreiben, das Sie nach Beantragung Ihres Ausweisdokuments von der für die Ausgabe Ihres Ausweisdokuments zuständigen Behörde erhalten haben. - You have entered the wrong PIN two times. For a third attempt you first have to enter your six-digit card access number. You can find your card access number on the front side of your ID card next to the date of expiry. On the electronic residence permit the card access number is printed above your signature. Sie haben Ihre PIN zweimal falsch eingegeben. Für einen dritten Versuch müssen Sie zunächst Ihre 6-stellige Zugangsnummer eingeben. Die Zugangsnummer finden Sie beim Personalausweis auf der Vorderseite rechts neben dem letzten Tag der Gültigkeitsdauer. Beim elektronischen Aufenthaltstitel finden Sie die Zugangsnummer über Ihrer Unterschrift. - <h4>PUK entry successful</h4><p>Your ID card is unblocked. You now have three more tries to change your PIN.</p> <h4>Eingabe der PUK erfolgreich</h4><p>Ihr Ausweis ist entsperrt. Sie haben nun erneut drei Versuche, Ihre PIN zu ändern.</p> - Current PIN / Transport PIN: Aktuelle PIN / Transport-PIN: - New PIN: Neue PIN: - Confirm: Bestätigen: - Card access number (CAN): Zugangsnummer (CAN): - PUK: PUK: - Click on "Change PIN" to enter a new PIN. Klicken Sie auf "PIN ändern", um eine neue PIN zu setzen. - Click on "Enter PUK" to unblock your ID card. Klicken Sie auf "PUK eingeben", um Ihren Ausweis zu entsperren. - deactivatedReaderImageLabel deactivatedReaderImageLabel - Click on "Change PIN" to enter your card access number and then set a new PIN. You can find your card access number on the front side of your ID card next to the date of expiry. On the electronic residence permit the card access number is printed above your signature. "Klicken Sie auf "PIN ändern", um zunächst Ihre Zugangsnummer einzugeben und anschließend eine neue PIN zu setzen. Die Zugangsnummer finden Sie beim Personalausweis auf der Vorderseite rechts neben dem letzten Tag der Gültigkeitsdauer. Beim elektronischen Aufenthaltstitel finden Sie die Zugangsnummer über Ihrer Unterschrift. - CAN on nPA icon Zugangsnummer auf nPA Icon - card reader icon Icon des Kartenlesegeräts - no reader icon Kein Kartenlesegerät Icon - multiple card reader icon Mehrer Kartenlesegerät Icon - deactivated card reader icon Deaktiverter Kartenlesegerät Icon - successful PIN change icon PIN änderung erfolgreich icon - no id card icon Kein Ausweisdokument icon - You have entered the wrong PIN three times. The online identification function is now blocked. Please use your personal unblocking key (PUK) to unblock your ID card. You received the PUK with the letter sent to you by your competent authority. Please note that you can only use the PUK to unblock the eID function. If you have forgotten your PIN, you can have a new PIN set at your competent authority. @@ -1684,107 +1419,24 @@ Please note that you can only use the PUK to unblock the eID function. If you ha Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entsperren können. Sollten Sie Ihre PIN vergessen haben, können Sie von der für die Ausgabe Ihres Ausweisdokuments zuständigen Behörde eine neue PIN setzen lassen. - <html> <h4>Extended Length is not supported.</h4> <p>Your remote reader does not meet the technical requirements (Extended Length not supported).</p> </html> - Ihr entferntes Kartenlesegerät erfüllt leider nicht die technischen Voraussetzungen (Extended Length wird nicht unterstützt). - - - - PinView - - - - - PIN Management - PIN-Verwaltung + Ihr entferntes Kartenlesegerät erfüllt leider nicht die technischen Voraussetzungen (Extended Length wird nicht unterstützt).< - - - Change PIN - PIN ändern - - - - - Please wait a moment... - Bitte warten Sie einen Moment... - - - - The online identification function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the online identification function. - Die Online-Ausweisfunktion Ihres Ausweisdokumentes ist nicht aktiviert. Bitte wenden Sie sich an die Behörde, die Ihr Ausweisdokument ausgegeben hat, um die Online-Ausweisfunktion zu aktivieren. - - - - Please observe the display of your card reader. - Bitte beachten Sie die Anzeige Ihres Kartenlesegeräts. - - - - You have entered the wrong PIN twice. Prior to a third attempt, you have to enter your six-digit card access number first. You can find your card access number on the front of your ID card. - Sie haben Ihre PIN zweimal falsch eingegeben. Für einen dritten Versuch müssen Sie vorher Ihre 6-stellige Zugangsnummer eingeben. Sie finden Ihre Zugangsnummer auf der Vorderseite Ihres Ausweises. - - - - You have entered a wrong PIN three times. Your PIN is now blocked. You have to enter the PUK now for unblocking. - Sie haben Ihre PIN dreimal falsch eingegeben. Ihre PIN ist jetzt gesperrt. Zum Entsperren geben Sie bitte Ihre PUK ein. - - - - PinViewContent - - - - PIN Management - PIN-Verwaltung - - - - - You have the opportunity to change your transport PIN into a personal PIN. You can also change the PIN at any time or unblock the PIN using the personal unblocking key (PUK). The transport PIN and the PUK can be found in the letter sent to you by your competent authority. - Hier haben Sie die Möglichkeit, Ihre Transport-PIN in eine persönliche PIN zu ändern. Zudem können Sie jederzeit Ihre persönliche PIN ändern oder eine Blockierung mit Hilfe der Entsperrnummer (PUK) aufheben. Sie finden Ihre initiale PIN und die PUK in dem Schreiben, das Sie nach Beantragung Ihres Ausweisdokuments von der für die Ausgabe Ihres Ausweisdokuments zuständigen Behörde erhalten haben. - - - - - Change PIN now - Jetzt PIN ändern - - - - PinWorkflow - - - PIN Management - PIN-Verwaltung + open on screen keyboard + öffne bildschirmtastatur ProviderContactInfo - Contact Kontakt - - Unknown - Unbekannt - - - - ProviderContactInfo_tablet - - - Contact - Kontakt - - - Unknown Unbekannt @@ -1792,7 +1444,6 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe ProviderContactTab - Unknown Unbekannt @@ -1800,15 +1451,6 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe ProviderDetailButtonBar - - ONLINE-APPLICATION - ONLINE-ANWENDUNG - - - - ProviderDetailButtonBar_tablet - - ONLINE APPLICATION Online-Anwendung @@ -1816,116 +1458,78 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe ProviderDetailDescription - Description Beschreibung - ProviderDetailDescription_tablet + ProviderDetailHistory - - Description - Beschreibung + History + Verlauf + + + Purpose for reading out requested data + Zweck des Auslesevorgangs - ProviderDetailHistoryInfo_tablet + ProviderDetailHistoryInfo - Service provider Diensteanbieter - Purpose for reading out requested data Zweck des Auslesevorgangs - Read data Daten auslesen - Terms of usage Nutzungsbedingungen - ProviderDetailHistoryItem_tablet + ProviderDetailHistoryItem - today heute - yesterday gestern - dddd dddd - dd.MM.yyyy dd.MM.yyyy - Touch for more details Berühren Sie hier für mehr Details - - ProviderDetailHistory_tablet - - - History - Verlauf - - - - Purpose for reading out requested data - Zweck des Auslesevorgangs - - ProviderDetailView - DESCRIPTION BESCHREIBUNG - CONTACT KONTAKT - - Description - Beschreibung - - - - Contact - Kontakt - - - - Description not available Beschreibung nicht verfügbar - - - ProviderDetailsDialog - Provider details Anbieterdetails @@ -1933,7 +1537,6 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe ProviderHeader - To service provider Zum Anbieter @@ -1941,34 +1544,29 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe ProviderInfoSection - Touch for more details Berühren Sie hier für mehr Details + + See details under "more..." + Weitere Details unter "mehr..." + ProviderModelItem - - Homepage Homepage - - E-Mail E-Mail - - Phone Telefon - - Contact Kontakt @@ -1976,63 +1574,41 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe ProviderView - - + Provider view + Anbieteransicht + + + This is the provider view of the AusweisApp2. + Dies ist die Anbieteransicht der AusweisApp2. + + Provider Anbieter - - - Citizen services Bürgerdienste - - - Insurances Versicherungen - - - Financials Finanzen - - - Other services Weitere Services - - - - - No match found Kein Ergebnis gefunden - - - This section lists offers of service providers that support online identification. Click on an entry to go to the provider&apos;s web site. This section will be continuously updated with further applications for the online identification function. - Dieser Bereich enthält eine Liste von Anbietern, die eine Online-Authentisierung anbieten. Diese Liste wird ständig aktualisiert. - - - - Search: - Suche: - ProviderViewDelegate - i i @@ -2040,22 +1616,18 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe ProviderWidget - No matching providers were found. Please modify your search criteria. Es wurden keine Anbieter gefunden. Bitte ändern Sie Ihr Suchkriterium. - This section lists offers of service providers that support online identification. Click on an entry to go to the provider's web site. This section will be continuously updated with further applications for the online identification function. Diese Dialogseite listet die Angebote von Diensteanbietern auf, die die Online-Ausweisfunktion unterstützen. Klicken Sie auf einen Eintrag, um auf die Webseite des Diensteanbieters zu gelangen. Diese Dialogseite wird kontinuierlich mit weiteren Angeboten zur Online-Ausweisfunktion aktualisiert. - Search: Suche: - Please enter your search Bitte geben Sie Ihre Suche ein @@ -2063,27 +1635,22 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe RandomPinDialog - OK OK - Cancel Abbrechen - Clear Löschen - PIN field PIN-Eingabefeld - Screen keyboard Bildschirmtastatur @@ -2091,7 +1658,6 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe ReaderConfigurationInfo - Unknown reader Unbekanntes Kartenlesegerät @@ -2099,12 +1665,10 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe ReaderDeviceDialog - Diagnosis Diagnose - Close Schließen @@ -2112,37 +1676,30 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe ReaderDeviceWidget - In order to use the online identification function of your ID card you need a separate card reader or a suitable smartphone. The following overview shows the status of a connected card reader or connected smartphone. Um die Online-Ausweisfunktion des Personalausweises nutzen zu können, benötigen Sie ein separates Kartenlesegerät oder auch ein geeignetes Smartphone. Die nachfolgende Übersicht stellt dar, in welchem Status sich ein angeschlossenes Kartenlesegerät oder verbundenes Smartphone befindet. - Smartphone as card reader - Smartphone als Kartenleser + Smartphone als Kartenlesegerät - Pair Koppeln - Forget Verwerfen - Card readers Kartenlesegeräte - After connecting a new card reader it may take a few seconds to recognize the driver. It may be necessary to restart your system after installing the driver. Nachdem ein neues Kartenlesegerät angeschlossen worden ist, kann es einige Sekunden dauern bis der Treiber erkannt wird. Unter Umständen kann ein Neustart Ihres Betriebssystems notwendig sein. - TextLabel TextLabel @@ -2150,12 +1707,10 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe RemotePinInputDialog - Pairing code entry Eingabe des Kopplungscodes - Enter the 4-digit pairing code shown on your other device. Geben Sie den 4-stelligen Kopplungscode ein, der auf Ihrem anderen Gerät angezeigt wird. @@ -2163,22 +1718,18 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe RemoteServicePairingPopup - Pairing code Kopplungscode - Enter the pairing code shown on your other device to use it as a card reader Geben Sie den Kopplungscode ein, der auf Ihrem anderen Gerät angezeigt wird, um es als Kartenlesegerät zu verwenden - Enter this code on your other device to use this device as a card reader Geben Sie diesen Code auf Ihrem anderen Gerät ein, um dieses Gerät als Kartenlesegerät zu verwenden - Start pairing Kopplung starten @@ -2186,173 +1737,153 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe RemoteServiceSettings - Configure remote service Fernzugriff konfigurieren - - - Pairing failed. Please try again to activate pairing on your other device and enter the shown pairing code. - Die Kopplung ist fehlgeschlagen. Bitte starten Sie eine neue Kopplung an Ihrem anderen Gerät und geben den angezeigten Kopplungscode ein. - - - - Device name - Gerätename - - - - Set device name: - Wählen Sie einen Gerätenamen: - - - - PIN pad mode - Tastaturmodus - - - - Enter PIN on this device - PIN-Eingabe auf diesem Gerät - - - - Paired devices - Gekoppelte Geräte - - - - No device is paired. - Kein Gerät gekoppelt. - - - - Available devices - Verfügbare Geräte - - - - No new remote reader was found on your network. Make sure that the remote reader functionality in AusweisApp2 on your other device is activated and that your devices are connected to the same network. - Kein entferntes Kartenlesegerät in Ihrem Netzwerk verfügbar. Bitte stellen Sie sicher, dass die Funktion "Fernzugriff" in der AusweisApp2 auf Ihrem anderen Gerät aktiviert ist. Beide Geräte müssen sich im selben Netzwerk befinden. - - - - Please start pairing mode first. - Starten Sie den Kopplungsmodus auf Ihrem Smartphone, falls noch nicht geschehen. - - - - OK - OK - - - - Pairing code - Kopplungscode - RemoteServiceView - - Smartphone as card reader - Smartphone als Kartenlesegerät + Configure local settings + Konfiguriere lokale Einstellungen - + Pair remote devices + Kopple entferntes Kartenlesegerät + + + Remote service + Fernzugriff + + + Pairing failed. Please try again to activate pairing on your other device and enter the shown pairing code. + Die Kopplung ist fehlgeschlagen. Bitte starten Sie eine neue Kopplung an Ihrem anderen Gerät und geben den angezeigten Kopplungscode ein. + + + + RemoteServiceViewLocal + + Device name + Gerätename + + + Set device name: + Wählen Sie einen Gerätenamen: + + + PIN pad mode + Tastaturmodus + + + Enter PIN on this device + PIN-Eingabe auf diesem Gerät + + + + RemoteServiceViewRemote + + Paired devices + Gekoppelte Geräte + + + No device is paired. + Kein Gerät gekoppelt. + + + Available devices + Verfügbare Geräte + + + No new remote reader was found on your network. Make sure that the remote reader functionality in AusweisApp2 on your other device is activated and that your devices are connected to the same network. + Kein entferntes Kartenlesegerät in Ihrem Netzwerk verfügbar. Bitte stellen Sie sicher, dass die Funktion "Fernzugriff" in der AusweisApp2 auf Ihrem anderen Gerät aktiviert ist. Beide Geräte müssen sich im selben Netzwerk befinden. + + + Please start pairing mode first. + Starten Sie den Kopplungsmodus auf Ihrem Smartphone, falls noch nicht geschehen. + + + OK + OK + + + Pairing code + Kopplungscode + + + + RemoteServiceViewStartStop + Please start the remote service in order to use your smartphone as a card reader with AusweisApp2. Please note: Both your devices have to be connected to the same WiFi. Bitte starten Sie den Fernzugriff, damit Sie Ihr Smartphone als Kartenlesegerät für die AusweisApp2 nutzen können. Bitte beachten Sie: dies ist nur möglich, wenn beide Geräte mit demselben WLAN verbunden sind. - Enable NFC NFC aktivieren - Stop remote service Fernzugriff stoppen - Start remote service Fernzugriff starten - Start pairing Kopplung starten - Card access in progress Kartenzugriff - Please pay attention to the display on your other device %1. Bitte beachten Sie die Anzeige auf Ihrem anderen Gerät %1. - - - Settings - Einstellungen - RemoteWorkflow - Continue Fortsetzen - Pair device Gerät koppeln - Enable Wifi WLAN aktivieren - Establish connection Verbindung wird hergestellt - Your remote device does not meet the technical requirements (Extended Length not supported). Ihr entferntes Kartenlesegerät erfüllt leider nicht die technischen Voraussetzungen (Extended Length wird nicht unterstützt). - The online identification function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the online identification function. Die Online-Ausweisfunktion Ihres Ausweisdokumentes ist nicht aktiviert. Bitte wenden Sie sich an die Behörde, die Ihr Ausweisdokument ausgegeben hat, um die Online-Ausweisfunktion zu aktivieren. - Connected to %1. Please insert your ID card. Verbunden mit %1. Bitte legen Sie Ihr Ausweisdokument auf. - Determine card Ermittle Ausweis - The device %1 was unpaired because it does not react to connection attempts. Retry the pairing process if you want to use this device to authenticate yourself. Das Gerät %1 wurde entkoppelt, da es nicht auf Verbindungsversuche reagiert hat. Versuchen Sie das Gerät erneut zu koppeln, wenn Sie es zur Authentisierung verwenden möchten. - To use the remote service WiFi has to be activated. Please activate WiFi in your device settings. Um den Fernzugriff zu nutzen, muss WLAN aktiviert werden. Bitte aktivieren Sie WLAN in Ihren Einstellungen. - No paired and activated remote device was detected. Make sure that you have started remote service on you remote device. Kein gekoppeltes und aktiviertes Gerät gefunden. Stellen Sie sicher, dass der Fernzugriff auf Ihrem Gerät gestartet wurde. @@ -2360,47 +1891,70 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe ResultView - - OK OK + + Result view + Ergebnis-Ansicht + + + This is the result of an authentication. + Dies ist das Ergebnis einer Authentisierung. + + + Send log file + Sende Protokoll + SearchBar - Search Suchen - Cancel Abbrechen + + SectionSwitch + + Service + Dienst + + + Pairing + Kopplung + + + Settings + Einstellungen + + SelfAuthenticationData - Identify Ausweisen - Successfull reading data Daten erfolgreich gelesen - OK OK + + Read data + Daten auslesen + SelfInfoWidget - The following data has been read out from your ID card: Folgende Daten wurden aus Ihrem Ausweisdokument ausgelesen: @@ -2408,22 +1962,18 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe SelfInformationWidget - You can use your ID card anywhere you see this logo. Überall wo Sie dieses Logo sehen, können Sie Ihr Ausweisdokument einsetzen. - See my personal data Meine Daten einsehen - See my personal data now... Meine Daten jetzt einsehen... - eID Logo eID Logo @@ -2431,32 +1981,26 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe SettingsWidget - General Allgemein - PIN Management PIN-Verwaltung - Diagnosis... Diagnose... - Cancel Abbrechen - Apply Übernehmen - Card Readers Kartenlesegeräte @@ -2464,146 +2008,983 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe StepAuthenticationEac1Widget - AusweisApp2 AusweisApp2 - Service provider Diensteanbieter - Purpose for reading out requested data: Zweck des Auslesevorgangs: - Name: Name: - more... mehr... - Data Daten - Important transactional information Wichtige Transaktionsinformationen - The following data is required by the service provider. You can deselect the non-mandatory data fields if you do not want this data to be transmitted. Folgende Daten werden von dem Diensteanbieter benötigt. Bei Datenfeldern, die als "abwählbar" gekennzeichnet sind, können Sie entscheiden, ob Sie diese Daten übermitteln möchten. - details Details + + StoreFeedbackPopup + + Would you like to rate this app? + Möchten Sie diese App bewerten? + + + No, Thanks + Nein danke + + + Rate + Bewerten + + + We would be very grateful if you could leave a rating on the Google Play Store! + Wir würden uns sehr über eine Bewertung im Google Play Store freuen! + + TechnologySwitch - NFC NFC - WiFi WLAN - Bluetooth Bluetooth - Use WiFi card reader instead<br/>of Bluetooth card reader WLAN Kartenlesegerät anstelle <br/>vom Bluetooth Kartenlesegerät verwenden - Use Bluetooth card reader instead<br/>of remote card reader Bluetooth Kartenlesegerät anstelle <br/>vom WLAN Kartenlesegerät verwenden + + TitleBar + + Titlebar + Titelleiste + + + This bar represents the navigation tree of the AusweisApp2. + Diese Leiste ist eine Repräsentation des Navigationsbaums der AusweisApp2. + + + Start + Start + + + Settings + Einstellungen + + + Help + Hilfe + + TitleBarAction - Cancel Abbrechen - Edit Bearbeiten - < back < Zurück + + TutorialFooter + + Fold in + Einklappen + + + Quit tutorial + Tutorial beenden + + + + TutorialHow + + You can find a list of compatible NFC-capable smartphones here: + Eine Liste kompatibler NFC-fähiger Smartphones finden Sie hier: + + + Direct connection via NFC chip + Direktes Auslesen über NFC-Chip + + + App on Android smartphone <b>with</b> NFC chip as card reader + App auf Android-Telefon <b>mit</b> NFC-Chip als Kartenleser + + + Smartphone as card reader + Smartphone als Kartenleser + + + App on computer <b>without</b> NFC chip + App auf Computer <b>ohne</b> NFC-Chip + + + Android smartphone <b>with</b> NFC chip as card reader + Android-Telefon <b>mit</b> NFC-Chip als Kartenlesegerät + + + App on tablet or smartphone <b>without</b> NFC chip + App auf Tablet oder Telefon <b>ohne</b> NFC-Chip + + + Using a bluetooth card reader + Mit Bluetooth-Kartenlesegerät + + + App on smartphone or tablet + App auf Telefon oder Tablet + + + Bluetooth card reader + Bluetooth-Kartenlesegerät + + + Another tip + Noch ein Tipp + + + For lenghty forms, e.g. a BAföG application, we recommend you to use the AusweisApp2 on a computer... + Für aufwändigere Formulare wie z.B. den BAföG-Antrag empfehlen wir Ihnen die AusweisApp2 am Computer zu verwenden... + + + Filling long forms is no fun on a smartphone! + Längere Anträge ausfüllen macht am Mobiltelefon keinen Spaß! + + + ... and to use a smartphone to communicate with your ID card. A USB reader is of course also an alternative. + ... und zum Lesen des Ausweises das Smartphone als Kartenlesegerät zu nutzen. Alternativ geht natürlich auch ein USB-Lesegerät. + + + https://www.ausweisapp.bund.de/mobile-geraete/ + https://www.ausweisapp.bund.de/mobile-geraete/ + + + How can I use the AusweisApp2 on my smartphone? + Wie kann ich die AusweisApp2 auf meinem Smartphone nutzen? + + + Many Android devices can access the id card via the NFC interface. + Viele Android-Geräte mit NFC können den Ausweis auslesen. + + + The AusweisApp2 offers the following options to access your id card: + Die AusweisApp2 bietet zum Auslesen die folgenden Möglichkeiten: + + + + TutorialImportant + + Please exchange your + Bitte tauschen Sie Ihre + + + Before you use the online ID function please change the + Vor der erstmaligen Nutzung der Online-Ausweisfunktion bitte die + + + 5 digits long + 5-stellige + + + transport PIN + Transport-PIN + + + with a personal + durch eine persönliche + + + 6 digits long PIN + 6-stellige PIN + + + before you use the online ID function! + bevor Sie die Online-Ausweisfunktion verwenden! + + + change! + ersetzen! + + + Choose for this purpose the menu entry PIN management + Hierzu den Menüpunkt PIN-Verwaltung auswählen + + + Let's go + Los geht's + + + Do you still have questions? + Oder haben Sie noch Fragen? + + + You can read our <b>FAQs</b> or <b>write</b> to us... + Dann können Sie in unsere <b>FAQs</b> schauen oder uns <b>schreiben</b>... + + + www.ausweisapp.bund.de + www.ausweisapp.bund.de + + + or + oder + + + www.personalausweisportal.de + www.personalausweisportal.de + + + ... or click this button to change your PIN right now: + ... oder auf diesen Button klicken um die PIN jetzt zu ändern: + + + Change PIN + PIN ändern + + + Later you can also change your personal PIN here + Hier können Sie auch später jederzeit Ihre persönliche PIN ändern + + + qrc:///images/tutorial/screenshot_pin_management_menu_en.png + qrc:///images/tutorial/screenshot_pin_management_menu_de.png + + + https://www.youtube.com/watch?v=wZglRda5Y60&index=4&list=PLLB5ERhVkn25qQXgMHQr-1KgyZsJKoSAm + https://www.youtube.com/watch?v=wZglRda5Y60&index=4&list=PLLB5ERhVkn25qQXgMHQr-1KgyZsJKoSAm + + + Learn more about this in the YouTube video + Sie können mehr dazu im YouTube-Video erfahren + + + The transport PIN is send to you by the Bundesdruckerei via mail. + Die Transport-PIN wird Ihnen von der Bundesdruckerei per Brief zugesandt. + + + You can always access this tutorial again from the side bar. + Sie können dieses Tutorial jederzeit wieder von der Seitenleiste aus aufrufen. + + + + TutorialReaderMethodBluetooth + + Tutorial: Bluetooth + Tutorial: Bluetooth + + + Using a bluetooth card reader + Mit Bluetooth-Kartenlesegerät + + + App on smartphone or tablet + App auf Telefon oder Tablet + + + Bluetooth card reader + Bluetooth-Kartenlesegerät + + + You need a suitable card reader if you want to use the bluetooth connection. + Die Bluetooth-Verbindung setzt voraus, dass ein geeignetes Kartenlesegerät vorhanden ist. + + + Set the card reader visible first... + Zunächst das Kartenlesegerät sichtbar machen... + + + ... and then pair it with your device. + ...und mit dem Gerät koppeln. + + + Click the link on the website of the service provider. + Link auf Webseite des Diensteanbieters +klicken. + + + qrc:///images/tutorial/generated/reader_nfc_userdata_example_en.svg + qrc:///images/tutorial/generated/reader_nfc_userdata_example_de.svg + + + The App opens automatically. + Die App öffnet sich automatisch. + + + The AusweisApp2 will display who wants to access which data. + Ihnen wird angezeigt, wer welche Ihrer Daten abfragen will. + + + Start the process with a click on: + Starten Sie den Vorgang mit: + + + Identify now + Jetzt ausweisen + + + qrc:///images/tutorial/screenshot_choose_reader_en.png + qrc:///images/tutorial/screenshot_choose_reader_de.png + + + Tap on Bluetooth + Tap auf Bluetooth + + + Insert card into card reader + Karte ins Lesegerät stecken + + + ... and confirm the displayed information. + ...und angezeigte Informationen bestätigen. + + + Enter + Eingeben + + + Now + Jetzt + + + 6 digits long PIN + 6-stellige PIN + + + now on the card reader! + jetzt auf dem Kartenlesegerät! + + + enter on the card reader! + am Kartenlesegerät eingeben! + + + This is only possible if you have exchanged the 5 digits long transport PIN with a 6 digits long personal PIN beforehand. + Funktioniert nur, wenn Sie Ihre 5-stellige Transport-PIN bereits in Ihre persönliche 6-stellige PIN geändert haben. + + + + TutorialReaderMethodFooter + + Back + Zurück + + + + TutorialReaderMethodNfc + + Tutorial: NFC + Tutorial: NFC + + + Direct connection via NFC chip + Direktes Auslesen über NFC Chip + + + App on Android smartphone <b>with</b> NFC chip as card reader + App auf Android-Telefon <b>mit</b> NFC-Chip als Kartenleser + + + Click link on the website of the service provider. + Link auf Webseite des Diensteanbieters klicken. + + + qrc:///images/tutorial/generated/reader_nfc_userdata_example_en.svg + qrc:///images/tutorial/generated/reader_nfc_userdata_example_de.svg + + + The App opens automatically. + Die App öffnet sich automatisch. + + + The AusweisApp2 will display who wants to access which data. + Ihnen wird angezeigt, wer welche Ihrer Daten abfragen will. + + + Start the process with a click on: + Starten Sie den Vorgang mit: + + + Identify now + Jetzt ausweisen + + + ... and place the id card flat onto the NFC interface. + ...und platzieren Sie den Ausweis flach an der NFC-Schnittstelle. + + + Do not move device or id card! + Gerät und Ausweis nicht bewegen! + + + The correct position is specific for your device... + Die genaue Position hängt von Ihrem Gerät ab... + + + https://www.ausweisapp.bund.de/mobile-geraete/ + https://www.ausweisapp.bund.de/mobile-geraete/ + + + To mobile devices + Zu den mobilen Geräten + + + Enter + Eingeben + + + Now + Jetzt + + + 6 digits long PIN + 6-stellige PIN + + + now! + jetzt! + + + enter! + eingeben! + + + This is only possible if you have exchanged the 5 digits long transport PIN with a 6 digits long personal PIN beforehand. + Funktioniert nur, wenn Sie Ihre 5-stellige Transport-PIN bereits in Ihre persönliche 6-stellige PIN geändert haben. + + + + TutorialReaderMethodSacDesktop + + Tutorial: Smartphone as card reader + Tutorial: Smartphone als Kartenlesegerät + + + Smartphone as card reader + Smartphone als Kartenlesegerät + + + App on computer <b>without</b> NFC chip + App auf Computer <b>ohne</b> NFC-Chip + + + Android smartphone <b>with</b> NFC chip as card reader + Android-Telefon <b>mit</b> NFC-Chip als Kartenlesegerät + + + Install AusweisApp2 on both your computer <b>and</b> your android smartphone with NFC ability. + Installieren Sie AusweisApp2 auf Ihren Computer und Ihrem Android-Telefon mit NFC. + + + Both devices have to be connected to the same wifi network + Beide Geräte müssen im selben WLAN sein + + + Now enter "Smartphone as card reader" in the app on your android smartphone... + Jetzt in der App auf Ihrem Android-Telefon im Menü auf „Smartphone als Kartenlesegerät“ tappen... + + + qrc:///images/tutorial/generated/reader_sac_menu_en.svg + qrc:///images/tutorial/generated/reader_sac_menu_de.svg + + + Start remote service + Fernzugriff starten + + + Now + Jetzt + + + Start pairing + Kopplung starten + + + Next + Dann + + + Pairing code + Kopplungscode + + + appears! + erscheint! + + + Start the App now on your computer and enter the settings. + Öffnen Sie nun die App auf dem Computer und gehen auf Einstellungen. + + + Select the <b>Card Readers</b> tab. + Den Reiter <b>Kartenlesegeräte</b> auswählen. + + + qrc:///images/tutorial/screenshot_pairing_en.png + qrc:///images/tutorial/screenshot_pairing_de.png + + + Select smartphone from list and click "pair" + Smartphone in der Liste wählen und auf „koppeln“ klicken + + + Enter pairing code next. + Dann Kopplungscode eingeben. + + + Click link on the website of the service provider. + Link auf Webseite des Diensteanbieters klicken. + + + qrc:///images/tutorial/generated/reader_nfc_userdata_example_en.svg + qrc:///images/tutorial/generated/reader_nfc_userdata_example_de.svg + + + The App opens automatically. + Die App öffnet sich automatisch. + + + The AusweisApp2 will display who wants to access which data. + Ihnen wird angezeigt, wer welche Ihrer Daten abfragen will. + + + Start the process with a click on: + Starten Sie den Vorgang mit: + + + Identify now + Jetzt ausweisen + + + qrc:///images/tutorial/generated/where_lay_down_id.svg + qrc:///images/tutorial/generated/where_lay_down_id.svg + + + ... and place the id card flat onto the NFC interface. + ... und platzieren Sie den Ausweis flach an der NFC-Schnittstelle Ihres Android-Telefons. + + + Do not move device or id card! + Gerät und Ausweis nicht bewegen! + + + The correct position is specific for your device... + Die genaue Position hängt von Ihrem Gerät ab... + + + https://www.ausweisapp.bund.de/mobile-geraete/ + https://www.ausweisapp.bund.de/mobile-geraete/ + + + To mobile devices + Zu den mobilen Geräten + + + Enter + Eingeben + + + 6 digits long PIN + 6-stellige PIN + + + now! + jetzt! + + + enter! + eingeben! + + + This is only possible if you have exchanged the 5 digits long transport PIN with a 6 digits long personal PIN beforehand. + Funktioniert nur, wenn Sie Ihre 5-stellige Transport-PIN bereits in Ihre persönliche 6-stellige PIN geändert haben. + + + + TutorialReaderMethodSacMobile + + Tutorial: Smartphone as card reader + Tutorial: Smartphone als Kartenlesegerät + + + App on tablet or smartphone <b>without</b> NFC chip + App auf Tablet oder Telefon <b>ohne</b> NFC-Chip + + + Android smartphone <b>with</b> NFC chip as card reader + Android-Telefon <b>mit</b> NFC-Chip als Kartenlesegerät + + + Install AusweisApp2 on both your device without NFC <b>and</b> your android smartphone with NFC ability. + Installieren Sie die AusweisApp2 auf Ihrem Gerät ohne NFC <b>und</b> auf Ihrem Android-Telefon mit NFC. + + + Both devices have to be connected to the same wifi network + Beide Geräte müssen im selben WLAN sein + + + Now enter "Smartphone as card reader" in the app on your android smartphone <b>with</b>... + Jetzt in der App auf Ihrem Android-Telefon <b>mit</b> NFC im Menü auf „Smartphone als Kartenlesegerät“ tappen... + + + qrc:///images/tutorial/generated/reader_sac_menu_en.svg + qrc:///images/tutorial/generated/reader_sac_menu_en.svg + + + Start remote service + Fernzugriff starten + + + Now + Jetzt + + + Start pairing + Kopplung starten + + + Next + Dann + + + Pairing code + Kopplungscode + + + appears! + erscheint! + + + Now open the app on your device <b>without</b> NFC and select <b>Smartphone as card reader</b>. + Nun die App auf dem Gerät <b>ohne</b> NFC öffnen und auf <b>Smartphone als Kartenlesegerät</b> gehen. + + + Now select <b>Settings</b>. + Dort <b>Einstellungen</b> auswählen. + + + Choose smartphone from list + Smartphone aus Liste wählen + + + Enter pairing code next. + Dann Kopplungscode eingeben. + + + Click link on the website of the service provider on the device <b>without</b> NFC. + Auf dem Gerät <b>ohne</b> NFC Link auf Webseite des Diensteanbieters klicken. + + + qrc:///images/tutorial/generated/reader_nfc_userdata_example_en.svg + qrc:///images/tutorial/generated/reader_nfc_userdata_example_de.svg + + + The App opens automatically. + Die App öffnet sich automatisch. + + + The AusweisApp2 will display who wants to access which data. + Ihnen wird angezeigt, wer welche Ihrer Daten abfragen will. + + + Start the process with a click on: + Den Vorgang starten mit: + + + Identify now + Jetzt ausweisen + + + qrc:///images/tutorial/screenshot_choose_reader_en.png + qrc:///images/tutorial/screenshot_choose_reader_de.png + + + Tap on Wifi + Tap auf WLAN + + + qrc:///images/tutorial/generated/where_lay_down_id.svg + qrc:///images/tutorial/generated/where_lay_down_id.svg + + + ... and place the id card flat onto the NFC interface. + ... und den Ausweis flach an der NFC-Schnittstelle Ihres Android-Telefons platzieren. + + + Do not move device or id card! + Gerät und Ausweis nicht bewegen! + + + The correct position is specific for your device... + Die genaue Position hängt von Ihrem Gerät ab... + + + https://www.ausweisapp.bund.de/mobile-geraete/ + https://www.ausweisapp.bund.de/mobile-geraete/ + + + To mobile devices + Zu den mobilen Geräten + + + Enter + Eingeben + + + 6 digits long PIN + 6-stellige PIN + + + now! + jetzt! + + + enter! + eingeben! + + + This is only possible if you have exchanged the 5 digits long transport PIN with a 6 digits long personal PIN beforehand. + Funktioniert nur, wenn Sie Ihre 5-stellige Transport-PIN bereits in Ihre persönliche 6-stellige PIN geändert haben. + + + + TutorialView + + Tutorial + Tutorial + + + What? + Was? + + + Where? + Wo? + + + How? + Wie? + + + Important! + Wichtig! + + + + TutorialWhat + + What is the online ID function? + Was ist die Online-Ausweisfunktion? + + + You can authenticate yourself in the internet + Mit ihr weisen Sie sich sicher im Internet aus + + + and use it to deal with administrative paperwork and business matters electronically! + und erledigen damit Behördengänge oder geschäftliche Angelegenheiten einfach elektronisch! + + + Alright, but is it secure? + Alles klar, nur ist das auch sicher? + + + Of course, because we use a so called + Natürlich, dafür haben wir die sogenannte + + + Mutual authentication + gegenseitige Authentifizierung + + + ... it establishes a secure connection between ID document and service provider. + ...sie stellt eine sichere Verbindung zwischen Ausweisdokument und Diensteanbieter her. + + + On every authentication you get displayed <b>who</b> wants to access <b>which</b> data + Ihnen wird immer zuerst angezeigt, <b>wer welche</b> Daten auslesen möchte + + + and you consent to the request with your personal PIN. + und Sie stimmen der Abfrage mit Ihrer persönlichen PIN zu. + + + ... is the provider authorized for this? + ... und darf der das? + + + The provider needs an authorization of the Federal Office of Administration. + Der Anbieter benötigt eine Berechtigung vom Bundesverwaltungsamt. + + + Certificate + Zertifikat + + + Everytime both participants authenticate each other... + Es weisen sich also immer beide Seiten eindeutig aus... + + + ... and therefore your data is protected and securely transfered. + ... so werden Ihre Daten geschützt und sicher übermittelt. + + + qrc:///images/tutorial/screenshot_cert_en.png + qrc:///images/tutorial/screenshot_cert_de.png + + + https://www.youtube.com/watch?v=fzbUZmHaZp4&index=5&list=PLLB5ERhVkn25qQXgMHQr-1KgyZsJKoSAm + https://www.youtube.com/watch?v=fzbUZmHaZp4&index=5&list=PLLB5ERhVkn25qQXgMHQr-1KgyZsJKoSAm + + + You can also watch a video on YouTube on this topic + Sie können sich hierzu auch ein Video auf YouTube anschauen + + + + TutorialWhere + + On every website of a service provider where you see this icon: + Überall, wo Sie auf der Webseite eines Online-Dienstes folgendes Logo sehen: + + + By the way, you can find many services directly in the AusweisApp2 <b>provider list</b>. + Übrigens, viele Dienste finden Sie direkt in der <b>Anbieterliste</b> der AusweisApp2. + + + The <b>integrated self-disclosure</b> is a special service to view the data saved on your ID card. + Die <b>integrierte Selbstauskunft</b> ist ein spezieller Dienst, um Daten einzusehen, die auf Ihrem Ausweis gespeichert sind. + + + You can access it by clicking "See my personal data" on the AusweisApp2 start page, followed by "Identify now" + Sie erreichen sie von der Startseite der AusweisApp2 mit "Meine Daten einsehen" und dann auf "Jetzt ausweisen" + + + And this is how it works + Und so funkioniert's + + + The AusweisApp2 will always display <b>who</b> wants to access <b>which</b> of your data. + Die AusweisApp2 zeigt Ihnen immer an, wer welche Ihrer Daten abfragen will. + + + qrc:///images/tutorial/generated/where_userdata_example_en.svg + qrc:///images/tutorial/generated/where_userdata_example_de.svg + + + Now lay down your ID card and place your device on the ID card. + Bitte legen Sie nun Ihren Ausweis hin und Ihr Gerät auf Ihren Ausweis. + + + Don't move your device during the procedure! + Gerät während des gesamten Vorgangs nicht bewegen! + + + Enter + Eingeben + + + Now + Jetzt + + + 6 digits long PIN + 6-stellige PIN + + + now! + jetzt! + + + enter! + eingeben! + + + Where can I use the online ID function? + Wo kann ich die Online-Ausweisfunktion nutzen? + + + qrc:///images/tutorial/generated/where_providerlist_screenshot_en.svg + qrc:///images/tutorial/generated/where_providerlist_screenshot_de.svg + + + qrc:///images/tutorial/generated/where_identify_now_en.svg + qrc:///images/tutorial/generated/where_identify_now_de.svg + + UpdateWindow - Software Update Software-Aktualisierung - A new version of AusweisApp2 is available! Eine aktuellere Version der AusweisApp2 ist verfügbar! - - AusweisApp2 %1 is now available - you have %2. Would you like to download it now? - AusweisApp2 %1 ist jetzt verfügbar - Sie haben %2. Wollen Sie die neue Version jetzt herunterladen? - - - The update file is located at: Die Aktualisierungsdatei finden Sie hier: - Release Notes: Aktualisierungshinweise: - Download this update and close current "AusweisApp2". Install the update and start "AusweisApp2" again. Laden Sie die Aktualisierung herunter und beenden Sie die laufende "AusweisApp2". Installieren Sie das Update und starten "AusweisApp2" erneut. - When you click "Download update", this link will be opened in your browser. Wenn Sie "Aktualisierung herunterladen" klicken, wird dieser Link in Ihrem Browser geöffnet. - Skip update Aktualisierung auslassen - Remind me later Später erinnern - Download update Aktualisierung herunterladen @@ -2611,35 +2992,104 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe VersionInformation - Version Information Versionsinformationen + + WhiteListSurveyView + + Feedback + Feedback + + + Would you like to help us to improve the AusweisApp2? + Möchten Sie uns helfen, die AusweisApp2 zu verbessern? + + + Supplying your device characteristics helps us to gather reliable information about the compatibility of your device. + Mit der Übermittlung Ihrer Gerätedaten helfen Sie uns, verlässliche Aussagen über die Kompatibilität Ihres Gerätes mit der AusweisApp2 zu treffen. + + + The transmission is anonymous. No personal data is collected or transmitted! + Die Übermittlung erfolgt anonym und es werden keine personenbezogenen Daten übermittelt! + + + The following information will be transmitted, if you decide so: + Nach Ihrer Bestätigung werden folgende Daten übermittelt: + + + Vendor + Name des Herstellers + + + Model number + Modellnummer + + + Model name + Modellname + + + Collection date + Datum der Erfassung + + + AusweisApp2 version + Version der AusweisApp2 + + + ROM build number + Build-Nummer des Betriebssystems + + + Android version + Android-Version + + + Kernel version + Kernel-Version + + + Max. NFC packet length + Max. NFC-Paket-Länge + + + Thank you for your assistance! + Vielen Dank für Ihre Mithilfe! + + + Cancel + Abbrechen + + + Transmit + Senden + + + Send device data? + Gerätedaten senden? + + governikus::AboutDialog - Further information Weitere Informationen - Version Version - The current release notes can be found %1 here.%2 Die aktuellen Release-Notes finden Sie %1 hier.%2 - About %1 - %2 Über %1 - %2 - The developer mode is aimed at integrators / developers for new service applications. For this reason, the developer mode works only in the test PKI. By activating the developer mode, some safety tests are deactivated. This means that the authentication process continues although the AusweisApp2 would usually abort the process with an error message when used in normal operation mode. Information on the disregarded error in the developer mode is displayed in the attached window below the AusweisApp2. Der Entwicklermodus richtet sich an Integratoren / Entwickler für neue Dienste. Aus diesem Grund funktioniert der Entwicklermodus lediglich in der Test-PKI. Durch Aktivierung des Entwicklermodus werden einige Sicherheitsprüfungen abgestellt. Die Authentisierung wird auch dann weitergeführt, wenn die AusweisApp2 im Normalbetrieb die Authentisierung mit einer Fehlermeldung abbrechen würde. Der übergangene Fehler im Entwicklermodus wird im angehängten Fenster unterhalb der AusweisApp2 angezeigt. @@ -2647,165 +3097,134 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::AccessRoleAndRightsUtil - WRITE_DG17 WRITE_DG17 - WRITE_DG18 WRITE_DG18 - WRITE_DG19 WRITE_DG19 - WRITE_DG20 WRITE_DG20 - WRITE_DG21 WRITE_DG21 - - Optional data Optionale Daten - Residence permit II Nebenbestimmungen II - Residence permit I Nebenbestimmungen I (nur eAT) - Pseudonym Pseudonym - Address Anschrift - Birth name Geburtsname - Date of birth Geburtsdatum - Doctoral degree Doktorgrad - Religious / artistic name Ordens- / Künstlername - Given name(s) Vorname(n) - Valid until Gültig bis - Issuing country Ausstellender Staat - Address verification Wohnortbestätigung - - - RFU RFU - Community-ID Wohnort-ID - Gender Geschlecht - Nationality Staatsangehörigkeit - Place of birth Geburtsort - Family name Familienname - Document type Dokumentenart - Installation of qualified signature certificates Installation qualifizierter Signaturzertifikate - Installation of signature certificates Installation von Signaturzertifikaten - PIN Management PIN-Verwaltung - CAN allowed CAN erlaubt - Privileged terminal Privilegiertes Terminal - Age verification Altersbestätigung - + Unknown (reserved) + Unbekannt (reserviert) + + Unknown Unbekannt @@ -2813,93 +3232,73 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::AppQtGui - - AusweisApp2 was started. - AusweisApp2 wurde gestartet. - - - Do not show this dialog again. Diesen Hinweis nicht mehr anzeigen. - - Open - Öffnen - - - Updates Aktualisierungen - - - - Information Information - The developer mode is enabled. Der Entwicklermodus ist aktiviert. - Do you want to disable the developer mode? Möchten Sie den Entwicklermodus deaktivieren? - - Did you change the initial transport PIN already?<br><br>Prior to the first use of the online identification function you have to replace the transport PIN by an individual 6-digit PIN. Online identification with transport PIN is not possible. - Haben Sie Ihre Transport-PIN schon geändert? <br><br>Bevor Sie das erste mal die Online-Ausweisfunktion nutzen können, müssen Sie Ihre Transport-PIN durch eine persönliche, 6-stellige PIN ersetzen. Die Online-Ausweisfunktion ist mit der Transport-PIN nicht möglich. - - - No, change transport PIN now Nein, PIN jetzt ändern - - Exit AusweisApp2 - AusweisApp2 beenden - - - The user interface of the %1 is closed. Die Benutzeroberfläche der %1 wird geschlossen. - The program remains available via the icon in the system tray. Click on the %1 icon to reopen the user interface. Das Programm steht weiterhin im Infobereich zur Verfügung. Klicken Sie auf das Symbol der %1, um die Anwendung wieder zu öffnen. - Your software is up to date. Ihre Software ist auf dem aktuellen Stand. - The device "%1" was unpaired because it does not react to connection attempts. Retry the pairing process if you want to use this device to authenticate yourself. Das Gerät "%1" wurde entkoppelt, da es nicht auf Verbindungsversuche reagiert hat. Versuchen Sie das Gerät erneut zu koppeln wenn Sie es zur Authentisierung verwenden möchten. + + SDK + SDK + + + Another application uses AusweisApp2. + Eine andere Anwendung verwendet die AusweisApp2. + + + Application: %1 + Anwendung: %1 + + + Did you change the transport PIN already?<br><br>Prior to the first use of the online identification function you have to replace the transport PIN by an individual 6-digit PIN. Online identification with transport PIN is not possible. + Haben Sie Ihre Transport-PIN schon geändert? <br><br>Bevor Sie das erste Mal die Online-Ausweisfunktion nutzen können, müssen Sie Ihre Transport-PIN durch eine persönliche, 6-stellige PIN ersetzen. Die Benutzung der Online-Ausweisfunktion ist mit der Transport-PIN nicht möglich. + governikus::AppQtMainWidget - https://www.ausweisapp.bund.de/en/service/haeufig-gestellte-fragen/ https://www.ausweisapp.bund.de/service/haeufig-gestellte-fragen/ - https://www.ausweisapp.bund.de/en/feedback/melden-sie-einen-fehler/ https://www.ausweisapp.bund.de/feedback/melden-sie-einen-fehler/ - https://www.ausweisapp.bund.de/en/feedback/bewerten-sie-uns/ https://www.ausweisapp.bund.de/feedback/bewerten-sie-uns/ @@ -2907,19 +3306,16 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::CardInfo - not inserted Karte nicht eingelegt - unknown type Karte unbekannter Typ - ID card (PA/eAT) Ausweisdokument (PA/eAT) @@ -2927,37 +3323,30 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::CertificateDescriptionModel - Service provider Diensteanbieter - Certificate issuer Aussteller des Berechtigungszertifikats - Name, address and mail address of the service provider Name, Adresse und E-Mail vom Diensteanbieter - Purpose Zweck - Indication of the bodies responsible for the service provider, that verify the compliance with data security regulations Angabe der für den Dienstanbieter zuständigen Datenschutzaufsicht - Service provider information Dienstanbieterinformationen - Validity Gültigkeit @@ -2965,7 +3354,6 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::CredentialDialog - Proxy security Proxy-Sicherheit @@ -2973,37 +3361,30 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::DeleteHistoryDialog - Delete history Verlauf löschen - Delete history for this period: Verlauf für diesen Zeitraum löschen: - Past hour Letzte Stunde - Past day Letzter Tag - Past week Letzte Woche - Last four weeks Letzte vier Wochen - All history Gesamter Zeitraum @@ -3011,7 +3392,6 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::DetailDialog - Service provider data Angaben zum Diensteanbieter @@ -3019,7 +3399,6 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::DetailWidget - Service provider details dialog Dialog mit Details des Diensteanbieters @@ -3027,17 +3406,14 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::DiagnosisController - pcsclite %1 pcsclite %1 - unknown unbekannt - not available nicht verfügbar @@ -3045,561 +3421,721 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::DiagnosisDialog - Diagnosis Diagnose - Save Speichern - - Text files Textdateien - File error Dateifehler - An error occurred while saving the file. Beim Speichern der Datei ist ein Fehler aufgetreten. - governikus::DiagnosisWidget + governikus::DiagnosisModel - - - - - Diagnosis is running... - Diagnose läuft... - - - - Diagnosis - Diagnose - - - - Operating system Betriebssystem - - Card reader Kartenlesegerät - - PC/SC PC/SC - - + Firewall + Firewall + + Time of diagnosis Diagnosezeitpunkt - - - - - Version: %1 - Version: %1 + Diagnosis is running... + Diagnose läuft... - - Components - Komponenten + No Firewall information available on this platform. + Auf dieser Platform sind keine Firewallinformationen verfügbar. - - Components: - Komponenten: - - - - Driver - Treiber - - - - Driver: - Treiber: - - - - - not recognised - keine erkannt - - - - Basic card reader - Basis-Kartenlesegerät - - - - Standard / deluxe card reader - Standard-/Komfort-Kartenlesegerät - - - - - Type: %1 - Typ: %1 - - - - - Card: %1 - Karte: %1 - - - - - Retry counter: %1 - Fehlbedienungszähler: %1 - - - - d. MMMM yyyy, hh:mm:ss AP - d. MMMM yyyy, HH:mm:ss 'Uhr' - - - - Vendor: %1 Hersteller: %1 - - + Version: %1 + Version: %1 + + File path: %1 Dateipfad: %1 + + Basic card reader + Basis-Kartenlesegerät + + + Standard / comfort card reader + Standard- / Komfortleser + + + Type: %1 + Typ: %1 + + + Card: %1 + Karte: %1 + + + Retry counter: %1 + Fehlbedienungszähler: %1 + + + Components + Komponenten + + + Driver + Treiber + + + Initial diagnosis running, please wait. + Führe initiale Diagnose durch, bitte warten. + + + d. MMMM yyyy, hh:mm:ss AP + d. MMMM yyyy, HH:mm:ss 'Uhr' + + + Diagnosis data + Diagnosedaten + + + Hardware address: %1 + Hardwareadresse: %1 + + + No Antivirus information available on this platform. + Keine Antivirus-Informationen auf dieser Platform verfügbar. + + + No devices paired + Keine gekoppelten Geräte + + + dd.MM.yyyy, hh:mm:ss + dd.MM.yyyy, hh:mm:ss + + + No information found for this certificate + Keine Informationen für dieses Zertifikat verfügbar + + + Proxy + Proxy + + + Hostname: %1 + Hostname: %1 + + + Port: %1 + Port: %1 + + + Capabilities: %1 + Fähigkeiten: %1 + + + Ping test to proxy: Successful + Proxy-Pingtest: Erfolgreich + + + Ping test to proxy: Failed + Proxy-Pingtest: Fehlgeschlagen + + + Connection test with proxy: Successful + Proxy-Verbindungstest: Erfolgreich + + + Connection test with proxy: Failed + Proxy-Verbindungstest: Fehlgeschlagen + + + No Proxy Found + Kein Proxy gefunden + + + Connection test without proxy: Successful + Verbindungstest ohne Proxy: Erfolgreich + + + Connection test without proxy: Failed + Verbindungstest ohne Proxy: Fehlgeschlagen + + + Last updated: %1 + Zuletzt aktualisiert: %1 + + + Executable path: %1 + Anwendungspfad: %1 + + + Antivirus detection failed. + Antivirus-Erkennung fehlgeschlagen. + + + Yes + Ja + + + No + Nein + + + No third party firewalls detected + Keine Drittanbieterfirewall erkannt + + + Firewalls from third party vendors + Firewalls von Drittanbietern + + + Enabled: %1 + Aktiviert: %1 + + + Up to date: %1 + Auf dem aktuellen Stand: %1 + + + Windows firewall rules + Windows-Firewallregeln + + + Outgoing AusweisApp2 rule + Ausgehende AusweisApp2-Regel + + + Exists: %1 + Existiert: %1 + + + Incoming AusweisApp2 rule + Eingehende AusweisApp2-Regel + + + Windows Firewall profiles + Windows-Firewall-Profile + + + Paired devices + Gekoppelte Geräte + + + Network interfaces + Netzwerkschnittstellen + + + Network connection test + Netzwerkverbindungstest + + + Installed antivirus software + Installierte Antivirus-Software + + + Not recognised + Nicht erkannt + + + Certificate fingerprint: %1 + Zertifikats-Fingerabdruck: %1 + + + Last connection: %1 + Zuletzt verbunden: %1 + + + <Not set> + <Nicht gesetzt> + + + No IP addresses assigned + Keine IP-Adressen zugewiesen + + + IP addresses + IP-Adressen + + + Warning: The current firewall status can be obscured by additional Group Policies on your system, often set by system administrators. + Warnung: Der aktuelle Firewallstatus kann durch zusätzliche Gruppenregeln, die oftmals von Administratoren gesetzt werden, auf Ihrem System verschleiert sein. + + + An error occurred while trying to gather firewall information. Please check the log for more information. + Beim Sammeln von Firewallinformationen ist ein Fehler aufgetreten. Bitte konsultieren Sie das Protokoll für weitere Informationen. + + + No Antivirus software detected. + Es konnte keine Antivirus-Software erkannt werden. + + + Third party firewalls cannot be detected on Windows 7. + Firewalls von Drittherstellern können auf Windows 7 nicht erkannt werden. + + + + governikus::ECardApiResult + + An unexpected error has occurred during processing. + Während der Verarbeitung ist ein unerwarteter Fehler aufgetreten. + + + Use of the function by the client application is not permitted. + Für die Operation fehlen die benötigten Rechte. + + + An internal error has occurred during processing. + Während der Verarbeitung ist ein interner Fehler aufgetreten. + + + There was some problem with a provided or omitted parameter. + Ein fehlerhafter Parameter wurde übermittelt. + + + The API function is unknown. + Die Schnittstellenfunktion ist unbekannt. + + + The framework or layer has not been initialized. + Die Schnittstelle ist nicht initialisiert. + + + The active session has been terminated. + Die aktuelle Sitzung wurde beendet. + + + A Communication error occurred during processing. + Während der Verarbeitung ist ein Verbindungsfehler aufgetreten. + + + The operation was terminated as the set time was exceeded. + Der Vorgang wurde wegen einer Zeitüberschreitung abgebrochen. + + + The operation was aborted as an invalid channel handle was used. + Der Vorgang wurde wegen einer ungültigen Verbindung abgebrochen. + + + A trusted channel could not be opened. + Es konnte keine sichere Verbindung hergestellt werden. + + + The operation was aborted as an unknown protocol was used. + Der Vorgang wurde wegen der Verwendung eines unbekannten Protokolls abgebrochen. + + + The operation was aborted as an unknown cipher suite was used. + Der Vorgang wurde wegen der Verwendung eines unbekannten Verschlüsselungsalgorithmus abgebrochen. + + + The operation was aborted as an unknown web service binding was used. + Der Vorgang wurde wegen der Verwendung eines unbekannten Webservice-Bindings abgebrochen. + + + The card is missing or was removed. + Es liegt keine Karte auf oder sie wurde entfernt. + + + The new PIN and the confirmation do not match. + Die neue PIN und ihre Wiederholung stimmen nicht überein. + + + The format of the PIN is wrong. + Die Zeichenanzahl der PIN ist inkorrekt. + + + Signature certificate key generation is not possible. + Die Erzeugung eines Schlüssels für das Signaturzertifikat war nicht möglich. + + + The process was cancelled by the user. + Der Benutzer hat den Vorgang abgebrochen. + + + One or more certificate checks failed. The operation will be aborted due to security reasons. + Eine oder mehrere Zertifikatsprüfungen schlugen fehl. Der Vorgang wird aus Sicherheitsgründen abgebrochen. + + + This action cannot be performed. The online identification function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the online identification function. + Diese Aktion kann leider nicht durchgeführt werden. Die Online-Ausweisfunktion Ihres Ausweisdokuments ist nicht aktiviert. Bitte wenden Sie sich an die Behörde, die Ihr Ausweisdokument ausgegeben hat, um die Online-Ausweisfunktion zu aktivieren. + + + The authenticity of your ID card could not be verified. Please make sure that you are using a genuine ID card. Please note that test applications require the use of a test ID card. + Die Echtheit Ihres Ausweisdokuments konnte nicht überprüft werden. Bitte stellen Sie sicher, dass Sie ein echtes Ausweisdokument verwenden. Bitte beachten Sie, dass Sie bei Testanwendungen einen Testausweis verwenden müssen. + + + The age verification failed. + Die Altersverifikation war nicht erfolgreich. + + + The community verification failed. + Die Wohnortverifikation war nicht erfolgreich. + + + The ID card is invalid or disabled. + Das Ausweisdokument ist ungültig oder gesperrt. + governikus::GlobalStatus - No error occurred. Es ist kein Fehler aufgetreten. - An unexpected error has occurred during processing. Während der Verarbeitung ist ein unerwarteter Fehler aufgetreten. - The ID card has been removed. The process is aborted. Das Ausweisdokument wurde entfernt. Der Vorgang wird abgebrochen. - The authenticity of your ID card could not be confirmed. Die Echtheit Ihres Ausweisdokuments konnte nicht bestätigt werden. - The program received an unknown message from the server. Die Anwendung hat eine unbekannte Nachricht vom Server erhalten. - The program received an unexpected message from the server. Die Anwendung hat eine nicht erwartete Nachricht vom Server erhalten. - Pre-verification failed. Eine oder mehrere Zertifikatsprüfungen schlugen fehl. - No unique AT CVC Kein eindeutiges AT CVC - No unique DV CVC Kein eindeutiges DV CVC - Authentication failed. Die Authentisierung ist fehlgeschlagen. - No certificate description available. Keine Zertifikatsbeschreibung vorhanden. - No subject url available in certificate description. Es konnte keine URL in der Zertifikatsbeschreibung gefunden werden. - The certificate description does not match the certificate. Die Zertifikatsbeschreibung passt nicht zum Zertifikat. - The subject URL in the certificate description and the TCToken URL don't satisfy the same origin policy. Die URL in der Zertifikatsbeschreibung und die TCToken-URL erfüllen die Same-Origin-Policy nicht. - Failed to establish secure connection Die sichere Verbindung konnte nicht aufgebaut werden - The program received an error from the server. Die Anwendung hat einen Fehler vom Server erhalten. - - Hash of certificate not in certificate description - Hashwert des Zertifikats nicht in Zertifikatsbeschreibung - - - Received no data. Keine Daten erhalten. - An error occurred. Please contact our support at <a href="https://www.ausweisapp.bund.de/en/service/support/">AusweisApp2 Support</a>. Ein Fehler ist aufgetreten. Bitte kontaktieren Sie unseren Support unter <a href="https://www.ausweisapp.bund.de/service/support/">AusweisApp2 Support</a>. - Cannot start authentication. An operation is already in progress. Die Authentisierung kann nicht gestartet werden. Es läuft bereits eine Operation. - After three wrong entries your PIN is blocked. Please use the PIN management in this app to unblock it with the help of your PUK. Ihre PIN ist nach dreimaliger Fehleingabe gesperrt. Verwenden Sie die PIN-Verwaltung dieser App, um diese mit Ihrer PUK zu entsperren. - Using the developer mode is only allowed in a test environment. Der Entwicklermodus darf nur in einer Testumgebung verwendet werden. - The service is temporarily not available. Please try again later. Der Dienst ist vorübergehend nicht verfügbar, bitte versuchen Sie es zu einem späteren Zeitpunkt erneut. - Establishing a connection is taking too long. Der Verbindungsaufbau dauert zu lange. - Establishing a connection via the proxy did not succeed. Der Verbindungsaufbau über den Proxy war nicht erfolgreich. - It wasn't possible to connect to the server: the server sent a non-standard response. Die Verbindung zum Server ist fehlgeschlagen: Der Server hat eine nicht-standard Antwort gesendet. - It wasn't possible to connect to the server: a secure connection could not be established. Die Verbindung zum Server ist fehlgeschlagen: Es konnte keine sichere Verbindung aufgebaut werden. - Application was invoked with wrong parameters. Die Anwendung wurde mit den falschen Parametern aufgerufen. - An unknown network error occurred. Ein unbekannter Netzwerkfehler ist aufgetreten. - The selected card reader cannot be accessed anymore. Das ausgewählte Kartenlesegerät kann nicht mehr angesprochen werden. - An error occurred while communicating with the card reader. Bei der Kommunikation mit dem Kartenlesegerät ist ein Fehler aufgetreten. - The server provided no or incomplete information. Your personal data could not be read out. Der Server lieferte keine oder nur unvollständige Informationen. Ihre persönlichen Daten konnten nicht ausgelesen werden. - Error while connecting to the service provider. The SSL connection uses an unsupported key algorithm or length. Fehler bei der Verbindung mit dem Diensteanbieter. Der Verschlüsselungsalgorithmus oder die Länge des Schlüssels der SSL-Verbindung wird nicht unterstützt. - - Error while connecting to the server. The SSL certificate uses an unsupported key algorithm or length. - Fehler bei der Verbindung zum Server. Der Verschlüsselungsalgorithmus oder die Länge des Schlüssels im SSL-Zertifikat des Servers wird nicht unterstützt. - - - Empty redirect URL Leere Redirect-URL - Expected redirect, got %1 Erwartete HTTP-redirect, tatsächlich erhalten: %1 - Invalid scheme: %1 Ungültiges URL-Schema: %1 - Malformed redirect URL: %1 Nicht wohlgeformte Redirect-URL: %1 - The process was cancelled by the user. Der Benutzer hat den Vorgang abgebrochen. - The maximum time was exceeded during input process. Bei der Eingabe wurde die maximale Zeit überschritten. - Card does not exist Karte nicht vorhanden - An error occurred while communicating with the ID card. Please make sure that your ID card is placed correctly on the card reader and try again. Bei der Kommunikation mit dem Ausweisdokument ist ein Fehler aufgetreten. Bitte überprüfen Sie, dass das Ausweisdokument korrekt aufgelegt ist und versuchen Sie es erneut. - A protocol error occurred. Please make sure that your ID card is placed correctly on the card reader and try again. If the problem occurs again, please contact our support at Ein Protokollfehler ist aufgetreten. Bitte überprüfen Sie, dass das Ausweisdokument korrekt aufgelegt ist und versuchen Sie es erneut. Wenn das Problem wieder auftritt kontaktieren Sie bitte unseren Support unter - https://www.ausweisapp.bund.de/en/service/support/ https://www.ausweisapp.bund.de/service/support/ - AusweisApp2 Support AusweisApp2 Support - The given PIN is invalid. Die eingegebene PIN ist ungültig. - The given card access number (CAN) is invalid. Die eingegebene Zugangsnummer (CAN) ist ungültig. - The given PUK is invalid. Die eingegebene PUK ist ungültig. - The PIN was blocked after too many unsuccessful attempts. Die PIN ist nach zu vielen Fehlversuchen gesperrt. - The PIN is not blocked. Die PIN ist nicht gesperrt. - The PUK was used ten times and is set inoperative. Please contact the competent authority that issued your ID document to unlock the PIN. Die eingegebene PUK wurde zehn Mal verwendet und ist außer Betrieb. Bitte kontaktieren Sie die für die Ausgabe Ihres Ausweisdokuments zuständige Behörde um Ihre PIN zu entsperren. - The new PIN and the confirmation do not match. Die neue PIN und ihre Wiederholung stimmen nicht überein. - The length of the new PIN is not valid. Die neue PIN hat eine ungültige Länge. - An error occurred while connecting to a reader device. Es ist ein Fehler während des Verbindungsaufbaus zu einem Kartenlesegerät aufgetreten. - An error occurred while scanning for reader devices. Es ist ein Fehler während der Suche nach einem Kartenlesegerät aufgetreten. - The remote reader connection was closed properly. Die Verbindung zum entfernten Kartenlesegerät wurde ordnungsgemäß geschlossen. - The remote card reader connection was not closed properly. Die Verbindung zum Remote-Lesegerät wurde abgebrochen. - Undefined error code occured when the remote card reader connection was closed. Unbekannter Fehler beim Beenden der Verbindung zum Remote-Lesegerät. - Remote reader connection request contains invalid parameters. Die Verbindungsanforderung zum entfernten Kartenlesegerät enthält einen ungültigen Parameter. - Empty password in extended encryption of remote reader connection request. Die Anforderung der erweiterten Verschlüsselung mit dem entfernten Kartenlesegerät enthält ein leeres Kennwort. - Your remote reader version is incompatible with the local version. Please install the latest AusweisApp2 version on both your smartphone and your computer. Die Version Ihres Smartphones als Kartenlesegerätes ist inkompatibel. Bitte aktualisieren Sie Ihre Installation. - A timeout occurred while trying to establish a connection to a remote reader. Bei der Verbindung zum entfernten Kartenlesegerät kam es zu einer Zeitüberschreitung. - An error occurred while trying to establish a connection to a remote reader. Bei der Verbindung zum entfernten Kartenlesegerät ist ein Fehler aufgetreten. - Remote device has rejected the connection. Please check the pairing code. Das zu koppelnde Gerät hat die Verbindung verweigert. Überprüfen Sie bitte den Kopplungscode. - File not found. Datei nicht gefunden. - Cannot save file. Speichern der Datei nicht möglich. - Received data were corrupted. Die empfangenen Daten waren beschädigt. + + Hash of certificate not in certificate description (issuer: %1). This indicates a misconfiguration or manipulation of the certificate. Please check that your antivirus-software and firewalls are not interfering with SSL traffic. + Der Hashwert des Zertifikats ist nicht in der Zertifikatsbeschreibung vorhanden (Aussteller: %1). Dies deutet auf eine Fehlkonfiguration oder Manipulation des Zertifikats hin. Bitte überprüfen Sie, dass weder eine Antivirus-Software noch eine Firewall in den SSL-Verkehr eingreifen. + + + Error while connecting to the server. The SSL certificate uses an unsupported key algorithm or length. Certificate issuer: %1 + Fehler bei der Verbindung zum Server. Der Verschlüsselungsalgorithmus oder die Länge des Schlüssels im SSL-Zertifikat des Servers wird nicht unterstützt. Zertifikatsaussteller: %1 + + + An error occurred. Please contact our support at <a href="https://www.ausweisapp.bund.de/en/service/support/">AusweisApp2 Support</a> or feel free to send us an email at <a href="mailto:support@ausweisapp.de?subject=Log file&body=<Please describe the error>">support@ausweisapp.de</a>. + Es ist ein Fehler aufgetreten. Bitte kontaktieren Sie unseren <a href="https://www.ausweisapp.bund.de/en/service/support/">AusweisApp2 Support</a> oder senden sie uns eine Email an <a href="mailto:support@ausweisapp.de?subject=Protokolldatei&body=<Bitte beschreiben Sie den Fehler>">support@ausweisapp.de</a>. + governikus::GuiUtils - - The given card access number (CAN) is not correct. - Die eingegebene Zugangsnummer (CAN) ist ungültig. - - - Wrong card access number (CAN) Falsche Zugangsnummer (CAN) - The given card access number (CAN) is not correct. You have one more try to enter the correct PIN. Please mind that you have to acknowledge this last try with your card access number (CAN). Die eingegebene Zugangsnummer (CAN) ist nicht korrekt. Sie haben noch eine weitere Möglichkeit die korrekte PIN einzugeben. Beachten Sie, dass Sie diesen letzten Versuch mit der Zugangsnummer (CAN) bestätigen müssen. - Wrong PUK Falsche PUK - PUK is inoperative PUK ist außer Betrieb - Please enter your PUK again. Bitte geben Sie Ihre PUK erneut ein. - + The given card access number (CAN) is not correct. + Die eingegebene Zugangsnummer (CAN) ist ungültig. + + You have correctly entered the PUK ten times and have thus reached the maximum count. The PUK is now inoperative and can no longer be used for unblocking the PIN. Please address your competent authority that has issued your ID card for unblocking your PIN. Sie haben die PUK zehn Mal korrekt eingegeben und damit die maximale Anzahl erreicht. Die PUK ist daher außer Betrieb und kann nicht mehr zum Entsperren der PIN verwendet werden. Bitte wenden Sie sich zum Entsperren der PIN an die zuständige Behörde, die Ihr Ausweisdokument ausgegeben hat. - Wrong PIN Falsche PIN - After three wrong entries your PIN is blocked. Using the online identification function is no longer possible. </p><p>You can unblock your PIN in the following dialog. The program supports you with the steps now required. Ihre PIN ist nach dreimaliger Fehleingabe gesperrt. Die Nutzung der Online-Ausweisfunktion ist in diesem Zustand nicht mehr möglich.</p><p>Sie können die PIN im folgenden Dialog entsperren. Die Anwendung unterstützt Sie in den nun notwendigen Schritten. - The given PIN is not correct. You have one more try to enter the correct PIN. Please mind that you have to acknowledge this last try with your card access number (CAN). Die eingegebene PIN ist nicht korrekt. Sie haben noch eine weitere Möglichkeit die korrekte PIN einzugeben. Beachten Sie, dass Sie diesen letzten Versuch mit der Zugangsnummer (CAN) bestätigen müssen. - - The given PIN is not correct. You have %1 tries to enter the correct PIN. - Die eingegebene PIN ist nicht korrekt. Sie haben noch %1 weitere Möglichkeiten die korrekte PIN einzugeben. - - - PIN blocked PIN gesperrt - After three wrong entries your PIN is blocked. Using the online identification function is no longer possible. <br/>You can unblock the PIN as follows:<ol><li> Select the "Settings" function.</li><li>Select the "PIN Management" tab. </li><li>Follow the instructions on the screen.</li></ol>Note: You will find the PUK in the letter you received during the application for the ID card in the "Unblocking key PUK" section. Further information is available on the site <a href="http://www.personalausweisportal.de">http://www.personalausweisportal.de</a>.<br>Do you want to unblock the PIN now? Sie haben Ihre PIN dreimal falsch eingegeben. Die Online-Ausweisfunktion ist jetzt blockiert. Die Blockierung können Sie mit Ihrer Entsperrnummer (PUK) aufheben. Sie finden Ihre PUK in dem Schreiben, das Sie nach Beantragung Ihres Ausweisdokuments von der für die Ausgabe Ihres Ausweisdokuments zuständigen Behörde erhalten haben. Bitte beachten Sie: Sie können mit Ihrer PUK lediglich Ihren Online-Ausweis entsperren. Sollten Sie Ihre PIN vergessen haben, können Sie von der für die Ausgabe Ihres Ausweisdokuments zuständigen Behörde eine neue PIN setzen lassen.<br>Wollen Sie die Blockierung nun aufheben? + + The given PIN is not correct. You have 2 remaining tries to enter the correct PIN. + Die eingegebene PIN ist nicht korrekt. Sie haben noch 2 weitere Möglichkeiten die korrekte PIN einzugeben. + governikus::HistoryModelSearchFilter - dd.MM.yyyy dd.MM.yyyy @@ -3607,142 +4143,149 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::HistoryWidget - Date Datum - Details Details - dd.MM.yyyy hh:mm AP dd.MM.yyyy HH:mm - Date: Datum: - Provider Diensteanbieter - Purpose Zweck - Data Daten - AusweisApp2.History.%1.pdf AusweisApp2.Verlauf.%1.pdf - Save Speichern - - PDF Documents PDF-Dokumente - governikus::LogFilesDialog + governikus::LogFileSaveDialog - - Log - Protokoll - - - - Current log - Akuelles Protokoll - - - - dd.MM.yyyy hh:mm:ss AP - dd.MM.yyyy HH:mm:ss - - - - File could not be opened: - Datei kann nicht geöffnet werden: - - - - File is larger than 3 MB and can not be displayed: - Die Datei ist größer als 3MB und kann nicht angezeigt werden: - - - - Delete log files - Protokolle löschen - - - - Do you really want to delete all old log files? - Wolle Sie wirklich alle alten Protokolle löschen? - - - Save Speichern - File error Dateifehler - An error occurred while saving the file. Beim Speichern der Datei ist ein Fehler aufgetreten. + + governikus::LogFilesDialog + + Log + Protokoll + + + Current log + Aktuelles Protokoll + + + dd.MM.yyyy hh:mm:ss AP + dd.MM.yyyy HH:mm:ss + + + File could not be opened: + Datei kann nicht geöffnet werden: + + + File is larger than 3 MB and can not be displayed: + Die Datei ist größer als 3MB und kann nicht angezeigt werden: + + + Delete log files + Protokolle löschen + + + Do you really want to delete all old log files? + Wolle Sie wirklich alle alten Protokolle löschen? + + governikus::LogHandler - An error occurred in log handling: %1 Es ist ein Fehler bei der Protokollverwaltung aufgetreten: %1 + + governikus::LogModel + + Current log + Aktuelles Protokoll + + + dd.MM.yyyy hh:mm:ss + dd.MM.yyyy hh:mm:ss + + + Send application log per email... + Anwendungsprotokoll per E-Mail senden... + + + Share application log... + Anwendungsprotokoll teilen... + + + support.ausweisapp2@governikus.de + support.ausweisapp2@governikus.de + + + Android log file + Android Protokolle + + + <Please describe the error> + <Bitte beschreiben Sie den Fehler> + + governikus::NumberModel - The given PIN is not correct. You have 2 tries to enter the correct PIN. Die eingegebene PIN ist nicht korrekt. Sie haben noch 2 weitere Möglichkeiten die korrekte PIN einzugeben. - You have entered the wrong PIN twice. Prior to a third attempt, you have to enter your six-digit card access number first. You can find your card access number on the front of your ID card. Sie haben Ihre PIN zweimal falsch eingegeben. Für einen dritten Versuch müssen Sie vorher Ihre 6-stellige Zugangsnummer eingeben. Sie finden Ihre Zugangsnummer auf der Vorderseite Ihres Ausweises. - You have entered a wrong PIN three times. Your PIN is now blocked. You have to enter the PUK now for unblocking. Sie haben Ihre PIN dreimal falsch eingegeben. Ihre PIN ist jetzt gesperrt. Zum Entsperren geben Sie bitte Ihre PUK ein. - You have entered a wrong CAN, please try again. Sie haben eine falsche CAN eingegeben. Bitte versuchen Sie es erneut. - You have entered a wrong PUK. Please try again. Sie haben eine falsche PUK eingegeben. Bitte versuchen Sie es erneut. @@ -3750,12 +4293,10 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::PdfCreator - AusweisApp2 is a product of Governikus GmbH & Co. KG - on behalf of the Federal Ministry of the Interior, Building and Community. Die AusweisApp2 ist ein Produkt der Governikus GmbH & Co. KG - im Auftrag des Bundesministeriums des Innern, für Bau und Heimat. - For further information, please see <a href='https://www.ausweisapp.bund.de/'>https://www.ausweisapp.bund.de/</a> Mehr Information finden Sie auf <a href='https://www.ausweisapp.bund.de/'>https://www.ausweisapp.bund.de/</a> @@ -3763,74 +4304,58 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::PdfExporter - Date Datum - Details Details - dd.MM.yyyy hh:mm AP dd.MM.yyyy HH:mm - Provider: Diensteanbieter: - Purpose: Zweck: - Data: Daten: - - dd.MM.yyyy dd.MM.yyyy - - hh:mm AP HH:mm - At %1 %2 the following data were saved: Die folgenden Daten wurden hier %1 %2 gespeichert: - History Verlauf - Entry Eintrag - Content Inhalt - At %1 %2 the following data has been read out of your ID card: Die folgenden Daten wurden hier %1 %2 aus Ihrem Ausweisdokument ausgelesen: - Information Information @@ -3838,84 +4363,65 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::PinSettingsWidget - - - - - Only digits (0-9) are allowed. Es sind nur Ziffern (0-9) erlaubt. - Enter PUK PUK eingeben - Change PIN PIN ändern - The PIN in the field "%1" does not match the PIN in the field "%2". Die PIN im Feld "%1" stimmt nicht mit der PIN im Feld "%2" überein. - - PIN correct. - Die PIN stimmt überein. - - - card inserted karte aufgelegt - no card inserted karte nicht aufgelegt + + PIN correct. + Die PIN stimmt überein. + governikus::ProviderModel - %1 seconds free, afterwards %1 Sekunde frei, danach - landline costs %1; Festnetzpreis %1; - mobile costs may vary. Mobilfunkpreise abweichen. - mobile costs %1 Mobilfunkpreise max. %1 - %1/min %1/min - %1/call %1/Anruf - %1 EUR %1 EUR - %1 ct %1 ct @@ -3923,28 +4429,17 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::ProviderWidget - Name Name - Address Adresse - - governikus::QmlExtension - - - Send application log per email... - Anwendungsprotokoll per E-Mail senden... - - governikus::RandomPinDialog - On screen password Bildschirmtastatur @@ -3952,7 +4447,6 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::ReaderDeviceDialog - Reader Driver Integration Treiber für Kartenlesegeräte @@ -3960,48 +4454,39 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::ReaderDeviceWidget - online help Is embedded in a sentence. Online-Hilfe - No smartphone with enabled remote service found. See %1 for details of use. Kein Smartphone mit aktiviertem Fernzugriff gefunden. Informationen zur Verwendung befinden sich unter %1. - No connected card reader found. See %1 for installation of card readers. Es wurde kein Kartenlesegerät gefunden. Details zur Installation finden Sie in der %1. - - Please connect suitable card reader + Please connect suitable card reader. Bitte schließen Sie ein geeignetes Kartenlesegerät an. - Select a device to display more information about it Wählen Sie ein Kartenlesegerät aus, um mehr Informationen zu erhalten - hh:mm:ss AP HH:mm:ss - The list of card readers was last updated at %1. Die Liste der Kartenlesegeräte wurde zuletzt um %1 aktualisiert. - Please start pairing mode first. Starten Sie den Kopplungsmodus auf Ihrem Smartphone, falls noch nicht geschehen. - Pairing Kopplung @@ -4009,37 +4494,30 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::ReaderDriverModel - - Not connected - Nicht angeschlossen - - - Driver installed Treiber installiert - No driver installed Treiber nicht installiert - + Not connected + Nicht angeschlossen + + Device Gerät - Status Status - Card reader ready for use. Das Kartenlesegerät kann verwendet werden. - Please download and install the driver you can find at: %1 Bitte installieren Sie den Treiber: %1 @@ -4047,60 +4525,49 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::RemoteDeviceModel - Not connected Nicht verbunden - Paired and available Gekoppelt und verfügbar - Paired, but unsupported Gekoppelt, aber nicht unterstützt - Paired, but unavailable Gekoppelt, aber nicht verfügbar - Unsupported version Nicht unterstützte Version - Not paired Nicht gekoppelt - Device Gerät - Status Status - - dd.MM.YYYY hh:mm AP + dd.MM.yyyy hh:mm AP dd.MM.yyyy HH:mm governikus::RemotePinInputDialog - Only digits (0-9) are allowed. Es sind nur Ziffern (0-9) erlaubt. - A pairing code has to be 4 digits long. Ein Kopplungscode besteht aus 4 Ziffern. @@ -4108,17 +4575,14 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::RemoteServiceModel - NFC is not available on your device. NFC ist auf Ihrem Gerät nicht verfügbar. - Please enable NFC to use the remote service. Bitte aktivieren Sie NFC, um den Fernzugriff zu benutzen. - Please connect your WiFi to use the remote service. Bitte verbinden Sie sich mit Ihrem WLAN, um den Fernzugriff zu benutzen. @@ -4126,199 +4590,65 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::RemoteServiceSettings - Remote Reader Entferntes Kartenlesegeräte - - governikus::Result - - - An unexpected error has occurred during processing. - Während der Verarbeitung ist ein unerwarteter Fehler aufgetreten. - - - - Use of the function by the client application is not permitted. - Für die Operation fehlen die benötigten Rechte. - - - - An internal error has occurred during processing. - Während der Verarbeitung ist ein interner Fehler aufgetreten. - - - - There was some problem with a provided or omitted parameter. - Ein fehlerhafter Parameter wurde übermittelt. - - - - The API function is unknown. - Die Schnittstellenfunktion ist unbekannt. - - - - The framework or layer has not been initialized. - Die Schnittstelle ist nicht initialisiert. - - - - - The active session has been terminated. - Die aktuelle Sitzung wurde beendet. - - - - - - A Communication error occurred during processing. - Während der Verarbeitung ist ein Verbindungsfehler aufgetreten. - - - - - The operation was terminated as the set time was exceeded. - Der Vorgang wurde wegen einer Zeitüberschreitung abgebrochen. - - - - The operation was aborted as an invalid channel handle was used. - Der Vorgang wurde wegen einer ungültigen Verbindung abgebrochen. - - - - A trusted channel could not be opened. - Es konnte keine sichere Verbindung hergestellt werden. - - - - The operation was aborted as an unknown protocol was used. - Der Vorgang wurde wegen der Verwendung eines unbekannten Protokolls abgebrochen. - - - - The operation was aborted as an unknown cipher suite was used. - Der Vorgang wurde wegen der Verwendung eines unbekannten Verschlüsselungsalgorithmus abgebrochen. - - - - The operation was aborted as an unknown web service binding was used. - Der Vorgang wurde wegen der Verwendung eines unbekannten Webservice-Bindings abgebrochen. - - - - Signature certificate key generation is not possible. - Die Erzeugung eines Schlüssels für das Signaturzertifikat war nicht möglich. - - - - The process was cancelled by the user. - Der Benutzer hat den Vorgang abgebrochen. - - - - One or more certificate checks failed. The operation will be aborted due to security reasons. - Eine oder mehrere Zertifikatsprüfungen schlugen fehl. Der Vorgang wird aus Sicherheitsgründen abgebrochen. - - - - This action cannot be performed. The online identification function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the online identification function. - Diese Aktion kann leider nicht durchgeführt werden. Die Online-Ausweisfunktion Ihres Ausweisdokuments ist nicht aktiviert. Bitte wenden Sie sich an die Behörde, die Ihr Ausweisdokument ausgegeben hat, um die Online-Ausweisfunktion zu aktivieren. - - - - The authenticity of your ID card could not be verified. Please make sure that you are using a genuine ID card. Please note that test applications require the use of a test ID card. - Die Echtheit Ihres Ausweisdokuments konnte nicht überprüft werden. Bitte stellen Sie sicher, dass Sie ein echtes Ausweisdokument verwenden. Bitte beachten Sie, dass Sie bei Testanwendungen einen Testausweis verwenden müssen. - - - - The age verification failed. - Die Altersverifikation war nicht erfolgreich. - - - - The community verification failed. - Die Wohnortverifikation war nicht erfolgreich. - - - - The ID card is invalid or disabled. - Das Ausweisdokument ist ungültig oder gesperrt. - - governikus::SelfData - This data has not been stored in this chip generation. Diese Datengruppe wurde in dieser Chip-Generation nicht gespeichert. - dd.MM.yyyy dd.MM.yyyy - Family name Familienname - Birth name Geburtsname - Given name(s) Vorname(n) - Doctoral degree Doktorgrad - Date of birth Geburtsdatum - Place of birth Geburtsort - - - Address Adresse - Document type Dokumentenart - Nationality Staatsangehörigkeit - Religious / artistic name Ordens- / Künstlername - Issuing country Ausstellender Staat - Residence permit I Nebenbestimmungen I (nur eAT) @@ -4326,28 +4656,22 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::SelfInfoWidget - Save as PDF... Als PDF speichern... - save id card data as pdf Daten des Ausweisdokuments als PDF speichern - AusweisApp2.Information.%1.pdf AusweisApp2.Selbstauskunft.%1.pdf - Save Speichern - - PDF Documents PDF-Dokumente @@ -4355,27 +4679,22 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::SelfInformationWidget - Use the button 'See my personal data now...' to display the data stored on your ID card. An Internet connection is required to display the data. - Über die Schaltfläche 'Meine Daten einsehen...' können Sie sich die im Chip Ihres Ausweisdokuments gespeicherten Daten anzeigen lassen. Um die Daten anzeigen zu können, benötigt diese Anwendung eine Internetverbindung. + Über die Schaltfläche 'Meine Daten einsehen' können Sie sich die im Chip Ihres Ausweisdokuments gespeicherten Daten anzeigen lassen. Um die Daten anzeigen zu können, benötigt diese Anwendung eine Internetverbindung. - https://www.ausweisapp.bund.de/datenschutz/ https://www.ausweisapp.bund.de/datenschutz/ - data privacy statement Datenschutzerklärung - Your personal data is neither saved nor processed in any way. Please see our %1 for details on how your personal data is processed. Es erfolgt keine Speicherung oder Weiterverarbeitung Ihrer persönlichen Daten. Näheres dazu erfahren Sie in unserer %1. - Test environment Testumgebung @@ -4383,27 +4702,22 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::SettingsWidget - Apply Übernehmen - Developer Settings Entwicklereinstellungen - Apply settings? Einstellungen übernehmen? - Do you want to apply the changes? Möchten Sie die Änderungen übernehmen? - OK OK @@ -4411,73 +4725,58 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::SetupAssistantWizard - - setup assistant - Einrichtungsassistent - - - - - Step %1 of %2 - Schritt %1 von %2 - - - - Introduction - Einleitung - - - Welcome to the AusweisApp2 setup assistant. This assistant will guide you through the setup process in %1 steps. You can cancel the setup assistant at any time. To restart it, go to the tab "Help" and select "Setup assistant". Willkommen zum Einrichtungsassistenten der AusweisApp2. Sie werden in %1 Schritten durch die Einrichtung geführt. Sie können diesen Einrichtungsassistenten jederzeit abbrechen und später über den Reiter "Hilfe" innerhalb der AusweisApp2 durch Auswahl des Eintrags "Einrichtungsassistent" erneut starten. - - History - Verlauf - - - - Do you want to save the history of your authentications? - Möchten Sie durchgeführte Authentisierungen in einem Verlauf speichern? - - - - save - speichern - - - - save history - Verlauf speichern - - - - Card Readers - Kartenlesegeräte - - - - Almost done! - Fast fertig! - - - - Personal 6 - digit PIN - Persönliche 6-stellige PIN - - - - Prior to the first use of the online identification function, you have to replace the transport PIN by an individual 6-digit PIN. The transport PIN was sent to you by postal mail. - Vor der ersten Nutzung der Online-Ausweisfunktion müssen Sie die Transport-PIN durch eine persönliche 6-stellige PIN ersetzen. Sie finden Ihre Transport-PIN in dem PIN-Brief, welcher Ihnen postalisch zugesandt wurde. - - - Set individual PIN Persönliche PIN setzen - + setup assistant + Einrichtungsassistent + + + Step %1 of %2 + Schritt %1 von %2 + + + Introduction + Einleitung + + + History + Verlauf + + + Do you want to save the history of your authentications? + Möchten Sie durchgeführte Authentisierungen in einem Verlauf speichern? + + + save + speichern + + + save history + Verlauf speichern + + + Card Readers + Kartenlesegeräte + + + Almost done! + Fast fertig! + + + Personal 6 - digit PIN + Persönliche 6-stellige PIN + + + Prior to the first use of the online identification function, you have to replace the transport PIN by an individual 6-digit PIN. The transport PIN was sent to you by postal mail. + Vor der ersten Nutzung der Online-Ausweisfunktion müssen Sie die Transport-PIN durch eine persönliche 6-stellige PIN ersetzen. Sie finden Ihre Transport-PIN in dem PIN-Brief, welcher Ihnen postalisch zugesandt wurde. + + AusweisApp2 is now ready for use. For more information on the software or the online identification function, visit the %1online help%2. Sie können die AusweisApp2 nun verwenden. Weitere Informationen zur Software und der Online-Ausweisfunktion erhalten Sie in der %1Online-Hilfe%2. @@ -4485,15 +4784,13 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::StateChangePin - You have successfully changed your PIN. Sie haben Ihre PIN erfolgreich geändert. - governikus::StateEstablishPacePuk + governikus::StateEstablishPaceChannel - PIN successfully unblocked PIN erfolgreich entsperrt @@ -4501,7 +4798,6 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::StateWriteHistory - Validity: %1 - %2 Gültigkeit: @@ -4511,12 +4807,10 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::StepAdviseUserToRemoveCardGui - Information Information - You may now remove your ID card from the card reader. Sie können nun Ihr Ausweisdokument vom Kartenlesegerät entfernen. @@ -4524,12 +4818,10 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::StepAuthenticationEac1Gui - Information Information - Please observe the display of your card reader. Bitte beachten Sie die Anzeige Ihres Kartenlesegeräts. @@ -4537,144 +4829,112 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::StepAuthenticationEac1Widget - - CAN: - CAN: - - - - Please enter the six-digit card access number (CAN) for identification. - Bitte geben Sie Ihre 6-stellige Zugangsnummer (CAN) ein. - - - Validity: %1 - %2 Gültigkeit: %1 - %2 - Only digits (0-9) are permitted. Es sind nur Ziffern (0-9) erlaubt. - Card is being verified Karte wird geprüft - Service provider is verified Diensteanbieter wird geprüft - Reading data Daten werden gelesen - Service provider is being verified Diensteanbieter wird geprüft - - - - - Identify now Jetzt ausweisen - - See details under more... + See details under "more..." Weitere Details unter "mehr..." - - OK - OK - - - - Identify - Ausweisen - - - - Identification successful - Ausweisen erfolgreich - - - - The process was cancelled by the user - Der Benutzer hat den Vorgang abgebrochen - - - - Result - Ergebnis - - - - Please pay attention to the display of your card reader. - Bitte beachten Sie die Anzeige auf Ihrem Kartenlesegerät. - - - - Please enter your six-digit card access number (CAN) and your PIN for identification. - Bitte geben Sie Ihre 6-stellige Zugangsnummer (CAN) und Ihre PIN ein, um sich auszuweisen. - - - - Please enter your six digit PIN for identification - Bitte geben Sie Ihre 6-stellige PIN ein, um sich auszuweisen - - - - Service provider: - Diensteanbieter: - - - - Certificate issuer: - Aussteller des Berechtigungszertifikats: - - - - Information on the service provider who wants to read out data from your ID card is given here. For further information press the button "more...". - Hier erhalten Sie Informationen über den Diensteanbieter, der die Daten aus Ihrem Personalausweis auslesen möchte. Für weitere Informationen drücken Sie bitte die Schaltfläche "mehr...". - - - - Here you can select or deselect data fields to be read out. Mandatory data fields required by the service provider cannot be deselected. - Hier können Sie die Datenfelder an/abwählen, die ausgelesen werden sollen. Felder, die Sie nicht abwählen können, sind durch den Diensteanbieter als Pflichtfelder festgelegt worden. Diese Felder sind daher nicht abwählbar. - - - - Card access number (CAN): - Zugangsnummer (CAN): - - - - open on screen keyboard öffne bildschirmtastatur - - please enter your can - bitte geben sie ihre zugangsnummer (can) ein - - - please enter your pin bitte geben sie ihre pin ein - + CAN: + CAN: + + + Identify + Ausweisen + + + Identification successful + Ausweisen erfolgreich + + + The process was cancelled by the user + Der Benutzer hat den Vorgang abgebrochen + + + Result + Ergebnis + + + Please pay attention to the display of your card reader. + Bitte beachten Sie die Anzeige auf Ihrem Kartenlesegerät. + + + Please enter the six-digit card access number (CAN) for identification. + Bitte geben Sie Ihre 6-stellige Zugangsnummer (CAN) ein. + + + Please enter your six-digit card access number (CAN) and your PIN for identification. + Bitte geben Sie Ihre 6-stellige Zugangsnummer (CAN) und Ihre PIN ein, um sich auszuweisen. + + + Please enter your six digit PIN for identification + Bitte geben Sie Ihre 6-stellige PIN ein, um sich auszuweisen + + + Service provider: + Diensteanbieter: + + + Certificate issuer: + Aussteller des Berechtigungszertifikats: + + + Information on the service provider who wants to read out data from your ID card is given here. For further information press the button "more...". + Hier erhalten Sie Informationen über den Diensteanbieter, der die Daten aus Ihrem Personalausweis auslesen möchte. Für weitere Informationen drücken Sie bitte die Schaltfläche "mehr...". + + + Here you can select or deselect data fields to be read out. Mandatory data fields required by the service provider cannot be deselected. + Hier können Sie die Datenfelder an/abwählen, die ausgelesen werden sollen. Felder, die Sie nicht abwählen können, sind durch den Diensteanbieter als Pflichtfelder festgelegt worden. Diese Felder sind daher nicht abwählbar. + + + Close + Schließen + + + please enter your can + bitte geben sie ihre zugangsnummer (can) ein + + + Card access number (CAN): + Zugangsnummer (CAN): + + PIN: PIN: @@ -4682,77 +4942,66 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::StepChooseCardGui - Information Information - Cancel Abbrechen - Settings Einstellungen - - If you need help or have problems with your card reader, you can consult the %1online help%2 for futher information. - Wenn Sie Hilfe benötigen oder Probleme mit Ihrem Kartenlesegerät haben, können Sie für weitere Informationen die %1Online-Hilfe%2 öffnen. - - - - Please make sure that only one card reader with an ID card on it is connected to your computer. If you have already placed an ID card on your card reader, you can consult the %1online help%2 for futher information. - Bitte stellen Sie sicher, dass an Ihrem Computer nur ein Kartenlesegerät mit aufliegendem Ausweisdokument angeschlossen ist. Sollten Sie bereits ein Ausweisdokument aufgelegt haben, können Sie für weitere Informationen die %1Online-Hilfe%2 öffnen. - - - - Extended Length is not supported. - Extended Length wird nicht überstützt. - - - - Your remote reader does not meet the technical requirements (Extended Length not supported). - Ihr entferntes Kartenlesegerät erfüllt leider nicht die technischen Voraussetzungen (Extended Length wird nicht unterstützt). - - - No card reader found. Please connect card reader first. Kein Kartenlesegerät erkannt. Bitte schließen Sie ein Kartenlesegerät an. - + If you need help or have problems with your card reader, you can consult the %1online help%2 for futher information. + Wenn Sie Hilfe benötigen oder Probleme mit Ihrem Kartenlesegerät haben, können Sie für weitere Informationen die %1Online-Hilfe%2 öffnen. + + Please choose "Settings" to install a card reader or configure your smartphone as a card reader. Falls Sie ein Kartenlesegerät einrichten oder Ihr Smartphone für die Nutzung als Kartenlesegerät konfigurieren möchten, klicken Sie bitte auf "Einstellungen". - + Extended Length is not supported. + Extended Length wird nicht überstützt. + + + Your remote reader does not meet the technical requirements (Extended Length not supported). + Ihr entferntes Kartenlesegerät erfüllt leider nicht die technischen Voraussetzungen (Extended Length wird nicht unterstützt). + + + At least one of your card readers does not meet the technical requirements (Extended Length not supported). Please place the ID card on a different card reader. + Mindestens eines Ihrer Kartenlesegeräte erfüllt leider nicht die technischen Voraussetzungen (Extended Length wird nicht unterstützt). Bitte platzieren Sie Ihren Ausweis auf einem anderen Kartenlesegerät. + + Connected to following remote readers: %1. Verbunden mit den folgenden entfernten Lesegeräten: %1. - - If you have already placed an ID card on your card reader, you can consult the %1online help%2 for futher information. - Sollten Sie bereits Ihr Ausweisdokument aufgelegt haben, können Sie für weitere Informationen die %1Online-Hilfe%2 öffnen. - - - Please place an ID card on the card reader. Bitte legen Sie ein Ausweisdokument auf. - + If you have already placed an ID card on your card reader, you can consult the %1online help%2 for futher information. + Sollten Sie bereits Ihr Ausweisdokument aufgelegt haben, können Sie für weitere Informationen die %1Online-Hilfe%2 öffnen. + + Please place only one ID card on the card reader. Bitte legen Sie nur ein Ausweisdokument auf. - + Please make sure that only one card reader with an ID card on it is connected to your computer. If you have already placed an ID card on your card reader, you can consult the %1online help%2 for futher information. + Bitte stellen Sie sicher, dass an Ihrem Computer nur ein Kartenlesegerät mit aufliegendem Ausweisdokument angeschlossen ist. Sollten Sie bereits ein Ausweisdokument aufgelegt haben, können Sie für weitere Informationen die %1Online-Hilfe%2 öffnen. + + Online identification function is disabled. Die Online-Ausweisfunktion ist deaktiviert. - This action cannot be performed. The online identification function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the online identification function. Diese Aktion kann leider nicht durchgeführt werden. Die Online-Ausweisfunktion Ihres Ausweisdokuments ist nicht aktiviert. Bitte wenden Sie sich an die Behörde, die Ihr Ausweisdokument ausgegeben hat, um die Online-Ausweisfunktion zu aktivieren. @@ -4760,101 +5009,98 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::StepErrorGui - Sorry, that should not have happened! Please contact the support team. Entschuldigung, das hätte nicht passieren dürfen! Bitte kontaktieren Sie das Support Team. - Error Fehler + + Save Log + Protokoll speichern + governikus::StepShowSelfAuthenticationDataGui - Close Schließen + + governikus::TrayIcon + + Open + Öffnen + + + Exit AusweisApp2 + AusweisApp2 beenden + + + AusweisApp2 was started. + AusweisApp2 wurde gestartet. + + governikus::UpdateWindow - Unable to open this link in a browser. Please copy and paste the link into the address bar of your browser. Der Link konnte im Browser nicht geöffnet werden. Bitte kopieren Sie den Link aus dieser Meldung und fügen Sie ihn manuell in die Adressleiste Ihres Browsers ein. - <h4>Download of release notes failed</h4> <h4>Herunterladen der Versionshinweise schlug fehl</h4> + + AusweisApp2 %1 is now available - you have %2. Would you like to download it now? + AusweisApp2 %1 ist jetzt verfügbar - Sie haben %2. Wollen Sie die neue Version jetzt herunterladen? + governikus::VersionInformationModel - Application Name Anwendungsname - Application Version Anwendungsversion - Organization Organisation - Organization domain Organisation Homepage - - Build date - Build-Datum - - - VersionCode Versions-Code - System version Systemversion - Kernel Kernel - Architecture Architektur - - Compiled architecture - Kompilierte Architektur - - - Device Gerät - Qt Version Qt-Version - OpenSSL Version OpenSSL-Version @@ -4862,138 +5108,109 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::WebserviceActivationContext - - - The browser connection was lost. Die Verbindung zum Browser ging verloren. - - Cannot start authentication Authentisierungsvorgang kann nicht gestartet werden - An operation is already in progress. Ein Vorgang ist bereits in Arbeit. - Would you like to try again? Möchten Sie es erneut versuchen? - Try again Erneut versuchen - 400 Bad Request 400 Ungültige Anfrage - 404 Not found 404 Nicht gefunden - Invalid request Ungültige Anfrage - Your browser sent a request that couldn't be interpreted. Ihr Browser hat eine Anfrage gesendet, die nicht interpretiert werden konnte. - Error message Fehlermeldung - Would you like to report this error? Möchten Sie diesen Fehler melden? - https://www.ausweisapp.bund.de/en/feedback/melden-sie-einen-fehler/ https://www.ausweisapp.bund.de/feedback/melden-sie-einen-fehler/ - Report now Jetzt melden - - The connection to the browser was lost. No forwarding was executed. Please try to call the URL again manually: <a href='%1'>%2</a> - Die Verbindung zum Browser ging verloren. Es konnte keine Weiterleitung durchgeführt werden. Bitte versuchen Sie die URL manuell aufzurufen: <a href='%1'>%2</a> + The connection to the browser was lost. No forwarding was executed. Please try to call the URL again manually: %1 + Die Verbindung zum Browser ging verloren. Es konnte keine Weiterleitung durchgeführt werden. Bitte versuchen Sie die URL manuell aufzurufen: %1 governikus::WebserviceActivationHandler - An unknown program uses the required port (%1). Please exit the other program and try again! Eine unbekannte Anwendung verwendet den benötigten Port (%1) bereits. Bitte beenden Sie zuerst die andere Anwendung und versuchen Sie es anschließend erneut! - Another program (%1) uses the required port (%2). Please exit this other program and try again! Eine andere Anwendung (%1) verwendet den benötigten Port (%2) bereits. Bitte beenden Sie zuerst die andere Anwendung und versuchen Sie es anschließend erneut! - 404 Not found 404 Nicht gefunden - Invalid request Ungültige Anfrage - Your browser sent a request that couldn't be interpreted. Ihr Browser hat eine Anfrage gesendet, die nicht interpretiert werden konnte. - Error message Fehlermeldung - Unknown request: %1 Unbekannte Anfrage: %1 - Would you like to report this error? Möchten Sie diesen Fehler melden? - https://www.ausweisapp.bund.de/en/feedback/melden-sie-einen-fehler/ https://www.ausweisapp.bund.de/feedback/melden-sie-einen-fehler/ - Report now Jetzt melden - You tried to start a newer version (%1) of currently running application. Please stop the current version (%2) and start again! Sie versuchen eine neuere Version (%1) der aktuell laufenden Anwendung zu starten. Bitte beenden Sie zuerst die andere Version (%2) und versuchen Sie es anschließend erneut! - You tried to start an older version (%1) of currently running application. Please open the currently running version (%2)! Sie versuchen eine ältere Version (%1) der aktuell laufenden Anwendung zu starten. Bitte öffnen Sie die aktuell laufende Version (%2)! @@ -5001,22 +5218,18 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::WorkflowAuthenticateQtGui - Identify Ausweisen - Cancel Abbrechen - <b>Do you really want to cancel?</b> <b>Wollen Sie wirklich abbrechen?</b> - You can as well identity later by calling the service provider's Internet page again. Sie können sich auch später ausweisen, indem Sie erneut auf die Internetseite des Diensteanbieters gehen. @@ -5024,12 +5237,10 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::WorkflowQtWidget - Cancel Abbrechen - Next Weiter @@ -5037,7 +5248,6 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe governikus::WorkflowSelfInfoQtGui - Identify Ausweisen @@ -5045,7 +5255,6 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe main - To close the app, quickly press the back button twice. Um die Anwendung zu schließen, drücken Sie zweimal schnell die Zurück-Taste. diff --git a/resources/updatable-files/reader/img_Cherry_ST_1275_mit_Ausweis.png b/resources/updatable-files/reader/img_Cherry_ST_1275_mit_ausweis.png similarity index 100% rename from resources/updatable-files/reader/img_Cherry_ST_1275_mit_Ausweis.png rename to resources/updatable-files/reader/img_Cherry_ST_1275_mit_ausweis.png diff --git a/resources/updatable-files/reader/img_HID_Omnikey_5421.png b/resources/updatable-files/reader/img_HID_Omnikey_542x.png similarity index 100% rename from resources/updatable-files/reader/img_HID_Omnikey_5421.png rename to resources/updatable-files/reader/img_HID_Omnikey_542x.png diff --git a/resources/updatable-files/reader/img_HID_Omnikey_5421_mit_ausweis.png b/resources/updatable-files/reader/img_HID_Omnikey_542x_mit_ausweis.png similarity index 100% rename from resources/updatable-files/reader/img_HID_Omnikey_5421_mit_ausweis.png rename to resources/updatable-files/reader/img_HID_Omnikey_542x_mit_ausweis.png diff --git a/resources/updatable-files/reader/img_Signotec_Omega_Pad_mit_Ausweis.png b/resources/updatable-files/reader/img_Signotec_Omega_Pad_mit_ausweis.png similarity index 100% rename from resources/updatable-files/reader/img_Signotec_Omega_Pad_mit_Ausweis.png rename to resources/updatable-files/reader/img_Signotec_Omega_Pad_mit_ausweis.png diff --git a/resources/updatable-files/supported-providers.json b/resources/updatable-files/supported-providers.json index 573452f..1df3bd6 100644 --- a/resources/updatable-files/supported-providers.json +++ b/resources/updatable-files/supported-providers.json @@ -751,6 +751,20 @@ "tcTokenUrlInfo" : "TcToken URL requires valid dynamic request id.", "subjectUrls": ["https://www.fp-demail.de"] }, + { + "shortName": {"" : "OpenPGP-eID"}, + "longName": {"" :"Schlüsselbeglaubigung"}, + "shortDescription": {"" : "OpenPGP-Schlüssel mit dem Online-Ausweis beglaubigen"}, + "longDescription": {"": "Die Beglaubigung Ihres PGP-Schlüssels stellt gegenüber dem Empfänger sicher, dass Sie und kein Dritter der Absender einer Nachricht sind. Ein Dienst i.A. des Bundesamtes für Sicherheit in der Informationstechnik (BSI)."}, + "address": "https://pgp.governikus.de", + "homepage": "https://www.governikus.de/", + "phone": "+49 421 204 95-0", + "email": "kontakt@governikus.com", + "postalAddress": "Governikus GmbH & Co. KG
Hochschulring 4
28359 Bremen", + "category": "other", + "tcTokenUrl" : "https://pgp.governikus.de/pgp/ausweis-app-2", + "subjectUrls": ["https://pgp.governikus-eid.de"] + }, { "shortName": {"" : "Selbstauskunft - „Meine Daten einsehen“"}, "longDescription": {"": "Die AusweisApp2 verfügt über die Funktion \"Meine Daten einsehen\". Mit dieser Funktion können die auf dem Personalausweis bzw. dem elektronischen Aufenthaltstitel gespeicherten Daten ausgelesen und angezeigt werden. Hierbei sprechen wir auch von einer sogenannten Selbstauskunft.

Sobald Sie die AusweisApp2 gestartet und ein geeignetes Kartenlesegerät installiert bzw. ein Android-Smartphone verbunden haben, können Sie diese Funktion unter dem Menüpunkt \"Ausweisen\" aufrufen. (Auf Mobilgeräten finden Sie die Ausweis-Auskunft übrigens auch direkt auf der Startseite sowie im Menüeintrag „Ausweisen“.)

Nach Ihrer PIN-Eingabe und erfolgreicher Datenübertragung werden die Daten in der AusweisApp2 dargestellt.

Bei der Selbstauskunft handelt es sich um einen reinen Demonstrationsdienst. Die ausgelesenen Daten werden lediglich zur Anzeige gebracht und nicht weitergegeben.

Bitte beachten Sie, dass Sie auch für diesen Vorgang eine Internetverbindung benötigen. Dies hat folgenden Hintergrund: Der Zugriff auf die Daten des Personalausweises bzw. des elektronischen Aufenthaltstitels ist nur möglich, wenn derjenige, der auf die Daten zugreifen möchte sich selbst zunächst eindeutig identifiziert. Dies geschieht über das sog. Berechtigungszertifikat. Es wird Ihnen immer angezeigt, wer auf Ihre Daten zugreifen möchte. Die Erlaubnis, ein Berechtigungszertifikat zu erhalten, wird einen Diensteanbieter auf Antrag und nach Prüfung bei der Vergabestelle für Berechtigungszertifikate beim Bundesverwaltungsamt erteilt. Um das technische Berechtigungszertifikat anzuzeigen und darüber hinaus eine Gültigkeitsprüfung des Ausweisdokuments durchführen zu können, ist eine Internetverbindung zwingend erforderlich. Aus diesem Grund spricht man auch von der Online-Ausweisfunktion."}, @@ -758,7 +772,7 @@ "homepage": "https://www.ausweisapp.bund.de/", "phone": "+49 1805 - 348743", "email": "support@ausweisapp.de", - "postalAddress": "Governikus GmbH & Co. KG
- im Auftrag des Bundesministeriums des Innern, für Bau und Heimat -
Am Fallturm 9
D-28359 Bremen", + "postalAddress": "Governikus GmbH & Co. KG
- im Auftrag des Bundesministeriums des Innern, für Bau und Heimat -
Hochschulring 4
D-28359 Bremen", "image": "Selbstauskunft.jpg", "icon": "npa.svg", "category": "citizen", diff --git a/resources/updatable-files/supported-readers.json b/resources/updatable-files/supported-readers.json index 9c5e474..943d56a 100644 --- a/resources/updatable-files/supported-readers.json +++ b/resources/updatable-files/supported-readers.json @@ -135,7 +135,7 @@ "URL": "https://www.reiner-sct.com/support/support-anfrage/?os=Windows&productGroup=77304735&product=77304828&q=driver#choice5" }, { - "Platforms": [{"os": "mac", "min": "10.11"}], + "Platforms": [{"os": "mac"}], "URL": "https://www.reiner-sct.com/support/support-anfrage/?os=MacOS&productGroup=77304735&product=77304828&q=driver#choice5" }, { @@ -146,12 +146,7 @@ "Information": [ { - "Platforms":[{"os": "mac", "max": "10.10"}], - "DE": "Trotz der Installation des vom Hersteller bereitgestellten Treibers, wird das Kartenlesegerät vom System nicht erkannt.", - "EN": "Despite the driver being installed correctly, the card reader will not be recognized by the operating system." - }, - { - "Platforms":[{"os": "mac", "min": "10.11"}], + "Platforms":[{"os": "mac"}], "DE": "Es ist notwendig, die Treiber vom Hersteller zu installieren. Dazu folgen Sie bitte dem jeweiligen Link für Ihr Betriebssystem zur Herstellerseite.", "EN": "It is necessary to install the drivers from the manufacturer. Please follow the relevant link for your operating system." } @@ -196,7 +191,7 @@ "URL": "http://www.scm-pc-card.de/index.php?page=download&function=show_downloads&lang=de&product_id=738" }, { - "Platforms": [{"os": "unknown"}], + "Platforms": [{"os": "mac", "min":"10.13"}, {"os": "unknown"}], "URL": "https://support.identiv.com/sdi010-011/" } ], @@ -213,9 +208,14 @@ "EN": "Please note that the card reader only works with the SDI011-nPA-version. A reboot is required after the installation of the driver." }, { - "Platforms": [{"os": "mac"}], + "Platforms": [{"os": "mac", "max": "10.12"}], "DE": "Der Status des Kartenlesegeräts wird nach der Treiberinstallation und einem Neustart zwar als 'Treiber installiert' angezeigt, allerdings tritt bei der PIN-Eingabe ein Protokollfehler auf.", "EN": "Once the driver is installed and the system is re-booted, the card reader state will be shown as 'Driver installed'. However, upon entering the PIN a protocol error occurs." + }, + { + "Platforms": [{"os": "mac", "min":"10.13"}], + "DE": "Es ist notwendig, die Treiber vom Hersteller zu installieren. Dazu folgen Sie bitte dem jeweiligen Link für Ihr Betriebssystem zur Herstellerseite.", + "EN": "It is necessary to install the drivers from the manufacturer. Please follow the relevant link for your operating system." } ] }, @@ -302,7 +302,7 @@ "Information": [ { - "Platforms": [{"os": "win"}, {"os":"mac", "max": "10.10"}], + "Platforms": [{"os": "win"}], "DE": "Es ist notwendig, die Treiber vom Hersteller zu installieren. Dazu folgen Sie bitte dem jeweiligen Link für Ihr Betriebssystem zur Herstellerseite.", "EN": "It is necessary to install the drivers from the manufacturer. Please follow the relevant link for your operating system." } @@ -334,9 +334,9 @@ "Information": [ { - "Platforms": [{"os": "mac"}], - "DE": "Es ist notwendig, die Treiber vom Hersteller zu installieren. Dazu folgen Sie bitte dem jeweiligen Link für Ihr Betriebssystem zur Herstellerseite.", - "EN": "It is necessary to install the drivers from the manufacturer. Please follow the relevant link for your operating system." + "Platforms": [{"os": "win"}, {"os": "mac"}], + "DE":"Das Kartenlesegerät funktioniert mit dem systemseitig installierten Treiber. Falls Sie jedoch den Treiber von der Webseite des Herstellers installieren möchten, ist anschließend ein Neustart erforderlich.", + "EN":"The card reader operates with the driver automatically installed by the system. In case you prefer to install the driver from the manufacturer's webseite, a reboot is required." } ] }, @@ -364,37 +364,12 @@ } ], "Information": - [ - { - "Platforms": [{"os": "win"}, {"os": "mac"}], - "DE": "Es ist notwendig, die Treiber vom Hersteller zu installieren. Dazu folgen Sie bitte dem jeweiligen Link für Ihr Betriebssystem zur Herstellerseite.", - "EN": "It is necessary to install the drivers from the manufacturer. Please follow the relevant link for your operating system." - } - ] - }, - - { - "VendorId": "0x072F", - "ProductId": "0x2200", - "Name": "ACS ACR122U", - "Pattern": "ACS ACR122U PICC Interface( 0)?", - "Icon": "img_ACS_ACR122U.png", - "IconWithNPA": "img_ACS_ACR122U_mit_ausweis.png", - "Drivers": - [ - { - "Platforms": [{"os": "win"}, {"os": "mac"}], - "URL": "https://www.acs.com.hk/en/products/3/acr122u-usb-nfc-reader/" - } - ], - "Information": [ { "Platforms": [{"os": "win"}, {"os": "mac"}], "DE":"Das Kartenlesegerät funktioniert mit dem systemseitig installierten Treiber. Falls Sie jedoch den Treiber von der Webseite des Herstellers installieren möchten, ist anschließend ein Neustart erforderlich.", "EN":"The card reader operates with the driver automatically installed by the system. In case you prefer to install the driver from the manufacturer's webseite, a reboot is required." } - ] }, @@ -558,8 +533,8 @@ "ProductId": "0x5421", "Name": "OMNIKEY 5421", "Pattern": "OMNIKEY CardMan 5x21-CL 0|OMNIKEY CardMan \\(076B:5421\\) 5421(\\(1\\)|\\(2\\))", - "Icon": "img_HID_Omnikey_5421.png", - "IconWithNPA": "img_HID_Omnikey_5421_mit_ausweis.png", + "Icon": "img_HID_Omnikey_542x.png", + "IconWithNPA": "img_HID_Omnikey_542x_mit_ausweis.png", "Drivers": [ { @@ -585,6 +560,35 @@ ] }, + { + "VendorId": "0x076B", + "ProductId": "0x5422", + "Name": "OMNIKEY 5422", + "Pattern": "HID Global OMNIKEY 5422(CL)? Smartcard Reader 0|HID Global OMNIKEY Smartcard Reader (\\(1\\)|\\(2\\))", + "Icon": "img_HID_Omnikey_542x.png", + "IconWithNPA": "img_HID_Omnikey_542x_mit_ausweis.png", + "Drivers": + [ + { + "Platforms": [{"os": "win"}, {"os":"mac", "min": "10.13"}], + "URL": "https://www.hidglobal.com/drivers" + } + ], + "Information": + [ + { + "Platforms": [{"os": "win"}], + "DE": "Das Kartenlesegerät funktioniert mit dem systemseitig installierten Treiber. Falls Sie jedoch den Treiber von der Webseite des Herstellers installieren möchten, ist anschließend ein Neustart erforderlich.", + "EN": "The card reader operates with the driver automatically installed by the system. In case you prefer to install the driver from the manufacturer's webseite, a reboot is required." + }, + { + "Platforms": [{"os": "mac"}, {"max": "10.12"}], + "DE": "Das Kartenlesegerät funktioniert nicht mit dem systemseitig installierten Treiber. Es existiert kein Treiber vom Hersteller", + "EN": "The card reader not operates with the driver automatically installed by the system. There is no driver from the manufacturer" + } + ] + }, + { "VendorId": "0x0AB1", "ProductId": "0x0003", @@ -635,9 +639,9 @@ "Information": [ { - "Platforms":[{"os": "win"}, {"os":"mac","max":"10.10"}], - "DE": "Es ist notwendig, die Treiber vom Hersteller zu installieren. Dazu folgen Sie bitte dem jeweiligen Link für Ihr Betriebssystem zur Herstellerseite.", - "EN": "It is necessary to install the drivers from the manufacturer. Please follow the relevant link for your operating system." + "Platforms":[{"os": "win"}, {"os":"mac"}], + "DE": "Das Kartenlesegerät funktioniert mit dem systemseitig installierten Treiber. Falls Sie jedoch den Treiber von der Webseite des Herstellers installieren möchten, ist anschließend ein Neustart erforderlich.", + "EN": "The card reader operates with the driver automatically installed by the system. In case you prefer to install the driver from the manufacturer's webseite, a reboot is required." } ] }, @@ -680,7 +684,7 @@ "Drivers": [ { - "Platforms": [{"os": "win"}, {"os": "mac", "min": "10.11"}, {"os": "unknown"}], + "Platforms": [{"os": "win"}, {"os": "mac"}, {"os": "unknown"}], "URL": "https://cherry.de/download/de/download.php" } ], @@ -692,12 +696,7 @@ "EN": "It is necessary to install the drivers from the manufacturer. Please follow the relevant link for your operating system." }, { - "Platforms": [{"os": "mac", "max": "10.10"}], - "DE": "Der vom Hersteller angebotene Treiber für OS X 10.10 führt nicht zum Erfolg. Bitte verwenden Sie alternativ den ebenfalls vom Hersteller angebotenen Treiber für macOS 10.12.", - "DE": "The driver from the manufacturer is not supported on OS X 10.10. Please install the driver for macOS 10.12 instead." - }, - { - "Platforms": [{"os": "mac", "min": "10.11"}], + "Platforms": [{"os": "mac"}], "DE": "Das Kartenlesegerät funktioniert mit dem systemseitig installierten Treiber. Falls Sie jedoch den Treiber von der Webseite des Herstellers installieren möchten, ist anschließend ein Neustart erforderlich.", "EN": "The card reader operates with the driver automatically installed by the system. In case you prefer to install the driver from the manufacturer's webseite, a reboot is required." } @@ -714,7 +713,7 @@ "Drivers": [ { - "Platforms": [{"os": "win"}, {"os": "mac", "min": "10.11"}, {"os": "unknown"}], + "Platforms": [{"os": "win"}, {"os": "mac"}, {"os": "unknown"}], "URL": "https://cherry.de/download/de/download.php" } ], @@ -726,12 +725,7 @@ "EN": "It is necessary to install the drivers from the manufacturer. Please follow the relevant link for your operating system." }, { - "Platforms": [{"os": "mac", "max": "10.10"}], - "DE": "Der vom Hersteller angebotene Treiber für OS X 10.10 führt nicht zum Erfolg. Bitte verwenden Sie alternativ den ebenfalls vom Hersteller angebotenen Treiber für macOS 10.12.", - "EN": "The driver from the manufacturer is not supported on OS X 10.10. Please install the driver for macOS 10.12 instead." - }, - { - "Platforms": [{"os": "mac", "min": "10.11"}], + "Platforms": [{"os": "mac"}], "DE": "Das Kartenlesegerät funktioniert mit dem systemseitig installierten Treiber. Falls Sie jedoch den Treiber von der Webseite des Herstellers installieren möchten, ist anschließend ein Neustart erforderlich.", "EN": "The card reader operates with the driver automatically installed by the system. In case you prefer to install the driver from the manufacturer's webseite, a reboot is required." } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b6dd3ec..fcb7696 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,36 +1,30 @@ +##################################################################### +# The main component that will link all necessary modules and plugins +# into AusweisApp2 executable for the specific platform. +# +# This component includes a main entry point and command line +# parser only. Everything else will be included from sub-modules. +##################################################################### + ADD_SUBDIRECTORY(external) ADD_SUBDIRECTORY(global) ADD_SUBDIRECTORY(secure_storage) -ADD_SUBDIRECTORY(configuration) ADD_SUBDIRECTORY(settings) -ADD_SUBDIRECTORY(card) -ADD_SUBDIRECTORY(network) -ADD_SUBDIRECTORY(services) -ADD_SUBDIRECTORY(activation) -ADD_SUBDIRECTORY(core) -ADD_SUBDIRECTORY(jsonapi) -ADD_SUBDIRECTORY(websocket) -ADD_SUBDIRECTORY(aidl) -ADD_SUBDIRECTORY(remote_device) + ADD_SUBDIRECTORY(file_provider) +ADD_SUBDIRECTORY(configuration) +ADD_SUBDIRECTORY(network) + ADD_SUBDIRECTORY(export) -IF(DESKTOP) - ADD_SUBDIRECTORY(cli) -ENDIF() +ADD_SUBDIRECTORY(card) +ADD_SUBDIRECTORY(services) -IF(TARGET Qt5::Qml) - ADD_SUBDIRECTORY(qml) -ENDIF() +ADD_SUBDIRECTORY(ui) +ADD_SUBDIRECTORY(activation) +ADD_SUBDIRECTORY(remote_device) +ADD_SUBDIRECTORY(core) -# Use this if we can use QSvgPlugin without Widgets -# IF(TARGET Qt5::Widgets) -# https://bugreports.qt.io/browse/QTBUG-41884 -IF(DESKTOP) - ADD_SUBDIRECTORY(widget) - IF(TARGET Qt5::UiPlugin) - ADD_SUBDIRECTORY(widgetDesignerPlugin) - ENDIF() -ENDIF() +ADD_SUBDIRECTORY(whitelist_client) FUNCTION(CONFIGURE_CONFIG_H) IF(VENDOR_GOVERNIKUS) @@ -45,7 +39,7 @@ FUNCTION(CONFIGURE_CONFIG_H) SET(VERSION ${PROJECT_VERSION}) ENDIF() - SET(PRODUCT ${EXECUTABLE_BASE_NAME}) + SET(PRODUCT ${PROJECT_NAME}) CONFIGURE_FILE(config.h.in config.h @ONLY) ENDFUNCTION() @@ -67,7 +61,7 @@ OPTION(UPDATE_TRANSLATIONS "Update translations/*.ts files (WARNING: make clean IF(UPDATE_TRANSLATIONS) OPTION(UPDATE_TRANSLATIONS_NO_OBSOLETE "Remove obsolete translations from translations/*.ts files )") - SET(LUPDATE_OPTIONS -extensions c,cpp,h,ui,m,mm,qml,js -no-ui-lines) + SET(LUPDATE_OPTIONS -extensions c,cpp,h,ui,m,mm,qml,js -no-ui-lines -locations none) IF(UPDATE_TRANSLATIONS_NO_OBSOLETE) QT5_CREATE_TRANSLATION(QM_FILES ${SRC_DIR} ${RESOURCES_DIR} ${TRANSLATION_FILES} OPTIONS ${LUPDATE_OPTIONS} -no-obsolete) ELSE() @@ -136,10 +130,11 @@ ADD_DEPENDENCIES(AusweisApp AusweisAppRcc) CONFIGURE_CONFIG_H() -TARGET_LINK_LIBRARIES(AusweisApp AusweisAppCard AusweisAppCore AusweisAppGlobal AusweisAppNetwork AusweisAppActivation AusweisAppActivationInternal) -SET_TARGET_PROPERTIES(AusweisApp PROPERTIES OUTPUT_NAME "${EXECUTABLE_BASE_NAME}") -SET_TARGET_PROPERTIES(AusweisApp PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME AusweisApp2) +TARGET_LINK_LIBRARIES(AusweisApp AusweisAppGlobal AusweisAppCore) +SET_TARGET_PROPERTIES(AusweisApp PROPERTIES OUTPUT_NAME "${PROJECT_NAME}") +SET_TARGET_PROPERTIES(AusweisApp PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME ${PROJECT_NAME}) SET_TARGET_PROPERTIES(AusweisApp PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER com.governikus.ausweisapp2) +SET_TARGET_PROPERTIES(AusweisApp PROPERTIES MACOSX_BUNDLE_COPYRIGHT "${COPYRIGHT_TEXT}") @@ -153,7 +148,7 @@ IF(IOS) TARGET_LINK_LIBRARIES(AusweisApp -lqtpcre2 -lqtlibpng -lQt5GraphicsSupport -lQt5FontDatabaseSupport -lQt5ClipboardSupport -lqios -lqsvg -lqjpeg -lqtfreetype) TARGET_LINK_LIBRARIES(AusweisApp "-lc++ -lz -lm -u _qt_registerPlatformPlugin") - TARGET_LINK_LIBRARIES(AusweisApp ${IOS_ASSETSLIBRARY} ${IOS_UIKIT} ${IOS_COREBLUETOOTH} ${IOS_COREFOUNDATION} ${IOS_OPENGLES} ${IOS_FOUNDATION} ${IOS_QUARTZCORE} ${IOS_CORETEXT} ${IOS_COREGRAPHICS} ${IOS_SECURITY} ${IOS_SYSTEMCONFIGURATION} ${IOS_MOBILECORESERVICES} ${IOS_AUDIOTOOLBOX}) + TARGET_LINK_LIBRARIES(AusweisApp ${IOS_ASSETSLIBRARY} ${IOS_UIKIT} ${IOS_COREBLUETOOTH} ${IOS_COREFOUNDATION} ${IOS_OPENGLES} ${IOS_FOUNDATION} ${IOS_QUARTZCORE} ${IOS_CORETEXT} ${IOS_COREGRAPHICS} ${IOS_SECURITY} ${IOS_SYSTEMCONFIGURATION} ${IOS_MOBILECORESERVICES} ${IOS_AUDIOTOOLBOX} ${IOS_IMAGEIO}) TARGET_LINK_LIBRARIES(AusweisApp -Wl,-e,_qt_main_wrapper) @@ -164,17 +159,10 @@ IF(IOS) TARGET_LINK_LIBRARIES(AusweisApp -L${QT_HOST_PREFIX}/qml/QtGraphicalEffects/private -lqtgraphicaleffectsprivate) TARGET_LINK_LIBRARIES(AusweisApp -L${QT_HOST_PREFIX}/qml/QtGraphicalEffects -lqtgraphicaleffectsplugin) - TARGET_LINK_LIBRARIES(AusweisApp -L${QT_HOST_PREFIX}/qml/QtQuick/Controls -lqtquickcontrolsplugin) - TARGET_LINK_LIBRARIES(AusweisApp -L${QT_HOST_PREFIX}/qml/QtQuick/Controls/Styles/Flat -lqtquickextrasflatplugin) - TARGET_LINK_LIBRARIES(AusweisApp -L${QT_HOST_PREFIX}/qml/QtQuick/Controls.2 -lqtquickcontrols2plugin) # TARGET_LINK_LIBRARIES(AusweisApp -L${QT_HOST_PREFIX}/qml/QtQuick/Controls.2/Material -lqtquickcontrols2materialstyleplugin) # TARGET_LINK_LIBRARIES(AusweisApp -L${QT_HOST_PREFIX}/qml/QtQuick/Controls.2/Universal -lqtquickcontrols2universalstyleplugin) - TARGET_LINK_LIBRARIES(AusweisApp -L${QT_HOST_PREFIX}/qml/QtQuick/Dialogs -ldialogplugin) -# TARGET_LINK_LIBRARIES(AusweisApp -L${QT_HOST_PREFIX}/qml/QtQuick/Dialogs/Private -ldialogsprivateplugin) - -# TARGET_LINK_LIBRARIES(AusweisApp -L${QT_HOST_PREFIX}/qml/QtQuick/Extras -lqtquickextrasplugin) TARGET_LINK_LIBRARIES(AusweisApp -L${QT_HOST_PREFIX}/qml/QtQuick/Layouts -lqquicklayoutsplugin) # TARGET_LINK_LIBRARIES(AusweisApp -L${QT_HOST_PREFIX}/qml/QtQuick/LocalStorage -lqmllocalstorageplugin) # TARGET_LINK_LIBRARIES(AusweisApp -L${QT_HOST_PREFIX}/qml/QtQuick/Particles.2 -lparticlesplugin) @@ -203,14 +191,25 @@ ENDIF() IF(MAC) TARGET_LINK_LIBRARIES(AusweisApp ${OSX_APPKIT}) + + SET(AUTOSTART_HELPER_NAME AutostartHelper) + SET(FULL_HELPER_NAME ${PROJECT_NAME}${AUTOSTART_HELPER_NAME}) + SET(HELPER_SOURCES autostart_helper/main.mm) + ADD_EXECUTABLE(${FULL_HELPER_NAME} MACOSX_BUNDLE ${HELPER_SOURCES}) + TARGET_LINK_LIBRARIES(${FULL_HELPER_NAME} ${OSX_APPKIT}) + CONFIGURE_FILE(${RESOURCES_DIR}/packaging/macos/autostart_helper/Info.plist ${CMAKE_CURRENT_BINARY_DIR}/AutostartHelperInfo.plist @ONLY) + SET_TARGET_PROPERTIES(${FULL_HELPER_NAME} PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_BINARY_DIR}/AutostartHelperInfo.plist) ENDIF() IF(ANDROID) TARGET_LINK_LIBRARIES(AusweisApp Qt5::AndroidExtras) - TARGET_LINK_LIBRARIES(AusweisApp AusweisAppCardNfc AusweisAppActivationIntent) + TARGET_LINK_LIBRARIES(AusweisApp AusweisAppCardNfc) + IF(NOT ANDROID_BUILD_AAR) + TARGET_LINK_LIBRARIES(AusweisApp AusweisAppActivationIntent) + ENDIF() ENDIF() -IF(ANDROID OR IOS) +IF((ANDROID AND NOT ANDROID_BUILD_AAR) OR IOS) TARGET_LINK_LIBRARIES(AusweisApp AusweisAppCardBluetooth) ELSEIF(LINUX) TARGET_LINK_LIBRARIES(AusweisApp debug AusweisAppCardBluetooth) @@ -220,25 +219,22 @@ IF(WIN32) TARGET_LINK_LIBRARIES(AusweisApp ${WIN_DEFAULT_LIBS}) ENDIF() +IF(TARGET Qt5::Qml) + TARGET_LINK_LIBRARIES(AusweisApp AusweisAppUiQml) +ENDIF() + IF(IOS OR ANDROID OR WINDOWS_STORE) - TARGET_LINK_LIBRARIES(AusweisApp AusweisAppQml) - TARGET_LINK_LIBRARIES(AusweisApp AusweisAppAidl) + TARGET_LINK_LIBRARIES(AusweisApp AusweisAppUiAidl) ELSE() - TARGET_LINK_LIBRARIES(AusweisApp debug AusweisAppQml) - TARGET_LINK_LIBRARIES(AusweisApp debug AusweisAppAidl) + TARGET_LINK_LIBRARIES(AusweisApp debug AusweisAppUiAidl) ENDIF() IF(DESKTOP) TARGET_LINK_LIBRARIES(AusweisApp AusweisAppCardPcsc AusweisAppCardDrivers AusweisAppActivationWebservice) - TARGET_LINK_LIBRARIES(AusweisApp AusweisAppWidget AusweisAppCli) -ENDIF() - -TARGET_LINK_LIBRARIES(AusweisApp debug AusweisAppWebSocket) - -IF(NOT WINDOWS_STORE) - TARGET_LINK_LIBRARIES(AusweisApp AusweisAppCardRemote) + TARGET_LINK_LIBRARIES(AusweisApp AusweisAppUiWidget AusweisAppUiCli) ENDIF() +TARGET_LINK_LIBRARIES(AusweisApp AusweisAppUiWebsocket) INCLUDE(Install) INCLUDE(FeatureSummary) diff --git a/src/CommandLineParser.cpp b/src/CommandLineParser.cpp index e911048..f007d4c 100644 --- a/src/CommandLineParser.cpp +++ b/src/CommandLineParser.cpp @@ -4,75 +4,40 @@ #include "CommandLineParser.h" +#include "controller/AppController.h" +#include "DatagramHandlerImpl.h" #include "LogHandler.h" #include "NetworkManager.h" +#include "PortFile.h" #include "SingletonHelper.h" -#include "view/UILoader.h" +#include "UILoader.h" #if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) #include "HttpServer.h" - -#ifndef Q_OS_WINRT - #include "GuiProfile.h" -#endif - -#endif - -#ifndef QT_NO_DEBUG -#include "UIPlugInWebSocket.h" #endif #include -#include using namespace governikus; + defineSingleton(CommandLineParser) -Q_DECLARE_LOGGING_CATEGORY(cmdline) - -namespace -{ -QString getPrefixUi() -{ - return QStringLiteral("UIPlugIn"); -} - - -QString defaultUi(const QVector& pPlugins) -{ - QStringList list; - for (auto entry : pPlugins) - { - list << QString(getEnumName(entry)).remove(getPrefixUi()); - } - return list.join(QLatin1Char(',')); -} - - -} CommandLineParser::CommandLineParser() : mParser() , mOptionKeepLog(QStringLiteral("keep"), QStringLiteral("Keep log file.")) + , mOptionNoLogFile(QStringLiteral("no-logfile"), QStringLiteral("Disable log file.")) , mOptionShowWindow(QStringLiteral("show"), QStringLiteral("Show window on startup.")) , mOptionProxy(QStringLiteral("no-proxy"), QStringLiteral("Disable system proxy.")) - , mOptionUi(QStringLiteral("ui"), QStringLiteral("Use given UI plugin."), defaultUi(UILoader::getInstance().getDefault())) - , mOptionPort(QStringLiteral("port"), QStringLiteral("Use listening port."), QStringLiteral("24727")) -#ifndef QT_NO_DEBUG - , mOptionPortWebSocket(QStringLiteral("port-websocket"), QStringLiteral("Use listening port for websocket."), QString::number(UIPlugInWebSocket::WEBSOCKET_DEFAULT_PORT)) -#endif + , mOptionUi(QStringLiteral("ui"), QStringLiteral("Use given UI plugin."), UILoader::getInstance().getDefault().join(QLatin1Char(','))) + , mOptionPort(QStringLiteral("port"), QStringLiteral("Use listening port."), QString::number(PortFile::cDefaultPort)) { addOptions(); } -CommandLineParser::~CommandLineParser() -{ -} - - CommandLineParser& CommandLineParser::getInstance() { return *Instance; @@ -85,6 +50,7 @@ void CommandLineParser::addOptions() mParser.addVersionOption(); mParser.addOption(mOptionKeepLog); + mParser.addOption(mOptionNoLogFile); #if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(Q_OS_WINRT) mParser.addOption(mOptionShowWindow); @@ -93,10 +59,6 @@ void CommandLineParser::addOptions() mParser.addOption(mOptionProxy); mParser.addOption(mOptionUi); mParser.addOption(mOptionPort); - -#ifndef QT_NO_DEBUG - mParser.addOption(mOptionPortWebSocket); -#endif } @@ -104,22 +66,27 @@ void CommandLineParser::parse(QCoreApplication* pApp) { if (!pApp) { - qCWarning(cmdline) << "QCoreApplication is undefined"; return; } mParser.process(*pApp); parseUiPlugin(); + const auto& logHandler = Env::getSingleton(); if (mParser.isSet(mOptionKeepLog)) { - LogHandler::getInstance().setAutoRemove(false); + logHandler->setAutoRemove(false); + } + + if (mParser.isSet(mOptionNoLogFile)) + { + logHandler->setLogfile(false); } #if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(Q_OS_WINRT) if (mParser.isSet(mOptionShowWindow)) { - GuiProfile::getProfile().setShowWindow(true); + AppController::cShowUi = true; } #endif @@ -128,37 +95,19 @@ void CommandLineParser::parse(QCoreApplication* pApp) NetworkManager::lockProxy(true); } -#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) if (mParser.isSet(mOptionPort)) { bool converted = false; const uint port = mParser.value(mOptionPort).toUInt(&converted); - if (converted && port < USHRT_MAX) + if (converted && port <= std::numeric_limits::max()) { - HttpServer::cPort = static_cast(port); - } - else - { - qCWarning(cmdline) << "Cannot use value as port:" << mParser.value(mOptionPort); - } - } -#endif + DatagramHandlerImpl::cPort = static_cast(port); -#ifndef QT_NO_DEBUG - if (mParser.isSet(mOptionPortWebSocket)) - { - bool converted = false; - const uint port = mParser.value(mOptionPortWebSocket).toUInt(&converted); - if (converted && port < USHRT_MAX) - { - UIPlugInWebSocket::setPort(static_cast(port)); - } - else - { - qCWarning(cmdline) << "Cannot use value as websocket port:" << mParser.value(mOptionPortWebSocket); +#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) + HttpServer::cPort = DatagramHandlerImpl::cPort; +#endif } } -#endif } @@ -166,24 +115,6 @@ void CommandLineParser::parseUiPlugin() { if (mParser.isSet(mOptionUi)) { - QVector selectedPlugins; - const auto& availablePlugins = Enum::getList(); - const auto& requestedUis = mParser.values(mOptionUi); - - for (const auto& parsedUiOption : requestedUis) - { - for (auto availablePluginEntry : availablePlugins) - { - if (parsedUiOption.compare(QString(getEnumName(availablePluginEntry)).remove(getPrefixUi()), Qt::CaseInsensitive) == 0) - { - selectedPlugins << availablePluginEntry; - } - } - } - - if (!selectedPlugins.isEmpty()) - { - UILoader::getInstance().setDefault(selectedPlugins); - } + UILoader::getInstance().setDefault(mParser.values(mOptionUi)); } } diff --git a/src/CommandLineParser.h b/src/CommandLineParser.h index 4a8d65e..276d520 100644 --- a/src/CommandLineParser.h +++ b/src/CommandLineParser.h @@ -17,13 +17,11 @@ class CommandLineParser private: QCommandLineParser mParser; const QCommandLineOption mOptionKeepLog; + const QCommandLineOption mOptionNoLogFile; const QCommandLineOption mOptionShowWindow; const QCommandLineOption mOptionProxy; const QCommandLineOption mOptionUi; const QCommandLineOption mOptionPort; -#ifndef QT_NO_DEBUG - const QCommandLineOption mOptionPortWebSocket; -#endif Q_DISABLE_COPY(CommandLineParser) @@ -32,7 +30,7 @@ class CommandLineParser protected: CommandLineParser(); - ~CommandLineParser(); + ~CommandLineParser() = default; public: static CommandLineParser& getInstance(); @@ -41,4 +39,4 @@ class CommandLineParser }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/activation/CMakeLists.txt b/src/activation/CMakeLists.txt index 422fa2e..6c9bb72 100644 --- a/src/activation/CMakeLists.txt +++ b/src/activation/CMakeLists.txt @@ -1,15 +1,12 @@ +##################################################################### +# The module activation is responsible to enable different kinds of +# eID activation. This interface will be implemented by plugins +# for the specific platform. +##################################################################### + ADD_SUBDIRECTORY(base) -IF(IOS) - ADD_SUBDIRECTORY(customscheme) -ENDIF() - -IF(ANDROID) - ADD_SUBDIRECTORY(intent) -ENDIF() - -IF(DESKTOP) - ADD_SUBDIRECTORY(webservice) -ENDIF() - +ADD_SUBDIRECTORY(customscheme) +ADD_SUBDIRECTORY(intent) +ADD_SUBDIRECTORY(webservice) ADD_SUBDIRECTORY(internal) diff --git a/src/activation/base/ActivationContext.cpp b/src/activation/base/ActivationContext.cpp index fdd3587..080d701 100644 --- a/src/activation/base/ActivationContext.cpp +++ b/src/activation/base/ActivationContext.cpp @@ -11,8 +11,3 @@ ActivationContext::ActivationContext() : mSendError() { } - - -ActivationContext::~ActivationContext() -{ -} diff --git a/src/activation/base/ActivationContext.h b/src/activation/base/ActivationContext.h index 88dbb0a..ef458af 100644 --- a/src/activation/base/ActivationContext.h +++ b/src/activation/base/ActivationContext.h @@ -5,8 +5,8 @@ #pragma once #include "GlobalStatus.h" -#include "HttpStatusCode.h" +#include #include #include #include @@ -14,7 +14,6 @@ namespace governikus { - class ActivationContext : public QObject { @@ -25,7 +24,7 @@ class ActivationContext public: ActivationContext(); - virtual ~ActivationContext(); + virtual ~ActivationContext() = default; virtual QUrl getActivationURL() const = 0; @@ -48,7 +47,7 @@ class ActivationContext * * \return true, if sending succeeded, false otherwise. On failure an error message can be retrieved via getSendError. */ - virtual bool sendErrorPage(HttpStatusCode pStatusCode, const GlobalStatus& pStatus) = 0; + virtual bool sendErrorPage(http_status pStatusCode, const GlobalStatus& pStatus) = 0; /*! * Sends a redirect to the caller. @@ -68,4 +67,4 @@ class ActivationContext }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/activation/base/ActivationHandler.cpp b/src/activation/base/ActivationHandler.cpp index 22f433e..12f804b 100644 --- a/src/activation/base/ActivationHandler.cpp +++ b/src/activation/base/ActivationHandler.cpp @@ -22,16 +22,6 @@ static Initializer::Entry X([] { }); -ActivationHandler::ActivationHandler() -{ -} - - -ActivationHandler::~ActivationHandler() -{ -} - - QMap ActivationHandler::getQueryParameter(const QUrl& pUrl) { QMap map; diff --git a/src/activation/base/ActivationHandler.h b/src/activation/base/ActivationHandler.h index a5798bc..a02bdc5 100644 --- a/src/activation/base/ActivationHandler.h +++ b/src/activation/base/ActivationHandler.h @@ -5,6 +5,7 @@ #pragma once #include "EnumHelper.h" +#include "UIPlugIn.h" #include #include @@ -14,18 +15,6 @@ namespace governikus class ActivationContext; -/*! - * UI modules that can be requested to show. - */ -defineEnumType(UiModule, - CURRENT, - DEFAULT, - IDENTIFY, - SETTINGS, - PINMANAGEMENT - ) - - /*! * Format types for status responses */ @@ -51,8 +40,8 @@ class ActivationHandler static bool isPlugIn(const QJsonObject& pJson); protected: - ActivationHandler(); - virtual ~ActivationHandler(); + ActivationHandler() = default; + virtual ~ActivationHandler() = default; /*! * \brief Get the query items with lower-case keys, so we can support case-insensitive keys. @@ -86,6 +75,6 @@ class ActivationHandler void fireAuthenticationRequest(const QSharedPointer& pActivationContext); }; -} /* namespace governikus */ +} // namespace governikus Q_DECLARE_INTERFACE(governikus::ActivationHandler, "governikus.ActivationHandler") diff --git a/src/activation/base/CMakeLists.txt b/src/activation/base/CMakeLists.txt index 03c5871..4cd4da5 100644 --- a/src/activation/base/CMakeLists.txt +++ b/src/activation/base/CMakeLists.txt @@ -1,3 +1,3 @@ ADD_PLATFORM_LIBRARY(AusweisAppActivation) -TARGET_LINK_LIBRARIES(AusweisAppActivation Qt5::Core AusweisAppGlobal AusweisAppNetwork) +TARGET_LINK_LIBRARIES(AusweisAppActivation Qt5::Core AusweisAppExternal::HttpParser AusweisAppGlobal AusweisAppNetwork AusweisAppUi) diff --git a/src/activation/customscheme/CMakeLists.txt b/src/activation/customscheme/CMakeLists.txt index 5c90306..c3741bb 100644 --- a/src/activation/customscheme/CMakeLists.txt +++ b/src/activation/customscheme/CMakeLists.txt @@ -1,4 +1,8 @@ +##################################################################### +# The activation plugin for custom scheme eid:// on iOS. +##################################################################### + ADD_PLATFORM_LIBRARY(AusweisAppActivationCustomScheme) -TARGET_LINK_LIBRARIES(AusweisAppActivationCustomScheme Qt5::Core Qt5::Gui AusweisAppGlobal AusweisAppNetwork AusweisAppActivation) +TARGET_LINK_LIBRARIES(AusweisAppActivationCustomScheme Qt5::Core Qt5::Gui AusweisAppGlobal AusweisAppActivation) TARGET_COMPILE_DEFINITIONS(AusweisAppActivationCustomScheme PRIVATE QT_STATICPLUGIN) diff --git a/src/activation/customscheme/CustomSchemeActivationContext.cpp b/src/activation/customscheme/CustomSchemeActivationContext.cpp index 1923bf6..53c7edb 100644 --- a/src/activation/customscheme/CustomSchemeActivationContext.cpp +++ b/src/activation/customscheme/CustomSchemeActivationContext.cpp @@ -58,7 +58,7 @@ bool CustomSchemeActivationContext::sendOperationAlreadyActive() } -bool CustomSchemeActivationContext::sendErrorPage(HttpStatusCode, const GlobalStatus&) +bool CustomSchemeActivationContext::sendErrorPage(http_status, const GlobalStatus&) { // The error is displayed in the application, // so here is nothing to be done in this case. diff --git a/src/activation/customscheme/CustomSchemeActivationContext.h b/src/activation/customscheme/CustomSchemeActivationContext.h index a370b6a..077fccf 100644 --- a/src/activation/customscheme/CustomSchemeActivationContext.h +++ b/src/activation/customscheme/CustomSchemeActivationContext.h @@ -26,11 +26,11 @@ class CustomSchemeActivationContext bool sendProcessing() override; bool sendOperationAlreadyActive() override; - bool sendErrorPage(HttpStatusCode pStatusCode, const GlobalStatus& pStatus) override; + bool sendErrorPage(http_status pStatusCode, const GlobalStatus& pStatus) override; bool sendRedirect(const QUrl& pRedirectAddress, const GlobalStatus& pResult) override; Q_SIGNALS: void fireShowUserInformation(const QString& pMessage); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/activation/customscheme/CustomSchemeActivationHandler.cpp b/src/activation/customscheme/CustomSchemeActivationHandler.cpp index 15cb556..5f73e2c 100644 --- a/src/activation/customscheme/CustomSchemeActivationHandler.cpp +++ b/src/activation/customscheme/CustomSchemeActivationHandler.cpp @@ -13,15 +13,6 @@ using namespace governikus; Q_DECLARE_LOGGING_CATEGORY(activation) -CustomSchemeActivationHandler::CustomSchemeActivationHandler() -{ -} - - -CustomSchemeActivationHandler::~CustomSchemeActivationHandler() -{ -} - bool CustomSchemeActivationHandler::start() { @@ -50,7 +41,7 @@ void CustomSchemeActivationHandler::onCustomUrl(const QUrl& pUrl) } qCDebug(activation) << "Request type: authentication"; - QSharedPointer context(new CustomSchemeActivationContext(pUrl)); + const auto& context = QSharedPointer::create(pUrl); connect(context.data(), &CustomSchemeActivationContext::fireShowUserInformation, this, &ActivationHandler::fireShowUserInformation); Q_EMIT fireAuthenticationRequest(context); } diff --git a/src/activation/customscheme/CustomSchemeActivationHandler.h b/src/activation/customscheme/CustomSchemeActivationHandler.h index 390bb80..ee128a7 100644 --- a/src/activation/customscheme/CustomSchemeActivationHandler.h +++ b/src/activation/customscheme/CustomSchemeActivationHandler.h @@ -25,11 +25,11 @@ class CustomSchemeActivationHandler void onCustomUrl(const QUrl& pUrl); public: - CustomSchemeActivationHandler(); - virtual ~CustomSchemeActivationHandler() override; + CustomSchemeActivationHandler() = default; + virtual ~CustomSchemeActivationHandler() override = default; virtual bool start() override; virtual void stop() override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/activation/intent/AusweisApp2Service.java b/src/activation/intent/AusweisApp2Service.java index af681f0..dd8203d 100644 --- a/src/activation/intent/AusweisApp2Service.java +++ b/src/activation/intent/AusweisApp2Service.java @@ -44,9 +44,6 @@ public class AusweisApp2Service { Log.d(LOG_TAG, "Android service created."); super.onCreate(); - - // register the broadcast receiver after loading the C++ library in super.onCreate() - AndroidBluetoothReceiver.register(this); } @@ -55,9 +52,6 @@ public class AusweisApp2Service { Log.d(LOG_TAG, "Android service destroyed."); - // unregister the broadcast receiver before unloading the C++ library in super.onDestroy() - AndroidBluetoothReceiver.unregister(this); - super.onDestroy(); // Workaround. When bound & unbound the QtService is in a funny state causing diff --git a/src/activation/intent/CMakeLists.txt b/src/activation/intent/CMakeLists.txt index 0988902..3a58e07 100644 --- a/src/activation/intent/CMakeLists.txt +++ b/src/activation/intent/CMakeLists.txt @@ -1,4 +1,8 @@ +##################################################################### +# The activation plugin for Android's intent mechanism. +##################################################################### + ADD_PLATFORM_LIBRARY(AusweisAppActivationIntent) -TARGET_LINK_LIBRARIES(AusweisAppActivationIntent Qt5::Core Qt5::Gui AusweisAppGlobal AusweisAppNetwork AusweisAppActivation) +TARGET_LINK_LIBRARIES(AusweisAppActivationIntent Qt5::Core Qt5::Gui AusweisAppGlobal AusweisAppActivation) TARGET_COMPILE_DEFINITIONS(AusweisAppActivationIntent PRIVATE QT_STATICPLUGIN) diff --git a/src/activation/intent/IntentActivationContext.cpp b/src/activation/intent/IntentActivationContext.cpp index 5e80d32..b7b9e03 100644 --- a/src/activation/intent/IntentActivationContext.cpp +++ b/src/activation/intent/IntentActivationContext.cpp @@ -58,7 +58,7 @@ bool IntentActivationContext::sendOperationAlreadyActive() } -bool IntentActivationContext::sendErrorPage(HttpStatusCode, const GlobalStatus&) +bool IntentActivationContext::sendErrorPage(http_status, const GlobalStatus&) { // the error is displayed in the application, // so here is nothing to be done in this case diff --git a/src/activation/intent/IntentActivationContext.h b/src/activation/intent/IntentActivationContext.h index a63f5e2..e376a61 100644 --- a/src/activation/intent/IntentActivationContext.h +++ b/src/activation/intent/IntentActivationContext.h @@ -29,11 +29,11 @@ class IntentActivationContext bool sendProcessing() override; bool sendOperationAlreadyActive() override; - bool sendErrorPage(HttpStatusCode pStatusCode, const GlobalStatus& pStatus) override; - bool sendRedirect(const QUrl& pRedirectAddress, const GlobalStatus& pResult) override; + bool sendErrorPage(http_status pStatusCode, const GlobalStatus& pStatus) override; + bool sendRedirect(const QUrl& pRedirectAddress, const GlobalStatus& pStatus) override; Q_SIGNALS: void fireShowUserInformation(const QString& pMessage); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/activation/intent/IntentActivationHandler.cpp b/src/activation/intent/IntentActivationHandler.cpp index ddbffa0..3a063da 100644 --- a/src/activation/intent/IntentActivationHandler.cpp +++ b/src/activation/intent/IntentActivationHandler.cpp @@ -43,7 +43,7 @@ void IntentActivationHandler::onIntent(const QUrl& pUrl) { qCDebug(activation) << "Got new authentication request"; qCDebug(activation) << "Request URL:" << pUrl; - QSharedPointer context(new IntentActivationContext(pUrl)); + const auto& context = QSharedPointer::create(pUrl); connect(context.data(), &IntentActivationContext::fireShowUserInformation, this, &ActivationHandler::fireShowUserInformation); Q_EMIT fireAuthenticationRequest(context); } diff --git a/src/activation/intent/IntentActivationHandler.h b/src/activation/intent/IntentActivationHandler.h index c5160f3..0883a71 100644 --- a/src/activation/intent/IntentActivationHandler.h +++ b/src/activation/intent/IntentActivationHandler.h @@ -46,4 +46,4 @@ class IntentActivationHandler virtual void stop() override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/activation/intent/MainActivity.java b/src/activation/intent/MainActivity.java index aea91b8..d5644cd 100644 --- a/src/activation/intent/MainActivity.java +++ b/src/activation/intent/MainActivity.java @@ -4,8 +4,6 @@ package com.governikus.ausweisapp2; -import org.qtproject.qt5.android.bindings.QtActivity; - import android.app.Activity; import android.app.PendingIntent; import android.content.Context; @@ -19,6 +17,7 @@ import android.os.Bundle; import android.util.Log; import android.view.WindowManager; +import org.qtproject.qt5.android.bindings.QtActivity; public class MainActivity extends QtActivity { @@ -87,6 +86,16 @@ public class MainActivity extends QtActivity } + public static boolean isStartedByAuth() + { + if (cIntent == null) + { + return false; + } + return cIntent.getAction().equals("android.intent.action.VIEW"); + } + + @Override public void onCreate(Bundle savedInstanceState) { diff --git a/src/activation/internal/CMakeLists.txt b/src/activation/internal/CMakeLists.txt index f387218..084bff0 100644 --- a/src/activation/internal/CMakeLists.txt +++ b/src/activation/internal/CMakeLists.txt @@ -1,3 +1,11 @@ +##################################################################### +# The activation plugin for internal usage. +# +# If another module needs to start an authentication it can be +# invoked by the plugin. +# This will be used by interal usage of the SDK. +##################################################################### + ADD_PLATFORM_LIBRARY(AusweisAppActivationInternal) TARGET_LINK_LIBRARIES(AusweisAppActivationInternal Qt5::Core AusweisAppGlobal AusweisAppActivation) diff --git a/src/activation/internal/InternalActivationContext.cpp b/src/activation/internal/InternalActivationContext.cpp index b51b43f..cd999b2 100644 --- a/src/activation/internal/InternalActivationContext.cpp +++ b/src/activation/internal/InternalActivationContext.cpp @@ -18,11 +18,6 @@ InternalActivationContext::InternalActivationContext(const QUrl& pUrl) } -InternalActivationContext::~InternalActivationContext() -{ -} - - QUrl InternalActivationContext::getActivationURL() const { return mTcTokenUrl; @@ -41,7 +36,7 @@ bool InternalActivationContext::sendOperationAlreadyActive() } -bool InternalActivationContext::sendErrorPage(HttpStatusCode pStatusCode, const GlobalStatus& pStatus) +bool InternalActivationContext::sendErrorPage(http_status pStatusCode, const GlobalStatus& pStatus) { Q_UNUSED(pStatusCode) Q_UNUSED(pStatus) diff --git a/src/activation/internal/InternalActivationContext.h b/src/activation/internal/InternalActivationContext.h index 210ad70..e741bf3 100644 --- a/src/activation/internal/InternalActivationContext.h +++ b/src/activation/internal/InternalActivationContext.h @@ -19,13 +19,13 @@ class InternalActivationContext public: InternalActivationContext(const QUrl& pUrl); - virtual ~InternalActivationContext() override; + virtual ~InternalActivationContext() override = default; QUrl getActivationURL() const override; bool sendProcessing() override; bool sendOperationAlreadyActive() override; - bool sendErrorPage(HttpStatusCode pStatusCode, const GlobalStatus& pStatus) override; + bool sendErrorPage(http_status pStatusCode, const GlobalStatus& pStatus) override; bool sendRedirect(const QUrl& pRedirectAddress, const GlobalStatus& pStatus) override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/activation/internal/InternalActivationHandler.h b/src/activation/internal/InternalActivationHandler.h index 798c9c9..c50fb08 100644 --- a/src/activation/internal/InternalActivationHandler.h +++ b/src/activation/internal/InternalActivationHandler.h @@ -29,4 +29,4 @@ class InternalActivationHandler void runAuthentication(const QSharedPointer& pContext); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/activation/webservice/CMakeLists.txt b/src/activation/webservice/CMakeLists.txt index f5a42df..a7a8102 100644 --- a/src/activation/webservice/CMakeLists.txt +++ b/src/activation/webservice/CMakeLists.txt @@ -1,3 +1,7 @@ +##################################################################### +# The activation plugin with a local HTTP-WebServer. +##################################################################### + ADD_PLATFORM_LIBRARY(AusweisAppActivationWebservice) TARGET_LINK_LIBRARIES(AusweisAppActivationWebservice Qt5::Core AusweisAppGlobal AusweisAppNetwork AusweisAppActivation) diff --git a/src/activation/webservice/Template.h b/src/activation/webservice/Template.h index 5fd4c1b..800dcc2 100644 --- a/src/activation/webservice/Template.h +++ b/src/activation/webservice/Template.h @@ -55,4 +55,4 @@ class Template QString render() const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/activation/webservice/WebserviceActivationContext.cpp b/src/activation/webservice/WebserviceActivationContext.cpp index b2878d6..3343cdb 100644 --- a/src/activation/webservice/WebserviceActivationContext.cpp +++ b/src/activation/webservice/WebserviceActivationContext.cpp @@ -31,11 +31,6 @@ WebserviceActivationContext::WebserviceActivationContext(const QSharedPointergetUrl(); @@ -50,7 +45,7 @@ bool WebserviceActivationContext::sendProcessing() return false; } - mRequest->send(HttpStatusCode::PROCESSING); + mRequest->send(HTTP_STATUS_PROCESSING); return true; } @@ -73,14 +68,14 @@ bool WebserviceActivationContext::sendOperationAlreadyActive() QByteArray htmlPage = htmlTemplate.render().toUtf8(); HttpResponse response; - response.setStatus(HttpStatusCode::NOT_FOUND); + response.setStatus(HTTP_STATUS_CONFLICT); response.setBody(htmlPage, QByteArrayLiteral("text/html; charset=utf-8")); mRequest->send(response); return true; } -bool WebserviceActivationContext::sendErrorPage(HttpStatusCode pStatusCode, const GlobalStatus& pStatus) +bool WebserviceActivationContext::sendErrorPage(http_status pStatusCode, const GlobalStatus& pStatus) { if (!mRequest->isConnected()) { @@ -88,14 +83,14 @@ bool WebserviceActivationContext::sendErrorPage(HttpStatusCode pStatusCode, cons return false; } - qCDebug(activation) << "Send error page to browser, error code " << pStatusCode; - Q_ASSERT(pStatusCode == HttpStatusCode::BAD_REQUEST || pStatusCode == HttpStatusCode::NOT_FOUND); + qCDebug(activation) << "Send error page to browser, error code" << pStatusCode; + Q_ASSERT(pStatusCode == HTTP_STATUS_BAD_REQUEST || pStatusCode == HTTP_STATUS_NOT_FOUND); QString statusCodeString; - if (pStatusCode == HttpStatusCode::BAD_REQUEST) + if (pStatusCode == HTTP_STATUS_BAD_REQUEST) { statusCodeString = tr("400 Bad Request"); } - else if (pStatusCode == HttpStatusCode::NOT_FOUND) + else if (pStatusCode == HTTP_STATUS_NOT_FOUND) { statusCodeString = tr("404 Not found"); } @@ -127,8 +122,8 @@ bool WebserviceActivationContext::sendRedirect(const QUrl& pRedirectAddress, con if (!mRequest->isConnected()) { - mSendError = tr("The connection to the browser was lost. No forwarding was executed. Please try to" - " call the URL again manually: %2").arg(redirectAddressWithResult.toString(), redirectAddressWithResult.host()); + const auto& url = QStringLiteral("%2").arg(redirectAddressWithResult.toString(), redirectAddressWithResult.host()); + mSendError = tr("The connection to the browser was lost. No forwarding was executed. Please try to call the URL again manually: %1").arg(url); return false; } @@ -136,7 +131,7 @@ bool WebserviceActivationContext::sendRedirect(const QUrl& pRedirectAddress, con HttpResponse response; setCommonHeaders(response); response.setHeader(QByteArrayLiteral("Location"), redirectAddressWithResult.toEncoded()); - response.setStatus(HttpStatusCode::SEE_OTHER); + response.setStatus(HTTP_STATUS_SEE_OTHER); mRequest->send(response); return true; } diff --git a/src/activation/webservice/WebserviceActivationContext.h b/src/activation/webservice/WebserviceActivationContext.h index 6f45f9d..5a0fd0a 100644 --- a/src/activation/webservice/WebserviceActivationContext.h +++ b/src/activation/webservice/WebserviceActivationContext.h @@ -9,6 +9,8 @@ #include +class test_WebserviceActivationContext; + namespace governikus { @@ -16,6 +18,7 @@ class WebserviceActivationContext : public ActivationContext { Q_OBJECT + friend class ::test_WebserviceActivationContext; const QSharedPointer mRequest; @@ -24,7 +27,7 @@ class WebserviceActivationContext public: WebserviceActivationContext(const QSharedPointer& pRequest); - virtual ~WebserviceActivationContext() override; + virtual ~WebserviceActivationContext() override = default; QUrl getActivationURL() const override; @@ -32,9 +35,9 @@ class WebserviceActivationContext bool sendOperationAlreadyActive() override; - bool sendErrorPage(HttpStatusCode pStatusCode, const GlobalStatus& pStatus) override; + bool sendErrorPage(http_status pStatusCode, const GlobalStatus& pStatus) override; bool sendRedirect(const QUrl& pRedirectAddress, const GlobalStatus& pStatus) override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/activation/webservice/WebserviceActivationHandler.cpp b/src/activation/webservice/WebserviceActivationHandler.cpp index 68e1ff2..e5360b2 100644 --- a/src/activation/webservice/WebserviceActivationHandler.cpp +++ b/src/activation/webservice/WebserviceActivationHandler.cpp @@ -48,8 +48,7 @@ bool WebserviceActivationHandler::start() return true; } - - const int port = HttpServer::cPort; + const quint16 port = HttpServer::cPort; HttpServerStatusParser parser(port); QString serverAppName = parser.request() ? parser.getVersionInfo().getName() : parser.getServerHeader(); if (serverAppName.startsWith(VersionInfo::getInstance().getName())) @@ -128,7 +127,7 @@ void WebserviceActivationHandler::onNewRequest(const QSharedPointer QByteArray htmlPage = htmlTemplate.render().toUtf8(); HttpResponse response; - response.setStatus(HttpStatusCode::NOT_FOUND); + response.setStatus(HTTP_STATUS_NOT_FOUND); response.setBody(htmlPage, QByteArrayLiteral("text/html; charset=utf-8")); pRequest->send(response); } @@ -136,7 +135,7 @@ void WebserviceActivationHandler::onNewRequest(const QSharedPointer void WebserviceActivationHandler::handleShowUiRequest(UiModule pUiModule, const QSharedPointer& pRequest) { - pRequest->send(HttpStatusCode::OK); + pRequest->send(HTTP_STATUS_OK); QString userAgent = QString::fromLatin1(pRequest->getHeader(QByteArrayLiteral("user-agent"))); if (userAgent.startsWith(QCoreApplication::applicationName())) @@ -169,14 +168,14 @@ void WebserviceActivationHandler::handleImageRequest(const QSharedPointer -#include - -namespace governikus -{ - -class PskManager -{ - private: - QByteArray mPsk; - bool mSecureRandomPsk; - QMutex mPskMutex; - - public: - static PskManager& getInstance(); - - PskManager(); - QByteArray generatePsk(); - QByteArray getPsk(); - bool isSecureRandomPsk(); -}; - -} diff --git a/src/aidl/UIPlugInAidl.cpp b/src/aidl/UIPlugInAidl.cpp deleted file mode 100644 index e23bc94..0000000 --- a/src/aidl/UIPlugInAidl.cpp +++ /dev/null @@ -1,211 +0,0 @@ -/*! - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "UIPlugInAidl.h" - -#include "view/UILoader.h" -#ifdef Q_OS_ANDROID -#include "PskManager.h" -#include -#endif - -#include -#include -#include -#include -#include - -#ifdef Q_OS_ANDROID -#include -#include -#include -#endif - - -Q_DECLARE_LOGGING_CATEGORY(aidl) - -using namespace governikus; - - -QAtomicPointer UIPlugInAidl::instance = nullptr; - -UIPlugInAidl::UIPlugInAidl() - : UIPlugIn() - , mJsonApi(nullptr) - , mContext() - , mWorkflowIsActive() - , mInitializationSuccessfull(false) -{ - if (UILoader::getInstance().load(UIPlugInName::UIPlugInJsonApi)) - { - mJsonApi = qobject_cast(UILoader::getInstance().getLoaded(UIPlugInName::UIPlugInJsonApi)); - Q_ASSERT(mJsonApi); - connect(mJsonApi, &UIPlugInJsonApi::fireMessage, this, &UIPlugInAidl::onToSend, Qt::QueuedConnection); - - mInitializationSuccessfull = true; - } - else - { - qWarning(aidl) << "Cannot start AIDL because JSON-API is missing"; - } - - instance = this; -} - - -UIPlugInAidl::~UIPlugInAidl() -{ -} - - -UIPlugInAidl* UIPlugInAidl::getInstance(bool pBlock) -{ - // The Java interface thread is ready before our core has booted. - // Hence we delay access to the UIPlugInAidl. - if (pBlock) - { - while (instance.load() == nullptr) - { - QThread::msleep(100); - } - } - - return instance.load(); -} - - -bool UIPlugInAidl::isSuccessfullInitialized() -{ - return mInitializationSuccessfull; -} - - -void UIPlugInAidl::onWorkflowStarted(QSharedPointer pContext) -{ - mWorkflowIsActive.lock(); - mContext = pContext; -} - - -void UIPlugInAidl::onWorkflowFinished(QSharedPointer pContext) -{ - Q_UNUSED(pContext); - - mContext.clear(); - mJsonApi->blockSignals(false); - mWorkflowIsActive.unlock(); -} - - -void UIPlugInAidl::onReceived(const QByteArray& pMessage) -{ - mJsonApi->doMessageProcessing(pMessage); -} - - -bool UIPlugInAidl::waitForWorkflowToFinish() -{ - const int fiveSeconds = 5000; - bool success = mWorkflowIsActive.tryLock(fiveSeconds); - if (success) - { - mWorkflowIsActive.unlock(); - } - return success; -} - - -void UIPlugInAidl::reset() -{ - if (mContext) - { - mJsonApi->blockSignals(true); - Q_EMIT mContext->fireCancelWorkflow(); - } -} - - -void UIPlugInAidl::onToSend(const QByteArray& pMessage) -{ -#ifdef Q_OS_ANDROID - const QString json = QString::fromUtf8(pMessage); - QAndroidJniObject jsonAndroidString = QAndroidJniObject::fromString(json); - - QAndroidJniObject aidlBinder = QtAndroid::androidService().callObjectMethod("getAidlBinder", "()Lcom/governikus/ausweisapp2/AidlBinder;"); - aidlBinder.callMethod("aidlReceive", "(Ljava/lang/String;)V", jsonAndroidString.object()); -#else - Q_UNUSED(pMessage); -#endif -} - - -void UIPlugInAidl::doShutdown() -{ -} - - -#ifdef Q_OS_ANDROID -extern "C" -{ - -// These functions need to be explicitly exported so that the JVM can bind to them. -// At the moment only the Q_Plugins seem to be appropriate locations. - -JNIEXPORT jstring JNICALL Java_com_governikus_ausweisapp2_AidlBinder_resetValidSessionID(JNIEnv* pEnv, jobject pObj) -{ - Q_UNUSED(pObj); - - UIPlugInAidl* plugin = UIPlugInAidl::getInstance(); - if (!plugin->isSuccessfullInitialized()) - { - qCCritical(aidl) << "Cannot call AIDL plugin"; - return pEnv->NewStringUTF(""); - } - QMetaObject::invokeMethod(plugin, "reset", Qt::QueuedConnection); - if (!plugin->waitForWorkflowToFinish()) - { - qCCritical(aidl) << "Cannot acquire workflow mutex"; - return pEnv->NewStringUTF(""); - } - - const auto& finalPsk = PskManager::getInstance().generatePsk(); - return pEnv->NewStringUTF(finalPsk.constData()); -} - - -JNIEXPORT jboolean JNICALL Java_com_governikus_ausweisapp2_AidlBinder_isSecureRandomPsk(JNIEnv* pEnv, jobject pObj) -{ - Q_UNUSED(pEnv); - Q_UNUSED(pObj); - - return PskManager::getInstance().isSecureRandomPsk(); -} - - -} - -extern "C" -{ - -JNIEXPORT void JNICALL Java_com_governikus_ausweisapp2_AidlBinder_aidlSend(JNIEnv* pEnv, jobject pObj, jstring pJson) -{ - Q_UNUSED(pObj); - - const char* nativeString = pEnv->GetStringUTFChars(pJson, 0); - const QString json = QString::fromUtf8(nativeString); - pEnv->ReleaseStringUTFChars(pJson, nativeString); - - UIPlugInAidl* plugin = UIPlugInAidl::getInstance(); - if (!plugin->isSuccessfullInitialized()) - { - qCritical(aidl) << "Cannot call AIDL plugin"; - return; - } - - QMetaObject::invokeMethod(plugin, "onReceived", Qt::QueuedConnection, Q_ARG(QByteArray, json.toUtf8())); -} - - -} -#endif diff --git a/src/aidl/UIPlugInAidl.h b/src/aidl/UIPlugInAidl.h deleted file mode 100644 index 47c813c..0000000 --- a/src/aidl/UIPlugInAidl.h +++ /dev/null @@ -1,51 +0,0 @@ -/*! - * \brief UIPlugIn implementation of the AIDL UI. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "UIPlugInJsonApi.h" -#include "view/UIPlugIn.h" - -#include -#include - -namespace governikus -{ - -class UIPlugInAidl - : public UIPlugIn -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID "governikus.UIPlugIn" FILE "metadata.json") - Q_INTERFACES(governikus::UIPlugIn) - - private: - UIPlugInJsonApi* mJsonApi; - QSharedPointer mContext; - QMutex mWorkflowIsActive; - - static QAtomicPointer instance; - bool mInitializationSuccessfull; - - public: - UIPlugInAidl(); - virtual ~UIPlugInAidl() override; - - static UIPlugInAidl* getInstance(bool pBlock = true); - bool isSuccessfullInitialized(); - Q_INVOKABLE void onReceived(const QByteArray& pMessage); - bool waitForWorkflowToFinish(); - - private Q_SLOTS: - void reset(); - virtual void doShutdown() override; - virtual void onWorkflowStarted(QSharedPointer pContext) override; - virtual void onWorkflowFinished(QSharedPointer pContext) override; - - void onToSend(const QByteArray& pMessage); -}; - -} /* namespace governikus */ diff --git a/src/autostart_helper/main.mm b/src/autostart_helper/main.mm new file mode 100644 index 0000000..cf49470 --- /dev/null +++ b/src/autostart_helper/main.mm @@ -0,0 +1,55 @@ +/* + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include +#include + +@interface AutostartDelegate + : NSObject +- (void) applicationWillFinishLaunching: (NSNotification*) pNotification; +@end + +@implementation AutostartDelegate +- (void) applicationWillFinishLaunching: (NSNotification*) pNotification +{ + (void) pNotification; + BOOL alreadyRunning = false; + + NSArray* runningApplications = [[NSWorkspace sharedWorkspace] runningApplications]; + for (NSRunningApplication* application in runningApplications) + { + if ([[application bundleIdentifier] isEqualToString:@"com.governikus.AusweisApp2"]) + { + NSLog(@"Application is already running."); + alreadyRunning = true; + } + } + + if (!alreadyRunning) + { + NSString* helperBundlePath = [[NSBundle mainBundle] bundlePath]; + NSArray* helperBundlePathComponents = [helperBundlePath pathComponents]; + // Remove last 4 components from helper path to get main path, current bundle is located at "main.app/Contents/Library/LoginItems/helper.app": + NSArray* mainBundleComponents = [helperBundlePathComponents subarrayWithRange:NSMakeRange(0, [helperBundlePathComponents count] - 4)]; + NSString* mainBundlePath = [NSString pathWithComponents:mainBundleComponents]; + NSLog(@"Launching application at: %@", mainBundlePath); + BOOL result = [[NSWorkspace sharedWorkspace] launchApplication:mainBundlePath]; + if (!result) + { + NSLog(@"Launching failed"); + } + } + [NSApp terminate:nil]; +} + + +@end + +int main(int argc, const char** argv) +{ + [NSApplication sharedApplication]; + [NSApp setDelegate: [AutostartDelegate new]]; + + return NSApplicationMain(argc, argv); +} diff --git a/src/card/CMakeLists.txt b/src/card/CMakeLists.txt index f26ece3..1ba6fb9 100644 --- a/src/card/CMakeLists.txt +++ b/src/card/CMakeLists.txt @@ -1,3 +1,14 @@ +##################################################################### +# The module card is responsible to handle all generic card commands +# (APDU). +# +# It provides an interface that can be implemented by plugins like +# PCSC or Android-NFC called ReaderManagerPlugIn. +# Also it provides a ReaderManager that can be used as a generic +# control interface. It handles access and commands to specific +# reader, cards and commands to the identity card. +##################################################################### + ADD_SUBDIRECTORY(base) IF(DESKTOP) @@ -8,9 +19,8 @@ IF(TARGET Qt5::Nfc) ADD_SUBDIRECTORY(nfc) ENDIF() -IF(ANDROID OR IOS OR WINDOWS_STORE OR "${CMAKE_BUILD_TYPE}" STREQUAL "DEBUG") +IF(TARGET Qt5::Bluetooth) ADD_SUBDIRECTORY(bluetooth) ENDIF() ADD_SUBDIRECTORY(drivers) -ADD_SUBDIRECTORY(remote) diff --git a/src/card/base/Apdu.cpp b/src/card/base/Apdu.cpp index 52b0dc0..ef1e0dd 100644 --- a/src/card/base/Apdu.cpp +++ b/src/card/base/Apdu.cpp @@ -4,29 +4,18 @@ #include "Apdu.h" -#include "Commands.h" - #include -#include - using namespace governikus; - Q_DECLARE_LOGGING_CATEGORY(card) - Apdu::Apdu(const QByteArray& pBuffer) : mBuffer(pBuffer) { } -Apdu::~Apdu() -{ -} - - int Apdu::length() const { return mBuffer.size(); @@ -37,337 +26,3 @@ const QByteArray& Apdu::getBuffer() const { return mBuffer; } - - -CommandApdu::CommandApdu(const QByteArray& pBuffer, bool pUpdateRetryCounter) - : Apdu(pBuffer) - , mUpdateRetryCounter(pUpdateRetryCounter) -{ -} - - -bool CommandApdu::isExtendedLength() const -{ - // no data, no le: size == 4 - // no data, short le: size == 5 - // no data, extended le: size == 7, high order le byte == 0 - // data, with/without le: high order size byte == 0 <=> extended data length - return length() > 5 && mBuffer.at(4) == '\0'; -} - - -bool CommandApdu::isExtendedLength(const QByteArray& pData, int pLe) -{ - return pData.size() > SHORT_MAX_LC || pLe > SHORT_MAX_LE; -} - - -bool CommandApdu::isSecureMessaging(const QByteArray& pCommandBuffer) -{ - return !pCommandBuffer.isEmpty() && ((pCommandBuffer.at(0) & 0x0F) == CLA_SECURE_MESSAGING); -} - - -CommandApdu::CommandApdu(const QByteArray& pHeader, const QByteArray& pData, int pLe) - : CommandApdu(pHeader.at(0), pHeader.at(1), pHeader.at(2), pHeader.at(3), pData, pLe) -{ -} - - -CommandApdu::CommandApdu(char pCla, char pIns, char pP1, char pP2, const QByteArray& pData, int pLe) - : Apdu(QByteArray()) - , mUpdateRetryCounter(false) -{ - if (pData.size() > EXTENDED_MAX_LC) - { - qCCritical(card) << "Command data exceeds maximum of 0xFFFF bytes"; - return; - } - if (pLe > EXTENDED_MAX_LE) - { - qCCritical(card) << "Expected length exceeds maximum value of 0x010000"; - return; - } - - mBuffer += pCla; - mBuffer += pIns; - mBuffer += pP1; - mBuffer += pP2; - - // - // according to ISO 7816 Part 4, chapter 5.1 - // - if (pData.size() > 0) // withPayload - { - if (CommandApdu::isExtendedLength(pData, pLe)) - { - mBuffer += '\0'; - mBuffer += static_cast(pData.size() >> 8 & 0xff); - mBuffer += static_cast(pData.size() & 0xff); - } - else - { - mBuffer += static_cast(pData.size() & 0xff); - } - - mBuffer += pData; - } - - if (pLe > 0) - { - if (CommandApdu::isExtendedLength(pData, pLe)) - { - if (pData.isEmpty()) - { - mBuffer += '\0'; - } - mBuffer += static_cast(pLe >> 8 & 0xff); - } - mBuffer += static_cast(pLe & 0xff); - } -} - - -CommandApdu::~CommandApdu() -{ -} - - -char CommandApdu::getCLA() const -{ - return length() > 0 ? mBuffer.at(0) : '\0'; -} - - -char CommandApdu::getINS() const -{ - return length() > 1 ? mBuffer.at(1) : '\0'; -} - - -char CommandApdu::getP1() const -{ - return length() > 2 ? mBuffer.at(2) : '\0'; -} - - -char CommandApdu::getP2() const -{ - return mBuffer.size() > 3 ? mBuffer.at(3) : '\0'; -} - - -static inline int readLength(const QByteArray& pByteArray, int pOffset) -{ - return static_cast(static_cast(pByteArray.at(pOffset)) << 8) | static_cast(static_cast(pByteArray.at(pOffset + 1))); -} - - -int CommandApdu::getLc() const -{ - if (length() <= 5) - { - return 0; - } - if (!isExtendedLength()) - { - // short command apdu - return static_cast(mBuffer.at(4)); - } - // extended length command apdu - if (length() <= 6) - { - qCCritical(card) << "Cannot determine Lc, returning 0"; - return 0; - } - return readLength(mBuffer, 5); -} - - -int CommandApdu::getLe() const -{ - int lc = getLc(); - if (isExtendedLength()) - { - // no data (so lc==0): we have 4 bytes header and the le field is prefixed with 0 byte - // with data: we have 4 bytes header, lc field encoded in 3 bytes and the data field - int offset = lc == 0 ? 5 : 7 + lc; - if (length() < offset + 2) - { - return 0; - } - int le = readLength(mBuffer, offset); - return le == 0 ? EXTENDED_MAX_LE : le; - } - // no data (so lc==0): we have 4 bytes header - // with data: we have 4 bytes header, lc field encoded in 1 byte and the data field - int offset = lc == 0 ? 4 : 5 + lc; - if (length() < offset + 1) - { - return 0; - } - int le = static_cast(mBuffer.at(offset)); - return le == 0 ? SHORT_MAX_LE : le; -} - - -QByteArray CommandApdu::getData() const -{ - int lc = getLc(); - if (lc == 0) - { - return QByteArray(); - } - if (isExtendedLength()) - { - // 4 bytes header + 3 bytes length - return mBuffer.mid(7, lc); - } - // 4 bytes header + 1 bytes length - return mBuffer.mid(5, lc); -} - - -bool CommandApdu::isUpdateRetryCounter() const -{ - return mUpdateRetryCounter; -} - - -ResponseApdu::ResponseApdu(StatusCode pStatusCode) - : Apdu(QByteArray()) -{ - char buffer[2]; - qToBigEndian(Enum::getValue(pStatusCode), buffer); - setBuffer(QByteArray(buffer, 2)); -} - - -ResponseApdu::ResponseApdu(const QByteArray& pBuffer) - : Apdu(pBuffer) -{ -} - - -ResponseApdu::~ResponseApdu() -{ -} - - -void ResponseApdu::setBuffer(const QByteArray& pBuffer) -{ - mBuffer = pBuffer; -} - - -QByteArray ResponseApdu::getData() const -{ - if (length() < RETURN_CODE_LENGTH) - { - return QByteArray(); - } - - return mBuffer.left(getDataLength()); -} - - -int ResponseApdu::getDataLength() const -{ - return length() - RETURN_CODE_LENGTH; -} - - -StatusCode ResponseApdu::getReturnCode() const -{ - if (mBuffer.isEmpty()) - { - return StatusCode::EMPTY; - } - - const int returnCodeAsInt = getReturnCodeAsHex().toInt(nullptr, 16); - return Enum::isValue(returnCodeAsInt) ? StatusCode(returnCodeAsInt) : StatusCode::INVALID; -} - - -QByteArray ResponseApdu::getReturnCodeAsHex() const -{ - return mBuffer.right(RETURN_CODE_LENGTH).toHex(); -} - - -int ResponseApdu::getRetryCounter() const -{ - StatusCode statusCode = getReturnCode(); - if (statusCode == StatusCode::SUCCESS) - { - return 3; - } - if (statusCode == StatusCode::PIN_RETRY_COUNT_2) - { - return 2; - } - if (statusCode == StatusCode::PIN_SUSPENDED) - { - return 1; - } - if (statusCode == StatusCode::PIN_BLOCKED || statusCode == StatusCode::PIN_DEACTIVATED) - { - return 0; - } - return -1; -} - - -SW1 ResponseApdu::getSW1() const -{ - if (length() < RETURN_CODE_LENGTH) - { - qCCritical(card) << "Buffer too short, returning 0"; - return SW1::INVALID; - } - return SW1(mBuffer.at(length() - RETURN_CODE_LENGTH)); -} - - -char ResponseApdu::getSW2() const -{ - if (length() < 1) - { - qCCritical(card) << "Buffer too short, returning 0"; - return 0; - } - return mBuffer.at(length() - 1); -} - - -CardReturnCode ResponseApdu::getCardReturnCode() const -{ - switch (getReturnCode()) - { - case StatusCode::SUCCESS: - return CardReturnCode::OK; - - case StatusCode::INPUT_TIMEOUT: - return CardReturnCode::INPUT_TIME_OUT; - - case StatusCode::INPUT_CANCELLED: - return CardReturnCode::CANCELLATION_BY_USER; - - case StatusCode::PASSWORDS_DIFFER: - return CardReturnCode::NEW_PIN_MISMATCH; - - case StatusCode::PASSWORD_OUTOF_RANGE: - return CardReturnCode::NEW_PIN_INVALID_LENGTH; - - case StatusCode::PIN_BLOCKED: - return CardReturnCode::PIN_BLOCKED; - - default: - return CardReturnCode::PROTOCOL_ERROR; - } - - Q_UNREACHABLE(); -} - - -#include "moc_Apdu.cpp" diff --git a/src/card/base/Apdu.h b/src/card/base/Apdu.h index e1241e0..c28c359 100644 --- a/src/card/base/Apdu.h +++ b/src/card/base/Apdu.h @@ -4,138 +4,23 @@ #pragma once -#include "CardReturnCode.h" -#include "EnumHelper.h" - #include namespace governikus { -defineTypedEnumType(StatusCode, quint16, - EMPTY = 0x0000, - INVALID = 0x0001, - SUCCESS = 0x9000, - NO_PKCS15_APP = 0x6200, - END_OF_FILE = 0x6282, - PIN_DEACTIVATED = 0x6283, - FCI_NO_ISO7816_4 = 0x6284, - VERIFICATION_FAILED = 0x6300, - INPUT_TIMEOUT = 0x6400, - INPUT_CANCELLED = 0x6401, - PASSWORDS_DIFFER = 0x6402, - PASSWORD_OUTOF_RANGE = 0x6403, - CARD_EJECTED_AND_REINSERTED = 0x64a2, - EEPROM_CELL_DEFECT = 0x6581, - SECURITY_ENVIRONMENT = 0x6600, - WRONG_LENGTH = 0x6700, - NO_BINARY_FILE = 0x6981, - ACCESS_DENIED = 0x6982, - PASSWORD_COUNTER_EXPIRED = 0x6983, - DIRECTORY_OR_PASSWORD_LOCKED_OR_NOT_ALLOWED = 0x6984, - NO_PARENT_FILE = 0x6985, - NOT_YET_INITIALIZED = 0x6985, - NO_CURRENT_DIRECTORY_SELECTED = 0x6986, - DATAFIELD_EXPECTED = 0x6987, - INVALID_SM_OBJECTS = 0x6988, - COMMAND_NOT_ALLOWED = 0x69f0, - INVALID_DATAFIELD = 0x6a80, - ALGORITHM_ID = 0x6a81, - FILE_NOT_FOUND = 0x6a82, - RECORD_NOT_FOUND = 0x6a83, - INVALID_PARAMETER = 0x6a86, - LC_INCONSISTANT = 0x6a87, - PASSWORD_NOT_FOUND = 0x6a88, - ILLEGAL_OFFSET = 0x6b00, - UNSUPPORTED_CLA = 0x6e00, - CANT_DISPLAY = 0x6410, - INVALID_P1P2 = 0x6a00, - UNSUPPORTED_INS = 0x6d00, - PIN_BLOCKED = 0x63c0, //retrys left 0 - PIN_SUSPENDED = 0x63c1, //retrys left 1 - PIN_RETRY_COUNT_2 = 0x63c2, //retrys left 2 - ) - - -/* - * As defined in ISO-7816-4 Table-5 - */ -defineEnumType(SW1, - INVALID = 0x00, - MORE_DATA_AVAILABLE = 0x61, - ERROR_COMMAND_NOT_ALLOWED = 0x69, - WRONG_LE_FIELD = 0x6c, - ) - class Apdu { protected: QByteArray mBuffer; Apdu(const QByteArray& pBuffer); - virtual ~Apdu(); + ~Apdu() = default; public: - static const int NO_LE = 0; - static const int SHORT_MAX_LC = 0xff; - static const int SHORT_MAX_LE = 0x0100; - static const int EXTENDED_MAX_LC = 0x00ffff; - static const int EXTENDED_MAX_LE = 0x010000; - static const char CLA = 0x00; - static const char CLA_COMMAND_CHAINING = 0x10; - static const char CLA_SECURE_MESSAGING = 0x0c; const QByteArray& getBuffer() const; int length() const; }; -class CommandApdu - : public Apdu -{ - private: - bool mUpdateRetryCounter; - inline bool isExtendedLength() const; - - public: - CommandApdu(const QByteArray& pBuffer, bool pUpdateRetryCounter = false); - CommandApdu(const QByteArray& pHeader, const QByteArray& pData, int pLe); - CommandApdu(char pCla, char pIns, char pP1, char pP2, const QByteArray& pData = QByteArray(), int pLe = NO_LE); - virtual ~CommandApdu(); - - char getCLA() const; - char getINS() const; - char getP1() const; - char getP2() const; - int getLc() const; - int getLe() const; - QByteArray getData() const; - bool isUpdateRetryCounter() const; - - static bool isExtendedLength(const QByteArray& pData, int pLe); - static bool isSecureMessaging(const QByteArray& pCommandBuffer); -}; - -class ResponseApdu - : public Apdu -{ - - private: - static const int RETURN_CODE_LENGTH = 2; - - public: - ResponseApdu(StatusCode pStatusCode); - ResponseApdu(const QByteArray& pBuffer = QByteArray()); - virtual ~ResponseApdu(); - - virtual void setBuffer(const QByteArray& pBuffer); - QByteArray getData() const; - int getDataLength() const; - StatusCode getReturnCode() const; - QByteArray getReturnCodeAsHex() const; - int getRetryCounter() const; - SW1 getSW1() const; - char getSW2() const; - CardReturnCode getCardReturnCode() const; -}; - -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/Card.cpp b/src/card/base/Card.cpp index e02c22e..2b2e5af 100644 --- a/src/card/base/Card.cpp +++ b/src/card/base/Card.cpp @@ -16,12 +16,7 @@ Card::Card() } -Card::~Card() -{ -} - - -CardReturnCode Card::establishPaceChannel(PACE_PASSWORD_ID pPasswordId, const QByteArray& pChat, const QByteArray& pCertificateDescription, EstablishPACEChannelOutput& pChannelOutput, quint8 pTimeoutSeconds) +CardReturnCode Card::establishPaceChannel(PacePasswordId pPasswordId, const QByteArray& pChat, const QByteArray& pCertificateDescription, EstablishPaceChannelOutput& pChannelOutput, quint8 pTimeoutSeconds) { Q_UNUSED(pPasswordId); Q_UNUSED(pChat); diff --git a/src/card/base/Card.h b/src/card/base/Card.h index 2ec64bf..2446f70 100644 --- a/src/card/base/Card.h +++ b/src/card/base/Card.h @@ -6,10 +6,11 @@ #pragma once -#include "Apdu.h" #include "CardReturnCode.h" +#include "CommandApdu.h" #include "Commands.h" -#include "EstablishPACEChannel.h" +#include "EstablishPaceChannelOutput.h" +#include "ResponseApdu.h" #include "SmartCardDefinitions.h" #include @@ -26,7 +27,7 @@ class Card public: Card(); - virtual ~Card(); + virtual ~Card() = default; /*! * Establish a connection to the smart card @@ -53,7 +54,7 @@ class Card /*! * Establishes a PACE channel, i.e. the corresponding reader is no basic reader. */ - virtual CardReturnCode establishPaceChannel(PACE_PASSWORD_ID pPasswordId, const QByteArray& pChat, const QByteArray& pCertificateDescription, EstablishPACEChannelOutput& pChannelOutput, quint8 pTimeoutSeconds = 60); + virtual CardReturnCode establishPaceChannel(PacePasswordId pPasswordId, const QByteArray& pChat, const QByteArray& pCertificateDescription, EstablishPaceChannelOutput& pChannelOutput, quint8 pTimeoutSeconds = 60); /*! * Destroys an existing PACE channel, i.e. the corresponding reader is no basic reader. @@ -67,4 +68,4 @@ class Card }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/CardConnection.cpp b/src/card/base/CardConnection.cpp index 36b950f..48bcce7 100644 --- a/src/card/base/CardConnection.cpp +++ b/src/card/base/CardConnection.cpp @@ -11,14 +11,21 @@ CardConnection::CardConnection(const QSharedPointer& pCard : QObject() , mCardConnectionWorker(pCardConnectionWorker) , mReaderInfo() + , mPaceCanSuccessful(false) + , mPacePinSuccessful(false) { Q_ASSERT(mCardConnectionWorker); - QMetaObject::invokeMethod(mCardConnectionWorker.data(), "getReaderInfo", Qt::BlockingQueuedConnection, Q_RETURN_ARG(ReaderInfo, mReaderInfo)); + QMetaObject::invokeMethod(mCardConnectionWorker.data(), &CardConnectionWorker::getReaderInfo, Qt::BlockingQueuedConnection, &mReaderInfo); connect(mCardConnectionWorker.data(), &CardConnectionWorker::fireReaderInfoChanged, this, &CardConnection::onReaderInfoChanged); } -CardConnection::~CardConnection() +CardConnection::CardConnection() + : QObject() + , mCardConnectionWorker() + , mReaderInfo() + , mPaceCanSuccessful(false) + , mPacePinSuccessful(false) { } @@ -29,10 +36,22 @@ const ReaderInfo& CardConnection::getReaderInfo() } +bool CardConnection::getPaceCanSuccessful() const +{ + return mPaceCanSuccessful; +} + + +bool CardConnection::getPacePinSuccessful() const +{ + return mPacePinSuccessful; +} + + bool CardConnection::stopSecureMessaging() { bool result; - QMetaObject::invokeMethod(mCardConnectionWorker.data(), "stopSecureMessaging", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, result)); + QMetaObject::invokeMethod(mCardConnectionWorker.data(), &CardConnectionWorker::stopSecureMessaging, Qt::BlockingQueuedConnection, &result); return result; } @@ -49,7 +68,7 @@ UnblockPinCommand* CardConnection::createUnblockPinCommand(const QString& pPuk) } -EstablishPaceChannelCommand* CardConnection::createEstablishPaceChannelCommand(PACE_PASSWORD_ID pPacePasswordId, const QString& pPacePassword, const QByteArray& pEffectiveChat, const QByteArray& pCertificateDescription) +EstablishPaceChannelCommand* CardConnection::createEstablishPaceChannelCommand(PacePasswordId pPacePasswordId, const QString& pPacePassword, const QByteArray& pEffectiveChat, const QByteArray& pCertificateDescription) { return new EstablishPaceChannelCommand(mCardConnectionWorker, pPacePasswordId, pPacePassword, pEffectiveChat, pCertificateDescription); } @@ -89,7 +108,7 @@ void CardConnection::onReaderInfoChanged(const ReaderInfo& pReaderInfo) } -TransmitCommand* CardConnection::createTransmitCommand(const QVector& pInputApduInfos, const QString pSlotHandle) +TransmitCommand* CardConnection::createTransmitCommand(const QVector& pInputApduInfos, const QString& pSlotHandle) { return new TransmitCommand(mCardConnectionWorker, pInputApduInfos, pSlotHandle); } diff --git a/src/card/base/CardConnection.h b/src/card/base/CardConnection.h index 2422099..6c506ba 100644 --- a/src/card/base/CardConnection.h +++ b/src/card/base/CardConnection.h @@ -28,6 +28,12 @@ #include +class test_WorkflowContext; +class test_SelfAuthModel; +class test_CardConnection; +class test_StateEstablishPaceChannel; + + namespace governikus { @@ -38,6 +44,11 @@ class CardConnection : public QObject { private: + friend class ::test_WorkflowContext; + friend class ::test_SelfAuthModel; + friend class ::test_CardConnection; + friend class ::test_StateEstablishPaceChannel; + Q_OBJECT /*! @@ -46,11 +57,14 @@ class CardConnection QSharedPointer mCardConnectionWorker; ReaderInfo mReaderInfo; - TransmitCommand* createTransmitCommand(const QVector& pInputApduInfos, const QString pSlotHandle); + bool mPaceCanSuccessful; + bool mPacePinSuccessful; + + TransmitCommand* createTransmitCommand(const QVector& pInputApduInfos, const QString& pSlotHandle); UpdateRetryCounterCommand* createUpdateRetryCounterCommand(); UnblockPinCommand* createUnblockPinCommand(const QString& pPuk); - EstablishPaceChannelCommand* createEstablishPaceChannelCommand(PACE_PASSWORD_ID pPacePasswordId, const QString& pPacePassword, const QByteArray& pEffectiveChat, const QByteArray& pCertificateDescription); + EstablishPaceChannelCommand* createEstablishPaceChannelCommand(PacePasswordId pPacePasswordId, const QString& pPacePassword, const QByteArray& pEffectiveChat, const QByteArray& pCertificateDescription); SetEidPinCommand* createSetEidPinCommand(const QString& pNewPin, quint8 pTimeoutSeconds); DestroyPaceChannelCommand* createDestroyPaceChannelCommand(); @@ -69,7 +83,7 @@ class CardConnection if (resultConnection) { - QMetaObject::invokeMethod(pCommand, "execute", Qt::QueuedConnection); + pCommand->run(); } else { @@ -84,13 +98,16 @@ class CardConnection private Q_SLOTS: void onReaderInfoChanged(const ReaderInfo& pReaderInfo); + protected: + CardConnection(); + public: CardConnection(const QSharedPointer& pCardConnectionWorker); /*! * Destroys the CardConnection and disconnects from the card. */ - virtual ~CardConnection(); + virtual ~CardConnection() = default; /*! * This method returns a stored copy of the reader info object. So calling this method any @@ -98,7 +115,10 @@ class CardConnection * * (In contrast ReaderManager::getReaderInfo(...) calls the worker thread and may be blocked.) */ - const ReaderInfo& getReaderInfo(); + virtual const ReaderInfo& getReaderInfo(); + + bool getPaceCanSuccessful() const; + bool getPacePinSuccessful() const; bool stopSecureMessaging(); @@ -133,9 +153,23 @@ class CardConnection template QMetaObject::Connection callEstablishPaceChannelCommand(const typename QtPrivate::FunctionPointer::Object* pReceiver, T pFunc, - PACE_PASSWORD_ID pPacePasswordId, const QString& pPacePassword, const QByteArray& pEffectiveChat = QByteArray(), const QByteArray& pCertificateDescription = QByteArray()) + PacePasswordId pPacePasswordId, const QString& pPacePassword, const QByteArray& pEffectiveChat = QByteArray(), const QByteArray& pCertificateDescription = QByteArray()) { EstablishPaceChannelCommand* command = createEstablishPaceChannelCommand(pPacePasswordId, pPacePassword, pEffectiveChat, pCertificateDescription); + + if (pPacePasswordId == PacePasswordId::PACE_CAN) + { + connect(command, &BaseCardCommand::commandDone, this, [this](QSharedPointer pCommand){ + mPaceCanSuccessful = pCommand->getReturnCode() == CardReturnCode::OK; + }); + } + else if (pPacePasswordId == PacePasswordId::PACE_PIN) + { + connect(command, &BaseCardCommand::commandDone, this, [this](QSharedPointer pCommand){ + mPacePinSuccessful = pCommand->getReturnCode() == CardReturnCode::OK; + }); + } + return call(command, pReceiver, pFunc); } @@ -160,7 +194,7 @@ class CardConnection template QMetaObject::Connection callTransmitCommand(const typename QtPrivate::FunctionPointer::Object* pReceiver, T pFunc, - const QVector& pInputApduInfos, const QString pSlotHandle = QString()) + const QVector& pInputApduInfos, const QString& pSlotHandle = QString()) { auto command = createTransmitCommand(pInputApduInfos, pSlotHandle); return call(command, pReceiver, pFunc); @@ -179,4 +213,4 @@ class CardConnection void fireReaderInfoChanged(const ReaderInfo& pReaderInfo); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/CardConnectionWorker.cpp b/src/card/base/CardConnectionWorker.cpp index f2b951c..d034adc 100644 --- a/src/card/base/CardConnectionWorker.cpp +++ b/src/card/base/CardConnectionWorker.cpp @@ -18,9 +18,9 @@ CardConnectionWorker::CardConnectionWorker(Reader* pReader) , mReader(pReader) , mSecureMessaging() { - connect(mReader, &Reader::fireCardInserted, this, &CardConnectionWorker::onReaderInfoChanged); - connect(mReader, &Reader::fireCardRemoved, this, &CardConnectionWorker::onReaderInfoChanged); - connect(mReader, &Reader::fireCardRetryCounterChanged, this, &CardConnectionWorker::onReaderInfoChanged); + connect(mReader.data(), &Reader::fireCardInserted, this, &CardConnectionWorker::onReaderInfoChanged); + connect(mReader.data(), &Reader::fireCardRemoved, this, &CardConnectionWorker::onReaderInfoChanged); + connect(mReader.data(), &Reader::fireCardRetryCounterChanged, this, &CardConnectionWorker::onReaderInfoChanged); } @@ -94,12 +94,6 @@ CardReturnCode CardConnectionWorker::transmit(const CommandApdu& pCommandApdu, R returnCode = mReader->getCard()->transmit(pCommandApdu, pResponseApdu); } - if (pCommandApdu.isUpdateRetryCounter()) - { - int retryCounter = pResponseApdu.getRetryCounter(); - mReader->setRetryCounter(retryCounter); - } - return returnCode; } @@ -130,11 +124,12 @@ CardReturnCode CardConnectionWorker::readFile(const FileRef& pFileRef, QByteArra } pFileContent += res.getData(); - if (res.getData().size() != 0xff && res.getReturnCode() == StatusCode::END_OF_FILE) + const StatusCode statusCode = res.getReturnCode(); + if (statusCode == StatusCode::END_OF_FILE) { return CardReturnCode::OK; } - if (res.getReturnCode() != StatusCode::SUCCESS) + if (statusCode != StatusCode::SUCCESS) { break; } @@ -156,22 +151,23 @@ bool CardConnectionWorker::stopSecureMessaging() } -CardReturnCode CardConnectionWorker::establishPaceChannel(PACE_PASSWORD_ID pPasswordId, +CardReturnCode CardConnectionWorker::establishPaceChannel(PacePasswordId pPasswordId, const QString& pPasswordValue, - EstablishPACEChannelOutput& pChannelOutput) + EstablishPaceChannelOutput& pChannelOutput) { return establishPaceChannel(pPasswordId, pPasswordValue, nullptr, nullptr, pChannelOutput); } -CardReturnCode CardConnectionWorker::establishPaceChannel(PACE_PASSWORD_ID pPasswordId, +CardReturnCode CardConnectionWorker::establishPaceChannel(PacePasswordId pPasswordId, const QString& pPasswordValue, const QByteArray& pChat, const QByteArray& pCertificateDescription, - EstablishPACEChannelOutput& pChannelOutput) + EstablishPaceChannelOutput& pChannelOutput) { if (!hasCard()) { + pChannelOutput.setPaceReturnCode(CardReturnCode::CARD_NOT_FOUND); return CardReturnCode::CARD_NOT_FOUND; } CardReturnCode returnCode; diff --git a/src/card/base/CardConnectionWorker.h b/src/card/base/CardConnectionWorker.h index df6692f..544a7ee 100644 --- a/src/card/base/CardConnectionWorker.h +++ b/src/card/base/CardConnectionWorker.h @@ -7,13 +7,14 @@ #pragma once #include "asn1/SecurityInfos.h" -#include "Apdu.h" #include "CardReturnCode.h" +#include "CommandApdu.h" #include "Commands.h" -#include "EstablishPACEChannel.h" +#include "EstablishPaceChannel.h" #include "FileRef.h" #include "pace/SecureMessaging.h" #include "Reader.h" +#include "ResponseApdu.h" #include "SmartCardDefinitions.h" #include @@ -77,20 +78,20 @@ class CardConnectionWorker * If the Reader is a basic reader and the PACE channel is successfully established, the subsequent transmits will be secured using, secure messaging. * I. e., a secure messaging channel is established. */ - virtual CardReturnCode establishPaceChannel(PACE_PASSWORD_ID pPasswordId, + virtual CardReturnCode establishPaceChannel(PacePasswordId pPasswordId, const QString& pPasswordValue, - EstablishPACEChannelOutput& pChannelOutput); + EstablishPaceChannelOutput& pChannelOutput); /*! * Performs PACE and establishes a PACE channel for later terminal authentication. * If the Reader is a basic reader and the PACE channel is successfully established, the subsequent transmits will be secured using, secure messaging. * I. e., a secure messaging channel is established. */ - virtual CardReturnCode establishPaceChannel(PACE_PASSWORD_ID pPasswordId, + virtual CardReturnCode establishPaceChannel(PacePasswordId pPasswordId, const QString& pPasswordValue, const QByteArray& pChat, const QByteArray& pCertificateDescription, - EstablishPACEChannelOutput& pChannelOutput); + EstablishPaceChannelOutput& pChannelOutput); /*! * Destroys a previously established PACE channel. @@ -108,4 +109,4 @@ class CardConnectionWorker void fireReaderInfoChanged(const ReaderInfo& pReaderInfo); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/CardInfo.cpp b/src/card/base/CardInfo.cpp index d48b7ec..844fa13 100644 --- a/src/card/base/CardInfo.cpp +++ b/src/card/base/CardInfo.cpp @@ -6,7 +6,7 @@ #include "CardInfo.h" -#include "asn1/PACEInfo.h" +#include "asn1/PaceInfo.h" #include "asn1/SecurityInfos.h" #include "CardConnectionWorker.h" @@ -210,7 +210,7 @@ bool CardInfoFactory::checkEfCardAccess(const QSharedPointer& pEfC * At least one PACEInfo must have standardized domain parameters */ bool containsStandardizedDomainParameters = false; - const auto& infos = pEfCardAccess->getPACEInfos(); + const auto& infos = pEfCardAccess->getPaceInfos(); for (const auto& paceInfo : infos) { if (paceInfo->isStandardizedDomainParameters()) @@ -232,7 +232,6 @@ bool CardInfoFactory::checkEfCardAccess(const QSharedPointer& pEfC namespace governikus { - QDebug operator<<(QDebug pDbg, const CardInfo& pCardInfo) { QDebugStateSaver saver(pDbg); @@ -245,4 +244,4 @@ QDebug operator<<(QDebug pDbg, const CardInfo& pCardInfo) } -} +} // namespace governikus diff --git a/src/card/base/CardInfo.h b/src/card/base/CardInfo.h index e61ebb3..ecb724a 100644 --- a/src/card/base/CardInfo.h +++ b/src/card/base/CardInfo.h @@ -12,11 +12,12 @@ #include #include +class test_CardInfo; + namespace governikus { class CardConnectionWorker; -class PACEInfo; class Reader; class ReaderInfo; @@ -29,6 +30,7 @@ class CardInfo Q_DECLARE_TR_FUNCTIONS(governikus::CardInfo) private: + friend class ::test_CardInfo; CardType mCardType; QSharedPointer mEfCardAccess; int mRetryCounter; @@ -102,4 +104,4 @@ class CardInfoFactory QDebug operator<<(QDebug pDbg, const CardInfo& pCardInfo); -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/CardOperationResult.h b/src/card/base/CardOperationResult.h index c06ba51..809dd5c 100644 --- a/src/card/base/CardOperationResult.h +++ b/src/card/base/CardOperationResult.h @@ -39,4 +39,4 @@ class CardOperationResult }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/CommandApdu.cpp b/src/card/base/CommandApdu.cpp new file mode 100644 index 0000000..0bacd6f --- /dev/null +++ b/src/card/base/CommandApdu.cpp @@ -0,0 +1,194 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "CommandApdu.h" + +#include +#include + +using namespace governikus; + +Q_DECLARE_LOGGING_CATEGORY(card) + +CommandApdu::CommandApdu(const QByteArray& pBuffer) + : Apdu(pBuffer) +{ +} + + +bool CommandApdu::isExtendedLength() const +{ + // no data, no le: size == 4 + // no data, short le: size == 5 + // no data, extended le: size == 7, high order le byte == 0 + // data, with/without le: high order size byte == 0 <=> extended data length + return length() > 5 && mBuffer.at(4) == '\0'; +} + + +bool CommandApdu::isExtendedLength(const QByteArray& pData, int pLe) +{ + return pData.size() > SHORT_MAX_LC || pLe > SHORT_MAX_LE; +} + + +bool CommandApdu::isSecureMessaging(const QByteArray& pCommandBuffer) +{ + return !pCommandBuffer.isEmpty() && ((pCommandBuffer.at(0) & 0x0F) == CLA_SECURE_MESSAGING); +} + + +CommandApdu::CommandApdu(const QByteArray& pHeader, const QByteArray& pData, int pLe) + : CommandApdu(pHeader.at(0), pHeader.at(1), pHeader.at(2), pHeader.at(3), pData, pLe) +{ +} + + +CommandApdu::CommandApdu(char pCla, char pIns, char pP1, char pP2, const QByteArray& pData, int pLe) + : Apdu(QByteArray()) +{ + if (pData.size() > EXTENDED_MAX_LC) + { + qCCritical(card) << "Command data exceeds maximum of 0xFFFF bytes"; + return; + } + if (pLe > EXTENDED_MAX_LE) + { + qCCritical(card) << "Expected length exceeds maximum value of 0x010000"; + return; + } + + mBuffer += pCla; + mBuffer += pIns; + mBuffer += pP1; + mBuffer += pP2; + + // + // according to ISO 7816 Part 4, chapter 5.1 + // + if (pData.size() > 0) // withPayload + { + if (CommandApdu::isExtendedLength(pData, pLe)) + { + mBuffer += '\0'; + mBuffer += static_cast(pData.size() >> 8 & 0xff); + mBuffer += static_cast(pData.size() & 0xff); + } + else + { + mBuffer += static_cast(pData.size() & 0xff); + } + + mBuffer += pData; + } + + if (pLe > 0) + { + if (CommandApdu::isExtendedLength(pData, pLe)) + { + if (pData.isEmpty()) + { + mBuffer += '\0'; + } + mBuffer += static_cast(pLe >> 8 & 0xff); + } + mBuffer += static_cast(pLe & 0xff); + } +} + + +char CommandApdu::getCLA() const +{ + return length() > 0 ? mBuffer.at(0) : '\0'; +} + + +char CommandApdu::getINS() const +{ + return length() > 1 ? mBuffer.at(1) : '\0'; +} + + +char CommandApdu::getP1() const +{ + return length() > 2 ? mBuffer.at(2) : '\0'; +} + + +char CommandApdu::getP2() const +{ + return mBuffer.size() > 3 ? mBuffer.at(3) : '\0'; +} + + +static inline int readLength(const QByteArray& pByteArray, int pOffset) +{ + Q_ASSERT(pByteArray.size() >= pOffset + 2); + return qFromBigEndian(pByteArray.mid(pOffset, 2).data()); +} + + +int CommandApdu::getLc() const +{ + if (length() <= 5) + { + return 0; + } + if (!isExtendedLength()) + { + // short command apdu + return static_cast(mBuffer.at(4)); + } + // extended length command apdu + if (length() <= 6) + { + qCCritical(card) << "Cannot determine Lc, returning 0"; + return 0; + } + return readLength(mBuffer, 5); +} + + +int CommandApdu::getLe() const +{ + int lc = getLc(); + if (isExtendedLength()) + { + // no data (so lc==0): we have 4 bytes header and the le field is prefixed with 0 byte + // with data: we have 4 bytes header, lc field encoded in 3 bytes and the data field + int offset = (lc == 0 ? 5 : 7 + lc); + if (length() < offset + 2) + { + return 0; + } + int le = readLength(mBuffer, offset); + return le == 0 ? EXTENDED_MAX_LE : le; + } + // no data (so lc==0): we have 4 bytes header + // with data: we have 4 bytes header, lc field encoded in 1 byte and the data field + int offset = lc == 0 ? 4 : 5 + lc; + if (length() < offset + 1) + { + return 0; + } + int le = static_cast(mBuffer.at(offset)); + return le == 0 ? SHORT_MAX_LE : le; +} + + +QByteArray CommandApdu::getData() const +{ + int lc = getLc(); + if (lc == 0) + { + return QByteArray(); + } + if (isExtendedLength()) + { + // 4 bytes header + 3 bytes length + return mBuffer.mid(7, lc); + } + // 4 bytes header + 1 bytes length + return mBuffer.mid(5, lc); +} diff --git a/src/card/base/CommandApdu.h b/src/card/base/CommandApdu.h new file mode 100644 index 0000000..b2fc5f4 --- /dev/null +++ b/src/card/base/CommandApdu.h @@ -0,0 +1,44 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "Apdu.h" + +namespace governikus +{ + +class CommandApdu final + : public Apdu +{ + private: + inline bool isExtendedLength() const; + + public: + static const int NO_LE = 0; + static const int SHORT_MAX_LC = 0xff; + static const int SHORT_MAX_LE = 0x0100; + static const int EXTENDED_MAX_LC = 0x00ffff; + static const int EXTENDED_MAX_LE = 0x010000; + static const char CLA = 0x00; + static const char CLA_COMMAND_CHAINING = 0x10; + static const char CLA_SECURE_MESSAGING = 0x0c; + + CommandApdu(const QByteArray& pBuffer); + CommandApdu(const QByteArray& pHeader, const QByteArray& pData, int pLe); + CommandApdu(char pCla, char pIns, char pP1, char pP2, const QByteArray& pData = QByteArray(), int pLe = NO_LE); + + char getCLA() const; + char getINS() const; + char getP1() const; + char getP2() const; + int getLc() const; + int getLe() const; + QByteArray getData() const; + + static bool isExtendedLength(const QByteArray& pData, int pLe); + static bool isSecureMessaging(const QByteArray& pCommandBuffer); +}; + +} // namespace governikus diff --git a/src/card/base/Commands.cpp b/src/card/base/Commands.cpp index 4cc055f..fae93d0 100644 --- a/src/card/base/Commands.cpp +++ b/src/card/base/Commands.cpp @@ -90,34 +90,6 @@ QByteArray GetChallengeResponse::getChallenge() const /* * MSEBuilder */ -bool MSEBuilder::isUpdateRetryCounterCommand(const QByteArray& cmd) -{ - if (cmd.size() < 4) - { - return false; - } - - if (cmd.at(0) != CommandApdu::CLA) - { - return false; - } - if (cmd.at(1) != static_cast(MSEBuilder::INS::MANAGE_SECURITY_ENVIRONMENT)) - { - return false; - } - if (cmd.at(2) != static_cast(MSEBuilder::P1::PERFORM_SECURITY_OPERATION)) - { - return false; - } - if (cmd.at(3) != static_cast(MSEBuilder::P2::SET_AT)) - { - return false; - } - - return true; -} - - MSEBuilder::MSEBuilder(P1 p1, P2 p2) : CommandApduBuilder() , mP1(p1) @@ -152,11 +124,11 @@ void MSEBuilder::setPublicKey(const QByteArray& pData) } -void MSEBuilder::setPublicKey(PACE_PASSWORD_ID pPasswordId) +void MSEBuilder::setPublicKey(PacePasswordId pPasswordId) { static const char TAG_PUBLIC_KEY = char(0x83); QByteArray data; - data += Enum::getValue(pPasswordId); + data += Enum::getValue(pPasswordId); mPublicKey = Asn1Util::encode(TAG_PUBLIC_KEY, data); } @@ -321,7 +293,7 @@ CommandApdu GABuilder::build() } data = Asn1Util::encode(TAG_DYNAMIC_AUTHENTICATION_DATA, data); - return CommandApdu(mClassByte, INS, 0, 0, data, Apdu::SHORT_MAX_LE); + return CommandApdu(mClassByte, INS, 0, 0, data, CommandApdu::SHORT_MAX_LE); } diff --git a/src/card/base/Commands.h b/src/card/base/Commands.h index 12073f4..63257fa 100644 --- a/src/card/base/Commands.h +++ b/src/card/base/Commands.h @@ -5,11 +5,12 @@ #pragma once #include "asn1/Chat.h" -#include "Apdu.h" -#include "CardReturnCode.h" +#include "CommandApdu.h" #include "FileRef.h" +#include "ResponseApdu.h" #include "SmartCardDefinitions.h" +class test_Commands; namespace governikus { @@ -82,13 +83,11 @@ class MSEBuilder SET_AT = char(0xa4), HASH_ALGORITHM = char(0xaa), COMPUTE_DIGITAL_SIGNATURE = char(0xb6), ENCRYPTION_OPERATION = char(0xb8), DEFAULT_CHANNEL = 0x01, }; - static bool isUpdateRetryCounterCommand(const QByteArray& cmd); - MSEBuilder(P1 p1, P2 p2); void setAuxiliaryData(const QByteArray& pData); void setOid(const QByteArray& pData); void setPublicKey(const QByteArray& pData); - void setPublicKey(PACE_PASSWORD_ID pPassword); + void setPublicKey(PacePasswordId pPassword); void setPrivateKey(const QByteArray& pData); void setEphemeralPublicKey(const QByteArray& pData); void setChat(const QByteArray& pData); @@ -125,6 +124,7 @@ class PSOBuilder CommandApdu build() override; private: + friend class ::test_Commands; P1 mP1; P2 mP2; QByteArray mCertificateBody; @@ -140,6 +140,7 @@ class EABuilder CommandApdu build() override; private: + friend class ::test_Commands; QByteArray mSignature; }; @@ -155,6 +156,7 @@ class GABuilder CommandApdu build() override; private: + friend class ::test_Commands; char mClassByte; QByteArray mCaEphemeralPublicKey; QByteArray mPaceMappingData; @@ -185,4 +187,4 @@ class ResetRetryCounterBuilder QByteArray mPin; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/DestroyPACEChannel.cpp b/src/card/base/DestroyPACEChannel.cpp deleted file mode 100644 index 8dd7062..0000000 --- a/src/card/base/DestroyPACEChannel.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/*! - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "DestroyPACEChannel.h" - - -using namespace governikus; - - -QByteArray DestroyPACEChannelBuilder::createCommandData() -{ - // Command data according to PC/SC Part 10 amendment 1.1 - static const char INDEX_DESTROY_PACE_CHANNEL = 0x03; - - QByteArray commandData; - commandData += INDEX_DESTROY_PACE_CHANNEL; - commandData += '\0'; - commandData += '\0'; - return commandData; -} - - -CommandApdu DestroyPACEChannelBuilder::createCommandDataCcid() -{ - // boxing command according to TR-03119 - return CommandApdu(char(0xFF), char(0x9A), 0x04, 0x03); -} diff --git a/src/card/base/DestroyPACEChannel.h b/src/card/base/DestroyPACEChannel.h deleted file mode 100644 index c8da26f..0000000 --- a/src/card/base/DestroyPACEChannel.h +++ /dev/null @@ -1,33 +0,0 @@ -/*! - * \brief Data object for creation of card command DestroyPACEChannel - * - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "Apdu.h" - -#include - - -namespace governikus -{ - - -class DestroyPACEChannelBuilder -{ - public: - /** - * Defined in pcsc10_v2.02.08_amd1.1 - */ - QByteArray createCommandData(); - - /** - * Defined in BSI-TR-03119_V1_pdf - */ - CommandApdu createCommandDataCcid(); -}; - - -} // namespace governikus diff --git a/src/card/base/DestroyPaceChannel.cpp b/src/card/base/DestroyPaceChannel.cpp new file mode 100644 index 0000000..13c7a28 --- /dev/null +++ b/src/card/base/DestroyPaceChannel.cpp @@ -0,0 +1,28 @@ +/*! + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "DestroyPaceChannel.h" + + +using namespace governikus; + + +QByteArray DestroyPaceChannelBuilder::createCommandData() +{ + // Command data according to PC/SC Part 10 amendment 1.1 + static const char INDEX_DESTROY_PACE_CHANNEL = 0x03; + + QByteArray commandData; + commandData += INDEX_DESTROY_PACE_CHANNEL; + commandData += '\0'; + commandData += '\0'; + return commandData; +} + + +CommandApdu DestroyPaceChannelBuilder::createCommandDataCcid() +{ + // boxing command according to TR-03119 + return CommandApdu(char(0xFF), char(0x9A), 0x04, 0x03); +} diff --git a/src/card/base/DestroyPaceChannel.h b/src/card/base/DestroyPaceChannel.h new file mode 100644 index 0000000..fb9a27a --- /dev/null +++ b/src/card/base/DestroyPaceChannel.h @@ -0,0 +1,32 @@ +/*! + * \brief Data object for creation of card command DestroyPACEChannel + * + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "CommandApdu.h" + +#include + + +namespace governikus +{ + +class DestroyPaceChannelBuilder +{ + public: + /** + * Defined in pcsc10_v2.02.08_amd1.1 + */ + QByteArray createCommandData(); + + /** + * Defined in BSI-TR-03119_V1_pdf + */ + CommandApdu createCommandDataCcid(); +}; + + +} // namespace governikus diff --git a/src/card/base/EstablishPACEChannel.cpp b/src/card/base/EstablishPACEChannel.cpp deleted file mode 100644 index e0ae5c9..0000000 --- a/src/card/base/EstablishPACEChannel.cpp +++ /dev/null @@ -1,544 +0,0 @@ -/*! - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "asn1/ASN1Util.h" -#include "EstablishPACEChannel.h" -#include "PersoSimWorkaround.h" - -#include -#include -#include -#include - - -using namespace governikus; - - -Q_DECLARE_LOGGING_CATEGORY(card) - - -namespace -{ -template QByteArray readByteArray(const QByteArray& pInput, int& pOffset) -{ - Q_ASSERT(sizeof(T) < INT_MAX); - - T length = qFromLittleEndian(pInput.data() + pOffset); - pOffset += static_cast(sizeof(T)); - QByteArray result = pInput.mid(pOffset, length); - pOffset += length; - return result; -} - - -} - - -namespace governikus -{ - - -/* - * There is no NUMERICSTRING implementation available in the macro system of OpenSSL, - * so we define it. - */ -ASN1_ITEM_TEMPLATE(NUMERICSTRING) = - ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_IMPTAG, 0x12, NUMERICSTRING, ASN1_OCTET_STRING) -ASN1_ITEM_TEMPLATE_END(NUMERICSTRING) - - -ASN1_SEQUENCE(ESTABLISHPACECHANNELOUTPUT) = { - ASN1_EXP(ESTABLISHPACECHANNELOUTPUT, mErrorCode, ASN1_OCTET_STRING, 0x01), - ASN1_EXP(ESTABLISHPACECHANNELOUTPUT, mStatusMSESetAt, ASN1_OCTET_STRING, 0x02), - ASN1_EXP(ESTABLISHPACECHANNELOUTPUT, mEfCardAccess, securityinfos_st, 0x03), - ASN1_EXP_OPT(ESTABLISHPACECHANNELOUTPUT, mIdPICC, ASN1_OCTET_STRING, 0x04), - ASN1_EXP_OPT(ESTABLISHPACECHANNELOUTPUT, mCurCAR, ASN1_OCTET_STRING, 0x05), - ASN1_EXP_OPT(ESTABLISHPACECHANNELOUTPUT, mPrevCAR, ASN1_OCTET_STRING, 0x06) -} - - -ASN1_SEQUENCE_END(ESTABLISHPACECHANNELOUTPUT) -IMPLEMENT_ASN1_FUNCTIONS(ESTABLISHPACECHANNELOUTPUT) -IMPLEMENT_ASN1_OBJECT(ESTABLISHPACECHANNELOUTPUT) - - -ASN1_SEQUENCE(ESTABLISHPACECHANNELINPUT) = { - ASN1_EXP(ESTABLISHPACECHANNELINPUT, mPasswordID, ASN1_INTEGER, 0x01), - ASN1_EXP_OPT(ESTABLISHPACECHANNELINPUT, mTransmittedPassword, NUMERICSTRING, 0x02), - ASN1_EXP_OPT(ESTABLISHPACECHANNELINPUT, mCHAT, ASN1_OCTET_STRING, 0x03), - ASN1_EXP_OPT(ESTABLISHPACECHANNELINPUT, mCertificateDescription, CertificateDescription, 0x04), - ASN1_EXP_OPT(ESTABLISHPACECHANNELINPUT, mHashOID, ASN1_OBJECT, 0x05) -} - - -ASN1_SEQUENCE_END(ESTABLISHPACECHANNELINPUT) -IMPLEMENT_ASN1_FUNCTIONS(ESTABLISHPACECHANNELINPUT) -IMPLEMENT_ASN1_OBJECT(ESTABLISHPACECHANNELINPUT) - - -} // namespace governikus - - -EstablishPACEChannelBuilder::EstablishPACEChannelBuilder() - : mPasswordId(PACE_PASSWORD_ID::PACE_MRZ) - , mChat(nullptr) - , mCertificateDescription() -{ -} - - -void EstablishPACEChannelBuilder::setCertificateDescription(const QByteArray& pCertificateDescription) -{ - mCertificateDescription = pCertificateDescription; -} - - -void EstablishPACEChannelBuilder::setChat(const QByteArray& pChat) -{ - mChat = pChat; -} - - -void EstablishPACEChannelBuilder::setPasswordId(PACE_PASSWORD_ID pPasswordId) -{ - mPasswordId = pPasswordId; -} - - -QByteArray EstablishPACEChannelBuilder::createCommandData() -{ - // Command data according to PC/SC Part 10 amendment 1.1 - static const char INDEX_ESTABLISH_PACE_CHANNEL = 0x02; - - QByteArray inputData; - inputData += static_cast(mPasswordId); - - if (mChat.size() > 0xFF) - { - qCCritical(card) << "Certificate Holder Authorization Template of size > 0xFF not supported"; - Q_ASSERT(mChat.size() <= 0xFF); - return QByteArray(); - } - inputData += static_cast(mChat.size()); - inputData += mChat; - - inputData += '\0'; // length of PIN - - if (mCertificateDescription.size() > 0xFFFF) - { - qCCritical(card) << "Certificate Description of size > 0xFFFF not supported"; - Q_ASSERT(mCertificateDescription.size() <= 0xFFFF); - return QByteArray(); - } - inputData += static_cast((mCertificateDescription.size() >> 0) & 0xff); - inputData += static_cast((mCertificateDescription.size() >> 8) & 0xff); - inputData += mCertificateDescription; - - QByteArray commandData; - commandData += (INDEX_ESTABLISH_PACE_CHANNEL); - - if (inputData.size() > 0xFFFF) - { - qCCritical(card) << "InputData of size > 0xFFFF not supported"; - Q_ASSERT(inputData.size() <= 0xFFFF); - return QByteArray(); - } - commandData += static_cast((inputData.size() >> 0) & 0xff); - commandData += static_cast((inputData.size() >> 8) & 0xff); - commandData += inputData; - return commandData; -} - - -CommandApdu EstablishPACEChannelBuilder::createCommandDataCcid() -{ - auto channelInput = newObject(); - - ASN1_INTEGER_set(channelInput->mPasswordID, static_cast(mPasswordId)); - if (!mChat.isNull()) - { - channelInput->mCHAT = ASN1_OCTET_STRING_new(); - Asn1OctetStringUtil::setValue(mChat, channelInput->mCHAT); - } - if (!mCertificateDescription.isEmpty()) - { - const uchar* unsignedCharPointer = reinterpret_cast(mCertificateDescription.constData()); - decodeAsn1Object(&channelInput->mCertificateDescription, &unsignedCharPointer, mCertificateDescription.size()); - } - - QByteArray data = encodeObject(channelInput.data()); - - // boxing command according to TR-03119 - return CommandApdu(char(0xFF), char(0x9A), 0x04, 0x02, data, CommandApdu::SHORT_MAX_LE); -} - - -EstablishPACEChannelOutput::EstablishPACEChannelOutput() - : mPaceReturnCode(CardReturnCode::UNKNOWN) - , mEfCardAccess() - , mCarCurr() - , mCarPrev() - , mIdIcc() - , mStatusMseSetAt() -{ -} - - -CardReturnCode EstablishPACEChannelOutput::getPaceReturnCode() const -{ - return mPaceReturnCode; -} - - -QByteArray EstablishPACEChannelOutput::getCARcurr() const -{ - return mCarCurr; -} - - -QByteArray EstablishPACEChannelOutput::getCARprev() const -{ - return mCarPrev; -} - - -QByteArray EstablishPACEChannelOutput::getEfCardAccess() const -{ - return mEfCardAccess; -} - - -QByteArray EstablishPACEChannelOutput::getIDicc() const -{ - return mIdIcc; -} - - -void EstablishPACEChannelOutput::setCarCurr(const QByteArray& pCarCurr) -{ - Q_ASSERT(mCarCurr.isNull()); - mCarCurr = pCarCurr; -} - - -void EstablishPACEChannelOutput::setCarPrev(const QByteArray& pCarPrev) -{ - Q_ASSERT(mCarPrev.isNull()); - mCarPrev = pCarPrev; -} - - -QByteArray EstablishPACEChannelOutput::getMseStatusSetAt() const -{ - return mStatusMseSetAt; -} - - -void EstablishPACEChannelOutput::setStatusMseSetAt(const QByteArray& pStatusMseSetAt) -{ - mStatusMseSetAt = pStatusMseSetAt; -} - - -void EstablishPACEChannelOutput::setEfCardAccess(const QByteArray& pEfCardAccess) -{ - Q_ASSERT(mEfCardAccess.isNull()); - mEfCardAccess = pEfCardAccess; -} - - -void EstablishPACEChannelOutput::setIdIcc(const QByteArray& pIDicc) -{ - Q_ASSERT(mIdIcc.isNull()); - mIdIcc = pIDicc; -} - - -void EstablishPACEChannelOutput::setPaceReturnCode(CardReturnCode pPaceReturnCode) -{ - mPaceReturnCode = pPaceReturnCode; -} - - -void EstablishPACEChannelOutput::parse(const QByteArray& pControlOutput, PACE_PASSWORD_ID pPasswordId) -{ - if (pControlOutput.size() < 6) - { - qCWarning(card) << "Output of EstablishPACEChannel has wrong size"; - return; - } - quint32 paceReturnCode = qFromLittleEndian(pControlOutput.data()); - mPaceReturnCode = parseReturnCode(paceReturnCode, pPasswordId); - if (mPaceReturnCode == CardReturnCode::UNKNOWN) - { - mPaceReturnCode = PersoSimWorkaround::parsingEstablishPACEChannelOutput(pControlOutput, pPasswordId); - } - - quint16 dataLength = qFromLittleEndian(pControlOutput.data() + 4); - if (pControlOutput.size() < 6 + dataLength) - { - qCWarning(card) << "Output of EstablishPACEChannel has wrong size"; - return; - } - if (dataLength == 0) - { - qCDebug(card) << "No more data available"; - return; - } - - // Response data according to PC/SC Part 10 amendment 1.1 - quint16 status = qFromBigEndian(pControlOutput.data() + 6); - if (status != StatusCode::SUCCESS) - { - qCWarning(card) << "PACE failed. Status code:" << status; - } - - int it = 8; - mEfCardAccess = readByteArray(pControlOutput, it); - - if (it >= pControlOutput.size()) - { - // in case of managing eSign PIN no CAR or IdICC is contained - qCDebug(card) << "No CAR or IdICC contained"; - return; - } - - mCarCurr = readByteArray(pControlOutput, it); - qCDebug(card) << "mCarCurr:" << mCarCurr; - mCarPrev = readByteArray(pControlOutput, it); - qCDebug(card) << "mCarPrev:" << mCarPrev; - mIdIcc = readByteArray(pControlOutput, it); - qCDebug(card) << "mIdIcc:" << mIdIcc.toHex(); -} - - -QByteArray EstablishPACEChannelOutput::toCcid() const -{ - auto establishPaceChannelOutput = newObject(); - - QByteArray paceReturnCodeBytes; - QDataStream(&paceReturnCodeBytes, QIODevice::WriteOnly) << Enum::getValue(generateReturnCode(mPaceReturnCode)); - establishPaceChannelOutput->mErrorCode = ASN1_OCTET_STRING_new(); - Asn1OctetStringUtil::setValue(paceReturnCodeBytes, establishPaceChannelOutput->mErrorCode); - - establishPaceChannelOutput->mStatusMSESetAt = ASN1_OCTET_STRING_new(); - if (mStatusMseSetAt.isEmpty()) - { - qCWarning(card) << "mStatusMseSetAt is empty! Using 0000 as dummy..."; - Asn1OctetStringUtil::setValue(QByteArray::fromHex(QByteArrayLiteral("0000")), establishPaceChannelOutput->mStatusMSESetAt); - } - else - { - Asn1OctetStringUtil::setValue(mStatusMseSetAt, establishPaceChannelOutput->mStatusMSESetAt); - } - - const uchar* unsignedCharPointer = reinterpret_cast(mEfCardAccess.constData()); - decodeAsn1Object(&establishPaceChannelOutput->mEfCardAccess, &unsignedCharPointer, mEfCardAccess.size()); - - establishPaceChannelOutput->mIdPICC = ASN1_OCTET_STRING_new(); - Asn1OctetStringUtil::setValue(mIdIcc, establishPaceChannelOutput->mIdPICC); - establishPaceChannelOutput->mCurCAR = ASN1_OCTET_STRING_new(); - Asn1OctetStringUtil::setValue(mCarCurr, establishPaceChannelOutput->mCurCAR); - establishPaceChannelOutput->mPrevCAR = ASN1_OCTET_STRING_new(); - Asn1OctetStringUtil::setValue(mCarPrev, establishPaceChannelOutput->mPrevCAR); - - QByteArray ccidOutput = encodeObject(establishPaceChannelOutput.data()); - - QByteArray ccidErrorCode; - QDataStream(&ccidErrorCode, QIODevice::WriteOnly) << Enum::getValue(StatusCode::SUCCESS); - ccidOutput += ccidErrorCode; - - return ccidOutput; -} - - -void EstablishPACEChannelOutput::parseFromCcid(const QByteArray& pOutput, PACE_PASSWORD_ID pPasswordId) -{ - mPaceReturnCode = CardReturnCode::UNKNOWN; - mEfCardAccess.clear(); - mCarCurr.clear(); - mCarPrev.clear(); - mIdIcc.clear(); - mStatusMseSetAt.clear(); - - if (pOutput.size() < 2) - { - qCCritical(card) << "EstablishPACEChannelOutput too short"; - return; - } - qCDebug(card) << "Reader returned " << pOutput.mid(pOutput.size() - 2).toHex(); - - QByteArray outputData = pOutput.mid(0, pOutput.size() - 2); - auto channelOutput = decodeObject(outputData); - if (channelOutput == nullptr) - { - auto outputDataHex = QString::fromLatin1(outputData.toHex()); - qCCritical(card) << "Parsing EstablishPACEChannelOutput failed" << outputDataHex; - - // Try to parse the value of EstablishPACEChannelOutput.errorCode - // the regular expression is determined by the ASN.1 structure of EstablishPACEChannelOutput - - QRegularExpression regExp(QStringLiteral("(.*)a1060404(?([[:xdigit:]]){8})a2040402")); - auto match = regExp.match(outputDataHex); - if (match.hasMatch()) - { - qCWarning(card) << "Determine at least PACE return code by regular expression"; - QByteArray paceReturnCodeBytes = QByteArray::fromHex(match.captured(QStringLiteral("a1")).toUtf8()); - quint32 paceReturnCode; - QDataStream(paceReturnCodeBytes) >> paceReturnCode; - mPaceReturnCode = parseReturnCode(paceReturnCode, pPasswordId); - qCDebug(card) << "mPaceReturnCode:" << mPaceReturnCode << paceReturnCodeBytes.toHex(); - } - return; - } - - QByteArray paceReturnCodeBytes = Asn1OctetStringUtil::getValue(channelOutput->mErrorCode); - quint32 paceReturnCode; - QDataStream(paceReturnCodeBytes) >> paceReturnCode; - mPaceReturnCode = parseReturnCode(paceReturnCode, pPasswordId); - qDebug() << "mPaceReturnCode:" << mPaceReturnCode << paceReturnCodeBytes.toHex(); - - if (channelOutput->mStatusMSESetAt) - { - mStatusMseSetAt = Asn1OctetStringUtil::getValue(channelOutput->mStatusMSESetAt); - qDebug() << "mStatusMseSetAt:" << mStatusMseSetAt.toHex(); - } - - if (channelOutput->mEfCardAccess) - { - mEfCardAccess = encodeObject(channelOutput->mEfCardAccess); - qDebug() << "mEfCardAccess:" << mEfCardAccess.toHex(); - } - - if (channelOutput->mIdPICC != nullptr) - { - mIdIcc = Asn1OctetStringUtil::getValue(channelOutput->mIdPICC); - qDebug() << "mIdIcc:" << mIdIcc.toHex(); - } - - if (channelOutput->mCurCAR != nullptr) - { - mCarCurr = Asn1OctetStringUtil::getValue(channelOutput->mCurCAR); - qDebug() << "mCarCurr:" << mCarCurr; - } - - if (channelOutput->mPrevCAR != nullptr) - { - mCarPrev = Asn1OctetStringUtil::getValue(channelOutput->mPrevCAR); - qDebug() << "mCarPrev:" << mCarPrev; - } -} - - -CardReturnCode EstablishPACEChannelOutput::parseReturnCode(quint32 pPaceReturnCode, PACE_PASSWORD_ID pPasswordId) -{ - // error codes from the reader - switch (EstablishPACEChannelErrorCode(pPaceReturnCode)) - { - case EstablishPACEChannelErrorCode::NoError: - // no error - return CardReturnCode::OK; - - case EstablishPACEChannelErrorCode::InconsistentLengthsInInput: - case EstablishPACEChannelErrorCode::UnexpectedDataInInput: - case EstablishPACEChannelErrorCode::UnexpectedCombinationOfDataInInput: - case EstablishPACEChannelErrorCode::SyntaxErrorInTLVResponse: - case EstablishPACEChannelErrorCode::UnexpectedOrMissingObjectInTLVResponse: - case EstablishPACEChannelErrorCode::UnknownPasswordID: - case EstablishPACEChannelErrorCode::WrongAuthenticationToken: - return CardReturnCode::COMMAND_FAILED; - - // 0xf00663c2 -- invalid PIN? - case EstablishPACEChannelErrorCode::CommunicationAbort: - case EstablishPACEChannelErrorCode::NoCard: - return CardReturnCode::COMMAND_FAILED; - - case EstablishPACEChannelErrorCode::Abort: - return CardReturnCode::CANCELLATION_BY_USER; - - case EstablishPACEChannelErrorCode::Timeout: - return CardReturnCode::INPUT_TIME_OUT; - - default: - break; - } - - // Error codes wrapping error codes from the card. The format is 0xXXXXYYZZ, where XXXX identifies - // the command/step, and YY and ZZ encode the SW1 and SW2 from the response APDU from the card. - switch (pPaceReturnCode & 0xffff0000) - { - case 0xf0000000: // Select EF.CardAccess - case 0xf0010000: // Read Binary EF.CardAccess - case 0xf0020000: // MSE: Set AT - break; - - case 0xf0030000: // General Authenticate Step 1 - case 0xf0040000: // General Authenticate Step 2 - case 0xf0050000: // General Authenticate Step 3 - case 0xf0060000: // General Authenticate Step 4 - if ((pPaceReturnCode & 0xff00) == 0x6300) - { - // SW1 == 0x63 is a warning, which includes incorrectly entered CAN/PIN. For the PIN - // we get SW2 == 0xcX, with X being the number of remaining retries. - switch (pPasswordId) - { - case PACE_PASSWORD_ID::PACE_MRZ: - // No separate error code (yet). - case PACE_PASSWORD_ID::PACE_CAN: - return CardReturnCode::INVALID_CAN; - - case PACE_PASSWORD_ID::PACE_PIN: - return CardReturnCode::INVALID_PIN; - - case PACE_PASSWORD_ID::PACE_PUK: - return CardReturnCode::INVALID_PUK; - } - } - break; - } - - return CardReturnCode::UNKNOWN; -} - - -EstablishPACEChannelErrorCode EstablishPACEChannelOutput::generateReturnCode(CardReturnCode pReturnCode) -{ - switch (pReturnCode) - { - case CardReturnCode::UNKNOWN: - case CardReturnCode::UNDEFINED: - case CardReturnCode::NEW_PIN_MISMATCH: - case CardReturnCode::NEW_PIN_INVALID_LENGTH: - case CardReturnCode::PIN_BLOCKED: - case CardReturnCode::PIN_NOT_BLOCKED: - case CardReturnCode::PUK_INOPERATIVE: - case CardReturnCode::UNEXPECTED_TRANSMIT_STATUS: - case CardReturnCode::PROTOCOL_ERROR: - return EstablishPACEChannelErrorCode::UnexpectedDataInInput; - - case CardReturnCode::INVALID_CAN: - case CardReturnCode::INVALID_PIN: - case CardReturnCode::INVALID_PUK: - return EstablishPACEChannelErrorCode::GeneralAuthenticateStep1_4_Warning; - - case CardReturnCode::OK: - return EstablishPACEChannelErrorCode::NoError; - - case CardReturnCode::CARD_NOT_FOUND: - return EstablishPACEChannelErrorCode::NoCard; - - case CardReturnCode::INPUT_TIME_OUT: - return EstablishPACEChannelErrorCode::Timeout; - - - case CardReturnCode::COMMAND_FAILED: - return EstablishPACEChannelErrorCode::CommunicationAbort; - - case CardReturnCode::CANCELLATION_BY_USER: - return EstablishPACEChannelErrorCode::Abort; - } - - Q_UNREACHABLE(); - return EstablishPACEChannelErrorCode::UnexpectedDataInInput; -} diff --git a/src/card/base/EstablishPACEChannel.h b/src/card/base/EstablishPACEChannel.h deleted file mode 100644 index b452f92..0000000 --- a/src/card/base/EstablishPACEChannel.h +++ /dev/null @@ -1,145 +0,0 @@ -/*! - * \brief Data object for output of card command EstablishPACEChannel - * - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "asn1/ASN1TemplateUtil.h" -#include "asn1/CertificateDescription.h" -#include "asn1/SecurityInfos.h" -#include "Apdu.h" -#include "CardReturnCode.h" -#include "pace/EstablishPACEChannelCode.h" -#include "SmartCardDefinitions.h" - -#include - - -namespace governikus -{ - - -/*! - * As defined in TR-03119: - * - * EstablishPACEChannelInput ::= SEQUENCE { - * passwordID [1] INTEGER - * transmittedPassword [2] NumericString OPTIONAL - * cHAT [3] OCTET STRING OPTIONAL - * certificateDescription [4] CertificateDescription OPTIONAL - * hashOID [5] OBJECT IDENTIFIER OPTIONAL - * } - * CertificateDescription is defined in [TR-03110]. - */ -struct ESTABLISHPACECHANNELINPUT -{ - ASN1_INTEGER* mPasswordID; - ASN1_STRING* mTransmittedPassword; - ASN1_OCTET_STRING* mCHAT; - CertificateDescription* mCertificateDescription; - ASN1_OBJECT* mHashOID; -}; -DECLARE_ASN1_OBJECT(ESTABLISHPACECHANNELINPUT) - - -class EstablishPACEChannelBuilder -{ - private: - PACE_PASSWORD_ID mPasswordId; - QByteArray mChat; - QByteArray mCertificateDescription; - - public: - EstablishPACEChannelBuilder(); - - /** - * Defined in pcsc10_v2.02.08_amd1.1 - */ - QByteArray createCommandData(); - - /** - * Defined in BSI-TR-03119_V1_pdf - */ - CommandApdu createCommandDataCcid(); - - void setCertificateDescription(const QByteArray& pCertificateDescription); - void setChat(const QByteArray& pChat); - void setPasswordId(PACE_PASSWORD_ID pPasswordId); -}; - - -/*! - * As defined in TR-03119: - * - * EstablishPACEChannelOutput ::= SEQUENCE { - * errorCode [1] OCTET STRING (SIZE(4)) - * statusMSESetAT [2] OCTET STRING (SIZE(2)) - * efCardAccess [3] SecurityInfos - * idPICC [4] OCTET STRING OPTIONAL - * curCAR [5] OCTET STRING OPTIONAL - * prevCAR [6] OCTET STRING OPTIONAL - * } - * SecurityInfos is defined in [TR-03110]. - */ -struct ESTABLISHPACECHANNELOUTPUT -{ - ASN1_OCTET_STRING* mErrorCode; - ASN1_OCTET_STRING* mStatusMSESetAt; - securityinfos_st* mEfCardAccess; - ASN1_OCTET_STRING* mIdPICC; - ASN1_OCTET_STRING* mCurCAR; - ASN1_OCTET_STRING* mPrevCAR; -}; -DECLARE_ASN1_OBJECT(ESTABLISHPACECHANNELOUTPUT) - - -class EstablishPACEChannelOutput -{ - private: - CardReturnCode mPaceReturnCode; - QByteArray mEfCardAccess; - QByteArray mCarCurr; - QByteArray mCarPrev; - QByteArray mIdIcc; - QByteArray mStatusMseSetAt; - - public: - EstablishPACEChannelOutput(); - - /** - * Defined in pcsc10_v2.02.08_amd1.1 - */ - void parse(const QByteArray& pControlOutput, PACE_PASSWORD_ID pPasswordId); - - /** - * Defined in TR-03119 - */ - QByteArray toCcid() const; - void parseFromCcid(const QByteArray& pOutput, PACE_PASSWORD_ID pPasswordId); - - CardReturnCode getPaceReturnCode() const; - void setPaceReturnCode(CardReturnCode); - - QByteArray getEfCardAccess() const; - void setEfCardAccess(const QByteArray&); - - QByteArray getIDicc() const; - void setIdIcc(const QByteArray&); - - QByteArray getCARcurr() const; - void setCarCurr(const QByteArray&); - - QByteArray getCARprev() const; - void setCarPrev(const QByteArray&); - - QByteArray getMseStatusSetAt() const; - void setStatusMseSetAt(const QByteArray& pStatusMseSetAt); - - static CardReturnCode parseReturnCode(quint32 pPaceReturnCode, PACE_PASSWORD_ID pPasswordId); - static EstablishPACEChannelErrorCode generateReturnCode(CardReturnCode pReturnCode); -}; - - -} // namespace governikus diff --git a/src/card/base/EstablishPACEChannelParser.cpp b/src/card/base/EstablishPACEChannelParser.cpp deleted file mode 100644 index eaba5e4..0000000 --- a/src/card/base/EstablishPACEChannelParser.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "EstablishPACEChannelParser.h" - -#include "asn1/ASN1TemplateUtil.h" -#include "Apdu.h" - -#include - -Q_DECLARE_LOGGING_CATEGORY(card) - - -using namespace governikus; - - -EstablishPACEChannelParser::EstablishPACEChannelParser(PACE_PASSWORD_ID pPasswordId, const QByteArray& pChat, const QByteArray& pCertificateDescription, const QByteArray& pCommandData) - : mPasswordId(pPasswordId) - , mChat(pChat) - , mCertificateDescription(pCertificateDescription) - , mCommandData(pCommandData) -{ - -} - - -EstablishPACEChannelParser EstablishPACEChannelParser::fromCcid(const QByteArray& pInput) -{ - CommandApdu command(pInput); - - if (command.getCLA() != char(0xFF) - || command.getINS() != char(0x9A) - || command.getP1() != 0x04 - || command.getP2() != 0x02) - { - qCDebug(card) << "Decapsulation of command failed. Unexpected header."; - return EstablishPACEChannelParser(); - } - - QByteArray commandData = command.getData(); - auto channelInput = decodeObject(commandData); - - Q_ASSERT(channelInput); - if (!channelInput) - { - qCDebug(card) << "Decapsulation of command failed. Bad command data."; - return EstablishPACEChannelParser(); - } - - PACE_PASSWORD_ID passwordId = PACE_PASSWORD_ID::PACE_PIN; - Q_ASSERT(channelInput->mPasswordID); - if (channelInput->mPasswordID) - { - char asn1_char = static_cast(ASN1_INTEGER_get(channelInput->mPasswordID)); - if (Enum::isValue(asn1_char)) - { - passwordId = PACE_PASSWORD_ID(asn1_char); - } - else - { - qCDebug(card) << "Decapsulation: Bad PIN ID!"; - Q_ASSERT(false); - } - } - else - { - qCDebug(card) << "Decapsulation: No PIN ID!"; - Q_ASSERT(false); - } - - QByteArray chat; - QByteArray certificateDescription; - // Chat and certificate description are only available in authentications via PIN mode or CAN allowed mode - if (passwordId == PACE_PASSWORD_ID::PACE_PIN || passwordId == PACE_PASSWORD_ID::PACE_CAN) - { - if (channelInput->mCertificateDescription) - { - certificateDescription = channelInput->mCertificateDescription->encode(); - } - else - { - qCDebug(card) << "Decapsulation: No certificate description"; - } - - if (channelInput->mCHAT) - { - chat = Asn1OctetStringUtil::getValue(channelInput->mCHAT); - } - else - { - qCDebug(card) << "Decapsulation: No CHAT"; - } - } - - return EstablishPACEChannelParser(passwordId, chat, certificateDescription, commandData); -} - - -PACE_PASSWORD_ID EstablishPACEChannelParser::getPasswordId() const -{ - return mPasswordId; -} - - -const QByteArray& EstablishPACEChannelParser::getChat() const -{ - return mChat; -} - - -const QByteArray& EstablishPACEChannelParser::getCertificateDescription() const -{ - return mCertificateDescription; -} - - -const QByteArray& EstablishPACEChannelParser::getCommandData() const -{ - return mCommandData; -} diff --git a/src/card/base/EstablishPACEChannelParser.h b/src/card/base/EstablishPACEChannelParser.h deleted file mode 100644 index 6505053..0000000 --- a/src/card/base/EstablishPACEChannelParser.h +++ /dev/null @@ -1,38 +0,0 @@ -/*! - * \brief Parser to decapsulation EstablishPACEChannel - * - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "EstablishPACEChannel.h" - - -namespace governikus -{ - -class EstablishPACEChannelParser -{ - private: - PACE_PASSWORD_ID mPasswordId; - QByteArray mChat; - QByteArray mCertificateDescription; - QByteArray mCommandData; - - EstablishPACEChannelParser(PACE_PASSWORD_ID pPasswordId = PACE_PASSWORD_ID::PACE_PIN, - const QByteArray& pChat = QByteArray(), - const QByteArray& pCertificateDescription = QByteArray(), - const QByteArray& pCommandData = QByteArray()); - - public: - static EstablishPACEChannelParser fromCcid(const QByteArray& pInput); - - PACE_PASSWORD_ID getPasswordId() const; - const QByteArray& getChat() const; - const QByteArray& getCertificateDescription() const; - const QByteArray& getCommandData() const; - -}; - -} diff --git a/src/card/base/EstablishPaceChannel.cpp b/src/card/base/EstablishPaceChannel.cpp new file mode 100644 index 0000000..23dbc58 --- /dev/null +++ b/src/card/base/EstablishPaceChannel.cpp @@ -0,0 +1,158 @@ +/*! + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "EstablishPaceChannel.h" + +#include "asn1/ASN1Util.h" +#include "ResponseApdu.h" + +#include +#include +#include +#include + + +using namespace governikus; + + +Q_DECLARE_LOGGING_CATEGORY(card) + + +namespace +{ +template QByteArray readByteArray(const QByteArray& pInput, int& pOffset) +{ + Q_ASSERT(sizeof(T) < INT_MAX); + + T length = qFromLittleEndian(pInput.data() + pOffset); + pOffset += static_cast(sizeof(T)); + QByteArray result = pInput.mid(pOffset, length); + pOffset += length; + return result; +} + + +} // namespace + + +namespace governikus +{ + +/* + * There is no NUMERICSTRING implementation available in the macro system of OpenSSL, + * so we define it. + */ +ASN1_ITEM_TEMPLATE(NUMERICSTRING) = + ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_IMPTAG, 0x12, NUMERICSTRING, ASN1_OCTET_STRING) +ASN1_ITEM_TEMPLATE_END(NUMERICSTRING) + +ASN1_SEQUENCE(ESTABLISHPACECHANNELINPUT) = { + ASN1_EXP(ESTABLISHPACECHANNELINPUT, mPasswordID, ASN1_INTEGER, 0x01), + ASN1_EXP_OPT(ESTABLISHPACECHANNELINPUT, mTransmittedPassword, NUMERICSTRING, 0x02), + ASN1_EXP_OPT(ESTABLISHPACECHANNELINPUT, mCHAT, ASN1_OCTET_STRING, 0x03), + ASN1_EXP_OPT(ESTABLISHPACECHANNELINPUT, mCertificateDescription, CertificateDescription, 0x04), + ASN1_EXP_OPT(ESTABLISHPACECHANNELINPUT, mHashOID, ASN1_OBJECT, 0x05) +} + + +ASN1_SEQUENCE_END(ESTABLISHPACECHANNELINPUT) +IMPLEMENT_ASN1_FUNCTIONS(ESTABLISHPACECHANNELINPUT) +IMPLEMENT_ASN1_OBJECT(ESTABLISHPACECHANNELINPUT) + + +} // namespace governikus + + +EstablishPaceChannel::EstablishPaceChannel() + : mPasswordId(PacePasswordId::PACE_MRZ) + , mChat(nullptr) + , mCertificateDescription() +{ +} + + +void EstablishPaceChannel::setCertificateDescription(const QByteArray& pCertificateDescription) +{ + mCertificateDescription = pCertificateDescription; +} + + +void EstablishPaceChannel::setChat(const QByteArray& pChat) +{ + mChat = pChat; +} + + +void EstablishPaceChannel::setPasswordId(PacePasswordId pPasswordId) +{ + mPasswordId = pPasswordId; +} + + +QByteArray EstablishPaceChannel::createCommandData() +{ + // Command data according to PC/SC Part 10 amendment 1.1 + static const char INDEX_ESTABLISH_PACE_CHANNEL = 0x02; + + QByteArray inputData; + inputData += static_cast(mPasswordId); + + if (mChat.size() > 0xFF) + { + qCCritical(card) << "Certificate Holder Authorization Template of size > 0xFF not supported"; + Q_ASSERT(mChat.size() <= 0xFF); + return QByteArray(); + } + inputData += static_cast(mChat.size()); + inputData += mChat; + + inputData += '\0'; // length of PIN + + if (mCertificateDescription.size() > 0xFFFF) + { + qCCritical(card) << "Certificate Description of size > 0xFFFF not supported"; + Q_ASSERT(mCertificateDescription.size() <= 0xFFFF); + return QByteArray(); + } + inputData += static_cast((mCertificateDescription.size() >> 0) & 0xff); + inputData += static_cast((mCertificateDescription.size() >> 8) & 0xff); + inputData += mCertificateDescription; + + QByteArray commandData; + commandData += (INDEX_ESTABLISH_PACE_CHANNEL); + + if (inputData.size() > 0xFFFF) + { + qCCritical(card) << "InputData of size > 0xFFFF not supported"; + Q_ASSERT(inputData.size() <= 0xFFFF); + return QByteArray(); + } + commandData += static_cast((inputData.size() >> 0) & 0xff); + commandData += static_cast((inputData.size() >> 8) & 0xff); + commandData += inputData; + return commandData; +} + + +CommandApdu EstablishPaceChannel::createCommandDataCcid() +{ + auto channelInput = newObject(); + + ASN1_INTEGER_set(channelInput->mPasswordID, static_cast(mPasswordId)); + if (!mChat.isNull()) + { + channelInput->mCHAT = ASN1_OCTET_STRING_new(); + Asn1OctetStringUtil::setValue(mChat, channelInput->mCHAT); + } + if (!mCertificateDescription.isEmpty()) + { + const uchar* unsignedCharPointer = reinterpret_cast(mCertificateDescription.constData()); + decodeAsn1Object(&channelInput->mCertificateDescription, &unsignedCharPointer, mCertificateDescription.size()); + } + + QByteArray data = encodeObject(channelInput.data()); + + // boxing command according to TR-03119 + return CommandApdu(char(0xFF), char(0x9A), 0x04, 0x02, data, CommandApdu::SHORT_MAX_LE); +} diff --git a/src/card/base/EstablishPaceChannel.h b/src/card/base/EstablishPaceChannel.h new file mode 100644 index 0000000..a2020dc --- /dev/null +++ b/src/card/base/EstablishPaceChannel.h @@ -0,0 +1,69 @@ +/*! + * \brief Data object for output of card command EstablishPaceChannel + * + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "asn1/ASN1TemplateUtil.h" +#include "asn1/CertificateDescription.h" +#include "asn1/SecurityInfos.h" +#include "CommandApdu.h" +#include "SmartCardDefinitions.h" + +#include + + +namespace governikus +{ + +/*! + * As defined in TR-03119: + * + * EstablishPaceChannelInput ::= SEQUENCE { + * passwordID [1] INTEGER + * transmittedPassword [2] NumericString OPTIONAL + * cHAT [3] OCTET STRING OPTIONAL + * certificateDescription [4] CertificateDescription OPTIONAL + * hashOID [5] OBJECT IDENTIFIER OPTIONAL + * } + * CertificateDescription is defined in [TR-03110]. + */ +struct ESTABLISHPACECHANNELINPUT +{ + ASN1_INTEGER* mPasswordID; + ASN1_STRING* mTransmittedPassword; + ASN1_OCTET_STRING* mCHAT; + CertificateDescription* mCertificateDescription; + ASN1_OBJECT* mHashOID; +}; +DECLARE_ASN1_OBJECT(ESTABLISHPACECHANNELINPUT) + + +class EstablishPaceChannel +{ + private: + PacePasswordId mPasswordId; + QByteArray mChat; + QByteArray mCertificateDescription; + + public: + EstablishPaceChannel(); + + /** + * Defined in pcsc10_v2.02.08_amd1.1 + */ + QByteArray createCommandData(); + + /** + * Defined in BSI-TR-03119_V1_pdf + */ + CommandApdu createCommandDataCcid(); + + void setCertificateDescription(const QByteArray& pCertificateDescription); + void setChat(const QByteArray& pChat); + void setPasswordId(PacePasswordId pPasswordId); +}; + +} // namespace governikus diff --git a/src/card/base/EstablishPaceChannelOutput.cpp b/src/card/base/EstablishPaceChannelOutput.cpp new file mode 100644 index 0000000..0dd9311 --- /dev/null +++ b/src/card/base/EstablishPaceChannelOutput.cpp @@ -0,0 +1,429 @@ +/*! + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "EstablishPaceChannelOutput.h" + +#include "ResponseApdu.h" + +#include +#include +#include +#include + + +using namespace governikus; + + +Q_DECLARE_LOGGING_CATEGORY(card) + + +namespace +{ +template QByteArray readByteArray(const QByteArray& pInput, int& pOffset) +{ + Q_ASSERT(sizeof(T) < INT_MAX); + + T length = qFromLittleEndian(pInput.data() + pOffset); + pOffset += static_cast(sizeof(T)); + QByteArray result = pInput.mid(pOffset, length); + pOffset += length; + return result; +} + + +} // namespace + + +namespace governikus +{ + +ASN1_SEQUENCE(ESTABLISHPACECHANNELOUTPUT) = { + ASN1_EXP(ESTABLISHPACECHANNELOUTPUT, mErrorCode, ASN1_OCTET_STRING, 0x01), + ASN1_EXP(ESTABLISHPACECHANNELOUTPUT, mStatusMSESetAt, ASN1_OCTET_STRING, 0x02), + ASN1_EXP(ESTABLISHPACECHANNELOUTPUT, mEfCardAccess, securityinfos_st, 0x03), + ASN1_EXP_OPT(ESTABLISHPACECHANNELOUTPUT, mIdPICC, ASN1_OCTET_STRING, 0x04), + ASN1_EXP_OPT(ESTABLISHPACECHANNELOUTPUT, mCurCAR, ASN1_OCTET_STRING, 0x05), + ASN1_EXP_OPT(ESTABLISHPACECHANNELOUTPUT, mPrevCAR, ASN1_OCTET_STRING, 0x06) +} + + +ASN1_SEQUENCE_END(ESTABLISHPACECHANNELOUTPUT) +IMPLEMENT_ASN1_FUNCTIONS(ESTABLISHPACECHANNELOUTPUT) +IMPLEMENT_ASN1_OBJECT(ESTABLISHPACECHANNELOUTPUT) + +} // namespace governikus + + +EstablishPaceChannelOutput::EstablishPaceChannelOutput() + : mPaceReturnCode(CardReturnCode::UNKNOWN) + , mEfCardAccess() + , mCarCurr() + , mCarPrev() + , mIdIcc() + , mStatusMseSetAt() +{ +} + + +CardReturnCode EstablishPaceChannelOutput::getPaceReturnCode() const +{ + return mPaceReturnCode; +} + + +QByteArray EstablishPaceChannelOutput::getCARcurr() const +{ + return mCarCurr; +} + + +QByteArray EstablishPaceChannelOutput::getCARprev() const +{ + return mCarPrev; +} + + +QByteArray EstablishPaceChannelOutput::getEfCardAccess() const +{ + return mEfCardAccess; +} + + +QByteArray EstablishPaceChannelOutput::getIDicc() const +{ + return mIdIcc; +} + + +void EstablishPaceChannelOutput::setCarCurr(const QByteArray& pCarCurr) +{ + Q_ASSERT(mCarCurr.isNull()); + mCarCurr = pCarCurr; +} + + +void EstablishPaceChannelOutput::setCarPrev(const QByteArray& pCarPrev) +{ + Q_ASSERT(mCarPrev.isNull()); + mCarPrev = pCarPrev; +} + + +QByteArray EstablishPaceChannelOutput::getMseStatusSetAt() const +{ + return mStatusMseSetAt; +} + + +void EstablishPaceChannelOutput::setStatusMseSetAt(const QByteArray& pStatusMseSetAt) +{ + mStatusMseSetAt = pStatusMseSetAt; +} + + +void EstablishPaceChannelOutput::setEfCardAccess(const QByteArray& pEfCardAccess) +{ + Q_ASSERT(mEfCardAccess.isNull()); + mEfCardAccess = pEfCardAccess; +} + + +void EstablishPaceChannelOutput::setIdIcc(const QByteArray& pIDicc) +{ + Q_ASSERT(mIdIcc.isNull()); + mIdIcc = pIDicc; +} + + +void EstablishPaceChannelOutput::setPaceReturnCode(CardReturnCode pPaceReturnCode) +{ + mPaceReturnCode = pPaceReturnCode; +} + + +void EstablishPaceChannelOutput::parse(const QByteArray& pControlOutput, PacePasswordId pPasswordId) +{ + if (pControlOutput.size() < 6) + { + qCWarning(card) << "Output of EstablishPaceChannel has wrong size"; + return; + } + quint32 paceReturnCode = qFromLittleEndian(pControlOutput.data()); + mPaceReturnCode = parseReturnCode(paceReturnCode, pPasswordId); + + quint16 dataLength = qFromLittleEndian(pControlOutput.data() + 4); + if (pControlOutput.size() < 6 + dataLength) + { + qCWarning(card) << "Output of EstablishPaceChannel has wrong size"; + return; + } + if (dataLength == 0) + { + qCDebug(card) << "No more data available"; + return; + } + + // Response data according to PC/SC Part 10 amendment 1.1 + quint16 status = qFromBigEndian(pControlOutput.data() + 6); + if (status != StatusCode::SUCCESS) + { + qCWarning(card) << "PACE failed. Status code:" << status; + } + + int it = 8; + mEfCardAccess = readByteArray(pControlOutput, it); + + if (it >= pControlOutput.size()) + { + // in case of managing eSign PIN no CAR or IdICC is contained + qCDebug(card) << "No CAR or IdICC contained"; + return; + } + + mCarCurr = readByteArray(pControlOutput, it); + qCDebug(card) << "mCarCurr:" << mCarCurr; + mCarPrev = readByteArray(pControlOutput, it); + qCDebug(card) << "mCarPrev:" << mCarPrev; + mIdIcc = readByteArray(pControlOutput, it); + qCDebug(card) << "mIdIcc:" << mIdIcc.toHex(); +} + + +QByteArray EstablishPaceChannelOutput::toCcid() const +{ + auto establishPaceChannelOutput = newObject(); + + QByteArray paceReturnCodeBytes; + QDataStream(&paceReturnCodeBytes, QIODevice::WriteOnly) << Enum::getValue(generateReturnCode(mPaceReturnCode)); + establishPaceChannelOutput->mErrorCode = ASN1_OCTET_STRING_new(); + Asn1OctetStringUtil::setValue(paceReturnCodeBytes, establishPaceChannelOutput->mErrorCode); + + establishPaceChannelOutput->mStatusMSESetAt = ASN1_OCTET_STRING_new(); + if (mStatusMseSetAt.isEmpty()) + { + qCWarning(card) << "mStatusMseSetAt is empty! Using 0000 as dummy..."; + Asn1OctetStringUtil::setValue(QByteArray::fromHex(QByteArrayLiteral("0000")), establishPaceChannelOutput->mStatusMSESetAt); + } + else + { + Asn1OctetStringUtil::setValue(mStatusMseSetAt, establishPaceChannelOutput->mStatusMSESetAt); + } + + const uchar* unsignedCharPointer = reinterpret_cast(mEfCardAccess.constData()); + decodeAsn1Object(&establishPaceChannelOutput->mEfCardAccess, &unsignedCharPointer, mEfCardAccess.size()); + + establishPaceChannelOutput->mIdPICC = ASN1_OCTET_STRING_new(); + Asn1OctetStringUtil::setValue(mIdIcc, establishPaceChannelOutput->mIdPICC); + establishPaceChannelOutput->mCurCAR = ASN1_OCTET_STRING_new(); + Asn1OctetStringUtil::setValue(mCarCurr, establishPaceChannelOutput->mCurCAR); + establishPaceChannelOutput->mPrevCAR = ASN1_OCTET_STRING_new(); + Asn1OctetStringUtil::setValue(mCarPrev, establishPaceChannelOutput->mPrevCAR); + + QByteArray ccidOutput = encodeObject(establishPaceChannelOutput.data()); + + QByteArray ccidErrorCode; + QDataStream(&ccidErrorCode, QIODevice::WriteOnly) << Enum::getValue(StatusCode::SUCCESS); + ccidOutput += ccidErrorCode; + + return ccidOutput; +} + + +void EstablishPaceChannelOutput::parseFromCcid(const QByteArray& pOutput, PacePasswordId pPasswordId) +{ + mPaceReturnCode = CardReturnCode::UNKNOWN; + mEfCardAccess.clear(); + mCarCurr.clear(); + mCarPrev.clear(); + mIdIcc.clear(); + mStatusMseSetAt.clear(); + + if (pOutput.size() < 2) + { + qCCritical(card) << "EstablishPaceChannelOutput too short"; + return; + } + qCDebug(card) << "Reader returned:" << pOutput.mid(pOutput.size() - 2).toHex(); + + QByteArray outputData = pOutput.mid(0, pOutput.size() - 2); + auto channelOutput = decodeObject(outputData); + if (channelOutput == nullptr) + { + auto outputDataHex = QString::fromLatin1(outputData.toHex()); + qCCritical(card) << "Parsing EstablishPaceChannelOutput failed" << outputDataHex; + + // Try to parse the value of EstablishPaceChannelOutput.errorCode + // the regular expression is determined by the ASN.1 structure of EstablishPaceChannelOutput + + QRegularExpression regExp(QStringLiteral("(.*)a1060404(?([[:xdigit:]]){8})a2040402")); + auto match = regExp.match(outputDataHex); + if (match.hasMatch()) + { + qCWarning(card) << "Determine at least PACE return code by regular expression"; + QByteArray paceReturnCodeBytes = QByteArray::fromHex(match.captured(QStringLiteral("a1")).toUtf8()); + quint32 paceReturnCode; + QDataStream(paceReturnCodeBytes) >> paceReturnCode; + mPaceReturnCode = parseReturnCode(paceReturnCode, pPasswordId); + qCDebug(card) << "mPaceReturnCode:" << mPaceReturnCode << paceReturnCodeBytes.toHex(); + } + return; + } + + QByteArray paceReturnCodeBytes = Asn1OctetStringUtil::getValue(channelOutput->mErrorCode); + quint32 paceReturnCode; + QDataStream(paceReturnCodeBytes) >> paceReturnCode; + mPaceReturnCode = parseReturnCode(paceReturnCode, pPasswordId); + qDebug() << "mPaceReturnCode:" << mPaceReturnCode << paceReturnCodeBytes.toHex(); + + if (channelOutput->mStatusMSESetAt) + { + mStatusMseSetAt = Asn1OctetStringUtil::getValue(channelOutput->mStatusMSESetAt); + qDebug() << "mStatusMseSetAt:" << mStatusMseSetAt.toHex(); + } + + if (channelOutput->mEfCardAccess) + { + mEfCardAccess = encodeObject(channelOutput->mEfCardAccess); + qDebug() << "mEfCardAccess:" << mEfCardAccess.toHex(); + } + + if (channelOutput->mIdPICC != nullptr) + { + mIdIcc = Asn1OctetStringUtil::getValue(channelOutput->mIdPICC); + qDebug() << "mIdIcc:" << mIdIcc.toHex(); + } + + if (channelOutput->mCurCAR != nullptr) + { + mCarCurr = Asn1OctetStringUtil::getValue(channelOutput->mCurCAR); + qDebug() << "mCarCurr:" << mCarCurr; + } + + if (channelOutput->mPrevCAR != nullptr) + { + mCarPrev = Asn1OctetStringUtil::getValue(channelOutput->mPrevCAR); + qDebug() << "mCarPrev:" << mCarPrev; + } +} + + +CardReturnCode EstablishPaceChannelOutput::parseReturnCode(quint32 pPaceReturnCode, PacePasswordId pPasswordId) +{ + // error codes from the reader + switch (EstablishPaceChannelErrorCode(pPaceReturnCode)) + { + case EstablishPaceChannelErrorCode::NoError: + // no error + return CardReturnCode::OK; + + case EstablishPaceChannelErrorCode::InconsistentLengthsInInput: + case EstablishPaceChannelErrorCode::UnexpectedDataInInput: + case EstablishPaceChannelErrorCode::UnexpectedCombinationOfDataInInput: + case EstablishPaceChannelErrorCode::SyntaxErrorInTLVResponse: + case EstablishPaceChannelErrorCode::UnexpectedOrMissingObjectInTLVResponse: + case EstablishPaceChannelErrorCode::UnknownPasswordID: + case EstablishPaceChannelErrorCode::WrongAuthenticationToken: + return CardReturnCode::COMMAND_FAILED; + + // 0xf00663c2 -- invalid PIN? + case EstablishPaceChannelErrorCode::CommunicationAbort: + return CardReturnCode::COMMAND_FAILED; + + case EstablishPaceChannelErrorCode::NoCard: + return CardReturnCode::CARD_NOT_FOUND; + + case EstablishPaceChannelErrorCode::Abort: + return CardReturnCode::CANCELLATION_BY_USER; + + case EstablishPaceChannelErrorCode::Timeout: + return CardReturnCode::INPUT_TIME_OUT; + + default: + break; + } + + // Error codes wrapping error codes from the card. The format is 0xXXXXYYZZ, where XXXX identifies + // the command/step, and YY and ZZ encode the SW1 and SW2 from the response APDU from the card. + switch (pPaceReturnCode & 0xffff0000) + { + case 0xf0000000: // Select EF.CardAccess + case 0xf0010000: // Read Binary EF.CardAccess + case 0xf0020000: // MSE: Set AT + break; + + case 0xf0030000: // General Authenticate Step 1 + case 0xf0040000: // General Authenticate Step 2 + case 0xf0050000: // General Authenticate Step 3 + case 0xf0060000: // General Authenticate Step 4 + if ((pPaceReturnCode & 0xff00) == 0x6300) + { + // SW1 == 0x63 is a warning, which includes incorrectly entered CAN/PIN. For the PIN + // we get SW2 == 0xcX, with X being the number of remaining retries. + switch (pPasswordId) + { + case PacePasswordId::PACE_MRZ: + // No separate error code (yet). + case PacePasswordId::PACE_CAN: + return CardReturnCode::INVALID_CAN; + + case PacePasswordId::PACE_PIN: + return CardReturnCode::INVALID_PIN; + + case PacePasswordId::PACE_PUK: + return CardReturnCode::INVALID_PUK; + + case PacePasswordId::UNKNOWN: + return CardReturnCode::UNKNOWN; + } + } + break; + } + + return CardReturnCode::UNKNOWN; +} + + +EstablishPaceChannelErrorCode EstablishPaceChannelOutput::generateReturnCode(CardReturnCode pReturnCode) +{ + switch (pReturnCode) + { + case CardReturnCode::UNKNOWN: + case CardReturnCode::UNDEFINED: + case CardReturnCode::NEW_PIN_MISMATCH: + case CardReturnCode::NEW_PIN_INVALID_LENGTH: + case CardReturnCode::PIN_BLOCKED: + case CardReturnCode::PIN_NOT_BLOCKED: + case CardReturnCode::PUK_INOPERATIVE: + case CardReturnCode::UNEXPECTED_TRANSMIT_STATUS: + case CardReturnCode::PROTOCOL_ERROR: + return EstablishPaceChannelErrorCode::UnexpectedDataInInput; + + case CardReturnCode::INVALID_CAN: + case CardReturnCode::INVALID_PIN: + case CardReturnCode::INVALID_PIN_2: + case CardReturnCode::INVALID_PIN_3: + case CardReturnCode::INVALID_PUK: + return EstablishPaceChannelErrorCode::GeneralAuthenticateStep1_4_Warning; + + case CardReturnCode::OK: + case CardReturnCode::OK_PUK: + return EstablishPaceChannelErrorCode::NoError; + + case CardReturnCode::CARD_NOT_FOUND: + case CardReturnCode::RETRY_ALLOWED: + return EstablishPaceChannelErrorCode::NoCard; + + case CardReturnCode::INPUT_TIME_OUT: + return EstablishPaceChannelErrorCode::Timeout; + + case CardReturnCode::COMMAND_FAILED: + return EstablishPaceChannelErrorCode::CommunicationAbort; + + case CardReturnCode::CANCELLATION_BY_USER: + return EstablishPaceChannelErrorCode::Abort; + } + + Q_UNREACHABLE(); + return EstablishPaceChannelErrorCode::UnexpectedDataInInput; +} diff --git a/src/card/base/EstablishPaceChannelOutput.h b/src/card/base/EstablishPaceChannelOutput.h new file mode 100644 index 0000000..c235c9c --- /dev/null +++ b/src/card/base/EstablishPaceChannelOutput.h @@ -0,0 +1,94 @@ +/*! + * \brief Data object for output of card command EstablishPaceChannel + * + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "asn1/CertificateDescription.h" +#include "asn1/SecurityInfos.h" +#include "CardReturnCode.h" +#include "pace/EstablishPaceChannelCode.h" +#include "SmartCardDefinitions.h" + + +#include + + +namespace governikus +{ + +/*! + * As defined in TR-03119: + * + * EstablishPaceChannelOutput ::= SEQUENCE { + * errorCode [1] OCTET STRING (SIZE(4)) + * statusMSESetAT [2] OCTET STRING (SIZE(2)) + * efCardAccess [3] SecurityInfos + * idPICC [4] OCTET STRING OPTIONAL + * curCAR [5] OCTET STRING OPTIONAL + * prevCAR [6] OCTET STRING OPTIONAL + * } + * SecurityInfos is defined in [TR-03110]. + */ +struct ESTABLISHPACECHANNELOUTPUT +{ + ASN1_OCTET_STRING* mErrorCode; + ASN1_OCTET_STRING* mStatusMSESetAt; + securityinfos_st* mEfCardAccess; + ASN1_OCTET_STRING* mIdPICC; + ASN1_OCTET_STRING* mCurCAR; + ASN1_OCTET_STRING* mPrevCAR; +}; +DECLARE_ASN1_OBJECT(ESTABLISHPACECHANNELOUTPUT) + + +class EstablishPaceChannelOutput +{ + private: + CardReturnCode mPaceReturnCode; + QByteArray mEfCardAccess; + QByteArray mCarCurr; + QByteArray mCarPrev; + QByteArray mIdIcc; + QByteArray mStatusMseSetAt; + + public: + EstablishPaceChannelOutput(); + + /** + * Defined in pcsc10_v2.02.08_amd1.1 + */ + void parse(const QByteArray& pControlOutput, PacePasswordId pPasswordId); + + /** + * Defined in TR-03119 + */ + QByteArray toCcid() const; + void parseFromCcid(const QByteArray& pOutput, PacePasswordId pPasswordId); + + CardReturnCode getPaceReturnCode() const; + void setPaceReturnCode(CardReturnCode); + + QByteArray getEfCardAccess() const; + void setEfCardAccess(const QByteArray&); + + QByteArray getIDicc() const; + void setIdIcc(const QByteArray&); + + QByteArray getCARcurr() const; + void setCarCurr(const QByteArray&); + + QByteArray getCARprev() const; + void setCarPrev(const QByteArray&); + + QByteArray getMseStatusSetAt() const; + void setStatusMseSetAt(const QByteArray& pStatusMseSetAt); + + static CardReturnCode parseReturnCode(quint32 pPaceReturnCode, PacePasswordId pPasswordId); + static EstablishPaceChannelErrorCode generateReturnCode(CardReturnCode pReturnCode); +}; + + +} // namespace governikus diff --git a/src/card/base/EstablishPaceChannelParser.cpp b/src/card/base/EstablishPaceChannelParser.cpp new file mode 100644 index 0000000..5a02d33 --- /dev/null +++ b/src/card/base/EstablishPaceChannelParser.cpp @@ -0,0 +1,120 @@ +/*! + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "EstablishPaceChannelParser.h" + +#include "asn1/ASN1TemplateUtil.h" +#include "Apdu.h" + +#include + +Q_DECLARE_LOGGING_CATEGORY(card) + + +using namespace governikus; + + +EstablishPaceChannelParser::EstablishPaceChannelParser(PacePasswordId pPasswordId, const QByteArray& pChat, const QByteArray& pCertificateDescription, const QByteArray& pCommandData) + : mPasswordId(pPasswordId) + , mChat(pChat) + , mCertificateDescription(pCertificateDescription) + , mCommandData(pCommandData) +{ +} + + +EstablishPaceChannelParser EstablishPaceChannelParser::fromCcid(const QByteArray& pInput) +{ + CommandApdu command(pInput); + + if (command.getCLA() != char(0xFF) + || command.getINS() != char(0x9A) + || command.getP1() != 0x04 + || command.getP2() != 0x02) + { + qCDebug(card) << "Decapsulation of command failed. Unexpected header."; + return EstablishPaceChannelParser(); + } + + QByteArray commandData = command.getData(); + auto channelInput = decodeObject(commandData); + + Q_ASSERT(channelInput); + if (!channelInput) + { + qCDebug(card) << "Decapsulation of command failed. Bad command data."; + return EstablishPaceChannelParser(); + } + + PacePasswordId passwordId = PacePasswordId::PACE_PIN; + Q_ASSERT(channelInput->mPasswordID); + if (channelInput->mPasswordID) + { + char asn1_char = static_cast(ASN1_INTEGER_get(channelInput->mPasswordID)); + if (Enum::isValue(asn1_char)) + { + passwordId = PacePasswordId(asn1_char); + } + else + { + qCDebug(card) << "Decapsulation: Bad PIN ID!"; + Q_ASSERT(false); + } + } + else + { + qCDebug(card) << "Decapsulation: No PIN ID!"; + Q_ASSERT(false); + } + + QByteArray chat; + QByteArray certificateDescription; + // Chat and certificate description are only available in authentications via PIN mode or CAN allowed mode + if (passwordId == PacePasswordId::PACE_PIN || passwordId == PacePasswordId::PACE_CAN) + { + if (channelInput->mCertificateDescription) + { + certificateDescription = channelInput->mCertificateDescription->encode(); + } + else + { + qCDebug(card) << "Decapsulation: No certificate description"; + } + + if (channelInput->mCHAT) + { + chat = Asn1OctetStringUtil::getValue(channelInput->mCHAT); + } + else + { + qCDebug(card) << "Decapsulation: No CHAT"; + } + } + + return EstablishPaceChannelParser(passwordId, chat, certificateDescription, commandData); +} + + +PacePasswordId EstablishPaceChannelParser::getPasswordId() const +{ + return mPasswordId; +} + + +const QByteArray& EstablishPaceChannelParser::getChat() const +{ + return mChat; +} + + +const QByteArray& EstablishPaceChannelParser::getCertificateDescription() const +{ + return mCertificateDescription; +} + + +const QByteArray& EstablishPaceChannelParser::getCommandData() const +{ + return mCommandData; +} diff --git a/src/card/base/EstablishPaceChannelParser.h b/src/card/base/EstablishPaceChannelParser.h new file mode 100644 index 0000000..6661394 --- /dev/null +++ b/src/card/base/EstablishPaceChannelParser.h @@ -0,0 +1,38 @@ +/*! + * \brief Parser to decapsulation EstablishPACEChannel + * + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "EstablishPaceChannel.h" + + +namespace governikus +{ + +class EstablishPaceChannelParser +{ + private: + PacePasswordId mPasswordId; + QByteArray mChat; + QByteArray mCertificateDescription; + QByteArray mCommandData; + + EstablishPaceChannelParser(PacePasswordId pPasswordId = PacePasswordId::PACE_PIN, + const QByteArray& pChat = QByteArray(), + const QByteArray& pCertificateDescription = QByteArray(), + const QByteArray& pCommandData = QByteArray()); + + public: + static EstablishPaceChannelParser fromCcid(const QByteArray& pInput); + + PacePasswordId getPasswordId() const; + const QByteArray& getChat() const; + const QByteArray& getCertificateDescription() const; + const QByteArray& getCommandData() const; + +}; + +} // namespace governikus diff --git a/src/card/base/FileRef.h b/src/card/base/FileRef.h index bf984ac..b0178df 100644 --- a/src/card/base/FileRef.h +++ b/src/card/base/FileRef.h @@ -26,4 +26,4 @@ struct FileRef }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/GeneralAuthenticateResponse.cpp b/src/card/base/GeneralAuthenticateResponse.cpp index 6c64344..ea17a43 100644 --- a/src/card/base/GeneralAuthenticateResponse.cpp +++ b/src/card/base/GeneralAuthenticateResponse.cpp @@ -20,14 +20,17 @@ GAResponseApdu::GAResponseApdu() } -GAResponseApdu::~GAResponseApdu() -{ -} - - void GAResponseApdu::setBuffer(const QByteArray& pBuffer) { ResponseApdu::setBuffer(pBuffer); + + StatusCode statusCode = getReturnCode(); + if (statusCode != StatusCode::SUCCESS) + { + qCCritical(card) << "Avoid parsing of the general authentication data because of StatusCode" << statusCode; + return; + } + parseDynamicAuthenticationData(getData()); } @@ -71,11 +74,6 @@ GAEncryptedNonceResponse::GAEncryptedNonceResponse() } -GAEncryptedNonceResponse::~GAEncryptedNonceResponse() -{ -} - - const QByteArray& GAEncryptedNonceResponse::getEncryptedNonce() { return mEncryptedNonce; @@ -121,11 +119,6 @@ GAMapNonceResponse::GAMapNonceResponse() } -GAMapNonceResponse::~GAMapNonceResponse() -{ -} - - const QByteArray& GAMapNonceResponse::getMappingData() { return mMappingData; @@ -171,11 +164,6 @@ GAPerformKeyAgreementResponse::GAPerformKeyAgreementResponse() } -GAPerformKeyAgreementResponse::~GAPerformKeyAgreementResponse() -{ -} - - const QByteArray& GAPerformKeyAgreementResponse::getEphemeralPublicKey() { return mEphemeralPublicKey; @@ -230,11 +218,6 @@ GAMutualAuthenticationResponse::GAMutualAuthenticationResponse() } -GAMutualAuthenticationResponse::~GAMutualAuthenticationResponse() -{ -} - - const QByteArray& GAMutualAuthenticationResponse::getAuthenticationToken() { return mAuthenticationToken; @@ -295,11 +278,6 @@ GAChipAuthenticationResponse::GAChipAuthenticationResponse() } -GAChipAuthenticationResponse::~GAChipAuthenticationResponse() -{ -} - - const QByteArray& GAChipAuthenticationResponse::getNonce() { return mNonce; diff --git a/src/card/base/GeneralAuthenticateResponse.h b/src/card/base/GeneralAuthenticateResponse.h index ad81771..8b269a1 100644 --- a/src/card/base/GeneralAuthenticateResponse.h +++ b/src/card/base/GeneralAuthenticateResponse.h @@ -7,13 +7,12 @@ #pragma once #include "asn1/ASN1TemplateUtil.h" -#include "Apdu.h" +#include "ResponseApdu.h" namespace governikus { - class GAResponseApdu : public ResponseApdu { @@ -22,7 +21,7 @@ class GAResponseApdu public: GAResponseApdu(); - virtual ~GAResponseApdu() override; + virtual ~GAResponseApdu() override = default; virtual void setBuffer(const QByteArray& pBuffer) override; }; @@ -50,7 +49,7 @@ class GAEncryptedNonceResponse public: GAEncryptedNonceResponse(); - virtual ~GAEncryptedNonceResponse() override; + virtual ~GAEncryptedNonceResponse() override = default; const QByteArray& getEncryptedNonce(); }; @@ -78,7 +77,7 @@ class GAMapNonceResponse public: GAMapNonceResponse(); - virtual ~GAMapNonceResponse() override; + virtual ~GAMapNonceResponse() override = default; const QByteArray& getMappingData(); }; @@ -106,7 +105,7 @@ class GAPerformKeyAgreementResponse public: GAPerformKeyAgreementResponse(); - virtual ~GAPerformKeyAgreementResponse() override; + virtual ~GAPerformKeyAgreementResponse() override = default; const QByteArray& getEphemeralPublicKey(); }; @@ -138,7 +137,7 @@ class GAMutualAuthenticationResponse public: GAMutualAuthenticationResponse(); - virtual ~GAMutualAuthenticationResponse() override; + virtual ~GAMutualAuthenticationResponse() override = default; const QByteArray& getAuthenticationToken(); const QByteArray& getCarCurr(); const QByteArray& getCarPrev(); @@ -170,7 +169,7 @@ class GAChipAuthenticationResponse public: GAChipAuthenticationResponse(); - virtual ~GAChipAuthenticationResponse() override; + virtual ~GAChipAuthenticationResponse() override = default; const QByteArray& getNonce(); const QByteArray& getAuthenticationToken(); diff --git a/src/card/base/InputAPDUInfo.cpp b/src/card/base/InputAPDUInfo.cpp index 3a3fcd2..1826e37 100644 --- a/src/card/base/InputAPDUInfo.cpp +++ b/src/card/base/InputAPDUInfo.cpp @@ -9,14 +9,12 @@ using namespace governikus; InputAPDUInfo::InputAPDUInfo() : mInputApdu() , mAcceptableStatusCodes() - , mUpdateRetryCounter(false) { } -InputAPDUInfo::InputAPDUInfo(const QByteArray& pInputApdu, bool pUpdateRetryCounter) +InputAPDUInfo::InputAPDUInfo(const QByteArray& pInputApdu) : mInputApdu(pInputApdu) , mAcceptableStatusCodes() - , mUpdateRetryCounter(pUpdateRetryCounter) { } diff --git a/src/card/base/InputAPDUInfo.h b/src/card/base/InputAPDUInfo.h index 4029be1..5f24ef2 100644 --- a/src/card/base/InputAPDUInfo.h +++ b/src/card/base/InputAPDUInfo.h @@ -4,7 +4,7 @@ * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany */ -#include "Apdu.h" +#include "CommandApdu.h" #include #include @@ -18,7 +18,7 @@ class InputAPDUInfo { public: InputAPDUInfo(); - InputAPDUInfo(const QByteArray& pInputApdu, bool pUpdateRetryCounter = false); + InputAPDUInfo(const QByteArray& pInputApdu); bool isValid() const @@ -29,7 +29,7 @@ class InputAPDUInfo const CommandApdu getInputApdu() const { - return CommandApdu(mInputApdu, mUpdateRetryCounter); + return CommandApdu(mInputApdu); } @@ -54,10 +54,6 @@ class InputAPDUInfo private: QByteArray mInputApdu; QByteArrayList mAcceptableStatusCodes; - // mUpdateRetryCounter is not part of the xml data. - // We use it internally to update the retry counter on a - // low level especially when we act as a remote card reader - bool mUpdateRetryCounter; }; -} +} // namespace governikus diff --git a/src/card/base/PersoSimWorkaround.h b/src/card/base/PersoSimWorkaround.h index eaafc13..6b7414f 100644 --- a/src/card/base/PersoSimWorkaround.h +++ b/src/card/base/PersoSimWorkaround.h @@ -17,8 +17,7 @@ namespace governikus class PersoSimWorkaround { private: - PersoSimWorkaround(); - ~PersoSimWorkaround(); + PersoSimWorkaround() = delete; Q_DISABLE_COPY(PersoSimWorkaround) public: @@ -35,36 +34,21 @@ class PersoSimWorkaround { ResponseApdu response; const CardReturnCode returnCode = pCardConnectionWorker->transmit(SelectBuilder(FileRef::efCardAccess()).build(), response); - return (returnCode == CardReturnCode::COMMAND_FAILED && response.getReturnCode() != StatusCode::EMPTY) ? CardReturnCode::OK : returnCode; - } + if (response.getReturnCode() == StatusCode::EMPTY) + { + return CardReturnCode::RETRY_ALLOWED; + } - /* - * The PersoSim engine sends the result bytes in the control output of PACE in little endian. - * So we try to parse it that way, if we get unknown return codes. - * - * As soon as PersoSim is fixed in that point, we will remove the workaround. - */ - static CardReturnCode parsingEstablishPACEChannelOutput(const QByteArray& pControlOutput, PACE_PASSWORD_ID pPasswordId) - { - quint32 paceReturnCode; - QDataStream(pControlOutput.mid(0, 4)) >> paceReturnCode; - return EstablishPACEChannelOutput::parseReturnCode(paceReturnCode, pPasswordId); - } + if (returnCode == CardReturnCode::COMMAND_FAILED) + { + return CardReturnCode::OK; + } - - /* - * The PersoSim engine sends 6A80 on wrong CAN entry in PACE. This means "Invalid data" which is - * wrong. We interpret it as wrong CAN - * - * As soon as PersoSim is fixed in that point, we will remove the workaround. - */ - static bool isWrongCanEntry(QSharedPointer pResponseApdu) - { - return pResponseApdu->getReturnCode() == StatusCode::INVALID_DATAFIELD; + return returnCode; } }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/PinModify.h b/src/card/base/PinModify.h index 2150223..b3f83fc 100644 --- a/src/card/base/PinModify.h +++ b/src/card/base/PinModify.h @@ -4,7 +4,7 @@ #pragma once -#include "Apdu.h" +#include "CommandApdu.h" #include @@ -36,4 +36,4 @@ class PinModify CommandApdu createCcidForBluetooth() const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/PinModifyOutput.h b/src/card/base/PinModifyOutput.h index ed279b9..3b1dea5 100644 --- a/src/card/base/PinModifyOutput.h +++ b/src/card/base/PinModifyOutput.h @@ -4,8 +4,8 @@ #pragma once -#include "Apdu.h" #include "CardReturnCode.h" +#include "ResponseApdu.h" #include @@ -35,4 +35,4 @@ class PinModifyOutput }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/Reader.cpp b/src/card/base/Reader.cpp index 64520b2..773b19f 100644 --- a/src/card/base/Reader.cpp +++ b/src/card/base/Reader.cpp @@ -3,9 +3,8 @@ */ -#include "asn1/PACEInfo.h" +#include "asn1/PaceInfo.h" #include "CardConnectionWorker.h" -#include "PersoSimWorkaround.h" #include "Reader.h" #include @@ -26,29 +25,12 @@ Reader::Reader(ReaderManagerPlugInType pPlugInType, const QString& pReaderName) } -Reader::~Reader() -{ -} - - void Reader::setPukInoperative() { mReaderInfo.mCardInfo.mPukInoperative = true; } -void Reader::setRetryCounter(int pRetryCounter) -{ - if (mReaderInfo.getRetryCounter() != pRetryCounter) - { - qCInfo(support) << "retry counter updated:" << pRetryCounter << ", was:" << mReaderInfo.getRetryCounter(); - - mReaderInfo.mCardInfo.mRetryCounter = pRetryCounter; - Q_EMIT fireCardRetryCounterChanged(mReaderInfo.getName()); - } -} - - QSharedPointer Reader::createCardConnectionWorker() { Card* currentCard = getCard(); @@ -123,32 +105,30 @@ CardReturnCode Reader::getRetryCounter(QSharedPointer pCar } // we don't need to establish PACE with this protocol (i.e. we don't need to support it), so we just take the fist one - const auto& paceInfo = mReaderInfo.getCardInfo().getEfCardAccess()->getPACEInfos().at(0); + const auto& paceInfo = mReaderInfo.getCardInfo().getEfCardAccess()->getPaceInfos().at(0); QByteArray cryptographicMechanismReference = paceInfo->getProtocolValueBytes(); QByteArray referencePrivateKey = paceInfo->getParameterId(); - CardReturnCode returnCode = PersoSimWorkaround::sendingMseSetAt(pCardConnectionWorker); - if (returnCode != CardReturnCode::OK) - { - qCCritical(card) << "Error on MSE:Set AT"; - return returnCode; - } - // MSE:Set AT MSEBuilder mseBuilder(MSEBuilder::P1::PERFORM_SECURITY_OPERATION, MSEBuilder::P2::SET_AT); mseBuilder.setOid(cryptographicMechanismReference); - mseBuilder.setPublicKey(PACE_PASSWORD_ID::PACE_PIN); + mseBuilder.setPublicKey(PacePasswordId::PACE_PIN); mseBuilder.setPrivateKey(referencePrivateKey); ResponseApdu mseSetAtResponse; - returnCode = pCardConnectionWorker->transmit(mseBuilder.build(), mseSetAtResponse); + CardReturnCode returnCode = pCardConnectionWorker->transmit(mseBuilder.build(), mseSetAtResponse); if (returnCode != CardReturnCode::OK) { return returnCode; } - StatusCode statusCode = mseSetAtResponse.getReturnCode(); - qCDebug(card) << "StatusCode: " << statusCode; + const StatusCode statusCode = mseSetAtResponse.getReturnCode(); + qCDebug(card) << "StatusCode:" << statusCode; + if (statusCode == StatusCode::INVALID) + { + return CardReturnCode::COMMAND_FAILED; + } + pRetryCounter = mseSetAtResponse.getRetryCounter(); pPinDeactivated = statusCode == StatusCode::PIN_DEACTIVATED; @@ -174,8 +154,3 @@ void Reader::fireUpdateSignal(CardEvent pCardEvent) break; } } - - -ConnectableReader::~ConnectableReader() -{ -} diff --git a/src/card/base/Reader.h b/src/card/base/Reader.h index f275a22..d782d86 100644 --- a/src/card/base/Reader.h +++ b/src/card/base/Reader.h @@ -5,19 +5,17 @@ #pragma once #include "Card.h" -#include "DeviceError.h" #include "ReaderInfo.h" #include #include #include +class test_Reader; namespace governikus { -class CardConnectionWorker; - class Reader : public QObject { @@ -34,12 +32,8 @@ class Reader void timerEvent(QTimerEvent* pEvent) override; - /*! - * Periodically called to perform an update of the readers and cards state. - */ - void update(); - private: + friend class ::test_Reader; virtual CardEvent updateCard() = 0; CardReturnCode getRetryCounter(QSharedPointer pCardConnectionWorker, int& pRetryCounter, bool& pPinDeactivated); @@ -48,7 +42,12 @@ class Reader public: Reader(ReaderManagerPlugInType pPlugInType, const QString& pReaderName); - virtual ~Reader() override; + virtual ~Reader() override = default; + + /*! + * Periodically called to perform an update of the readers and cards state. + */ + void update(); const QString& getName() const { @@ -62,9 +61,6 @@ class Reader } - void setRetryCounter(int pRetryCounter); - - virtual Card* getCard() const = 0; void setPukInoperative(); @@ -82,7 +78,7 @@ class Reader void fireCardRemoved(const QString& pReaderName); void fireCardRetryCounterChanged(const QString& pReaderName); void fireReaderPropertiesUpdated(const QString& pReaderName); - void fireReaderDeviceError(DeviceError pDeviceError); + void fireReaderDeviceError(GlobalStatus::Code pErrorCode); }; @@ -95,10 +91,10 @@ class ConnectableReader public: using Reader::Reader; - virtual ~ConnectableReader(); + virtual ~ConnectableReader() override = default; virtual void connectReader() = 0; virtual void disconnectReader() = 0; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/ReaderFilter.cpp b/src/card/base/ReaderFilter.cpp index cec2dd1..074400c 100644 --- a/src/card/base/ReaderFilter.cpp +++ b/src/card/base/ReaderFilter.cpp @@ -4,7 +4,6 @@ #include "ReaderFilter.h" -#include "EnumHelper.h" #include "ReaderConfiguration.h" #include "ReaderManagerPlugIn.h" @@ -14,7 +13,6 @@ ReaderFilter::ReaderFilter() : mFilterType(NoFilter) , mPluginTypes() { - } @@ -22,7 +20,6 @@ ReaderFilter::ReaderFilter(const QVector& pPluginTypes) : mFilterType(PluginTypeFilter) , mPluginTypes(pPluginTypes) { - } @@ -30,7 +27,6 @@ ReaderFilter::ReaderFilter(const ReaderFilter::FilterType pFilterType) : mFilterType(pFilterType) , mPluginTypes() { - } diff --git a/src/card/base/ReaderFilter.h b/src/card/base/ReaderFilter.h index 73df75c..03e8ea5 100644 --- a/src/card/base/ReaderFilter.h +++ b/src/card/base/ReaderFilter.h @@ -41,7 +41,6 @@ class ReaderFilter QVector apply(const QVector& pInputList) const; }; - -} +} // namespace governikus Q_DECLARE_OPERATORS_FOR_FLAGS(governikus::ReaderFilter::FilterTypes) diff --git a/src/card/base/ReaderInfo.cpp b/src/card/base/ReaderInfo.cpp index 66eae81..347a3b4 100644 --- a/src/card/base/ReaderInfo.cpp +++ b/src/card/base/ReaderInfo.cpp @@ -4,7 +4,6 @@ #include "ReaderInfo.h" -#include "Env.h" #include "Initializer.h" #include "ReaderDetector.h" @@ -19,10 +18,15 @@ ReaderInfo::ReaderInfo(const QString& pName, const CardInfo& pCardInfo) : mPlugInType(pPlugInType) , mName(pName) - , mReaderConfigurationInfo(Env::getSingleton()->getReaderConfigurationInfo(pName)) , mBasicReader(true) , mCardInfo(pCardInfo) , mConnected(false) , mMaxApduLength(pPlugInType == ReaderManagerPlugInType::NFC ? 0 : 500) { } + + +ReaderConfigurationInfo ReaderInfo::getReaderConfigurationInfo() const +{ + return Env::getSingleton()->getReaderConfigurationInfo(mName); +} diff --git a/src/card/base/ReaderInfo.h b/src/card/base/ReaderInfo.h index e55a75c..1fbf46a 100644 --- a/src/card/base/ReaderInfo.h +++ b/src/card/base/ReaderInfo.h @@ -5,7 +5,6 @@ #pragma once #include "CardInfo.h" -#include "EnumHelper.h" #include "ReaderConfigurationInfo.h" #include "ReaderManagerPlugInInfo.h" #include "SmartCardDefinitions.h" @@ -20,7 +19,6 @@ class ReaderInfo ReaderManagerPlugInType mPlugInType; QString mName; - ReaderConfigurationInfo mReaderConfigurationInfo; bool mBasicReader; CardInfo mCardInfo; bool mConnected; @@ -31,6 +29,8 @@ class ReaderInfo ReaderManagerPlugInType pPlugInType = ReaderManagerPlugInType::UNKNOWN, const CardInfo& pCardInfo = CardInfo(CardType::NONE)); + ReaderConfigurationInfo getReaderConfigurationInfo() const; + ReaderManagerPlugInType getPlugInType() const { @@ -38,12 +38,6 @@ class ReaderInfo } - const ReaderConfigurationInfo& getReaderConfigurationInfo() const - { - return mReaderConfigurationInfo; - } - - const CardInfo& getCardInfo() const { return mCardInfo; @@ -152,4 +146,4 @@ class ReaderInfo }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/ReaderManager.cpp b/src/card/base/ReaderManager.cpp index 5d4a429..94cf339 100644 --- a/src/card/base/ReaderManager.cpp +++ b/src/card/base/ReaderManager.cpp @@ -4,7 +4,6 @@ #include "ReaderManager.h" -#include "CardConnection.h" #include "SingletonHelper.h" #include @@ -18,9 +17,9 @@ Q_DECLARE_LOGGING_CATEGORY(card) ReaderManager::ReaderManager() : QObject() + , mMutex() , mThread() , mWorker() - , mRemoteClient() { mThread.setObjectName(QStringLiteral("ReaderManagerThread")); } @@ -28,11 +27,16 @@ ReaderManager::ReaderManager() ReaderManager::~ReaderManager() { - if (mThread.isRunning()) { - qCWarning(card) << "ReaderManager is not stopped correctly..."; - shutdown(); + const QMutexLocker mutexLocker(&mMutex); + if (!mThread.isRunning()) + { + return; + } } + + qCWarning(card) << "ReaderManager is not stopped correctly..."; + shutdown(); } @@ -42,8 +46,10 @@ ReaderManager& ReaderManager::getInstance() } -void ReaderManager::init(const QSharedPointer& pRemoteClient) +void ReaderManager::init() { + const QMutexLocker mutexLocker(&mMutex); + if (mThread.isRunning()) { qCWarning(card) << "ReaderManager already initialized"; @@ -52,8 +58,7 @@ void ReaderManager::init(const QSharedPointer& pRemoteClient) if (mWorker.isNull()) { - mRemoteClient = pRemoteClient; - mWorker = new ReaderManagerWorker(pRemoteClient); + mWorker = new ReaderManagerWorker(); mWorker->moveToThread(&mThread); connect(&mThread, &QThread::started, mWorker.data(), &ReaderManagerWorker::onThreadStarted); @@ -83,26 +88,32 @@ void ReaderManager::init(const QSharedPointer& pRemoteClient) void ReaderManager::shutdown() { + const QMutexLocker mutexLocker(&mMutex); + if (mThread.isRunning() && !mThread.isInterruptionRequested()) { qCDebug(card) << "Shutdown ReaderManager..."; mThread.requestInterruption(); // do not try to stop AGAIN from dtor mThread.quit(); - mThread.wait(2500); - qCDebug(card) << "Stopping..." << mThread.isRunning(); + mThread.wait(5000); + qCDebug(card).noquote() << mThread.objectName() << "stopped:" << !mThread.isRunning(); } } void ReaderManager::startScan(ReaderManagerPlugInType pType, bool pAutoConnect) { + const QMutexLocker mutexLocker(&mMutex); + if (!mThread.isRunning()) { qCWarning(card) << "Cannot start scan if ReaderManager-Thread is not active"; return; } - QMetaObject::invokeMethod(mWorker.data(), "startScan", Qt::QueuedConnection, Q_ARG(ReaderManagerPlugInType, pType), Q_ARG(bool, pAutoConnect)); + QMetaObject::invokeMethod(mWorker.data(), [ = ] { + mWorker->startScan(pType, pAutoConnect); + }, Qt::QueuedConnection); } @@ -117,13 +128,17 @@ void ReaderManager::startScanAll(bool pAutoConnect) void ReaderManager::stopScan(ReaderManagerPlugInType pType) { + const QMutexLocker mutexLocker(&mMutex); + if (!mThread.isRunning()) { qCWarning(card) << "Cannot stop scan if ReaderManager-Thread is not active"; return; } - QMetaObject::invokeMethod(mWorker.data(), "stopScan", Qt::QueuedConnection, Q_ARG(ReaderManagerPlugInType, pType)); + QMetaObject::invokeMethod(mWorker.data(), [ = ] { + mWorker->stopScan(pType); + }, Qt::QueuedConnection); } @@ -138,53 +153,83 @@ void ReaderManager::stopScanAll() QVector ReaderManager::getPlugInInfos() const { + const QMutexLocker mutexLocker(&mMutex); + QVector list; - QMetaObject::invokeMethod(mWorker.data(), "getPlugInInfos", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QVector, list)); + QMetaObject::invokeMethod(mWorker.data(), &ReaderManagerWorker::getPlugInInfos, Qt::BlockingQueuedConnection, &list); return list; } QVector ReaderManager::getReaderInfos(ReaderManagerPlugInType pType) const { - return getReaderInfos(QVector() << pType); + return getReaderInfos(QVector {pType}); } QVector ReaderManager::getReaderInfos(const ReaderFilter& pFilter) const { + const QMutexLocker mutexLocker(&mMutex); + QVector list; - QMetaObject::invokeMethod(mWorker.data(), "getReaderInfos", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QVector, list), Q_ARG(ReaderFilter, pFilter)); - return list; + QMetaObject::invokeMethod(mWorker.data(), [ = ] { + return mWorker->getReaderInfos(pFilter); + }, Qt::BlockingQueuedConnection, &list); + return pFilter.apply(list); } ReaderInfo ReaderManager::getReaderInfo(const QString& pReaderName) const { + const QMutexLocker mutexLocker(&mMutex); + ReaderInfo info(pReaderName); - QMetaObject::invokeMethod(mWorker.data(), "getReaderInfo", Qt::BlockingQueuedConnection, Q_RETURN_ARG(ReaderInfo, info), Q_ARG(QString, pReaderName)); + QMetaObject::invokeMethod(mWorker.data(), [ = ] { + return mWorker->getReaderInfo(pReaderName); + }, Qt::BlockingQueuedConnection, &info); return info; } +void ReaderManager::updateReaderInfo(const QString& pReaderName) +{ + QMetaObject::invokeMethod(mWorker.data(), [ = ] { + mWorker->updateReaderInfo(pReaderName); + }, Qt::BlockingQueuedConnection); // needed to force the ReaderInfo update, else StateMachine loops based on stale state can occur +} + + void ReaderManager::connectReader(const QString& pReaderName) { - QMetaObject::invokeMethod(mWorker.data(), "connectReader", Qt::QueuedConnection, Q_ARG(QString, pReaderName)); + const QMutexLocker mutexLocker(&mMutex); + + QMetaObject::invokeMethod(mWorker.data(), [ = ] { + mWorker->connectReader(pReaderName); + }, Qt::QueuedConnection); } void ReaderManager::disconnectReader(const QString& pReaderName) { - QMetaObject::invokeMethod(mWorker.data(), "disconnectReader", Qt::QueuedConnection, Q_ARG(QString, pReaderName)); + const QMutexLocker mutexLocker(&mMutex); + + QMetaObject::invokeMethod(mWorker.data(), [ = ] { + mWorker->disconnectReader(pReaderName); + }, Qt::QueuedConnection); } void ReaderManager::disconnectAllReaders() { - QMetaObject::invokeMethod(mWorker.data(), "disconnectAllReaders", Qt::QueuedConnection); + const QMutexLocker mutexLocker(&mMutex); + + QMetaObject::invokeMethod(mWorker.data(), &ReaderManagerWorker::disconnectAllReaders, Qt::QueuedConnection); } -QSharedPointer ReaderManager::getRemoteClient() +void ReaderManager::updateRetryCounters() { - return mRemoteClient; + const QMutexLocker mutexLocker(&mMutex); + + QMetaObject::invokeMethod(mWorker.data(), &ReaderManagerWorker::updateRetryCounters, Qt::QueuedConnection); } diff --git a/src/card/base/ReaderManager.h b/src/card/base/ReaderManager.h index 8d839ec..6883253 100644 --- a/src/card/base/ReaderManager.h +++ b/src/card/base/ReaderManager.h @@ -5,39 +5,40 @@ #pragma once #include "command/CreateCardConnectionCommand.h" -#include "DeviceError.h" +#include "Env.h" #include "Reader.h" #include "ReaderManagerWorker.h" -#include "RemoteClient.h" +#include #include #include + namespace governikus { - class ReaderManager : public QObject + , private Env::ThreadSafe { Q_OBJECT + friend class Env; private: + mutable QMutex mMutex; QThread mThread; QPointer mWorker; - QSharedPointer mRemoteClient; protected: ReaderManager(); ~ReaderManager(); - - public: static ReaderManager& getInstance(); + public: /*! * Initialize the reader manager service. * The thread is started and the plug-ins are initialized, too. */ - void init(const QSharedPointer& pRemoteClient = QSharedPointer()); + void init(); /*! * Starts a scan for all device types. @@ -65,6 +66,7 @@ class ReaderManager QVector getReaderInfos(ReaderManagerPlugInType pType) const; virtual QVector getReaderInfos(const ReaderFilter& pFilter = ReaderFilter()) const; ReaderInfo getReaderInfo(const QString& pReaderName) const; + void updateReaderInfo(const QString& pReaderName); /*! * Executes a command to create a CardConnection for a specified reader. @@ -79,7 +81,7 @@ class ReaderManager QMetaObject::Connection connection = connect(command, &CreateCardConnectionCommand::fireCommandDone, pReceiver, pSlot, Qt::UniqueConnection); if (connection) { - QMetaObject::invokeMethod(command, "execute", Qt::QueuedConnection); + command->run(); } else { @@ -94,15 +96,14 @@ class ReaderManager void connectReader(const QString& pReaderName); void disconnectReader(const QString& pReaderName); void disconnectAllReaders(); - - QSharedPointer getRemoteClient(); + void updateRetryCounters(); Q_SIGNALS: void firePluginAdded(const ReaderManagerPlugInInfo& pInfo); void fireStatusChanged(const ReaderManagerPlugInInfo& pInfo); void fireReaderAdded(const QString& pReaderName); void fireReaderRemoved(const QString& pReaderName); - void fireReaderDeviceError(DeviceError pDeviceError); + void fireReaderDeviceError(GlobalStatus::Code pError); void fireReaderPropertiesUpdated(const QString& pReaderName); void fireCardInserted(const QString& pReaderName); void fireCardRemoved(const QString& pReaderName); @@ -118,4 +119,4 @@ class ReaderManager void shutdown(); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/ReaderManagerPlugIn.cpp b/src/card/base/ReaderManagerPlugIn.cpp index bf1424c..5233f91 100644 --- a/src/card/base/ReaderManagerPlugIn.cpp +++ b/src/card/base/ReaderManagerPlugIn.cpp @@ -7,38 +7,19 @@ using namespace governikus; -void ReaderManagerPlugIn::onConnectToKnownReadersChanged() -{ -} - - ReaderManagerPlugIn::ReaderManagerPlugIn(ReaderManagerPlugInType pPlugInType, bool pAvailable, bool pPlugInEnabled) : mInfo(pPlugInType, pPlugInEnabled, pAvailable) - , mScanInProgress(false) - , mConnectToKnownReaders(false) { } -void ReaderManagerPlugIn::startScan() +void ReaderManagerPlugIn::startScan(bool /*pAutoConnect*/) { - mScanInProgress = true; } void ReaderManagerPlugIn::stopScan() { - mScanInProgress = false; -} - - -void ReaderManagerPlugIn::setConnectToKnownReaders(bool pConnectToKnownReaders) -{ - if (mConnectToKnownReaders != pConnectToKnownReaders) - { - mConnectToKnownReaders = pConnectToKnownReaders; - onConnectToKnownReadersChanged(); - } } diff --git a/src/card/base/ReaderManagerPlugIn.h b/src/card/base/ReaderManagerPlugIn.h index 60d3e91..973f11c 100644 --- a/src/card/base/ReaderManagerPlugIn.h +++ b/src/card/base/ReaderManagerPlugIn.h @@ -7,9 +7,8 @@ #pragma once -#include "DeviceError.h" +#include "GlobalStatus.h" #include "ReaderManagerPlugInInfo.h" -#include "RemoteDispatcher.h" #include #include @@ -18,7 +17,6 @@ namespace governikus { class Reader; -class RemoteClient; class ReaderManagerPlugIn : public QObject @@ -27,11 +25,6 @@ class ReaderManagerPlugIn ReaderManagerPlugInInfo mInfo; protected: - bool mScanInProgress; - bool mConnectToKnownReaders; - - virtual void onConnectToKnownReadersChanged(); - void setReaderInfoEnabled(bool pEnabled) { if (mInfo.isEnabled() != pEnabled) @@ -81,7 +74,7 @@ class ReaderManagerPlugIn virtual void init() { - Q_ASSERT(thread() == QThread::currentThread()); + Q_ASSERT(QObject::thread() == QThread::currentThread()); } @@ -90,28 +83,20 @@ class ReaderManagerPlugIn } - virtual void startScan(); + virtual void startScan(bool pAutoConnect); virtual void stopScan(); - void setConnectToKnownReaders(bool pConnectToKnownReaders); - - virtual void setRemoteClient(const QSharedPointer& pRemoteClient) - { - Q_UNUSED(pRemoteClient); - } - - Q_SIGNALS: void fireStatusChanged(const ReaderManagerPlugInInfo& pInfo); void fireReaderAdded(const QString& pReaderName); void fireReaderRemoved(const QString& pReaderName); - void fireReaderDeviceError(DeviceError pDeviceError); + void fireReaderDeviceError(GlobalStatus::Code pError); void fireCardInserted(const QString& pReaderName); void fireCardRemoved(const QString& pReaderName); void fireCardRetryCounterChanged(const QString& pReaderName); void fireReaderPropertiesUpdated(const QString& pReaderName); }; -} /* namespace governikus */ +} // namespace governikus Q_DECLARE_INTERFACE(governikus::ReaderManagerPlugIn, "governikus.ReaderManagerPlugIn") diff --git a/src/card/base/ReaderManagerPlugInInfo.h b/src/card/base/ReaderManagerPlugInInfo.h index 40fc3fd..cfd16a7 100644 --- a/src/card/base/ReaderManagerPlugInInfo.h +++ b/src/card/base/ReaderManagerPlugInInfo.h @@ -15,7 +15,6 @@ namespace governikus { - defineEnumType(ReaderManagerPlugInType, UNKNOWN, PCSC, BLUETOOTH, NFC, REMOTE) @@ -106,4 +105,4 @@ class ReaderManagerPlugInInfo bool mResponding; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/ReaderManagerWorker.cpp b/src/card/base/ReaderManagerWorker.cpp index bbcd20c..fbb0716 100644 --- a/src/card/base/ReaderManagerWorker.cpp +++ b/src/card/base/ReaderManagerWorker.cpp @@ -6,7 +6,6 @@ #include "Initializer.h" #include "Reader.h" -#include "RemoteClient.h" #include #include @@ -21,9 +20,8 @@ static Initializer::Entry X([] { }); -ReaderManagerWorker::ReaderManagerWorker(const QSharedPointer& pRemoteClient) +ReaderManagerWorker::ReaderManagerWorker() : QObject() - , mRemoteClient(pRemoteClient) , mPlugIns() { } @@ -31,7 +29,7 @@ ReaderManagerWorker::ReaderManagerWorker(const QSharedPointer& pRe ReaderManagerWorker::~ReaderManagerWorker() { - Q_ASSERT(thread() == QThread::currentThread()); + Q_ASSERT(QObject::thread() == QThread::currentThread()); for (auto& plugin : qAsConst(mPlugIns)) { @@ -43,7 +41,7 @@ ReaderManagerWorker::~ReaderManagerWorker() void ReaderManagerWorker::onThreadStarted() { - Q_ASSERT(thread() == QThread::currentThread()); + Q_ASSERT(QObject::thread() == QThread::currentThread()); qCDebug(card) << "Thread started"; registerPlugIns(); @@ -70,7 +68,6 @@ void ReaderManagerWorker::registerPlugIns() { registerPlugIn(pluginInstance); pluginInstance->init(); - pluginInstance->setRemoteClient(mRemoteClient); Q_EMIT firePluginAdded(pluginInstance->getInfo()); } @@ -90,7 +87,7 @@ void ReaderManagerWorker::registerPlugIn(ReaderManagerPlugIn* pPlugIn) Q_ASSERT(pPlugIn != nullptr); Q_ASSERT(!mPlugIns.contains(pPlugIn)); - mPlugIns.push_back(pPlugIn); + mPlugIns << pPlugIn; connect(pPlugIn, &ReaderManagerPlugIn::fireReaderAdded, this, &ReaderManagerWorker::fireReaderAdded); connect(pPlugIn, &ReaderManagerPlugIn::fireReaderRemoved, this, &ReaderManagerWorker::fireReaderRemoved); @@ -105,15 +102,14 @@ void ReaderManagerWorker::registerPlugIn(ReaderManagerPlugIn* pPlugIn) void ReaderManagerWorker::startScan(ReaderManagerPlugInType pType, bool pAutoConnect) { - Q_ASSERT(thread() == QThread::currentThread()); + Q_ASSERT(QObject::thread() == QThread::currentThread()); for (auto& plugin : qAsConst(mPlugIns)) { if (plugin->getInfo().getPlugInType() == pType) { qCDebug(card) << "Start scan on plugin:" << plugin->metaObject()->className(); - plugin->setConnectToKnownReaders(pAutoConnect); - plugin->startScan(); + plugin->startScan(pAutoConnect); } } } @@ -121,14 +117,13 @@ void ReaderManagerWorker::startScan(ReaderManagerPlugInType pType, bool pAutoCon void ReaderManagerWorker::stopScan(ReaderManagerPlugInType pType) { - Q_ASSERT(thread() == QThread::currentThread()); + Q_ASSERT(QObject::thread() == QThread::currentThread()); for (auto& plugin : qAsConst(mPlugIns)) { if (plugin->getInfo().getPlugInType() == pType) { qCDebug(card) << "Stop scan on plugin:" << plugin->metaObject()->className(); - plugin->setConnectToKnownReaders(false); plugin->stopScan(); } } @@ -137,7 +132,7 @@ void ReaderManagerWorker::stopScan(ReaderManagerPlugInType pType) QVector ReaderManagerWorker::getPlugInInfos() const { - Q_ASSERT(thread() == QThread::currentThread()); + Q_ASSERT(QObject::thread() == QThread::currentThread()); QVector infos; infos.reserve(mPlugIns.size()); @@ -152,7 +147,7 @@ QVector ReaderManagerWorker::getPlugInInfos() const QVector ReaderManagerWorker::getReaderInfos(const ReaderFilter& pFilter) const { - Q_ASSERT(thread() == QThread::currentThread()); + Q_ASSERT(QObject::thread() == QThread::currentThread()); QVector list; const QVector& plugIns = pFilter.apply(mPlugIns); @@ -164,22 +159,36 @@ QVector ReaderManagerWorker::getReaderInfos(const ReaderFilter& pFil list += reader->getReaderInfo(); } } - return pFilter.apply(list); + return list; } ReaderInfo ReaderManagerWorker::getReaderInfo(const QString& pReaderName) const { - Q_ASSERT(thread() == QThread::currentThread()); + Q_ASSERT(QObject::thread() == QThread::currentThread()); const Reader* reader = getReader(pReaderName); return reader ? reader->getReaderInfo() : ReaderInfo(pReaderName); } +void ReaderManagerWorker::updateReaderInfo(const QString& pReaderName) +{ + Q_ASSERT(QObject::thread() == QThread::currentThread()); + + Reader* reader = getReader(pReaderName); + if (!reader) + { + qCWarning(card) << "Requested reader does not exist:" << pReaderName; + return; + } + reader->update(); +} + + Reader* ReaderManagerWorker::getReader(const QString& pReaderName) const { - Q_ASSERT(thread() == QThread::currentThread()); + Q_ASSERT(QObject::thread() == QThread::currentThread()); for (auto& plugin : qAsConst(mPlugIns)) { @@ -200,7 +209,7 @@ Reader* ReaderManagerWorker::getReader(const QString& pReaderName) const void ReaderManagerWorker::createCardConnectionWorker(const QString& pReaderName) { - Q_ASSERT(thread() == QThread::currentThread()); + Q_ASSERT(QObject::thread() == QThread::currentThread()); QSharedPointer worker; if (auto reader = getReader(pReaderName)) @@ -213,7 +222,7 @@ void ReaderManagerWorker::createCardConnectionWorker(const QString& pReaderName) void ReaderManagerWorker::connectReader(const QString& pReaderName) { - Q_ASSERT(thread() == QThread::currentThread()); + Q_ASSERT(QObject::thread() == QThread::currentThread()); if (ConnectableReader* reader = qobject_cast(getReader(pReaderName))) { @@ -224,7 +233,7 @@ void ReaderManagerWorker::connectReader(const QString& pReaderName) void ReaderManagerWorker::disconnectReader(const QString& pReaderName) { - Q_ASSERT(thread() == QThread::currentThread()); + Q_ASSERT(QObject::thread() == QThread::currentThread()); if (ConnectableReader* reader = qobject_cast(getReader(pReaderName))) { @@ -235,10 +244,30 @@ void ReaderManagerWorker::disconnectReader(const QString& pReaderName) void ReaderManagerWorker::disconnectAllReaders() { - Q_ASSERT(thread() == QThread::currentThread()); + Q_ASSERT(QObject::thread() == QThread::currentThread()); for (auto& info : getReaderInfos()) { disconnectReader(info.getName()); } } + + +void ReaderManagerWorker::updateRetryCounters() +{ + Q_ASSERT(QObject::thread() == QThread::currentThread()); + + const auto& readerInfos = getReaderInfos(); + for (const auto& readerInfo : readerInfos) + { + QSharedPointer worker; + if (const auto& reader = getReader(readerInfo.getName())) + { + worker = reader->createCardConnectionWorker(); + if (worker) + { + worker->updateRetryCounter(); + } + } + } +} diff --git a/src/card/base/ReaderManagerWorker.h b/src/card/base/ReaderManagerWorker.h index 485454b..13232bb 100644 --- a/src/card/base/ReaderManagerWorker.h +++ b/src/card/base/ReaderManagerWorker.h @@ -7,7 +7,6 @@ #pragma once #include "CardConnectionWorker.h" -#include "DeviceError.h" #include "ReaderFilter.h" #include "ReaderInfo.h" #include "ReaderManagerPlugIn.h" @@ -17,15 +16,12 @@ namespace governikus { -class RemoteClient; - class ReaderManagerWorker : public QObject { Q_OBJECT private: - const QSharedPointer mRemoteClient; QVector mPlugIns; void registerPlugIns(); @@ -34,7 +30,7 @@ class ReaderManagerWorker Reader* getReader(const QString& pReaderName) const; public: - ReaderManagerWorker(const QSharedPointer& pRemoteClient); + ReaderManagerWorker(); ~ReaderManagerWorker(); Q_INVOKABLE void startScan(ReaderManagerPlugInType pType, bool pAutoConnect); @@ -43,9 +39,11 @@ class ReaderManagerWorker Q_INVOKABLE QVector getPlugInInfos() const; Q_INVOKABLE QVector getReaderInfos(const ReaderFilter& pFilter = ReaderFilter()) const; Q_INVOKABLE ReaderInfo getReaderInfo(const QString& pReaderName) const; + Q_INVOKABLE void updateReaderInfo(const QString& pReaderName); Q_INVOKABLE void createCardConnectionWorker(const QString& pReaderName); Q_INVOKABLE void connectReader(const QString& pReaderName); Q_INVOKABLE void disconnectReader(const QString& pReaderName); + Q_INVOKABLE void updateRetryCounters(); Q_INVOKABLE void disconnectAllReaders(); Q_SIGNALS: @@ -53,7 +51,7 @@ class ReaderManagerWorker void fireStatusChanged(const ReaderManagerPlugInInfo& pInfo); void fireReaderAdded(const QString& pReaderName); void fireReaderRemoved(const QString& pReaderName); - void fireReaderDeviceError(DeviceError pDeviceError); + void fireReaderDeviceError(GlobalStatus::Code pError); void fireReaderPropertiesUpdated(const QString& pReaderName); void fireCardInserted(const QString& pReaderName); void fireCardRemoved(const QString& pReaderName); @@ -65,4 +63,4 @@ class ReaderManagerWorker void onThreadStarted(); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/RemoteClient.h b/src/card/base/RemoteClient.h deleted file mode 100644 index a108af4..0000000 --- a/src/card/base/RemoteClient.h +++ /dev/null @@ -1,54 +0,0 @@ -/*! - * \brief An interface for RemoteClientImpl, meant to omit the - * dependency between card_base and remote_device. - * - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "GlobalStatus.h" -#include "RemoteServiceSettings.h" - -#include -#include - -namespace governikus -{ - -class RemoteDispatcher; -class RemoteDeviceListEntry; - -class RemoteClient - : public QObject -{ - Q_OBJECT - - Q_SIGNALS: - void fireDeviceAppeared(const QSharedPointer& pEntry); - void fireDeviceVanished(const QSharedPointer& pEntry); - void fireEstablishConnectionDone(const QSharedPointer& pEntry, GlobalStatus pStatus); - - void fireNewRemoteDispatcher(const QSharedPointer& pRemoteDispatcher); - void fireRemoteDevicesInfo(const QVector >& pRemoteDevices); - void fireDispatcherDestroyed(GlobalStatus::Code pCloseCode, const QSharedPointer& pRemoteDispatcher); - void fireDetectionChanged(); - void fireCertificateRemoved(QString pDeviceName); - - public: - RemoteClient() = default; - virtual ~RemoteClient(); - - Q_INVOKABLE virtual void startDetection() = 0; - Q_INVOKABLE virtual void stopDetection() = 0; - Q_INVOKABLE virtual bool isDetecting() = 0; - - Q_INVOKABLE virtual void establishConnection(const QSharedPointer& pEntry, const QString& pPsk) = 0; - - virtual QVector > getRemoteDevices() const; - Q_INVOKABLE virtual void requestRemoteDevices(); - virtual QVector getConnectedDeviceInfos() = 0; -}; - - -} /* namespace governikus */ diff --git a/src/card/base/RemoteDispatcher.cpp b/src/card/base/RemoteDispatcher.cpp deleted file mode 100644 index 1c41829..0000000 --- a/src/card/base/RemoteDispatcher.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "RemoteDispatcher.h" - -#include "Initializer.h" - -#include - - -Q_DECLARE_LOGGING_CATEGORY(remote_device) - - -using namespace governikus; - - -static Initializer::Entry E([] { - qRegisterMetaType >("QSharedPointer"); - }); - - -RemoteDispatcher::RemoteDispatcher() - : QObject() - , QEnableSharedFromThis() -{ -} - - -RemoteDispatcher::~RemoteDispatcher() -{ -} diff --git a/src/card/base/RemoteDispatcher.h b/src/card/base/RemoteDispatcher.h deleted file mode 100644 index 5725af3..0000000 --- a/src/card/base/RemoteDispatcher.h +++ /dev/null @@ -1,45 +0,0 @@ -/*! - * - * \brief An interface for RemoteHandleImpl, meant to omit the - * dependency between card_base and remote_device. - * - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "GlobalStatus.h" - -#include -#include - - -namespace governikus -{ - -class DataChannel; -class RemoteMessage; - -class RemoteDispatcher - : public QObject - , public QEnableSharedFromThis -{ - Q_OBJECT - - protected: - RemoteDispatcher(); - - public: - virtual ~RemoteDispatcher(); - - virtual const QString& getId() const = 0; - virtual const QString& getContextHandle() const = 0; - virtual void close() = 0; - Q_INVOKABLE virtual void send(const QSharedPointer& pMessage) = 0; - - Q_SIGNALS: - void fireReceived(const QSharedPointer& pMessage, const QSharedPointer& pRemoteDispatcher); - void fireClosed(GlobalStatus::Code pCloseCode, const QSharedPointer& pRemoteDispatcher); -}; - -} /* namespace governikus */ diff --git a/src/card/base/ResponseApdu.cpp b/src/card/base/ResponseApdu.cpp new file mode 100644 index 0000000..eb0fec7 --- /dev/null +++ b/src/card/base/ResponseApdu.cpp @@ -0,0 +1,157 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "ResponseApdu.h" + +#include +#include + +using namespace governikus; + +Q_DECLARE_LOGGING_CATEGORY(card) + +ResponseApdu::ResponseApdu(StatusCode pStatusCode) + : Apdu(QByteArray()) +{ + char buffer[2]; + qToBigEndian(Enum::getValue(pStatusCode), buffer); + setBuffer(QByteArray(buffer, 2)); +} + + +ResponseApdu::ResponseApdu(const QByteArray& pBuffer) + : Apdu(pBuffer) +{ +} + + +ResponseApdu::~ResponseApdu() +{ +} + + +void ResponseApdu::setBuffer(const QByteArray& pBuffer) +{ + mBuffer = pBuffer; +} + + +QByteArray ResponseApdu::getData() const +{ + if (length() < RETURN_CODE_LENGTH) + { + return QByteArray(); + } + + return mBuffer.left(getDataLength()); +} + + +int ResponseApdu::getDataLength() const +{ + return length() - RETURN_CODE_LENGTH; +} + + +StatusCode ResponseApdu::getReturnCode() const +{ + if (mBuffer.isEmpty()) + { + return StatusCode::EMPTY; + } + + const int returnCodeAsInt = getReturnCodeAsHex().toInt(nullptr, 16); + return Enum::isValue(returnCodeAsInt) ? StatusCode(returnCodeAsInt) : StatusCode::INVALID; +} + + +QByteArray ResponseApdu::getReturnCodeAsHex() const +{ + return mBuffer.right(RETURN_CODE_LENGTH).toHex(); +} + + +int ResponseApdu::getRetryCounter() const +{ + StatusCode statusCode = getReturnCode(); + if (statusCode == StatusCode::SUCCESS) + { + return 3; + } + if (statusCode == StatusCode::PIN_RETRY_COUNT_2) + { + return 2; + } + if (statusCode == StatusCode::PIN_SUSPENDED) + { + return 1; + } + if (statusCode == StatusCode::PIN_BLOCKED || statusCode == StatusCode::PIN_DEACTIVATED) + { + return 0; + } + return -1; +} + + +SW1 ResponseApdu::getSW1() const +{ + if (length() < RETURN_CODE_LENGTH) + { + qCCritical(card) << "Buffer too short, returning 0"; + return SW1::INVALID; + } + const char value = mBuffer.at(length() - RETURN_CODE_LENGTH); + if (!Enum::isValue(value)) + { + qCCritical(card) << "Unknown SW1 value, returning INVALID, value:" << QString::number(value, 16); + return SW1::INVALID; + } + return SW1(value); +} + + +char ResponseApdu::getSW2() const +{ + if (length() < 1) + { + qCCritical(card) << "Buffer too short, returning 0"; + + return 0; + } + return mBuffer.at(length() - 1); +} + + +CardReturnCode ResponseApdu::getCardReturnCode() const +{ + switch (getReturnCode()) + { + case StatusCode::SUCCESS: + return CardReturnCode::OK; + + case StatusCode::INPUT_TIMEOUT: + return CardReturnCode::INPUT_TIME_OUT; + + case StatusCode::INPUT_CANCELLED: + return CardReturnCode::CANCELLATION_BY_USER; + + case StatusCode::PASSWORDS_DIFFER: + return CardReturnCode::NEW_PIN_MISMATCH; + + case StatusCode::PASSWORD_OUTOF_RANGE: + return CardReturnCode::NEW_PIN_INVALID_LENGTH; + + case StatusCode::PIN_BLOCKED: + return CardReturnCode::PIN_BLOCKED; + + default: + return CardReturnCode::PROTOCOL_ERROR; + } + + Q_UNREACHABLE(); +} + + +#include "moc_ResponseApdu.cpp" diff --git a/src/card/base/ResponseApdu.h b/src/card/base/ResponseApdu.h new file mode 100644 index 0000000..a165ceb --- /dev/null +++ b/src/card/base/ResponseApdu.h @@ -0,0 +1,104 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "Apdu.h" +#include "CardReturnCode.h" +#include "EnumHelper.h" + + +namespace governikus +{ +defineTypedEnumType(StatusCode, quint16, + EMPTY = 0x0000, + INVALID = 0x0001, + SUCCESS = 0x9000, + NO_PKCS15_APP = 0x6200, + END_OF_FILE = 0x6282, + PIN_DEACTIVATED = 0x6283, + FCI_NO_ISO7816_4 = 0x6284, + VERIFICATION_FAILED = 0x6300, + INPUT_TIMEOUT = 0x6400, + INPUT_CANCELLED = 0x6401, + PASSWORDS_DIFFER = 0x6402, + PASSWORD_OUTOF_RANGE = 0x6403, + CARD_EJECTED_AND_REINSERTED = 0x64a2, + EEPROM_CELL_DEFECT = 0x6581, + SECURITY_ENVIRONMENT = 0x6600, + WRONG_LENGTH = 0x6700, + NO_BINARY_FILE = 0x6981, + LAST_CHAIN_CMD_EXPECTED = 0x6883, + ACCESS_DENIED = 0x6982, + PASSWORD_COUNTER_EXPIRED = 0x6983, + DIRECTORY_OR_PASSWORD_LOCKED_OR_NOT_ALLOWED = 0x6984, + NO_PARENT_FILE = 0x6985, + NOT_YET_INITIALIZED = 0x6985, + NO_CURRENT_DIRECTORY_SELECTED = 0x6986, + DATAFIELD_EXPECTED = 0x6987, + INVALID_SM_OBJECTS = 0x6988, + COMMAND_NOT_ALLOWED = 0x69f0, + INVALID_DATAFIELD = 0x6a80, + ALGORITHM_ID = 0x6a81, + FILE_NOT_FOUND = 0x6a82, + RECORD_NOT_FOUND = 0x6a83, + INVALID_PARAMETER = 0x6a86, + LC_INCONSISTANT = 0x6a87, + PASSWORD_NOT_FOUND = 0x6a88, + ILLEGAL_OFFSET = 0x6b00, + UNSUPPORTED_CLA = 0x6e00, + CANT_DISPLAY = 0x6410, + INVALID_P1P2 = 0x6a00, + UNSUPPORTED_INS = 0x6d00, + PIN_BLOCKED = 0x63c0, // retries left: 0 + PIN_SUSPENDED = 0x63c1, // retries left: 1 + PIN_RETRY_COUNT_2 = 0x63c2, // retries left: 2 + ) + +/* + * As defined in ISO-7816-4 Table-5 + */ +defineTypedEnumType(SW1, quint8, + INVALID = 0x00, + MORE_DATA_AVAILABLE = 0x61, + NONVOLATILE_MEMORY_UNCHANGED_1 = 0x62, + NONVOLATILE_MEMORY_CHANGED_1 = 0x63, + NONVOLATILE_MEMORY_UNCHANGED_2 = 0x64, + NONVOLATILE_MEMORY_CHANGED_2 = 0x65, + SECURITY_ISSUE = 0x66, + WRONG_LENGTH = 0x67, + FUNCTIONS_IN_CLASS_NOT_SUPPORTED = 0x68, + ERROR_COMMAND_NOT_ALLOWED = 0x69, + WRONG_PARAMETERS_P1_P2 = 0x6a, + WRONG_PARAMETERS_P1_P2_NO_INFO = 0x6b, + WRONG_LE_FIELD = 0x6c, + INSTRUCTION_CODE_INVALID = 0x6d, + CLASS_NOT_SUPPORTED = 0x6e, + NO_PRECISE_DIAGNOSIS = 0x6f, + SUCCESS = 0x90, + ) + +class ResponseApdu + : public Apdu +{ + private: + static const int RETURN_CODE_LENGTH = 2; + + public: + ResponseApdu(StatusCode pStatusCode); + ResponseApdu(const QByteArray& pBuffer = QByteArray()); + virtual ~ResponseApdu(); + + virtual void setBuffer(const QByteArray& pBuffer); + QByteArray getData() const; + int getDataLength() const; + StatusCode getReturnCode() const; + QByteArray getReturnCodeAsHex() const; + int getRetryCounter() const; + SW1 getSW1() const; + char getSW2() const; + CardReturnCode getCardReturnCode() const; +}; + +} // namespace governikus diff --git a/src/card/base/SecureMessagingResponse.cpp b/src/card/base/SecureMessagingResponse.cpp index 08015dc..55aaa34 100644 --- a/src/card/base/SecureMessagingResponse.cpp +++ b/src/card/base/SecureMessagingResponse.cpp @@ -2,9 +2,10 @@ * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany */ -#include "asn1/ASN1Util.h" #include "SecureMessagingResponse.h" +#include "asn1/ASN1Util.h" + #include @@ -51,7 +52,7 @@ SecureMessagingResponse::SecureMessagingResponse(const QByteArray& pBuffer) ResponseApdu::setBuffer(pBuffer); QByteArray data = getData(); - if (auto tmp = decodeObject(data)) + if (auto tmp = decodeObject(data, false)) { mEncryptedData = tmp; QByteArray encryptedDataValue = Asn1OctetStringUtil::getValue(mEncryptedData.data()); diff --git a/src/card/base/SecureMessagingResponse.h b/src/card/base/SecureMessagingResponse.h index 8d2e703..2a27a61 100644 --- a/src/card/base/SecureMessagingResponse.h +++ b/src/card/base/SecureMessagingResponse.h @@ -13,7 +13,6 @@ namespace governikus { - /*! * Message part of the Secure Messaging response APDU containing * the encrypted data. diff --git a/src/card/base/SmartCardDefinitions.h b/src/card/base/SmartCardDefinitions.h index ad4294d..0e1e673 100644 --- a/src/card/base/SmartCardDefinitions.h +++ b/src/card/base/SmartCardDefinitions.h @@ -13,10 +13,13 @@ defineEnumType(CardType, UNKNOWN, EID_CARD) -defineTypedEnumType(PACE_PASSWORD_ID, char, +defineTypedEnumType(PacePasswordId, char, + UNKNOWN = 0x00, PACE_MRZ = 0x01, PACE_CAN = 0x02, PACE_PIN = 0x03, PACE_PUK = 0x04) -} +} // namespace governikus + +Q_DECLARE_METATYPE(governikus::PacePasswordId) diff --git a/src/card/base/asn1/ASN1TemplateUtil.cpp b/src/card/base/asn1/ASN1TemplateUtil.cpp new file mode 100644 index 0000000..13b65eb --- /dev/null +++ b/src/card/base/asn1/ASN1TemplateUtil.cpp @@ -0,0 +1,15 @@ +/* + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "ASN1TemplateUtil.h" + +QByteArray governikus::getOpenSslError() +{ + QByteArrayList list; + while (const auto errNum = ERR_get_error()) + { + list += ERR_error_string(errNum, nullptr); + } + return list.join(QByteArrayLiteral(" | ")); +} diff --git a/src/card/base/asn1/ASN1TemplateUtil.h b/src/card/base/asn1/ASN1TemplateUtil.h index e3d77df..a1ef6b4 100644 --- a/src/card/base/asn1/ASN1TemplateUtil.h +++ b/src/card/base/asn1/ASN1TemplateUtil.h @@ -10,12 +10,15 @@ #include #include -#include +#include #include +Q_DECLARE_LOGGING_CATEGORY(card) + namespace governikus { +QByteArray getOpenSslError(); /*! * Default template function for creating an OpenSSL type. This must be specialized for each ASN.1 type. @@ -59,15 +62,15 @@ int encodeAsn1Object(T*, unsigned char**) template QByteArray encodeObject(T* pObject) { + ERR_clear_error(); unsigned char* encoded = nullptr; - int length = encodeAsn1Object(pObject, &encoded); + const int length = encodeAsn1Object(pObject, &encoded); if (length < 0) { - BIO* bio_err = BIO_new_fp(stderr, BIO_NOCLOSE); - ERR_print_errors(bio_err); - BIO_free(bio_err); - length = 0; + qCWarning(card) << "Cannot encode ASN.1 object:" << getOpenSslError(); + return QByteArray(); } + return QByteArray(reinterpret_cast(encoded), length); } @@ -97,13 +100,18 @@ void freeAsn1Object(T*) * Template function for decoding an OpenSSL type from DER encoded QByteArray. */ template -QSharedPointer decodeObject(const QByteArray& pData) +QSharedPointer decodeObject(const QByteArray& pData, bool pLogging = true) { + ERR_clear_error(); const char* tmp = pData.constData(); const unsigned char** dataPointer = reinterpret_cast(&tmp); T* object = nullptr; - decodeAsn1Object(&object, dataPointer, pData.length()); + if (!decodeAsn1Object(&object, dataPointer, pData.length()) && pLogging) + { + qCWarning(card) << "Cannot decode ASN.1 object:" << getOpenSslError(); + } + static auto deleter = [](T* pTypeObject) { freeAsn1Object(pTypeObject); @@ -156,4 +164,4 @@ static const int CB_ERROR = 0; template<> void freeAsn1Object(name * pObject); -} +} // namespace governikus diff --git a/src/card/base/asn1/ASN1Util.cpp b/src/card/base/asn1/ASN1Util.cpp index b40c481..26bfcd4 100644 --- a/src/card/base/asn1/ASN1Util.cpp +++ b/src/card/base/asn1/ASN1Util.cpp @@ -42,7 +42,7 @@ QByteArray Asn1ObjectUtil::convertTo(const ASN1_OBJECT* pAsn1Object) QByteArray Asn1ObjectUtil::getValue(const ASN1_OBJECT* pAsn1Object) { -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) return QByteArray(reinterpret_cast(pAsn1Object->data), pAsn1Object->length); #else @@ -169,7 +169,6 @@ QDate Asn1BCDDateUtil::convertFromUnpackedBCDToQDate(ASN1_OCTET_STRING* pDateBCD int day = pDateBCD->data[4] * 10 + pDateBCD->data[5]; return QDate(year, month, day); - } diff --git a/src/card/base/asn1/ASN1Util.h b/src/card/base/asn1/ASN1Util.h index 32aca02..4497b7f 100644 --- a/src/card/base/asn1/ASN1Util.h +++ b/src/card/base/asn1/ASN1Util.h @@ -16,7 +16,7 @@ /*! * OpenSSL type declarations */ -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) DECLARE_STACK_OF(ASN1_OCTET_STRING) #else DEFINE_STACK_OF(ASN1_OCTET_STRING) @@ -25,7 +25,6 @@ DEFINE_STACK_OF(ASN1_OCTET_STRING) namespace governikus { - class Asn1Util { Asn1Util() = delete; @@ -122,4 +121,4 @@ class Asn1BCDDateUtil }; -} +} // namespace governikus diff --git a/src/card/base/asn1/AccessRoleAndRight.cpp b/src/card/base/asn1/AccessRoleAndRight.cpp index 25cf64d..f579714 100644 --- a/src/card/base/asn1/AccessRoleAndRight.cpp +++ b/src/card/base/asn1/AccessRoleAndRight.cpp @@ -202,16 +202,21 @@ QString AccessRoleAndRightsUtil::toDisplayText(AccessRight pRight) case AccessRight::AGE_VERIFICATION: return tr("Age verification"); - default: - return tr("Unknown"); + case AccessRight::RFU_29: + case AccessRight::RFU_30: + case AccessRight::RFU_31: + case AccessRight::RFU_32: + return tr("Unknown (reserved)"); } + + return tr("Unknown"); } QLatin1String AccessRoleAndRightsUtil::toTechnicalName(AccessRight pRight) { const auto name = getEnumName(static_cast(pRight)); - if (!name.size()) + if (name.isEmpty()) { qCritical() << "Requested AccessRight without mapping:" << pRight; } @@ -219,6 +224,32 @@ QLatin1String AccessRoleAndRightsUtil::toTechnicalName(AccessRight pRight) } +QString AccessRoleAndRightsUtil::joinFromTechnicalName(const QStringList& pStr, const QString& pJoin) +{ + return fromTechnicalName(pStr).join(pJoin); +} + + +QStringList AccessRoleAndRightsUtil::fromTechnicalName(const QStringList& pStr) +{ + QStringList result; + for (auto entry : pStr) + { + fromTechnicalName(entry, [&entry](AccessRight pRight){ + entry = AccessRoleAndRightsUtil::toDisplayText(pRight); + }); + result << entry; + } + return result; +} + + +bool AccessRoleAndRightsUtil::fromTechnicalName(const QString& pStr, const std::function& pFunc) +{ + return fromTechnicalName(pStr.toLatin1().constData(), pFunc); +} + + bool AccessRoleAndRightsUtil::fromTechnicalName(const char* pStr, const std::function& pFunc) { const AccessRightNames undefined = static_cast(UINT_MAX); diff --git a/src/card/base/asn1/AccessRoleAndRight.h b/src/card/base/asn1/AccessRoleAndRight.h index bb7a39d..4dcd55e 100644 --- a/src/card/base/asn1/AccessRoleAndRight.h +++ b/src/card/base/asn1/AccessRoleAndRight.h @@ -3,7 +3,7 @@ * * Note: When using a QHash directly or indirectly (e.g. via QSet), * this header must be included before (also indirectly via or ), - * or otherwise the complain about the qHash() function for AccessRight not being + * or otherwise they complain about the qHash() function for AccessRight not being * found. * * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany @@ -22,7 +22,6 @@ namespace governikus { - defineTypedEnumType(AccessRight, uint, WRITE_DG17 = 37, WRITE_DG18 = 36, @@ -108,15 +107,18 @@ class AccessRoleAndRightsUtil static QList mAllRights; static QList mAllDisplayedOrderedRights; AccessRoleAndRightsUtil() = delete; + static bool fromTechnicalName(const char* pStr, const std::function& pFunc); + static QStringList fromTechnicalName(const QStringList& pStr); public: static const QList& allDisplayedOrderedRights(); static const QList& allRights(); static QString toDisplayText(AccessRight pRight); static QLatin1String toTechnicalName(AccessRight pRight); - static bool fromTechnicalName(const char* pStr, const std::function& pFunc); + static bool fromTechnicalName(const QString& pStr, const std::function& pFunc); + static QString joinFromTechnicalName(const QStringList& pStr, const QString& pJoin = QStringLiteral(", ")); }; -} +} // namespace governikus Q_DECLARE_TYPEINFO(governikus::AccessRight, Q_PRIMITIVE_TYPE); diff --git a/src/card/base/asn1/AuthenticatedAuxiliaryData.cpp b/src/card/base/asn1/AuthenticatedAuxiliaryData.cpp index bbf16b2..a675905 100644 --- a/src/card/base/asn1/AuthenticatedAuxiliaryData.cpp +++ b/src/card/base/asn1/AuthenticatedAuxiliaryData.cpp @@ -99,12 +99,12 @@ DECLARE_ASN1_FUNCTIONS(AuthenticatedAuxiliaryDataInternal) DECLARE_ASN1_OBJECT(AuthenticatedAuxiliaryDataInternal) -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) #define sk_AuxDataTemplate_num(data) SKM_sk_num(AuxDataTemplate, data) #define sk_AuxDataTemplate_value(data, i) SKM_sk_value(AuxDataTemplate, data, i) #endif -} /* namespace governikus */ +} // namespace governikus AuthenticatedAuxiliaryData::AuthenticatedAuxiliaryData(const QSharedPointer& pData) @@ -151,7 +151,7 @@ QSharedPointer AuthenticatedAuxiliaryData::decode(co return nullptr; } - return QSharedPointer(new AuthenticatedAuxiliaryData(auxDate)); + return QSharedPointer::create(auxDate); } diff --git a/src/card/base/asn1/AuthenticatedAuxiliaryData.h b/src/card/base/asn1/AuthenticatedAuxiliaryData.h index 9f416f8..c6217fc 100644 --- a/src/card/base/asn1/AuthenticatedAuxiliaryData.h +++ b/src/card/base/asn1/AuthenticatedAuxiliaryData.h @@ -49,7 +49,7 @@ typedef struct auxdatatemplate_st } AuxDataTemplate; -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) DECLARE_STACK_OF(AuxDataTemplate) using AuthenticatedAuxiliaryDataInternal = stack_st_AuxDataTemplate; #else @@ -59,14 +59,14 @@ using AuthenticatedAuxiliaryDataInternal = STACK_OF(AuxDataTemplate); class AuthenticatedAuxiliaryData { - private: - friend class ::test_AuxiliaryAuthenticatedData; - QSharedPointer mData; + friend class ::test_AuxiliaryAuthenticatedData; + friend class QSharedPointer; + QSharedPointer mData; - AuthenticatedAuxiliaryData(const QSharedPointer& pData); - AuxDataTemplate* getAuxDataTemplateFor(KnownOIDs::AuxilaryData pData) const; + AuthenticatedAuxiliaryData(const QSharedPointer& pData); + AuxDataTemplate* getAuxDataTemplateFor(KnownOIDs::AuxilaryData pData) const; - QString getRequiredAge(const QDate& pEffectiveDate) const; + QString getRequiredAge(const QDate& pEffectiveDate) const; public: static QSharedPointer fromHex(const QByteArray& pHexValue); @@ -84,4 +84,4 @@ class AuthenticatedAuxiliaryData QByteArray getCommunityID() const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/asn1/CVCertificate.cpp b/src/card/base/asn1/CVCertificate.cpp index cf4c4e0..70bc8fb 100644 --- a/src/card/base/asn1/CVCertificate.cpp +++ b/src/card/base/asn1/CVCertificate.cpp @@ -2,9 +2,10 @@ * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany */ +#include "CVCertificate.h" + #include "ASN1TemplateUtil.h" #include "ASN1Util.h" -#include "CVCertificate.h" #include "pace/ec/EcUtil.h" #include @@ -64,13 +65,12 @@ int CVCertificate::decodeCallback(int pOperation, ASN1_VALUE** pVal, const ASN1_ BIGNUM* r = BN_bin2bn(sig, siglen / 2, 0); BIGNUM* s = BN_bin2bn(sig + (siglen / 2), siglen / 2, 0); -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) cvc->mEcdsaSignature.data()->r = r; cvc->mEcdsaSignature.data()->s = s; #else ECDSA_SIG_set0(cvc->mEcdsaSignature.data(), r, s); #endif - } } return CB_SUCCESS; @@ -94,7 +94,6 @@ QVector > CVCertificate::fromHex(const QByte QSharedPointer CVCertificate::fromHex(const QByteArray& pHexBytes) { return decodeObject(QByteArray::fromHex(pHexBytes)); - } @@ -150,7 +149,8 @@ QDebug operator <<(QDebug pDbg, const governikus::CVCertificate& pCvc) << ", car=" << pCvc.getBody().getCertificationAuthorityReference() << ", chr=" << pCvc.getBody().getCertificateHolderReference() << ", valid=[" << pCvc.getBody().getCertificateEffectiveDate().toString(Qt::ISODate) - << "," << pCvc.getBody().getCertificateExpirationDate().toString(Qt::ISODate) << "])"; + << "," << pCvc.getBody().getCertificateExpirationDate().toString(Qt::ISODate) << "]=" + << pCvc.isValidOn(QDateTime::currentDateTime()) << ")"; return pDbg; } diff --git a/src/card/base/asn1/CVCertificate.h b/src/card/base/asn1/CVCertificate.h index 3f10181..3979315 100644 --- a/src/card/base/asn1/CVCertificate.h +++ b/src/card/base/asn1/CVCertificate.h @@ -20,7 +20,6 @@ namespace governikus { - /*! * According to * - TR-03110-3, chapter C.1 and @@ -83,7 +82,7 @@ inline bool operator!=(const CVCertificate& pLeft, const CVCertificate& pRight) } -} /* namespace governikus */ +} // namespace governikus QDebug operator<<(QDebug pDbg, const governikus::CVCertificate& pCvc); QDebug operator<<(QDebug pDbg, const QSharedPointer& pCvc); diff --git a/src/card/base/asn1/CVCertificateBody.cpp b/src/card/base/asn1/CVCertificateBody.cpp index a3fe04f..52eea5e 100644 --- a/src/card/base/asn1/CVCertificateBody.cpp +++ b/src/card/base/asn1/CVCertificateBody.cpp @@ -19,7 +19,6 @@ Q_DECLARE_LOGGING_CATEGORY(card) namespace governikus { - ASN1_ITEM_TEMPLATE(CertificateProfileIdentifier) = ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_IMPTAG | ASN1_TFLG_APPLICATION, 0x29, CertificateProfileIdentifier, ASN1_OCTET_STRING) ASN1_ITEM_TEMPLATE_END(CertificateProfileIdentifier) @@ -97,12 +96,12 @@ IMPLEMENT_ASN1_FUNCTIONS(CVCertificateBody) IMPLEMENT_ASN1_OBJECT(CVCertificateBody) -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) #define sk_CERTIFICATEEXTENSION_num(data) SKM_sk_num(CERTIFICATEEXTENSION, data) #define sk_CERTIFICATEEXTENSION_value(data, i) SKM_sk_value(CERTIFICATEEXTENSION, data, i) #endif -} /* namespace governikus */ +} // namespace governikus QSharedPointer CVCertificateBody::fromHex(const QString& pHexValue) diff --git a/src/card/base/asn1/CVCertificateBody.h b/src/card/base/asn1/CVCertificateBody.h index 2e05a94..3730de4 100644 --- a/src/card/base/asn1/CVCertificateBody.h +++ b/src/card/base/asn1/CVCertificateBody.h @@ -35,7 +35,7 @@ typedef struct CERTIFICATEEXTENSION_st } CERTIFICATEEXTENSION; DECLARE_ASN1_FUNCTIONS(CERTIFICATEEXTENSION) -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) DECLARE_STACK_OF(CERTIFICATEEXTENSION) #else DEFINE_STACK_OF(CERTIFICATEEXTENSION) @@ -78,4 +78,4 @@ typedef struct certificateprofilebody_st DECLARE_ASN1_FUNCTIONS(CVCertificateBody) DECLARE_ASN1_OBJECT(CVCertificateBody) -} +} // namespace governikus diff --git a/src/card/base/asn1/CVCertificateChain.h b/src/card/base/asn1/CVCertificateChain.h index a9b1c57..6abcde4 100644 --- a/src/card/base/asn1/CVCertificateChain.h +++ b/src/card/base/asn1/CVCertificateChain.h @@ -50,4 +50,4 @@ class CVCertificateChain bool isProductive() const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/asn1/CVCertificateChainBuilder.cpp b/src/card/base/asn1/CVCertificateChainBuilder.cpp index 4d586bc..dba14f3 100644 --- a/src/card/base/asn1/CVCertificateChainBuilder.cpp +++ b/src/card/base/asn1/CVCertificateChainBuilder.cpp @@ -22,7 +22,6 @@ bool CVCertificateChainBuilder::isChild(const QSharedPointer >(), pProductive) { - } @@ -64,7 +63,7 @@ void CVCertificateChainBuilder::removeInvalidChains() } -CVCertificateChain CVCertificateChainBuilder::getChainForCertificationAuthority(const EstablishPACEChannelOutput& pPaceOutput) const +CVCertificateChain CVCertificateChainBuilder::getChainForCertificationAuthority(const EstablishPaceChannelOutput& pPaceOutput) const { CVCertificateChain chain = getChainForCertificationAuthority(pPaceOutput.getCARcurr()); if (!chain.isValid()) diff --git a/src/card/base/asn1/CVCertificateChainBuilder.h b/src/card/base/asn1/CVCertificateChainBuilder.h index f7b91a9..549c71f 100644 --- a/src/card/base/asn1/CVCertificateChainBuilder.h +++ b/src/card/base/asn1/CVCertificateChainBuilder.h @@ -9,13 +9,12 @@ #include "ChainBuilder.h" #include "CVCertificate.h" #include "CVCertificateChain.h" -#include "EstablishPACEChannel.h" +#include "EstablishPaceChannelOutput.h" namespace governikus { - class CVCertificateChainBuilder : private ChainBuilder > { @@ -51,7 +50,7 @@ class CVCertificateChainBuilder /*! * Get a chain with root having Certification Authority Reference (CAR) equal to one of the - * CARs in the passed parameter EstablishPACEChannelOutput. The chain root will not be a self + * CARs in the passed parameter EstablishPaceChannelOutput. The chain root will not be a self * signed CVCA. * The chain is returned top down, i.e. the chain root is at position 0 in the list, * the end entity CVC (the terminal CVC) is the last element in the list. @@ -60,7 +59,7 @@ class CVCertificateChainBuilder * occurred ( e.g. the last element in the list is no terminal CVC), an invalid chain is * returned (see CVCertificateChain::isValid()). */ - CVCertificateChain getChainForCertificationAuthority(const EstablishPACEChannelOutput& pPaceOutput) const; + CVCertificateChain getChainForCertificationAuthority(const EstablishPaceChannelOutput& pPaceOutput) const; }; -} +} // namespace governikus diff --git a/src/card/base/asn1/CertificateDescription.cpp b/src/card/base/asn1/CertificateDescription.cpp index f9bd5e1..7489269 100644 --- a/src/card/base/asn1/CertificateDescription.cpp +++ b/src/card/base/asn1/CertificateDescription.cpp @@ -65,13 +65,12 @@ QString getField(const QString& pData, const QStringList& pSearchItems) } -} +} // namespace namespace governikus { - ASN1_SEQUENCE(CertificateDescription) = { ASN1_SIMPLE(CertificateDescription, mDescriptionType, ASN1_OBJECT), ASN1_EXP(CertificateDescription, mIssuerName, ASN1_UTF8STRING, 1), @@ -89,7 +88,7 @@ ASN1_SEQUENCE_END(CertificateDescription) IMPLEMENT_ASN1_FUNCTIONS(CertificateDescription) IMPLEMENT_ASN1_OBJECT(CertificateDescription) -} /* namespace governikus */ +} // namespace governikus QSharedPointer CertificateDescription::fromHex(const QByteArray& pHexValue) diff --git a/src/card/base/asn1/CertificateDescription.h b/src/card/base/asn1/CertificateDescription.h index cdf2ca1..a8c1668 100644 --- a/src/card/base/asn1/CertificateDescription.h +++ b/src/card/base/asn1/CertificateDescription.h @@ -21,7 +21,6 @@ namespace governikus { - /** * As specified in TR-03110-4 chapter 2.2.6: * @@ -106,9 +105,9 @@ struct CertificateDescription DECLARE_ASN1_FUNCTIONS(CertificateDescription) DECLARE_ASN1_OBJECT(CertificateDescription) -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) #define sk_ASN1_OCTET_STRING_num(data) data->stack.num #define sk_ASN1_OCTET_STRING_value(data, i) SKM_sk_value(ASN1_OCTET_STRING, data, i) #endif -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/asn1/ChainBuilder.h b/src/card/base/asn1/ChainBuilder.h index edbf91e..deb3475 100644 --- a/src/card/base/asn1/ChainBuilder.h +++ b/src/card/base/asn1/ChainBuilder.h @@ -48,8 +48,8 @@ class ChainBuilder } else if (mIsChildFunc(pChain.first(), elem)) { - QVector extendedChain(pChain); - extendedChain.prepend(elem); + QVector extendedChain({elem}); + extendedChain += pChain; buildChain(pAllElements, extendedChain); chainComplete = false; } @@ -100,4 +100,4 @@ class ChainBuilder }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/asn1/Chat.cpp b/src/card/base/asn1/Chat.cpp index 0947206..2955f3a 100644 --- a/src/card/base/asn1/Chat.cpp +++ b/src/card/base/asn1/Chat.cpp @@ -252,5 +252,4 @@ void chat_st::setTemplateBit(uint pBitIndex, bool pOn) { mTemplate->data[byteNumber] = static_cast(mTemplate->data[byteNumber] & ~(0x01 << bitNumberInByte)); } - } diff --git a/src/card/base/asn1/Chat.h b/src/card/base/asn1/Chat.h index 6fc886f..2c237db 100644 --- a/src/card/base/asn1/Chat.h +++ b/src/card/base/asn1/Chat.h @@ -103,4 +103,4 @@ typedef struct chat_st DECLARE_ASN1_FUNCTIONS(CHAT) DECLARE_ASN1_OBJECT(CHAT) -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/asn1/ChipAuthenticationInfo.cpp b/src/card/base/asn1/ChipAuthenticationInfo.cpp index 1ff5093..d59f4ec 100644 --- a/src/card/base/asn1/ChipAuthenticationInfo.cpp +++ b/src/card/base/asn1/ChipAuthenticationInfo.cpp @@ -15,7 +15,6 @@ using namespace governikus::KnownOIDs; namespace governikus { - ASN1_SEQUENCE(chipauthenticationinfo_st) = { ASN1_SIMPLE(chipauthenticationinfo_st, mProtocol, ASN1_OBJECT), ASN1_SIMPLE(chipauthenticationinfo_st, mVersion, ASN1_INTEGER), diff --git a/src/card/base/asn1/ChipAuthenticationInfo.h b/src/card/base/asn1/ChipAuthenticationInfo.h index db00554..32236a2 100644 --- a/src/card/base/asn1/ChipAuthenticationInfo.h +++ b/src/card/base/asn1/ChipAuthenticationInfo.h @@ -13,7 +13,6 @@ namespace governikus { - /** * ChipAuthenticationInfo ::= SEQUENCE { * protocol OBJECT IDENTIFIER( id-CA-DH-3DES-CBC-CBC | id-CA-DH-AES-CBC-CMAC-128 | id-CA-DH-AES-CBC-CMAC-192 | @@ -40,22 +39,21 @@ DECLARE_ASN1_FUNCTIONS(chipauthenticationinfo_st) class ChipAuthenticationInfo : public SecurityInfo { + friend class QSharedPointer; const QSharedPointer mDelegate; ChipAuthenticationInfo(const QSharedPointer& pDelegate); - ASN1_OBJECT* getProtocolObjectIdentifier() const override; - static bool acceptsProtocol(const ASN1_OBJECT* pObjectIdentifier); public: static QSharedPointer decode(const QByteArray& pBytes) { - if (const auto& delegate = decodeObject(pBytes)) + if (const auto& delegate = decodeObject(pBytes, false)) { if (ChipAuthenticationInfo::acceptsProtocol(delegate->mProtocol)) { - return QSharedPointer(new ChipAuthenticationInfo(delegate)); + return QSharedPointer::create(delegate); } } return QSharedPointer(); diff --git a/src/card/base/asn1/EFCardSecurity.cpp b/src/card/base/asn1/EFCardSecurity.cpp index a41ebde..13a908b 100644 --- a/src/card/base/asn1/EFCardSecurity.cpp +++ b/src/card/base/asn1/EFCardSecurity.cpp @@ -16,9 +16,9 @@ using namespace governikus::KnownOIDs; Q_DECLARE_LOGGING_CATEGORY(card) +#ifndef OPENSSL_NO_CMS namespace governikus { - template<> CMS_ContentInfo* decodeAsn1Object(CMS_ContentInfo** pObject, const unsigned char** pData, long pDataLen) { @@ -33,7 +33,9 @@ void freeAsn1Object(CMS_ContentInfo* pObject) } -} +} // namespace governikus + +#endif QSharedPointer EFCardSecurity::fromHex(const QByteArray& pHexString) @@ -44,6 +46,12 @@ QSharedPointer EFCardSecurity::fromHex(const QByteArray& pHexStr QSharedPointer EFCardSecurity::decode(const QByteArray& pBytes) { +#ifdef OPENSSL_NO_CMS +#error Cryptographic Message Syntax (CMS) is required. Do you use LibreSSL? + Q_UNUSED(pBytes) + return QSharedPointer(); + +#else const auto contentInfo = decodeObject(pBytes); if (contentInfo == nullptr) { @@ -80,7 +88,9 @@ QSharedPointer EFCardSecurity::decode(const QByteArray& pBytes) return QSharedPointer(); } - return QSharedPointer(new EFCardSecurity(securityInfos)); + return QSharedPointer::create(securityInfos); + +#endif } diff --git a/src/card/base/asn1/EFCardSecurity.h b/src/card/base/asn1/EFCardSecurity.h index 3692e14..b0b68d6 100644 --- a/src/card/base/asn1/EFCardSecurity.h +++ b/src/card/base/asn1/EFCardSecurity.h @@ -10,7 +10,9 @@ #include "SecurityInfos.h" +#ifndef OPENSSL_NO_CMS #include +#endif #include @@ -18,7 +20,6 @@ namespace governikus { - /*! * EF.CardSecurity is defined in TR-03110-3 as ContentInfo with contentType id-signedData, * where the SignedData has eContentType id-SecurityObject. @@ -85,6 +86,7 @@ namespace governikus */ class EFCardSecurity { + friend class QSharedPointer; const QSharedPointer mSecurityInfos; EFCardSecurity(const QSharedPointer& pSecurityInfos); @@ -97,11 +99,12 @@ class EFCardSecurity const QSharedPointer& getSecurityInfos() const; }; +#ifndef OPENSSL_NO_CMS template<> CMS_ContentInfo* decodeAsn1Object(CMS_ContentInfo** pObject, const unsigned char** pData, long pDataLen); - template<> void freeAsn1Object(CMS_ContentInfo* pObject); +#endif -} // namespace governikus +} // namespace governikus diff --git a/src/card/base/asn1/EcdsaPublicKey.cpp b/src/card/base/asn1/EcdsaPublicKey.cpp index bbcefd0..5210cef 100644 --- a/src/card/base/asn1/EcdsaPublicKey.cpp +++ b/src/card/base/asn1/EcdsaPublicKey.cpp @@ -126,7 +126,6 @@ void EcdsaPublicKey::initEcKey() return; } - QSharedPointer p = EcUtil::create(BN_new()); if (!BN_bin2bn(mPrimeModulus->data, mPrimeModulus->length, p.data())) { @@ -162,7 +161,6 @@ void EcdsaPublicKey::initEcKey() return; } - QSharedPointer order = EcUtil::create(BN_new()); if (!BN_bin2bn(mOrderOfTheBasePoint->data, mOrderOfTheBasePoint->length, order.data())) { diff --git a/src/card/base/asn1/EcdsaPublicKey.h b/src/card/base/asn1/EcdsaPublicKey.h index 55f113b..ac2d6c6 100644 --- a/src/card/base/asn1/EcdsaPublicKey.h +++ b/src/card/base/asn1/EcdsaPublicKey.h @@ -73,4 +73,4 @@ typedef struct ecdsapublickey_st DECLARE_ASN1_FUNCTIONS(EcdsaPublicKey) DECLARE_ASN1_OBJECT(EcdsaPublicKey) -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/asn1/KnownOIDs.cpp b/src/card/base/asn1/KnownOIDs.cpp index 3fd3c5e..d2229d9 100644 --- a/src/card/base/asn1/KnownOIDs.cpp +++ b/src/card/base/asn1/KnownOIDs.cpp @@ -125,6 +125,7 @@ QByteArray governikus::toByteArray(id_ca pValue) { switch (pValue) { + // DH case id_ca::DH: return SecurityProtocol::ID_CA + QByteArrayLiteral(".1"); @@ -140,7 +141,7 @@ QByteArray governikus::toByteArray(id_ca pValue) case id_ca::DH_AES_CBC_CMAC_256: return id_ca::DH + QByteArrayLiteral(".4"); - + // ECDH case id_ca::ECDH: return SecurityProtocol::ID_CA + QByteArrayLiteral(".2"); @@ -204,6 +205,7 @@ QByteArray governikus::toByteArray(id_PACE::DH pValue) { switch (pValue) { + // GM case id_PACE::DH::GM: return SecurityProtocol::ID_PACE + QByteArrayLiteral(".1"); @@ -219,7 +221,7 @@ QByteArray governikus::toByteArray(id_PACE::DH pValue) case id_PACE::DH::GM_AES_CBC_CMAC_256: return id_PACE::DH::GM + QByteArrayLiteral(".4"); - + // IM case id_PACE::DH::IM: return SecurityProtocol::ID_PACE + QByteArrayLiteral(".3"); @@ -244,6 +246,7 @@ QByteArray governikus::toByteArray(id_PACE::ECDH pValue) { switch (pValue) { + // GM case id_PACE::ECDH::GM: return SecurityProtocol::ID_PACE + QByteArrayLiteral(".2"); @@ -259,7 +262,7 @@ QByteArray governikus::toByteArray(id_PACE::ECDH pValue) case id_PACE::ECDH::GM_AES_CBC_CMAC_256: return id_PACE::ECDH::GM + QByteArrayLiteral(".4"); - + // IM case id_PACE::ECDH::IM: return SecurityProtocol::ID_PACE + QByteArrayLiteral(".4"); diff --git a/src/card/base/asn1/PACEInfo.cpp b/src/card/base/asn1/PACEInfo.cpp deleted file mode 100644 index 4739b3b..0000000 --- a/src/card/base/asn1/PACEInfo.cpp +++ /dev/null @@ -1,198 +0,0 @@ -/*! - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - - -#include "ASN1TemplateUtil.h" -#include "ASN1Util.h" -#include "KnownOIDs.h" -#include "PACEInfo.h" - -#include - - -using namespace governikus; -using namespace governikus::KnownOIDs; - - -Q_DECLARE_LOGGING_CATEGORY(card) - - -namespace governikus -{ - - -ASN1_SEQUENCE(paceinfo_st) = { - ASN1_SIMPLE(paceinfo_st, mProtocol, ASN1_OBJECT), - ASN1_SIMPLE(paceinfo_st, mVersion, ASN1_INTEGER), - ASN1_OPT(paceinfo_st, mParameterId, ASN1_INTEGER) -} - - -ASN1_SEQUENCE_END(paceinfo_st) - -IMPLEMENT_ASN1_FUNCTIONS(paceinfo_st) - -template<> -paceinfo_st* decodeAsn1Object(paceinfo_st** pObject, const unsigned char** pData, long pDataLen) -{ - return d2i_paceinfo_st(pObject, pData, pDataLen); -} - - -template<> -void freeAsn1Object(paceinfo_st* pObject) -{ - paceinfo_st_free(pObject); -} - - -} // namespace governikus - - -bool PACEInfo::acceptsProtocol(const ASN1_OBJECT* pObjectIdentifier) -{ - const auto protocol = Asn1ObjectUtil::convertTo(pObjectIdentifier); - return protocol == id_PACE::DH::GM_3DES_CBC_CBC - || protocol == id_PACE::DH::GM_AES_CBC_CMAC_128 - || protocol == id_PACE::DH::GM_AES_CBC_CMAC_192 - || protocol == id_PACE::DH::GM_AES_CBC_CMAC_256 - || protocol == id_PACE::ECDH::GM_3DES_CBC_CBC - || protocol == id_PACE::ECDH::GM_AES_CBC_CMAC_128 - || protocol == id_PACE::ECDH::GM_AES_CBC_CMAC_192 - || protocol == id_PACE::ECDH::GM_AES_CBC_CMAC_256 - || protocol == id_PACE::DH::IM_3DES_CBC_CBC - || protocol == id_PACE::DH::IM_AES_CBC_CMAC_128 - || protocol == id_PACE::DH::IM_AES_CBC_CMAC_192 - || protocol == id_PACE::DH::IM_AES_CBC_CMAC_256 - || protocol == id_PACE::ECDH::IM_3DES_CBC_CBC - || protocol == id_PACE::ECDH::IM_AES_CBC_CMAC_128 - || protocol == id_PACE::ECDH::IM_AES_CBC_CMAC_192 - || protocol == id_PACE::ECDH::IM_AES_CBC_CMAC_256; -} - - -PACEInfo::PACEInfo(const QSharedPointer& pDelegate) - : SecurityInfo() - , mDelegate(pDelegate) -{ - if (getVersion() != 2) - { - qCWarning(card) << "Expect version=2, got: " << getVersion(); - } -} - - -ASN1_OBJECT* PACEInfo::getProtocolObjectIdentifier() const -{ - return mDelegate->mProtocol; -} - - -QByteArray PACEInfo::getParameterId() const -{ - if (mDelegate->mParameterId) - { - return Asn1IntegerUtil::getValue(mDelegate->mParameterId); - } - return QByteArray(); -} - - -int PACEInfo::getVersion() const -{ - const auto& version = Asn1IntegerUtil::getValue(mDelegate->mVersion); - return version.isEmpty() || version.size() > 1 ? -1 : version[0]; -} - - -KeyAgreementType PACEInfo::getKeyAgreementType() const -{ - const auto& protocol = getProtocol(); - if (protocol.startsWith(toByteArray(KnownOIDs::id_PACE::DH::GM)) - || protocol.startsWith(toByteArray(KnownOIDs::id_PACE::DH::IM))) - { - return KeyAgreementType::DH; - } - else - { - return KeyAgreementType::ECDH; - } -} - - -MappingType PACEInfo::getMappingType() const -{ - const auto& protocol = getProtocol(); - if (protocol.startsWith(toByteArray(KnownOIDs::id_PACE::DH::GM)) - || protocol.startsWith(toByteArray((KnownOIDs::id_PACE::ECDH::GM)))) - { - return MappingType::GM; - } - else - { - return MappingType::IM; - } -} - - -int PACEInfo::getParameterIdAsInt() const -{ - bool conversionOkay = false; - - /* - * According to the Qt documentation of QByteArray: - * "base must be between 2 and 36, or 0." - * So we convert first to hex and parse it then with base 16 - */ - int parameterIdAsInt = getParameterId().toHex().toInt(&conversionOkay, 16); - if (!conversionOkay) - { - qCCritical(card) << "Conversion error on parameterId"; - return -1; - } - return parameterIdAsInt; -} - - -bool PACEInfo::isStandardizedDomainParameters() const -{ - /* - * According to TR-03110-3 the standardized domain parameters are: - * - * 0 1024-bit MODP Group with 160-bit Prime Order Subgroup 1024/160 GFP - * 1 2048-bit MODP Group with 224-bit Prime Order Subgroup 2048/224 GFP - * 2 2048-bit MODP Group with 256-bit Prime Order Subgroup 2048/256 GFP - * 3 - 7 RFU - * 8 NIST P-192 (secp192r1) 192 ECP - * 9 BrainpoolP192r1 192 ECP - * 10 NIST P-224 (secp224r1) 224 ECP - This curve cannot be used with the integrated mapping. - * 11 BrainpoolP224r1 224 ECP - * 12 NIST P-256 (secp256r1) 256 ECP - * 13 BrainpoolP256r1 256 ECP - * 14 BrainpoolP320r1 320 ECP - * 15 NIST P-384 (secp384r1) 384 ECP - * 16 BrainpoolP384r1 384 ECP - * 17 BrainpoolP512r1 512 ECP - * 18 NIST P-521 (secp521r1) 521 ECP - * 19-31 RFU - */ - int paramId = getParameterIdAsInt(); - if (getKeyAgreementType() == KeyAgreementType::DH) - { - return 0 <= paramId && paramId <= 3; - } - if (getKeyAgreementType() == KeyAgreementType::ECDH && getMappingType() == MappingType::GM) - { - return 8 <= paramId && paramId <= 18; - } - if (getKeyAgreementType() == KeyAgreementType::ECDH && getMappingType() == MappingType::IM) - { - return 8 <= paramId && paramId <= 18 && paramId != 10; - } - - return false; -} - - -#include "moc_PACEInfo.cpp" diff --git a/src/card/base/asn1/PACEInfo.h b/src/card/base/asn1/PACEInfo.h deleted file mode 100644 index bf28660..0000000 --- a/src/card/base/asn1/PACEInfo.h +++ /dev/null @@ -1,103 +0,0 @@ -/*! - * \brief Implementation of PACEInfo - * - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "EnumHelper.h" -#include "SecurityInfo.h" - - -namespace governikus -{ - - -/*! - * Method used for key agreement: - * * DH, i.e. Diffie-Hellman - * * ECDH, i.e. elliptic curve Diffie-Hellman - */ -defineEnumType(KeyAgreementType, DH, ECDH) - - -/*! - * Method used for mapping: - * * GM, i.e. generic mapping - * * IM, i.e. integrated mapping - */ -defineEnumType(MappingType, GM, IM) - - -/** - * PACEInfo ::= SEQUENCE { - * protocol OBJECT IDENTIFIER( id-PACE-DH-GM-3DES-CBC-CBC | id-PACE-DH-GM-AES-CBC-CMAC-128 | - * id-PACE-DH-GM-AES-CBC-CMAC-192 | id-PACE-DH-GM-AES-CBC-CMAC-256 | - * id-PACE-ECDH-GM-3DES-CBC-CBC | id-PACE-ECDH-GM-AES-CBC-CMAC-128 | - * id-PACE-ECDH-GM-AES-CBC-CMAC-192 | id-PACE-ECDH-GM-AES-CBC-CMAC-256 | - * id-PACE-DH-IM-3DES-CBC-CBC | id-PACE-DH-IM-AES-CBC-CMAC-128 | - * id-PACE-DH-IM-AES-CBC-CMAC-192 | id-PACE-DH-IM-AES-CBC-CMAC-256 | - * id-PACE-ECDH-IM-3DES-CBC-CBC | id-PACE-ECDH-IM-AES-CBC-CMAC-128 | - * id-PACE-ECDH-IM-AES-CBC-CMAC-192 | id-PACE-ECDH-IM-AES-CBC-CMAC-256), - * version INTEGER, -- SHOULD be 2 - * parameterId INTEGER OPTIONAL - * } - * - * defined in TR 3110 Part 3 - */ -struct paceinfo_st -{ - ASN1_OBJECT* mProtocol; - ASN1_INTEGER* mVersion; - ASN1_INTEGER* mParameterId; -}; -DECLARE_ASN1_FUNCTIONS(paceinfo_st) - - -/* - * Wrapper for structure paceinfo_st. - */ -class PACEInfo - : public SecurityInfo -{ - const QSharedPointer mDelegate; - - PACEInfo(const QSharedPointer& pDelegate); - - ASN1_OBJECT* getProtocolObjectIdentifier() const override; - - static bool acceptsProtocol(const ASN1_OBJECT* pObjectIdentifier); - - public: - static QSharedPointer decode(const QByteArray& pBytes) - { - if (const auto& delegate = decodeObject(pBytes)) - { - if (PACEInfo::acceptsProtocol(delegate->mProtocol)) - { - return QSharedPointer(new PACEInfo(delegate)); - } - } - return QSharedPointer(); - } - - - QByteArray getParameterId() const; - int getParameterIdAsInt() const; - int getVersion() const; - KeyAgreementType getKeyAgreementType() const; - MappingType getMappingType() const; - bool isStandardizedDomainParameters() const; -}; - - -template<> -paceinfo_st* decodeAsn1Object(paceinfo_st** pObject, const unsigned char** pData, long pDataLen); - - -template<> -void freeAsn1Object(paceinfo_st* pObject); - - -} // namespace governikus diff --git a/src/card/base/asn1/PaceInfo.cpp b/src/card/base/asn1/PaceInfo.cpp new file mode 100644 index 0000000..98ed8c8 --- /dev/null +++ b/src/card/base/asn1/PaceInfo.cpp @@ -0,0 +1,197 @@ +/*! + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + + +#include "ASN1TemplateUtil.h" +#include "ASN1Util.h" +#include "KnownOIDs.h" +#include "PaceInfo.h" + +#include + + +using namespace governikus; +using namespace governikus::KnownOIDs; + + +Q_DECLARE_LOGGING_CATEGORY(card) + + +namespace governikus +{ + +ASN1_SEQUENCE(paceinfo_st) = { + ASN1_SIMPLE(paceinfo_st, mProtocol, ASN1_OBJECT), + ASN1_SIMPLE(paceinfo_st, mVersion, ASN1_INTEGER), + ASN1_OPT(paceinfo_st, mParameterId, ASN1_INTEGER) +} + + +ASN1_SEQUENCE_END(paceinfo_st) + +IMPLEMENT_ASN1_FUNCTIONS(paceinfo_st) + +template<> +paceinfo_st* decodeAsn1Object(paceinfo_st** pObject, const unsigned char** pData, long pDataLen) +{ + return d2i_paceinfo_st(pObject, pData, pDataLen); +} + + +template<> +void freeAsn1Object(paceinfo_st* pObject) +{ + paceinfo_st_free(pObject); +} + + +} // namespace governikus + + +bool PaceInfo::acceptsProtocol(const ASN1_OBJECT* pObjectIdentifier) +{ + const auto protocol = Asn1ObjectUtil::convertTo(pObjectIdentifier); + return protocol == id_PACE::DH::GM_3DES_CBC_CBC + || protocol == id_PACE::DH::GM_AES_CBC_CMAC_128 + || protocol == id_PACE::DH::GM_AES_CBC_CMAC_192 + || protocol == id_PACE::DH::GM_AES_CBC_CMAC_256 + || protocol == id_PACE::ECDH::GM_3DES_CBC_CBC + || protocol == id_PACE::ECDH::GM_AES_CBC_CMAC_128 + || protocol == id_PACE::ECDH::GM_AES_CBC_CMAC_192 + || protocol == id_PACE::ECDH::GM_AES_CBC_CMAC_256 + || protocol == id_PACE::DH::IM_3DES_CBC_CBC + || protocol == id_PACE::DH::IM_AES_CBC_CMAC_128 + || protocol == id_PACE::DH::IM_AES_CBC_CMAC_192 + || protocol == id_PACE::DH::IM_AES_CBC_CMAC_256 + || protocol == id_PACE::ECDH::IM_3DES_CBC_CBC + || protocol == id_PACE::ECDH::IM_AES_CBC_CMAC_128 + || protocol == id_PACE::ECDH::IM_AES_CBC_CMAC_192 + || protocol == id_PACE::ECDH::IM_AES_CBC_CMAC_256; +} + + +PaceInfo::PaceInfo(const QSharedPointer& pDelegate) + : SecurityInfo() + , mDelegate(pDelegate) +{ + if (getVersion() != 2) + { + qCWarning(card) << "Expect version=2, got: " << getVersion(); + } +} + + +ASN1_OBJECT* PaceInfo::getProtocolObjectIdentifier() const +{ + return mDelegate->mProtocol; +} + + +QByteArray PaceInfo::getParameterId() const +{ + if (mDelegate->mParameterId) + { + return Asn1IntegerUtil::getValue(mDelegate->mParameterId); + } + return QByteArray(); +} + + +int PaceInfo::getVersion() const +{ + const auto& version = Asn1IntegerUtil::getValue(mDelegate->mVersion); + return version.isEmpty() || version.size() > 1 ? -1 : version[0]; +} + + +KeyAgreementType PaceInfo::getKeyAgreementType() const +{ + const auto& protocol = getProtocol(); + if (protocol.startsWith(toByteArray(KnownOIDs::id_PACE::DH::GM)) + || protocol.startsWith(toByteArray(KnownOIDs::id_PACE::DH::IM))) + { + return KeyAgreementType::DH; + } + else + { + return KeyAgreementType::ECDH; + } +} + + +MappingType PaceInfo::getMappingType() const +{ + const auto& protocol = getProtocol(); + if (protocol.startsWith(toByteArray(KnownOIDs::id_PACE::DH::GM)) + || protocol.startsWith(toByteArray((KnownOIDs::id_PACE::ECDH::GM)))) + { + return MappingType::GM; + } + else + { + return MappingType::IM; + } +} + + +int PaceInfo::getParameterIdAsInt() const +{ + bool conversionOkay = false; + + /* + * According to the Qt documentation of QByteArray: + * "base must be between 2 and 36, or 0." + * So we convert first to hex and parse it then with base 16 + */ + int parameterIdAsInt = getParameterId().toHex().toInt(&conversionOkay, 16); + if (!conversionOkay) + { + qCCritical(card) << "Conversion error on parameterId"; + return -1; + } + return parameterIdAsInt; +} + + +bool PaceInfo::isStandardizedDomainParameters() const +{ + /* + * According to TR-03110-3 the standardized domain parameters are: + * + * 0 1024-bit MODP Group with 160-bit Prime Order Subgroup 1024/160 GFP + * 1 2048-bit MODP Group with 224-bit Prime Order Subgroup 2048/224 GFP + * 2 2048-bit MODP Group with 256-bit Prime Order Subgroup 2048/256 GFP + * 3 - 7 RFU + * 8 NIST P-192 (secp192r1) 192 ECP + * 9 BrainpoolP192r1 192 ECP + * 10 NIST P-224 (secp224r1) 224 ECP - This curve cannot be used with the integrated mapping. + * 11 BrainpoolP224r1 224 ECP + * 12 NIST P-256 (secp256r1) 256 ECP + * 13 BrainpoolP256r1 256 ECP + * 14 BrainpoolP320r1 320 ECP + * 15 NIST P-384 (secp384r1) 384 ECP + * 16 BrainpoolP384r1 384 ECP + * 17 BrainpoolP512r1 512 ECP + * 18 NIST P-521 (secp521r1) 521 ECP + * 19-31 RFU + */ + int paramId = getParameterIdAsInt(); + if (getKeyAgreementType() == KeyAgreementType::DH) + { + return 0 <= paramId && paramId <= 3; + } + if (getKeyAgreementType() == KeyAgreementType::ECDH && getMappingType() == MappingType::GM) + { + return 8 <= paramId && paramId <= 18; + } + if (getKeyAgreementType() == KeyAgreementType::ECDH && getMappingType() == MappingType::IM) + { + return 8 <= paramId && paramId <= 18 && paramId != 10; + } + + return false; +} + + +#include "moc_PaceInfo.cpp" diff --git a/src/card/base/asn1/PaceInfo.h b/src/card/base/asn1/PaceInfo.h new file mode 100644 index 0000000..106ae89 --- /dev/null +++ b/src/card/base/asn1/PaceInfo.h @@ -0,0 +1,101 @@ +/*! + * \brief Implementation of PACEInfo + * + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "EnumHelper.h" +#include "SecurityInfo.h" + + +namespace governikus +{ + +/*! + * Method used for key agreement: + * * DH, i.e. Diffie-Hellman + * * ECDH, i.e. elliptic curve Diffie-Hellman + */ +defineEnumType(KeyAgreementType, DH, ECDH) + + +/*! + * Method used for mapping: + * * GM, i.e. generic mapping + * * IM, i.e. integrated mapping + */ +defineEnumType(MappingType, GM, IM) + + +/** + * PACEInfo ::= SEQUENCE { + * protocol OBJECT IDENTIFIER( id-PACE-DH-GM-3DES-CBC-CBC | id-PACE-DH-GM-AES-CBC-CMAC-128 | + * id-PACE-DH-GM-AES-CBC-CMAC-192 | id-PACE-DH-GM-AES-CBC-CMAC-256 | + * id-PACE-ECDH-GM-3DES-CBC-CBC | id-PACE-ECDH-GM-AES-CBC-CMAC-128 | + * id-PACE-ECDH-GM-AES-CBC-CMAC-192 | id-PACE-ECDH-GM-AES-CBC-CMAC-256 | + * id-PACE-DH-IM-3DES-CBC-CBC | id-PACE-DH-IM-AES-CBC-CMAC-128 | + * id-PACE-DH-IM-AES-CBC-CMAC-192 | id-PACE-DH-IM-AES-CBC-CMAC-256 | + * id-PACE-ECDH-IM-3DES-CBC-CBC | id-PACE-ECDH-IM-AES-CBC-CMAC-128 | + * id-PACE-ECDH-IM-AES-CBC-CMAC-192 | id-PACE-ECDH-IM-AES-CBC-CMAC-256), + * version INTEGER, -- SHOULD be 2 + * parameterId INTEGER OPTIONAL + * } + * + * defined in TR 3110 Part 3 + */ +struct paceinfo_st +{ + ASN1_OBJECT* mProtocol; + ASN1_INTEGER* mVersion; + ASN1_INTEGER* mParameterId; +}; +DECLARE_ASN1_FUNCTIONS(paceinfo_st) + + +/* + * Wrapper for structure paceinfo_st. + */ +class PaceInfo + : public SecurityInfo +{ + friend class QSharedPointer; + const QSharedPointer mDelegate; + + PaceInfo(const QSharedPointer& pDelegate); + ASN1_OBJECT* getProtocolObjectIdentifier() const override; + static bool acceptsProtocol(const ASN1_OBJECT* pObjectIdentifier); + + public: + static QSharedPointer decode(const QByteArray& pBytes) + { + if (const auto& delegate = decodeObject(pBytes, false)) + { + if (PaceInfo::acceptsProtocol(delegate->mProtocol)) + { + return QSharedPointer::create(delegate); + } + } + return QSharedPointer(); + } + + + QByteArray getParameterId() const; + int getParameterIdAsInt() const; + int getVersion() const; + KeyAgreementType getKeyAgreementType() const; + MappingType getMappingType() const; + bool isStandardizedDomainParameters() const; +}; + + +template<> +paceinfo_st* decodeAsn1Object(paceinfo_st** pObject, const unsigned char** pData, long pDataLen); + + +template<> +void freeAsn1Object(paceinfo_st* pObject); + + +} // namespace governikus diff --git a/src/card/base/asn1/SecurityInfo.cpp b/src/card/base/asn1/SecurityInfo.cpp index b0e893d..d6d2a5b 100644 --- a/src/card/base/asn1/SecurityInfo.cpp +++ b/src/card/base/asn1/SecurityInfo.cpp @@ -6,7 +6,7 @@ #include "ASN1Util.h" #include "ChipAuthenticationInfo.h" #include "KnownOIDs.h" -#include "PACEInfo.h" +#include "PaceInfo.h" #include "SecurityInfo.h" #include @@ -22,7 +22,6 @@ Q_DECLARE_LOGGING_CATEGORY(card) namespace governikus { - ASN1_SEQUENCE(securityinfo_st) = { ASN1_SIMPLE(securityinfo_st, mProtocol, ASN1_OBJECT), ASN1_SIMPLE(securityinfo_st, mRequiredData, ASN1_ANY), diff --git a/src/card/base/asn1/SecurityInfo.h b/src/card/base/asn1/SecurityInfo.h index a9c0de3..3fccf2c 100644 --- a/src/card/base/asn1/SecurityInfo.h +++ b/src/card/base/asn1/SecurityInfo.h @@ -15,7 +15,6 @@ namespace governikus { - /** * SecurityInfo ::= SEQUENCE { * protocol OBJECT IDENTIFIER, @@ -33,7 +32,7 @@ struct securityinfo_st }; DECLARE_ASN1_FUNCTIONS(securityinfo_st) -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) DECLARE_STACK_OF(securityinfo_st) #else DEFINE_STACK_OF(securityinfo_st) @@ -45,10 +44,11 @@ DEFINE_STACK_OF(securityinfo_st) */ class SecurityInfo { + Q_DISABLE_COPY(SecurityInfo) + friend class QSharedPointer; const QSharedPointer mDelegate; SecurityInfo(const QSharedPointer& pDelegate); - Q_DISABLE_COPY(SecurityInfo) /* * Sub classes must override this method to allow the base class to access @@ -64,7 +64,7 @@ class SecurityInfo { if (const auto& delegate = decodeObject(pBytes)) { - return QSharedPointer(new SecurityInfo(delegate)); + return QSharedPointer::create(delegate); } return QSharedPointer(); } @@ -86,4 +86,4 @@ class SecurityInfo DECLARE_ASN1_OBJECT(securityinfo_st) -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/asn1/SecurityInfos.cpp b/src/card/base/asn1/SecurityInfos.cpp index aa9f12e..d64b7cf 100644 --- a/src/card/base/asn1/SecurityInfos.cpp +++ b/src/card/base/asn1/SecurityInfos.cpp @@ -25,12 +25,12 @@ IMPLEMENT_ASN1_FUNCTIONS(securityinfos_st) IMPLEMENT_ASN1_OBJECT(securityinfos_st) -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) #define sk_securityinfo_st_num(data) SKM_sk_num(securityinfo_st, data) #define sk_securityinfo_st_value(data, i) SKM_sk_value(securityinfo_st, data, i) #endif -} /* namespace governikus */ +} // namespace governikus QSharedPointer SecurityInfos::fromHex(const QByteArray& pHexString) @@ -48,7 +48,7 @@ QSharedPointer SecurityInfos::decode(const QByteArray& pBytes) } QVector > securityInfos; - QVector > paceInfos; + QVector > paceInfos; QVector > chipAuthenticationInfos; for (int i = 0; i < sk_securityinfo_st_num(securityInfosStruct.data()); ++i) @@ -56,7 +56,7 @@ QSharedPointer SecurityInfos::decode(const QByteArray& pBytes) securityinfo_st* secInfoStruct = sk_securityinfo_st_value(securityInfosStruct.data(), i); QByteArray bytes = encodeObject(secInfoStruct); - if (auto pi = PACEInfo::decode(bytes)) + if (auto pi = PaceInfo::decode(bytes)) { qCDebug(card) << "Parsed PACEInfo"; paceInfos << pi; @@ -80,17 +80,17 @@ QSharedPointer SecurityInfos::decode(const QByteArray& pBytes) } } - return QSharedPointer(new SecurityInfos(pBytes, securityInfos, paceInfos, chipAuthenticationInfos)); + return QSharedPointer::create(pBytes, securityInfos, paceInfos, chipAuthenticationInfos); } SecurityInfos::SecurityInfos(const QByteArray& pBytes, const QVector >& pSecurityInfos, - const QVector >& pPACEInfos, + const QVector >& pPaceInfos, const QVector >& pChipAuthenticationInfos) : mContentBytes(pBytes) , mSecurityInfos(pSecurityInfos) - , mPACEInfos(pPACEInfos) + , mPaceInfos(pPaceInfos) , mChipAuthenticationInfos(pChipAuthenticationInfos) { } @@ -108,9 +108,9 @@ const QVector >& SecurityInfos::getSecurityIn } -const QVector >& SecurityInfos::getPACEInfos() const +const QVector >& SecurityInfos::getPaceInfos() const { - return mPACEInfos; + return mPaceInfos; } diff --git a/src/card/base/asn1/SecurityInfos.h b/src/card/base/asn1/SecurityInfos.h index 7f7b8a6..4170ee4 100644 --- a/src/card/base/asn1/SecurityInfos.h +++ b/src/card/base/asn1/SecurityInfos.h @@ -7,7 +7,7 @@ #pragma once #include "ChipAuthenticationInfo.h" -#include "PACEInfo.h" +#include "PaceInfo.h" #include "SecurityInfo.h" #include @@ -29,16 +29,18 @@ DECLARE_ASN1_OBJECT(securityinfos_st) */ class SecurityInfos { + Q_DISABLE_COPY(SecurityInfos) + friend class QSharedPointer; + const QByteArray mContentBytes; const QVector > mSecurityInfos; - const QVector > mPACEInfos; + const QVector > mPaceInfos; const QVector > mChipAuthenticationInfos; SecurityInfos(const QByteArray& pBytes, const QVector >& pSecurityInfos, - const QVector >& pPACEInfos, + const QVector >& pPaceInfos, const QVector >& pChipAuthenticationInfos); - Q_DISABLE_COPY(SecurityInfos) public: static QSharedPointer fromHex(const QByteArray& pHexString); @@ -46,7 +48,7 @@ class SecurityInfos const QByteArray& getContentBytes() const; const QVector >& getSecurityInfos() const; - const QVector >& getPACEInfos() const; + const QVector >& getPaceInfos() const; const QVector >& getChipAuthenticationInfos() const; }; @@ -59,4 +61,4 @@ class SecurityInfos typedef SecurityInfos EFCardAccess; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/asn1/SignatureChecker.cpp b/src/card/base/asn1/SignatureChecker.cpp index 8c18104..28944d5 100644 --- a/src/card/base/asn1/SignatureChecker.cpp +++ b/src/card/base/asn1/SignatureChecker.cpp @@ -3,6 +3,8 @@ */ #include "asn1/SignatureChecker.h" + +#include "ASN1TemplateUtil.h" #include "pace/ec/EcUtil.h" #include @@ -58,13 +60,14 @@ bool SignatureChecker::check() bool SignatureChecker::checkSignature(const QSharedPointer& pCert, const QSharedPointer& pSigningCert, const EC_KEY* pKey) { + ERR_clear_error(); + // We duplicate the key because we modify it by setting the public point. - QSharedPointer signingKey = EcUtil::create(EC_KEY_dup(pKey)); + const QSharedPointer signingKey = EcUtil::create(EC_KEY_dup(pKey)); - - QByteArray uncompPublicPoint = pSigningCert->getBody().getPublicKey().getUncompressedPublicPoint(); + const QByteArray uncompPublicPoint = pSigningCert->getBody().getPublicKey().getUncompressedPublicPoint(); const unsigned char* uncompPublicPointData = reinterpret_cast(uncompPublicPoint.constData()); - size_t uncompPublicPointLen = static_cast(uncompPublicPoint.size()); + const auto uncompPublicPointLen = static_cast(uncompPublicPoint.size()); EC_POINT* publicPoint = EC_POINT_new(EC_KEY_get0_group(signingKey.data())); const EC_GROUP* ecGroup = EC_KEY_get0_group(signingKey.data()); @@ -75,16 +78,15 @@ bool SignatureChecker::checkSignature(const QSharedPointer& } EC_KEY_set_public_key(signingKey.data(), publicPoint); - QByteArray bodyHash = QCryptographicHash::hash(pCert->getRawBody(), pSigningCert->getBody().getHashAlgorithm()); + const QByteArray bodyHash = QCryptographicHash::hash(pCert->getRawBody(), pSigningCert->getBody().getHashAlgorithm()); const unsigned char* dgst = reinterpret_cast(bodyHash.constData()); - int dgstlen = bodyHash.size(); - - int result = ECDSA_do_verify(dgst, dgstlen, pCert->getEcdsaSignature().data(), signingKey.data()); + const int dgstlen = bodyHash.size(); + const int result = ECDSA_do_verify(dgst, dgstlen, pCert->getEcdsaSignature().data(), signingKey.data()); if (result == -1) { - ERR_load_crypto_strings(); - qCCritical(card) << "Signature verification failed, an error occured: " << ERR_error_string(ERR_get_error(), nullptr); + qCCritical(card) << "Signature verification failed, an error occured:" << getOpenSslError(); } - return(result == 1); + + return result == 1; } diff --git a/src/card/base/asn1/SignatureChecker.h b/src/card/base/asn1/SignatureChecker.h index c55d6ec..80ad385 100644 --- a/src/card/base/asn1/SignatureChecker.h +++ b/src/card/base/asn1/SignatureChecker.h @@ -26,4 +26,4 @@ class SignatureChecker bool check(); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/command/BaseCardCommand.cpp b/src/card/base/command/BaseCardCommand.cpp index 2416a0b..c0989a8 100644 --- a/src/card/base/command/BaseCardCommand.cpp +++ b/src/card/base/command/BaseCardCommand.cpp @@ -34,40 +34,24 @@ BaseCardCommand::~BaseCardCommand() } +void BaseCardCommand::run() +{ + QMetaObject::invokeMethod(this, &BaseCardCommand::execute, Qt::QueuedConnection); +} + + void BaseCardCommand::execute() { - Q_ASSERT(thread() == QThread::currentThread()); + Q_ASSERT(QObject::thread() == QThread::currentThread()); internalExecute(); - qDebug(card) << "ReturnCode of internal execute:" << mReturnCode; + qDebug(card) << metaObject()->className() << "| ReturnCode of internal execute:" << mReturnCode; + // A "Command" is created by CardConnection::call() in Main-Thread and moved to ReaderManager-Thread. + // The internal execution of a command will be self-sufficient until it has finished. After the + // command is finished it is a data container only. It will fires a signal with itself wrapped into a + // QSharedPointer to ensure the destruction in correct thread. This structure is used to have an + // easy-to-use API of commands without knowledge about threads. QSharedPointer command(this, &QObject::deleteLater); Q_EMIT commandDone(command); } - - -CardReturnCode BaseCardCommand::checkRetryCounterAndPrepareForPace(const QString& pCan) -{ - CardReturnCode returnCode = mCardConnectionWorker->updateRetryCounter(); - if (returnCode != CardReturnCode::OK) - { - return returnCode; - } - - switch (mCardConnectionWorker->getReaderInfo().getRetryCounter()) - { - case 1: // CAN required - { - EstablishPACEChannelOutput output; - return mCardConnectionWorker->establishPaceChannel(PACE_PASSWORD_ID::PACE_CAN, pCan, output); - } - - case 2: - case 3: // only PIN required - return CardReturnCode::OK; - - default: - qCWarning(card) << "Card blocked or deactivated."; - return CardReturnCode::PIN_BLOCKED; - } -} diff --git a/src/card/base/command/BaseCardCommand.h b/src/card/base/command/BaseCardCommand.h index 4d95dde..90b043d 100644 --- a/src/card/base/command/BaseCardCommand.h +++ b/src/card/base/command/BaseCardCommand.h @@ -12,21 +12,20 @@ #include class test_BaseCardCommand; +class test_CardConnection; namespace governikus { -class CardConnection; - - class BaseCardCommand : public QObject { Q_OBJECT - friend class ::test_BaseCardCommand; private: - Q_INVOKABLE void execute(); // will be invoked by CardConnection + friend class ::test_BaseCardCommand; + friend class ::test_CardConnection; + Q_INVOKABLE void execute(); protected: QSharedPointer mCardConnectionWorker; @@ -34,12 +33,12 @@ class BaseCardCommand BaseCardCommand(QSharedPointer pCardConnectionWorker); - CardReturnCode checkRetryCounterAndPrepareForPace(const QString& pCan); - virtual void internalExecute() = 0; virtual ~BaseCardCommand(); public: + void run(); + CardReturnCode getReturnCode() const { return mReturnCode; @@ -50,4 +49,4 @@ class BaseCardCommand void commandDone(QSharedPointer pCommand); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/command/CreateCardConnectionCommand.cpp b/src/card/base/command/CreateCardConnectionCommand.cpp index 1b36fa5..cd554fc 100644 --- a/src/card/base/command/CreateCardConnectionCommand.cpp +++ b/src/card/base/command/CreateCardConnectionCommand.cpp @@ -25,13 +25,23 @@ CreateCardConnectionCommand::CreateCardConnectionCommand(const QString& pReaderN } +void CreateCardConnectionCommand::run() +{ + QMetaObject::invokeMethod(this, &CreateCardConnectionCommand::execute, Qt::QueuedConnection); +} + + void CreateCardConnectionCommand::execute() { - Q_ASSERT(thread() == QThread::currentThread()); + Q_ASSERT(QObject::thread() == QThread::currentThread()); if (connect(mReaderManagerWorker.data(), &ReaderManagerWorker::fireCardConnectionWorkerCreated, this, &CreateCardConnectionCommand::onCardConnectionWorkerCreated, Qt::UniqueConnection)) { - QMetaObject::invokeMethod(mReaderManagerWorker.data(), "createCardConnectionWorker", Qt::QueuedConnection, Q_ARG(QString, mReaderName)); + const auto& localCopy = mReaderManagerWorker; + const auto& name = mReaderName; + QMetaObject::invokeMethod(localCopy.data(), [localCopy, name] { + localCopy->createCardConnectionWorker(name); + }, Qt::QueuedConnection); } else { @@ -39,7 +49,6 @@ void CreateCardConnectionCommand::execute() QSharedPointer command(this, &QObject::deleteLater); Q_EMIT fireCommandDone(command); } - } diff --git a/src/card/base/command/CreateCardConnectionCommand.h b/src/card/base/command/CreateCardConnectionCommand.h index bd5bbac..f792a3c 100644 --- a/src/card/base/command/CreateCardConnectionCommand.h +++ b/src/card/base/command/CreateCardConnectionCommand.h @@ -12,27 +12,30 @@ #include #include +class test_StateConnectCard; namespace governikus { -class CardConnectionWorker; class ReaderManagerWorker; - class CreateCardConnectionCommand : public QObject { Q_OBJECT private: + friend class ::test_StateConnectCard; const QString mReaderName; QPointer mReaderManagerWorker; QSharedPointer mCardConnection; + Q_INVOKABLE void execute(); + public: CreateCardConnectionCommand(const QString& pReaderName, const QPointer& pReaderManagerWorker); - Q_INVOKABLE void execute(); + + void run(); QSharedPointer getCardConnection() const; const QString& getReaderName() const; @@ -44,4 +47,4 @@ class CreateCardConnectionCommand }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/command/DestroyPaceChannelCommand.cpp b/src/card/base/command/DestroyPaceChannelCommand.cpp index 7d16f8c..78c684e 100644 --- a/src/card/base/command/DestroyPaceChannelCommand.cpp +++ b/src/card/base/command/DestroyPaceChannelCommand.cpp @@ -15,11 +15,6 @@ DestroyPaceChannelCommand::DestroyPaceChannelCommand(QSharedPointerdestroyPaceChannel(); diff --git a/src/card/base/command/DestroyPaceChannelCommand.h b/src/card/base/command/DestroyPaceChannelCommand.h index 4f6b5e2..9361731 100644 --- a/src/card/base/command/DestroyPaceChannelCommand.h +++ b/src/card/base/command/DestroyPaceChannelCommand.h @@ -8,22 +8,25 @@ #include "BaseCardCommand.h" +class test_DestroyPaceChannelCommand; + namespace governikus { -class CardConnection; - class DestroyPaceChannelCommand : public BaseCardCommand { Q_OBJECT + private: + friend class ::test_DestroyPaceChannelCommand; + protected: virtual void internalExecute() override; - virtual ~DestroyPaceChannelCommand() override; + virtual ~DestroyPaceChannelCommand() override = default; public: DestroyPaceChannelCommand(QSharedPointer pCardConnectionWorker); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/command/DidAuthenticateEAC1Command.cpp b/src/card/base/command/DidAuthenticateEAC1Command.cpp index 6c0acca..f0b810e 100644 --- a/src/card/base/command/DidAuthenticateEAC1Command.cpp +++ b/src/card/base/command/DidAuthenticateEAC1Command.cpp @@ -11,6 +11,7 @@ Q_DECLARE_LOGGING_CATEGORY(card) + using namespace governikus; @@ -21,12 +22,6 @@ DidAuthenticateEAC1Command::DidAuthenticateEAC1Command(QSharedPointer pCardConnectionWorker); - const QByteArray& getChallenge() const { return mChallenge; @@ -39,4 +41,4 @@ class DidAuthenticateEAC1Command }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/command/DidAuthenticateEAC2Command.cpp b/src/card/base/command/DidAuthenticateEAC2Command.cpp index ce66b0d..d62a7c7 100644 --- a/src/card/base/command/DidAuthenticateEAC2Command.cpp +++ b/src/card/base/command/DidAuthenticateEAC2Command.cpp @@ -34,12 +34,6 @@ DidAuthenticateEAC2Command::DidAuthenticateEAC2Command(QSharedPointerreadFile(FileRef::efCardSecurity(), efCardSecurityBytes); @@ -133,7 +126,7 @@ CardReturnCode DidAuthenticateEAC2Command::putCertificateChain(const CVCertifica if (mseResult.getReturnCode() != StatusCode::SUCCESS) { - qCWarning(card) << "TA MSE:Set DST failed: " << mseResult.getReturnCode(); + qCWarning(card) << "TA MSE:Set DST failed:" << mseResult.getReturnCode(); return CardReturnCode::PROTOCOL_ERROR; } @@ -152,7 +145,7 @@ CardReturnCode DidAuthenticateEAC2Command::putCertificateChain(const CVCertifica if (psoResult.getReturnCode() != StatusCode::SUCCESS) { - qCWarning(card) << "TA PSO:Verify Certificate failed: " << psoResult.getReturnCode(); + qCWarning(card) << "TA PSO:Verify Certificate failed:" << psoResult.getReturnCode(); return CardReturnCode::PROTOCOL_ERROR; } } @@ -179,12 +172,12 @@ CardReturnCode DidAuthenticateEAC2Command::performTerminalAuthentication(const Q CardReturnCode returnCode = mCardConnectionWorker->transmit(mseBuilder.build(), mseResult); if (returnCode != CardReturnCode::OK) { - qCWarning(card) << "TA MSE:Set AT failed: " << CardReturnCodeUtil::toGlobalStatus(returnCode); + qCWarning(card) << "TA MSE:Set AT failed:" << CardReturnCodeUtil::toGlobalStatus(returnCode); return returnCode; } if (mseResult.getReturnCode() != StatusCode::SUCCESS) { - qCWarning(card) << "TA MSE:Set AT failed: " << mseResult.getReturnCode(); + qCWarning(card) << "TA MSE:Set AT failed:" << mseResult.getReturnCode(); return CardReturnCode::PROTOCOL_ERROR; } @@ -195,12 +188,12 @@ CardReturnCode DidAuthenticateEAC2Command::performTerminalAuthentication(const Q returnCode = mCardConnectionWorker->transmit(eaBuilder.build(), eaResult); if (returnCode != CardReturnCode::OK) { - qCWarning(card) << "TA External Authenticate failed: " << CardReturnCodeUtil::toGlobalStatus(returnCode); + qCWarning(card) << "TA External Authenticate failed:" << CardReturnCodeUtil::toGlobalStatus(returnCode); return returnCode; } if (eaResult.getReturnCode() != StatusCode::SUCCESS) { - qCWarning(card) << "TA External Authenticate failed: " << eaResult.getReturnCode(); + qCWarning(card) << "TA External Authenticate failed:" << eaResult.getReturnCode(); return CardReturnCode::PROTOCOL_ERROR; } @@ -226,7 +219,7 @@ CardReturnCode DidAuthenticateEAC2Command::performChipAuthentication(QSharedPoin } if (mseResult.getReturnCode() != StatusCode::SUCCESS) { - qCWarning(card) << "CA MSE:Set AT failed: " << mseResult.getReturnCode(); + qCWarning(card) << "CA MSE:Set AT failed:" << mseResult.getReturnCode(); return CardReturnCode::PROTOCOL_ERROR; } @@ -242,7 +235,7 @@ CardReturnCode DidAuthenticateEAC2Command::performChipAuthentication(QSharedPoin } if (gaResponse.getReturnCode() != StatusCode::SUCCESS) { - qCWarning(card) << "CA General Authenticate failed: " << gaResponse.getReturnCode(); + qCWarning(card) << "CA General Authenticate failed:" << gaResponse.getReturnCode(); return CardReturnCode::PROTOCOL_ERROR; } pNonceAsHex += gaResponse.getNonce().toHex(); diff --git a/src/card/base/command/DidAuthenticateEAC2Command.h b/src/card/base/command/DidAuthenticateEAC2Command.h index 1d2eb33..35f4606 100644 --- a/src/card/base/command/DidAuthenticateEAC2Command.h +++ b/src/card/base/command/DidAuthenticateEAC2Command.h @@ -9,18 +9,18 @@ #include "asn1/CVCertificateChain.h" #include "BaseCardCommand.h" +class test_CardConnection; + namespace governikus { -class CardConnection; -class ChipAuthenticationInfo; - class DidAuthenticateEAC2Command : public BaseCardCommand { Q_OBJECT private: + friend class ::test_CardConnection; CVCertificateChain mCvcChain; QString mEphemeralPublicKeyAsHex; QString mSignatureAsHex; @@ -42,7 +42,7 @@ class DidAuthenticateEAC2Command protected: virtual void internalExecute() override; - virtual ~DidAuthenticateEAC2Command() override; + virtual ~DidAuthenticateEAC2Command() override = default; public: DidAuthenticateEAC2Command(QSharedPointer pCardConnectionWorker, @@ -70,4 +70,4 @@ class DidAuthenticateEAC2Command }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/command/EstablishPaceChannelCommand.cpp b/src/card/base/command/EstablishPaceChannelCommand.cpp index 0a4bbea..c4ea8e5 100644 --- a/src/card/base/command/EstablishPaceChannelCommand.cpp +++ b/src/card/base/command/EstablishPaceChannelCommand.cpp @@ -2,15 +2,13 @@ * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany */ -#include "CardConnection.h" #include "EstablishPaceChannelCommand.h" - using namespace governikus; EstablishPaceChannelCommand::EstablishPaceChannelCommand(QSharedPointer pCardConnectionWorker, - PACE_PASSWORD_ID pPacePasswordId, + PacePasswordId pPacePasswordId, const QString& pPacePassword, const QByteArray& pEffectiveChat, const QByteArray& pCertificateDescription) : BaseCardCommand(pCardConnectionWorker) , mPacePasswordId(pPacePasswordId) @@ -20,16 +18,11 @@ EstablishPaceChannelCommand::EstablishPaceChannelCommand(QSharedPointer pCardConnectionWorker, - PACE_PASSWORD_ID pPacePasswordId, const QString& pPacePassword, const QByteArray& pEffectiveChat, const QByteArray& pCertificateDescription); + PacePasswordId pPacePasswordId, const QString& pPacePassword, const QByteArray& pEffectiveChat, const QByteArray& pCertificateDescription); - const EstablishPACEChannelOutput& getPaceOutput() const; + const EstablishPaceChannelOutput& getPaceOutput() const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/command/SetEidPinCommand.cpp b/src/card/base/command/SetEidPinCommand.cpp index 121d2a5..e2d94b6 100644 --- a/src/card/base/command/SetEidPinCommand.cpp +++ b/src/card/base/command/SetEidPinCommand.cpp @@ -2,10 +2,8 @@ * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany */ -#include "CardConnection.h" #include "SetEidPinCommand.h" - using namespace governikus; @@ -19,11 +17,6 @@ SetEidPinCommand::SetEidPinCommand(QSharedPointer pCardCon } -SetEidPinCommand::~SetEidPinCommand() -{ -} - - void SetEidPinCommand::internalExecute() { mReturnCode = mCardConnectionWorker->setEidPin(mNewPin, mTimeoutSeconds, mResponseApdu); diff --git a/src/card/base/command/SetEidPinCommand.h b/src/card/base/command/SetEidPinCommand.h index 10d9049..e4c722f 100644 --- a/src/card/base/command/SetEidPinCommand.h +++ b/src/card/base/command/SetEidPinCommand.h @@ -8,24 +8,26 @@ #include "BaseCardCommand.h" +class test_SetEidPinCommand; + namespace governikus { -class CardConnection; - class SetEidPinCommand : public BaseCardCommand { Q_OBJECT private: + friend class ::test_SetEidPinCommand; + QString mNewPin; quint8 mTimeoutSeconds; ResponseApdu mResponseApdu; protected: virtual void internalExecute() override; - virtual ~SetEidPinCommand() override; + virtual ~SetEidPinCommand() override = default; public: SetEidPinCommand(QSharedPointer pCardConnectionWorker, @@ -34,4 +36,4 @@ class SetEidPinCommand const ResponseApdu& getResponseApdu() const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/command/TransmitCommand.cpp b/src/card/base/command/TransmitCommand.cpp index 1fe84a0..e9c3573 100644 --- a/src/card/base/command/TransmitCommand.cpp +++ b/src/card/base/command/TransmitCommand.cpp @@ -27,12 +27,6 @@ TransmitCommand::TransmitCommand(QSharedPointer pCardConne } -TransmitCommand::~TransmitCommand() -{ - -} - - bool TransmitCommand::isAcceptable(const InputAPDUInfo& pInputApduInfo, const ResponseApdu& pResponse) { if (pInputApduInfo.getAcceptableStatusCodes().isEmpty()) diff --git a/src/card/base/command/TransmitCommand.h b/src/card/base/command/TransmitCommand.h index 1b6eacc..2ea235a 100644 --- a/src/card/base/command/TransmitCommand.h +++ b/src/card/base/command/TransmitCommand.h @@ -10,12 +10,11 @@ #include "InputAPDUInfo.h" class test_TransmitCommand; +class test_CardConnection; namespace governikus { -class CardConnection; - class TransmitCommand : public BaseCardCommand { @@ -23,6 +22,7 @@ class TransmitCommand private: friend class ::test_TransmitCommand; + friend class ::test_CardConnection; const QVector mInputApduInfos; const QString mSlotHandle; @@ -32,7 +32,7 @@ class TransmitCommand protected: virtual void internalExecute() override; - virtual ~TransmitCommand() override; + virtual ~TransmitCommand() override = default; public: TransmitCommand(QSharedPointer pCardConnectionWorker, @@ -53,4 +53,4 @@ class TransmitCommand }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/command/UnblockPinCommand.cpp b/src/card/base/command/UnblockPinCommand.cpp index 708b3aa..b27d4f9 100644 --- a/src/card/base/command/UnblockPinCommand.cpp +++ b/src/card/base/command/UnblockPinCommand.cpp @@ -2,10 +2,8 @@ * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany */ -#include "CardConnection.h" #include "UnblockPinCommand.h" - using namespace governikus; @@ -16,28 +14,23 @@ UnblockPinCommand::UnblockPinCommand(QSharedPointer pCardC } -UnblockPinCommand::~UnblockPinCommand() -{ - -} - - void UnblockPinCommand::internalExecute() { - mReturnCode = mCardConnectionWorker->updateRetryCounter(); - if (mReturnCode != CardReturnCode::OK) + if (!mCardConnectionWorker->getReaderInfo().hasEidCard()) { + mReturnCode = CardReturnCode::CARD_NOT_FOUND; return; } - if (mCardConnectionWorker->getReaderInfo().getRetryCounter() != 0 || mCardConnectionWorker->getReaderInfo().isPinDeactivated()) + Q_ASSERT(mCardConnectionWorker->getReaderInfo().getRetryCounter() == 0); + if (mCardConnectionWorker->getReaderInfo().getRetryCounter() > 0 || mCardConnectionWorker->getReaderInfo().isPinDeactivated()) { mReturnCode = CardReturnCode::PIN_NOT_BLOCKED; return; } - EstablishPACEChannelOutput output; - mReturnCode = mCardConnectionWorker->establishPaceChannel(PACE_PASSWORD_ID::PACE_PUK, mPuk, output); + EstablishPaceChannelOutput output; + mReturnCode = mCardConnectionWorker->establishPaceChannel(PacePasswordId::PACE_PUK, mPuk, output); if (mReturnCode != CardReturnCode::OK) { return; diff --git a/src/card/base/command/UnblockPinCommand.h b/src/card/base/command/UnblockPinCommand.h index ebb0c36..fbfb552 100644 --- a/src/card/base/command/UnblockPinCommand.h +++ b/src/card/base/command/UnblockPinCommand.h @@ -8,26 +8,27 @@ #include "BaseCardCommand.h" +class test_UnblockPinCommand; + namespace governikus { -class CardConnection; - class UnblockPinCommand : public BaseCardCommand { Q_OBJECT private: + friend class ::test_UnblockPinCommand; QString mPuk; protected: virtual void internalExecute() override; - virtual ~UnblockPinCommand() override; + virtual ~UnblockPinCommand() override = default; public: UnblockPinCommand(QSharedPointer pCardConnectionWorker, const QString& pPuk); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/command/UpdateRetryCounterCommand.cpp b/src/card/base/command/UpdateRetryCounterCommand.cpp index 84594dd..74746ef 100644 --- a/src/card/base/command/UpdateRetryCounterCommand.cpp +++ b/src/card/base/command/UpdateRetryCounterCommand.cpp @@ -2,10 +2,8 @@ * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany */ -#include "CardConnection.h" #include "UpdateRetryCounterCommand.h" - using namespace governikus; @@ -15,12 +13,6 @@ UpdateRetryCounterCommand::UpdateRetryCounterCommand(QSharedPointerupdateRetryCounter(); diff --git a/src/card/base/command/UpdateRetryCounterCommand.h b/src/card/base/command/UpdateRetryCounterCommand.h index 084f024..9c2d152 100644 --- a/src/card/base/command/UpdateRetryCounterCommand.h +++ b/src/card/base/command/UpdateRetryCounterCommand.h @@ -8,23 +8,26 @@ #include "BaseCardCommand.h" +class test_UpdateRetryCounterCommand; + namespace governikus { -class CardConnection; - class UpdateRetryCounterCommand : public BaseCardCommand { Q_OBJECT + private: + friend class ::test_UpdateRetryCounterCommand; + protected: virtual void internalExecute() override; - virtual ~UpdateRetryCounterCommand() override; + virtual ~UpdateRetryCounterCommand() override = default; public: UpdateRetryCounterCommand(QSharedPointer pCardConnectionWorker); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/pace/CipherMac.cpp b/src/card/base/pace/CipherMac.cpp index 3281a01..977943b 100644 --- a/src/card/base/pace/CipherMac.cpp +++ b/src/card/base/pace/CipherMac.cpp @@ -2,14 +2,14 @@ * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany */ -#include "asn1/KnownOIDs.h" #include "pace/CipherMac.h" +#include "asn1/KnownOIDs.h" + #include #include #include - using namespace governikus; @@ -46,12 +46,12 @@ CipherMac::CipherMac(const QByteArray& pPaceAlgorithm, const QByteArray& pKeyByt } else { - qCCritical(card) << "Unknown algorithm: " << pPaceAlgorithm; + qCCritical(card) << "Unknown algorithm:" << pPaceAlgorithm; return; } if (mKeyBytes.size() != EVP_CIPHER_key_length(cipher)) { - qCCritical(card) << "Key has wrong size (expected/got):" << EVP_CIPHER_key_length(cipher) << "/" << mKeyBytes.size(); + qCCritical(card) << "Key has wrong size (expected/got):" << EVP_CIPHER_key_length(cipher) << '/' << mKeyBytes.size(); return; } mCtx = CMAC_CTX_new(); diff --git a/src/card/base/pace/CipherMac.h b/src/card/base/pace/CipherMac.h index d60239e..917a787 100644 --- a/src/card/base/pace/CipherMac.h +++ b/src/card/base/pace/CipherMac.h @@ -12,7 +12,7 @@ namespace governikus { -class CipherMac +class CipherMac final { private: QByteArray mKeyBytes; @@ -28,7 +28,7 @@ class CipherMac * \param pKeyBytes the bytes of the key */ CipherMac(const QByteArray& pPaceAlgorithm, const QByteArray& pKeyBytes); - virtual ~CipherMac(); + ~CipherMac(); /*! * Returns true, if initialization succeeded, i.e. the algorithm is known, supported and the key bytes have correct size. @@ -43,4 +43,4 @@ class CipherMac QByteArray generate(const QByteArray& pMessage); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/pace/DomainParameterMapping.h b/src/card/base/pace/DomainParameterMapping.h index 9aef6fe..e5dc89b 100644 --- a/src/card/base/pace/DomainParameterMapping.h +++ b/src/card/base/pace/DomainParameterMapping.h @@ -16,15 +16,7 @@ template class DomainParameterMapping { public: - DomainParameterMapping() - { - } - - - virtual ~DomainParameterMapping() - { - } - + virtual ~DomainParameterMapping() = default; /*! * \brief Generates the terminal's mapping data, that will be send to the card. @@ -42,4 +34,4 @@ class DomainParameterMapping const QByteArray& pNonce) = 0; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/pace/EstablishPACEChannelCode.h b/src/card/base/pace/EstablishPACEChannelCode.h deleted file mode 100644 index 6524d99..0000000 --- a/src/card/base/pace/EstablishPACEChannelCode.h +++ /dev/null @@ -1,102 +0,0 @@ -/*! - * \brief EstablishPACEChannel error code definitions - * - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "EnumHelper.h" - -namespace governikus -{ - - -// -// EstablishPACEChannel error codes according to TR-03119, D.1.2 -// -defineTypedEnumType(EstablishPACEChannelErrorCode, quint32, - NoError - = 0x00000000, - - // Error in input data - InconsistentLengthsInInput - = 0xD0000001, - UnexpectedDataInInput - = 0xD0000002, - UnexpectedCombinationOfDataInInput - = 0xD0000003, - - // Errors during protocol execution - SyntaxErrorInTLVResponse - = 0xE0000001, - UnexpectedOrMissingObjectInTLVResponse - = 0xE0000002, - UnknownPasswordID - = 0xE0000003, - WrongAuthenticationToken - = 0xE0000006, - CertificateChainForTerminalAuthenticationCannotBeBuilt - = 0xE0000007, - UnexpectedDataStructureInResponseToChipAuthentication - = 0xE0000008, - PassiveAuthenticationFailed - = 0xE0000009, - IncorrectTokenForChipAuthentication - = 0xE000000A, - - // Response APDU of the card reports error (status code SW1SW2) - // Select EF.CardAccess - // 0xF000SW1SW2 - // Read Binary EF.CardAccess - // 0xF001SW1SW2 - // MSE: Set AT for PACE - // 0xF002SW1SW2 - // General Authenticate Step 1 - 4 - // 0xF003SW1SW2 - 0xF006SW1SW2 - - // A specific case with "SW1 == 0x63 == warning" and a "dummy SW2". - GeneralAuthenticateStep1_4_Warning - = 0xf0066300, - - // APDU created by PCD for terminal/chip authentication reports error (status code SW1SW2) - // MSE: Set DST (first certificate) - // 0xF800SW1SW2 - // PSO: Verify Certificate (first certificate) - // 0xF801SW1SW2 - // MSE: Set DST (second certificate) - // 0xF802SW1SW2 - // PSO: Verify Certificate (second certificate) - // 0xF803SW1SW2 - // MSE: Set DST (third certificate) - // 0xF804SW1SW2 - // PSO: Verify Certificate (third certificate) - // 0xF805SW1SW2 - // MSE: Set AT for terminal authentication - // 0xF806SW1SW2 - // Get Challenge - // 0xF807SW1SW2 - // External Authenticate - // 0xF808SW1SW2 - // Select EF.CardSecurity - // 0xF809SW1SW2 - // Read Binary EF.CardSecurity - // 0xF80ASW1SW2 - // MSE: Set AT for chip authentication - // 0xF80BSW1SW2 - // General Authenticate - // 0xF80CSW1SW2 - - // Others - CommunicationAbort - = 0xF0100001, - NoCard - = 0xF0100002, - Abort - = 0xF0200001, - Timeout - = 0xF0200002 - ) - - -} diff --git a/src/card/base/pace/EstablishPaceChannelCode.h b/src/card/base/pace/EstablishPaceChannelCode.h new file mode 100644 index 0000000..0bed38d --- /dev/null +++ b/src/card/base/pace/EstablishPaceChannelCode.h @@ -0,0 +1,101 @@ +/*! + * \brief EstablishPaceChannel error code definitions + * + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "EnumHelper.h" + +namespace governikus +{ + +// +// EstablishPaceChannel error codes according to TR-03119, D.1.2 +// +defineTypedEnumType(EstablishPaceChannelErrorCode, quint32, + NoError + = 0x00000000, + + // Error in input data + InconsistentLengthsInInput + = 0xD0000001, + UnexpectedDataInInput + = 0xD0000002, + UnexpectedCombinationOfDataInInput + = 0xD0000003, + + // Errors during protocol execution + SyntaxErrorInTLVResponse + = 0xE0000001, + UnexpectedOrMissingObjectInTLVResponse + = 0xE0000002, + UnknownPasswordID + = 0xE0000003, + WrongAuthenticationToken + = 0xE0000006, + CertificateChainForTerminalAuthenticationCannotBeBuilt + = 0xE0000007, + UnexpectedDataStructureInResponseToChipAuthentication + = 0xE0000008, + PassiveAuthenticationFailed + = 0xE0000009, + IncorrectTokenForChipAuthentication + = 0xE000000A, + + // Response APDU of the card reports error (status code SW1SW2) + // Select EF.CardAccess + // 0xF000SW1SW2 + // Read Binary EF.CardAccess + // 0xF001SW1SW2 + // MSE: Set AT for PACE + // 0xF002SW1SW2 + // General Authenticate Step 1 - 4 + // 0xF003SW1SW2 - 0xF006SW1SW2 + + // A specific case with "SW1 == 0x63 == warning" and a "dummy SW2". + GeneralAuthenticateStep1_4_Warning + = 0xf0066300, + + // APDU created by PCD for terminal/chip authentication reports error (status code SW1SW2) + // MSE: Set DST (first certificate) + // 0xF800SW1SW2 + // PSO: Verify Certificate (first certificate) + // 0xF801SW1SW2 + // MSE: Set DST (second certificate) + // 0xF802SW1SW2 + // PSO: Verify Certificate (second certificate) + // 0xF803SW1SW2 + // MSE: Set DST (third certificate) + // 0xF804SW1SW2 + // PSO: Verify Certificate (third certificate) + // 0xF805SW1SW2 + // MSE: Set AT for terminal authentication + // 0xF806SW1SW2 + // Get Challenge + // 0xF807SW1SW2 + // External Authenticate + // 0xF808SW1SW2 + // Select EF.CardSecurity + // 0xF809SW1SW2 + // Read Binary EF.CardSecurity + // 0xF80ASW1SW2 + // MSE: Set AT for chip authentication + // 0xF80BSW1SW2 + // General Authenticate + // 0xF80CSW1SW2 + + // Others + CommunicationAbort + = 0xF0100001, + NoCard + = 0xF0100002, + Abort + = 0xF0200001, + Timeout + = 0xF0200002 + ) + + +} // namespace governikus diff --git a/src/card/base/pace/KeyAgreement.cpp b/src/card/base/pace/KeyAgreement.cpp index d311bb3..ce52502 100644 --- a/src/card/base/pace/KeyAgreement.cpp +++ b/src/card/base/pace/KeyAgreement.cpp @@ -5,14 +5,13 @@ #include "KeyAgreement.h" -#include "asn1/PACEInfo.h" +#include "asn1/PaceInfo.h" #include "Commands.h" #include "GlobalStatus.h" #include "pace/CipherMac.h" #include "pace/ec/EcdhKeyAgreement.h" #include "pace/KeyDerivationFunction.h" #include "pace/SymmetricCipher.h" -#include "PersoSimWorkaround.h" #include @@ -25,12 +24,7 @@ Q_DECLARE_LOGGING_CATEGORY(card) static QString getResponseErrorString(CardReturnCode pReturnCode, StatusCode pResponseReturnCode) { - QString errorString = CardReturnCodeUtil::toGlobalStatus(pReturnCode).toErrorDescription(); - if (pReturnCode == CardReturnCode::OK) - { - errorString += QStringLiteral(" | ") + pResponseReturnCode; - } - return errorString; + return CardReturnCodeUtil::toGlobalStatus(pReturnCode).toErrorDescription() + QStringLiteral(" | ") + pResponseReturnCode; } @@ -44,13 +38,22 @@ static CardOperationResult makeTransmitResult(CardReturnCode pReturn return CardOperationResult(pReturnCode, pResultData); } - const CardReturnCode newReturnCode = pReturnCode == CardReturnCode::OK ? CardReturnCode::PROTOCOL_ERROR : CardReturnCode::COMMAND_FAILED; - qCCritical(card).noquote() << pLogMessage << getResponseErrorString(pReturnCode, pResponseReturnCode); + CardReturnCode newReturnCode = CardReturnCode::COMMAND_FAILED; + if (pResponseReturnCode == StatusCode::EMPTY) + { + newReturnCode = CardReturnCode::RETRY_ALLOWED; + } + else if (pReturnCode == CardReturnCode::OK) + { + newReturnCode = CardReturnCode::PROTOCOL_ERROR; + } + + qCCritical(card).noquote() << pLogMessage << getResponseErrorString(newReturnCode, pResponseReturnCode); return CardOperationResult(newReturnCode, QByteArray()); } -QSharedPointer KeyAgreement::create(const QSharedPointer& pPaceInfo, +QSharedPointer KeyAgreement::create(const QSharedPointer& pPaceInfo, QSharedPointer pCardConnectionWorker) { if (pPaceInfo->getKeyAgreementType() == KeyAgreementType::ECDH) @@ -65,7 +68,7 @@ QSharedPointer KeyAgreement::create(const QSharedPointer& pPaceInfo, const QSharedPointer& pCardConnectionWorker) +KeyAgreement::KeyAgreement(const QSharedPointer& pPaceInfo, const QSharedPointer& pCardConnectionWorker) : mCardConnectionWorker(pCardConnectionWorker) , mEncryptionKey() , mMacKey() @@ -93,8 +96,11 @@ KeyAgreementStatus KeyAgreement::perform(const QString& pPin) case CardReturnCode::COMMAND_FAILED: return KeyAgreementStatus::COMMUNICATION_ERROR; + case CardReturnCode::RETRY_ALLOWED: + return KeyAgreementStatus::RETRY_ALLOWED; + default: - ; + {} } QByteArray nonce = nonceResult.getPayload(); @@ -107,8 +113,11 @@ KeyAgreementStatus KeyAgreement::perform(const QString& pPin) case CardReturnCode::PROTOCOL_ERROR: return KeyAgreementStatus::PROTOCOL_ERROR; + case CardReturnCode::RETRY_ALLOWED: + return KeyAgreementStatus::RETRY_ALLOWED; + default: - ; + {} } QByteArray sharedSecret = sharedSecretResult.getPayload(); @@ -143,11 +152,14 @@ KeyAgreementStatus KeyAgreement::performMutualAuthenticate() QByteArray mutualAuthenticationCardData = cmac.generate(uncompressedCardPublicKey); QSharedPointer response = transmitGAMutualAuthentication(mutualAuthenticationCardData); - if (response->getReturnCode() == StatusCode::VERIFICATION_FAILED || + if (response->getReturnCode() == StatusCode::EMPTY) + { + return KeyAgreementStatus::RETRY_ALLOWED; + } + else if (response->getReturnCode() == StatusCode::VERIFICATION_FAILED || response->getReturnCode() == StatusCode::PIN_BLOCKED || response->getReturnCode() == StatusCode::PIN_SUSPENDED || - response->getReturnCode() == StatusCode::PIN_RETRY_COUNT_2 || - PersoSimWorkaround::isWrongCanEntry(response)) + response->getReturnCode() == StatusCode::PIN_RETRY_COUNT_2) { return KeyAgreementStatus::FAILED; } @@ -161,7 +173,7 @@ KeyAgreementStatus KeyAgreement::performMutualAuthenticate() if (mutualAuthenticationTerminalData != response->getAuthenticationToken()) { - qCCritical(card) << "Error on mutual authentication "; + qCCritical(card) << "Error on mutual authentication"; return KeyAgreementStatus::PROTOCOL_ERROR; } @@ -209,7 +221,7 @@ QSharedPointer KeyAgreement::transmitGAMutualAut { GABuilder commandBuilder(CommandApdu::CLA); commandBuilder.setPaceAuthenticationToken(pMutualAuthenticationData); - QSharedPointer response(new GAMutualAuthenticationResponse()); + auto response = QSharedPointer::create(); CardReturnCode returnCode = mCardConnectionWorker->transmit(commandBuilder.build(), *response); if (returnCode != CardReturnCode::OK || response->getReturnCode() != StatusCode::SUCCESS) diff --git a/src/card/base/pace/KeyAgreement.h b/src/card/base/pace/KeyAgreement.h index aac6ca8..9b69e64 100644 --- a/src/card/base/pace/KeyAgreement.h +++ b/src/card/base/pace/KeyAgreement.h @@ -6,7 +6,6 @@ #pragma once -#include "asn1/SecurityInfo.h" #include "CardConnectionWorker.h" #include "CardOperationResult.h" #include "GeneralAuthenticateResponse.h" @@ -17,13 +16,10 @@ namespace governikus { - -class PACEInfo; - - enum class KeyAgreementStatus { SUCCESS, + RETRY_ALLOWED, COMMUNICATION_ERROR, FAILED, PROTOCOL_ERROR @@ -74,10 +70,10 @@ class KeyAgreement KeyAgreementStatus performMutualAuthenticate(); protected: - const QSharedPointer mPaceInfo; + const QSharedPointer mPaceInfo; KeyDerivationFunction mKeyDerivationFunction; - KeyAgreement(const QSharedPointer& pPaceInfo, const QSharedPointer& pCardConnectionWorker); + KeyAgreement(const QSharedPointer& pPaceInfo, const QSharedPointer& pCardConnectionWorker); /*! * \brief Transmit the General Authenticate (Mapping Data) command to the card. @@ -121,7 +117,7 @@ class KeyAgreement * \param pCardConnectionWorker the reader connection to transmit card commands * \return new instance */ - static QSharedPointer create(const QSharedPointer& pPaceInfo, + static QSharedPointer create(const QSharedPointer& pPaceInfo, QSharedPointer pCardConnectionWorker); virtual ~KeyAgreement(); @@ -159,4 +155,4 @@ class KeyAgreement const QByteArray& getCarPrev() const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/pace/KeyDerivationFunction.cpp b/src/card/base/pace/KeyDerivationFunction.cpp index 8815f80..fbf6566 100644 --- a/src/card/base/pace/KeyDerivationFunction.cpp +++ b/src/card/base/pace/KeyDerivationFunction.cpp @@ -2,9 +2,10 @@ * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany */ -#include "asn1/KnownOIDs.h" #include "pace/KeyDerivationFunction.h" +#include "asn1/KnownOIDs.h" + #include #include @@ -45,16 +46,11 @@ KeyDerivationFunction::KeyDerivationFunction(const QByteArray& pPaceAlgorithm) } else { - qCCritical(card) << "Unknown algorithm: " << pPaceAlgorithm; + qCCritical(card) << "Unknown algorithm:" << pPaceAlgorithm; } } -KeyDerivationFunction::~KeyDerivationFunction() -{ -} - - bool KeyDerivationFunction::isInitialized() { return mKeySize != 0; diff --git a/src/card/base/pace/KeyDerivationFunction.h b/src/card/base/pace/KeyDerivationFunction.h index 5091dbe..07427ed 100644 --- a/src/card/base/pace/KeyDerivationFunction.h +++ b/src/card/base/pace/KeyDerivationFunction.h @@ -13,7 +13,7 @@ namespace governikus { -class KeyDerivationFunction +class KeyDerivationFunction final { private: QCryptographicHash::Algorithm mHashAlgorithm; @@ -27,7 +27,7 @@ class KeyDerivationFunction * PACE protocol of id_PACE::DH::GM_AES_CBC_CMAC_128 will result in SHA256 to be used internally to derive keys. */ KeyDerivationFunction(const QByteArray& pPaceAlgorithm); - virtual ~KeyDerivationFunction(); + ~KeyDerivationFunction() = default; /*! * Returns true, if initialization succeeded, i.e. the algorithm is known, supported and the key bytes have correct size. @@ -57,4 +57,4 @@ class KeyDerivationFunction }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/pace/PaceHandler.cpp b/src/card/base/pace/PaceHandler.cpp index dfbffe3..5d743b6 100644 --- a/src/card/base/pace/PaceHandler.cpp +++ b/src/card/base/pace/PaceHandler.cpp @@ -5,14 +5,12 @@ #include "pace/PaceHandler.h" #include "asn1/KnownOIDs.h" -#include "asn1/PACEInfo.h" +#include "asn1/PaceInfo.h" #include "Commands.h" -#include "FileRef.h" #include "pace/ec/EllipticCurveFactory.h" #include "pace/KeyAgreement.h" #include "PersoSimWorkaround.h" -#include #include using namespace governikus; @@ -44,7 +42,7 @@ QByteArray PaceHandler::getPaceProtocol() const } -CardReturnCode PaceHandler::establishPaceChannel(PACE_PASSWORD_ID pPasswordId, const QString& pPassword) +CardReturnCode PaceHandler::establishPaceChannel(PacePasswordId pPasswordId, const QString& pPassword) { auto efCardAccess = mCardConnectionWorker->getReaderInfo().getCardInfo().getEfCardAccess(); if (!initialize(efCardAccess)) @@ -60,38 +58,54 @@ CardReturnCode PaceHandler::establishPaceChannel(PACE_PASSWORD_ID pPasswordId, c case CardReturnCode::COMMAND_FAILED: return CardReturnCode::COMMAND_FAILED; + case CardReturnCode::RETRY_ALLOWED: + return CardReturnCode::RETRY_ALLOWED; + default: - ; + {} } KeyAgreementStatus keyAgreementStatus = mKeyAgreement->perform(pPassword); - if (keyAgreementStatus == KeyAgreementStatus::PROTOCOL_ERROR) + switch (keyAgreementStatus) { - return CardReturnCode::PROTOCOL_ERROR; - } - else if (keyAgreementStatus == KeyAgreementStatus::FAILED) - { - switch (pPasswordId) - { - case PACE_PASSWORD_ID::PACE_MRZ: - // No separate error code (yet). - case PACE_PASSWORD_ID::PACE_CAN: - return CardReturnCode::INVALID_CAN; + case KeyAgreementStatus::RETRY_ALLOWED: + return CardReturnCode::RETRY_ALLOWED; - case PACE_PASSWORD_ID::PACE_PIN: - return CardReturnCode::INVALID_PIN; + case KeyAgreementStatus::PROTOCOL_ERROR: + return CardReturnCode::PROTOCOL_ERROR; - case PACE_PASSWORD_ID::PACE_PUK: - return CardReturnCode::INVALID_PUK; - } + case KeyAgreementStatus::COMMUNICATION_ERROR: + return CardReturnCode::COMMAND_FAILED; + + case KeyAgreementStatus::FAILED: + switch (pPasswordId) + { + case PacePasswordId::PACE_MRZ: + // No separate error code (yet). + case PacePasswordId::PACE_CAN: + return CardReturnCode::INVALID_CAN; + + case PacePasswordId::PACE_PIN: + return CardReturnCode::INVALID_PIN; + + case PacePasswordId::PACE_PUK: + return CardReturnCode::INVALID_PUK; + + case PacePasswordId::UNKNOWN: + return CardReturnCode::UNKNOWN; + } + return CardReturnCode::UNKNOWN; + + case KeyAgreementStatus::SUCCESS: + mEncryptionKey = mKeyAgreement->getEncryptionKey(); + mMacKey = mKeyAgreement->getMacKey(); + mCarCurr = mKeyAgreement->getCarCurr(); + mCarPrev = mKeyAgreement->getCarPrev(); + mIdIcc = mKeyAgreement->getCompressedCardPublicKey(); + qCDebug(card) << "Pace channel established"; + return CardReturnCode::OK; } - mEncryptionKey = mKeyAgreement->getEncryptionKey(); - mMacKey = mKeyAgreement->getMacKey(); - mCarCurr = mKeyAgreement->getCarCurr(); - mCarPrev = mKeyAgreement->getCarPrev(); - mIdIcc = mKeyAgreement->getCompressedCardPublicKey(); - qCDebug(card) << "Pace channel established"; - return CardReturnCode::OK; + Q_UNREACHABLE(); } @@ -102,7 +116,7 @@ bool PaceHandler::initialize(const QSharedPointer& pEfCardAc return false; } - const auto& infos = pEfCardAccess->getPACEInfos(); + const auto& infos = pEfCardAccess->getPaceInfos(); for (const auto& paceInfo : infos) { if (isSupportedProtocol(paceInfo)) @@ -122,7 +136,7 @@ bool PaceHandler::initialize(const QSharedPointer& pEfCardAc } -bool PaceHandler::isSupportedProtocol(const QSharedPointer& pPaceInfo) const +bool PaceHandler::isSupportedProtocol(const QSharedPointer& pPaceInfo) const { if (pPaceInfo->getVersion() != 2) { @@ -138,23 +152,23 @@ bool PaceHandler::isSupportedProtocol(const QSharedPointer& pPac { if (pPaceInfo->isStandardizedDomainParameters()) { - qCDebug(card) << "Use ECDH with standardized domain parameters: " << pPaceInfo->getProtocol(); + qCDebug(card) << "Use ECDH with standardized domain parameters:" << pPaceInfo->getProtocol(); return true; } } - qCWarning(card) << "Unsupported domain parameters: " << pPaceInfo->getProtocol(); + qCWarning(card) << "Unsupported domain parameters:" << pPaceInfo->getProtocol(); return false; } -CardReturnCode PaceHandler::transmitMSESetAT(PACE_PASSWORD_ID pPasswordId) +CardReturnCode PaceHandler::transmitMSESetAT(PacePasswordId pPasswordId) { CardReturnCode cardReturnCode = PersoSimWorkaround::sendingMseSetAt(mCardConnectionWorker); if (cardReturnCode != CardReturnCode::OK) { - qCCritical(card) << "Error on MSE:Set AT"; - return CardReturnCode::COMMAND_FAILED; + qCCritical(card) << "Error on MSE:Set AT |" << cardReturnCode; + return cardReturnCode; } MSEBuilder mseBuilder(MSEBuilder::P1::PERFORM_SECURITY_OPERATION, MSEBuilder::P2::SET_AT); @@ -168,15 +182,13 @@ CardReturnCode PaceHandler::transmitMSESetAT(PACE_PASSWORD_ID pPasswordId) ResponseApdu response; cardReturnCode = mCardConnectionWorker->transmit(mseBuilder.build(), response); - - Q_ASSERT(response.getBuffer().length() == 2); mStatusMseSetAt = response.getBuffer().left(2); const StatusCode responseReturnCode = response.getReturnCode(); if (cardReturnCode != CardReturnCode::OK) { qCCritical(card) << "Error on MSE:Set AT"; - return responseReturnCode == StatusCode::EMPTY ? CardReturnCode::COMMAND_FAILED : CardReturnCode::PROTOCOL_ERROR; + return responseReturnCode == StatusCode::EMPTY ? CardReturnCode::RETRY_ALLOWED : CardReturnCode::PROTOCOL_ERROR; } if (responseReturnCode != StatusCode::SUCCESS && responseReturnCode != StatusCode::PIN_RETRY_COUNT_2 && responseReturnCode != StatusCode::PIN_SUSPENDED) { diff --git a/src/card/base/pace/PaceHandler.h b/src/card/base/pace/PaceHandler.h index 881c8f6..468b2a0 100644 --- a/src/card/base/pace/PaceHandler.h +++ b/src/card/base/pace/PaceHandler.h @@ -6,11 +6,8 @@ #pragma once -#include "asn1/PACEInfo.h" #include "asn1/SecurityInfos.h" #include "CardConnectionWorker.h" -#include "CardOperationResult.h" -#include "EnumHelper.h" #include "pace/KeyAgreement.h" #include @@ -21,14 +18,14 @@ class test_PaceHandler; namespace governikus { -class PaceHandler +class PaceHandler final { friend class ::test_PaceHandler; private: const QSharedPointer mCardConnectionWorker; QSharedPointer mKeyAgreement; - QSharedPointer mPaceInfo; + QSharedPointer mPaceInfo; QByteArray mStatusMseSetAt; QByteArray mIdIcc; QByteArray mEncryptionKey; @@ -39,7 +36,7 @@ class PaceHandler /*! * \brief checks for implementation support */ - bool isSupportedProtocol(const QSharedPointer& pPaceInfo) const; + bool isSupportedProtocol(const QSharedPointer& pPaceInfo) const; /*! * \brief Perform initialization of the handler. During initialization the PACE protocol parameters to be used are determined. @@ -53,7 +50,7 @@ class PaceHandler * \param pPasswordId the PACE password id to use, e.g. PIN, CAN or PUK * \return false on any card errors */ - CardReturnCode transmitMSESetAT(PACE_PASSWORD_ID pPasswordId); + CardReturnCode transmitMSESetAT(PacePasswordId pPasswordId); Q_DISABLE_COPY(PaceHandler) @@ -66,7 +63,7 @@ class PaceHandler * \param pPassword the password value, e.g. "123456" * \return false on any errors during establishment */ - CardReturnCode establishPaceChannel(PACE_PASSWORD_ID pPasswordId, const QString& pPassword); + CardReturnCode establishPaceChannel(PacePasswordId pPasswordId, const QString& pPassword); /*! * \brief The certificate holder authorization template to be supplied to the card. May be empty @@ -116,4 +113,4 @@ class PaceHandler QByteArray getPaceProtocol() const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/pace/SecureMessaging.cpp b/src/card/base/pace/SecureMessaging.cpp index 53000db..96b2b38 100644 --- a/src/card/base/pace/SecureMessaging.cpp +++ b/src/card/base/pace/SecureMessaging.cpp @@ -39,16 +39,11 @@ SecureMessaging::SecureMessaging(const QByteArray& pPaceAlgorithm, const QByteAr , mCipherMac(pPaceAlgorithm, pMacKey) , mSendSequenceCounter(0) { - qCDebug(secure) << "Encryption key: " << pEncKey.toHex(); + qCDebug(secure) << "Encryption key:" << pEncKey.toHex(); qCDebug(secure) << "MAC key:" << pMacKey.toHex(); } -SecureMessaging::~SecureMessaging() -{ -} - - bool SecureMessaging::isInitialized() { return mCipher.isInitialized() && mCipherMac.isInitialized(); @@ -101,7 +96,7 @@ CommandApdu SecureMessaging::encrypt(const CommandApdu& pCommandApdu) ++mSendSequenceCounter; - qCDebug(secure) << "Plain CommandApdu: " << pCommandApdu.getBuffer().toHex(); + qCDebug(secure) << "Plain CommandApdu:" << pCommandApdu.getBuffer().toHex(); QByteArray formattedEncryptedData; if (!pCommandApdu.getData().isEmpty()) @@ -117,7 +112,7 @@ CommandApdu SecureMessaging::encrypt(const CommandApdu& pCommandApdu) QByteArray securedHeader = createSecuredHeader(pCommandApdu); QByteArray securedLe; - if (pCommandApdu.getLe() > Apdu::NO_LE) + if (pCommandApdu.getLe() > CommandApdu::NO_LE) { auto protectedLeObject = newObject(); Asn1OctetStringUtil::setValue(createSecuredLe(pCommandApdu.getLe()), protectedLeObject.data()); @@ -134,7 +129,7 @@ CommandApdu SecureMessaging::encrypt(const CommandApdu& pCommandApdu) QByteArray SecureMessaging::createSecuredHeader(const CommandApdu& pCommandApdu) const { QByteArray securedHeader; - securedHeader += static_cast((pCommandApdu.getCLA() & 0xF0) | Apdu::CLA_SECURE_MESSAGING); + securedHeader += static_cast((pCommandApdu.getCLA() & 0xF0) | CommandApdu::CLA_SECURE_MESSAGING); securedHeader += pCommandApdu.getINS(); securedHeader += pCommandApdu.getP1(); securedHeader += pCommandApdu.getP2(); @@ -145,9 +140,9 @@ QByteArray SecureMessaging::createSecuredHeader(const CommandApdu& pCommandApdu) QByteArray SecureMessaging::createSecuredLe(int pLe) { QByteArray buffer; - if (pLe > Apdu::NO_LE) + if (pLe > CommandApdu::NO_LE) { - if (pLe > Apdu::SHORT_MAX_LE) + if (pLe > CommandApdu::SHORT_MAX_LE) { buffer += static_cast(pLe >> 0x08 & 0xff); } @@ -249,7 +244,7 @@ bool SecureMessaging::decrypt(const ResponseApdu& pEncryptedResponseApdu, Respon pDecryptedResponseApdu.setBuffer(decryptedData + secureResponse.getSecuredStatusCodeBytes()); - qCDebug(secure) << "Plain ResponseApdu: " << pDecryptedResponseApdu.getBuffer().toHex(); + qCDebug(secure) << "Plain ResponseApdu:" << pDecryptedResponseApdu.getBuffer().toHex(); return true; } diff --git a/src/card/base/pace/SecureMessaging.h b/src/card/base/pace/SecureMessaging.h index 6ef1968..1671e64 100644 --- a/src/card/base/pace/SecureMessaging.h +++ b/src/card/base/pace/SecureMessaging.h @@ -7,9 +7,10 @@ #pragma once #include "asn1/ASN1TemplateUtil.h" -#include "Apdu.h" +#include "CommandApdu.h" #include "pace/CipherMac.h" #include "pace/SymmetricCipher.h" +#include "ResponseApdu.h" #include @@ -26,7 +27,7 @@ struct SM_PROTECTED_LE DECLARE_ASN1_OBJECT(SM_PROTECTED_LE) -class SecureMessaging +class SecureMessaging final { private: SymmetricCipher mCipher; @@ -45,7 +46,7 @@ class SecureMessaging public: SecureMessaging(const QByteArray& pPaceAlgorithm, const QByteArray& pEncKey, const QByteArray& pMacKey); - virtual ~SecureMessaging(); + ~SecureMessaging() = default; /*! * Returns true, if initialization succeeded, i.e. the algorithm is known, supported and the keys have correct size. @@ -56,4 +57,4 @@ class SecureMessaging bool decrypt(const ResponseApdu& pEncryptedResponseApdu, ResponseApdu& pDecryptedResponseApdu); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/pace/SymmetricCipher.cpp b/src/card/base/pace/SymmetricCipher.cpp index f6cde06..f13e6a5 100644 --- a/src/card/base/pace/SymmetricCipher.cpp +++ b/src/card/base/pace/SymmetricCipher.cpp @@ -2,13 +2,13 @@ * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany */ -#include "asn1/KnownOIDs.h" #include "pace/SymmetricCipher.h" +#include "asn1/KnownOIDs.h" + #include #include - using namespace governikus; @@ -46,7 +46,7 @@ SymmetricCipher::SymmetricCipher(const QByteArray& pPaceAlgorithm, const QByteAr } else { - qCCritical(card) << "Unknown algorithm: " << pPaceAlgorithm; + qCCritical(card) << "Unknown algorithm:" << pPaceAlgorithm; return; } @@ -59,16 +59,25 @@ SymmetricCipher::SymmetricCipher(const QByteArray& pPaceAlgorithm, const QByteAr } mCtx = EVP_CIPHER_CTX_new(); +#if OPENSSL_VERSION_NUMBER < 0x10100000L EVP_CIPHER_CTX_init(mCtx); +#else + EVP_CIPHER_CTX_reset(mCtx); +#endif } SymmetricCipher::~SymmetricCipher() { // also frees the memory - if (mCtx != nullptr && !EVP_CIPHER_CTX_cleanup(mCtx)) + if (mCtx != nullptr && +#if OPENSSL_VERSION_NUMBER < 0x10100000L + !EVP_CIPHER_CTX_cleanup(mCtx)) +#else + !EVP_CIPHER_CTX_reset(mCtx)) +#endif { - qCCritical(card) << "Error on EVP_CIPHER_CTX_cleanup"; + qCCritical(card) << "Cannot free EVP_CIPHER_CTX"; } } diff --git a/src/card/base/pace/SymmetricCipher.h b/src/card/base/pace/SymmetricCipher.h index 6bd681f..f778c6b 100644 --- a/src/card/base/pace/SymmetricCipher.h +++ b/src/card/base/pace/SymmetricCipher.h @@ -12,7 +12,7 @@ namespace governikus { -class SymmetricCipher +class SymmetricCipher final { private: EVP_CIPHER_CTX* mCtx; @@ -60,4 +60,4 @@ class SymmetricCipher int getBlockSize() const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/pace/ec/EcUtil.h b/src/card/base/pace/ec/EcUtil.h index d09dc11..34deb6e 100644 --- a/src/card/base/pace/ec/EcUtil.h +++ b/src/card/base/pace/ec/EcUtil.h @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -147,4 +148,4 @@ inline QSharedPointer EcUtil::create(ECDSA_SIG* pEcdsaSignature) } -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/pace/ec/EcdhGenericMapping.cpp b/src/card/base/pace/ec/EcdhGenericMapping.cpp index a363d86..75342d1 100644 --- a/src/card/base/pace/ec/EcdhGenericMapping.cpp +++ b/src/card/base/pace/ec/EcdhGenericMapping.cpp @@ -24,11 +24,6 @@ EcdhGenericMapping::EcdhGenericMapping(const QSharedPointer& pCurve) } -EcdhGenericMapping::~EcdhGenericMapping() -{ -} - - QByteArray EcdhGenericMapping::generateTerminalMappingData() { Q_ASSERT(!mCurve.isNull()); diff --git a/src/card/base/pace/ec/EcdhGenericMapping.h b/src/card/base/pace/ec/EcdhGenericMapping.h index 45716b6..fc6fc40 100644 --- a/src/card/base/pace/ec/EcdhGenericMapping.h +++ b/src/card/base/pace/ec/EcdhGenericMapping.h @@ -29,11 +29,11 @@ class EcdhGenericMapping public: EcdhGenericMapping(const QSharedPointer& pCurve); - virtual ~EcdhGenericMapping() override; + virtual ~EcdhGenericMapping() override = default; QByteArray generateTerminalMappingData() override; QSharedPointer generateEphemeralDomainParameters(const QByteArray& pCardMappingData, const QByteArray& pNonce) override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/pace/ec/EcdhKeyAgreement.cpp b/src/card/base/pace/ec/EcdhKeyAgreement.cpp index 0d245a7..9fbd42a 100644 --- a/src/card/base/pace/ec/EcdhKeyAgreement.cpp +++ b/src/card/base/pace/ec/EcdhKeyAgreement.cpp @@ -3,9 +3,8 @@ */ #include "asn1/KnownOIDs.h" -#include "asn1/PACEInfo.h" +#include "asn1/PaceInfo.h" #include "Commands.h" -#include "pace/CipherMac.h" #include "pace/ec/EcdhGenericMapping.h" #include "pace/ec/EcdhKeyAgreement.h" #include "pace/ec/EcUtil.h" @@ -21,7 +20,7 @@ Q_DECLARE_LOGGING_CATEGORY(card) Q_DECLARE_LOGGING_CATEGORY(secure) -QByteArray EcdhKeyAgreement::encodeUncompressedPublicKey(const QSharedPointer& pPaceInfo, const QSharedPointer& pCurve, const QSharedPointer& pPoint) +QByteArray EcdhKeyAgreement::encodeUncompressedPublicKey(const QSharedPointer& pPaceInfo, const QSharedPointer& pCurve, const QSharedPointer& pPoint) { const QByteArray& publicKeyData = Asn1Util::encode(char(0x06), pPaceInfo->getProtocolValueBytes()) + @@ -42,7 +41,7 @@ QByteArray EcdhKeyAgreement::encodeCompressedPublicKey(const QSharedPointer& pPaceInfo, const QSharedPointer& pCardConnectionWorker) +EcdhKeyAgreement::EcdhKeyAgreement(const QSharedPointer& pPaceInfo, const QSharedPointer& pCardConnectionWorker) : KeyAgreement(pPaceInfo, pCardConnectionWorker) , mMapping() , mEphemeralCurve() @@ -52,7 +51,7 @@ EcdhKeyAgreement::EcdhKeyAgreement(const QSharedPointer& pPaceIn } -QSharedPointer EcdhKeyAgreement::create(const QSharedPointer& pPaceInfo, +QSharedPointer EcdhKeyAgreement::create(const QSharedPointer& pPaceInfo, const QSharedPointer& pCardConnectionWorker) { QSharedPointer keyAgreement(new EcdhKeyAgreement(pPaceInfo, pCardConnectionWorker)); @@ -75,11 +74,6 @@ QSharedPointer EcdhKeyAgreement::create(const QSharedPointer EcdhKeyAgreement::determineSharedSecret(const QByteArray& pNonce) { CardOperationResult > ephemeralCurveResult = determineEphemeralDomainParameters(pNonce); @@ -145,7 +139,7 @@ CardOperationResult > EcdhKeyAgreement::performKeyExcha return CardOperationResult >(resultCode, QSharedPointer()); } QByteArray cardEphemeralPublicKeyBytes = result.getPayload(); - qCDebug(secure) << "uncompressedCardEphemeralPublicKey: " << cardEphemeralPublicKeyBytes.toHex(); + qCDebug(secure) << "uncompressedCardEphemeralPublicKey:" << cardEphemeralPublicKeyBytes.toHex(); mCardPublicKey = EcUtil::oct2point(pCurve, cardEphemeralPublicKeyBytes); if (!mCardPublicKey) diff --git a/src/card/base/pace/ec/EcdhKeyAgreement.h b/src/card/base/pace/ec/EcdhKeyAgreement.h index 345a965..683a0d1 100644 --- a/src/card/base/pace/ec/EcdhKeyAgreement.h +++ b/src/card/base/pace/ec/EcdhKeyAgreement.h @@ -31,7 +31,7 @@ class EcdhKeyAgreement CardOperationResult > determineEphemeralDomainParameters(const QByteArray& pNonce); CardOperationResult > performKeyExchange(const QSharedPointer& pCurve); - static QByteArray encodeUncompressedPublicKey(const QSharedPointer& pPaceInfo, const QSharedPointer& pCurve, const QSharedPointer& pPoint); + static QByteArray encodeUncompressedPublicKey(const QSharedPointer& pPaceInfo, const QSharedPointer& pCurve, const QSharedPointer& pPoint); static QByteArray encodeCompressedPublicKey(const QSharedPointer& pCurve, const QSharedPointer& pPoint); CardOperationResult determineSharedSecret(const QByteArray& pNonce) override; @@ -39,13 +39,13 @@ class EcdhKeyAgreement QByteArray getUncompressedCardPublicKey() override; QByteArray getCompressedCardPublicKey() override; - EcdhKeyAgreement(const QSharedPointer& pPaceInfo, const QSharedPointer& pCardConnectionWorker); + EcdhKeyAgreement(const QSharedPointer& pPaceInfo, const QSharedPointer& pCardConnectionWorker); public: - static QSharedPointer create(const QSharedPointer& pPaceInfo, + static QSharedPointer create(const QSharedPointer& pPaceInfo, const QSharedPointer& pCardConnectionWorker); - virtual ~EcdhKeyAgreement() override; + virtual ~EcdhKeyAgreement() override = default; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/pace/ec/EllipticCurveFactory.cpp b/src/card/base/pace/ec/EllipticCurveFactory.cpp index 2daa2dd..e63c1a0 100644 --- a/src/card/base/pace/ec/EllipticCurveFactory.cpp +++ b/src/card/base/pace/ec/EllipticCurveFactory.cpp @@ -5,7 +5,7 @@ #include #include -#include "asn1/PACEInfo.h" +#include "asn1/PaceInfo.h" #include "pace/ec/EcUtil.h" #include "pace/ec/EllipticCurveFactory.h" @@ -17,7 +17,7 @@ using namespace governikus; Q_DECLARE_LOGGING_CATEGORY(card) -QSharedPointer EllipticCurveFactory::create(const QSharedPointer& pPaceInfo) +QSharedPointer EllipticCurveFactory::create(const QSharedPointer& pPaceInfo) { if (pPaceInfo->isStandardizedDomainParameters()) { @@ -38,7 +38,7 @@ QSharedPointer EllipticCurveFactory::create(const QSharedPointer EllipticCurveFactory::createCurve(int pNid) { - qCDebug(card) << "Create elliptic curve " << OBJ_nid2sn(pNid); + qCDebug(card) << "Create elliptic curve:" << OBJ_nid2sn(pNid); EC_GROUP* ecGroup = EC_GROUP_new_by_curve_name(pNid); if (ecGroup == nullptr) { @@ -89,7 +89,7 @@ QSharedPointer EllipticCurveFactory::create(int pCurveIndex) return createCurve(NID_secp521r1); default: - qCWarning(card) << "Creation of standardized elliptic curve " << pCurveIndex << " not supported"; + qCWarning(card) << "Creation of standardized elliptic curve" << pCurveIndex << "not supported"; return EcUtil::create(static_cast(nullptr)); } } diff --git a/src/card/base/pace/ec/EllipticCurveFactory.h b/src/card/base/pace/ec/EllipticCurveFactory.h index 6a67e5e..ded5c98 100644 --- a/src/card/base/pace/ec/EllipticCurveFactory.h +++ b/src/card/base/pace/ec/EllipticCurveFactory.h @@ -13,8 +13,7 @@ namespace governikus { - -class PACEInfo; +class PaceInfo; class EllipticCurveFactory @@ -29,7 +28,7 @@ class EllipticCurveFactory * \param pPaceInfo PACEInfo element containing domain parameter * \return elliptic curve object */ - static QSharedPointer create(const QSharedPointer& pPaceInfo); + static QSharedPointer create(const QSharedPointer& pPaceInfo); /*! * \brief Creates a standardized elliptic curve with specified curve index.. @@ -39,4 +38,4 @@ class EllipticCurveFactory static QSharedPointer create(int pCurveIndex); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/bluetooth/AndroidBluetoothAdapter.h b/src/card/bluetooth/AndroidBluetoothAdapter.h index a36fef2..9b2d9b4 100644 --- a/src/card/bluetooth/AndroidBluetoothAdapter.h +++ b/src/card/bluetooth/AndroidBluetoothAdapter.h @@ -46,4 +46,4 @@ class AndroidBluetoothAdapter bool isAvailable() const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/bluetooth/BluetoothCard.cpp b/src/card/bluetooth/BluetoothCard.cpp index baa5b99..ec04598 100644 --- a/src/card/bluetooth/BluetoothCard.cpp +++ b/src/card/bluetooth/BluetoothCard.cpp @@ -4,9 +4,9 @@ #include "BluetoothCard.h" -#include "DestroyPACEChannel.h" +#include "DestroyPaceChannel.h" +#include "EstablishPaceChannel.h" #include "messages/BluetoothMessageCreator.h" -#include "messages/BluetoothMessageDisconnectResponse.h" #include "messages/BluetoothMessageSetTransportProtocolResponse.h" #include "messages/BluetoothMessageTransferApduResponse.h" #include "PinModify.h" @@ -100,7 +100,7 @@ CardReturnCode BluetoothCard::transmit(const CommandApdu& pCmd, ResponseApdu& pR return CardReturnCode::COMMAND_FAILED; } - qCDebug(bluetooth) << "Transmit command APDU: " << pCmd.getBuffer().toHex(); + qCDebug(bluetooth) << "Transmit command APDU:" << pCmd.getBuffer().toHex(); auto request = BluetoothMessageCreator::createTransferApduRequest(pCmd.getBuffer()); auto response = SynchronousBtCall(mDevice).send(request, BluetoothMsgId::TransferApduResponse, pTimeoutSeconds); if (response.isNull()) @@ -112,22 +112,22 @@ CardReturnCode BluetoothCard::transmit(const CommandApdu& pCmd, ResponseApdu& pR auto apduResponse = response.staticCast(); if (apduResponse->getResultCode() != BluetoothResultCode::Ok) { - qCCritical(bluetooth) << "TransferApduResponse failed : " << apduResponse->getResultCode(); + qCCritical(bluetooth) << "TransferApduResponse failed:" << apduResponse->getResultCode(); return CardReturnCode::COMMAND_FAILED; } pRes.setBuffer(apduResponse->getResponseAPDU()); - qCDebug(bluetooth) << "Transmit response APDU: " << pRes.getBuffer().toHex(); + qCDebug(bluetooth) << "Transmit response APDU:" << pRes.getBuffer().toHex(); return CardReturnCode::OK; } -CardReturnCode BluetoothCard::establishPaceChannel(PACE_PASSWORD_ID pPasswordId, +CardReturnCode BluetoothCard::establishPaceChannel(PacePasswordId pPasswordId, const QByteArray& pChat, const QByteArray& pCertificateDescription, - EstablishPACEChannelOutput& pChannelOutput, + EstablishPaceChannelOutput& pChannelOutput, quint8 pTimeoutSeconds) { - EstablishPACEChannelBuilder builder; + EstablishPaceChannel builder; builder.setPasswordId(pPasswordId); builder.setChat(pChat); builder.setCertificateDescription(pCertificateDescription); @@ -147,7 +147,7 @@ CardReturnCode BluetoothCard::establishPaceChannel(PACE_PASSWORD_ID pPasswordId, CardReturnCode BluetoothCard::destroyPaceChannel() { - DestroyPACEChannelBuilder builder; + DestroyPaceChannelBuilder builder; ResponseApdu response; return transmit(builder.createCommandDataCcid(), response); } diff --git a/src/card/bluetooth/BluetoothCard.h b/src/card/bluetooth/BluetoothCard.h index 64fd8f5..3796a3f 100644 --- a/src/card/bluetooth/BluetoothCard.h +++ b/src/card/bluetooth/BluetoothCard.h @@ -34,11 +34,11 @@ class BluetoothCard CardReturnCode transmit(const CommandApdu& pCmd, ResponseApdu& pRes) override; - CardReturnCode establishPaceChannel(PACE_PASSWORD_ID pPasswordId, const QByteArray& pChat, const QByteArray& pCertificateDescription, EstablishPACEChannelOutput& pChannelOutput, quint8 pTimeoutSeconds) override; + CardReturnCode establishPaceChannel(PacePasswordId pPasswordId, const QByteArray& pChat, const QByteArray& pCertificateDescription, EstablishPaceChannelOutput& pChannelOutput, quint8 pTimeoutSeconds) override; CardReturnCode destroyPaceChannel() override; CardReturnCode setEidPin(quint8 pTimeoutSeconds, ResponseApdu& pResponseApdu) override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/bluetooth/BluetoothDebug.cpp b/src/card/bluetooth/BluetoothDebug.cpp index cca29c7..8b1b016 100644 --- a/src/card/bluetooth/BluetoothDebug.cpp +++ b/src/card/bluetooth/BluetoothDebug.cpp @@ -33,7 +33,7 @@ QString toString(QBluetoothDeviceInfo::CoreConfigurations pConfig) } -} +} // namespace QDebug operator <<(QDebug pDbg, const QBluetoothDeviceInfo::CoreConfigurations& pConfig) diff --git a/src/card/bluetooth/BluetoothReader.cpp b/src/card/bluetooth/BluetoothReader.cpp index 706820c..e6b17cb 100644 --- a/src/card/bluetooth/BluetoothReader.cpp +++ b/src/card/bluetooth/BluetoothReader.cpp @@ -5,7 +5,6 @@ #include "BluetoothCard.h" #include "BluetoothDebug.h" #include "BluetoothReader.h" -#include "DeviceError.h" #include "messages/BluetoothMessageCreator.h" #include "messages/BluetoothMessageParser.h" #include "messages/BluetoothMessageSetTransportProtocolResponse.h" @@ -52,7 +51,8 @@ void BluetoothReader::connectReader() void BluetoothReader::onInitialized(const QBluetoothDeviceInfo&) { - qCDebug(bluetooth) << "Connected reader" << getName() << "is valid:" << mDevice->isValid(); + const QString& name = mReaderInfo.getName(); + qCDebug(bluetooth) << "Connected reader" << name << "is valid:" << mDevice->isValid(); /* * Attention: This also triggers the pairing! @@ -63,6 +63,7 @@ void BluetoothReader::onInitialized(const QBluetoothDeviceInfo&) if (response.isNull()) { qCCritical(bluetooth) << "Response is empty"; + Q_EMIT fireReaderConnectionFailed(name); return; } @@ -70,10 +71,11 @@ void BluetoothReader::onInitialized(const QBluetoothDeviceInfo&) if (protocolResponse->getResultCode() != BluetoothResultCode::Ok) { qCCritical(bluetooth) << "Error setting transport protocol"; + Q_EMIT fireReaderConnectionFailed(name); return; } - Q_EMIT fireReaderConnected(mReaderInfo.getName()); + Q_EMIT fireReaderConnected(name); mReaderInfo.setConnected(mDevice->isValid()); mLastCardEvent = CardEvent::CARD_REMOVED; @@ -108,7 +110,7 @@ void BluetoothReader::onError(QLowEnergyController::Error pError) { if (pError == QLowEnergyController::ConnectionError) { - Q_EMIT fireReaderDeviceError(DeviceError::DEVICE_CONNECTION_ERROR); + Q_EMIT fireReaderDeviceError(GlobalStatus::Code::Workflow_Reader_Device_Connection_Error); } } diff --git a/src/card/bluetooth/BluetoothReader.h b/src/card/bluetooth/BluetoothReader.h index d3fb46e..4a6b051 100644 --- a/src/card/bluetooth/BluetoothReader.h +++ b/src/card/bluetooth/BluetoothReader.h @@ -17,7 +17,6 @@ namespace governikus { - class BluetoothReader : public ConnectableReader { @@ -39,6 +38,7 @@ class BluetoothReader Q_SIGNALS: void fireReaderConnected(const QString& pReaderName); + void fireReaderConnectionFailed(const QString& pReaderName); public: BluetoothReader(const QSharedPointer& pDevice); @@ -49,4 +49,4 @@ class BluetoothReader void disconnectReader() override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/bluetooth/BluetoothReaderManagerPlugIn.cpp b/src/card/bluetooth/BluetoothReaderManagerPlugIn.cpp index 437c099..20ae629 100644 --- a/src/card/bluetooth/BluetoothReaderManagerPlugIn.cpp +++ b/src/card/bluetooth/BluetoothReaderManagerPlugIn.cpp @@ -9,8 +9,7 @@ #include "BluetoothDeviceUtil.h" #include "BluetoothReader.h" #include "BluetoothReaderManagerPlugIn_p.h" -#include "DeviceError.h" -#include "Initializer.h" +#include "GlobalStatus.h" #include "ScopeGuard.h" #include @@ -29,6 +28,7 @@ BluetoothReaderManagerPlugIn::BluetoothReaderManagerPlugIn() , mReaders() , mReadersDiscoveredInCurrentScan() , mTimerIdDiscoverPairedDevices(0) + , mScanInProgress(false) { connect(&mDeviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, this, &BluetoothReaderManagerPlugIn::onDeviceDiscovered); connect(&mDeviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::finished, this, &BluetoothReaderManagerPlugIn::onDeviceDiscoveryFinished); @@ -61,7 +61,7 @@ QList BluetoothReaderManagerPlugIn::getReaders() const } -void BluetoothReaderManagerPlugIn::startScan() +void BluetoothReaderManagerPlugIn::startScan(bool /*pAutoConnect*/) { if (mDeviceDiscoveryAgent.isActive()) { @@ -78,9 +78,10 @@ void BluetoothReaderManagerPlugIn::startScan() if (mReaders.size() < 1) { qCDebug(bluetooth) << "Starting Bluetooth device discovery"; - mScanInProgress = true; mDeviceDiscoveryAgent.start(); } + + setScanInProgress(true); } @@ -109,17 +110,27 @@ void BluetoothReaderManagerPlugIn::stopScan() qCWarning(bluetooth) << "Bluetooth device discovery not running"; } - mScanInProgress = false; + setScanInProgress(false); } -void BluetoothReaderManagerPlugIn::onConnectToKnownReadersChanged() +void BluetoothReaderManagerPlugIn::setScanInProgress(bool pScanInProgress) +{ + if (mScanInProgress != pScanInProgress) + { + mScanInProgress = pScanInProgress; + onScanInProgressChanged(); + } +} + + +void BluetoothReaderManagerPlugIn::onScanInProgressChanged() { const auto& values = mReaders.values(); for (BluetoothReader* const reader : values) { const bool connected = reader->getReaderInfo().isConnected(); - if (mConnectToKnownReaders && !connected) + if (mScanInProgress && !connected) { /* * Workaround for pairing problem on Android: @@ -131,11 +142,11 @@ void BluetoothReaderManagerPlugIn::onConnectToKnownReadersChanged() * * This led to setting a delay of 1000 msecs. */ - QTimer::singleShot(1000, [ = ] { + QTimer::singleShot(1000, reader, [ = ] { reader->connectReader(); }); } - else if (connected && !mConnectToKnownReaders) + else if (connected && !mScanInProgress) { reader->disconnectReader(); } @@ -143,6 +154,24 @@ void BluetoothReaderManagerPlugIn::onConnectToKnownReadersChanged() } +QVector BluetoothReaderManagerPlugIn::deviceIdsForReaderName(const QString& pReaderName) +{ + QVector result; + + for (QMap::const_iterator it = mReaders.constBegin(); it != mReaders.constEnd(); ++it) + { + const QString& deviceId = it.key(); + const Reader* reader = it.value(); + if (reader->getName() == pReaderName) + { + result += deviceId; + } + } + + return result; +} + + void BluetoothReaderManagerPlugIn::onDeviceDiscovered(const QBluetoothDeviceInfo& pInfo) { setReaderInfoResponding(true); @@ -166,7 +195,7 @@ void BluetoothReaderManagerPlugIn::onDeviceDiscovered(const QBluetoothDeviceInfo qCDebug(bluetooth) << "Bluetooth device discovered" << pInfo; - QSharedPointer newDevice(new CyberJackWaveDevice(pInfo)); + const auto& newDevice = QSharedPointer::create(pInfo); mInitializingDevices.insert(deviceId, newDevice); connect(newDevice.data(), &CyberJackWaveDevice::fireInitialized, this, &BluetoothReaderManagerPlugIn::onDeviceInitialized); newDevice->initialize(); @@ -197,9 +226,10 @@ void BluetoothReaderManagerPlugIn::onDeviceInitialized(const QBluetoothDeviceInf BluetoothReader* reader = new BluetoothReader(device); qCDebug(bluetooth) << "Device is successfully initialized, create reader" << reader->getName(); - connect(reader, &BluetoothReader::fireReaderConnected, this, &ReaderManagerPlugIn::fireReaderAdded); + connect(reader, &BluetoothReader::fireReaderConnected, this, &BluetoothReaderManagerPlugIn::onReaderConnected); + connect(reader, &BluetoothReader::fireReaderConnectionFailed, this, &BluetoothReaderManagerPlugIn::onReaderConnectionFailed); connect(reader, &Reader::fireCardInserted, this, &ReaderManagerPlugIn::fireCardInserted); - connect(reader, &Reader::fireCardRemoved, this, &ReaderManagerPlugIn::fireCardRemoved); + connect(reader, &Reader::fireCardRemoved, this, &BluetoothReaderManagerPlugIn::onCardRemoved); connect(reader, &Reader::fireCardRetryCounterChanged, this, &ReaderManagerPlugIn::fireCardRetryCounterChanged); connect(reader, &Reader::fireReaderDeviceError, this, &ReaderManagerPlugIn::fireReaderDeviceError); @@ -207,8 +237,10 @@ void BluetoothReaderManagerPlugIn::onDeviceInitialized(const QBluetoothDeviceInf mReaders.insert(deviceId, reader); connect(device.data(), &CyberJackWaveDevice::fireDisconnected, this, &BluetoothReaderManagerPlugIn::onDeviceDisconnected); - if (mConnectToKnownReaders) + if (mScanInProgress) { + mPendingConnections.insert(reader->getName(), 1); + /* * Workaround for pairing problem on Android: * @@ -219,7 +251,7 @@ void BluetoothReaderManagerPlugIn::onDeviceInitialized(const QBluetoothDeviceInf * * This led to setting a delay of 1000 msecs. */ - QTimer::singleShot(1000, [ = ] { + QTimer::singleShot(1000, reader, [ = ] { reader->connectReader(); }); } @@ -264,7 +296,7 @@ void BluetoothReaderManagerPlugIn::onDeviceDiscoveryError(QBluetoothDeviceDiscov setReaderInfoResponding(false); } - Q_EMIT fireReaderDeviceError(pError == QBluetoothDeviceDiscoveryAgent::PoweredOffError ? DeviceError::DEVICE_POWERED_OFF : DeviceError::DEVICE_SCAN_ERROR); + Q_EMIT fireReaderDeviceError(pError == QBluetoothDeviceDiscoveryAgent::PoweredOffError ? GlobalStatus::Code::No_Error : GlobalStatus::Code::Workflow_Reader_Device_Scan_Error); } @@ -301,3 +333,85 @@ void BluetoothReaderManagerPlugIn::timerEvent(QTimerEvent* pEvent) ReaderManagerPlugIn::timerEvent(pEvent); } + + +void BluetoothReaderManagerPlugIn::onReaderConnected(const QString& pReaderName) +{ + Q_EMIT fireReaderAdded(pReaderName); +} + + +void BluetoothReaderManagerPlugIn::onCardRemoved(const QString& pReaderName) +{ + const QVector ids = deviceIdsForReaderName(pReaderName); + if (ids.isEmpty()) + { + qCWarning(bluetooth) << "Card removed for unknown reader:" << pReaderName; + return; + } + + for (const QString& id : ids) + { + const BluetoothReader* const reader = mReaders.value(id); + const bool connected = reader->getReaderInfo().isConnected(); + + Q_EMIT fireCardRemoved(pReaderName); + if (!connected) + { + qCDebug(bluetooth) << "Device is disconnected:" << pReaderName; + Q_EMIT fireReaderRemoved(pReaderName); + } + } +} + + +void BluetoothReaderManagerPlugIn::onReaderConnectionFailed(const QString& pReaderName) +{ + if (!mPendingConnections.contains(pReaderName)) + { + qCWarning(bluetooth) << "Connection failed for a reader with no pending connection:" << pReaderName; + + return; + } + + const int attemptCount = mPendingConnections.value(pReaderName); + if (attemptCount >= 2) + { + qCWarning(bluetooth) << "Maximum connection attempt count reached for reader:" << pReaderName; + + return; + } + + BluetoothReader* const reader = mReaders.value(pReaderName, nullptr); + if (reader) + { + mPendingConnections.insert(pReaderName, attemptCount + 1); + + /* + * Sometimes a connection failure occurs after a successful + * pairing. In this case, the bluetooth device is connected but + * does not report a successful connection. + * Before we try to reconnect to the device, it is necessary + * to disconnect from it. + */ + reader->disconnectReader(); + + /* + * Workaround for pairing problem on Android: + * + * The reader detection process performs a BT connect, determines the services and characteristics + * and disconnects afterwards. When selecting a reader we connect again. + * This causes some timing issue. Other developers have similar problems, e.g. see + * https://github.com/NordicSemiconductor/Android-DFU-Library/issues/1#issuecomment-156790789 + * + * This led to setting a delay of 1000 msecs. + */ + QTimer::singleShot(1000, reader, [ = ] { + reader->connectReader(); + }); + } + else + { + qCWarning(bluetooth) << "Cannot reconnect to reader, reader not found:" << pReaderName; + } +} diff --git a/src/card/bluetooth/BluetoothReaderManagerPlugIn.h b/src/card/bluetooth/BluetoothReaderManagerPlugIn.h index c2be533..11ecd72 100644 --- a/src/card/bluetooth/BluetoothReaderManagerPlugIn.h +++ b/src/card/bluetooth/BluetoothReaderManagerPlugIn.h @@ -16,7 +16,6 @@ namespace governikus { - class BluetoothReader; class BluetoothReaderManagerPlugInPrivate; @@ -35,11 +34,17 @@ class BluetoothReaderManagerPlugIn QBluetoothDeviceDiscoveryAgent mDeviceDiscoveryAgent; QMap > mInitializingDevices; QMap mReaders; + QMap mPendingConnections; QStringList mReadersDiscoveredInCurrentScan; int mTimerIdDiscoverPairedDevices; + bool mScanInProgress; void onRemoveReader(const QString& pDeviceId); void timerEvent(QTimerEvent* event) override; + void setScanInProgress(bool pScanInProgress); + void onScanInProgressChanged(); + + QVector deviceIdsForReaderName(const QString& pReaderName); private Q_SLOTS: void onDeviceInitialized(const QBluetoothDeviceInfo& pInfo); @@ -49,17 +54,17 @@ class BluetoothReaderManagerPlugIn void onDeviceDiscoveryError(QBluetoothDeviceDiscoveryAgent::Error pError); void onDeviceDiscoveryCanceled(); void setBluetoothStatus(bool pEnabled); - - protected: - virtual void onConnectToKnownReadersChanged() override; + void onCardRemoved(const QString& pReaderName); + void onReaderConnected(const QString& pReaderName); + void onReaderConnectionFailed(const QString& pReaderName); public: BluetoothReaderManagerPlugIn(); void init() override; QList getReaders() const override; - virtual void startScan() override; + virtual void startScan(bool pAutoConnect) override; virtual void stopScan() override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/bluetooth/BluetoothReaderManagerPlugIn_p.h b/src/card/bluetooth/BluetoothReaderManagerPlugIn_p.h index 32995da..183281b 100644 --- a/src/card/bluetooth/BluetoothReaderManagerPlugIn_p.h +++ b/src/card/bluetooth/BluetoothReaderManagerPlugIn_p.h @@ -20,13 +20,15 @@ class BluetoothReaderManagerPlugInPrivate BluetoothReaderManagerPlugIn* const q_ptr; BluetoothReaderManagerPlugInPrivate(BluetoothReaderManagerPlugIn* pPublic); + public Q_SLOTS: + void onBluetoothStatusChanged(bool pEnabled); + private Q_SLOTS: void init(); - void onBluetoothStatusChanged(bool pEnabled); void onScanStart(); void handlePairedDevices(); void onDeviceDiscoveryCanceled(); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/bluetooth/BluetoothReaderManagerPlugIn_p_android.cpp b/src/card/bluetooth/BluetoothReaderManagerPlugIn_p_android.cpp index 57a7ab1..e0fdc66 100644 --- a/src/card/bluetooth/BluetoothReaderManagerPlugIn_p_android.cpp +++ b/src/card/bluetooth/BluetoothReaderManagerPlugIn_p_android.cpp @@ -36,13 +36,17 @@ JNIEXPORT void JNICALL Java_com_governikus_ausweisapp2_AndroidBluetoothReceiver_ { qCDebug(bluetooth) << "Bluetooth was powered off"; // jump from Android thread into Qt thread by invoking per BlockingQueuedConnection - QMetaObject::invokeMethod(instance, "onBluetoothStatusChanged", Qt::BlockingQueuedConnection, Q_ARG(bool, false)); + QMetaObject::invokeMethod(instance, [] { + instance->onBluetoothStatusChanged(false); + }, Qt::BlockingQueuedConnection); } else if (currentState == 12) // STATE_ON (class android.bluetooth.BluetoothAdapter) { qCDebug(bluetooth) << "Bluetooth was powered on"; // jump from Android thread into Qt thread by invoking per BlockingQueuedConnection - QMetaObject::invokeMethod(instance, "onBluetoothStatusChanged", Qt::BlockingQueuedConnection, Q_ARG(bool, true)); + QMetaObject::invokeMethod(instance, [] { + instance->onBluetoothStatusChanged(true); + }, Qt::BlockingQueuedConnection); } } @@ -81,7 +85,6 @@ void BluetoothReaderManagerPlugInPrivate::onBluetoothStatusChanged(bool pEnabled void BluetoothReaderManagerPlugInPrivate::onScanStart() { - } diff --git a/src/card/bluetooth/BluetoothReaderManagerPlugIn_p_ios.mm b/src/card/bluetooth/BluetoothReaderManagerPlugIn_p_ios.mm index e99c63b..11fc430 100644 --- a/src/card/bluetooth/BluetoothReaderManagerPlugIn_p_ios.mm +++ b/src/card/bluetooth/BluetoothReaderManagerPlugIn_p_ios.mm @@ -1,6 +1,4 @@ /*! - * BluetoothReaderManagerPlugInPrivate_p_ios.mm - * * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany */ diff --git a/src/card/bluetooth/CMakeLists.txt b/src/card/bluetooth/CMakeLists.txt index 5badf80..c817e0b 100644 --- a/src/card/bluetooth/CMakeLists.txt +++ b/src/card/bluetooth/CMakeLists.txt @@ -1,3 +1,7 @@ +##################################################################### +# The ReaderManagerPlugin for bluetooth devices. +##################################################################### + ADD_PLATFORM_LIBRARY(AusweisAppCardBluetooth) TARGET_LINK_LIBRARIES(AusweisAppCardBluetooth Qt5::Core Qt5::Bluetooth AusweisAppGlobal AusweisAppCard) diff --git a/src/card/bluetooth/CyberJackWaveDevice.cpp b/src/card/bluetooth/CyberJackWaveDevice.cpp index e83208b..002a7fe 100644 --- a/src/card/bluetooth/CyberJackWaveDevice.cpp +++ b/src/card/bluetooth/CyberJackWaveDevice.cpp @@ -3,7 +3,6 @@ */ #include "BluetoothDebug.h" -#include "BluetoothReader.h" #include "CyberJackWaveDevice.h" #include "NotificationEnabler.h" @@ -84,7 +83,6 @@ void CyberJackWaveDevice::initialize() return; } - qCDebug(bluetooth) << "Connecting device" << mDeviceInfo; mLeController.connectToDevice(); } diff --git a/src/card/bluetooth/CyberJackWaveDevice.h b/src/card/bluetooth/CyberJackWaveDevice.h index 9cca124..c42825f 100644 --- a/src/card/bluetooth/CyberJackWaveDevice.h +++ b/src/card/bluetooth/CyberJackWaveDevice.h @@ -99,4 +99,4 @@ class CyberJackWaveDevice void fireStatusCharacteristicChanged(const QByteArray& pValue); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/bluetooth/NotificationEnabler.cpp b/src/card/bluetooth/NotificationEnabler.cpp index cbfa221..86254f7 100644 --- a/src/card/bluetooth/NotificationEnabler.cpp +++ b/src/card/bluetooth/NotificationEnabler.cpp @@ -28,11 +28,6 @@ NotificationEnabler::NotificationEnabler(QLowEnergyService* pService, int pTimeo } -NotificationEnabler::~NotificationEnabler() -{ -} - - bool NotificationEnabler::enable(const QLowEnergyCharacteristic& pCharacteristic) { // see Qt documentation on QLowEnergyService and topic "Service Interaction" diff --git a/src/card/bluetooth/NotificationEnabler.h b/src/card/bluetooth/NotificationEnabler.h index 3f5897b..cfa111b 100644 --- a/src/card/bluetooth/NotificationEnabler.h +++ b/src/card/bluetooth/NotificationEnabler.h @@ -47,10 +47,10 @@ class NotificationEnabler * \param pTimeoutSeconds time out in seconds. */ NotificationEnabler(QLowEnergyService* pService, int pTimeoutSeconds = 5); - virtual ~NotificationEnabler(); + virtual ~NotificationEnabler() = default; bool enable(const QLowEnergyCharacteristic& pCharacteristic); bool disable(const QLowEnergyCharacteristic& pCharacteristic); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/bluetooth/SynchronousBtCall.cpp b/src/card/bluetooth/SynchronousBtCall.cpp index deb76c0..c497cbd 100644 --- a/src/card/bluetooth/SynchronousBtCall.cpp +++ b/src/card/bluetooth/SynchronousBtCall.cpp @@ -31,12 +31,12 @@ QSharedPointer SynchronousBtCall::send(const BluetoothMe if (mMessage.isNull()) { - qCCritical(bluetooth) << "No response received for " << pRequest.getBluetoothMsgId(); + qCCritical(bluetooth) << "No response received for" << pRequest.getBluetoothMsgId(); return QSharedPointer(); } else if (mMessage->getBluetoothMsgId() != pResponseType) { - qCCritical(bluetooth) << "Got unexpected response type " << mMessage->getBluetoothMsgId(); + qCCritical(bluetooth) << "Got unexpected response type" << mMessage->getBluetoothMsgId(); return QSharedPointer(); } diff --git a/src/card/bluetooth/SynchronousBtCall.h b/src/card/bluetooth/SynchronousBtCall.h index 51faf16..720e61a 100644 --- a/src/card/bluetooth/SynchronousBtCall.h +++ b/src/card/bluetooth/SynchronousBtCall.h @@ -36,4 +36,4 @@ class SynchronousBtCall void onCharacteristicChanged(const QByteArray& pNewValue); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/bluetooth/messages/BluetoothIDs.h b/src/card/bluetooth/messages/BluetoothIDs.h index e18a5a1..b249d40 100644 --- a/src/card/bluetooth/messages/BluetoothIDs.h +++ b/src/card/bluetooth/messages/BluetoothIDs.h @@ -78,4 +78,4 @@ defineTypedEnumType(BluetoothTransportProtocol, char, T0 = 0x00, T1 = 0x01) defineEnumType(BluetoothCardReaderStatus, Unknown = 0x00, CardInserted = 0x78, CardRemoved = 0x38) -} +} // namespace governikus diff --git a/src/card/bluetooth/messages/BluetoothMessage.cpp b/src/card/bluetooth/messages/BluetoothMessage.cpp index 3d63a09..6788a3e 100644 --- a/src/card/bluetooth/messages/BluetoothMessage.cpp +++ b/src/card/bluetooth/messages/BluetoothMessage.cpp @@ -13,7 +13,7 @@ Q_DECLARE_LOGGING_CATEGORY(bluetooth) using namespace governikus; static Initializer::Entry X([] { - qRegisterMetaType("BluetoothMessage::Ptr"); + qRegisterMetaType >("QSharedPointer"); }); @@ -36,13 +36,13 @@ BluetoothMessage::~BluetoothMessage() } -BluetoothMessageParameter::Ptr BluetoothMessage::getParameter(BluetoothParamId pId) const +QSharedPointer BluetoothMessage::getParameter(BluetoothParamId pId) const { return mMessageParameter.value(pId); } -void BluetoothMessage::addParameter(BluetoothMessageParameter::Ptr pMessageParameter) +void BluetoothMessage::addParameter(const QSharedPointer& pMessageParameter) { Q_ASSERT(!pMessageParameter.isNull()); diff --git a/src/card/bluetooth/messages/BluetoothMessage.h b/src/card/bluetooth/messages/BluetoothMessage.h index bafd8ee..09cb95b 100644 --- a/src/card/bluetooth/messages/BluetoothMessage.h +++ b/src/card/bluetooth/messages/BluetoothMessage.h @@ -18,25 +18,22 @@ namespace governikus class BluetoothMessage { - public: - using Ptr = QSharedPointer; - private: friend class ::test_BluetoothMessageParser; BluetoothMsgId mMsgId; - QMap mMessageParameter; + QMap > mMessageParameter; protected: - BluetoothMessageParameter::Ptr getParameter(BluetoothParamId pId) const; + QSharedPointer getParameter(BluetoothParamId pId) const; public: BluetoothMessage(BluetoothMsgId pMsgId); virtual ~BluetoothMessage(); - void addParameter(BluetoothMessageParameter::Ptr pMessageParameter); + void addParameter(const QSharedPointer& pMessageParameter); template void copyParameter(const T& pMessageParameter) { - addParameter(BluetoothMessageParameter::Ptr(new T(pMessageParameter))); + addParameter(QSharedPointer::create(pMessageParameter)); } @@ -45,7 +42,7 @@ class BluetoothMessage QString toString() const; }; -} /* namespace governikus */ +} // namespace governikus QDebug operator<<(QDebug pDbg, const governikus::BluetoothMessage& pMsg); diff --git a/src/card/bluetooth/messages/BluetoothMessageConnectResponse.h b/src/card/bluetooth/messages/BluetoothMessageConnectResponse.h index ae4b2e5..20dfa71 100644 --- a/src/card/bluetooth/messages/BluetoothMessageConnectResponse.h +++ b/src/card/bluetooth/messages/BluetoothMessageConnectResponse.h @@ -26,4 +26,4 @@ class BluetoothMessageConnectResponse unsigned int getMaxMsgSize() const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/bluetooth/messages/BluetoothMessageCreator.h b/src/card/bluetooth/messages/BluetoothMessageCreator.h index 3c072f1..69a989e 100644 --- a/src/card/bluetooth/messages/BluetoothMessageCreator.h +++ b/src/card/bluetooth/messages/BluetoothMessageCreator.h @@ -120,4 +120,4 @@ class BluetoothMessageCreator }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/bluetooth/messages/BluetoothMessageDisconnectResponse.h b/src/card/bluetooth/messages/BluetoothMessageDisconnectResponse.h index 831939d..5ea1d5c 100644 --- a/src/card/bluetooth/messages/BluetoothMessageDisconnectResponse.h +++ b/src/card/bluetooth/messages/BluetoothMessageDisconnectResponse.h @@ -18,4 +18,4 @@ class BluetoothMessageDisconnectResponse virtual ~BluetoothMessageDisconnectResponse(); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/bluetooth/messages/BluetoothMessageParser.cpp b/src/card/bluetooth/messages/BluetoothMessageParser.cpp index 06e1175..9fedd53 100644 --- a/src/card/bluetooth/messages/BluetoothMessageParser.cpp +++ b/src/card/bluetooth/messages/BluetoothMessageParser.cpp @@ -39,7 +39,6 @@ BluetoothMessageParser::BluetoothMessageParser(const QByteArray& pData) BluetoothMessageParser::~BluetoothMessageParser() { - } @@ -109,7 +108,7 @@ ushort BluetoothMessageParser::getParamLength(uchar pHigh, uchar pLow) const } -const QVector& BluetoothMessageParser::getMessages() const +const QVector >& BluetoothMessageParser::getMessages() const { return mMessages; } diff --git a/src/card/bluetooth/messages/BluetoothMessageParser.h b/src/card/bluetooth/messages/BluetoothMessageParser.h index c5765e7..1070721 100644 --- a/src/card/bluetooth/messages/BluetoothMessageParser.h +++ b/src/card/bluetooth/messages/BluetoothMessageParser.h @@ -14,7 +14,7 @@ namespace governikus class BluetoothMessageParser { private: - QVector mMessages; + QVector > mMessages; QByteArray mRemainingBytes; inline ushort getParamLength(uchar pHigh, uchar pLow) const; @@ -28,8 +28,8 @@ class BluetoothMessageParser BluetoothMessageParser(const QByteArray& pData); virtual ~BluetoothMessageParser(); - const QVector& getMessages() const; + const QVector >& getMessages() const; const QByteArray& getRemainingBytes() const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/bluetooth/messages/BluetoothMessagePowerSimOffResponse.h b/src/card/bluetooth/messages/BluetoothMessagePowerSimOffResponse.h index f08fa16..ce89e59 100644 --- a/src/card/bluetooth/messages/BluetoothMessagePowerSimOffResponse.h +++ b/src/card/bluetooth/messages/BluetoothMessagePowerSimOffResponse.h @@ -22,4 +22,4 @@ class BluetoothMessagePowerSimOffResponse BluetoothResultCode getResultCode() const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/bluetooth/messages/BluetoothMessagePowerSimOnResponse.h b/src/card/bluetooth/messages/BluetoothMessagePowerSimOnResponse.h index 276c4b8..41debe5 100644 --- a/src/card/bluetooth/messages/BluetoothMessagePowerSimOnResponse.h +++ b/src/card/bluetooth/messages/BluetoothMessagePowerSimOnResponse.h @@ -23,4 +23,4 @@ class BluetoothMessagePowerSimOnResponse BluetoothResultCode getResultCode() const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/bluetooth/messages/BluetoothMessageResetSimResponse.h b/src/card/bluetooth/messages/BluetoothMessageResetSimResponse.h index b4fd33f..44f55a9 100644 --- a/src/card/bluetooth/messages/BluetoothMessageResetSimResponse.h +++ b/src/card/bluetooth/messages/BluetoothMessageResetSimResponse.h @@ -23,4 +23,4 @@ class BluetoothMessageResetSimResponse BluetoothResultCode getResultCode() const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/bluetooth/messages/BluetoothMessageSetTransportProtocolResponse.h b/src/card/bluetooth/messages/BluetoothMessageSetTransportProtocolResponse.h index c45ec59..6fbb250 100644 --- a/src/card/bluetooth/messages/BluetoothMessageSetTransportProtocolResponse.h +++ b/src/card/bluetooth/messages/BluetoothMessageSetTransportProtocolResponse.h @@ -24,4 +24,4 @@ class BluetoothMessageSetTransportProtocolResponse BluetoothResultCode getResultCode() const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/bluetooth/messages/BluetoothMessageStatusInd.h b/src/card/bluetooth/messages/BluetoothMessageStatusInd.h index 4373f4f..dc52373 100644 --- a/src/card/bluetooth/messages/BluetoothMessageStatusInd.h +++ b/src/card/bluetooth/messages/BluetoothMessageStatusInd.h @@ -26,4 +26,4 @@ class BluetoothMessageStatusInd BluetoothStatusChange getStatusChange() const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/bluetooth/messages/BluetoothMessageTransferApduResponse.h b/src/card/bluetooth/messages/BluetoothMessageTransferApduResponse.h index 5075503..e7abd42 100644 --- a/src/card/bluetooth/messages/BluetoothMessageTransferApduResponse.h +++ b/src/card/bluetooth/messages/BluetoothMessageTransferApduResponse.h @@ -24,4 +24,4 @@ class BluetoothMessageTransferApduResponse const QByteArray& getResponseAPDU() const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/bluetooth/messages/BluetoothMessageTransferCardReaderStatusResponse.h b/src/card/bluetooth/messages/BluetoothMessageTransferCardReaderStatusResponse.h index b205ad1..bc6b620 100644 --- a/src/card/bluetooth/messages/BluetoothMessageTransferCardReaderStatusResponse.h +++ b/src/card/bluetooth/messages/BluetoothMessageTransferCardReaderStatusResponse.h @@ -6,7 +6,6 @@ #include "messages/BluetoothMessage.h" #include "parameter/BluetoothMessageParameterCardReaderStatus.h" -#include "parameter/BluetoothMessageParameterStatusChange.h" namespace governikus { @@ -21,4 +20,4 @@ class BluetoothMessageTransferCardReaderStatusResponse BluetoothStatusChange getStatusChange() const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/bluetooth/messages/BluetoothUtils.h b/src/card/bluetooth/messages/BluetoothUtils.h index 7eb356c..db0f4f7 100644 --- a/src/card/bluetooth/messages/BluetoothUtils.h +++ b/src/card/bluetooth/messages/BluetoothUtils.h @@ -24,4 +24,4 @@ class BluetoothUtils static void addPadding(QByteArray& pData, const QByteArray& pContent, ushort pPaddingLen = DEFAULT_PADDING_LENGTH); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/bluetooth/messages/parameter/BluetoothMessageParameter.h b/src/card/bluetooth/messages/parameter/BluetoothMessageParameter.h index 110639a..3a03370 100644 --- a/src/card/bluetooth/messages/parameter/BluetoothMessageParameter.h +++ b/src/card/bluetooth/messages/parameter/BluetoothMessageParameter.h @@ -50,8 +50,6 @@ class BluetoothMessageParameter public: - using Ptr = QSharedPointer; - BluetoothMessageParameter(BluetoothParamId pParamId, const QByteArray& pValue); virtual ~BluetoothMessageParameter(); @@ -64,6 +62,6 @@ class BluetoothMessageParameter virtual QString toStringValue() const; }; -} /* namespace governikus */ +} // namespace governikus QDebug operator<<(QDebug pDbg, const governikus::BluetoothMessageParameter& pMsg); diff --git a/src/card/bluetooth/messages/parameter/BluetoothMessageParameterApduResponse.h b/src/card/bluetooth/messages/parameter/BluetoothMessageParameterApduResponse.h index cf453d2..e28aae1 100644 --- a/src/card/bluetooth/messages/parameter/BluetoothMessageParameterApduResponse.h +++ b/src/card/bluetooth/messages/parameter/BluetoothMessageParameterApduResponse.h @@ -22,4 +22,4 @@ class BluetoothMessageParameterApduResponse virtual QString toStringValue() const override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/bluetooth/messages/parameter/BluetoothMessageParameterCardReaderStatus.h b/src/card/bluetooth/messages/parameter/BluetoothMessageParameterCardReaderStatus.h index e4b8b2c..fdcc2cc 100644 --- a/src/card/bluetooth/messages/parameter/BluetoothMessageParameterCardReaderStatus.h +++ b/src/card/bluetooth/messages/parameter/BluetoothMessageParameterCardReaderStatus.h @@ -6,6 +6,8 @@ #include "messages/parameter/BluetoothMessageParameter.h" +class test_BluetoothMessageParameterCardReaderStatus; + namespace governikus { @@ -13,6 +15,7 @@ class BluetoothMessageParameterCardReaderStatus : public BluetoothMessageParameter { private: + friend class ::test_BluetoothMessageParameterCardReaderStatus; BluetoothCardReaderStatus mCardReaderStatus; public: @@ -26,4 +29,4 @@ class BluetoothMessageParameterCardReaderStatus virtual QString toStringValue() const override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/bluetooth/messages/parameter/BluetoothMessageParameterConnectionStatus.h b/src/card/bluetooth/messages/parameter/BluetoothMessageParameterConnectionStatus.h index fd367cb..4dd7a9d 100644 --- a/src/card/bluetooth/messages/parameter/BluetoothMessageParameterConnectionStatus.h +++ b/src/card/bluetooth/messages/parameter/BluetoothMessageParameterConnectionStatus.h @@ -25,4 +25,4 @@ class BluetoothMessageParameterConnectionStatus virtual QString toStringValue() const override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/bluetooth/messages/parameter/BluetoothMessageParameterMaxMsgSize.h b/src/card/bluetooth/messages/parameter/BluetoothMessageParameterMaxMsgSize.h index 025e10f..b756871 100644 --- a/src/card/bluetooth/messages/parameter/BluetoothMessageParameterMaxMsgSize.h +++ b/src/card/bluetooth/messages/parameter/BluetoothMessageParameterMaxMsgSize.h @@ -26,4 +26,4 @@ class BluetoothMessageParameterMaxMsgSize virtual QString toStringValue() const override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/bluetooth/messages/parameter/BluetoothMessageParameterResultCode.h b/src/card/bluetooth/messages/parameter/BluetoothMessageParameterResultCode.h index 5be6da2..b603c7d 100644 --- a/src/card/bluetooth/messages/parameter/BluetoothMessageParameterResultCode.h +++ b/src/card/bluetooth/messages/parameter/BluetoothMessageParameterResultCode.h @@ -25,4 +25,4 @@ class BluetoothMessageParameterResultCode virtual QString toStringValue() const override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/bluetooth/messages/parameter/BluetoothMessageParameterStatusChange.h b/src/card/bluetooth/messages/parameter/BluetoothMessageParameterStatusChange.h index 4c1707b..26a424b 100644 --- a/src/card/bluetooth/messages/parameter/BluetoothMessageParameterStatusChange.h +++ b/src/card/bluetooth/messages/parameter/BluetoothMessageParameterStatusChange.h @@ -27,4 +27,4 @@ class BluetoothMessageParameterStatusChange virtual QString toStringValue() const override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/drivers/CMakeLists.txt b/src/card/drivers/CMakeLists.txt index b279e62..69d3c47 100644 --- a/src/card/drivers/CMakeLists.txt +++ b/src/card/drivers/CMakeLists.txt @@ -1,3 +1,10 @@ +##################################################################### +# The module card drivers. +# +# This module provides an interface to scan for connected card +# readers and their specific drivers. +##################################################################### + ADD_PLATFORM_LIBRARY(AusweisAppCardDrivers) TARGET_LINK_LIBRARIES(AusweisAppCardDrivers Qt5::Core AusweisAppGlobal AusweisAppConfiguration) diff --git a/src/card/drivers/ReaderDetector.cpp b/src/card/drivers/ReaderDetector.cpp index 24600b1..820613e 100644 --- a/src/card/drivers/ReaderDetector.cpp +++ b/src/card/drivers/ReaderDetector.cpp @@ -4,8 +4,6 @@ #include "ReaderDetector.h" -#include "Env.h" -#include "FuncUtils.h" #include "ReaderConfiguration.h" #include "SingletonHelper.h" @@ -14,22 +12,13 @@ #include #include - using namespace governikus; - defineSingleton(ReaderDetector) - Q_DECLARE_LOGGING_CATEGORY(card_drivers) -ReaderDetector & ReaderDetector::getInstance() -{ - return *Instance; -} - - ReaderDetector::ReaderDetector() #ifdef Q_OS_LINUX : mDeviceListener(nullptr) @@ -45,6 +34,12 @@ ReaderDetector::~ReaderDetector() } +ReaderDetector& ReaderDetector::getInstance() +{ + return *Instance; +} + + QVector ReaderDetector::getAttachedSupportedDevices() const { const auto& readerConfiguration = Env::getSingleton(); @@ -56,7 +51,6 @@ QVector ReaderDetector::getAttachedSupportedDevices() c const auto& readerConfigurationInfo = readerConfiguration->getReaderConfigurationInfoById(devId); if (readerConfigurationInfo.isKnownReader() && !readerConfigurationInfo.getUrl().isEmpty()) { - qCDebug(card_drivers) << "Found known reader:" << devId; attachedSupportedDevices += readerConfigurationInfo; } } diff --git a/src/card/drivers/ReaderDetector.h b/src/card/drivers/ReaderDetector.h index 8dc2655..e6b4db4 100644 --- a/src/card/drivers/ReaderDetector.h +++ b/src/card/drivers/ReaderDetector.h @@ -7,6 +7,7 @@ #pragma once +#include "Env.h" #include "ReaderConfiguration.h" #include "UsbId.h" @@ -36,6 +37,7 @@ class ReaderDetector #endif { Q_OBJECT + friend class Env; private: #ifdef Q_OS_MACOS @@ -49,16 +51,14 @@ class ReaderDetector #endif bool initNativeEvents(); - bool terminateNativeEvents(); - public: + protected: + ReaderDetector(); + virtual ~ReaderDetector(); static ReaderDetector& getInstance(); - ReaderDetector(); - - virtual ~ReaderDetector(); - + public: virtual QVector attachedDevIds() const; #ifdef Q_OS_WIN @@ -75,7 +75,6 @@ class ReaderDetector Q_SIGNALS: void fireReaderChangeDetected(); - }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/drivers/ReaderDetector_linux.cpp b/src/card/drivers/ReaderDetector_linux.cpp index 479a64a..65f01a7 100644 --- a/src/card/drivers/ReaderDetector_linux.cpp +++ b/src/card/drivers/ReaderDetector_linux.cpp @@ -39,7 +39,28 @@ class DeviceListener FD_ZERO(&fds); FD_SET(mFileDescriptor, &fds); - int ret = select(mFileDescriptor + 1, &fds, nullptr, nullptr, nullptr); + // On Linux, select() modifies timeout to reflect the amount of time not + // slept; most other implementations do not do this. (POSIX.1 permits + // either behavior.) + struct timeval timeout; +#ifndef QT_NO_DEBUG + if (QCoreApplication::applicationName().startsWith(QLatin1String("Test"))) + { + timeout = { + 0, /*long tv_sec*/ + 100 /*long tv_usec*/ + }; + } + else +#endif + { + timeout = { + 1, /*long tv_sec*/ + 0 /*long tv_usec*/ + }; + } + + int ret = select(mFileDescriptor + 1, &fds, nullptr, nullptr, &timeout); // Check if our file descriptor has received data if (ret > 0 && FD_ISSET(mFileDescriptor, &fds)) @@ -51,6 +72,12 @@ class DeviceListener Q_EMIT fireDeviceChangeDetected(); } + + if (isInterruptionRequested()) + { + qCDebug(card_drivers) << "Thread interruption requested."; + break; + } } } @@ -98,8 +125,14 @@ bool ReaderDetector::initNativeEvents() bool ReaderDetector::terminateNativeEvents() { disconnect(mDeviceListener, &DeviceListener::fireDeviceChangeDetected, this, &ReaderDetector::fireReaderChangeDetected); - mDeviceListener->terminate(); - mDeviceListener->wait(); + mDeviceListener->requestInterruption(); + const int waitForMilliseconds = 3 * 1000; + mDeviceListener->wait(waitForMilliseconds); + if (mDeviceListener->isRunning()) + { + qCDebug(card_drivers) << "Terminating device listener thread."; + mDeviceListener->terminate(); + } delete mDeviceListener; return true; diff --git a/src/card/drivers/ReaderDetector_osx.cpp b/src/card/drivers/ReaderDetector_osx.cpp index 99b83c9..194e615 100644 --- a/src/card/drivers/ReaderDetector_osx.cpp +++ b/src/card/drivers/ReaderDetector_osx.cpp @@ -49,8 +49,7 @@ static bool listenTo(const io_name_t notificationType, ReaderDetector* readerDet matchingDict, deviceChanged, readerDetector, - iterator - ); + iterator); if (kr != KERN_SUCCESS) { diff --git a/src/card/nfc/CMakeLists.txt b/src/card/nfc/CMakeLists.txt index 858670e..c7d8d92 100644 --- a/src/card/nfc/CMakeLists.txt +++ b/src/card/nfc/CMakeLists.txt @@ -1,3 +1,10 @@ +##################################################################### +# The ReaderManagerPlugin for QtNfc. +# +# This plugin is a generic wrapper around Qt's NFC module. +# Supported: Android +##################################################################### + ADD_PLATFORM_LIBRARY(AusweisAppCardNfc) TARGET_LINK_LIBRARIES(AusweisAppCardNfc Qt5::Core Qt5::Nfc AusweisAppGlobal AusweisAppCard) diff --git a/src/card/nfc/NfcCard.cpp b/src/card/nfc/NfcCard.cpp index 63e359f..4742943 100644 --- a/src/card/nfc/NfcCard.cpp +++ b/src/card/nfc/NfcCard.cpp @@ -107,7 +107,7 @@ CardReturnCode NfcCard::transmit(const CommandApdu& pCmd, ResponseApdu& pRes) return CardReturnCode::COMMAND_FAILED; } - qCDebug(card_nfc) << "Transmit command APDU: " << pCmd.getBuffer().toHex(); + qCDebug(card_nfc) << "Transmit command APDU:" << pCmd.getBuffer().toHex(); if (!mNearFieldTarget->accessMethods().testFlag(QNearFieldTarget::AccessMethod::TagTypeSpecificAccess)) { @@ -122,10 +122,8 @@ CardReturnCode NfcCard::transmit(const CommandApdu& pCmd, ResponseApdu& pRes) return CardReturnCode::COMMAND_FAILED; } - if (!mNearFieldTarget->waitForRequestCompleted(id, 750)) + if (!mNearFieldTarget->waitForRequestCompleted(id, 1500)) { - Q_EMIT fireCardRemoved(); - qCWarning(card_nfc) << "Transmit timeout reached"; return CardReturnCode::COMMAND_FAILED; } @@ -138,7 +136,7 @@ CardReturnCode NfcCard::transmit(const CommandApdu& pCmd, ResponseApdu& pRes) } QByteArray recvBuffer = response.toByteArray(); - qCDebug(card_nfc) << "Transmit response APDU: " << recvBuffer.toHex(); + qCDebug(card_nfc) << "Transmit response APDU:" << recvBuffer.toHex(); pRes.setBuffer(recvBuffer); return CardReturnCode::OK; } diff --git a/src/card/nfc/NfcCard.h b/src/card/nfc/NfcCard.h index 9a81cdf..e38c0a0 100644 --- a/src/card/nfc/NfcCard.h +++ b/src/card/nfc/NfcCard.h @@ -26,9 +26,6 @@ class NfcCard private Q_SLOTS: void onError(QNearFieldTarget::Error pError, const QNearFieldTarget::RequestId& pId); - Q_SIGNALS: - void fireCardRemoved(); - public: NfcCard(QNearFieldTarget* pNearFieldTarget); virtual ~NfcCard() override; @@ -43,4 +40,4 @@ class NfcCard virtual CardReturnCode transmit(const CommandApdu& pCmd, ResponseApdu& pRes) override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/nfc/NfcReader.cpp b/src/card/nfc/NfcReader.cpp index 3a75c8e..ab5a425 100644 --- a/src/card/nfc/NfcReader.cpp +++ b/src/card/nfc/NfcReader.cpp @@ -86,7 +86,7 @@ NfcReader::~NfcReader() Card* NfcReader::getCard() const { - if (mCard->isValid()) + if (mCard && mCard->isValid()) { return mCard.data(); } diff --git a/src/card/nfc/NfcReader.h b/src/card/nfc/NfcReader.h index adcc700..70fe04f 100644 --- a/src/card/nfc/NfcReader.h +++ b/src/card/nfc/NfcReader.h @@ -40,4 +40,4 @@ class NfcReader virtual Card* getCard() const override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/nfc/NfcReaderManagerPlugIn.cpp b/src/card/nfc/NfcReaderManagerPlugIn.cpp index d50616b..206ec30 100644 --- a/src/card/nfc/NfcReaderManagerPlugIn.cpp +++ b/src/card/nfc/NfcReaderManagerPlugIn.cpp @@ -40,7 +40,7 @@ bool isAvailable() } -} +} // namespace void NfcReaderManagerPlugIn::onNfcAdapterStateChanged(bool pEnabled) diff --git a/src/card/nfc/NfcReaderManagerPlugIn.h b/src/card/nfc/NfcReaderManagerPlugIn.h index ffb7239..3b6dabc 100644 --- a/src/card/nfc/NfcReaderManagerPlugIn.h +++ b/src/card/nfc/NfcReaderManagerPlugIn.h @@ -40,4 +40,4 @@ class NfcReaderManagerPlugIn virtual void shutdown() override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/pcsc/CMakeLists.txt b/src/card/pcsc/CMakeLists.txt index d9eb586..9d0f391 100644 --- a/src/card/pcsc/CMakeLists.txt +++ b/src/card/pcsc/CMakeLists.txt @@ -1,3 +1,10 @@ +##################################################################### +# The ReaderManagerPlugin for PCSC. +# +# This plugin is a wrapper around different PCSC implementations. +# Supported: Windows, macOS and pcsclite on Unix/Linux. +##################################################################### + ADD_PLATFORM_LIBRARY(AusweisAppCardPcsc) TARGET_INCLUDE_DIRECTORIES(AusweisAppCardPcsc SYSTEM PUBLIC ${PCSC_INCLUDE_DIRS}) diff --git a/src/card/pcsc/PcscCard.cpp b/src/card/pcsc/PcscCard.cpp index ec24545..34f5e3e 100644 --- a/src/card/pcsc/PcscCard.cpp +++ b/src/card/pcsc/PcscCard.cpp @@ -4,7 +4,8 @@ #include "PcscCard.h" -#include "DestroyPACEChannel.h" +#include "DestroyPaceChannel.h" +#include "EstablishPaceChannel.h" #include "PinModify.h" #include @@ -46,7 +47,7 @@ QLatin1String protocolToString(PCSC_INT pProtocol) } -} +} // namespace PcscCard::PcscCard(PcscReader* pPcscReader) @@ -296,10 +297,10 @@ PCSC_RETURNCODE PcscCard::transmit(const QByteArray& pSendBuffer, } -CardReturnCode PcscCard::establishPaceChannel(PACE_PASSWORD_ID pPasswordId, +CardReturnCode PcscCard::establishPaceChannel(PacePasswordId pPasswordId, const QByteArray& pChat, const QByteArray& pCertificateDescription, - EstablishPACEChannelOutput& pChannelOutput, quint8 pTimeoutSeconds) + EstablishPaceChannelOutput& pChannelOutput, quint8 pTimeoutSeconds) { Q_UNUSED(pTimeoutSeconds); if (!mReader->hasFeature(FeatureID::EXECUTE_PACE)) @@ -308,7 +309,7 @@ CardReturnCode PcscCard::establishPaceChannel(PACE_PASSWORD_ID pPasswordId, } PCSC_INT cmdID = mReader->getFeatureValue(FeatureID::EXECUTE_PACE); - EstablishPACEChannelBuilder builder; + EstablishPaceChannel builder; builder.setPasswordId(pPasswordId); builder.setChat(pChat); builder.setCertificateDescription(pCertificateDescription); @@ -334,7 +335,7 @@ CardReturnCode PcscCard::destroyPaceChannel() } PCSC_INT cmdID = mReader->getFeatureValue(FeatureID::EXECUTE_PACE); - DestroyPACEChannelBuilder builder; + DestroyPaceChannelBuilder builder; QByteArray controlRes; PCSC_RETURNCODE returnCode = control(cmdID, builder.createCommandData(), controlRes); if (returnCode != PcscUtils::Scard_S_Success) diff --git a/src/card/pcsc/PcscCard.h b/src/card/pcsc/PcscCard.h index 7b0bb3f..59e8412 100644 --- a/src/card/pcsc/PcscCard.h +++ b/src/card/pcsc/PcscCard.h @@ -54,11 +54,11 @@ class PcscCard virtual CardReturnCode transmit(const CommandApdu& pCmd, ResponseApdu& pRes) override; - virtual CardReturnCode establishPaceChannel(PACE_PASSWORD_ID pPasswordId, const QByteArray& pChat, const QByteArray& pCertificateDescription, EstablishPACEChannelOutput& pChannelOutput, quint8 pTimeoutSeconds) override; + virtual CardReturnCode establishPaceChannel(PacePasswordId pPasswordId, const QByteArray& pChat, const QByteArray& pCertificateDescription, EstablishPaceChannelOutput& pChannelOutput, quint8 pTimeoutSeconds) override; virtual CardReturnCode destroyPaceChannel() override; virtual CardReturnCode setEidPin(quint8 pTimeoutSeconds, ResponseApdu& pResponseApdu) override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/pcsc/PcscReader.cpp b/src/card/pcsc/PcscReader.cpp index e7ff9e7..c5cb8c0 100644 --- a/src/card/pcsc/PcscReader.cpp +++ b/src/card/pcsc/PcscReader.cpp @@ -3,13 +3,13 @@ */ #include "PcscCard.h" -#include "PcscReader.h" #include #include using namespace governikus; + Q_DECLARE_LOGGING_CATEGORY(card_pcsc) PcscReader::PcscReader(const QString& pReaderName) @@ -24,7 +24,7 @@ PcscReader::PcscReader(const QString& pReaderName) setObjectName(pReaderName); PCSC_RETURNCODE returnCode = SCardEstablishContext(SCARD_SCOPE_USER, nullptr, nullptr, &mContextHandle); - qCDebug(card_pcsc) << "SCardEstablishContext: " << PcscUtils::toString(returnCode); + qCDebug(card_pcsc) << "SCardEstablishContext:" << PcscUtils::toString(returnCode); if (returnCode != PcscUtils::Scard_S_Success) { qCWarning(card_pcsc) << "Cannot establish context"; @@ -44,7 +44,7 @@ PcscReader::PcscReader(const QString& pReaderName) returnCode = readReaderFeaturesAndPACECapabilities(); if (returnCode != PcscUtils::Scard_S_Success) { - qCWarning(card_pcsc) << "Features / Capabilities not successful: " << returnCode; + qCWarning(card_pcsc) << "Features / Capabilities not successful:" << returnCode; return; } @@ -250,7 +250,7 @@ PCSC_RETURNCODE PcscReader::readReaderFeaturesAndPACECapabilities() qCDebug(card_pcsc) << str; PCSC_RETURNCODE returnCode = SCardConnect(mContextHandle, mReaderState.szReader, SCARD_SHARE_DIRECT, PROTOCOL, &cardHandle, &protocol); - qCDebug(card_pcsc) << "SCardConnect for " << mReaderInfo.getName() << ": " << PcscUtils::toString(returnCode); + qCDebug(card_pcsc) << "SCardConnect for" << mReaderInfo.getName() << ':' << PcscUtils::toString(returnCode); if (returnCode != PcscUtils::Scard_S_Success) { return returnCode; @@ -270,7 +270,7 @@ PCSC_RETURNCODE PcscReader::readReaderFeaturesAndPACECapabilities() PCSC_INT clen = 0; returnCode = SCardControl(cardHandle, CM_IOCTL_GET_FEATURE_REQUEST, inBuffer1, 2, buffer, sizeof(buffer), &clen); - qCDebug(card_pcsc) << "SCardControl for " << mReaderInfo.getName() << ": " << PcscUtils::toString(returnCode); + qCDebug(card_pcsc) << "SCardControl for" << mReaderInfo.getName() << ':' << PcscUtils::toString(returnCode); if (returnCode != PcscUtils::Scard_S_Success) { @@ -302,9 +302,9 @@ PCSC_RETURNCODE PcscReader::readReaderFeaturesAndPACECapabilities() const uchar inBuffer2[3] = { 1, 0, 0 }; // idx for GetReaderPACECapabilities (0x01), length (0, 0) - qCDebug(card_pcsc) << "SCardControl ... "; + qCDebug(card_pcsc) << "SCardControl ..."; returnCode = SCardControl(cardHandle, cmdID, inBuffer2, 3, buffer, sizeof(buffer), &clen); - qCDebug(card_pcsc) << "SCardControl for " << mReaderInfo.getName() << ": " << PcscUtils::toString(returnCode); + qCDebug(card_pcsc) << "SCardControl for" << mReaderInfo.getName() << ':' << PcscUtils::toString(returnCode); if (returnCode != PcscUtils::Scard_S_Success) { @@ -334,7 +334,7 @@ PCSC_RETURNCODE PcscReader::readReaderFeaturesAndPACECapabilities() // disconnect returnCode = SCardDisconnect(cardHandle, SCARD_LEAVE_CARD); - qCDebug(card_pcsc) << "SCardDisconnect for " << mReaderInfo.getName() << ": " << PcscUtils::toString(returnCode); + qCDebug(card_pcsc) << "SCardDisconnect for" << mReaderInfo.getName() << ':' << PcscUtils::toString(returnCode); return returnCode; } diff --git a/src/card/pcsc/PcscReader.h b/src/card/pcsc/PcscReader.h index 449de04..5991a5b 100644 --- a/src/card/pcsc/PcscReader.h +++ b/src/card/pcsc/PcscReader.h @@ -6,7 +6,6 @@ #pragma once -#include "CardConnectionWorker.h" #include "PcscReaderFeature.h" #include "PcscReaderPaceCapability.h" #include "PcscUtils.h" @@ -18,9 +17,6 @@ namespace governikus { - -class PcscReaderFeature; -class PcscReaderPaceCapability; class PcscCard; @@ -61,4 +57,4 @@ class PcscReader }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/pcsc/PcscReaderFeature.h b/src/card/pcsc/PcscReaderFeature.h index ff2f2ea..5a47500 100644 --- a/src/card/pcsc/PcscReaderFeature.h +++ b/src/card/pcsc/PcscReaderFeature.h @@ -61,4 +61,4 @@ inline QDebug operator<<(QDebug pDbg, const governikus::PcscReaderFeature& pPcsc } -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/pcsc/PcscReaderManagerPlugIn.cpp b/src/card/pcsc/PcscReaderManagerPlugIn.cpp index f2dd55b..9012557 100644 --- a/src/card/pcsc/PcscReaderManagerPlugIn.cpp +++ b/src/card/pcsc/PcscReaderManagerPlugIn.cpp @@ -49,12 +49,13 @@ QList PcscReaderManagerPlugIn::getReaders() const } -void PcscReaderManagerPlugIn::init() +void PcscReaderManagerPlugIn::startScan(bool pAutoConnect) { - ReaderManagerPlugIn::init(); + Q_UNUSED(pAutoConnect); + PCSC_RETURNCODE returnCode = SCardEstablishContext(SCARD_SCOPE_USER, nullptr, nullptr, &mContextHandle); setReaderInfoEnabled(returnCode == PcscUtils::Scard_S_Success); - qCDebug(card_pcsc) << "SCardEstablishContext: " << PcscUtils::toString(returnCode); + qCDebug(card_pcsc) << "SCardEstablishContext:" << PcscUtils::toString(returnCode); if (returnCode != PcscUtils::Scard_S_Success) { qCWarning(card_pcsc) << "Not started: Cannot establish context"; @@ -67,7 +68,7 @@ void PcscReaderManagerPlugIn::init() } -void PcscReaderManagerPlugIn::shutdown() +void PcscReaderManagerPlugIn::stopScan() { if (mTimerId) { @@ -77,13 +78,14 @@ void PcscReaderManagerPlugIn::shutdown() if (mContextHandle) { PCSC_RETURNCODE returnCode = SCardReleaseContext(mContextHandle); - qCDebug(card_pcsc) << "SCardReleaseContext: " << PcscUtils::toString(returnCode); + qCDebug(card_pcsc) << "SCardReleaseContext:" << PcscUtils::toString(returnCode); mContextHandle = 0; if (returnCode != PcscUtils::Scard_S_Success) { qCWarning(card_pcsc) << "Error releasing context"; } } + updateReaders(); } @@ -109,16 +111,9 @@ void PcscReaderManagerPlugIn::initReaderState() void PcscReaderManagerPlugIn::updateReaders() { - QStringList readersToRemove; + QStringList readersToRemove(mReaders.keys()); QStringList readersToAdd; - readersToRemove.reserve(mReaders.size()); - const auto& readerNames = mReaders.keys(); - for (const auto& readerName : readerNames) - { - readersToRemove += readerName; - } - PCSC_RETURNCODE returnCode = readReaderNames(readersToAdd); if (returnCode != PcscUtils::Scard_S_Success && returnCode != PcscUtils::Scard_E_No_Readers_Available) { @@ -131,8 +126,8 @@ void PcscReaderManagerPlugIn::updateReaders() // contexts fail with SCARD_E_NO_SERVICE. We try to restart the manager // in that case. qCDebug(card_pcsc) << "got SCARD_E_NO_SERVICE, trying to restart plugin"; - shutdown(); - init(); + stopScan(); + startScan(true); } else if (returnCode == PcscUtils::Scard_E_Service_Stopped && mTimerId != 0) { @@ -141,18 +136,17 @@ void PcscReaderManagerPlugIn::updateReaders() // contexts fail with SCARD_E_SERVICE_STOPPED. We try to restart the manager // in that case. qCDebug(card_pcsc) << "got SCARD_E_SERVICE_STOPPED, trying to restart plugin"; - shutdown(); - init(); + stopScan(); + startScan(true); } else if (returnCode == PcscUtils::Scard_E_Invalid_Handle && mTimerId != 0) { // If the pc/sc daemon terminates on Linux, the handle is invalidated. We try // to restart the manager in this case. qCDebug(card_pcsc) << "got SCARD_E_INVALID_HANDLE, trying to restart plugin"; - shutdown(); - init(); + stopScan(); + startScan(true); } - } for (QMutableListIterator it(readersToAdd); it.hasNext();) @@ -180,7 +174,7 @@ void PcscReaderManagerPlugIn::updateReaders() connect(reader, &Reader::fireCardRemoved, this, &PcscReaderManagerPlugIn::fireCardRemoved); connect(reader, &Reader::fireCardRetryCounterChanged, this, &PcscReaderManagerPlugIn::fireCardRetryCounterChanged); - qCDebug(card_pcsc) << "fireReaderAdded " << readerName; + qCDebug(card_pcsc) << "fireReaderAdded:" << readerName; Q_EMIT fireReaderAdded(readerName); } } @@ -200,27 +194,31 @@ QString PcscReaderManagerPlugIn::extractReaderName(PCSC_CHAR_PTR pReaderPointer) void PcscReaderManagerPlugIn::removeReader(const QString& pReaderName) { + Q_ASSERT(mReaders.contains(pReaderName)); delete mReaders.take(pReaderName); - qCDebug(card_pcsc) << "fireReaderRemoved " << pReaderName; + qCDebug(card_pcsc) << "fireReaderRemoved:" << pReaderName; Q_EMIT fireReaderRemoved(pReaderName); } PCSC_RETURNCODE PcscReaderManagerPlugIn::readReaderNames(QStringList& pReaderNames) { - QVarLengthArray readers; + if (!mContextHandle) + { + return PcscUtils::Scard_E_Invalid_Handle; + } + QVarLengthArray readers; PCSC_INT maxReadersSize = static_cast(readers.capacity()); PCSC_RETURNCODE returnCode = SCardListReaders(mContextHandle, nullptr, readers.data(), &maxReadersSize); - if (returnCode == PcscUtils::Scard_E_No_Readers_Available) + if (returnCode != PcscUtils::Scard_S_Success) { - return returnCode; - } - else if (returnCode != PcscUtils::Scard_S_Success) - { - qCWarning(card_pcsc) << "SCardListReaders: " << PcscUtils::toString(returnCode); - qCWarning(card_pcsc) << "Cannot read reader names"; + if (returnCode != PcscUtils::Scard_E_No_Readers_Available) + { + qCWarning(card_pcsc) << "SCardListReaders:" << PcscUtils::toString(returnCode); + qCWarning(card_pcsc) << "Cannot read reader names"; + } return returnCode; } diff --git a/src/card/pcsc/PcscReaderManagerPlugIn.h b/src/card/pcsc/PcscReaderManagerPlugIn.h index c254c7b..1e24f45 100644 --- a/src/card/pcsc/PcscReaderManagerPlugIn.h +++ b/src/card/pcsc/PcscReaderManagerPlugIn.h @@ -45,8 +45,8 @@ class PcscReaderManagerPlugIn QList getReaders() const override; - void init() override; - void shutdown() override; + virtual void startScan(bool pAutoConnect) override; + virtual void stopScan() override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/pcsc/PcscReaderPaceCapability.h b/src/card/pcsc/PcscReaderPaceCapability.h index 6d9c63a..9a8c065 100644 --- a/src/card/pcsc/PcscReaderPaceCapability.h +++ b/src/card/pcsc/PcscReaderPaceCapability.h @@ -39,4 +39,4 @@ inline QDebug operator<<(QDebug pDbg, const governikus::PcscReaderPaceCapability } -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/pcsc/PcscUtils.h b/src/card/pcsc/PcscUtils.h index 27d8159..b595275 100644 --- a/src/card/pcsc/PcscUtils.h +++ b/src/card/pcsc/PcscUtils.h @@ -204,4 +204,4 @@ class PcscUtils #undef SCARD_W_CARD_NOT_AUTHENTICATED #endif -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/remote/CMakeLists.txt b/src/card/remote/CMakeLists.txt deleted file mode 100644 index 03f4a5f..0000000 --- a/src/card/remote/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -ADD_PLATFORM_LIBRARY(AusweisAppCardRemote) - -TARGET_LINK_LIBRARIES(AusweisAppCardRemote Qt5::Core AusweisAppGlobal AusweisAppCard AusweisAppRemoteDevice) -TARGET_COMPILE_DEFINITIONS(AusweisAppCardRemote PRIVATE QT_STATICPLUGIN) diff --git a/src/card/remote/RemoteCard.cpp b/src/card/remote/RemoteCard.cpp deleted file mode 100644 index 9f031c9..0000000 --- a/src/card/remote/RemoteCard.cpp +++ /dev/null @@ -1,244 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "RemoteCard.h" - -#include "messages/IfdConnect.h" -#include "messages/IfdConnectResponse.h" -#include "messages/IfdDisconnect.h" -#include "messages/IfdDisconnectResponse.h" -#include "messages/IfdError.h" -#include "messages/IfdEstablishPaceChannel.h" -#include "messages/IfdEstablishPaceChannelResponse.h" -#include "messages/IfdModifyPin.h" -#include "messages/IfdModifyPinResponse.h" -#include "messages/IfdTransmit.h" -#include "messages/IfdTransmitResponse.h" -#include "PinModify.h" -#include "PinModifyOutput.h" - -#include -#include - - -using namespace governikus; - - -Q_DECLARE_LOGGING_CATEGORY(card_remote) - - -bool RemoteCard::sendMessage(const QSharedPointer& pMessage, RemoteCardMessageType pExpectedAnswer, unsigned long pTimeout) -{ - // mResponseAvailable is locked by the constructor, to revert the mutex behavior. - // Locking this is a requirement for QWaitCondition. - - mWaitingForAnswer = true; - mExpectedAnswerType = pExpectedAnswer; - - const auto& connection = QObject::connect(mRemoteDispatcher.data(), &RemoteDispatcher::fireReceived, this, &RemoteCard::onMessageReceived, Qt::DirectConnection); - QMetaObject::invokeMethod(mRemoteDispatcher.data(), "send", Qt::QueuedConnection, Q_ARG(QSharedPointer, pMessage)); - - mWaitCondition.wait(&mResponseAvailable, pTimeout); - QObject::disconnect(connection); - - QMutexLocker locker(&mProcessResponse); - - if (mWaitingForAnswer) - { - mWaitingForAnswer = false; - return false; - } - - return true; -} - - -void RemoteCard::onMessageReceived(const QSharedPointer& pMessage) -{ - QMutexLocker locker(&mProcessResponse); - - if (!mWaitingForAnswer) - { - return; - } - - if (pMessage->getType() == mExpectedAnswerType || pMessage->getType() == RemoteCardMessageType::IFDError) - { - mResponse = pMessage; - mWaitingForAnswer = false; - mWaitCondition.wakeOne(); - } -} - - -void RemoteCard::onDispatcherClosed(GlobalStatus::Code pCloseCode, const QSharedPointer&) -{ - QMutexLocker locker(&mProcessResponse); - - if (mWaitingForAnswer) - { - qCWarning(card_remote) << "RemoteDispatcher was closed while waiting for an answer:" << pCloseCode; - - mResponse.reset(); - mWaitingForAnswer = false; - mWaitCondition.wakeOne(); - } -} - - -RemoteCard::RemoteCard(const QSharedPointer& pRemoteDispatcher, const QString& pReaderName) - : Card() - , mWaitingForAnswer(false) - , mWaitCondition() - , mResponseAvailable() - , mProcessResponse() - , mExpectedAnswerType() - , mResponse() - , mRemoteDispatcher(pRemoteDispatcher) - , mReaderName(pReaderName) - , mConnected(false) -{ - Q_ASSERT(mRemoteDispatcher); - - mResponseAvailable.lock(); - const QString& contextHandle = mRemoteDispatcher->getContextHandle(); - mReaderName.remove(contextHandle); - - QObject::connect(mRemoteDispatcher.data(), &RemoteDispatcher::fireClosed, this, &RemoteCard::onDispatcherClosed, Qt::DirectConnection); -} - - -RemoteCard::~RemoteCard() -{ - mResponseAvailable.unlock(); -} - - -CardReturnCode RemoteCard::connect() -{ - const QSharedPointer connectMsg(new IfdConnect(mReaderName)); - if (sendMessage(connectMsg, RemoteCardMessageType::IFDConnectResponse, 5000)) - { - const QSharedPointer response = mResponse.dynamicCast(); - if (response) - { - if (!response->resultHasError()) - { - mConnected = true; - mSlotHandle = response->getSlotHandle(); - return CardReturnCode::OK; - } - qCWarning(card_remote) << response->getResultMinor(); - } - } - return CardReturnCode::COMMAND_FAILED; -} - - -CardReturnCode RemoteCard::disconnect() -{ - const QSharedPointer disconnectCmd(new IfdDisconnect(mSlotHandle)); - if (sendMessage(disconnectCmd, RemoteCardMessageType::IFDDisconnectResponse, 5000)) - { - const QSharedPointer response = mResponse.dynamicCast(); - if (response) - { - if (!response->resultHasError()) - { - mConnected = false; - return CardReturnCode::OK; - } - qCWarning(card_remote) << response->getResultMinor(); - } - } - return CardReturnCode::COMMAND_FAILED; -} - - -bool RemoteCard::isConnected() -{ - return mConnected; -} - - -CardReturnCode RemoteCard::transmit(const CommandApdu& pCommand, ResponseApdu& pResponse) -{ - QSharedPointer transmitCmd(new IfdTransmit(mSlotHandle, pCommand.getBuffer())); - if (sendMessage(transmitCmd, RemoteCardMessageType::IFDTransmitResponse, 5000)) - { - const QSharedPointer response = mResponse.dynamicCast(); - if (response) - { - if (!response->resultHasError()) - { - pResponse.setBuffer(response->getResponseApdu()); - return CardReturnCode::OK; - } - qCWarning(card_remote) << response->getResultMinor(); - } - } - return CardReturnCode::COMMAND_FAILED; -} - - -CardReturnCode RemoteCard::establishPaceChannel(PACE_PASSWORD_ID pPasswordId, const QByteArray& pChat, const QByteArray& pCertificateDescription, EstablishPACEChannelOutput& pChannelOutput, quint8 pTimeoutSeconds) -{ - EstablishPACEChannelBuilder builder; - builder.setPasswordId(pPasswordId); - builder.setChat(pChat); - builder.setCertificateDescription(pCertificateDescription); - const QByteArray inputData = builder.createCommandDataCcid().getBuffer(); - - QSharedPointer message(new IfdEstablishPaceChannel(mSlotHandle, inputData)); - if (sendMessage(message, RemoteCardMessageType::IFDEstablishPACEChannelResponse, pTimeoutSeconds * 1000)) - { - const QSharedPointer response = mResponse.dynamicCast(); - if (response) - { - pChannelOutput.parseFromCcid(response->getOutputData(), pPasswordId); - return pChannelOutput.getPaceReturnCode(); - } - } - - return CardReturnCode::COMMAND_FAILED; -} - - -CardReturnCode RemoteCard::setEidPin(quint8 pTimeoutSeconds, ResponseApdu& pResponseApdu) -{ - PinModify pinModify(pTimeoutSeconds); - const QByteArray inputData = pinModify.createCcidForRemote(); - - QSharedPointer message(new IfdModifyPin(mSlotHandle, inputData)); - if (sendMessage(message, RemoteCardMessageType::IFDModifyPINResponse, pTimeoutSeconds * 1000)) - { - const QSharedPointer response = mResponse.dynamicCast(); - if (response) - { - PinModifyOutput output(response->getOutputData()); - pResponseApdu.setBuffer(output.getResponseApdu().getBuffer()); - if (response->resultHasError()) - { - return response->getReturnCode(); - } - else - { - return output.getReturnCode(); - } - } - - const QSharedPointer ifdError = mResponse.dynamicCast(); - if (ifdError) - { - if (ifdError->getResultMinor() == QLatin1String("http://www.bsi.bund.de/ecard/api/1.1/resultminor/al/common#unknownAPIFunction")) - { - return CardReturnCode::PROTOCOL_ERROR; - } - - return CardReturnCode::UNKNOWN; - } - } - - return CardReturnCode::COMMAND_FAILED; -} diff --git a/src/card/remote/RemoteCard.h b/src/card/remote/RemoteCard.h deleted file mode 100644 index f302567..0000000 --- a/src/card/remote/RemoteCard.h +++ /dev/null @@ -1,63 +0,0 @@ -/*! - * \brief Implementation of \ref Card for remote reader. - * - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "Card.h" -#include "messages/RemoteMessage.h" -#include "RemoteDispatcher.h" - -#include -#include -#include -#include - - -namespace governikus -{ - -class RemoteCard - : public Card -{ - Q_OBJECT - - private: - bool mWaitingForAnswer; - QWaitCondition mWaitCondition; - QMutex mResponseAvailable, mProcessResponse; - - RemoteCardMessageType mExpectedAnswerType; - QSharedPointer mResponse; - const QSharedPointer mRemoteDispatcher; - QString mReaderName; - QString mSlotHandle; - bool mConnected; - - bool sendMessage(const QSharedPointer& pMessage, RemoteCardMessageType pExpectedAnswer, unsigned long pTimeout); - - private Q_SLOTS: - void onMessageReceived(const QSharedPointer& pMessage); - void onDispatcherClosed(GlobalStatus::Code pCloseCode, const QSharedPointer& pRemoteDispatcher); - - Q_SIGNALS: - void fireCardRemoved(); - - public: - RemoteCard(const QSharedPointer& pRemoteDispatcher, const QString& pReaderName); - virtual ~RemoteCard() override; - - virtual CardReturnCode connect() override; - virtual CardReturnCode disconnect() override; - virtual bool isConnected() override; - - virtual CardReturnCode transmit(const CommandApdu& pCmd, ResponseApdu& pRes) override; - - virtual CardReturnCode establishPaceChannel(PACE_PASSWORD_ID pPasswordId, const QByteArray& pChat, const QByteArray& pCertificateDescription, EstablishPACEChannelOutput& pChannelOutput, quint8 pTimeoutSeconds = 60) override; - - virtual CardReturnCode setEidPin(quint8 pTimeoutSeconds, ResponseApdu& pResponseApdu) override; -}; - -} /* namespace governikus */ diff --git a/src/card/remote/RemoteReader.cpp b/src/card/remote/RemoteReader.cpp deleted file mode 100644 index 35fd670..0000000 --- a/src/card/remote/RemoteReader.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "RemoteReader.h" - -#include "CardConnectionWorker.h" - -#include -#include - -using namespace governikus; - -Q_DECLARE_LOGGING_CATEGORY(card_remote) - - -RemoteReader::RemoteReader(const QString& pReaderName, const QSharedPointer& pRemoteDispatcher, const IfdStatus& pIfdStatus) - : Reader(ReaderManagerPlugInType::REMOTE, pReaderName) - , mRemoteDispatcher(pRemoteDispatcher) -{ - mReaderInfo.setBasicReader(!pIfdStatus.getPaceCapabilities().getPace()); - mReaderInfo.setConnected(true); - - update(pIfdStatus); -} - - -RemoteReader::~RemoteReader() -{ - mCard.reset(); -} - - -Card* RemoteReader::getCard() const -{ - return mCard.data(); -} - - -Reader::CardEvent RemoteReader::updateCard() -{ - return CardEvent::NONE; -} - - -void RemoteReader::update(const IfdStatus& pIfdStatus) -{ - if (mReaderInfo.getMaxApduLength() != pIfdStatus.getMaxApduLength()) - { - mReaderInfo.setMaxApduLength(pIfdStatus.getMaxApduLength()); - Q_EMIT fireReaderPropertiesUpdated(getName()); - - if (!mReaderInfo.sufficientApduLength()) - { - qCDebug(card_remote) << "ExtendedLengthApduSupport missing. maxAPDULength:" << mReaderInfo.getMaxApduLength(); - } - } - - if (mCard) - { - if (!pIfdStatus.getCardAvailable()) - { - qCDebug(card_remote) << "Card removed"; - mReaderInfo.setCardInfo(CardInfo(CardType::NONE)); - mCard.reset(); - Q_EMIT fireCardRemoved(getName()); - } - return; - } - - if (pIfdStatus.getCardAvailable()) - { - qCDebug(card_remote) << "Card inserted"; - mCard.reset(new RemoteCard(mRemoteDispatcher, getName())); - QSharedPointer cardConnection = createCardConnectionWorker(); - CardInfoFactory::create(cardConnection, mReaderInfo); - Q_EMIT fireCardInserted(getName()); - } -} diff --git a/src/card/remote/RemoteReader.h b/src/card/remote/RemoteReader.h deleted file mode 100644 index 83c6dd6..0000000 --- a/src/card/remote/RemoteReader.h +++ /dev/null @@ -1,41 +0,0 @@ -/*! - * \brief Implementation of \ref Reader for remote reader. - * - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "messages/IfdStatus.h" -#include "Reader.h" -#include "RemoteCard.h" -#include "RemoteDispatcher.h" - -#include -#include - - -namespace governikus -{ - -class RemoteReader - : public Reader -{ - Q_OBJECT - - private: - QScopedPointer mCard; - const QSharedPointer mRemoteDispatcher; - - virtual CardEvent updateCard() override; - - public: - RemoteReader(const QString& pReaderName, const QSharedPointer& pRemoteDispatcher, const IfdStatus& pIfdStatus); - virtual ~RemoteReader() override; - - virtual Card* getCard() const override; - - void update(const IfdStatus& pIfdStatus); -}; - -} /* namespace governikus */ diff --git a/src/card/remote/RemoteReaderManagerPlugIn.cpp b/src/card/remote/RemoteReaderManagerPlugIn.cpp deleted file mode 100644 index 6bc6c89..0000000 --- a/src/card/remote/RemoteReaderManagerPlugIn.cpp +++ /dev/null @@ -1,341 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "RemoteReaderManagerPlugIn.h" - -#include "AppSettings.h" -#include "FuncUtils.h" -#include "messages/GetIfdStatus.h" -#include "messages/IfdError.h" -#include "messages/IfdEstablishContext.h" -#include "messages/IfdEstablishContextResponse.h" -#include "messages/IfdStatus.h" -#include "RemoteClient.h" -#include "RemoteDeviceList.h" -#include "RemoteReader.h" - -#include - - -using namespace governikus; - - -Q_DECLARE_LOGGING_CATEGORY(card_remote) - - -void RemoteReaderManagerPlugIn::updateReader(const IfdStatus& pIfdStatus) -{ - const QSharedPointer remoteToUpdate = mRemoteToUpdate.toStrongRef(); - const QString& contextHandle = remoteToUpdate->getContextHandle(); - const QString& readerName = pIfdStatus.getSlotName() + contextHandle; - - if (mReaderList.contains(readerName)) - { - if (pIfdStatus.getConnectedReader()) - { - static_cast(mReaderList[readerName])->update(pIfdStatus); - } - else - { - qCDebug(card_remote) << "Removed reader" << readerName; - Q_EMIT fireReaderRemoved(readerName); - delete mReaderList.take(readerName); - mRemoteDispatchers.remove(remoteToUpdate, readerName); - } - return; - } - - if (pIfdStatus.getConnectedReader()) - { - RemoteReader* reader = new RemoteReader(readerName, remoteToUpdate, pIfdStatus); - - connect(reader, &RemoteReader::fireCardInserted, this, &RemoteReaderManagerPlugIn::fireCardInserted); - connect(reader, &RemoteReader::fireCardRemoved, this, &RemoteReaderManagerPlugIn::fireCardRemoved); - connect(reader, &RemoteReader::fireCardRetryCounterChanged, this, &RemoteReaderManagerPlugIn::fireCardRetryCounterChanged); - connect(reader, &RemoteReader::fireReaderPropertiesUpdated, this, &RemoteReaderManagerPlugIn::fireReaderPropertiesUpdated); - - mReaderList.insert(readerName, reader); - mRemoteDispatchers.insert(remoteToUpdate, readerName); - qCDebug(card_remote) << "Add reader" << readerName; - Q_EMIT fireReaderAdded(readerName); - - // Also update card - reader->update(pIfdStatus); - } -} - - -void RemoteReaderManagerPlugIn::removeDispatcher(const QSharedPointer& pRemoteDispatcher) -{ - const auto& remoteReader = mRemoteDispatchers.values(pRemoteDispatcher); - for (const auto& readerName : remoteReader) - { - if (readerName.isEmpty()) - { - continue; - } - Q_EMIT fireReaderRemoved(readerName); - delete mReaderList.take(readerName); - } - disconnect(pRemoteDispatcher.data(), &RemoteDispatcher::fireReceived, this, &RemoteReaderManagerPlugIn::onRemoteMessage); - disconnect(pRemoteDispatcher.data(), &RemoteDispatcher::fireClosed, this, &RemoteReaderManagerPlugIn::onDispatcherClosed); - - mRemoteDispatchers.remove(pRemoteDispatcher); -} - - -void RemoteReaderManagerPlugIn::removeAllDispatchers() -{ - const auto& keys = mRemoteDispatchers.keys(); - for (const auto& dispatcher : keys) - { - QMetaObject::invokeMethod(dispatcher.data(), "close", Qt::QueuedConnection); - } -} - - -void RemoteReaderManagerPlugIn::connectToPairedReaders() -{ - if (mConnectionCheckInProgress) - { - return; - } - if (!mRemoteClient.isNull()) - { - mConnectionCheckInProgress = true; - connect(mRemoteClient.data(), &RemoteClient::fireRemoteDevicesInfo, this, &RemoteReaderManagerPlugIn::continueConnectToPairedReaders); - QMetaObject::invokeMethod(mRemoteClient.data(), "requestRemoteDevices", Qt::QueuedConnection); - } -} - - -void RemoteReaderManagerPlugIn::unexpectedMessage(const QSharedPointer& pMessage, const QSharedPointer& pRemoteDispatcher) -{ - qCWarning(card_remote) << "Received an unexpected message of type:" << pMessage->getType(); - - const QSharedPointer errorMessage(new IfdError(QString(), QStringLiteral("/al/common#unknownAPIFunction"))); - QMetaObject::invokeMethod(pRemoteDispatcher.data(), "send", Qt::QueuedConnection, Q_ARG(QSharedPointer, errorMessage)); -} - - -void RemoteReaderManagerPlugIn::continueConnectToPairedReaders(const QVector >& pRemoteDevices) -{ - disconnect(mRemoteClient.data(), &RemoteClient::fireRemoteDevicesInfo, this, &RemoteReaderManagerPlugIn::continueConnectToPairedReaders); - - const QList > remoteDispatchers = mRemoteDispatchers.keys(); - const QStringList connectionIds = map, QString>([](const QSharedPointer& d){ - return d->getId(); - }, - remoteDispatchers); - - const RemoteServiceSettings& remoteServiceSettings = AppSettings::getInstance().getRemoteServiceSettings(); - for (const QSharedPointer& remoteDevice : pRemoteDevices) - { - if (!remoteDevice->getRemoteDeviceDescriptor().isSupported()) - { - continue; - } - - const QString ifdId = remoteDevice->getRemoteDeviceDescriptor().getIfdId(); - - // If already connected: skip. - if (connectionIds.contains(ifdId)) - { - continue; - } - - const RemoteServiceSettings::RemoteInfo remoteInfo = remoteServiceSettings.getRemoteInfo(ifdId); - // If we find a remote info for this fingerprint (IfdId), then the remote device is paired. - if (remoteInfo.getFingerprint() == ifdId) - { - QMetaObject::invokeMethod(mRemoteClient.data(), "establishConnection", Qt::QueuedConnection, Q_ARG(QSharedPointer, remoteDevice), Q_ARG(QString, QString())); - } - } - mConnectionCheckInProgress = false; -} - - -void RemoteReaderManagerPlugIn::onRemoteMessage(const QSharedPointer& pMessage, const QSharedPointer& pRemoteDispatcher) -{ - const QVector clientMessageTypes({ - RemoteCardMessageType::IFDEstablishContext, - RemoteCardMessageType::IFDGetStatus, - RemoteCardMessageType::IFDConnect, - RemoteCardMessageType::IFDDisconnect, - RemoteCardMessageType::IFDTransmit, - RemoteCardMessageType::IFDEstablishPACEChannel, - RemoteCardMessageType::IFDModifyPIN - }); - - if (clientMessageTypes.contains(pMessage->getType())) - { - unexpectedMessage(pMessage, pRemoteDispatcher); - return; - } - - mRemoteToUpdate = pRemoteDispatcher; - receive(pMessage, pRemoteDispatcher); -} - - -void RemoteReaderManagerPlugIn::onDispatcherClosed(GlobalStatus::Code pCloseCode, const QSharedPointer& pRemoteDispatcher) -{ - qCDebug(card_remote) << "RemoteDispatcher was closed with:" << pCloseCode; - removeDispatcher(pRemoteDispatcher); -} - - -void RemoteReaderManagerPlugIn::checkRemoteDevices() -{ - if (mConnectToKnownReaders) - { - connectToPairedReaders(); - } -} - - -void RemoteReaderManagerPlugIn::onConnectToKnownReadersChanged() -{ - if (mConnectToKnownReaders) - { - connectToPairedReaders(); - } - else - { - removeAllDispatchers(); - } -} - - -RemoteReaderManagerPlugIn::RemoteReaderManagerPlugIn() - : ReaderManagerPlugIn(ReaderManagerPlugInType::REMOTE, true) - , MessageReceiver() - , mScanTimer() - , mRemoteClient() - , mReaderList() - , mConnectionCheckInProgress(false) -{ - mScanTimer.setInterval(1000); - connect(&mScanTimer, &QTimer::timeout, this, &RemoteReaderManagerPlugIn::checkRemoteDevices); -} - - -RemoteReaderManagerPlugIn::~RemoteReaderManagerPlugIn() -{ - mScanTimer.stop(); - removeAllDispatchers(); -} - - -void RemoteReaderManagerPlugIn::setRemoteClient(const QSharedPointer& pRemoteClient) -{ - mRemoteClient = pRemoteClient; - if (!mRemoteClient.isNull()) - { - connect(mRemoteClient.data(), &RemoteClient::fireNewRemoteDispatcher, this, &RemoteReaderManagerPlugIn::addRemoteDispatcher); - } -} - - -QList RemoteReaderManagerPlugIn::getReaders() const -{ - return mReaderList.values(); -} - - -void RemoteReaderManagerPlugIn::addRemoteDispatcher(const QSharedPointer& pRemoteDispatcher) -{ - Q_ASSERT(pRemoteDispatcher); - - mRemoteDispatchers.insert(pRemoteDispatcher, QString()); - - connect(pRemoteDispatcher.data(), &RemoteDispatcher::fireReceived, this, &RemoteReaderManagerPlugIn::onRemoteMessage); - connect(pRemoteDispatcher.data(), &RemoteDispatcher::fireClosed, this, &RemoteReaderManagerPlugIn::onDispatcherClosed); - - RemoteServiceSettings& settings = Env::getSingleton()->getRemoteServiceSettings(); - - const QSharedPointer establishContext(new IfdEstablishContext(IfdVersion::latest(), settings.getServerName())); - QMetaObject::invokeMethod(pRemoteDispatcher.data(), "send", Qt::QueuedConnection, Q_ARG(QSharedPointer, establishContext)); -} - - -void RemoteReaderManagerPlugIn::process(const QSharedPointer& pMessage) -{ - if (pMessage->resultHasError()) - { - qCDebug(card_remote) << "EstablishContext failed:" << pMessage->getResultMinor(); - return; - } - - bool initialPairing = false; - - //update IFD name - QSharedPointer currentDispatcher = mRemoteToUpdate.toStrongRef(); - if (!currentDispatcher) - { - return; - } - - RemoteServiceSettings& settings = AppSettings::getInstance().getRemoteServiceSettings(); - auto info = settings.getRemoteInfo(currentDispatcher->getId()); - if (info.getName().isEmpty()) - { - initialPairing = true; - } - info.setName(pMessage->getIfdName()); - settings.updateRemoteInfo(info); - - if (initialPairing) - { - QMetaObject::invokeMethod(currentDispatcher.data(), "close", Qt::QueuedConnection); - } - else - { - const QSharedPointer getIfdStatus(new GetIfdStatus()); - QMetaObject::invokeMethod(mRemoteToUpdate.data(), "send", Qt::QueuedConnection, Q_ARG(QSharedPointer, getIfdStatus)); - } -} - - -void RemoteReaderManagerPlugIn::process(const QSharedPointer& pMessage) -{ - updateReader(*pMessage); -} - - -void RemoteReaderManagerPlugIn::unprocessed(const QSharedPointer& pMessage) -{ - Q_UNUSED(pMessage); -} - - -void RemoteReaderManagerPlugIn::startScan() -{ - if (mRemoteClient.isNull()) - { - qCWarning(card_remote) << "Cannot start remote device discovery: no remote client found"; - return; - } - - connect(mRemoteClient.data(), &RemoteClient::fireDeviceAppeared, this, &RemoteReaderManagerPlugIn::checkRemoteDevices); - - mScanTimer.start(); - QMetaObject::invokeMethod(mRemoteClient.data(), "startDetection", Qt::QueuedConnection); - mScanInProgress = true; -} - - -void RemoteReaderManagerPlugIn::stopScan() -{ - if (mRemoteClient.isNull()) - { - qCWarning(card_remote) << "Cannot stop remote device discovery: no remote client found"; - return; - } - - disconnect(mRemoteClient.data(), &RemoteClient::fireDeviceAppeared, this, &RemoteReaderManagerPlugIn::checkRemoteDevices); - mScanTimer.stop(); - QMetaObject::invokeMethod(mRemoteClient.data(), "stopDetection", Qt::QueuedConnection); - mScanInProgress = false; -} diff --git a/src/card/remote/RemoteReaderManagerPlugIn.h b/src/card/remote/RemoteReaderManagerPlugIn.h deleted file mode 100644 index e4f74ae..0000000 --- a/src/card/remote/RemoteReaderManagerPlugIn.h +++ /dev/null @@ -1,75 +0,0 @@ -/*! - * \brief Implementation of \ref ReaderManagerPlugIn for remote reader. - * - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "Env.h" -#include "messages/MessageReceiver.h" -#include "Reader.h" -#include "ReaderManagerPlugIn.h" - -#include -#include -#include -#include - -namespace governikus -{ -class IfdStatus; -class RemoteClient; -class ReaderDescription; -class RemoteDeviceListEntry; - -class RemoteReaderManagerPlugIn - : public ReaderManagerPlugIn - , public MessageReceiver -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID "governikus.ReaderManagerPlugIn" FILE "metadata.json") - Q_INTERFACES(governikus::ReaderManagerPlugIn) - - private: - QTimer mScanTimer; - QSharedPointer mRemoteClient; - QWeakPointer mRemoteToUpdate; - QMultiMap, QString> mRemoteDispatchers; - QMap mReaderList; - bool mConnectionCheckInProgress; - - void updateReader(const IfdStatus& pIfdStatus); - void removeDispatcher(const QSharedPointer& pRemoteDispatcher); - void removeAllDispatchers(); - void connectToPairedReaders(); - void unexpectedMessage(const QSharedPointer& pMessage, const QSharedPointer& pRemoteDispatcher = QSharedPointer()); - - private Q_SLOTS: - void onRemoteMessage(const QSharedPointer& pMessage, const QSharedPointer& pRemoteDispatcher); - void onDispatcherClosed(GlobalStatus::Code pCloseCode, const QSharedPointer& pRemoteDispatcher); - void addRemoteDispatcher(const QSharedPointer& pRemoteDispatcher); - void checkRemoteDevices(); - void continueConnectToPairedReaders(const QVector >& pRemoteDevices); - - protected: - virtual void onConnectToKnownReadersChanged() override; - - public: - RemoteReaderManagerPlugIn(); - virtual ~RemoteReaderManagerPlugIn() override; - - virtual void setRemoteClient(const QSharedPointer& pRemoteClient) override; - virtual QList getReaders() const override; - - virtual void process(const QSharedPointer& pMessage) override; - virtual void process(const QSharedPointer& pMessage) override; - - virtual void unprocessed(const QSharedPointer& pMessage) override; - - virtual void startScan() override; - virtual void stopScan() override; - -}; - -} /* namespace governikus */ diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt deleted file mode 100644 index 74e61d3..0000000 --- a/src/cli/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -ADD_PLATFORM_LIBRARY(AusweisAppCli) - -TARGET_LINK_LIBRARIES(AusweisAppCli Qt5::Core AusweisAppCore AusweisAppGlobal AusweisAppActivationWebservice) -TARGET_COMPILE_DEFINITIONS(AusweisAppCli PRIVATE QT_STATICPLUGIN) diff --git a/src/cli/ConsoleReader.h b/src/cli/ConsoleReader.h deleted file mode 100644 index c8a4016..0000000 --- a/src/cli/ConsoleReader.h +++ /dev/null @@ -1,74 +0,0 @@ -/*! - * \brief Helper to read stdin in non-blocking mode. - * - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include - -#if defined(Q_OS_WIN) -#include - -#include -#include -#else -#include -#endif - - -namespace governikus -{ - -#if defined(Q_OS_WIN) - -class ConsoleInputThread - : public QThread -{ - Q_OBJECT - - public: - void run() Q_DECL_OVERRIDE; - virtual ~ConsoleInputThread(); - - Q_SIGNALS: - void fireText(const QString& pData); -}; - -#endif - - -class ConsoleReader - : public QObject -{ - Q_OBJECT - - private: -#if defined(Q_OS_WIN) - QScopedPointer mConsoleInputThread; -#else - QScopedPointer mNotifier; - - bool mInputOpen; - - private Q_SLOTS: - void onData(); -#endif - - public: - ConsoleReader(QObject* pParent = nullptr); - void init(); - void shutdown(); - bool isInputOpen() const; - - QString readText(); - - Q_SIGNALS: - void fireShutdown(); - void fireText(const QString& pData); -}; - - -} /* namespace governikus */ diff --git a/src/cli/UIPlugInCli.cpp b/src/cli/UIPlugInCli.cpp deleted file mode 100644 index 2599ab5..0000000 --- a/src/cli/UIPlugInCli.cpp +++ /dev/null @@ -1,234 +0,0 @@ -/*! - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "UIPlugInCli.h" - -#include "states/StateEstablishPacePin.h" - -#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) -#include "Env.h" -#include "HttpServer.h" -#endif - -#include -#include - -Q_DECLARE_LOGGING_CATEGORY(stdinput) -Q_DECLARE_LOGGING_CATEGORY(cli) - -using namespace governikus; - -UIPlugInCli::UIPlugInCli() - : mReader() - , mAvailableCommands() -{ - addCommand(QStringLiteral("cancel"), &UIPlugInCli::handleCancelWorkflow); - addCommand(QStringLiteral("changepin"), &UIPlugInCli::handleChangePin); - addCommand(QStringLiteral("enter-pin"), &UIPlugInCli::handleEnterPin); - addCommand(QStringLiteral("help"), &UIPlugInCli::handleHelp); - addCommand(QStringLiteral("ping"), &UIPlugInCli::handlePing); - addCommand(QStringLiteral("port"), &UIPlugInCli::handlePort); - addCommand(QStringLiteral("quit"), &UIPlugInCli::handleQuit); - - connect(&mReader, &ConsoleReader::fireText, this, &UIPlugInCli::doInput); - mReader.init(); -} - - -UIPlugInCli::~UIPlugInCli() -{ -} - - -void UIPlugInCli::onApplicationStarted() -{ - qCInfo(cli) << "ready"; -} - - -void UIPlugInCli::doShutdown() -{ - mReader.shutdown(); -} - - -void UIPlugInCli::onWorkflowStarted(QSharedPointer pContext) -{ - mContext = pContext; - if (!mContext.isNull()) - { - connect(mContext.data(), &WorkflowContext::fireStateChanged, - this, &UIPlugInCli::onStateChanged); - mContext->setReaderPlugInTypes({ReaderManagerPlugInType::PCSC}); - } -} - - -void UIPlugInCli::onWorkflowFinished(QSharedPointer pContext) -{ - Q_UNUSED(pContext); - if (!mContext.isNull()) - { - mContext->disconnect(this); - mContext.reset(); - } -} - - -void UIPlugInCli::onStateChanged(const QString& pState) -{ - Q_UNUSED(pState); - - bool userInteractionRequired = false; - - if (AbstractState::isState(pState)) - { - userInteractionRequired = true; - qCInfo(cli) << "enter-pin"; - } - - mContext->setStateApproved(!userInteractionRequired); -} - - -void UIPlugInCli::doInput(const QString& pData) -{ - qCInfo(stdinput) << pData; - - const QStringList chunks = pData.split(QLatin1Char(' ')); - auto func = mAvailableCommands.value(chunks.at(0).toLower()); - if (func) - { - mCurrentCommandArgs = chunks.mid(1); - func(); - } - else - { - qCWarning(cli) << "Unknown command:" << pData; - } -} - - -void UIPlugInCli::handleChangePin() -{ - mOldPin.clear(); - mNewPin.clear(); - - qCDebug(cli) << "Change PIN requested"; - qCInfo(cli) << "Please enter old PIN"; - - disconnect(&mReader, &ConsoleReader::fireText, this, &UIPlugInCli::doInput); - connect(&mReader, &ConsoleReader::fireText, this, &UIPlugInCli::handleOldPinEntered); -} - - -void UIPlugInCli::handleOldPinEntered(const QString& pLine) -{ - const QRegularExpression regexOldPin(QStringLiteral("^[0-9]{5,6}$")); - if (regexOldPin.match(pLine).hasMatch() && mReader.isInputOpen()) - { - mOldPin = pLine; - qCInfo(cli) << "Please enter new PIN"; - disconnect(&mReader, &ConsoleReader::fireText, this, &UIPlugInCli::handleOldPinEntered); - connect(&mReader, &ConsoleReader::fireText, this, &UIPlugInCli::handleNewPinEntered); - } - else - { - qCInfo(cli) << "Please enter old PIN"; - } -} - - -void UIPlugInCli::handleNewPinEntered(const QString& pLine) -{ - const QRegularExpression regexNewPin(QStringLiteral("^[0-9]{6}$")); - if (regexNewPin.match(pLine).hasMatch() && mReader.isInputOpen()) - { - mNewPin = pLine; - qCInfo(cli) << "Please enter new PIN again"; - disconnect(&mReader, &ConsoleReader::fireText, this, &UIPlugInCli::handleNewPinEntered); - connect(&mReader, &ConsoleReader::fireText, this, &UIPlugInCli::handleNewPinEnteredAgain); - } - else - { - qCInfo(cli) << "Please enter new PIN"; - } -} - - -void UIPlugInCli::handleNewPinEnteredAgain(const QString& pLine) -{ - if (mNewPin != pLine) - { - qCInfo(cli) << "PINs were not equal"; - qCInfo(cli) << "Please enter new PIN"; - - disconnect(&mReader, &ConsoleReader::fireText, this, &UIPlugInCli::handleNewPinEnteredAgain); - connect(&mReader, &ConsoleReader::fireText, this, &UIPlugInCli::handleNewPinEntered); - } - else if (mReader.isInputOpen()) - { - qCDebug(cli) << "Start"; - Q_EMIT fireChangePinRequest(); - connect(&mReader, &ConsoleReader::fireText, this, &UIPlugInCli::doInput); - } -} - - -void UIPlugInCli::handleCancelWorkflow() -{ - if (mContext.isNull()) - { - qCInfo(cli) << "error"; - } - else - { - Q_EMIT mContext->fireCancelWorkflow(); - qCInfo(cli) << "ok"; - } -} - - -void UIPlugInCli::handleEnterPin() -{ - if (mCurrentCommandArgs.size() != 1 || mContext.isNull()) - { - qCInfo(cli) << "error"; - } - else - { - mContext->setPin(mCurrentCommandArgs.at(0)); - mContext->setStateApproved(true); - qCInfo(cli) << "ok"; - } -} - - -void UIPlugInCli::handleHelp() -{ - qCInfo(cli) << "Available commands:" << mAvailableCommands.keys().join(QStringLiteral(" | ")); -} - - -void UIPlugInCli::handlePing() -{ - qCInfo(cli) << "Pong!"; -} - - -void UIPlugInCli::handlePort() -{ -#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) - qCInfo(cli) << "Port:" << Env::getShared()->getServerPort(); -#else - qCInfo(cli) << "Port is undefined"; -#endif -} - - -void UIPlugInCli::handleQuit() -{ - qCInfo(cli) << "Shutdown application..."; - Q_EMIT fireQuitApplicationRequest(); -} diff --git a/src/cli/UIPlugInCli.h b/src/cli/UIPlugInCli.h deleted file mode 100644 index be48b34..0000000 --- a/src/cli/UIPlugInCli.h +++ /dev/null @@ -1,72 +0,0 @@ -/*! - * \brief UIPlugIn implementation of CLI. - * - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "context/WorkflowContext.h" -#include "ConsoleReader.h" -#include "view/UIPlugIn.h" - -#include -#include - -#include - -namespace governikus -{ - -class UIPlugInCli - : public UIPlugIn -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID "governikus.UIPlugIn" FILE "metadata.json") - Q_INTERFACES(governikus::UIPlugIn) - - using MemberFunc = void (UIPlugInCli::*)(); - - private: - QString mOldPin; - QString mNewPin; - ConsoleReader mReader; - QMap > mAvailableCommands; - QSharedPointer mContext; - QStringList mCurrentCommandArgs; - - void addCommand(const QString& pCmd, MemberFunc pFunc) - { - mAvailableCommands.insert(pCmd, std::bind(pFunc, this)); - } - - - void handleCancelWorkflow(); - void handleChangePin(); - void handleEnterPin(); - void handleHelp(); - void handlePing(); - void handlePort(); - void handleQuit(); - - public: - UIPlugInCli(); - virtual ~UIPlugInCli() override; - - public Q_SLOTS: - virtual void onApplicationStarted() override; - - virtual void doShutdown() override; - - private Q_SLOTS: - void doInput(const QString& pData); - virtual void onWorkflowStarted(QSharedPointer pContext) override; - virtual void onWorkflowFinished(QSharedPointer pContext) override; - void onStateChanged(const QString& pState); - - void handleOldPinEntered(const QString& pLine); - void handleNewPinEntered(const QString& pLine); - void handleNewPinEnteredAgain(const QString& pLine); -}; - -} /* namespace governikus */ diff --git a/src/config.h.in b/src/config.h.in index 12bc281..c856c91 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -1,5 +1,7 @@ /* DO NOT TOUCH THIS MANUALLY */ +#cmakedefine ANDROID_BUILD_AAR + #define PRODUCT "@PRODUCT@" #define VENDOR "@VENDOR@" #define VENDOR_DOMAIN "@VENDOR_DOMAIN@" diff --git a/src/configuration/CMakeLists.txt b/src/configuration/CMakeLists.txt index d7f0b81..16c574e 100644 --- a/src/configuration/CMakeLists.txt +++ b/src/configuration/CMakeLists.txt @@ -1,3 +1,11 @@ +##################################################################### +# The module configuration is responsible to maintain the setup of +# all dynamic content that can be updated by a configuration file +# on our server. +# +# For example: Configuration of provider and reader driver. +##################################################################### + ADD_PLATFORM_LIBRARY(AusweisAppConfiguration) TARGET_LINK_LIBRARIES(AusweisAppConfiguration Qt5::Core AusweisAppGlobal AusweisAppFileProvider) diff --git a/src/configuration/CallCost.cpp b/src/configuration/CallCost.cpp index 11e6c7d..ea19e18 100644 --- a/src/configuration/CallCost.cpp +++ b/src/configuration/CallCost.cpp @@ -18,7 +18,7 @@ inline double getValue(const QJsonValue& pJson, const char* pSection, const char } -} +} // namespace CallCost::CallCost(int pFreeSeconds, double pLandlineCentsPerMinute, double pLandlineCentsPerCall, double pMobileCentsPerMinute, double pMobileCentsPerCall) : mFreeSeconds(pFreeSeconds) diff --git a/src/configuration/CallCost.h b/src/configuration/CallCost.h index 4f6c242..584846e 100644 --- a/src/configuration/CallCost.h +++ b/src/configuration/CallCost.h @@ -13,7 +13,6 @@ namespace governikus { - class CallCost { friend bool operator==(const CallCost& pLeft, const CallCost& pRight); @@ -49,6 +48,6 @@ inline bool operator==(const CallCost& pLeft, const CallCost& pRight) } -} /* namespace governikus */ +} // namespace governikus QDebug operator<<(QDebug pDbg, const governikus::CallCost& pCallCost); diff --git a/src/configuration/LanguageString.h b/src/configuration/LanguageString.h index 260b728..d2301af 100644 --- a/src/configuration/LanguageString.h +++ b/src/configuration/LanguageString.h @@ -43,4 +43,4 @@ inline bool operator==(const LanguageString& pLeft, const LanguageString& pRight } -} /* namespace governikus */ +} // namespace governikus diff --git a/src/configuration/ProviderConfiguration.cpp b/src/configuration/ProviderConfiguration.cpp index 7b3493e..6b297c2 100644 --- a/src/configuration/ProviderConfiguration.cpp +++ b/src/configuration/ProviderConfiguration.cpp @@ -4,7 +4,6 @@ #include "ProviderConfiguration.h" -#include "Env.h" #include "FileProvider.h" #include "ProviderConfigurationParser.h" #include "SingletonHelper.h" diff --git a/src/configuration/ProviderConfiguration.h b/src/configuration/ProviderConfiguration.h index a55ccfd..1e7550d 100644 --- a/src/configuration/ProviderConfiguration.h +++ b/src/configuration/ProviderConfiguration.h @@ -7,6 +7,7 @@ #pragma once #include "CallCost.h" +#include "Env.h" #include "ProviderConfigurationInfo.h" #include "UpdatableFile.h" @@ -19,11 +20,11 @@ namespace governikus { - class ProviderConfiguration : public QObject { Q_OBJECT + friend class Env; private: const QSharedPointer mUpdatableFile; @@ -38,10 +39,9 @@ class ProviderConfiguration protected: ProviderConfiguration(); virtual ~ProviderConfiguration() = default; - - public: static ProviderConfiguration& getInstance(); + public: void update(); const QVector& getProviderConfigurationInfos() const; const CallCost getCallCost(const ProviderConfigurationInfo& pProvider) const; @@ -51,4 +51,4 @@ class ProviderConfiguration }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/configuration/ProviderConfigurationInfo.cpp b/src/configuration/ProviderConfigurationInfo.cpp index e2a70a9..5307800 100644 --- a/src/configuration/ProviderConfigurationInfo.cpp +++ b/src/configuration/ProviderConfigurationInfo.cpp @@ -4,7 +4,6 @@ #include "ProviderConfigurationInfo.h" -#include "Env.h" #include "FileProvider.h" #include diff --git a/src/configuration/ProviderConfigurationInfo.h b/src/configuration/ProviderConfigurationInfo.h index e7f3583..fe1dbc3 100644 --- a/src/configuration/ProviderConfigurationInfo.h +++ b/src/configuration/ProviderConfigurationInfo.h @@ -19,7 +19,6 @@ namespace governikus { - class ProviderConfigurationInfo { private: @@ -144,4 +143,4 @@ class ProviderConfigurationInfo }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/configuration/ProviderConfigurationParser.cpp b/src/configuration/ProviderConfigurationParser.cpp index e66c8ac..717f8ef 100644 --- a/src/configuration/ProviderConfigurationParser.cpp +++ b/src/configuration/ProviderConfigurationParser.cpp @@ -45,7 +45,7 @@ inline QLatin1String getCurrentOS() } -} +} // namespace bool ProviderConfigurationParser::isExcludedPlatform(const QJsonArray& pExcludedArray, QLatin1String pCurrentOS) { diff --git a/src/configuration/ProviderConfigurationParser.h b/src/configuration/ProviderConfigurationParser.h index 8d9b4fe..a4d866a 100644 --- a/src/configuration/ProviderConfigurationParser.h +++ b/src/configuration/ProviderConfigurationParser.h @@ -13,7 +13,6 @@ #include #include -class test_ProviderConfigurationParser; namespace governikus { @@ -33,4 +32,4 @@ class ProviderConfigurationParser }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/configuration/ReaderConfiguration.cpp b/src/configuration/ReaderConfiguration.cpp index 5b9db43..c4f6c0a 100644 --- a/src/configuration/ReaderConfiguration.cpp +++ b/src/configuration/ReaderConfiguration.cpp @@ -4,7 +4,6 @@ #include "ReaderConfiguration.h" -#include "Env.h" #include "FileDestination.h" #include "FileProvider.h" #include "FuncUtils.h" @@ -27,7 +26,7 @@ defineSingleton(ReaderConfiguration) bool ReaderConfiguration::parseReaderConfiguration() { const QString& path = mUpdatableFile->lookupPath(); - if (!QFile::exists(path)) + if (path.isEmpty() || !QFile::exists(path)) { qCCritical(configuration) << "ReaderConfiguration file not found"; return false; diff --git a/src/configuration/ReaderConfiguration.h b/src/configuration/ReaderConfiguration.h index d129a8c..c320f24 100644 --- a/src/configuration/ReaderConfiguration.h +++ b/src/configuration/ReaderConfiguration.h @@ -7,6 +7,7 @@ #pragma once +#include "Env.h" #include "ReaderConfigurationInfo.h" #include "UpdatableFile.h" #include "UsbId.h" @@ -25,6 +26,7 @@ class ReaderConfiguration : public QObject { Q_OBJECT + friend class Env; private: friend class MockReaderConfiguration; @@ -40,9 +42,9 @@ class ReaderConfiguration protected: ReaderConfiguration(); virtual ~ReaderConfiguration() = default; + static ReaderConfiguration& getInstance(); public: - static ReaderConfiguration& getInstance(); static QString getNoReaderFoundIconPath(); static QString getMultipleReaderIconPath(); @@ -57,4 +59,4 @@ class ReaderConfiguration void fireUpdated(); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/configuration/ReaderConfigurationInfo.cpp b/src/configuration/ReaderConfigurationInfo.cpp index 5b21aa5..ec03012 100644 --- a/src/configuration/ReaderConfigurationInfo.cpp +++ b/src/configuration/ReaderConfigurationInfo.cpp @@ -4,7 +4,6 @@ #include "ReaderConfigurationInfo.h" -#include "Env.h" #include "FileProvider.h" using namespace governikus; @@ -70,7 +69,7 @@ const QString& ReaderConfigurationInfo::getUrl() const if (QCoreApplication::applicationName() == QLatin1String("Test_configuration_ReaderConfiguration")) { // Make the reader available on all platforms - static const QString url = QLatin1String("https://www.governikus.de/"); + static const QString url = QStringLiteral("https://www.governikus.de/"); return url; } #endif diff --git a/src/configuration/ReaderConfigurationInfo.h b/src/configuration/ReaderConfigurationInfo.h index 8919d24..5715f4c 100644 --- a/src/configuration/ReaderConfigurationInfo.h +++ b/src/configuration/ReaderConfigurationInfo.h @@ -45,7 +45,6 @@ class ReaderConfigurationInfo , mIcon(pIcon) , mIconWithNPA(pIconWithNPA) { - } @@ -94,4 +93,4 @@ inline uint qHash(const ReaderConfigurationInfo& info) } -} /* namespace governikus */ +} // namespace governikus diff --git a/src/configuration/ReaderConfigurationParser.h b/src/configuration/ReaderConfigurationParser.h index 0eb445a..211a328 100644 --- a/src/configuration/ReaderConfigurationParser.h +++ b/src/configuration/ReaderConfigurationParser.h @@ -55,4 +55,4 @@ class ReaderConfigurationParser }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index bb37cc2..4dfb7aa 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,6 +1,12 @@ +##################################################################### +# The module core is responsible to start up the application and main +# event loop. It will initialize any necessary module and control +# any workflow and communication between the modules. +##################################################################### + ADD_PLATFORM_LIBRARY(AusweisAppCore) -TARGET_LINK_LIBRARIES(AusweisAppCore Qt5::Network Qt5::Xml AusweisAppCard AusweisAppGlobal AusweisAppActivation AusweisAppSettings AusweisAppNetwork AusweisAppRemoteDevice AusweisAppServices) +TARGET_LINK_LIBRARIES(AusweisAppCore Qt5::Network AusweisAppCard AusweisAppGlobal AusweisAppUi AusweisAppActivation AusweisAppSettings AusweisAppNetwork AusweisAppRemoteDevice AusweisAppServices AusweisAppWhitelistClient) IF(WIN32) TARGET_LINK_LIBRARIES(AusweisAppCore ${WIN_DEFAULT_LIBS}) diff --git a/src/core/CertificateChecker.cpp b/src/core/CertificateChecker.cpp index dcf9f24..1e0df6c 100644 --- a/src/core/CertificateChecker.cpp +++ b/src/core/CertificateChecker.cpp @@ -5,7 +5,6 @@ #include "CertificateChecker.h" #include "AppSettings.h" -#include "paos/retrieve/DidAuthenticateEac1.h" #include "TlsChecker.h" #include @@ -31,15 +30,15 @@ CertificateChecker::CertificateStatus CertificateChecker::checkAndSaveCertificat if (pEAC1 && pDvCvc) { - if (auto certificateDescription = pEAC1->getCertificateDescription()) + if (const auto& certificateDescription = pEAC1->getCertificateDescription()) { - const QSet certHashes = certificateDescription->getCommCertificates(); + const QSet& certHashes = certificateDescription->getCommCertificates(); QCryptographicHash::Algorithm hashAlgo = pDvCvc->getBody().getHashAlgorithm(); if (!TlsChecker::checkCertificate(pCertificate, hashAlgo, certHashes)) { - auto hashError = QStringLiteral("hash of certificate not in certificate description"); + const auto& hashError = QStringLiteral("hash of certificate not in certificate description"); - if (AppSettings::getInstance().getGeneralSettings().isDeveloperMode()) + if (Env::getSingleton()->getGeneralSettings().isDeveloperMode()) { qCCritical(developermode) << hashError; } diff --git a/src/core/CertificateChecker.h b/src/core/CertificateChecker.h index 2836c46..5107353 100644 --- a/src/core/CertificateChecker.h +++ b/src/core/CertificateChecker.h @@ -7,6 +7,7 @@ #pragma once #include "asn1/CVCertificate.h" +#include "paos/retrieve/DidAuthenticateEac1.h" #include #include @@ -16,7 +17,6 @@ namespace governikus { -class DIDAuthenticateEAC1; /*! * \brief Utility class for checking various constraints on certificates diff --git a/src/core/DiagnosisAntivirusDetection.cpp b/src/core/DiagnosisAntivirusDetection.cpp new file mode 100644 index 0000000..19b2507 --- /dev/null +++ b/src/core/DiagnosisAntivirusDetection.cpp @@ -0,0 +1,139 @@ +/* + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "DiagnosisAntivirusDetection.h" + +#include +#include + +using namespace governikus; + +AntivirInfo::AntivirInfo(const QString& pDisplayName, const QString& pLastUpdate, const QString& pExePath) + : mDisplayName(pDisplayName), + mLastUpdate(pLastUpdate), + mExePath(pExePath) +{ +} + + +DiagnosisAntivirusDetection::DiagnosisAntivirusDetection() + : QObject() +#if defined(Q_OS_WIN) + , mProcess(QSharedPointer::create()) +#endif + , mAntivirInfos() +{ +} + + +void DiagnosisAntivirusDetection::startInformationProcess() +{ +#if defined(Q_OS_WIN) + QString powershellCommand = QStandardPaths::findExecutable(QStringLiteral("powershell.exe")); + if (powershellCommand.isEmpty()) + { + powershellCommand = QStringLiteral("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"); + if (!QFile::exists(powershellCommand)) + { + onError(QProcess::ProcessError::FailedToStart); + return; + } + } + + QStringList parameters; + parameters << QStringLiteral("-NoProfile"); + parameters << QStringLiteral("-Command"); + parameters << QStringLiteral("Get-WmiObject -Namespace root\\SecurityCenter2 -Class AntiVirusProduct"); + + connect(mProcess.data(), QOverload::of(&QProcess::finished), this, &DiagnosisAntivirusDetection::onFinished); + connect(mProcess.data(), &QProcess::errorOccurred, this, &DiagnosisAntivirusDetection::onError); + + mProcess->start(powershellCommand, parameters); + mProcess->closeWriteChannel(); +#endif +} + + +#if defined(Q_OS_WIN) +void DiagnosisAntivirusDetection::onFinished(int exitCode, QProcess::ExitStatus exitStatus) +{ + if (exitCode != 0) + { + qDebug() << "Error while getting antivirus informations, error code:" << exitCode; + Q_EMIT fireDetectionFailed(); + return; + } + + if (exitStatus == QProcess::CrashExit) + { + return; + } + + QString output = QString::fromUtf8(mProcess->readAllStandardOutput()); + + QString displayName; + QString lastUpdate; + QString exePath; + + const auto& lines = output.split(QStringLiteral("\r\n")); + for (const auto& line : lines) + { + const QString& trimmedLine = line.trimmed(); + if (trimmedLine.isEmpty()) + { + if (!displayName.isEmpty() && !exePath.isEmpty()) + { + auto newInfo = QSharedPointer::create(displayName, lastUpdate, exePath); + mAntivirInfos << newInfo; + } + displayName = QString(); + lastUpdate = QString(); + exePath = QString(); + continue; + } + + const QStringList& lineparts = trimmedLine.split(QStringLiteral(" : ")); + if (lineparts.length() != 2) + { + continue; + } + if (lineparts[0].startsWith(QLatin1String("displayName"))) + { + displayName = lineparts[1]; + } + else if (lineparts[0].startsWith(QLatin1String("timestamp"))) + { + lastUpdate = lineparts[1]; + } + else if (lineparts[0].startsWith(QLatin1String("pathToSignedProductExe"))) + { + exePath = lineparts[1]; + } + } + + Q_EMIT fireAntivirusInformationChanged(); +} + + +#endif + + +#if defined(Q_OS_WIN) +void DiagnosisAntivirusDetection::onError(QProcess::ProcessError pError) +{ + qDebug() << "Error calling process:" << pError; + Q_EMIT fireDetectionFailed(); +} + + +#endif + + +const QVector >& DiagnosisAntivirusDetection::getAntivirusInformations() const +{ + return mAntivirInfos; +} + + +#include "moc_DiagnosisAntivirusDetection.cpp" diff --git a/src/core/DiagnosisAntivirusDetection.h b/src/core/DiagnosisAntivirusDetection.h new file mode 100644 index 0000000..7fb1a96 --- /dev/null +++ b/src/core/DiagnosisAntivirusDetection.h @@ -0,0 +1,73 @@ +/*! + * \brief Class for retrieving informations about installed antivirus software on windows. + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include +#include + +namespace governikus +{ + +class AntivirInfo +{ + private: + QString mDisplayName; + QString mLastUpdate; + QString mExePath; + + public: + AntivirInfo(const QString& pDisplayName, const QString& pLastUpdate, const QString& pExePath); + + const QString& getDisplayName() const + { + return mDisplayName; + } + + + const QString& getLastUpdate() const + { + return mLastUpdate; + } + + + const QString& getExePath() const + { + return mExePath; + } + + +}; + +class DiagnosisAntivirusDetection + : public QObject +{ + Q_OBJECT + + private: +#if defined(Q_OS_WIN) + QSharedPointer mProcess; +#endif + QVector > mAntivirInfos; + + private Q_SLOTS: +#if defined(Q_OS_WIN) + void onFinished(int exitCode, QProcess::ExitStatus exitStatus); + void onError(QProcess::ProcessError pError); +#endif + + Q_SIGNALS: + void fireAntivirusInformationChanged(); + void fireDetectionFailed(); + + public: + DiagnosisAntivirusDetection(); + void startInformationProcess(); + const QVector >& getAntivirusInformations() const; +}; + +} // namespace governikus diff --git a/src/core/DiagnosisConnectionTest.cpp b/src/core/DiagnosisConnectionTest.cpp new file mode 100644 index 0000000..7cf16dc --- /dev/null +++ b/src/core/DiagnosisConnectionTest.cpp @@ -0,0 +1,198 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "DiagnosisConnectionTest.h" + +#include "SecureStorage.h" + +using namespace governikus; + +DiagnosisConnectionTest::DiagnosisConnectionTest() + : QObject() + , mIsProxySet(false) + , mProxyHostName() + , mProxyPort() + , mProxyType() + , mProxyCapabilities() + , mConnectionTestWithProxySuccessful(false) + , mConnectionTestWithoutProxySuccessful(false) + , mPingTestOnProxySuccessful(false) + , mProxyPingDone(false) + , mConnectionTestWithProxyDone(false) + , mConnectionTestWithoutProxyDone(false) + , mPingSocketToProxy() + , mTcpSocketWithProxy() + , mTcpSocketWithoutProxy() +{ +} + + +void DiagnosisConnectionTest::onProxyPingTestDone() +{ + mPingTestOnProxySuccessful = true; + mPingSocketToProxy.disconnectFromHost(); + mProxyPingDone = true; + checkIfAllProcessesDone(); +} + + +void DiagnosisConnectionTest::onProxyPingTestError(QAbstractSocket::SocketError pSocketError) +{ + qDebug() << "Error occurred while trying to ping proxy:" << pSocketError; + mPingTestOnProxySuccessful = false; + mProxyPingDone = true; + checkIfAllProcessesDone(); +} + + +void DiagnosisConnectionTest::onSocketConnectionTestWithProxyDone() +{ + mConnectionTestWithProxySuccessful = true; + mTcpSocketWithProxy.disconnectFromHost(); + mConnectionTestWithProxyDone = true; + checkIfAllProcessesDone(); +} + + +void DiagnosisConnectionTest::onSocketConnectionTestWithProxyError(QAbstractSocket::SocketError pSocketError) +{ + qDebug() << "Could not connect to test server with proxy:" << pSocketError; + mConnectionTestWithProxySuccessful = false; + mConnectionTestWithProxyDone = true; + checkIfAllProcessesDone(); +} + + +void DiagnosisConnectionTest::onSocketConnectionTestWithoutProxyDone() +{ + mConnectionTestWithoutProxySuccessful = true; + mTcpSocketWithProxy.disconnectFromHost(); + mConnectionTestWithoutProxyDone = true; + checkIfAllProcessesDone(); +} + + +void DiagnosisConnectionTest::onSocketConnectionTestWithoutProxyError(QAbstractSocket::SocketError pSocketError) +{ + qDebug() << "Could not connect to test server without proxy:" << pSocketError; + mConnectionTestWithoutProxySuccessful = false; + mConnectionTestWithoutProxyDone = true; + checkIfAllProcessesDone(); +} + + +void DiagnosisConnectionTest::checkIfAllProcessesDone() +{ + if (mProxyPingDone && mConnectionTestWithProxyDone && mConnectionTestWithoutProxyDone) + { + Q_EMIT fireConnectionTestDone(); + } +} + + +QString DiagnosisConnectionTest::getProxyTypeAsQString(QNetworkProxy::ProxyType pType) +{ + switch (pType) + { + case QNetworkProxy::NoProxy: + return QStringLiteral("NoProxy"); + + case QNetworkProxy::DefaultProxy: + return QStringLiteral("DefaultProxy"); + + case QNetworkProxy::Socks5Proxy: + return QStringLiteral("Socks5Proxy"); + + case QNetworkProxy::HttpProxy: + return QStringLiteral("HttpProxy"); + + case QNetworkProxy::HttpCachingProxy: + return QStringLiteral("HttpCachingProxy"); + + case QNetworkProxy::FtpCachingProxy: + return QStringLiteral("FtpCachingProxy"); + } + Q_UNREACHABLE(); +} + + +QString DiagnosisConnectionTest::getProxyCapabilitiesAsQString(QNetworkProxy::Capabilities pCaps) +{ + QStringList scaps; + if (pCaps & QNetworkProxy::TunnelingCapability) + { + scaps << QStringLiteral("Tunnel"); + } + if (pCaps & QNetworkProxy::ListeningCapability) + { + scaps << QStringLiteral("Listen"); + } + if (pCaps & QNetworkProxy::UdpTunnelingCapability) + { + scaps << QStringLiteral("UDP"); + } + if (pCaps & QNetworkProxy::CachingCapability) + { + scaps << QStringLiteral("Caching"); + } + if (pCaps & QNetworkProxy::HostNameLookupCapability) + { + scaps << QStringLiteral("NameLookup"); + } + if (pCaps & QNetworkProxy::SctpTunnelingCapability) + { + scaps << QStringLiteral("SctpTunnel"); + } + if (pCaps & QNetworkProxy::SctpListeningCapability) + { + scaps << QStringLiteral("SctpListen"); + } + return scaps.join(QLatin1String(", ")); +} + + +void DiagnosisConnectionTest::startConnectionTest() +{ + mProxyPingDone = false; + mConnectionTestWithProxyDone = false; + mConnectionTestWithoutProxyDone = false; + + const auto& proxy = QNetworkProxy::applicationProxy(); + if (proxy.type() == QNetworkProxy::ProxyType::NoProxy) + { + mIsProxySet = false; + mProxyPingDone = true; + mConnectionTestWithProxyDone = true; + } + else + { + mIsProxySet = true; + mProxyHostName = proxy.hostName(); + mProxyPort = QString::number(proxy.port()); + mProxyType = getProxyTypeAsQString(proxy.type()); + mProxyCapabilities = getProxyCapabilitiesAsQString(proxy.capabilities()); + } + + const QUrl& testUrl = QUrl(SecureStorage::getInstance().getUpdateServerBaseUrl()); + if (mIsProxySet) + { + mPingSocketToProxy.reset(); + mPingSocketToProxy.setProxy(QNetworkProxy::NoProxy); + connect(&mPingSocketToProxy, &QTcpSocket::connected, this, &DiagnosisConnectionTest::onProxyPingTestDone); + connect(&mPingSocketToProxy, QOverload::of(&QTcpSocket::error), this, &DiagnosisConnectionTest::onProxyPingTestError); + mPingSocketToProxy.connectToHost(proxy.hostName(), proxy.port()); + + mTcpSocketWithProxy.reset(); + mTcpSocketWithProxy.setProxy(proxy); + connect(&mTcpSocketWithProxy, &QTcpSocket::connected, this, &DiagnosisConnectionTest::onSocketConnectionTestWithProxyDone); + connect(&mTcpSocketWithProxy, QOverload::of(&QTcpSocket::error), this, &DiagnosisConnectionTest::onSocketConnectionTestWithProxyError); + mTcpSocketWithProxy.connectToHost(testUrl.host(), 443); + } + + mTcpSocketWithoutProxy.reset(); + mTcpSocketWithoutProxy.setProxy(QNetworkProxy::NoProxy); + connect(&mTcpSocketWithoutProxy, &QTcpSocket::connected, this, &DiagnosisConnectionTest::onSocketConnectionTestWithoutProxyDone); + connect(&mTcpSocketWithoutProxy, QOverload::of(&QTcpSocket::error), this, &DiagnosisConnectionTest::onSocketConnectionTestWithoutProxyError); + mTcpSocketWithoutProxy.connectToHost(testUrl.host(), 443); +} diff --git a/src/core/DiagnosisConnectionTest.h b/src/core/DiagnosisConnectionTest.h new file mode 100644 index 0000000..01b2a5f --- /dev/null +++ b/src/core/DiagnosisConnectionTest.h @@ -0,0 +1,114 @@ +/*! + * \brief Class for retrieving informations about the system proxy, pinging the proxy, trying + * to establish a connection to a test server with and without the proxy and providing the + * results to the DiagnosisModel. + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include +#include + +class test_DiagnosisConnectionTest; + +namespace governikus +{ + +class DiagnosisConnectionTest + : public QObject +{ + Q_OBJECT + + private: + friend class ::test_DiagnosisConnectionTest; + bool mIsProxySet; + QString mProxyHostName; + QString mProxyPort; + QString mProxyType; + QString mProxyCapabilities; + + bool mConnectionTestWithProxySuccessful; + bool mConnectionTestWithoutProxySuccessful; + + bool mPingTestOnProxySuccessful; + + bool mProxyPingDone; + bool mConnectionTestWithProxyDone; + bool mConnectionTestWithoutProxyDone; + + QTcpSocket mPingSocketToProxy; + QTcpSocket mTcpSocketWithProxy; + QTcpSocket mTcpSocketWithoutProxy; + + void checkIfAllProcessesDone(); + static QString getProxyTypeAsQString(QNetworkProxy::ProxyType pType); + static QString getProxyCapabilitiesAsQString(QNetworkProxy::Capabilities pCaps); + + private Q_SLOTS: + void onProxyPingTestDone(); + void onProxyPingTestError(QAbstractSocket::SocketError pSocketError); + void onSocketConnectionTestWithProxyDone(); + void onSocketConnectionTestWithProxyError(QAbstractSocket::SocketError pSocketError); + void onSocketConnectionTestWithoutProxyDone(); + void onSocketConnectionTestWithoutProxyError(QAbstractSocket::SocketError pSocketError); + + public: + DiagnosisConnectionTest(); + void startConnectionTest(); + + bool getIsProxySet() const + { + return mIsProxySet; + } + + + const QString& getProxyHostName() const + { + return mProxyHostName; + } + + + const QString& getProxyPort() const + { + return mProxyPort; + } + + + const QString& getProxyType() const + { + return mProxyType; + } + + + const QString& getProxyCapabilities() const + { + return mProxyCapabilities; + } + + + bool getConnectionTestWithProxySuccessful() const + { + return mConnectionTestWithProxySuccessful; + } + + + bool getConnectionTestWithoutProxySuccessful() const + { + return mConnectionTestWithoutProxySuccessful; + } + + + bool getPingTestOnProxySuccessful() const + { + return mPingTestOnProxySuccessful; + } + + + Q_SIGNALS: + void fireConnectionTestDone(); +}; + +} // namespace governikus diff --git a/src/core/DiagnosisFirewallDetection.cpp b/src/core/DiagnosisFirewallDetection.cpp new file mode 100644 index 0000000..719aef6 --- /dev/null +++ b/src/core/DiagnosisFirewallDetection.cpp @@ -0,0 +1,385 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "DiagnosisFirewallDetection.h" + +#include +#include +#include +#include + +using namespace governikus; + +#if defined(Q_OS_WIN) +void DiagnosisFirewallDetection::checkIfAllInformationReady() +{ + if (mFirstRuleDone && mSecondRuleDone && mProfilesDone && mInstalledFirewallsDone) + { + Q_EMIT fireFirewallInformationReady(); + } +} + + +void DiagnosisFirewallDetection::onFirstRuleDone(int exitCode, QProcess::ExitStatus exitStatus) +{ + Q_UNUSED(exitStatus); + + if (exitCode != 0) + { + mFirstRuleDone = true; + mFirstFirewallRuleExists = false; + mFirstFirewallRuleEnabled = false; + checkIfAllInformationReady(); + return; + } + + const QString& output = QString::fromUtf8(mFirewallFirstRuleProcess.readAllStandardOutput()); + + const auto& lines = output.split(QStringLiteral("\r\n")); + for (auto line : lines) + { + const QStringList& lineparts = line.split(QStringLiteral(" : ")); + if (lineparts.length() != 2) + { + continue; + } + if (lineparts[0].startsWith(QLatin1String("Name"))) + { + if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8 && lineparts[1].startsWith(QLatin1String("AusweisApp2"))) + { + mFirstFirewallRuleExists = true; + } + else if (lineparts[1].startsWith(QLatin1String("AusweisApp2-Firewall-Rule"))) + { + mFirstFirewallRuleExists = true; + } + } + else if (lineparts[0].startsWith(QLatin1String("Enabled")) && lineparts[1].startsWith(QLatin1String("True"))) + { + mFirstFirewallRuleEnabled = true; + } + } + mFirstRuleDone = true; + checkIfAllInformationReady(); +} + + +void DiagnosisFirewallDetection::onFirstRuleError(QProcess::ProcessError pError) +{ + qDebug() << "Error while getting first firewall rule:" << pError; + mFirstRuleDone = false; + Q_EMIT fireDetectionFailed(); +} + + +void DiagnosisFirewallDetection::onSecondRuleDone(int exitCode, QProcess::ExitStatus exitStatus) +{ + Q_UNUSED(exitStatus); + + if (exitCode != 0) + { + mSecondRuleDone = true; + mSecondFirewallRuleExists = false; + mSecondFirewallRuleEnabled = false; + checkIfAllInformationReady(); + return; + } + + const QString& output = QString::fromUtf8(mFirewallSecondRuleProcess.readAllStandardOutput()); + + const auto& lines = output.split(QStringLiteral("\r\n")); + for (auto line : lines) + { + const QStringList& lineparts = line.split(QStringLiteral(" : ")); + if (lineparts.length() != 2) + { + continue; + } + if (lineparts[0].startsWith(QLatin1String("Name"))) + { + if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8 && lineparts[1].startsWith(QLatin1String("AusweisApp2-Firewall-Rule-SaC-In"))) + { + mSecondFirewallRuleExists = true; + } + else if (lineparts[1].startsWith(QLatin1String("AusweisApp2-Firewall-Rule-In"))) + { + mSecondFirewallRuleExists = true; + } + } + else if (lineparts[0].startsWith(QLatin1String("Enabled")) && lineparts[1].startsWith(QLatin1String("True"))) + { + mSecondFirewallRuleEnabled = true; + } + } + mSecondRuleDone = true; + checkIfAllInformationReady(); +} + + +void DiagnosisFirewallDetection::onSecondRuleError(QProcess::ProcessError pError) +{ + qDebug() << "Error while getting second firewall rule:" << pError; + mSecondRuleDone = false; + Q_EMIT fireDetectionFailed(); +} + + +void DiagnosisFirewallDetection::onProfilesDone(int exitCode, QProcess::ExitStatus exitStatus) +{ + Q_UNUSED(exitStatus); + if (exitCode != 0) + { + qDebug() << "Error while getting firewall profiles, error code:" << exitCode; + mProfilesDone = false; + Q_EMIT fireDetectionFailed(); + return; + } + + const QString& output = QString::fromUtf8(mFirewallProfilesProcess.readAllStandardOutput()); + + QString name; + bool enabled = false; + + const auto& lines = output.split(QStringLiteral("\r\n")); + for (auto line : lines) + { + const QString& trimmedLine = line.trimmed(); + if (trimmedLine.isEmpty() && !name.isEmpty()) + { + mFirewallProfiles << QSharedPointer::create(name, enabled); + name = QString(); + enabled = false; + continue; + } + + const QStringList& lineparts = line.split(QStringLiteral(" : ")); + if (lineparts.length() != 2) + { + continue; + } + if (lineparts[0].startsWith(QLatin1String("Name"))) + { + name = lineparts[1]; + } + else if (lineparts[0].startsWith(QLatin1String("Enabled")) && lineparts[1].startsWith(QLatin1String("True"))) + { + enabled = true; + } + } + + mProfilesDone = true; + checkIfAllInformationReady(); +} + + +void DiagnosisFirewallDetection::onProfilesError(QProcess::ProcessError pError) +{ + qDebug() << "Error while getting firewall profiles:" << pError; + mProfilesDone = false; + Q_EMIT fireDetectionFailed(); +} + + +void DiagnosisFirewallDetection::onInstalledFirewallSoftwareDone(int exitCode, QProcess::ExitStatus exitStatus) +{ + Q_UNUSED(exitStatus); + + if (exitCode != 0) + { + qDebug() << "Error while getting installed firewalls, error code:" << exitCode; + mInstalledFirewallsDone = false; + Q_EMIT fireDetectionFailed(); + return; + } + + const QString& output = QString::fromUtf8(mInstalledFirewallSoftwareProcess.readAllStandardOutput()); + + QString name; + bool enabled = false; + bool uptodate = false; + + const auto& lines = output.split(QStringLiteral("\r\n")); + for (auto line : lines) + { + const QString& trimmedLine = line.trimmed(); + if (trimmedLine.isEmpty() && !name.isEmpty()) + { + mDetectedFirewalls << QSharedPointer::create(name, enabled, uptodate); + name = QString(); + enabled = false; + uptodate = false; + continue; + } + + const QStringList& lineparts = line.split(QStringLiteral(" : ")); + if (lineparts.length() != 2) + { + continue; + } + if (lineparts[0].startsWith(QLatin1String("displayName"))) + { + name = lineparts[1]; + } + else if (lineparts[0].startsWith(QLatin1String("productState"))) + { + // status decoding based on https://gallery.technet.microsoft.com/scriptcenter/Get-the-status-of-4b748f25 + bool convertStatus; + int statusValue = lineparts[1].toInt(&convertStatus, 10); + if (convertStatus) + { + QString hexString = QString::number(statusValue, 16); + if (hexString.length() >= 4) + { + enabled = hexString.right(4).left(2) == QStringLiteral("10"); + uptodate = hexString.right(2) == QStringLiteral("00"); + } + else + { + qDebug() << "Cannot parse productState value, hex value of int got wrong length:" << statusValue; + enabled = false; + uptodate = false; + } + } + else + { + qDebug() << "Cannot parse productState value, is not an integer:" << lineparts[2]; + enabled = false; + uptodate = false; + } + } + } + + mInstalledFirewallsDone = true; + checkIfAllInformationReady(); +} + + +void DiagnosisFirewallDetection::onInstalledFirewallSoftwareError(QProcess::ProcessError pError) +{ + qDebug() << "Error while getting installed firewall software:" << pError; + mInstalledFirewallsDone = false; + Q_EMIT fireDetectionFailed(); +} + + +#endif + +DiagnosisFirewallDetection::DiagnosisFirewallDetection() + : QObject() + , mFirstFirewallRuleExists(false) + , mFirstFirewallRuleEnabled(false) + , mSecondFirewallRuleExists(false) + , mSecondFirewallRuleEnabled(false) + , mDetectedFirewalls() + , mFirewallProfiles() + , mFirstRuleDone(false) + , mSecondRuleDone(false) + , mProfilesDone(false) + , mInstalledFirewallsDone(false) +#if defined(Q_OS_WIN) + , mFirewallFirstRuleProcess() + , mFirewallSecondRuleProcess() + , mFirewallProfilesProcess() + , mInstalledFirewallSoftwareProcess() +#endif +{ +} + + +void DiagnosisFirewallDetection::startDetection() +{ +#if defined(Q_OS_WIN) + QString powershellCommand = QStandardPaths::findExecutable(QStringLiteral("powershell.exe")); + if (powershellCommand.isEmpty()) + { + powershellCommand = QStringLiteral("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"); + if (!QFile::exists(powershellCommand)) + { + qDebug() << "Could not find powershell"; + Q_EMIT fireDetectionFailed(); + return; + } + } + + QStringList firstRuleParameters; + firstRuleParameters << QStringLiteral("-NoProfile"); + firstRuleParameters << QStringLiteral("-Command"); + if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8) + { + firstRuleParameters << QStringLiteral("(New-object -comObject HNetCfg.FwPolicy2).rules | where-object {$_.name -like \"AusweisApp2\"}"); + } + else + { + firstRuleParameters << QStringLiteral("Get-NetFirewallRule -Name \"AusweisApp2-Firewall-Rule\""); + } + + connect(&mFirewallFirstRuleProcess, QOverload::of(&QProcess::finished), this, &DiagnosisFirewallDetection::onFirstRuleDone); + connect(&mFirewallFirstRuleProcess, &QProcess::errorOccurred, this, &DiagnosisFirewallDetection::onFirstRuleError); + + mFirewallFirstRuleProcess.start(powershellCommand, firstRuleParameters); + mFirewallFirstRuleProcess.closeWriteChannel(); + + QStringList secondRuleParameters; + secondRuleParameters << QStringLiteral("-NoProfile"); + secondRuleParameters << QStringLiteral("-Command"); + if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8) + { + secondRuleParameters << QStringLiteral("(New-object -comObject HNetCfg.FwPolicy2).rules | where-object {$_.name -like \"AusweisApp2-Firewall-Rule-SaC-In\"}"); + } + else + { + secondRuleParameters << QStringLiteral("Get-NetFirewallRule -Name \"AusweisApp2-Firewall-Rule-In\""); + } + + connect(&mFirewallSecondRuleProcess, QOverload::of(&QProcess::finished), this, &DiagnosisFirewallDetection::onSecondRuleDone); + connect(&mFirewallSecondRuleProcess, &QProcess::errorOccurred, this, &DiagnosisFirewallDetection::onSecondRuleError); + + mFirewallSecondRuleProcess.start(powershellCommand, secondRuleParameters); + mFirewallSecondRuleProcess.closeWriteChannel(); + + QStringList profilesParameters; + profilesParameters << QStringLiteral("-NoProfile"); + profilesParameters << QStringLiteral("-Command"); + if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8) + { + profilesParameters << QStringLiteral("$fw=New-object -comObject HNetCfg.FwPolicy2; $fwProfileTypes= @{1=\"Domain\"; 2=\"Private\" ; 4=\"Public\"}; @(1,2,4) | select @{Name=\"Name\" ;expression={$fwProfileTypes[$_]}}, @{Name=\"Enabled\" ;expression={$fw.FireWallEnabled($_)}} | Format-List Name, Enabled"); + } + else + { + profilesParameters << QStringLiteral("Get-NetFirewallProfile"); + } + + connect(&mFirewallProfilesProcess, QOverload::of(&QProcess::finished), this, &DiagnosisFirewallDetection::onProfilesDone); + connect(&mFirewallProfilesProcess, &QProcess::errorOccurred, this, &DiagnosisFirewallDetection::onProfilesError); + + mFirewallProfilesProcess.start(powershellCommand, profilesParameters); + mFirewallProfilesProcess.closeWriteChannel(); + + QStringList installedFirewallSoftwareParameters; + installedFirewallSoftwareParameters << QStringLiteral("-Command"); + installedFirewallSoftwareParameters << QStringLiteral("Get-WmiObject -Namespace root\\SecurityCenter2 -Class FirewallProduct"); + + connect(&mInstalledFirewallSoftwareProcess, QOverload::of(&QProcess::finished), this, &DiagnosisFirewallDetection::onInstalledFirewallSoftwareDone); + connect(&mInstalledFirewallSoftwareProcess, &QProcess::errorOccurred, this, &DiagnosisFirewallDetection::onInstalledFirewallSoftwareError); + + mInstalledFirewallSoftwareProcess.start(powershellCommand, installedFirewallSoftwareParameters); + mInstalledFirewallSoftwareProcess.closeWriteChannel(); + +#endif +} + + +FirewallSoftware::FirewallSoftware(QString pName, bool pEnabled, bool pUpToDate) + : mName(pName) + , mEnabled(pEnabled) + , mUpToDate(pUpToDate) +{ +} + + +FirewallProfile::FirewallProfile(QString pName, bool pEnabled) + : mName(pName) + , mEnabled(pEnabled) +{ +} diff --git a/src/core/DiagnosisFirewallDetection.h b/src/core/DiagnosisFirewallDetection.h new file mode 100644 index 0000000..d1c6894 --- /dev/null +++ b/src/core/DiagnosisFirewallDetection.h @@ -0,0 +1,155 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include +#include + +#if defined(Q_OS_WIN) +#include +#endif + + +namespace governikus +{ + +class FirewallProfile +{ + private: + const QString mName; + const bool mEnabled; + + public: + FirewallProfile(QString pName, bool pEnabled); + + const QString& getName() const + { + return mName; + } + + + bool getEnabled() const + { + return mEnabled; + } + + +}; + +class FirewallSoftware +{ + private: + QString mName; + bool mEnabled; + bool mUpToDate; + + public: + FirewallSoftware(QString pName, bool pEnabled, bool pUpToDate); + + const QString& getName() const + { + return mName; + } + + + bool getEnabled() const + { + return mEnabled; + } + + + bool getUpToDate() const + { + return mUpToDate; + } + + +}; + +class DiagnosisFirewallDetection + : public QObject +{ + Q_OBJECT + + private: + bool mFirstFirewallRuleExists; + bool mFirstFirewallRuleEnabled; + bool mSecondFirewallRuleExists; + bool mSecondFirewallRuleEnabled; + QVector > mDetectedFirewalls; + QVector > mFirewallProfiles; + + bool mFirstRuleDone; + bool mSecondRuleDone; + bool mProfilesDone; + bool mInstalledFirewallsDone; + +#if defined(Q_OS_WIN) + QProcess mFirewallFirstRuleProcess; + QProcess mFirewallSecondRuleProcess; + QProcess mFirewallProfilesProcess; + QProcess mInstalledFirewallSoftwareProcess; + + void checkIfAllInformationReady(); + + private Q_SLOTS: + void onFirstRuleDone(int exitCode, QProcess::ExitStatus exitStatus); + void onFirstRuleError(QProcess::ProcessError pError); + void onSecondRuleDone(int exitCode, QProcess::ExitStatus exitStatus); + void onSecondRuleError(QProcess::ProcessError pError); + void onProfilesDone(int exitCode, QProcess::ExitStatus exitStatus); + void onProfilesError(QProcess::ProcessError pError); + void onInstalledFirewallSoftwareDone(int exitCode, QProcess::ExitStatus exitStatus); + void onInstalledFirewallSoftwareError(QProcess::ProcessError pError); +#endif + + public: + DiagnosisFirewallDetection(); + void startDetection(); + + bool getFirstRuleExists() const + { + return mFirstFirewallRuleExists; + } + + + bool getFirstRuleEnabled() const + { + return mFirstFirewallRuleEnabled; + } + + + bool getSecondRuleExists() const + { + return mSecondFirewallRuleExists; + } + + + bool getSecondRuleEnabled() const + { + return mSecondFirewallRuleEnabled; + } + + + const QVector >& getFirewallProfiles() const + { + return mFirewallProfiles; + } + + + const QVector >& getDetectedFirewalls() const + { + return mDetectedFirewalls; + } + + + Q_SIGNALS: + void fireFirewallInformationReady(); + void fireDetectionFailed(); +}; + + +} // namespace governikus diff --git a/src/core/DiagnosisItem.cpp b/src/core/DiagnosisItem.cpp new file mode 100644 index 0000000..f70786e --- /dev/null +++ b/src/core/DiagnosisItem.cpp @@ -0,0 +1,94 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "DiagnosisItem.h" + +using namespace governikus; + +DiagnosisItem::DiagnosisItem(const QString& pText) + : QObject() + , QEnableSharedFromThis() + , mText(pText) + , mChildren() +{ +} + + +const QString& DiagnosisItem::getText() const +{ + return mText; +} + + +void DiagnosisItem::addChild(const QSharedPointer& pChild) +{ + mChildren << pChild; + pChild->setParent(sharedFromThis()); +} + + +void DiagnosisItem::setParent(const QSharedPointer& pParent) +{ + mParent = pParent; +} + + +void DiagnosisItem::clearChildren() +{ + mChildren.clear(); +} + + +const QSharedPointer DiagnosisItem::getChild(int pRow) const +{ + return mChildren.at(pRow); +} + + +int DiagnosisItem::childCount() const +{ + return mChildren.length(); +} + + +QSharedPointer DiagnosisItem::parentItem() +{ + return mParent; +} + + +int DiagnosisItem::row() const +{ + if (!mParent.isNull()) + { + return mParent->getIndexOf(this); + } + + return 0; +} + + +int DiagnosisItem::getIndexOf(const DiagnosisItem* pChild) const +{ + for (int i = 0; i < mChildren.length(); ++i) + { + auto child = mChildren.at(i); + if (child.data() == pChild) + { + return i; + } + } + return -1; +} + + +void DiagnosisItem::appendPlaintextContent(QStringList& pOutput, const QString& pPrefix) +{ + pOutput << pPrefix + getText(); + const QString childPrefix = pPrefix + QLatin1Char(' '); + for (const auto& child : qAsConst(mChildren)) + { + child->appendPlaintextContent(pOutput, childPrefix); + } +} diff --git a/src/core/DiagnosisItem.h b/src/core/DiagnosisItem.h new file mode 100644 index 0000000..bb065cc --- /dev/null +++ b/src/core/DiagnosisItem.h @@ -0,0 +1,44 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include +#include +#include + + +namespace governikus +{ + +class DiagnosisItem + : public QObject + , public QEnableSharedFromThis +{ + Q_OBJECT + + private: + QString mText; + QVector > mChildren; + QSharedPointer mParent; + + int getIndexOf(const DiagnosisItem* pChild) const; + void setParent(const QSharedPointer& pParent); + + public: + explicit DiagnosisItem(const QString& pText); + + void addChild(const QSharedPointer& pChild); + const QString& getText() const; + + int childCount() const; + const QSharedPointer getChild(int pRow) const; + void clearChildren(); + QSharedPointer parentItem(); + int row() const; + void appendPlaintextContent(QStringList& pOutput, const QString& pPrefix = QString()); +}; + +} // namespace governikus diff --git a/src/core/DiagnosisModel.cpp b/src/core/DiagnosisModel.cpp new file mode 100644 index 0000000..4022010 --- /dev/null +++ b/src/core/DiagnosisModel.cpp @@ -0,0 +1,578 @@ +/* + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "DiagnosisModel.h" + +#include "AppSettings.h" +#include "LanguageLoader.h" +#include "RemoteServiceSettings.h" +#include "ScopeGuard.h" + +#include +#include + +using namespace governikus; + + +DiagnosisModel::DiagnosisModel(const QSharedPointer& pContext) + : mContext(pContext) + , mRootItem(new DiagnosisItem(tr("Diagnosis data"))) + , mAppVersionItem(new DiagnosisItem(QCoreApplication::applicationName())) + , mOperatingSystemItem(new DiagnosisItem(tr("Operating system"))) + , mReaderItem(new DiagnosisItem(tr("Card reader"))) + , mPcScItem(new DiagnosisItem(tr("PC/SC"))) + , mPairedDevices(new DiagnosisItem(tr("Paired devices"))) + , mNetworkInterfaces(new DiagnosisItem(tr("Network interfaces"))) + , mNetworkConnectionTest(new DiagnosisItem(tr("Network connection test"))) + , mInstalledAntivirus(new DiagnosisItem(tr("Installed antivirus software"))) + , mWindowsFirewall(new DiagnosisItem(tr("Firewall"))) + , mTimestampItem(new DiagnosisItem(tr("Time of diagnosis"))) + , mAntivirusDetection() + , mFirewallDetection() + , mConnectionTest() +{ + mRootItem->addChild(mAppVersionItem); + initAppVersionInfo(); + + mRootItem->addChild(mOperatingSystemItem); + mOperatingSystemItem->addChild(QSharedPointer::create(QSysInfo::prettyProductName())); + mOperatingSystemItem->addChild(QSharedPointer::create(QSysInfo::kernelVersion())); + mOperatingSystemItem->addChild(QSharedPointer::create(QSysInfo::currentCpuArchitecture())); + + mRootItem->addChild(mReaderItem); + mReaderItem->addChild(QSharedPointer::create(tr("Diagnosis is running..."))); + connect(mContext.data(), &DiagnosisContext::readerInfosChanged, this, &DiagnosisModel::onReaderInfosChanged); + + mRootItem->addChild(mPcScItem); + mPcScItem->addChild(QSharedPointer::create(tr("Diagnosis is running..."))); + connect(mContext.data(), &DiagnosisContext::pcscInfoChanged, this, &DiagnosisModel::onPcscInfoChanged); + + mRootItem->addChild(mPairedDevices); + RemoteServiceSettings& settings = Env::getSingleton()->getRemoteServiceSettings(); + connect(&settings, &RemoteServiceSettings::fireTrustedRemoteInfosChanged, this, &DiagnosisModel::onRemoteInfosChanged); + onRemoteInfosChanged(); + + mRootItem->addChild(mNetworkInterfaces); + connect(mContext.data(), &DiagnosisContext::fireNetworkInfoChanged, this, &DiagnosisModel::onNetworkInfoChanged); + + mRootItem->addChild(mNetworkConnectionTest); + mNetworkConnectionTest->addChild(QSharedPointer::create(tr("Diagnosis is running..."))); + connect(&mConnectionTest, &DiagnosisConnectionTest::fireConnectionTestDone, this, &DiagnosisModel::onConnectionTestDone); + mConnectionTest.startConnectionTest(); + + mRootItem->addChild(mInstalledAntivirus); + mRootItem->addChild(mWindowsFirewall); +#ifdef Q_OS_WIN + connect(&mAntivirusDetection, &DiagnosisAntivirusDetection::fireAntivirusInformationChanged, this, &DiagnosisModel::onAntivirusInformationChanged); + connect(&mAntivirusDetection, &DiagnosisAntivirusDetection::fireDetectionFailed, this, &DiagnosisModel::onAntivirusDetectionFailed); + mAntivirusDetection.startInformationProcess(); + mInstalledAntivirus->addChild(QSharedPointer::create(tr("Diagnosis is running..."))); + + connect(&mFirewallDetection, &DiagnosisFirewallDetection::fireFirewallInformationReady, this, &DiagnosisModel::onFirewallInformationReady); + connect(&mFirewallDetection, &DiagnosisFirewallDetection::fireDetectionFailed, this, &DiagnosisModel::onFirewallInformationFailed); + mFirewallDetection.startDetection(); + mWindowsFirewall->addChild(QSharedPointer::create(tr("Diagnosis is running..."))); +#else + mInstalledAntivirus->addChild(QSharedPointer::create(tr("No Antivirus information available on this platform."))); + mWindowsFirewall->addChild(QSharedPointer::create(tr("No Firewall information available on this platform."))); +#endif + + mRootItem->addChild(mTimestampItem); + connect(mContext.data(), &DiagnosisContext::timestampChanged, this, &DiagnosisModel::onTimestampChanged); + onTimestampChanged(); +} + + +void DiagnosisModel::initAppVersionInfo() +{ + const QStringList appVersion({ + QCoreApplication::applicationVersion(), + QCoreApplication::organizationName(), + QStringLiteral("Qt ") + QString::fromLatin1(qVersion()), + QSslSocket::sslLibraryVersionString() + }); + for (const auto& str : appVersion) + { + mAppVersionItem->addChild(QSharedPointer::create(str)); + } +} + + +void DiagnosisModel::insertPcScComponentList(const QVector& pComponents, const QSharedPointer& pParentItem) +{ + if (pComponents.isEmpty()) + { + return; + } + + for (const DiagnosisContext::ComponentInfo& info : pComponents) + { + auto descriptionItem = QSharedPointer::create(info.getDescription()); + pParentItem->addChild(descriptionItem); + + auto companyItem = QSharedPointer::create(tr("Vendor: %1").arg(info.getManufacturer())); + pParentItem->addChild(companyItem); + + auto versionItem = QSharedPointer::create(tr("Version: %1").arg(info.getVersion())); + pParentItem->addChild(versionItem); + + auto pathItem = QSharedPointer::create(tr("File path: %1").arg(info.getPath())); + descriptionItem->addChild(pathItem); + } +} + + +void DiagnosisModel::onReaderInfosChanged() +{ + beginResetModel(); + mReaderItem->clearChildren(); + + const auto& readerInfos = mContext->getReaderInfos(); + if (readerInfos.isEmpty()) + { + mReaderItem->addChild(QSharedPointer::create(tr("Not recognised"))); + } + + for (const ReaderInfo& info : readerInfos) + { + auto readerName = QSharedPointer::create(info.getName()); + mReaderItem->addChild(readerName); + + QString readerTypeString = info.isBasicReader() ? tr("Basic card reader") : tr("Standard / comfort card reader"); + auto readerType = QSharedPointer::create(tr("Type: %1").arg(readerTypeString)); + readerName->addChild(readerType); + + QString cardTypeString = info.getCardTypeString(); + auto cardType = QSharedPointer::create(tr("Card: %1").arg(cardTypeString)); + readerName->addChild(cardType); + + if (info.hasEidCard()) + { + auto retryCounter = QSharedPointer::create(tr("Retry counter: %1").arg(3 - info.getRetryCounter())); + readerName->addChild(retryCounter); + } + } + endResetModel(); +} + + +void DiagnosisModel::onPcscInfoChanged() +{ + beginResetModel(); + mPcScItem->clearChildren(); + + auto pcscVersion = QSharedPointer::create(tr("Version: %1").arg(mContext->getPcscVersion())); + mPcScItem->addChild(pcscVersion); + + auto pcscComponents = QSharedPointer::create(tr("Components")); + mPcScItem->addChild(pcscComponents); + insertPcScComponentList(mContext->getPcscComponents(), pcscComponents); + + auto driverItem = QSharedPointer::create(tr("Driver")); + mPcScItem->addChild(driverItem); + insertPcScComponentList(mContext->getPcscDrivers(), driverItem); + endResetModel(); +} + + +void DiagnosisModel::onRemoteInfosChanged() +{ + beginResetModel(); + + const ScopeGuard guard([this] { + endResetModel(); + }); + + mPairedDevices->clearChildren(); + + const RemoteServiceSettings& settings = Env::getSingleton()->getRemoteServiceSettings(); + const auto& trustedCertificates = settings.getTrustedCertificates(); + + if (trustedCertificates.isEmpty()) + { + mPairedDevices->addChild(QSharedPointer::create(tr("No devices paired"))); + return; + } + + for (const auto& cert : trustedCertificates) + { + const auto& info = settings.getRemoteInfo(cert); + + if (!info.getFingerprint().isEmpty()) + { + const auto& deviceName = QSharedPointer::create(info.getName()); + mPairedDevices->addChild(deviceName); + + const auto& deviceFingerprint = QSharedPointer::create((tr("Certificate fingerprint: %1").arg(info.getFingerprint()))); + deviceName->addChild(deviceFingerprint); + + const QString& timestamp = LanguageLoader::getInstance().getUsedLocale().toString(info.getLastConnected(), tr("dd.MM.yyyy, hh:mm:ss")); + const auto& deviceLastConnected = QSharedPointer::create(tr("Last connection: %1").arg(timestamp)); + deviceName->addChild(deviceLastConnected); + } + else + { + const auto& deviceName = QSharedPointer::create(RemoteServiceSettings::generateFingerprint(cert)); + mPairedDevices->addChild(deviceName); + + const auto& deviceInfo = QSharedPointer::create(tr("No information found for this certificate")); + deviceName->addChild(deviceInfo); + } + } +} + + +void DiagnosisModel::onTimestampChanged() +{ + beginResetModel(); + mTimestampItem->clearChildren(); + QDateTime timestampValue = mContext->getTimestamp(); + if (!timestampValue.isValid()) + { + mTimestampItem->addChild(QSharedPointer::create(tr("Initial diagnosis running, please wait."))); + } + else + { + QString timestamp = LanguageLoader::getInstance().getUsedLocale().toString(timestampValue, tr("d. MMMM yyyy, hh:mm:ss AP")); + mTimestampItem->addChild(QSharedPointer::create(timestamp)); + } + endResetModel(); +} + + +void DiagnosisModel::onNetworkInfoChanged() +{ + beginResetModel(); + mNetworkInterfaces->clearChildren(); + const auto& networkInterfaces = mContext->getNetworkInterfaces(); + for (const auto& interface : networkInterfaces) + { + const auto& interfaceName = QSharedPointer::create(interface.humanReadableName()); + mNetworkInterfaces->addChild(interfaceName); + + QString hardwareAddress = interface.hardwareAddress().isEmpty() ? tr("") : interface.hardwareAddress(); + interfaceName->addChild(QSharedPointer::create(tr("Hardware address: %1").arg(hardwareAddress))); + + const auto& addresses = interface.addressEntries(); + if (addresses.isEmpty()) + { + interfaceName->addChild(QSharedPointer::create(tr("No IP addresses assigned"))); + } + else + { + const auto& interfaceAddresses = QSharedPointer::create(tr("IP addresses")); + interfaceName->addChild(interfaceAddresses); + for (const auto& address : addresses) + { + const auto& ip = QSharedPointer::create(address.ip().toString()); + interfaceAddresses->addChild(ip); + } + } + } + endResetModel(); +} + + +void DiagnosisModel::onConnectionTestDone() +{ + beginResetModel(); + mNetworkConnectionTest->clearChildren(); + if (mConnectionTest.getIsProxySet()) + { + auto proxy = QSharedPointer::create(tr("Proxy")); + mNetworkConnectionTest->addChild(proxy); + + proxy->addChild(QSharedPointer::create(tr("Hostname: %1").arg(mConnectionTest.getProxyHostName()))); + proxy->addChild(QSharedPointer::create(tr("Port: %1").arg(mConnectionTest.getProxyPort()))); + proxy->addChild(QSharedPointer::create(tr("Type: %1").arg(mConnectionTest.getProxyType()))); + proxy->addChild(QSharedPointer::create(tr("Capabilities: %1").arg(mConnectionTest.getProxyCapabilities()))); + + if (mConnectionTest.getPingTestOnProxySuccessful()) + { + proxy->addChild(QSharedPointer::create(tr("Ping test to proxy: Successful"))); + } + else + { + proxy->addChild(QSharedPointer::create(tr("Ping test to proxy: Failed"))); + } + + if (mConnectionTest.getConnectionTestWithProxySuccessful()) + { + mNetworkConnectionTest->addChild(QSharedPointer::create(tr("Connection test with proxy: Successful"))); + } + else + { + mNetworkConnectionTest->addChild(QSharedPointer::create(tr("Connection test with proxy: Failed"))); + } + } + else + { + mNetworkConnectionTest->addChild(QSharedPointer::create(tr("No Proxy Found"))); + } + + if (mConnectionTest.getConnectionTestWithoutProxySuccessful()) + { + mNetworkConnectionTest->addChild(QSharedPointer::create(tr("Connection test without proxy: Successful"))); + } + else + { + mNetworkConnectionTest->addChild(QSharedPointer::create(tr("Connection test without proxy: Failed"))); + } + endResetModel(); +} + + +void DiagnosisModel::onAntivirusInformationChanged() +{ + beginResetModel(); + mInstalledAntivirus->clearChildren(); + + const auto& antivirusInfos = mAntivirusDetection.getAntivirusInformations(); + if (antivirusInfos.isEmpty()) + { + mInstalledAntivirus->addChild(QSharedPointer::create(tr("No Antivirus software detected."))); + } + else + { + for (const auto& info : antivirusInfos) + { + auto antivirusName = QSharedPointer::create(info->getDisplayName()); + mInstalledAntivirus->addChild(antivirusName); + + if (!info->getLastUpdate().isEmpty()) + { + antivirusName->addChild(QSharedPointer::create(tr("Last updated: %1").arg(info->getLastUpdate()))); + } + antivirusName->addChild(QSharedPointer::create(tr("Executable path: %1").arg(info->getExePath()))); + } + } + endResetModel(); +} + + +void DiagnosisModel::onAntivirusDetectionFailed() +{ + beginResetModel(); + mInstalledAntivirus->clearChildren(); + mInstalledAntivirus->addChild(QSharedPointer::create(tr("Antivirus detection failed."))); + endResetModel(); +} + + +const QString DiagnosisModel::boolToString(bool pBoolean) +{ + return pBoolean ? tr("Yes") : tr("No"); +} + + +void DiagnosisModel::onFirewallInformationReady() +{ + beginResetModel(); + mWindowsFirewall->clearChildren(); + + auto installedFirewalls = mFirewallDetection.getDetectedFirewalls(); + if (installedFirewalls.isEmpty()) + { +#if defined(Q_OS_WIN) + if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8) + { + mWindowsFirewall->addChild(QSharedPointer::create(tr("Third party firewalls cannot be detected on Windows 7."))); + } + else + { +#else + { +#endif + mWindowsFirewall->addChild(QSharedPointer::create(tr("No third party firewalls detected"))); + } + } + else + { + auto thirdPartyFirewalls = QSharedPointer::create(tr("Firewalls from third party vendors")); + mWindowsFirewall->addChild(thirdPartyFirewalls); + for (const auto& firewall : installedFirewalls) + { + auto name = QSharedPointer::create(firewall->getName()); + thirdPartyFirewalls->addChild(name); + + QString enabled = boolToString(firewall->getEnabled()); + QString uptodate = boolToString(firewall->getUpToDate()); + name->addChild(QSharedPointer::create(tr("Enabled: %1").arg(enabled))); + name->addChild(QSharedPointer::create(tr("Up to date: %1").arg(uptodate))); + } + } + + auto firewallRules = QSharedPointer::create(tr("Windows firewall rules")); + mWindowsFirewall->addChild(firewallRules); + + QString firstRuleExists = boolToString(mFirewallDetection.getFirstRuleExists()); + QString firstRuleEnabled = boolToString(mFirewallDetection.getFirstRuleEnabled()); + auto outgoing = QSharedPointer::create(tr("Outgoing AusweisApp2 rule")); + firewallRules->addChild(outgoing); + outgoing->addChild(QSharedPointer::create(tr("Exists: %1").arg(firstRuleExists))); + outgoing->addChild(QSharedPointer::create(tr("Enabled: %1").arg(firstRuleEnabled))); + + QString secondRuleExists = boolToString(mFirewallDetection.getSecondRuleExists()); + QString secondRuleEnabled = boolToString(mFirewallDetection.getSecondRuleEnabled()); + auto incoming = QSharedPointer::create(tr("Incoming AusweisApp2 rule")); + firewallRules->addChild(incoming); + incoming->addChild(QSharedPointer::create(tr("Exists: %1").arg(secondRuleExists))); + incoming->addChild(QSharedPointer::create(tr("Enabled: %1").arg(secondRuleEnabled))); + + auto profiles = QSharedPointer::create(tr("Windows Firewall profiles")); + mWindowsFirewall->addChild(profiles); + + auto firewallProfiles = mFirewallDetection.getFirewallProfiles(); + for (const auto& profile : firewallProfiles) + { + auto name = QSharedPointer::create(profile->getName()); + profiles->addChild(name); + + QString enabled = boolToString(profile->getEnabled()); + name->addChild(QSharedPointer::create(tr("Enabled: %1").arg(enabled))); + } + profiles->addChild(QSharedPointer::create(tr("Warning: The current firewall status can be obscured by additional Group Policies on your system, often set by system administrators."))); + + endResetModel(); +} + + +void DiagnosisModel::onFirewallInformationFailed() +{ + beginResetModel(); + mWindowsFirewall->clearChildren(); + mWindowsFirewall->addChild(QSharedPointer::create(tr("An error occurred while trying to gather firewall information. Please check the log for more information."))); + endResetModel(); +} + + +QVariant DiagnosisModel::data(const QModelIndex& pIndex, int pRole) const +{ + if (!pIndex.isValid()) + { + return QVariant(); + } + + if (pRole != Qt::DisplayRole) + { + return QVariant(); + } + + DiagnosisItem* item = static_cast(pIndex.internalPointer()); + + return item->getText(); +} + + +QModelIndex DiagnosisModel::index(int pRow, int pColumn, const QModelIndex& pParent) const +{ + if (!hasIndex(pRow, pColumn, pParent)) + { + return QModelIndex(); + } + + DiagnosisItem* parentItem; + + if (pParent.isValid()) + { + parentItem = static_cast(pParent.internalPointer()); + } + else + { + parentItem = mRootItem.data(); + } + + DiagnosisItem* childItem = parentItem->getChild(pRow).data(); + if (childItem) + { + return createIndex(pRow, pColumn, childItem); + } + else + { + return QModelIndex(); + } +} + + +QModelIndex DiagnosisModel::parent(const QModelIndex& pIndex) const +{ + if (!pIndex.isValid()) + { + return QModelIndex(); + } + + DiagnosisItem* childItem = static_cast(pIndex.internalPointer()); + if (childItem == nullptr) + { + return QModelIndex(); + } + + DiagnosisItem* parentItem = childItem->parentItem().data(); + + if (parentItem == mRootItem.data()) + { + return QModelIndex(); + } + + return createIndex(parentItem->row(), 0, parentItem); +} + + +int DiagnosisModel::rowCount(const QModelIndex& pParent) const +{ + DiagnosisItem* parentItem; + if (pParent.column() > 0) + { + return 0; + } + + if (pParent.isValid()) + { + parentItem = static_cast(pParent.internalPointer()); + } + else + { + parentItem = mRootItem.data(); + } + + return parentItem->childCount(); +} + + +int DiagnosisModel::columnCount(const QModelIndex& pParent) const +{ + Q_UNUSED(pParent); + return 1; +} + + +QDateTime DiagnosisModel::getCreationTime() const +{ + return mContext->getTimestamp(); +} + + +QString DiagnosisModel::getAsPlaintext() const +{ + QStringList modelPlaintext; + mRootItem->appendPlaintextContent(modelPlaintext); +#ifdef Q_OS_WIN + return modelPlaintext.join(QLatin1String("\r\n")); + +#else + return modelPlaintext.join(QLatin1String("\n")); + +#endif +} + + +QVariant DiagnosisModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + Q_UNUSED(section); + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) + { + return mRootItem->getText(); + } + return QVariant(); +} diff --git a/src/core/DiagnosisModel.h b/src/core/DiagnosisModel.h new file mode 100644 index 0000000..a6c5eca --- /dev/null +++ b/src/core/DiagnosisModel.h @@ -0,0 +1,77 @@ +/* + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/DiagnosisContext.h" +#include "DiagnosisAntivirusDetection.h" +#include "DiagnosisConnectionTest.h" +#include "DiagnosisFirewallDetection.h" +#include "DiagnosisItem.h" + +#include +#include +#include +#include + +class test_DiagnosisModel; + +namespace governikus +{ + +class DiagnosisModel + : public QAbstractItemModel +{ + Q_OBJECT + + private: + friend class ::test_DiagnosisModel; + QSharedPointer mContext; + QSharedPointer mRootItem; + QSharedPointer mAppVersionItem; + QSharedPointer mOperatingSystemItem; + QSharedPointer mReaderItem; + QSharedPointer mPcScItem; + QSharedPointer mPairedDevices; + QSharedPointer mNetworkInterfaces; + QSharedPointer mNetworkConnectionTest; + QSharedPointer mInstalledAntivirus; + QSharedPointer mWindowsFirewall; + QSharedPointer mTimestampItem; + DiagnosisAntivirusDetection mAntivirusDetection; + DiagnosisFirewallDetection mFirewallDetection; + DiagnosisConnectionTest mConnectionTest; + + void initAppVersionInfo(); + void insertPcScComponentList(const QVector& pComponents, const QSharedPointer& pParentItem); + static const QString boolToString(bool pBoolean); + + private Q_SLOTS: + void onReaderInfosChanged(); + void onPcscInfoChanged(); + void onTimestampChanged(); + void onNetworkInfoChanged(); + void onRemoteInfosChanged(); + void onAntivirusInformationChanged(); + void onAntivirusDetectionFailed(); + void onFirewallInformationReady(); + void onFirewallInformationFailed(); + void onConnectionTestDone(); + + public: + explicit DiagnosisModel(const QSharedPointer& pContext); + + QVariant data(const QModelIndex& pIndex, int pRole) const override; + QModelIndex index(int pRow, int pColumn, + const QModelIndex& pParent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex& pIndex) const override; + int rowCount(const QModelIndex& pParent = QModelIndex()) const override; + int columnCount(const QModelIndex& pParent = QModelIndex()) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + + QDateTime getCreationTime() const; + QString getAsPlaintext() const; +}; + +} // namespace governikus diff --git a/src/core/SelfAuthenticationData.cpp b/src/core/SelfAuthenticationData.cpp index bcdd7ee..b562be9 100644 --- a/src/core/SelfAuthenticationData.cpp +++ b/src/core/SelfAuthenticationData.cpp @@ -6,7 +6,7 @@ #include "LanguageLoader.h" -#include +#include #include Q_DECLARE_LOGGING_CATEGORY(secure) @@ -54,23 +54,25 @@ SelfAuthenticationData::SelfData::SelfData(const QByteArray& pData) { return; } - qCDebug(secure) << "parsing data:" << pData; + qCDebug(secure).noquote() << "parsing data:\n" << pData; - QDomDocument doc(QStringLiteral("dataXML")); + mValid = parse(pData); +} - QString errorMsg; - int errorLine; - int errorColumn; - if (!doc.setContent(pData, true, &errorMsg, &errorLine, &errorColumn)) + +bool SelfAuthenticationData::SelfData::parse(const QByteArray& pData) +{ + QJsonParseError jsonError; + const auto& json = QJsonDocument::fromJson(pData, &jsonError); + if (jsonError.error != QJsonParseError::NoError) { - qDebug() << "XML parsing failed. No valid values to parse. Error in line" << errorLine << "column" - << errorColumn << ":" << errorMsg; - return; + qDebug() << "JSON parsing failed:" << jsonError.errorString(); + return false; } - const auto& parseOperations = std::bind(&SelfAuthenticationData::SelfData::parseOperationsAllowedByUser, this, std::placeholders::_1); - const auto& parsePersonal = std::bind(&SelfAuthenticationData::SelfData::parsePersonalData, this, std::placeholders::_1); - mValid = parse(doc, QStringLiteral("OperationsAllowedByUser"), parseOperations) && parse(doc, QStringLiteral("PersonalData"), parsePersonal); + const auto& obj = json.object(); + return parseOperationsAllowedByUser(obj.value(QLatin1String("OperationsAllowedByUser")).toObject()) + && parsePersonalData(obj.value(QLatin1String("PersonalData")).toObject()); } @@ -94,21 +96,23 @@ QString SelfAuthenticationData::SelfData::getValue(SelfAuthData pData) const } -bool SelfAuthenticationData::SelfData::parseOperationsAllowedByUser(const QDomElement& pElement) +bool SelfAuthenticationData::SelfData::parseOperationsAllowedByUser(const QJsonObject& pObject) { - for (auto elem = pElement; !elem.isNull(); elem = elem.nextSiblingElement()) + const auto& keys = pObject.keys(); + for (const auto& entry : keys) { - auto authData = Enum::fromString(elem.tagName(), SelfAuthData::UNKNOWN); + auto authData = Enum::fromString(entry, SelfAuthData::UNKNOWN); if (authData == SelfAuthData::UNKNOWN) { - qWarning() << "SelfAuthData is unknown:" << elem.tagName(); + qWarning() << "SelfAuthData is unknown:" << entry; continue; } - auto permission = Enum::fromString(elem.text(), SelfAuthDataPermission::UNKNOWN); + const auto tagName = pObject.value(entry).toString(); + auto permission = Enum::fromString(tagName, SelfAuthDataPermission::UNKNOWN); if (permission == SelfAuthDataPermission::UNKNOWN) { - qWarning() << "SelfAuthDataPermission is unknown:" << elem.tagName(); + qWarning() << "SelfAuthDataPermission is unknown:" << entry << '|' << tagName; continue; } @@ -119,28 +123,29 @@ bool SelfAuthenticationData::SelfData::parseOperationsAllowedByUser(const QDomEl } -bool SelfAuthenticationData::SelfData::parsePersonalData(const QDomElement& pElement) +bool SelfAuthenticationData::SelfData::parsePersonalData(const QJsonObject& pObject) { - for (auto elem = pElement; !elem.isNull(); elem = elem.nextSiblingElement()) + const auto& keys = pObject.keys(); + for (const auto& entry : keys) { - auto authData = Enum::fromString(elem.tagName(), SelfAuthData::UNKNOWN); + const auto subvalue = [&pObject, &entry](const char* pValue){ + return pObject.value(entry).toObject().value(QLatin1String(pValue)); + }; + + auto authData = Enum::fromString(entry, SelfAuthData::UNKNOWN); if (authData == SelfAuthData::UNKNOWN) { - qWarning() << "PersonalData is unknown:" << elem.tagName(); + qWarning() << "PersonalData is unknown:" << entry; continue; } - if (authData == SelfAuthData::DateOfBirth) + if (authData == SelfAuthData::PlaceOfBirth) { - tryToInsertChild(elem.firstChildElement(QStringLiteral("DateValue")), authData); - } - else if (authData == SelfAuthData::PlaceOfBirth) - { - tryToInsertChild(elem.firstChildElement(QStringLiteral("FreetextPlace")), authData); + tryToInsertChild(subvalue("FreetextPlace"), authData); } else if (authData == SelfAuthData::PlaceOfResidence) { - if (tryToInsertChild(elem.firstChildElement(QStringLiteral("NoPlaceInfo")), SelfAuthData::PlaceOfResidenceNoPlaceInfo)) + if (tryToInsertChild(subvalue("NoPlaceInfo"), SelfAuthData::PlaceOfResidenceNoPlaceInfo)) { mOperationsAllowed.insert(SelfAuthData::PlaceOfResidenceNoPlaceInfo, mOperationsAllowed.value(authData)); } @@ -153,10 +158,10 @@ bool SelfAuthenticationData::SelfData::parsePersonalData(const QDomElement& pEle {QStringLiteral("ZipCode"), SelfAuthData::PlaceOfResidenceZipCode} }; - auto structuredPlace = elem.firstChildElement(QStringLiteral("StructuredPlace")); + const auto structuredPlace = subvalue("StructuredPlace").toObject(); for (auto iter = placeInfo.constBegin(); iter != placeInfo.constEnd(); ++iter) { - if (tryToInsertChild(structuredPlace.firstChildElement(iter.key()), iter.value())) + if (tryToInsertChild(structuredPlace.value(iter.key()), iter.value())) { mOperationsAllowed.insert(iter.value(), mOperationsAllowed.value(authData)); } @@ -165,7 +170,7 @@ bool SelfAuthenticationData::SelfData::parsePersonalData(const QDomElement& pEle } else { - tryToInsertChild(elem, authData); + tryToInsertChild(pObject.value(entry), authData); } } @@ -173,28 +178,15 @@ bool SelfAuthenticationData::SelfData::parsePersonalData(const QDomElement& pEle } -bool SelfAuthenticationData::SelfData::tryToInsertChild(const QDomElement& pElement, SelfAuthData pAuthData) +bool SelfAuthenticationData::SelfData::tryToInsertChild(const QJsonValue& pValue, SelfAuthData pAuthData) { - if (pElement.isNull() || pElement.text().isNull()) + if (pValue.isString()) { - return false; + mSelfAuthData.insert(pAuthData, pValue.toString()); + return true; } - mSelfAuthData.insert(pAuthData, pElement.text()); - return true; -} - - -bool SelfAuthenticationData::SelfData::parse(const QDomDocument& pDoc, const QString& pElementName, const std::function& pParserFunc) -{ - const QDomNodeList nodeList = pDoc.documentElement().elementsByTagName(pElementName); - if (nodeList.size() == 0) - { - qCritical() << "XML parsing failed. No valid values to parse for:" << pElementName; - return false; - } - - return pParserFunc(nodeList.at(0).toElement().firstChildElement()); + return false; } @@ -213,7 +205,7 @@ SelfAuthenticationData::OrderedSelfData SelfAuthenticationData::SelfData::getOrd }; const auto& add = [&](const QString& pKey, const QString& pValue){ -#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) +#if !defined(Q_OS_ANDROID) if (!pKey.isEmpty()) { orderedSelfData << qMakePair(pKey + QLatin1Char(':'), pValue); diff --git a/src/core/SelfAuthenticationData.h b/src/core/SelfAuthenticationData.h index 56e98ec..b52092f 100644 --- a/src/core/SelfAuthenticationData.h +++ b/src/core/SelfAuthenticationData.h @@ -1,5 +1,5 @@ /*! - * \brief Parses self authentication data from XML data and provides its content. + * \brief Parses self authentication data from JSON data and provides its content. * * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany */ @@ -12,7 +12,8 @@ #include #include -#include +#include +#include #include #include #include @@ -66,10 +67,10 @@ class SelfAuthenticationData Q_DECLARE_TR_FUNCTIONS(governikus::SelfData) private: - bool parse(const QDomDocument& pDoc, const QString& pElementName, const std::function& pParserFunc); - bool parseOperationsAllowedByUser(const QDomElement& pElement); - bool parsePersonalData(const QDomElement& pElement); - bool tryToInsertChild(const QDomElement& pElement, SelfAuthData pAuthData); + bool parse(const QByteArray& pData); + bool parseOperationsAllowedByUser(const QJsonObject& pObject); + bool parsePersonalData(const QJsonObject& pObject); + bool tryToInsertChild(const QJsonValue& pValue, SelfAuthData pAuthData); public: bool mValid; @@ -97,4 +98,4 @@ class SelfAuthenticationData OrderedSelfData getOrderedSelfData() const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/SignalHandler.cpp b/src/core/SignalHandler.cpp index c62c569..2c4a4bd 100644 --- a/src/core/SignalHandler.cpp +++ b/src/core/SignalHandler.cpp @@ -40,7 +40,7 @@ void SignalHandler::init() { if (!mInit) { -#if (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) || defined(Q_OS_MACOS) || defined(Q_OS_FREEBSD) +#if (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) || (defined(Q_OS_BSD4) && !defined(Q_OS_IOS)) initUnix(); #elif defined(Q_OS_WIN) && !defined(Q_OS_WINRT) SetConsoleCtrlHandler(PHANDLER_ROUTINE(ctrlHandler), true); @@ -63,7 +63,7 @@ void SignalHandler::quit() if (mAppController) { - QMetaObject::invokeMethod(mAppController.data(), "doShutdown", Qt::QueuedConnection); + QMetaObject::invokeMethod(mAppController.data(), &AppController::doShutdown, Qt::QueuedConnection); } else { @@ -82,7 +82,6 @@ bool SignalHandler::shouldQuit() const // A dummy is required for all platform since moc fails to handle some defines. void SignalHandler::onSignalSocketActivated() { - } diff --git a/src/core/SignalHandler.h b/src/core/SignalHandler.h index b0e623d..0f6643b 100644 --- a/src/core/SignalHandler.h +++ b/src/core/SignalHandler.h @@ -45,7 +45,7 @@ class SignalHandler #elif defined(Q_OS_WIN) private: - static BOOL ctrlHandler(DWORD pCtrlType); + static BOOL __RPC_CALLEE ctrlHandler(DWORD pCtrlType); #endif private Q_SLOTS: @@ -62,4 +62,4 @@ class SignalHandler bool shouldQuit() const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/SignalHandler_win.cpp b/src/core/SignalHandler_win.cpp index 57d1258..02108fc 100644 --- a/src/core/SignalHandler_win.cpp +++ b/src/core/SignalHandler_win.cpp @@ -16,7 +16,7 @@ using namespace governikus; Q_DECLARE_LOGGING_CATEGORY(system) -BOOL SignalHandler::ctrlHandler(DWORD pCtrlType) +BOOL __RPC_CALLEE SignalHandler::ctrlHandler(DWORD pCtrlType) { qCWarning(system) << "Got signal:" << pCtrlType; diff --git a/src/core/TcToken.cpp b/src/core/TcToken.cpp index b20497b..8c7e5c0 100644 --- a/src/core/TcToken.cpp +++ b/src/core/TcToken.cpp @@ -3,9 +3,10 @@ */ -#include "AppSettings.h" #include "TcToken.h" +#include "AppSettings.h" + #include #include #include @@ -36,8 +37,7 @@ TcToken::~TcToken() void TcToken::parse(const QByteArray& pData) { - qDebug() << "Parsing TcToken:"; - qDebug() << pData; + qDebug().noquote() << "Parsing TcToken:\n" << pData; QXmlStreamReader reader(pData); QString binding, pathSecurityProtocol, serverAddress, refreshAddress, communicationErrorAddress; @@ -125,7 +125,7 @@ bool TcToken::valuesAreSchemaConform(const QString& pBinding, if (!pPathSecurityProtocol.isNull() && !QUrl(pPathSecurityProtocol).isValid()) { - qCritical() << "PathSecurity-Protocol is no valid URI " << pPathSecurityProtocol; + qCritical() << "PathSecurity-Protocol is no valid URI:" << pPathSecurityProtocol; } if (pPsk.isNull()) @@ -139,7 +139,6 @@ bool TcToken::valuesAreSchemaConform(const QString& pBinding, qWarning() << "SessionIdentifier is null"; } - if (pServerAddress.isNull() || !isAnyUri(pServerAddress)) { qCritical() << "ServerAddress no valid anyUri:" << pServerAddress; @@ -177,13 +176,13 @@ bool TcToken::isValid() const if (mBinding != QLatin1String("urn:liberty:paos:2006-08")) { - qCritical() << "Wrong binding: " << mBinding; + qCritical() << "Wrong binding:" << mBinding; return false; } if (!mPathSecurityProtocol.isNull() && mPathSecurityProtocol != QLatin1String("urn:ietf:rfc:4279")) { - qCritical() << "Wrong PathSecurity-Protocol: " << mPathSecurityProtocol; + qCritical() << "Wrong PathSecurity-Protocol:" << mPathSecurityProtocol; return false; } @@ -196,7 +195,7 @@ bool TcToken::isValid() const if (mRefreshAddress.scheme() != QLatin1String("https")) { const QString errorInvalidUrl = QStringLiteral("RefreshAddress no valid https url: %1").arg(mRefreshAddress.toString()); - if (AppSettings::getInstance().getGeneralSettings().isDeveloperMode()) + if (Env::getSingleton()->getGeneralSettings().isDeveloperMode()) { qCCritical(developermode) << errorInvalidUrl; } @@ -213,7 +212,6 @@ bool TcToken::isValid() const return false; } - return true; } @@ -265,9 +263,3 @@ const QString& TcToken::getBinding() const { return mBinding; } - - -void TcToken::clearPsk() -{ - mPsk.clear(); -} diff --git a/src/core/TcToken.h b/src/core/TcToken.h index 3d63222..63ffc5a 100644 --- a/src/core/TcToken.h +++ b/src/core/TcToken.h @@ -9,12 +9,17 @@ #include #include +class test_TcToken; +class test_StateGenericSendReceive; + namespace governikus { class TcToken { private: + friend class ::test_TcToken; + friend class ::test_StateGenericSendReceive; bool mSchemaConform; QString mBinding; QString mPathSecurityProtocol; @@ -53,7 +58,6 @@ class TcToken const QUrl& getCommunicationErrorAddress() const; bool usePsk() const; const QByteArray& getPsk() const; - void clearPsk(); bool isSchemaConform() const { @@ -63,4 +67,4 @@ class TcToken }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/context/AuthContext.cpp b/src/core/context/AuthContext.cpp index c214da4..0c53c91 100644 --- a/src/core/context/AuthContext.cpp +++ b/src/core/context/AuthContext.cpp @@ -6,7 +6,6 @@ #include "asn1/Chat.h" #include "AppSettings.h" -#include "Env.h" #include "paos/retrieve/DidAuthenticateEac1Parser.h" #include "SecureStorage.h" @@ -22,7 +21,7 @@ AuthContext::AuthContext(const QSharedPointer& pActivationCon , mTcTokenUrl() , mTcToken() , mRefreshUrl() - , mMessageIdHandler(new MessageIdHandler()) + , mReceivedMessageId() , mStartPaos() , mInitializeFramework() , mInitializeFrameworkResponse() @@ -36,7 +35,6 @@ AuthContext::AuthContext(const QSharedPointer& pActivationCon , mDIDAuthenticateResponseEAC2() , mTransmits() , mTransmitResponses() - , mTransmitResponseFailed(false) , mDisconnectResponse() , mStartPaosResponse() , mEffectiveAccessRights() @@ -52,11 +50,6 @@ AuthContext::AuthContext(const QSharedPointer& pActivationCon } -AuthContext::~AuthContext() -{ -} - - void AuthContext::initializeChat() { Q_ASSERT(mTerminalCvc); @@ -256,20 +249,28 @@ CVCertificateChain AuthContext::getChainStartingWith(const QSharedPointer class test_StateRedirectBrowser; -class test_StatePreVerification; -class test_StateProcessCertificatesFromEac2; -class test_StateCertificateDescriptionCheck; +class test_ChatModel; namespace governikus { @@ -50,20 +47,20 @@ class AuthContext Q_OBJECT private: - friend class ::test_StatePrepareChat; friend class ::test_StateRedirectBrowser; friend class ::test_StatePreVerification; friend class ::test_StateProcessCertificatesFromEac2; friend class ::test_StateCertificateDescriptionCheck; + friend class ::test_ChatModel; bool mTcTokenNotFound; bool mErrorReportedToServer; QSharedPointer mActivationContext; QUrl mTcTokenUrl; - QSharedPointer mTcToken; + QSharedPointer mTcToken; QUrl mRefreshUrl; - QSharedPointer mMessageIdHandler; + QString mReceivedMessageId; QSharedPointer mStartPaos; QSharedPointer mInitializeFramework; QSharedPointer mInitializeFrameworkResponse; @@ -77,7 +74,6 @@ class AuthContext QSharedPointer mDIDAuthenticateResponseEAC2; QVector > mTransmits; QVector > mTransmitResponses; - bool mTransmitResponseFailed; QSharedPointer mDisconnect; QSharedPointer mDisconnectResponse; QSharedPointer mStartPaosResponse; @@ -99,8 +95,6 @@ class AuthContext public: AuthContext(const QSharedPointer& pActivationContext); - virtual ~AuthContext(); - bool isErrorReportedToServer() const { @@ -164,21 +158,27 @@ class AuthContext } - const QSharedPointer& getTcToken() const + const QSharedPointer& getTcToken() const { return mTcToken; } - void setTcToken(const QSharedPointer& pTcToken) + void setTcToken(const QSharedPointer& pTcToken) { mTcToken = pTcToken; } - const QSharedPointer& getMessageIdHandler() const + const QString& getReceivedMessageId() const { - return mMessageIdHandler; + return mReceivedMessageId; + } + + + void setReceivedMessageId(const QString& pReceivedMessageId) + { + mReceivedMessageId = pReceivedMessageId; } @@ -364,18 +364,6 @@ class AuthContext } - bool getTransmitResponseFailed() const - { - return mTransmitResponseFailed; - } - - - void setTransmitResponseFailed(bool pFailed) - { - mTransmitResponseFailed = pFailed; - } - - const QVector >& getTransmits() { return mTransmits; @@ -446,10 +434,10 @@ class AuthContext CVCertificateChain getChainStartingWith(const QSharedPointer& pChainRoot) const; - bool hasChainForCertificationAuthority(const EstablishPACEChannelOutput& pPaceOutput) const; + bool hasChainForCertificationAuthority(const EstablishPaceChannelOutput& pPaceOutput) const; - CVCertificateChain getChainForCertificationAuthority(const EstablishPACEChannelOutput& pPaceOutput) const; + CVCertificateChain getChainForCertificationAuthority(const EstablishPaceChannelOutput& pPaceOutput) const; void initCvcChainBuilder(const QVector >& pAdditionalCertificates = QVector >()); @@ -479,4 +467,4 @@ class AuthContext void setSslSession(const QByteArray& pSession); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/context/ChangePinContext.cpp b/src/core/context/ChangePinContext.cpp index 60d65ef..cd82645 100644 --- a/src/core/context/ChangePinContext.cpp +++ b/src/core/context/ChangePinContext.cpp @@ -7,10 +7,11 @@ using namespace governikus; -ChangePinContext::ChangePinContext() +ChangePinContext::ChangePinContext(bool pRequestTransportPin) : WorkflowContext() , mNewPin() , mSuccessMessage() + , mRequestTransportPin(pRequestTransportPin) { } @@ -31,6 +32,13 @@ void ChangePinContext::setNewPin(const QString& pNewPin) } +void ChangePinContext::resetPacePasswords() +{ + setNewPin(QString()); + WorkflowContext::resetPacePasswords(); +} + + const QString& ChangePinContext::getSuccessMessage() const { return mSuccessMessage; @@ -45,3 +53,9 @@ void ChangePinContext::setSuccessMessage(const QString& pSuccessMessage) Q_EMIT fireSuccessMessageChanged(); } } + + +bool ChangePinContext::requestTransportPin() const +{ + return mRequestTransportPin; +} diff --git a/src/core/context/ChangePinContext.h b/src/core/context/ChangePinContext.h index f2b114c..67f57e0 100644 --- a/src/core/context/ChangePinContext.h +++ b/src/core/context/ChangePinContext.h @@ -18,19 +18,24 @@ class ChangePinContext QString mNewPin; QString mSuccessMessage; + const bool mRequestTransportPin; public: - ChangePinContext(); + explicit ChangePinContext(bool pRequestTransportPin = false); const QString& getNewPin() const; void setNewPin(const QString& pNewPin); + void resetPacePasswords() override; + const QString& getSuccessMessage() const; void setSuccessMessage(const QString& pSuccessMessage); + bool requestTransportPin() const; + Q_SIGNALS: void fireNewPinChanged(); void fireSuccessMessageChanged(); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/context/DiagnosisContext.cpp b/src/core/context/DiagnosisContext.cpp index 0627652..9fbb79f 100644 --- a/src/core/context/DiagnosisContext.cpp +++ b/src/core/context/DiagnosisContext.cpp @@ -44,3 +44,17 @@ void DiagnosisContext::setTimestamp(const QDateTime& pTimestamp) Q_EMIT timestampChanged(); } + + +void DiagnosisContext::setNetworkInterfaces(const QList& pNetworkInterface) +{ + mNetworkInterfaces = pNetworkInterface; + + Q_EMIT fireNetworkInfoChanged(); +} + + +const QList& DiagnosisContext::getNetworkInterfaces() const +{ + return mNetworkInterfaces; +} diff --git a/src/core/context/DiagnosisContext.h b/src/core/context/DiagnosisContext.h index 801c514..2ac40af 100644 --- a/src/core/context/DiagnosisContext.h +++ b/src/core/context/DiagnosisContext.h @@ -6,13 +6,14 @@ #pragma once +#include "ReaderInfo.h" + #include +#include #include #include #include -#include "ReaderInfo.h" - namespace governikus { @@ -30,6 +31,7 @@ class DiagnosisContext QVector mPcscDrivers; QVector mReaderInfos; QDateTime mTimestamp; + QList mNetworkInterfaces; public: DiagnosisContext(); @@ -70,12 +72,17 @@ class DiagnosisContext void setTimestamp(const QDateTime& pTimestamp); + void setNetworkInterfaces(const QList& pNetworkInterface); + const QList& getNetworkInterfaces() const; + Q_SIGNALS: void pcscVersionChanged(); void readerInfosChanged(); void timestampChanged(); void pcscInfoChanged(); void modelChanged(); + void fireDataChanged(); + void fireNetworkInfoChanged(); }; @@ -123,4 +130,4 @@ class DiagnosisContext::ComponentInfo }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/context/RemoteServiceContext.cpp b/src/core/context/RemoteServiceContext.cpp index 613d739..65527fd 100644 --- a/src/core/context/RemoteServiceContext.cpp +++ b/src/core/context/RemoteServiceContext.cpp @@ -13,12 +13,9 @@ RemoteServiceContext::RemoteServiceContext() : mRemoteServer(Env::create()) , mNewPin() , mEstablishPaceChannelMessage() + , mEstablishPaceChannelOutput() , mModifyPinMessage() -{ -} - - -RemoteServiceContext::~RemoteServiceContext() + , mModifyPinMessageResponseApdu() { } @@ -52,6 +49,9 @@ void RemoteServiceContext::setNewPin(const QString& pNewPin) void RemoteServiceContext::setEstablishPaceChannelMessage(const QSharedPointer& pMessage) { mEstablishPaceChannelMessage = pMessage; + mEstablishPaceChannelOutput = EstablishPaceChannelOutput(); + + Q_EMIT fireEstablishPaceChannelMessageUpdated(mEstablishPaceChannelMessage); } @@ -61,9 +61,22 @@ const QSharedPointer& RemoteServiceContext::getEs } +void RemoteServiceContext::setEstablishPaceChannelOutput(EstablishPaceChannelOutput pEstablishPaceChannelOutput) +{ + mEstablishPaceChannelOutput = pEstablishPaceChannelOutput; +} + + +const EstablishPaceChannelOutput& RemoteServiceContext::getEstablishPaceChannelOutput() const +{ + return mEstablishPaceChannelOutput; +} + + void RemoteServiceContext::setModifyPinMessage(const QSharedPointer& pMessage) { mModifyPinMessage = pMessage; + mModifyPinMessageResponseApdu = ResponseApdu(); } @@ -73,14 +86,30 @@ const QSharedPointer& RemoteServiceContext::getModifyPinMess } +void RemoteServiceContext::setModifyPinMessageResponseApdu(const ResponseApdu& pModifyPinMessageResponseApdu) +{ + mModifyPinMessageResponseApdu = pModifyPinMessageResponseApdu; +} + + +const ResponseApdu& RemoteServiceContext::getModifyPinMessageResponseApdu() const +{ + return mModifyPinMessageResponseApdu; +} + + +void RemoteServiceContext::resetPacePasswords() +{ + setNewPin(QString()); + WorkflowContext::resetPacePasswords(); +} + + void RemoteServiceContext::onResetMessageHandler() { - setCardConnection(QSharedPointer()); - setCan(QString()); - setPin(QString()); - setPuk(QString()); - setNewPin(QString()); - resetLastPaceResultAndRetryCounter(); - mEstablishPaceChannelMessage = QSharedPointer(); + resetPacePasswords(); + resetCardConnection(); + resetLastPaceResult(); + setEstablishPaceChannelMessage(QSharedPointer()); mModifyPinMessage = QSharedPointer(); } diff --git a/src/core/context/RemoteServiceContext.h b/src/core/context/RemoteServiceContext.h index e682f5e..831348e 100644 --- a/src/core/context/RemoteServiceContext.h +++ b/src/core/context/RemoteServiceContext.h @@ -6,6 +6,7 @@ #pragma once +#include "EstablishPaceChannelOutput.h" #include "messages/IfdEstablishPaceChannel.h" #include "messages/IfdModifyPin.h" #include "RemoteServer.h" @@ -14,6 +15,7 @@ #include + namespace governikus { @@ -27,14 +29,16 @@ class RemoteServiceContext QString mNewPin; QSharedPointer mEstablishPaceChannelMessage; + EstablishPaceChannelOutput mEstablishPaceChannelOutput; QSharedPointer mModifyPinMessage; + ResponseApdu mModifyPinMessageResponseApdu; Q_SIGNALS: void fireCancelPasswordRequest(); + void fireEstablishPaceChannelMessageUpdated(const QSharedPointer& pMessage); public: RemoteServiceContext(); - virtual ~RemoteServiceContext(); const QSharedPointer& getRemoteServer() const; bool isRunning() const; @@ -45,11 +49,19 @@ class RemoteServiceContext void setEstablishPaceChannelMessage(const QSharedPointer& pMessage); const QSharedPointer& getEstablishPaceChannelMessage() const; + void setEstablishPaceChannelOutput(EstablishPaceChannelOutput pEstablishPaceChannelOutput); + const EstablishPaceChannelOutput& getEstablishPaceChannelOutput() const; + void setModifyPinMessage(const QSharedPointer& pMessage); const QSharedPointer& getModifyPinMessage() const; + void setModifyPinMessageResponseApdu(const ResponseApdu& pModifyPinMessageResponseApdu); + const ResponseApdu& getModifyPinMessageResponseApdu() const; + + void resetPacePasswords() override; + public Q_SLOTS: void onResetMessageHandler(); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/context/SelfAuthContext.cpp b/src/core/context/SelfAuthContext.cpp index 8c9d6fc..f56992e 100644 --- a/src/core/context/SelfAuthContext.cpp +++ b/src/core/context/SelfAuthContext.cpp @@ -11,8 +11,3 @@ SelfAuthContext::SelfAuthContext() , mSelfAuthenticationData() { } - - -SelfAuthContext::~SelfAuthContext() -{ -} diff --git a/src/core/context/SelfAuthContext.h b/src/core/context/SelfAuthContext.h index d16bca3..6dc63a3 100644 --- a/src/core/context/SelfAuthContext.h +++ b/src/core/context/SelfAuthContext.h @@ -26,8 +26,6 @@ class SelfAuthContext public: SelfAuthContext(); - virtual ~SelfAuthContext(); - const SelfAuthenticationData& getSelfAuthenticationData() const { @@ -44,4 +42,4 @@ class SelfAuthContext }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/context/WorkflowContext.cpp b/src/core/context/WorkflowContext.cpp index 30809b2..5924e9a 100644 --- a/src/core/context/WorkflowContext.cpp +++ b/src/core/context/WorkflowContext.cpp @@ -4,35 +4,52 @@ #include "WorkflowContext.h" -#include "FuncUtils.h" #include "ReaderManager.h" +#include + using namespace governikus; + +void WorkflowContext::onWorkflowCancelled() +{ + mWorkflowCancelled = true; +} + + WorkflowContext::WorkflowContext() : QObject() , mStateApproved(false) + , mWorkflowKilled(false) , mCurrentState() , mReaderPlugInTypes() , mReaderName() , mCardConnection() + , mCardVanishedDuringPacePinCount(0) + , mCardVanishedDuringPacePinTimer() , mCan() , mPin() , mPuk() + , mEstablishPaceChannelType(PacePasswordId::UNKNOWN) , mPaceOutputData() - , mOldRetryCounter(-1) + , mExpectedReaderName() + , mExpectedRetryCounter(-1) , mLastPaceResult(CardReturnCode::OK) , mStatus(GlobalStatus::Code::No_Error) + , mStartPaosResult(ECardApiResult::createOk()) , mErrorReportedToUser(true) + , mPaceResultReportedToUser(false) , mWorkflowFinished(false) + , mWorkflowCancelled(false) , mCanAllowedMode(false) { + connect(this, &WorkflowContext::fireCancelWorkflow, this, &WorkflowContext::onWorkflowCancelled); } bool WorkflowContext::isErrorReportedToUser() const { - return mErrorReportedToUser; + return mErrorReportedToUser || mWorkflowKilled; } @@ -42,6 +59,18 @@ void WorkflowContext::setErrorReportedToUser(bool pErrorReportedToUser) } +bool WorkflowContext::isPaceResultReportedToUser() const +{ + return mPaceResultReportedToUser; +} + + +void WorkflowContext::setPaceResultReportedToUser(bool pReported) +{ + mPaceResultReportedToUser = pReported; +} + + void WorkflowContext::setStateApproved(bool pApproved) { if (mStateApproved != pApproved) @@ -52,7 +81,27 @@ void WorkflowContext::setStateApproved(bool pApproved) } -bool WorkflowContext::isStateApproved() +void WorkflowContext::killWorkflow() +{ + qWarning() << "Killing the current workflow."; + mWorkflowKilled = true; + mStatus = GlobalStatus::Code::Card_Cancellation_By_User; + if (!mStateApproved) + { + setStateApproved(true); + } + + Q_EMIT fireCancelWorkflow(); +} + + +bool WorkflowContext::isWorkflowKilled() const +{ + return mWorkflowKilled; +} + + +bool WorkflowContext::isStateApproved() const { return mStateApproved; } @@ -68,6 +117,11 @@ void WorkflowContext::setCurrentState(const QString& pNewState) { mCurrentState = pNewState; Q_EMIT fireStateChanged(pNewState); + + if (mWorkflowKilled) + { + setStateApproved(true); + } } @@ -119,6 +173,52 @@ void WorkflowContext::setCardConnection(const QSharedPointer& pC } +void WorkflowContext::resetCardConnection() +{ + setCardConnection(QSharedPointer()); + if (!mReaderName.isEmpty()) + { + const auto readerManager = Env::getSingleton(); + readerManager->updateReaderInfo(mReaderName); + } +} + + +bool WorkflowContext::isNpaRepositioningRequired() const +{ + if (mCardVanishedDuringPacePinCount >= 10) + { + return true; + } + + const qint64 fourSeconds = 4000; + if (mCardVanishedDuringPacePinTimer.isValid() && mCardVanishedDuringPacePinTimer.elapsed() > fourSeconds) + { + return true; + } + + return false; +} + + +void WorkflowContext::setNpaPositionVerified() +{ + mCardVanishedDuringPacePinCount = 0; + mCardVanishedDuringPacePinTimer.invalidate(); +} + + +void WorkflowContext::handleWrongNpaPosition() +{ + if (mCardVanishedDuringPacePinCount > 0 && !mCardVanishedDuringPacePinTimer.isValid()) + { + mCardVanishedDuringPacePinTimer.restart(); + } + mCardVanishedDuringPacePinCount += 1; + resetCardConnection(); +} + + bool WorkflowContext::isPinBlocked() { return mCardConnection != nullptr && mCardConnection->getReaderInfo().getRetryCounter() == 0; @@ -173,15 +273,35 @@ void WorkflowContext::setPin(const QString& pPin) } -EstablishPACEChannelOutput* WorkflowContext::getPaceOutputData() const +PacePasswordId WorkflowContext::getEstablishPaceChannelType() const +{ + return mEstablishPaceChannelType; +} + + +void WorkflowContext::setEstablishPaceChannelType(PacePasswordId pType) +{ + mEstablishPaceChannelType = pType; +} + + +void WorkflowContext::resetPacePasswords() +{ + setCan(QString()); + setPin(QString()); + setPuk(QString()); +} + + +EstablishPaceChannelOutput* WorkflowContext::getPaceOutputData() const { return mPaceOutputData.data(); } -void WorkflowContext::setPaceOutputData(const EstablishPACEChannelOutput& pPaceOutputData) +void WorkflowContext::setPaceOutputData(const EstablishPaceChannelOutput& pPaceOutputData) { - mPaceOutputData.reset(new EstablishPACEChannelOutput(pPaceOutputData)); + mPaceOutputData.reset(new EstablishPaceChannelOutput(pPaceOutputData)); } @@ -191,27 +311,44 @@ CardReturnCode WorkflowContext::getLastPaceResult() const } -int WorkflowContext::getOldRetryCounter() const +void WorkflowContext::setLastPaceResult(CardReturnCode pLastPaceResult) { - return mOldRetryCounter; -} - - -void WorkflowContext::setLastPaceResultAndRetryCounter(CardReturnCode pLastPaceResult, int pOldRetryCounter) -{ - if (mLastPaceResult != pLastPaceResult || mOldRetryCounter != pOldRetryCounter) + mPaceResultReportedToUser = false; + if (mLastPaceResult != pLastPaceResult) { mLastPaceResult = pLastPaceResult; - mOldRetryCounter = pOldRetryCounter; Q_EMIT fireLastPaceResultChanged(); } } -void WorkflowContext::resetLastPaceResultAndRetryCounter() +void WorkflowContext::resetLastPaceResult() { - mOldRetryCounter = -1; - mLastPaceResult = CardReturnCode::OK; + setLastPaceResult(CardReturnCode::OK); +} + + +bool WorkflowContext::isExpectedReader() const +{ + return mExpectedReaderName == mReaderName; +} + + +void WorkflowContext::rememberReader() +{ + mExpectedReaderName = mReaderName; +} + + +int WorkflowContext::getExpectedRetryCounter() const +{ + return mExpectedRetryCounter; +} + + +void WorkflowContext::setExpectedRetryCounter(int pExpectedRetryCounter) +{ + mExpectedRetryCounter = pExpectedRetryCounter; } @@ -221,16 +358,23 @@ const GlobalStatus& WorkflowContext::getStatus() const } -void WorkflowContext::setStatus(const GlobalStatus& pStatus, bool pReportToUser) +void WorkflowContext::setStatus(const GlobalStatus& pStatus) { - const bool forceReport = mStatus.isNoError() && pStatus.isError(); - mStatus = pStatus; - if (pReportToUser || forceReport) - { - mErrorReportedToUser = false; - Q_EMIT fireResultChanged(); - } + mErrorReportedToUser = false; + Q_EMIT fireResultChanged(); +} + + +const ECardApiResult WorkflowContext::getStartPaosResult() const +{ + return mStartPaosResult; +} + + +void WorkflowContext::setStartPaosResult(const ECardApiResult& pStartPaosResult) +{ + mStartPaosResult = pStartPaosResult; } @@ -246,6 +390,12 @@ void WorkflowContext::setWorkflowFinished(bool pWorkflowFinished) } +bool WorkflowContext::isWorkflowCancelled() const +{ + return mWorkflowCancelled; +} + + bool WorkflowContext::isCanAllowedMode() const { return mCanAllowedMode; diff --git a/src/core/context/WorkflowContext.h b/src/core/context/WorkflowContext.h index de23247..f771d1e 100644 --- a/src/core/context/WorkflowContext.h +++ b/src/core/context/WorkflowContext.h @@ -7,12 +7,16 @@ #pragma once #include "CardConnection.h" +#include "ECardApiResult.h" #include "GlobalStatus.h" -#include "Result.h" +#include "SmartCardDefinitions.h" +#include #include #include +class test_WorkflowContext; + namespace governikus { @@ -22,24 +26,33 @@ class WorkflowContext Q_OBJECT private: + friend class ::test_WorkflowContext; bool mStateApproved; + bool mWorkflowKilled; QString mCurrentState; QVector mReaderPlugInTypes; QString mReaderName; QSharedPointer mCardConnection; + int mCardVanishedDuringPacePinCount; + QElapsedTimer mCardVanishedDuringPacePinTimer; QString mCan; QString mPin; QString mPuk; - QScopedPointer mPaceOutputData; - int mOldRetryCounter; + PacePasswordId mEstablishPaceChannelType; + QScopedPointer mPaceOutputData; + QString mExpectedReaderName; + int mExpectedRetryCounter; CardReturnCode mLastPaceResult; GlobalStatus mStatus; + ECardApiResult mStartPaosResult; bool mErrorReportedToUser; + bool mPaceResultReportedToUser; bool mWorkflowFinished; + bool mWorkflowCancelled; bool mCanAllowedMode; - protected: - void resetLastPaceResultAndRetryCounter(); + private Q_SLOTS: + void onWorkflowCancelled(); Q_SIGNALS: void fireStateApprovedChanged(); @@ -63,8 +76,14 @@ class WorkflowContext bool isErrorReportedToUser() const; void setErrorReportedToUser(bool pErrorReportedToUser = true); + bool isPaceResultReportedToUser() const; + void setPaceResultReportedToUser(bool pReported = true); + void setStateApproved(bool pApproved = true); - bool isStateApproved(); + bool isStateApproved() const; + + void killWorkflow(); + bool isWorkflowKilled() const; const QString& getCurrentState() const; void setCurrentState(const QString& pNewState); @@ -77,6 +96,11 @@ class WorkflowContext const QSharedPointer& getCardConnection() const; void setCardConnection(const QSharedPointer& pCardConnection); + void resetCardConnection(); + + bool isNpaRepositioningRequired() const; + void setNpaPositionVerified(); + void handleWrongNpaPosition(); const QString& getPuk() const; void setPuk(const QString& pPuk); @@ -87,22 +111,38 @@ class WorkflowContext const QString& getPin() const; void setPin(const QString& pPin); - EstablishPACEChannelOutput* getPaceOutputData() const; - void setPaceOutputData(const EstablishPACEChannelOutput& pPaceOutputData); + PacePasswordId getEstablishPaceChannelType() const; + void setEstablishPaceChannelType(PacePasswordId pType); + + virtual void resetPacePasswords(); + + EstablishPaceChannelOutput* getPaceOutputData() const; + void setPaceOutputData(const EstablishPaceChannelOutput& pPaceOutputData); bool isPinBlocked(); CardReturnCode getLastPaceResult() const; - int getOldRetryCounter() const; - void setLastPaceResultAndRetryCounter(CardReturnCode pLastPaceResult, int pOldRetryCounter); + void setLastPaceResult(CardReturnCode pLastPaceResult); + void resetLastPaceResult(); + + bool isExpectedReader() const; + void rememberReader(); + + int getExpectedRetryCounter() const; + void setExpectedRetryCounter(int pExpectedRetryCounter); const GlobalStatus& getStatus() const; - void setStatus(const GlobalStatus& pResult, bool pReportToUser = true); + void setStatus(const GlobalStatus& pResult); + + const ECardApiResult getStartPaosResult() const; + void setStartPaosResult(const ECardApiResult& pStartPaosResult); bool isWorkflowFinished() const; void setWorkflowFinished(bool pWorkflowFinished); + bool isWorkflowCancelled() const; + bool isCanAllowedMode() const; void setCanAllowedMode(bool pCanAllowedMode); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/controller/AppController.cpp b/src/core/controller/AppController.cpp index 0419144..87b5e20 100644 --- a/src/core/controller/AppController.cpp +++ b/src/core/controller/AppController.cpp @@ -14,25 +14,31 @@ #include "controller/ChangePinController.h" #include "controller/RemoteServiceController.h" #include "controller/SelfAuthController.h" -#include "Env.h" #include "HttpServerRequestor.h" #include "HttpServerStatusParser.h" #include "LanguageLoader.h" #include "LogHandler.h" #include "NetworkManager.h" #include "ReaderManager.h" +#include "RemoteClient.h" #include "ResourceLoader.h" -#include "view/UILoader.h" -#include "view/UIPlugIn.h" +#include "UILoader.h" +#include "UIPlugIn.h" #include #include #include +#if defined(Q_OS_WIN) +#include +#endif + namespace governikus { -class WorkflowRequest +bool AppController::cShowUi = false; + +class WorkflowRequest final { private: const Action mAction; @@ -46,7 +52,7 @@ class WorkflowRequest inline QSharedPointer getContext() const; }; -} +} // namespace governikus using namespace governikus; @@ -86,10 +92,16 @@ AppController::AppController() : mCurrentAction(Action::NONE) , mWaitingRequest() , mActiveController() + , mShutdownRunning(false) + , mUiDomination(nullptr) { setObjectName(QStringLiteral("AppController")); - connect(&AppSettings::getInstance().getGeneralSettings(), &GeneralSettings::fireSettingsChanged, this, &AppController::onSettingsChanged, Qt::DirectConnection); +#if defined(Q_OS_WIN) + QCoreApplication::instance()->installNativeEventFilter(this); +#endif + + connect(&Env::getSingleton()->getGeneralSettings(), &GeneralSettings::fireSettingsChanged, this, &AppController::onSettingsChanged, Qt::DirectConnection); onSettingsChanged(); ResourceLoader::getInstance().init(); @@ -111,23 +123,36 @@ AppController::~AppController() bool AppController::eventFilter(QObject* pObj, QEvent* pEvent) { #ifdef Q_OS_MACOS - // This event gets send on reopen events in macOS (opening the app again while - // it is in the background). Currently the event handling is only needed on macOS. - if (pEvent && pEvent->type() == QEvent::ApplicationActivate) + if (pEvent) { - qCDebug(system) << "Got an ApplicationActivate event, showing current UI Workflow."; - Q_EMIT fireShowUi(UiModule::CURRENT); + // This event gets sent on reopen events in macOS (opening the app again while + // it is in the background). Currently the event handling is only needed on macOS. + if (pEvent->type() == QEvent::ApplicationActivate) + { + qCDebug(system) << "Got an ApplicationActivate event, showing current UI Workflow."; + Q_EMIT fireShowUi(UiModule::CURRENT); + } + else if (pEvent->type() == QEvent::Close && pObj == QCoreApplication::instance()) + { + qCDebug(system) << "Got CloseEvent from system, hiding UI."; + Q_EMIT fireHideUi(); + } } #endif + return QObject::eventFilter(pObj, pEvent); } bool AppController::start() { - ReaderManager::getInstance().init(QSharedPointer(Env::create())); - connect(this, &AppController::fireShutdown, &ReaderManager::getInstance(), &ReaderManager::shutdown, Qt::DirectConnection); - connect(&ReaderManager::getInstance(), &ReaderManager::fireInitialized, this, &AppController::fireStarted, Qt::QueuedConnection); + // Force construction of RemoteClient in the MainThread + Env::getSingleton(); + + const auto readerManager = Env::getSingleton(); + readerManager->init(); + connect(this, &AppController::fireShutdown, readerManager, &ReaderManager::shutdown, Qt::QueuedConnection); + connect(readerManager, &ReaderManager::fireInitialized, this, &AppController::fireStarted, Qt::QueuedConnection); connect(&UILoader::getInstance(), &UILoader::fireLoadedPlugin, this, &AppController::onUiPlugin); if (!UILoader::getInstance().load()) @@ -151,6 +176,13 @@ bool AppController::start() qDebug() << "Successfully started activation handler:" << handler; } + connect(this, &AppController::fireStarted, this, [this] { + if (cShowUi) + { + Q_EMIT fireShowUi(UiModule::DEFAULT); + } + }, Qt::QueuedConnection); + QCoreApplication::instance()->installEventFilter(this); return true; @@ -178,22 +210,22 @@ void AppController::onWorkflowFinished() const QSharedPointer context = mWaitingRequest->getContext().objectCast(); Q_ASSERT(context); startNewWorkflow(Action::PIN, context); + break; } - break; case Action::AUTH: { const QSharedPointer context = mWaitingRequest->getContext().objectCast(); Q_ASSERT(context); startNewWorkflow(Action::AUTH, context); + break; } - break; case Action::READER_SETTINGS: { Q_EMIT fireShowReaderSettings(); + break; } - break; default: qWarning() << "Waiting action is unhandled!"; @@ -202,6 +234,11 @@ void AppController::onWorkflowFinished() mWaitingRequest.reset(); } + + if (mShutdownRunning) + { + completeShutdown(); + } } @@ -209,18 +246,18 @@ void AppController::onCloseReminderFinished(bool pDontRemindAgain) { if (pDontRemindAgain) { - AppSettings::getInstance().getGeneralSettings().setRemindUserToClose(false); - AppSettings::getInstance().getGeneralSettings().save(); + Env::getSingleton()->getGeneralSettings().setRemindUserToClose(false); + Env::getSingleton()->getGeneralSettings().save(); } } void AppController::onChangePinRequested() { - qDebug() << "PIN change requested"; - const QSharedPointer context(new ChangePinContext); if (canStartNewAction()) { + qDebug() << "PIN change requested"; + const auto& context = QSharedPointer::create(); startNewWorkflow(Action::PIN, context); return; @@ -228,6 +265,8 @@ void AppController::onChangePinRequested() if (mWaitingRequest.isNull()) { + qDebug() << "PIN change enqueued"; + const auto& context = QSharedPointer::create(true); mWaitingRequest.reset(new WorkflowRequest(Action::PIN, context)); return; @@ -237,32 +276,12 @@ void AppController::onChangePinRequested() } -void AppController::onSwitchToReaderSettingsRequested() -{ - if (canStartNewAction()) - { - Q_EMIT fireShowReaderSettings(); - - return; - } - - if (mWaitingRequest.isNull()) - { - mWaitingRequest.reset(new WorkflowRequest(Action::READER_SETTINGS, QSharedPointer())); - - return; - } - - qWarning() << "Cannot enqueue action" << Action::READER_SETTINGS << ", queue is already full."; -} - - void AppController::onSelfAuthenticationRequested() { - qDebug() << "self authentication requested"; + qDebug() << "Self authentication requested"; if (canStartNewAction()) { - const QSharedPointer context(new SelfAuthContext()); + const auto& context = QSharedPointer::create(); startNewWorkflow(Action::SELF, context); } } @@ -270,8 +289,8 @@ void AppController::onSelfAuthenticationRequested() void AppController::onAuthenticationRequest(const QSharedPointer& pActivationContext) { - qDebug() << "authentication requested"; - const QSharedPointer authContext(new AuthContext(pActivationContext)); + qDebug() << "Authentication requested"; + const auto& authContext = QSharedPointer::create(pActivationContext); if (canStartNewAction()) { startNewWorkflow(Action::AUTH, authContext); @@ -282,8 +301,9 @@ void AppController::onAuthenticationRequest(const QSharedPointer activeContext = mActiveController->getContext(); Q_ASSERT(!activeContext.isNull()); - if (activeContext->isWorkflowFinished()) + if (activeContext->isWorkflowFinished() && activeContext->getStatus().isNoError()) { + qDebug() << "Auto-approving the current state"; if (mWaitingRequest.isNull()) { mWaitingRequest.reset(new WorkflowRequest(Action::AUTH, authContext)); @@ -298,7 +318,7 @@ void AppController::onAuthenticationRequest(const QSharedPointersendOperationAlreadyActive()) { - qCritical() << "Cannot send \"Operation already active\" to caller: " << pActivationContext->getSendError(); + qCritical() << "Cannot send \"Operation already active\" to caller:" << pActivationContext->getSendError(); } } @@ -308,7 +328,7 @@ void AppController::onRemoteServiceRequested() qDebug() << "remote service requested"; if (canStartNewAction()) { - const QSharedPointer context(new RemoteServiceContext()); + const auto& context = QSharedPointer::create(); startNewWorkflow(Action::REMOTE_SERVICE, context); } } @@ -317,7 +337,7 @@ void AppController::onRemoteServiceRequested() void AppController::onSettingsChanged() { LanguageLoader& languageLoader = LanguageLoader::getInstance(); - const QLocale& newLocale = QLocale(AppSettings::getInstance().getGeneralSettings().getLanguage()); + const QLocale& newLocale = QLocale(Env::getSingleton()->getGeneralSettings().getLanguage()); const QLocale& usedLocale = languageLoader.getUsedLocale(); if (newLocale == usedLocale) @@ -342,18 +362,51 @@ void AppController::onSettingsChanged() void AppController::doShutdown() +{ + mShutdownRunning = true; + if (mActiveController) + { + // Make sure that any request for a new workflow is removed from the queue. + mWaitingRequest.reset(); + + // Make sure running workflow is not blocked by any open dialogs + // (hiding the GUI also closes open dialogs). + Q_EMIT fireHideUi(); + + // Make sure the workflow runs to the end without user interaction. + const QSharedPointer context = mActiveController->getContext(); + if (context) + { + context->killWorkflow(); + } + } + else + { + // Make sure the GUI is not blocked by any open dialogs + // (hiding the GUI also closes open dialogs). + Q_EMIT fireHideUi(); + + completeShutdown(); + } +} + + +void AppController::completeShutdown() { qDebug() << "Emit fire shutdown"; Q_EMIT fireShutdown(); + ResourceLoader::getInstance().shutdown(); + for (const auto& handler : ActivationHandler::getInstances()) + { + handler->stop(); + } + QTimer* timer = new QTimer(); static const int TIMER_INTERVAL = 50; timer->setInterval(TIMER_INTERVAL); - connect(timer, &QTimer::timeout, [ = ](){ - // Only wait for connections created by the global network manager here. We assume - // that each context has already cleaned up its own connections before discarding - // their NetworkManager instances. + connect(timer, &QTimer::timeout, this, [ = ](){ const int openConnectionCount = Env::getSingleton()->getOpenConnectionCount(); if (openConnectionCount > 0) { @@ -363,38 +416,77 @@ void AppController::doShutdown() { return; } + qWarning() << "Closing with" << openConnectionCount << "pending network connections."; + Q_ASSERT(false); } timer->deleteLater(); - qDebug() << "Quit event loop of QCoreApplication"; - qApp->quit(); + + connect(&UILoader::getInstance(), &UILoader::fireShutdownComplete, this, &AppController::onUILoaderShutdownComplete, Qt::QueuedConnection); + QMetaObject::invokeMethod(&UILoader::getInstance(), &UILoader::shutdown, Qt::QueuedConnection); }); + timer->start(); } +void AppController::onUILoaderShutdownComplete() +{ + qDebug() << "Quit event loop of QCoreApplication"; + qApp->quit(); +} + + +void AppController::onUiDominationRequested(const UIPlugIn* pUi, const QString& pInformation) +{ + bool accepted = false; + if (!mUiDomination && mCurrentAction == Action::NONE) + { + mUiDomination = pUi; + accepted = true; + } + + qDebug() << pUi->metaObject()->className() << "requested ui domination:" << pInformation << "|" << accepted; + Q_EMIT fireUiDomination(pUi, pInformation, accepted); +} + + +void AppController::onUiDominationRelease() +{ + if (mUiDomination) + { + mUiDomination = nullptr; + Q_EMIT fireUiDominationReleased(); + } +} + + void AppController::onUiPlugin(UIPlugIn* pPlugin) { qDebug() << "Register UI:" << pPlugin->metaObject()->className(); - connect(this, &AppController::fireShutdown, pPlugin, &UIPlugIn::doShutdown, Qt::DirectConnection); + connect(this, &AppController::fireShutdown, pPlugin, &UIPlugIn::doShutdown, Qt::QueuedConnection); connect(this, &AppController::fireWorkflowStarted, pPlugin, &UIPlugIn::onWorkflowStarted); connect(this, &AppController::fireWorkflowFinished, pPlugin, &UIPlugIn::onWorkflowFinished); connect(this, &AppController::fireStarted, pPlugin, &UIPlugIn::onApplicationStarted); connect(this, &AppController::fireShowUi, pPlugin, &UIPlugIn::onShowUi); + connect(this, &AppController::fireHideUi, pPlugin, &UIPlugIn::onHideUi); connect(this, &AppController::fireShowUserInformation, pPlugin, &UIPlugIn::fireShowUserInformation); connect(this, &AppController::fireShowReaderSettings, pPlugin, &UIPlugIn::onShowReaderSettings); + connect(this, &AppController::fireUiDomination, pPlugin, &UIPlugIn::onUiDomination); + connect(this, &AppController::fireUiDominationReleased, pPlugin, &UIPlugIn::onUiDominationReleased); #ifndef QT_NO_NETWORKPROXY connect(this, &AppController::fireProxyAuthenticationRequired, pPlugin, &UIPlugIn::onProxyAuthenticationRequired); #endif connect(pPlugin, &UIPlugIn::fireChangePinRequest, this, &AppController::onChangePinRequested, Qt::QueuedConnection); - connect(pPlugin, &UIPlugIn::fireSwitchToReaderSettingsRequested, this, &AppController::onSwitchToReaderSettingsRequested, Qt::QueuedConnection); connect(pPlugin, &UIPlugIn::fireSelfAuthenticationRequested, this, &AppController::onSelfAuthenticationRequested, Qt::QueuedConnection); connect(pPlugin, &UIPlugIn::fireRemoteServiceRequested, this, &AppController::onRemoteServiceRequested, Qt::QueuedConnection); connect(pPlugin, &UIPlugIn::fireQuitApplicationRequest, this, &AppController::doShutdown); connect(pPlugin, &UIPlugIn::fireCloseReminderFinished, this, &AppController::onCloseReminderFinished); + connect(pPlugin, &UIPlugIn::fireUiDominationRequest, this, &AppController::onUiDominationRequested); + connect(pPlugin, &UIPlugIn::fireUiDominationRelease, this, &AppController::onUiDominationRelease); } @@ -412,7 +504,7 @@ bool AppController::startNewWorkflow(Action pAction, const QSharedPointer()->resetBacklog(); qDebug() << "Start" << mActiveController->metaObject()->className(); //first: run new active controller so that it can be canceled during UI activation if required @@ -423,3 +515,25 @@ bool AppController::startNewWorkflow(Action pAction, const QSharedPointer(pMessage); + if (msg->message == WM_QUERYENDSESSION) + { + qDebug() << "WM_QUERYENDSESSION received"; + Q_EMIT fireHideUi(); + } + } +#endif + + return false; +} diff --git a/src/core/controller/AppController.h b/src/core/controller/AppController.h index 21bde26..21c5973 100644 --- a/src/core/controller/AppController.h +++ b/src/core/controller/AppController.h @@ -9,10 +9,9 @@ #include "ActivationHandler.h" #include "EnumHelper.h" +#include #include -class QAuthenticator; -class QNetworkProxy; namespace governikus { @@ -26,14 +25,13 @@ defineEnumType(Action, REMOTE_SERVICE) -class UIPlugIn; -class WorkflowContext; class WorkflowController; class WorkflowRequest; +class CommandLineParser; - -class AppController +class AppController final : public QObject + , public QAbstractNativeEventFilter { Q_OBJECT @@ -41,18 +39,24 @@ class AppController Q_DISABLE_COPY(AppController) friend class SignalHandler; + friend class CommandLineParser; + static bool cShowUi; Action mCurrentAction; QScopedPointer mWaitingRequest; QScopedPointer mActiveController; + bool mShutdownRunning; + const UIPlugIn* mUiDomination; bool canStartNewAction(); + void completeShutdown(); public: AppController(); virtual ~AppController() override; virtual bool eventFilter(QObject* pObj, QEvent* pEvent) override; + bool nativeEventFilter(const QByteArray& pEventType, void* pMessage, long* pResult) override; bool start(); @@ -62,11 +66,14 @@ class AppController void fireWorkflowStarted(QSharedPointer pContext); void fireWorkflowFinished(QSharedPointer pContext); void fireShowUi(UiModule pModule); + void fireHideUi(); void fireShowUserInformation(const QString& pInformationMessage); void fireShowReaderSettings(); #ifndef QT_NO_NETWORKPROXY void fireProxyAuthenticationRequired(const QNetworkProxy& pProxy, QAuthenticator* pAuthenticator); #endif + void fireUiDomination(const UIPlugIn* pUi, const QString& pInformation, bool pAccepted); + void fireUiDominationReleased(); private Q_SLOTS: void doShutdown(); @@ -74,15 +81,17 @@ class AppController void onWorkflowFinished(); void onCloseReminderFinished(bool pDontRemindAgain); void onChangePinRequested(); - void onSwitchToReaderSettingsRequested(); void onSelfAuthenticationRequested(); void onAuthenticationRequest(const QSharedPointer& pActivationContext); void onRemoteServiceRequested(); void onSettingsChanged(); + void onUILoaderShutdownComplete(); + void onUiDominationRequested(const UIPlugIn* pUi, const QString& pInformation); + void onUiDominationRelease(); private: template bool startNewWorkflow(Action pAction, const QSharedPointer& pContext); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/controller/AuthController.cpp b/src/core/controller/AuthController.cpp index e9cbeba..ce1db03 100644 --- a/src/core/controller/AuthController.cpp +++ b/src/core/controller/AuthController.cpp @@ -5,27 +5,25 @@ #include "controller/AuthController.h" #include "context/AuthContext.h" +#include "states/CompositeStatePace.h" #include "states/CompositeStateProcessCvcsAndSetRights.h" -#include "states/CompositeStateSelectCard.h" #include "states/FinalState.h" #include "states/StateCheckRefreshAddress.h" #include "states/StateCleanUpReaderManager.h" +#include "states/StateClearPacePasswords.h" #include "states/StateDidAuthenticateEac1.h" #include "states/StateDidAuthenticateEac2.h" #include "states/StateDidList.h" #include "states/StateEACAdditionalInputType.h" -#include "states/StateEstablishPaceCan.h" -#include "states/StateEstablishPacePin.h" -#include "states/StateEstablishPacePuk.h" #include "states/StateGenericSendReceive.h" #include "states/StateGetTcToken.h" -#include "states/StateHandleRetryCounter.h" #include "states/StateInitializeFramework.h" #include "states/StateParseTcTokenUrl.h" #include "states/StateProcessCertificatesFromEac2.h" #include "states/StateProcessing.h" #include "states/StateRedirectBrowser.h" #include "states/StateSelectPasswordId.h" +#include "states/StateSendWhitelistSurvey.h" #include "states/StateStartPaos.h" #include "states/StateStartPaosResponse.h" #include "states/StateTransmit.h" @@ -54,15 +52,9 @@ AuthController::AuthController(QSharedPointer pContext) auto sSendDidListResponse = addState(); auto sProcessCvcsAndSetRights = new CompositeStateProcessCvcsAndSetRights(pContext); mStateMachine.addState(sProcessCvcsAndSetRights); - auto sSelectCard = new CompositeStateSelectCard(pContext); - mStateMachine.addState(sSelectCard); - auto sSelectPasswordId = addState(); - auto sUpdateRetryCounter = addState(); - auto sHandleRetryCounter = addState(); - auto sEstablishPaceCan = addState(); - auto sEstablishPacePin = addState(); - auto sEstablishPacePuk = addState(); - auto sEstablishPaceCanCanMode = addState(); + auto sStatePace = new CompositeStatePace(pContext); + mStateMachine.addState(sStatePace); + auto sClearPacePasswords = addState(); auto sDidAuthenticateEac1 = addState(); auto sSendDidAuthenticateResponseEac1 = addState(); auto sEacAdditionalInputType = addState(); @@ -79,6 +71,7 @@ AuthController::AuthController(QSharedPointer pContext) auto sRedirectBrowser = addState(); auto sUpdateRetryCounterFinal = addState(); auto sCleanUpReaderManager = addState(); + auto sSendWhitelistSurvey = addState(); auto sFinal = addState(); sProcessing->addTransition(sProcessing, &AbstractState::fireContinue, sParseTcTokenUrl); @@ -115,40 +108,15 @@ AuthController::AuthController(QSharedPointer pContext) sSendDidListResponse->addTransition(sSendDidListResponse, &StateSendDIDListResponse::fireReceivedDisconnect, sSendDisconnectResponse); sSendDidListResponse->addTransition(sSendDidListResponse, &StateSendDIDListResponse::fireReceivedStartPaosResponse, sStartPaosResponse); - sProcessCvcsAndSetRights->addTransition(sProcessCvcsAndSetRights, &CompositeStateProcessCvcsAndSetRights::fireContinue, sSelectCard); + sProcessCvcsAndSetRights->addTransition(sProcessCvcsAndSetRights, &CompositeStateProcessCvcsAndSetRights::fireContinue, sStatePace); sProcessCvcsAndSetRights->addTransition(sProcessCvcsAndSetRights, &CompositeStateProcessCvcsAndSetRights::fireAbort, sSendDidAuthenticateResponseEac1); - sSelectCard->addTransition(sSelectCard, &CompositeStateSelectCard::fireContinue, sSelectPasswordId); - sSelectCard->addTransition(sSelectCard, &CompositeStateSelectCard::fireAbort, sSendDidAuthenticateResponseEac1); + sStatePace->addTransition(sStatePace, &CompositeStatePace::firePaceChannelEstablished, sClearPacePasswords); + sStatePace->addTransition(sStatePace, &CompositeStatePace::firePacePukEstablished, sStatePace); + sStatePace->addTransition(sStatePace, &CompositeStatePace::fireAbort, sSendDidAuthenticateResponseEac1); - sSelectPasswordId->addTransition(sSelectPasswordId, &AbstractState::fireContinue, sUpdateRetryCounter); - sSelectPasswordId->addTransition(sSelectPasswordId, &StateSelectPasswordId::firePasswordIdCAN, sEstablishPaceCanCanMode); - sSelectPasswordId->addTransition(sSelectPasswordId, &AbstractState::fireAbort, sSendDidAuthenticateResponseEac1); - - sUpdateRetryCounter->addTransition(sUpdateRetryCounter, &AbstractState::fireContinue, sHandleRetryCounter); - sUpdateRetryCounter->addTransition(sUpdateRetryCounter, &AbstractState::fireAbort, sSendDidAuthenticateResponseEac1); - - sHandleRetryCounter->addTransition(sHandleRetryCounter, &StateHandleRetryCounter::fireRetryCounterIsGTOne, sEstablishPacePin); - sHandleRetryCounter->addTransition(sHandleRetryCounter, &StateHandleRetryCounter::fireRetryCounterIsOne, sEstablishPaceCan); - sHandleRetryCounter->addTransition(sHandleRetryCounter, &StateHandleRetryCounter::fireRetryCounterIsZero, sEstablishPacePuk); - sHandleRetryCounter->addTransition(sHandleRetryCounter, &AbstractState::fireAbort, sSendDidAuthenticateResponseEac1); - - sEstablishPacePuk->addTransition(sEstablishPacePuk, &AbstractState::fireContinue, sEstablishPacePin); - sEstablishPacePuk->addTransition(sEstablishPacePuk, &StateEstablishPacePuk::fireInvalidPuk, sUpdateRetryCounter); - sEstablishPacePuk->addTransition(sEstablishPacePuk, &StateEstablishPacePuk::fireInoperativePuk, sUpdateRetryCounter); - sEstablishPacePuk->addTransition(sEstablishPacePuk, &AbstractState::fireAbort, sSendDidAuthenticateResponseEac1); - - sEstablishPaceCan->addTransition(sEstablishPaceCan, &AbstractState::fireContinue, sEstablishPacePin); - sEstablishPaceCan->addTransition(sEstablishPaceCan, &StateEstablishPaceCan::fireInvalidCan, sUpdateRetryCounter); - sEstablishPaceCan->addTransition(sEstablishPaceCan, &AbstractState::fireAbort, sSendDidAuthenticateResponseEac1); - - sEstablishPacePin->addTransition(sEstablishPacePin, &AbstractState::fireContinue, sDidAuthenticateEac1); - sEstablishPacePin->addTransition(sEstablishPacePin, &StateEstablishPacePin::fireInvalidPin, sUpdateRetryCounter); - sEstablishPacePin->addTransition(sEstablishPacePin, &AbstractState::fireAbort, sSendDidAuthenticateResponseEac1); - - sEstablishPaceCanCanMode->addTransition(sEstablishPaceCanCanMode, &AbstractState::fireContinue, sDidAuthenticateEac1); - sEstablishPaceCanCanMode->addTransition(sEstablishPaceCanCanMode, &StateEstablishPaceCan::fireInvalidCan, sEstablishPaceCanCanMode); - sEstablishPaceCanCanMode->addTransition(sEstablishPaceCanCanMode, &AbstractState::fireAbort, sSendDidAuthenticateResponseEac1); + sClearPacePasswords->addTransition(sClearPacePasswords, &AbstractState::fireContinue, sDidAuthenticateEac1); + sClearPacePasswords->addTransition(sClearPacePasswords, &AbstractState::fireAbort, sDidAuthenticateEac1); sDidAuthenticateEac1->addTransition(sDidAuthenticateEac1, &AbstractState::fireContinue, sSendDidAuthenticateResponseEac1); sDidAuthenticateEac1->addTransition(sDidAuthenticateEac1, &AbstractState::fireAbort, sSendDidAuthenticateResponseEac1); @@ -200,14 +168,12 @@ AuthController::AuthController(QSharedPointer pContext) sCheckRefreshAddress->addTransition(sCheckRefreshAddress, &AbstractState::fireContinue, sWriteHistory); sCheckRefreshAddress->addTransition(sCheckRefreshAddress, &AbstractState::fireAbort, sRedirectBrowser); - sWriteHistory->addTransition(sWriteHistory, &AbstractState::fireContinue, sRedirectBrowser); + sWriteHistory->addTransition(sWriteHistory, &AbstractState::fireContinue, sSendWhitelistSurvey); sWriteHistory->addTransition(sWriteHistory, &AbstractState::fireAbort, sRedirectBrowser); + sSendWhitelistSurvey->addTransition(sSendWhitelistSurvey, &AbstractState::fireContinue, sRedirectBrowser); + sSendWhitelistSurvey->addTransition(sSendWhitelistSurvey, &AbstractState::fireAbort, sRedirectBrowser); + sRedirectBrowser->addTransition(sRedirectBrowser, &AbstractState::fireContinue, sFinal); sRedirectBrowser->addTransition(sRedirectBrowser, &AbstractState::fireAbort, sFinal); } - - -AuthController::~AuthController() -{ -} diff --git a/src/core/controller/AuthController.h b/src/core/controller/AuthController.h index 4c8edfe..e1b8665 100644 --- a/src/core/controller/AuthController.h +++ b/src/core/controller/AuthController.h @@ -20,7 +20,7 @@ class AuthController public: AuthController(QSharedPointer pContext); - virtual ~AuthController(); + virtual ~AuthController() = default; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/controller/ChangePinController.cpp b/src/core/controller/ChangePinController.cpp index 8ebb83e..2f14d43 100644 --- a/src/core/controller/ChangePinController.cpp +++ b/src/core/controller/ChangePinController.cpp @@ -5,15 +5,14 @@ #include "ChangePinController.h" #include "context/ChangePinContext.h" -#include "states/CompositeStateSelectCard.h" +#include "states/CompositeStatePace.h" #include "states/FinalState.h" #include "states/StateChangePin.h" #include "states/StateCleanUpReaderManager.h" +#include "states/StateClearPacePasswords.h" #include "states/StateDestroyPace.h" -#include "states/StateEstablishPaceCan.h" -#include "states/StateEstablishPacePin.h" -#include "states/StateEstablishPacePuk.h" -#include "states/StateHandleRetryCounter.h" +#include "states/StateEnterNewPacePin.h" +#include "states/StatePrepareChangePin.h" #include "states/StateUpdateRetryCounter.h" #include @@ -26,51 +25,39 @@ using namespace governikus; ChangePinController::ChangePinController(QSharedPointer pContext) : WorkflowController(pContext) { - auto sSelectCard = new CompositeStateSelectCard(pContext); - mStateMachine.addState(sSelectCard); - auto sUpdateRetryCounter = addState(); - auto sHandleRetryCounter = addState(); - auto sEstablishPacePin = addState(); - auto sEstablishPaceCan = addState(); - auto sEstablishPacePuk = addState(); + auto sStatePace = new CompositeStatePace(pContext); + mStateMachine.addState(sStatePace); + auto sPrepareChangePin = addState(); + auto sEnterNewPacePin = addState(); auto sChangePin = addState(); + auto sClearPacePasswords = addState(); auto sDestroyPace = addState(); auto sUpdateRetryCounterFinal = addState(); auto sCleanUpReaderManager = addState(); auto sFinal = addState(); - mStateMachine.setInitialState(sSelectCard); + mStateMachine.setInitialState(sStatePace); - sSelectCard->addTransition(sSelectCard, &CompositeStateSelectCard::fireContinue, sUpdateRetryCounter); - sSelectCard->addTransition(sSelectCard, &CompositeStateSelectCard::fireAbort, sCleanUpReaderManager); + sStatePace->addTransition(sStatePace, &CompositeStatePace::firePaceChannelEstablished, sPrepareChangePin); + sStatePace->addTransition(sStatePace, &CompositeStatePace::firePacePukEstablished, sClearPacePasswords); + sStatePace->addTransition(sStatePace, &CompositeStatePace::fireAbort, sClearPacePasswords); - sUpdateRetryCounter->addTransition(sUpdateRetryCounter, &AbstractState::fireContinue, sHandleRetryCounter); - sUpdateRetryCounter->addTransition(sUpdateRetryCounter, &AbstractState::fireAbort, sCleanUpReaderManager); + sPrepareChangePin->addTransition(sPrepareChangePin, &StatePrepareChangePin::fireContinue, sChangePin); + sPrepareChangePin->addTransition(sPrepareChangePin, &StatePrepareChangePin::fireEnterNewPacePin, sEnterNewPacePin); + sPrepareChangePin->addTransition(sPrepareChangePin, &StatePrepareChangePin::fireAbort, sStatePace); - sHandleRetryCounter->addTransition(sHandleRetryCounter, &StateHandleRetryCounter::fireRetryCounterIsZero, sEstablishPacePuk); - sHandleRetryCounter->addTransition(sHandleRetryCounter, &StateHandleRetryCounter::fireRetryCounterIsOne, sEstablishPaceCan); - sHandleRetryCounter->addTransition(sHandleRetryCounter, &StateHandleRetryCounter::fireRetryCounterIsGTOne, sEstablishPacePin); - sHandleRetryCounter->addTransition(sHandleRetryCounter, &AbstractState::fireAbort, sUpdateRetryCounterFinal); - - sEstablishPacePuk->addTransition(sEstablishPacePuk, &StateEstablishPacePuk::fireInvalidPuk, sUpdateRetryCounter); - sEstablishPacePuk->addTransition(sEstablishPacePuk, &StateEstablishPacePuk::fireInoperativePuk, sUpdateRetryCounterFinal); - sEstablishPacePuk->addTransition(sEstablishPacePuk, &AbstractState::fireContinue, sUpdateRetryCounterFinal); - sEstablishPacePuk->addTransition(sEstablishPacePuk, &AbstractState::fireAbort, sUpdateRetryCounterFinal); - - sEstablishPaceCan->addTransition(sEstablishPaceCan, &StateEstablishPaceCan::fireInvalidCan, sUpdateRetryCounter); - sEstablishPaceCan->addTransition(sEstablishPaceCan, &AbstractState::fireContinue, sEstablishPacePin); - sEstablishPaceCan->addTransition(sEstablishPaceCan, &AbstractState::fireAbort, sUpdateRetryCounterFinal); - - sEstablishPacePin->addTransition(sEstablishPacePin, &StateEstablishPacePin::fireInvalidPin, sUpdateRetryCounter); - sEstablishPacePin->addTransition(sEstablishPacePin, &AbstractState::fireContinue, sChangePin); - sEstablishPacePin->addTransition(sEstablishPacePin, &AbstractState::fireAbort, sUpdateRetryCounterFinal); + sEnterNewPacePin->addTransition(sEnterNewPacePin, &AbstractState::fireContinue, sChangePin); + sEnterNewPacePin->addTransition(sEnterNewPacePin, &AbstractState::fireAbort, sStatePace); sChangePin->addTransition(sChangePin, &AbstractState::fireContinue, sDestroyPace); - sChangePin->addTransition(sChangePin, &StateChangePin::fireInvalidPin, sUpdateRetryCounterFinal); - sChangePin->addTransition(sChangePin, &AbstractState::fireAbort, sUpdateRetryCounterFinal); + sChangePin->addTransition(sChangePin, &StateChangePin::fireInvalidPin, sClearPacePasswords); + sChangePin->addTransition(sChangePin, &AbstractState::fireAbort, sStatePace); - sDestroyPace->addTransition(sDestroyPace, &AbstractState::fireContinue, sUpdateRetryCounterFinal); - sDestroyPace->addTransition(sDestroyPace, &AbstractState::fireAbort, sUpdateRetryCounterFinal); + sDestroyPace->addTransition(sDestroyPace, &AbstractState::fireContinue, sClearPacePasswords); + sDestroyPace->addTransition(sDestroyPace, &AbstractState::fireAbort, sClearPacePasswords); + + sClearPacePasswords->addTransition(sClearPacePasswords, &StateClearPacePasswords::fireContinue, sUpdateRetryCounterFinal); + sClearPacePasswords->addTransition(sClearPacePasswords, &StateClearPacePasswords::fireAbort, sUpdateRetryCounterFinal); sUpdateRetryCounterFinal->addTransition(sUpdateRetryCounterFinal, &AbstractState::fireContinue, sCleanUpReaderManager); sUpdateRetryCounterFinal->addTransition(sUpdateRetryCounterFinal, &AbstractState::fireAbort, sCleanUpReaderManager); diff --git a/src/core/controller/ChangePinController.h b/src/core/controller/ChangePinController.h index a723df2..36804a5 100644 --- a/src/core/controller/ChangePinController.h +++ b/src/core/controller/ChangePinController.h @@ -24,4 +24,4 @@ class ChangePinController virtual ~ChangePinController(); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/controller/DiagnosisController.cpp b/src/core/controller/DiagnosisController.cpp index 3c6f285..28429f9 100644 --- a/src/core/controller/DiagnosisController.cpp +++ b/src/core/controller/DiagnosisController.cpp @@ -3,20 +3,17 @@ */ #include "DiagnosisController.h" + #include "ReaderManager.h" +#include #include #include -#ifdef Q_OS_UNIX -# include -#endif - - using namespace governikus; -DiagnosisController::DiagnosisController(DiagnosisContext* pContext, QObject* pParent) +DiagnosisController::DiagnosisController(const QSharedPointer& pContext, QObject* pParent) : QObject(pParent) , mContext(pContext) , mWatcherPcscInfo() @@ -33,6 +30,7 @@ DiagnosisController::~DiagnosisController() void DiagnosisController::run() { mWatcherPcscInfo.setFuture(QtConcurrent::run(&DiagnosisController::retrievePcscInfo)); + collectInterfaceInformation(); } @@ -48,18 +46,24 @@ void DiagnosisController::checkDone() { if (mWatcherPcscInfo.isFinished()) { - mContext->setReaderInfos(ReaderManager::getInstance().getReaderInfos()); + mContext->setReaderInfos(Env::getSingleton()->getReaderInfos()); mContext->setTimestamp(QDateTime::currentDateTime()); } } +void DiagnosisController::collectInterfaceInformation() +{ + mContext->setNetworkInterfaces(QNetworkInterface::allInterfaces()); +} + + DiagnosisController::PcscInfo DiagnosisController::retrievePcscInfo() { PcscInfo result; bool hasPcsc = false; - const auto infos = ReaderManager::getInstance().getPlugInInfos(); + const auto infos = Env::getSingleton()->getPlugInInfos(); for (const auto& info : infos) { if (info.getPlugInType() == ReaderManagerPlugInType::PCSC) diff --git a/src/core/controller/DiagnosisController.h b/src/core/controller/DiagnosisController.h index d36db50..d3ceea2 100644 --- a/src/core/controller/DiagnosisController.h +++ b/src/core/controller/DiagnosisController.h @@ -28,17 +28,19 @@ class DiagnosisController }; private: - QScopedPointer mContext; + QSharedPointer mContext; QFutureWatcher mWatcherPcscInfo; void checkDone(); + void collectInterfaceInformation(); + static PcscInfo retrievePcscInfo(); static void getPcscInfo(QVector& pComponents, QVector& pDrivers); public: - DiagnosisController(DiagnosisContext* pContext, QObject* pParent = nullptr); + explicit DiagnosisController(const QSharedPointer& pContext, QObject* pParent = nullptr); virtual ~DiagnosisController(); void run(); @@ -50,4 +52,4 @@ class DiagnosisController }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/controller/DiagnosisController_generic.cpp b/src/core/controller/DiagnosisController_generic.cpp index a7fac52..afb0d97 100644 --- a/src/core/controller/DiagnosisController_generic.cpp +++ b/src/core/controller/DiagnosisController_generic.cpp @@ -16,5 +16,4 @@ void DiagnosisController::getPcscInfo(QVector& { Q_UNUSED(pComponents); Q_UNUSED(pDrivers); - } diff --git a/src/core/controller/RemoteServiceController.cpp b/src/core/controller/RemoteServiceController.cpp index 99d3c4c..e16a46e 100644 --- a/src/core/controller/RemoteServiceController.cpp +++ b/src/core/controller/RemoteServiceController.cpp @@ -7,10 +7,19 @@ #include "context/RemoteServiceContext.h" #include "states/FinalState.h" #include "states/remote_service/StateChangePinRemote.h" -#include "states/remote_service/StateEstablishPaceChannel.h" +#include "states/remote_service/StateChangePinResponse.h" +#include "states/remote_service/StateEnterNewPacePinRemote.h" +#include "states/remote_service/StateEnterPacePasswordRemote.h" +#include "states/remote_service/StateEstablishPaceChannelRemote.h" +#include "states/remote_service/StateEstablishPaceChannelResponse.h" +#include "states/remote_service/StatePrepareChangePinRemote.h" +#include "states/remote_service/StatePreparePaceRemote.h" #include "states/remote_service/StateProcessRemoteMessages.h" #include "states/remote_service/StateStartRemoteService.h" #include "states/remote_service/StateStopRemoteService.h" +#include "states/StateClearPacePasswords.h" +#include "states/StateUpdateRetryCounter.h" +#include "states/StateVerifyRetryCounter.h" #include #include @@ -25,23 +34,64 @@ RemoteServiceController::RemoteServiceController(QSharedPointer(); mStateMachine.setInitialState(sStartRemoteService); auto sProcessRemoteMessages = addState(); - auto sEstablishPaceChannel = addState(); + auto sUpdateRetryCounter = addState(); + auto sVerifyRetryCounter = addState(); + auto sPreparePaceRemote = addState(); + auto sEnterPacePasswordRemote = addState(); + auto sEstablishPaceChannelRemote = addState(); + auto sEstablishPaceChannelResponse = addState(); + auto sPrepareChangePinRemote = addState(); + auto sEnterNewPacePinRemote = addState(); auto sChangePinRemote = addState(); + auto sChangePinResponse = addState(); + auto sClearPacePasswords = addState(); auto sStopRemoteService = addState(); auto sFinal = addState(); sStartRemoteService->addTransition(sStartRemoteService, &AbstractState::fireContinue, sProcessRemoteMessages); sStartRemoteService->addTransition(sStartRemoteService, &AbstractState::fireAbort, sStopRemoteService); - sProcessRemoteMessages->addTransition(sProcessRemoteMessages, &AbstractState::fireAbort, sStopRemoteService); - sProcessRemoteMessages->addTransition(sProcessRemoteMessages, &StateProcessRemoteMessages::fireEstablishPaceChannel, sEstablishPaceChannel); - sProcessRemoteMessages->addTransition(sProcessRemoteMessages, &StateProcessRemoteMessages::fireModifyPin, sChangePinRemote); + sProcessRemoteMessages->addTransition(sProcessRemoteMessages, &StateProcessRemoteMessages::fireAbort, sStopRemoteService); + sProcessRemoteMessages->addTransition(sProcessRemoteMessages, &StateProcessRemoteMessages::fireEstablishPaceChannel, sUpdateRetryCounter); + sProcessRemoteMessages->addTransition(sProcessRemoteMessages, &StateProcessRemoteMessages::fireModifyPin, sPrepareChangePinRemote); + sProcessRemoteMessages->addTransition(sProcessRemoteMessages, &StateProcessRemoteMessages::fireSecureMessagingStopped, sClearPacePasswords); - sEstablishPaceChannel->addTransition(sEstablishPaceChannel, &AbstractState::fireContinue, sProcessRemoteMessages); - sEstablishPaceChannel->addTransition(sEstablishPaceChannel, &AbstractState::fireAbort, sStopRemoteService); + sUpdateRetryCounter->addTransition(sUpdateRetryCounter, &AbstractState::fireContinue, sVerifyRetryCounter); + sUpdateRetryCounter->addTransition(sUpdateRetryCounter, &AbstractState::fireAbort, sEstablishPaceChannelResponse); - sChangePinRemote->addTransition(sChangePinRemote, &AbstractState::fireContinue, sProcessRemoteMessages); - sChangePinRemote->addTransition(sChangePinRemote, &AbstractState::fireAbort, sStopRemoteService); + sVerifyRetryCounter->addTransition(sVerifyRetryCounter, &AbstractState::fireContinue, sPreparePaceRemote); + sVerifyRetryCounter->addTransition(sVerifyRetryCounter, &AbstractState::fireAbort, sEstablishPaceChannelResponse); + + sPreparePaceRemote->addTransition(sPreparePaceRemote, &StatePreparePaceRemote::fireContinue, sEstablishPaceChannelRemote); + sPreparePaceRemote->addTransition(sPreparePaceRemote, &StatePreparePaceRemote::fireEnterPacePassword, sEnterPacePasswordRemote); + sPreparePaceRemote->addTransition(sPreparePaceRemote, &StatePreparePaceRemote::fireAbort, sEstablishPaceChannelRemote); + + sEnterPacePasswordRemote->addTransition(sEnterPacePasswordRemote, &AbstractState::fireContinue, sEstablishPaceChannelRemote); + sEnterPacePasswordRemote->addTransition(sEnterPacePasswordRemote, &AbstractState::fireAbort, sEstablishPaceChannelResponse); + + sEstablishPaceChannelRemote->addTransition(sEstablishPaceChannelRemote, &StateEstablishPaceChannelRemote::fireContinue, sEstablishPaceChannelResponse); + sEstablishPaceChannelRemote->addTransition(sEstablishPaceChannelRemote, &StateEstablishPaceChannelRemote::fireAbort, sEstablishPaceChannelResponse); + + sEstablishPaceChannelResponse->addTransition(sEstablishPaceChannelResponse, &StateEstablishPaceChannelResponse::fireContinue, sProcessRemoteMessages); + sEstablishPaceChannelResponse->addTransition(sEstablishPaceChannelResponse, &StateEstablishPaceChannelResponse::fireWrongPacePassword, sClearPacePasswords); + sEstablishPaceChannelResponse->addTransition(sEstablishPaceChannelResponse, &StateEstablishPaceChannelResponse::fireAbort, sProcessRemoteMessages); + + sPrepareChangePinRemote->addTransition(sPrepareChangePinRemote, &StatePrepareChangePinRemote::fireContinue, sChangePinRemote); + sPrepareChangePinRemote->addTransition(sPrepareChangePinRemote, &StatePrepareChangePinRemote::fireEnterNewPacePin, sEnterNewPacePinRemote); + sPrepareChangePinRemote->addTransition(sPrepareChangePinRemote, &StatePrepareChangePinRemote::fireAbort, sChangePinRemote); + + sEnterNewPacePinRemote->addTransition(sEnterNewPacePinRemote, &StateEnterNewPacePinRemote::fireContinue, sChangePinRemote); + sEnterNewPacePinRemote->addTransition(sEnterNewPacePinRemote, &StateEnterNewPacePinRemote::fireAbort, sChangePinResponse); + + sChangePinRemote->addTransition(sChangePinRemote, &StateChangePinRemote::fireContinue, sChangePinResponse); + sChangePinRemote->addTransition(sChangePinRemote, &StateChangePinRemote::fireAbort, sChangePinResponse); + + sChangePinResponse->addTransition(sChangePinResponse, &StateChangePinResponse::fireContinue, sProcessRemoteMessages); + sChangePinResponse->addTransition(sChangePinResponse, &StateChangePinResponse::firePacePasswordModified, sClearPacePasswords); + sChangePinResponse->addTransition(sChangePinResponse, &StateChangePinResponse::fireAbort, sProcessRemoteMessages); + + sClearPacePasswords->addTransition(sClearPacePasswords, &AbstractState::fireContinue, sProcessRemoteMessages); + sClearPacePasswords->addTransition(sClearPacePasswords, &AbstractState::fireAbort, sProcessRemoteMessages); sStopRemoteService->addTransition(sStopRemoteService, &AbstractState::fireContinue, sFinal); sStopRemoteService->addTransition(sStopRemoteService, &AbstractState::fireAbort, sFinal); diff --git a/src/core/controller/RemoteServiceController.h b/src/core/controller/RemoteServiceController.h index 7dd8d97..5040f30 100644 --- a/src/core/controller/RemoteServiceController.h +++ b/src/core/controller/RemoteServiceController.h @@ -23,4 +23,4 @@ class RemoteServiceController virtual ~RemoteServiceController(); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/controller/SelfAuthController.cpp b/src/core/controller/SelfAuthController.cpp index 75cc904..7e52c37 100644 --- a/src/core/controller/SelfAuthController.cpp +++ b/src/core/controller/SelfAuthController.cpp @@ -5,34 +5,31 @@ #include "controller/SelfAuthController.h" #include "context/SelfAuthContext.h" +#include "states/CompositeStatePace.h" #include "states/CompositeStateProcessCvcsAndSetRights.h" -#include "states/CompositeStateSelectCard.h" #include "states/FinalState.h" #include "states/StateCheckError.h" #include "states/StateCheckRefreshAddress.h" #include "states/StateCleanUpReaderManager.h" +#include "states/StateClearPacePasswords.h" #include "states/StateDidAuthenticateEac1.h" #include "states/StateDidAuthenticateEac2.h" #include "states/StateDidList.h" #include "states/StateEACAdditionalInputType.h" -#include "states/StateEstablishPaceCan.h" -#include "states/StateEstablishPacePin.h" -#include "states/StateEstablishPacePuk.h" #include "states/StateGenericSendReceive.h" #include "states/StateGetSelfAuthenticationData.h" #include "states/StateGetTcToken.h" -#include "states/StateHandleRetryCounter.h" #include "states/StateInitializeFramework.h" #include "states/StateLoadTcTokenUrl.h" #include "states/StateProcessCertificatesFromEac2.h" -#include "states/StateSelectPasswordId.h" +#include "states/StateSendWhitelistSurvey.h" +#include "states/StateShowSelfInfo.h" #include "states/StateStartPaos.h" #include "states/StateStartPaosResponse.h" #include "states/StateTransmit.h" #include "states/StateUpdateRetryCounter.h" #include "states/StateWriteHistory.h" - #include #include @@ -54,15 +51,9 @@ SelfAuthController::SelfAuthController(QSharedPointer pContext) auto sSendDidListResponse = addState(); auto sProcessCvcsAndSetRights = new CompositeStateProcessCvcsAndSetRights(pContext); mStateMachine.addState(sProcessCvcsAndSetRights); - auto sSelectCard = new CompositeStateSelectCard(pContext); - mStateMachine.addState(sSelectCard); - auto sSelectPasswordId = addState(); - auto sUpdateRetryCounter = addState(); - auto sHandleRetryCounter = addState(); - auto sEstablishPaceCan = addState(); - auto sEstablishPacePin = addState(); - auto sEstablishPacePuk = addState(); - auto sEstablishPaceCanCanMode = addState(); + auto sStatePace = new CompositeStatePace(pContext); + mStateMachine.addState(sStatePace); + auto sClearPacePasswords = addState(); auto sDidAuthenticateEac1 = addState(); auto sSendDidAuthenticateResponseEac1 = addState(); auto sEacAdditionalInputType = addState(); @@ -77,9 +68,11 @@ SelfAuthController::SelfAuthController(QSharedPointer pContext) auto sCheckErrorEpilogue = addState(); auto sCheckRefreshAddress = addState(); auto sWriteHistory = addState(); + auto sSendWhitelistSurvey = addState(); auto sGetSelfAuthenticationData = addState(); auto sUpdateRetryCounterFinal = addState(); auto sCleanUpReaderManager = addState(); + auto sShowSelfInfo = addState(); auto sFinal = addState(); sLoadTcTokenUrl->addTransition(sLoadTcTokenUrl, &AbstractState::fireContinue, sGetTcToken); @@ -113,39 +106,15 @@ SelfAuthController::SelfAuthController(QSharedPointer pContext) sSendDidListResponse->addTransition(sSendDidListResponse, &StateSendDIDListResponse::fireReceivedDisconnect, sSendDisconnectResponse); sSendDidListResponse->addTransition(sSendDidListResponse, &StateSendDIDListResponse::fireReceivedStartPaosResponse, sStartPaosResponse); - sProcessCvcsAndSetRights->addTransition(sProcessCvcsAndSetRights, &CompositeStateProcessCvcsAndSetRights::fireContinue, sSelectCard); + sProcessCvcsAndSetRights->addTransition(sProcessCvcsAndSetRights, &CompositeStateProcessCvcsAndSetRights::fireContinue, sStatePace); sProcessCvcsAndSetRights->addTransition(sProcessCvcsAndSetRights, &CompositeStateProcessCvcsAndSetRights::fireAbort, sSendDidAuthenticateResponseEac1); - sSelectCard->addTransition(sSelectCard, &CompositeStateSelectCard::fireContinue, sSelectPasswordId); - sSelectCard->addTransition(sSelectCard, &CompositeStateSelectCard::fireAbort, sSendDidAuthenticateResponseEac1); + sStatePace->addTransition(sStatePace, &CompositeStatePace::firePaceChannelEstablished, sClearPacePasswords); + sStatePace->addTransition(sStatePace, &CompositeStatePace::firePacePukEstablished, sStatePace); + sStatePace->addTransition(sStatePace, &CompositeStatePace::fireAbort, sSendDidAuthenticateResponseEac1); - sSelectPasswordId->addTransition(sSelectPasswordId, &AbstractState::fireContinue, sUpdateRetryCounter); - sSelectPasswordId->addTransition(sSelectPasswordId, &StateSelectPasswordId::firePasswordIdCAN, sEstablishPaceCanCanMode); - sSelectPasswordId->addTransition(sSelectPasswordId, &AbstractState::fireAbort, sSendDidAuthenticateResponseEac1); - - sUpdateRetryCounter->addTransition(sUpdateRetryCounter, &AbstractState::fireContinue, sHandleRetryCounter); - sUpdateRetryCounter->addTransition(sUpdateRetryCounter, &AbstractState::fireAbort, sSendDidAuthenticateResponseEac1); - - sHandleRetryCounter->addTransition(sHandleRetryCounter, &StateHandleRetryCounter::fireRetryCounterIsGTOne, sEstablishPacePin); - sHandleRetryCounter->addTransition(sHandleRetryCounter, &StateHandleRetryCounter::fireRetryCounterIsOne, sEstablishPaceCan); - sHandleRetryCounter->addTransition(sHandleRetryCounter, &StateHandleRetryCounter::fireRetryCounterIsZero, sEstablishPacePuk); - sHandleRetryCounter->addTransition(sHandleRetryCounter, &AbstractState::fireAbort, sSendDidAuthenticateResponseEac1); - - sEstablishPacePuk->addTransition(sEstablishPacePuk, &AbstractState::fireContinue, sEstablishPacePin); - sEstablishPacePuk->addTransition(sEstablishPacePuk, &StateEstablishPacePuk::fireInvalidPuk, sUpdateRetryCounter); - sEstablishPacePuk->addTransition(sEstablishPacePuk, &AbstractState::fireAbort, sSendDidAuthenticateResponseEac1); - - sEstablishPaceCan->addTransition(sEstablishPaceCan, &AbstractState::fireContinue, sEstablishPacePin); - sEstablishPaceCan->addTransition(sEstablishPaceCan, &StateEstablishPaceCan::fireInvalidCan, sUpdateRetryCounter); - sEstablishPaceCan->addTransition(sEstablishPaceCan, &AbstractState::fireAbort, sSendDidAuthenticateResponseEac1); - - sEstablishPacePin->addTransition(sEstablishPacePin, &AbstractState::fireContinue, sDidAuthenticateEac1); - sEstablishPacePin->addTransition(sEstablishPacePin, &StateEstablishPacePin::fireInvalidPin, sUpdateRetryCounter); - sEstablishPacePin->addTransition(sEstablishPacePin, &AbstractState::fireAbort, sSendDidAuthenticateResponseEac1); - - sEstablishPaceCanCanMode->addTransition(sEstablishPaceCanCanMode, &AbstractState::fireContinue, sDidAuthenticateEac1); - sEstablishPaceCanCanMode->addTransition(sEstablishPaceCanCanMode, &StateEstablishPaceCan::fireInvalidCan, sEstablishPaceCanCanMode); - sEstablishPaceCanCanMode->addTransition(sEstablishPaceCanCanMode, &AbstractState::fireAbort, sSendDidAuthenticateResponseEac1); + sClearPacePasswords->addTransition(sClearPacePasswords, &AbstractState::fireContinue, sDidAuthenticateEac1); + sClearPacePasswords->addTransition(sClearPacePasswords, &AbstractState::fireAbort, sDidAuthenticateEac1); sDidAuthenticateEac1->addTransition(sDidAuthenticateEac1, &AbstractState::fireContinue, sSendDidAuthenticateResponseEac1); sDidAuthenticateEac1->addTransition(sDidAuthenticateEac1, &AbstractState::fireAbort, sSendDidAuthenticateResponseEac1); @@ -203,11 +172,12 @@ SelfAuthController::SelfAuthController(QSharedPointer pContext) sWriteHistory->addTransition(sWriteHistory, &AbstractState::fireContinue, sGetSelfAuthenticationData); sWriteHistory->addTransition(sWriteHistory, &AbstractState::fireAbort, sGetSelfAuthenticationData); - sGetSelfAuthenticationData->addTransition(sGetSelfAuthenticationData, &AbstractState::fireContinue, sFinal); + sGetSelfAuthenticationData->addTransition(sGetSelfAuthenticationData, &AbstractState::fireContinue, sShowSelfInfo); sGetSelfAuthenticationData->addTransition(sGetSelfAuthenticationData, &AbstractState::fireAbort, sFinal); -} + sShowSelfInfo->addTransition(sShowSelfInfo, &AbstractState::fireContinue, sSendWhitelistSurvey); + sShowSelfInfo->addTransition(sShowSelfInfo, &AbstractState::fireAbort, sFinal); -SelfAuthController::~SelfAuthController() -{ + sSendWhitelistSurvey->addTransition(sSendWhitelistSurvey, &AbstractState::fireContinue, sFinal); + sSendWhitelistSurvey->addTransition(sSendWhitelistSurvey, &AbstractState::fireAbort, sFinal); } diff --git a/src/core/controller/SelfAuthController.h b/src/core/controller/SelfAuthController.h index 3bf93d7..220c2e1 100644 --- a/src/core/controller/SelfAuthController.h +++ b/src/core/controller/SelfAuthController.h @@ -20,7 +20,7 @@ class SelfAuthController public: SelfAuthController(QSharedPointer pContext); - virtual ~SelfAuthController(); + virtual ~SelfAuthController() = default; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/controller/WorkflowController.h b/src/core/controller/WorkflowController.h index 9dc1752..98abdb8 100644 --- a/src/core/controller/WorkflowController.h +++ b/src/core/controller/WorkflowController.h @@ -21,7 +21,6 @@ class WorkflowController : public QObject { Q_OBJECT - friend class ::test_ChangePinController; protected: QStateMachine mStateMachine; @@ -53,4 +52,4 @@ class WorkflowController }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/paos/ElementDetector.cpp b/src/core/paos/ElementDetector.cpp index de353ad..bd6a8bb 100644 --- a/src/core/paos/ElementDetector.cpp +++ b/src/core/paos/ElementDetector.cpp @@ -31,7 +31,7 @@ void ElementDetector::detectStartElements(const QStringList& pStartElementNames) { if (mReader.hasError()) { - qCWarning(paos) << "Error parsing PAOS message: " << mReader.errorString(); + qCWarning(paos) << "Error parsing PAOS message:" << mReader.errorString(); return; } else if (mReader.isStartElement()) diff --git a/src/core/paos/ElementDetector.h b/src/core/paos/ElementDetector.h index 6f079fe..e0a1184 100644 --- a/src/core/paos/ElementDetector.h +++ b/src/core/paos/ElementDetector.h @@ -31,4 +31,4 @@ class ElementDetector virtual ~ElementDetector(); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/paos/MessageIdHandler.cpp b/src/core/paos/MessageIdHandler.cpp deleted file mode 100644 index 5ae69a1..0000000 --- a/src/core/paos/MessageIdHandler.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "paos/MessageIdHandler.h" - -#include - -using namespace governikus; - -QString MessageIdHandler::createNewMsgId() -{ - return QUuid::createUuid().toString().mid(1, 36).prepend(QLatin1String("urn:uuid:")); -} - - -const QString& MessageIdHandler::getRemoteMsgId() const -{ - return mRemoteMsgId; -} - - -void MessageIdHandler::setRemoteMsgId(const QString& pNewRemoteMsgId) -{ - mRemoteMsgId = pNewRemoteMsgId; -} diff --git a/src/core/paos/MessageIdHandler.h b/src/core/paos/MessageIdHandler.h deleted file mode 100644 index c0f9517..0000000 --- a/src/core/paos/MessageIdHandler.h +++ /dev/null @@ -1,25 +0,0 @@ -/*! - * \brief Handle MessageIDs from XML header to use it in next paos message. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include - -namespace governikus -{ - -class MessageIdHandler -{ - private: - QString mRemoteMsgId; - - public: - QString createNewMsgId(); - const QString& getRemoteMsgId() const; - void setRemoteMsgId(const QString& pNewRemoteMsgId); -}; - -} /* namespace governikus */ diff --git a/src/core/paos/PaosHandler.cpp b/src/core/paos/PaosHandler.cpp index 2f584d2..b39212c 100644 --- a/src/core/paos/PaosHandler.cpp +++ b/src/core/paos/PaosHandler.cpp @@ -59,7 +59,6 @@ void PaosHandler::parse() { setParsedObject(new StartPaosResponse(mXmlData)); } - } @@ -89,9 +88,7 @@ bool PaosHandler::handleFoundElement(const QString& pElementName, const QString& } else if (pElementName == QLatin1String("DIDAuthenticate")) { - QStringList expectedElements; - expectedElements.push_back(QStringLiteral("AuthenticationProtocolData")); - detectStartElements(expectedElements); + detectStartElements({QStringLiteral("AuthenticationProtocolData")}); } else if (pElementName == QLatin1String("AuthenticationProtocolData")) { @@ -129,13 +126,15 @@ bool PaosHandler::handleFoundElement(const QString& pElementName, const QString& void PaosHandler::detect() { - QStringList expectedElements; - expectedElements.push_back(QStringLiteral("InitializeFramework")); - expectedElements.push_back(QStringLiteral("DIDList")); - expectedElements.push_back(QStringLiteral("DIDAuthenticate")); - expectedElements.push_back(QStringLiteral("Transmit")); - expectedElements.push_back(QStringLiteral("Disconnect")); - expectedElements.push_back(QStringLiteral("StartPAOSResponse")); + const QStringList expectedElements({ + QStringLiteral("InitializeFramework"), + QStringLiteral("DIDList"), + QStringLiteral("DIDAuthenticate"), + QStringLiteral("Transmit"), + QStringLiteral("Disconnect"), + QStringLiteral("StartPAOSResponse") + }); + detectStartElements(expectedElements); } diff --git a/src/core/paos/PaosHandler.h b/src/core/paos/PaosHandler.h index 3eab371..43be2c3 100644 --- a/src/core/paos/PaosHandler.h +++ b/src/core/paos/PaosHandler.h @@ -36,4 +36,4 @@ class PaosHandler QSharedPointer getPaosMessage() const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/paos/PaosMessage.h b/src/core/paos/PaosMessage.h index 3e87022..0a3d159 100644 --- a/src/core/paos/PaosMessage.h +++ b/src/core/paos/PaosMessage.h @@ -57,4 +57,4 @@ class PaosMessage }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/paos/PaosType.h b/src/core/paos/PaosType.h index 0488274..05d4fcc 100644 --- a/src/core/paos/PaosType.h +++ b/src/core/paos/PaosType.h @@ -30,4 +30,4 @@ defineEnumType(PaosType, DISCONNECT_RESPONSE ) -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/paos/RequestType.h b/src/core/paos/RequestType.h index 1bd9fb8..9f45d6a 100644 --- a/src/core/paos/RequestType.h +++ b/src/core/paos/RequestType.h @@ -20,4 +20,4 @@ class RequestType virtual ~RequestType(); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/paos/ResponseType.cpp b/src/core/paos/ResponseType.cpp index 1275ecd..85e2f15 100644 --- a/src/core/paos/ResponseType.cpp +++ b/src/core/paos/ResponseType.cpp @@ -9,7 +9,7 @@ using namespace governikus; ResponseType::ResponseType(PaosType pType) : PaosMessage(pType) - , mResult(Result::createOk()) + , mResult(ECardApiResult::createOk()) { } @@ -19,13 +19,13 @@ ResponseType::~ResponseType() } -const Result& ResponseType::getResult() const +const ECardApiResult& ResponseType::getResult() const { return mResult; } -void ResponseType::setResult(const Result& result) +void ResponseType::setResult(const ECardApiResult& result) { mResult = result; } diff --git a/src/core/paos/ResponseType.h b/src/core/paos/ResponseType.h index f1e306e..3a03e2b 100644 --- a/src/core/paos/ResponseType.h +++ b/src/core/paos/ResponseType.h @@ -8,8 +8,8 @@ #pragma once +#include "ECardApiResult.h" #include "PaosMessage.h" -#include "Result.h" namespace governikus @@ -19,14 +19,14 @@ class ResponseType : public PaosMessage { private: - Result mResult; + ECardApiResult mResult; public: ResponseType(PaosType pType); virtual ~ResponseType(); - const Result& getResult() const; - void setResult(const Result& result); + const ECardApiResult& getResult() const; + void setResult(const ECardApiResult& result); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/paos/element/ConnectionHandle.h b/src/core/paos/element/ConnectionHandle.h index e44beed..1c9549c 100644 --- a/src/core/paos/element/ConnectionHandle.h +++ b/src/core/paos/element/ConnectionHandle.h @@ -39,4 +39,4 @@ class ConnectionHandle const QString& getContextHandle() const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/paos/element/ConnectionHandleParser.cpp b/src/core/paos/element/ConnectionHandleParser.cpp index 12f8f46..5208af0 100644 --- a/src/core/paos/element/ConnectionHandleParser.cpp +++ b/src/core/paos/element/ConnectionHandleParser.cpp @@ -11,7 +11,6 @@ using namespace governikus; ConnectionHandleParser::ConnectionHandleParser(QSharedPointer pXmlReader) : ElementParser(pXmlReader) { - } diff --git a/src/core/paos/element/ConnectionHandleParser.h b/src/core/paos/element/ConnectionHandleParser.h index 2a0fe12..c6d3d2b 100644 --- a/src/core/paos/element/ConnectionHandleParser.h +++ b/src/core/paos/element/ConnectionHandleParser.h @@ -27,4 +27,4 @@ class ConnectionHandleParser ConnectionHandle parse(); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/paos/element/Eac1InputType.h b/src/core/paos/element/Eac1InputType.h index b2c19c6..a70d8f1 100644 --- a/src/core/paos/element/Eac1InputType.h +++ b/src/core/paos/element/Eac1InputType.h @@ -21,6 +21,7 @@ class test_StateExtractCvcsFromEac1InputType; class test_StatePreVerification; class test_StateCertificateDescriptionCheck; class test_StateProcessCertificatesFromEac2; +class test_AuthModel; namespace governikus { @@ -29,12 +30,12 @@ class TestAuthContext; class Eac1InputType { friend class DidAuthenticateEac1Parser; - friend class ::test_StatePrepareChat; friend class TestAuthContext; friend class ::test_StateExtractCvcsFromEac1InputType; friend class ::test_StatePreVerification; friend class ::test_StateCertificateDescriptionCheck; friend class ::test_StateProcessCertificatesFromEac2; + friend class ::test_AuthModel; private: QVector > mCvCertificates; @@ -147,4 +148,4 @@ class Eac1InputType }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/paos/element/Eac2InputType.h b/src/core/paos/element/Eac2InputType.h index 2643245..8e7acfe 100644 --- a/src/core/paos/element/Eac2InputType.h +++ b/src/core/paos/element/Eac2InputType.h @@ -41,4 +41,4 @@ class Eac2InputType const QByteArrayList& getCvCertificatesAsBinary() const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/paos/element/ElementParser.cpp b/src/core/paos/element/ElementParser.cpp index 796d1bd..865e0a1 100644 --- a/src/core/paos/element/ElementParser.cpp +++ b/src/core/paos/element/ElementParser.cpp @@ -10,7 +10,6 @@ ElementParser::ElementParser(QSharedPointer pXmlReader) : mXmlReader(pXmlReader) , mParseError(false) { - } diff --git a/src/core/paos/element/ElementParser.h b/src/core/paos/element/ElementParser.h index 821b934..e3e8f14 100644 --- a/src/core/paos/element/ElementParser.h +++ b/src/core/paos/element/ElementParser.h @@ -91,4 +91,4 @@ template bool ElementParser::assertMandatoryList(const QVector& p } -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/paos/element/SupportedApi.h b/src/core/paos/element/SupportedApi.h index b621aae..f43aea5 100644 --- a/src/core/paos/element/SupportedApi.h +++ b/src/core/paos/element/SupportedApi.h @@ -19,4 +19,4 @@ class SupportedAPI QString getSubminor() const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/paos/element/UserAgent.h b/src/core/paos/element/UserAgent.h index e9b5c90..4afae46 100644 --- a/src/core/paos/element/UserAgent.h +++ b/src/core/paos/element/UserAgent.h @@ -27,4 +27,4 @@ class UserAgent const QString& getVersionSubminor() const; }; -} +} // namespace governikus diff --git a/src/core/paos/invoke/DidAuthenticateResponseEac1.cpp b/src/core/paos/invoke/DidAuthenticateResponseEac1.cpp index a11ed51..c2255d2 100644 --- a/src/core/paos/invoke/DidAuthenticateResponseEac1.cpp +++ b/src/core/paos/invoke/DidAuthenticateResponseEac1.cpp @@ -4,7 +4,8 @@ #include "DidAuthenticateResponseEac1.h" -#include "EstablishPACEChannel.h" +#include "EstablishPaceChannel.h" +#include "EstablishPaceChannelOutput.h" #include "paos/PaosType.h" using namespace governikus; @@ -27,12 +28,6 @@ DIDAuthenticateResponseEAC1::~DIDAuthenticateResponseEAC1() } -QDomElement DIDAuthenticateResponseEAC1::getDocumentStructure() -{ - return createEnvelopeElement(createDIDAuthenticateResponseEAC1Element(), getRelatesTo(), getMessageId()); -} - - const QByteArray& DIDAuthenticateResponseEAC1::getCertificateHolderAuthorizationTemplate() const { return mCertificateHolderAuthorizationTemplate; @@ -75,7 +70,7 @@ void DIDAuthenticateResponseEAC1::setIDPICC(const QByteArray& pValue) } -void DIDAuthenticateResponseEAC1::setCertificationAuthorityReference(const EstablishPACEChannelOutput& pPaceChannelOutput) +void DIDAuthenticateResponseEAC1::setCertificationAuthorityReference(const EstablishPaceChannelOutput& pPaceChannelOutput) { mCertificationAuthorityReferences += pPaceChannelOutput.getCARcurr(); if (!pPaceChannelOutput.getCARprev().isEmpty()) @@ -91,52 +86,52 @@ void DIDAuthenticateResponseEAC1::setChallenge(const QByteArray& pValue) } -QDomElement DIDAuthenticateResponseEAC1::createDIDAuthenticateResponseEAC1Element() +void DIDAuthenticateResponseEAC1::createBodyElement() { - QDomElement element = mDoc.createElement(QStringLiteral("DIDAuthenticateResponse")); - element.setAttribute(getNamespacePrefix(Namespace::DEFAULT), getNamespace(Namespace::TECHSCHEMA)); - element.setAttribute(QStringLiteral("Profile"), getNamespace(Namespace::ECARD)); + mWriter.writeStartElement(QStringLiteral("DIDAuthenticateResponse")); + mWriter.writeAttribute(getNamespacePrefix(Namespace::DEFAULT), getNamespace(Namespace::TECHSCHEMA)); + mWriter.writeAttribute(QStringLiteral("Profile"), getNamespace(Namespace::ECARD)); - element.appendChild(createResultElement(*this)); - element.appendChild(createAuthenticationProtocolDataElement()); + createResultElement(*this); + createAuthenticationProtocolDataElement(); - return element; + mWriter.writeEndElement(); // DIDAuthenticateResponse } -QDomElement DIDAuthenticateResponseEAC1::createAuthenticationProtocolDataElement() +void DIDAuthenticateResponseEAC1::createAuthenticationProtocolDataElement() { - QDomElement element = mDoc.createElement(QStringLiteral("AuthenticationProtocolData")); + mWriter.writeStartElement(QStringLiteral("AuthenticationProtocolData")); - element.setAttribute(getNamespacePrefix(Namespace::XSI, QStringLiteral("type")), getNamespaceType(Namespace::TECHSCHEMA, QStringLiteral("EAC1OutputType"))); - element.setAttribute(QStringLiteral("Protocol"), QStringLiteral("urn:oid:1.3.162.15480.3.0.14.2")); + mWriter.writeAttribute(getNamespacePrefix(Namespace::XSI, QStringLiteral("type")), getNamespaceType(Namespace::TECHSCHEMA, QStringLiteral("EAC1OutputType"))); + mWriter.writeAttribute(QStringLiteral("Protocol"), QStringLiteral("urn:oid:1.3.162.15480.3.0.14.2")); if (!mCertificateHolderAuthorizationTemplate.isNull()) { - element.appendChild(createTextElement(QStringLiteral("CertificateHolderAuthorizationTemplate"), mCertificateHolderAuthorizationTemplate)); + writeTextElement(QStringLiteral("CertificateHolderAuthorizationTemplate"), mCertificateHolderAuthorizationTemplate); } for (const auto& reference : qAsConst(mCertificationAuthorityReferences)) { - element.appendChild(createTextElement(QStringLiteral("CertificationAuthorityReference"), reference)); + writeTextElement(QStringLiteral("CertificationAuthorityReference"), reference); } if (!mEfCardAccess.isNull()) { - element.appendChild(createTextElement(QStringLiteral("EFCardAccess"), mEfCardAccess)); + writeTextElement(QStringLiteral("EFCardAccess"), mEfCardAccess); } if (!mIdPICC.isNull()) { - element.appendChild(createTextElement(QStringLiteral("IDPICC"), mIdPICC)); + writeTextElement(QStringLiteral("IDPICC"), mIdPICC); } if (!mChallenge.isNull()) { - element.appendChild(createTextElement(QStringLiteral("Challenge"), mChallenge)); + writeTextElement(QStringLiteral("Challenge"), mChallenge); } - return element; + mWriter.writeEndElement(); // AuthenticationProtocolData } -Result DIDAuthenticateResponseEAC1::getResult() const +ECardApiResult DIDAuthenticateResponseEAC1::getResult() const { return ResponseType::getResult(); } diff --git a/src/core/paos/invoke/DidAuthenticateResponseEac1.h b/src/core/paos/invoke/DidAuthenticateResponseEac1.h index 27fec4f..f1597ad 100644 --- a/src/core/paos/invoke/DidAuthenticateResponseEac1.h +++ b/src/core/paos/invoke/DidAuthenticateResponseEac1.h @@ -15,7 +15,7 @@ namespace governikus { -class EstablishPACEChannelOutput; +class EstablishPaceChannelOutput; class DIDAuthenticateResponseEAC1 @@ -29,11 +29,11 @@ class DIDAuthenticateResponseEAC1 QByteArray mIdPICC; QByteArray mChallenge; - QDomElement createDIDAuthenticateResponseEAC1Element(); - QDomElement createAuthenticationProtocolDataElement(); + void createDIDAuthenticateResponseEAC1Element(); + void createAuthenticationProtocolDataElement(); - virtual QDomElement getDocumentStructure() override; - virtual Result getResult() const; + virtual void createBodyElement() override; + virtual ECardApiResult getResult() const; Q_DISABLE_COPY(DIDAuthenticateResponseEAC1) @@ -48,10 +48,10 @@ class DIDAuthenticateResponseEAC1 const QByteArray& getChallenge() const; void setCertificateHolderAuthorizationTemplate(const QByteArray& pValue); - void setCertificationAuthorityReference(const EstablishPACEChannelOutput& pPaceChannelOutput); + void setCertificationAuthorityReference(const EstablishPaceChannelOutput& pPaceChannelOutput); void setEFCardAccess(const QByteArray& pValue); void setIDPICC(const QByteArray& pValue); void setChallenge(const QByteArray& pValue); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/paos/invoke/DidAuthenticateResponseEac2.cpp b/src/core/paos/invoke/DidAuthenticateResponseEac2.cpp index 4417920..36302f3 100644 --- a/src/core/paos/invoke/DidAuthenticateResponseEac2.cpp +++ b/src/core/paos/invoke/DidAuthenticateResponseEac2.cpp @@ -18,49 +18,43 @@ DIDAuthenticateResponseEAC2::DIDAuthenticateResponseEAC2() } -QDomElement DIDAuthenticateResponseEAC2::createDIDAuthenticateResponseEAC2Element() +void DIDAuthenticateResponseEAC2::createBodyElement() { - QDomElement element = mDoc.createElement(QStringLiteral("DIDAuthenticateResponse")); - element.setAttribute(getNamespacePrefix(Namespace::DEFAULT), getNamespace(Namespace::TECHSCHEMA)); - element.setAttribute(QStringLiteral("Profile"), getNamespace(Namespace::ECARD)); + mWriter.writeStartElement(QStringLiteral("DIDAuthenticateResponse")); + mWriter.writeAttribute(getNamespacePrefix(Namespace::DEFAULT), getNamespace(Namespace::TECHSCHEMA)); + mWriter.writeAttribute(QStringLiteral("Profile"), getNamespace(Namespace::ECARD)); - element.appendChild(createResultElement(*this)); - element.appendChild(createAuthenticationProtocolDataElement()); + createResultElement(*this); + createAuthenticationProtocolDataElement(); - return element; + mWriter.writeEndElement(); // DIDAuthenticateResponse } -QDomElement DIDAuthenticateResponseEAC2::createAuthenticationProtocolDataElement() +void DIDAuthenticateResponseEAC2::createAuthenticationProtocolDataElement() { - QDomElement element = mDoc.createElement(QStringLiteral("AuthenticationProtocolData")); - - element.setAttribute(getNamespacePrefix(Namespace::XSI, QStringLiteral("type")), getNamespaceType(Namespace::TECHSCHEMA, QStringLiteral("EAC2OutputType"))); - element.setAttribute(QStringLiteral("Protocol"), QStringLiteral("urn:oid:1.3.162.15480.3.0.14.2")); + mWriter.writeStartElement(QStringLiteral("AuthenticationProtocolData")); + mWriter.writeAttribute(getNamespacePrefix(Namespace::XSI, QStringLiteral("type")), getNamespaceType(Namespace::TECHSCHEMA, QStringLiteral("EAC2OutputType"))); + mWriter.writeAttribute(QStringLiteral("Protocol"), QStringLiteral("urn:oid:1.3.162.15480.3.0.14.2")); if (!mEfCardSecurity.isNull()) { - element.appendChild(createTextElement(QStringLiteral("EFCardSecurity"), mEfCardSecurity)); + writeTextElement(QStringLiteral("EFCardSecurity"), mEfCardSecurity); } if (!mAuthenticationToken.isNull()) { - element.appendChild(createTextElement(QStringLiteral("AuthenticationToken"), mAuthenticationToken)); + writeTextElement(QStringLiteral("AuthenticationToken"), mAuthenticationToken); } if (!mNonce.isNull()) { - element.appendChild(createTextElement(QStringLiteral("Nonce"), mNonce)); + writeTextElement(QStringLiteral("Nonce"), mNonce); } if (!mChallenge.isNull()) { - element.appendChild(createTextElement(QStringLiteral("Challenge"), mChallenge)); + writeTextElement(QStringLiteral("Challenge"), mChallenge); } - return element; -} - -QDomElement DIDAuthenticateResponseEAC2::getDocumentStructure() -{ - return createEnvelopeElement(createDIDAuthenticateResponseEAC2Element(), getRelatesTo(), getMessageId()); + mWriter.writeEndElement(); // AuthenticationProtocolData } diff --git a/src/core/paos/invoke/DidAuthenticateResponseEac2.h b/src/core/paos/invoke/DidAuthenticateResponseEac2.h index a22a6b7..4504041 100644 --- a/src/core/paos/invoke/DidAuthenticateResponseEac2.h +++ b/src/core/paos/invoke/DidAuthenticateResponseEac2.h @@ -24,10 +24,10 @@ class DIDAuthenticateResponseEAC2 QByteArray mNonce; QByteArray mChallenge; - QDomElement createDIDAuthenticateResponseEAC2Element(); - QDomElement createAuthenticationProtocolDataElement(); + void createDIDAuthenticateResponseEAC2Element(); + void createAuthenticationProtocolDataElement(); - virtual QDomElement getDocumentStructure() override; + virtual void createBodyElement() override; Q_DISABLE_COPY(DIDAuthenticateResponseEAC2) @@ -40,4 +40,4 @@ class DIDAuthenticateResponseEAC2 void setChallenge(const QByteArray& pChallenge); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/paos/invoke/DidListResponse.cpp b/src/core/paos/invoke/DidListResponse.cpp index 9a81be0..c067625 100644 --- a/src/core/paos/invoke/DidListResponse.cpp +++ b/src/core/paos/invoke/DidListResponse.cpp @@ -12,29 +12,17 @@ DIDListResponse::DIDListResponse() } -QDomElement DIDListResponse::getDocumentStructure() +void DIDListResponse::createBodyElement() { - return createEnvelopeElement(createDidListResponseElement(), getRelatesTo(), getMessageId()); -} - - -QDomElement DIDListResponse::createDidListResponseElement() -{ - QDomElement element = mDoc.createElement(QStringLiteral("DIDListResponse")); - element.setAttribute(getNamespacePrefix(Namespace::TECHSCHEMA), getNamespace(Namespace::TECHSCHEMA)); - element.setAttribute(QStringLiteral("Profile"), getNamespace(Namespace::ECARD)); - - element.appendChild(createResultElement(*this)); - element.appendChild(createDidListElement()); - - return element; -} - - -QDomElement DIDListResponse::createDidListElement() -{ - QDomElement didNameList = mDoc.createElement(QStringLiteral("DIDNameList")); - // create enumeration for PIN (see TR) - didNameList.appendChild(createTextElement(QStringLiteral("DIDName"), QStringLiteral("PIN"))); - return didNameList; + mWriter.writeStartElement(QStringLiteral("DIDListResponse")); + mWriter.writeAttribute(getNamespacePrefix(Namespace::TECHSCHEMA), getNamespace(Namespace::TECHSCHEMA)); + mWriter.writeAttribute(QStringLiteral("Profile"), getNamespace(Namespace::ECARD)); + + createResultElement(*this); + + mWriter.writeStartElement(QStringLiteral("DIDNameList")); + mWriter.writeTextElement(QStringLiteral("DIDName"), QStringLiteral("PIN")); + mWriter.writeEndElement(); // DIDNameList + + mWriter.writeEndElement(); // DIDListResponse } diff --git a/src/core/paos/invoke/DidListResponse.h b/src/core/paos/invoke/DidListResponse.h index 62d213c..904f007 100644 --- a/src/core/paos/invoke/DidListResponse.h +++ b/src/core/paos/invoke/DidListResponse.h @@ -17,10 +17,7 @@ class DIDListResponse , public ResponseType { private: - QDomElement createDidListResponseElement(); - QDomElement createDidListElement(); - - virtual QDomElement getDocumentStructure() override; + virtual void createBodyElement() override; Q_DISABLE_COPY(DIDListResponse) @@ -28,4 +25,4 @@ class DIDListResponse DIDListResponse(); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/paos/invoke/DisconnectResponse.cpp b/src/core/paos/invoke/DisconnectResponse.cpp index 6bc46dd..fcc7b7a 100644 --- a/src/core/paos/invoke/DisconnectResponse.cpp +++ b/src/core/paos/invoke/DisconnectResponse.cpp @@ -13,25 +13,19 @@ DisconnectResponse::DisconnectResponse() } -QDomElement DisconnectResponse::getDocumentStructure() +void DisconnectResponse::createBodyElement() { - return createEnvelopeElement(createDisconnectResponse(), getRelatesTo(), getMessageId()); -} + mWriter.writeStartElement(QStringLiteral("DisconnectResponse")); + mWriter.writeAttribute(getNamespacePrefix(Namespace::DEFAULT), getNamespace(Namespace::TECHSCHEMA)); + mWriter.writeAttribute(QStringLiteral("Profile"), getNamespace(Namespace::ECARD)); - -QDomElement DisconnectResponse::createDisconnectResponse() -{ - QDomElement element = mDoc.createElement(QStringLiteral("DisconnectResponse")); - element.setAttribute(getNamespacePrefix(Namespace::DEFAULT), getNamespace(Namespace::TECHSCHEMA)); - element.setAttribute(QStringLiteral("Profile"), getNamespace(Namespace::ECARD)); - - element.appendChild(createResultElement(*this)); + createResultElement(*this); if (!mSlotHandle.isNull()) { - element.appendChild(createTextElement(QStringLiteral("SlotHandle"), mSlotHandle)); + mWriter.writeTextElement(QStringLiteral("SlotHandle"), mSlotHandle); } - return element; + mWriter.writeEndElement(); // DisconnectResponse } diff --git a/src/core/paos/invoke/DisconnectResponse.h b/src/core/paos/invoke/DisconnectResponse.h index 987c51e..440e868 100644 --- a/src/core/paos/invoke/DisconnectResponse.h +++ b/src/core/paos/invoke/DisconnectResponse.h @@ -25,13 +25,13 @@ class DisconnectResponse Q_DISABLE_COPY(DisconnectResponse) - QDomElement createDisconnectResponse(); + void createDisconnectResponse(); - virtual QDomElement getDocumentStructure() override; + virtual void createBodyElement() override; public: DisconnectResponse(); void setSlotHandle(const QString& slotHandle); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/paos/invoke/InitializeFrameworkResponse.cpp b/src/core/paos/invoke/InitializeFrameworkResponse.cpp index 201c587..05c2038 100644 --- a/src/core/paos/invoke/InitializeFrameworkResponse.cpp +++ b/src/core/paos/invoke/InitializeFrameworkResponse.cpp @@ -12,32 +12,27 @@ InitializeFrameworkResponse::InitializeFrameworkResponse() } -QDomElement InitializeFrameworkResponse::getDocumentStructure() +void InitializeFrameworkResponse::createBodyElement() { - return createEnvelopeElement(createInitializeFrameworkResponse(), getRelatesTo(), getMessageId()); + mWriter.writeStartElement(QStringLiteral("InitializeFrameworkResponse")); + + mWriter.writeAttribute(getNamespacePrefix(Namespace::DEFAULT), getNamespace(Namespace::ECARD)); + mWriter.writeAttribute(QStringLiteral("Profile"), getNamespace(Namespace::ECARD)); + + createResultElement(*this); + createVersionElement(); + + mWriter.writeEndElement(); // InitializeFrameworkResponse } -QDomElement InitializeFrameworkResponse::createInitializeFrameworkResponse() +void InitializeFrameworkResponse::createVersionElement() { - QDomElement element = mDoc.createElement(QStringLiteral("InitializeFrameworkResponse")); - element.setAttribute(getNamespacePrefix(Namespace::DEFAULT), getNamespace(Namespace::ECARD)); - element.setAttribute(QStringLiteral("Profile"), getNamespace(Namespace::ECARD)); + mWriter.writeStartElement(QStringLiteral("Version")); - element.appendChild(createResultElement(*this)); - element.appendChild(createVersionElement()); + mWriter.writeTextElement(QStringLiteral("Major"), mSupportedAPI.getMajor()); + mWriter.writeTextElement(QStringLiteral("Minor"), mSupportedAPI.getMinor()); + mWriter.writeTextElement(QStringLiteral("SubMinor"), mSupportedAPI.getSubminor()); - return element; -} - - -QDomElement InitializeFrameworkResponse::createVersionElement() -{ - QDomElement element = mDoc.createElement(QStringLiteral("Version")); - - element.appendChild(createTextElement(QStringLiteral("Major"), mSupportedAPI.getMajor())); - element.appendChild(createTextElement(QStringLiteral("Minor"), mSupportedAPI.getMinor())); - element.appendChild(createTextElement(QStringLiteral("SubMinor"), mSupportedAPI.getSubminor())); - - return element; + mWriter.writeEndElement(); // InitializeFrameworkResponse } diff --git a/src/core/paos/invoke/InitializeFrameworkResponse.h b/src/core/paos/invoke/InitializeFrameworkResponse.h index 151fe6b..16bee08 100644 --- a/src/core/paos/invoke/InitializeFrameworkResponse.h +++ b/src/core/paos/invoke/InitializeFrameworkResponse.h @@ -20,10 +20,10 @@ class InitializeFrameworkResponse private: SupportedAPI mSupportedAPI; - QDomElement createVersionElement(); - QDomElement createInitializeFrameworkResponse(); + void createVersionElement(); + void createInitializeFrameworkResponse(); - virtual QDomElement getDocumentStructure() override; + virtual void createBodyElement() override; Q_DISABLE_COPY(InitializeFrameworkResponse) @@ -31,4 +31,4 @@ class InitializeFrameworkResponse InitializeFrameworkResponse(); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/paos/invoke/PaosCreator.cpp b/src/core/paos/invoke/PaosCreator.cpp index 608f441..e5aaa8b 100644 --- a/src/core/paos/invoke/PaosCreator.cpp +++ b/src/core/paos/invoke/PaosCreator.cpp @@ -34,15 +34,16 @@ const QMap PaosCreator::mNamespace = { }; PaosCreator::PaosCreator() - : mDoc() + : mContent() + , mRelatedMessageId() + , mWriter(&mContent) { - + mWriter.setAutoFormatting(true); } PaosCreator::~PaosCreator() { - } @@ -85,125 +86,117 @@ QString PaosCreator::getNamespacePrefix(Namespace pPrefix, const QString& pSuffi QByteArray PaosCreator::marshall() { - if (mDoc.isNull()) + if (mContent.isNull()) { - mDoc.appendChild(getDocumentStructure()); + createEnvelopeElement(); } - return mDoc.toByteArray(); + return mContent; } -QDomElement PaosCreator::createTextElement(const QString& pName, const QByteArray& pContent) +void PaosCreator::setRelatedMessageId(const QString& pId) { - return createTextElement(pName, QString::fromLatin1(pContent)); + mRelatedMessageId = pId; } -QDomElement PaosCreator::createTextElement(const QString& pName, const QString& pContent) +void PaosCreator::createHeaderElement() { - QDomElement element = mDoc.createElement(pName); - element.appendChild(mDoc.createTextNode(pContent)); - return element; -} + mWriter.writeStartElement(getNamespacePrefix(Namespace::SOAP, QStringLiteral("Header"))); - -QDomElement PaosCreator::createTextElement(PaosCreator::Namespace pNamespace, const QString& pName, const QByteArray& pContent) -{ - return createTextElement(pNamespace, pName, QString::fromLatin1(pContent)); -} - - -QDomElement PaosCreator::createTextElement(PaosCreator::Namespace pNamespace, const QString& pName, const QString& pContent) -{ - QDomElement element = mDoc.createElement(getNamespacePrefix(pNamespace, pName)); - element.appendChild(mDoc.createTextNode(pContent)); - return element; -} - - -QDomElement PaosCreator::createHeaderElement(const QString& pRelatesTo, const QString& pMessageID) -{ - Q_ASSERT(!pMessageID.isEmpty()); - - QDomElement headerElement = mDoc.createElement(getNamespacePrefix(Namespace::SOAP, QStringLiteral("Header"))); - - QDomElement paosElement = mDoc.createElement(getNamespacePrefix(Namespace::PAOS, QStringLiteral("PAOS"))); - headerElement.appendChild(paosElement); - paosElement.setAttribute(getNamespacePrefix(Namespace::SOAP, QStringLiteral("actor")), QStringLiteral("http://schemas.xmlsoap.org/soap/actor/next")); - paosElement.setAttribute(getNamespacePrefix(Namespace::SOAP, QStringLiteral("mustUnderstand")), QStringLiteral("1")); - QDomElement versionElement = createTextElement(getNamespaceType(Namespace::PAOS, QStringLiteral("Version")), getNamespace(Namespace::PAOS)); - paosElement.appendChild(versionElement); - QDomElement endpointReferenceElement = mDoc.createElement(getNamespaceType(Namespace::PAOS, QStringLiteral("EndpointReference"))); - paosElement.appendChild(endpointReferenceElement); - QDomElement addressElement = createTextElement(getNamespaceType(Namespace::PAOS, QStringLiteral("Address")), QStringLiteral("http://www.projectliberty.org/2006/01/role/paos")); - endpointReferenceElement.appendChild(addressElement); - QDomElement metaDataElement = mDoc.createElement(getNamespaceType(Namespace::PAOS, QStringLiteral("MetaData"))); - endpointReferenceElement.appendChild(metaDataElement); - QDomElement serviceTypeElement = createTextElement(getNamespaceType(Namespace::PAOS, QStringLiteral("ServiceType")), QStringLiteral("http://www.bsi.bund.de/ecard/api/1.1/PAOS/GetNextCommand")); - metaDataElement.appendChild(serviceTypeElement); - - QDomElement replyToElement = mDoc.createElement(getNamespaceType(Namespace::ADDRESSING, QStringLiteral("ReplyTo"))); - headerElement.appendChild(replyToElement); - replyToElement.appendChild(createTextElement(getNamespaceType(Namespace::ADDRESSING, QStringLiteral("Address")), QStringLiteral("http://www.projectliberty.org/2006/02/role/paos"))); - - if (!pRelatesTo.isNull()) + mWriter.writeStartElement(getNamespacePrefix(Namespace::PAOS, QStringLiteral("PAOS"))); { - QDomElement relatesToElement = createTextElement(getNamespaceType(Namespace::ADDRESSING, QStringLiteral("RelatesTo")), pRelatesTo); - headerElement.appendChild(relatesToElement); + mWriter.writeAttribute(getNamespacePrefix(Namespace::SOAP, QStringLiteral("mustUnderstand")), QStringLiteral("1")); + mWriter.writeAttribute(getNamespacePrefix(Namespace::SOAP, QStringLiteral("actor")), QStringLiteral("http://schemas.xmlsoap.org/soap/actor/next")); + mWriter.writeTextElement(getNamespaceType(Namespace::PAOS, QStringLiteral("Version")), getNamespace(Namespace::PAOS)); + + mWriter.writeStartElement(getNamespaceType(Namespace::PAOS, QStringLiteral("EndpointReference"))); + { + mWriter.writeTextElement(getNamespaceType(Namespace::PAOS, QStringLiteral("Address")), QStringLiteral("http://www.projectliberty.org/2006/01/role/paos")); + + mWriter.writeStartElement(getNamespaceType(Namespace::PAOS, QStringLiteral("MetaData"))); + { + mWriter.writeTextElement(getNamespaceType(Namespace::PAOS, QStringLiteral("ServiceType")), QStringLiteral("http://www.bsi.bund.de/ecard/api/1.1/PAOS/GetNextCommand")); + } + mWriter.writeEndElement(); // MetaData + } + mWriter.writeEndElement(); // EndpointReference + } + mWriter.writeEndElement(); // PAOS + + mWriter.writeStartElement(getNamespaceType(Namespace::ADDRESSING, QStringLiteral("ReplyTo"))); + { + mWriter.writeTextElement(getNamespaceType(Namespace::ADDRESSING, QStringLiteral("Address")), QStringLiteral("http://www.projectliberty.org/2006/02/role/paos")); + } + mWriter.writeEndElement(); // ReplyTo + + if (!mRelatedMessageId.isNull()) + { + mWriter.writeTextElement(getNamespaceType(Namespace::ADDRESSING, QStringLiteral("RelatesTo")), mRelatedMessageId); } - QDomElement messageIdElement = createTextElement(getNamespaceType(Namespace::ADDRESSING, QStringLiteral("MessageID")), pMessageID); - headerElement.appendChild(messageIdElement); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + const auto& id = QStringLiteral("urn:uuid:") + QUuid::createUuid().toString(QUuid::WithoutBraces); +#else + const auto& id = QStringLiteral("urn:uuid:") + QUuid::createUuid().toString().mid(1, 36); +#endif + mWriter.writeTextElement(getNamespaceType(Namespace::ADDRESSING, QStringLiteral("MessageID")), id); - return headerElement; + mWriter.writeEndElement(); // Header } -QDomElement PaosCreator::createBodyElement(const QDomElement& pBody) +void PaosCreator::writeTextElement(const QString& pQualifiedName, const QByteArray& pText) { - QDomElement element = mDoc.createElement(getNamespacePrefix(Namespace::SOAP, QStringLiteral("Body"))); - element.appendChild(pBody); - return element; + mWriter.writeTextElement(pQualifiedName, QString::fromLatin1(pText)); } -QDomElement PaosCreator::createEnvelopeElement(const QDomElement& pBody, const QString& pRelatesTo, const QString& pMessageID) +void PaosCreator::createEnvelopeElement() { - QDomElement element = mDoc.createElement(getNamespacePrefix(Namespace::SOAP, QStringLiteral("Envelope"))); - element.setAttribute(getNamespacePrefix(Namespace::SOAP), getNamespace(Namespace::SOAP)); - element.setAttribute(getNamespacePrefix(Namespace::XSD), getNamespace(Namespace::XSD)); - element.setAttribute(getNamespacePrefix(Namespace::XSI), getNamespace(Namespace::XSI)); - element.setAttribute(getNamespacePrefix(Namespace::PAOS), getNamespace(Namespace::PAOS)); - element.setAttribute(getNamespacePrefix(Namespace::ADDRESSING), getNamespace(Namespace::ADDRESSING)); - element.setAttribute(getNamespacePrefix(Namespace::DSS), getNamespace(Namespace::DSS)); - element.setAttribute(getNamespacePrefix(Namespace::ECARD), getNamespace(Namespace::ECARD)); - element.setAttribute(getNamespacePrefix(Namespace::TECHSCHEMA), getNamespace(Namespace::TECHSCHEMA)); + mWriter.writeStartDocument(); - element.appendChild(createHeaderElement(pRelatesTo, pMessageID)); - element.appendChild(createBodyElement(pBody)); + mWriter.writeStartElement(getNamespacePrefix(Namespace::SOAP, QStringLiteral("Envelope"))); + mWriter.writeAttribute(getNamespacePrefix(Namespace::SOAP), getNamespace(Namespace::SOAP)); + mWriter.writeAttribute(getNamespacePrefix(Namespace::XSD), getNamespace(Namespace::XSD)); + mWriter.writeAttribute(getNamespacePrefix(Namespace::XSI), getNamespace(Namespace::XSI)); + mWriter.writeAttribute(getNamespacePrefix(Namespace::PAOS), getNamespace(Namespace::PAOS)); + mWriter.writeAttribute(getNamespacePrefix(Namespace::ADDRESSING), getNamespace(Namespace::ADDRESSING)); + mWriter.writeAttribute(getNamespacePrefix(Namespace::DSS), getNamespace(Namespace::DSS)); + mWriter.writeAttribute(getNamespacePrefix(Namespace::ECARD), getNamespace(Namespace::ECARD)); + mWriter.writeAttribute(getNamespacePrefix(Namespace::TECHSCHEMA), getNamespace(Namespace::TECHSCHEMA)); - return element; + createHeaderElement(); + + mWriter.writeStartElement(getNamespacePrefix(Namespace::SOAP, QStringLiteral("Body"))); + createBodyElement(); + mWriter.writeEndElement(); // Body + + mWriter.writeEndElement(); // Envelope + + mWriter.writeEndDocument(); } -QDomElement PaosCreator::createResultElement(const ResponseType& pResponse) +void PaosCreator::createResultElement(const ResponseType& pResponse) { - QDomElement element = mDoc.createElement(QStringLiteral("Result")); - element.setAttribute(getNamespacePrefix(Namespace::DEFAULT), getNamespace(Namespace::DSS)); + mWriter.writeStartElement(QStringLiteral("Result")); + mWriter.writeAttribute(getNamespacePrefix(Namespace::DEFAULT), getNamespace(Namespace::DSS)); - const Result& result = pResponse.getResult(); - element.appendChild(createTextElement(QStringLiteral("ResultMajor"), result.getMajorString())); - if (result.getMinor() != GlobalStatus::Code::No_Error) + const ECardApiResult& result = pResponse.getResult(); + mWriter.writeTextElement(QStringLiteral("ResultMajor"), result.getMajorString()); + if (result.getMinor() != ECardApiResult::Minor::null) { - element.appendChild(createTextElement(QStringLiteral("ResultMinor"), result.getMinorString())); + mWriter.writeTextElement(QStringLiteral("ResultMinor"), result.getMinorString()); } if (!result.getMessage().isNull()) { - QDomElement resultElement = createTextElement(QStringLiteral("ResultMessage"), result.getMessage()); - resultElement.setAttribute(QStringLiteral("xml:lang"), result.getMessageLang()); - element.appendChild(resultElement); + mWriter.writeStartElement(QStringLiteral("ResultMessage")); + mWriter.writeAttribute(QStringLiteral("xml:lang"), result.getMessageLang()); + mWriter.writeCharacters(result.getMessage()); + mWriter.writeEndElement(); // ResultMessage } - return element; + mWriter.writeEndElement(); // Result } diff --git a/src/core/paos/invoke/PaosCreator.h b/src/core/paos/invoke/PaosCreator.h index a860fff..8c91d84 100644 --- a/src/core/paos/invoke/PaosCreator.h +++ b/src/core/paos/invoke/PaosCreator.h @@ -8,7 +8,7 @@ #include "paos/ResponseType.h" -#include +#include class test_PaosCreator; @@ -31,19 +31,19 @@ class PaosCreator Q_DISABLE_COPY(PaosCreator) + QByteArray mContent; + QString mRelatedMessageId; + + void createEnvelopeElement(); + void createHeaderElement(); + protected: - QDomDocument mDoc; + QXmlStreamWriter mWriter; - virtual QDomElement getDocumentStructure() = 0; - QDomElement createTextElement(const QString& pName, const QByteArray& pContent); - QDomElement createTextElement(const QString& pName, const QString& pContent); - QDomElement createTextElement(Namespace pNamespace, const QString& pName, const QByteArray& pContent); - QDomElement createTextElement(Namespace pNamespace, const QString& pName, const QString& pContent); - QDomElement createBodyElement(const QDomElement& pBody); - QDomElement createHeaderElement(const QString& pRrelatesTo, const QString& pMessageID); - QDomElement createEnvelopeElement(const QDomElement& pBody, const QString& pRelatesTo, const QString& pMessageID); + void writeTextElement(const QString& pQualifiedName, const QByteArray& pText); + virtual void createBodyElement() = 0; - QDomElement createResultElement(const ResponseType& pResponse); + void createResultElement(const ResponseType& pResponse); PaosCreator(); virtual ~PaosCreator(); @@ -57,9 +57,10 @@ class PaosCreator */ QByteArray marshall(); + void setRelatedMessageId(const QString& pId); static QString getNamespace(Namespace pPrefix); static QString getNamespacePrefix(Namespace pPrefix, const QString& pSuffix = QString()); static QString getNamespaceType(Namespace pPrefix, const QString& pType); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/paos/invoke/StartPaos.cpp b/src/core/paos/invoke/StartPaos.cpp index 2b6390d..c506760 100644 --- a/src/core/paos/invoke/StartPaos.cpp +++ b/src/core/paos/invoke/StartPaos.cpp @@ -19,65 +19,59 @@ StartPaos::StartPaos(const QByteArray& pSessionId) } -QDomElement StartPaos::createConnectionHandleElement() +void StartPaos::createConnectionHandleElement() { - QDomElement element = mDoc.createElement(QStringLiteral("ConnectionHandle")); - element.setAttribute(getNamespacePrefix(Namespace::XSI), getNamespace(Namespace::XSI)); - element.setAttribute(getNamespacePrefix(Namespace::XSI, QStringLiteral("type")), QStringLiteral("ConnectionHandleType")); + mWriter.writeStartElement(QStringLiteral("ConnectionHandle")); + mWriter.writeAttribute(getNamespacePrefix(Namespace::XSI), getNamespace(Namespace::XSI)); + mWriter.writeAttribute(getNamespacePrefix(Namespace::XSI, QStringLiteral("type")), QStringLiteral("ConnectionHandleType")); - element.appendChild(createTextElement(QStringLiteral("CardApplication"), QStringLiteral("e80704007f00070302"))); - element.appendChild(createTextElement(QStringLiteral("SlotHandle"), QStringLiteral("00"))); + mWriter.writeTextElement(QStringLiteral("CardApplication"), QStringLiteral("e80704007f00070302")); + mWriter.writeTextElement(QStringLiteral("SlotHandle"), QStringLiteral("00")); - return element; + mWriter.writeEndElement(); // ConnectionHandle } -QDomElement StartPaos::createUserAgentElement() +void StartPaos::createUserAgentElement() { - QDomElement element = mDoc.createElement(QStringLiteral("UserAgent")); + mWriter.writeStartElement(QStringLiteral("UserAgent")); - element.appendChild(createTextElement(QStringLiteral("Name"), mUserAgent.getName())); - element.appendChild(createTextElement(QStringLiteral("VersionMajor"), mUserAgent.getVersionMajor())); - element.appendChild(createTextElement(QStringLiteral("VersionMinor"), mUserAgent.getVersionMinor())); - element.appendChild(createTextElement(QStringLiteral("VersionSubminor"), mUserAgent.getVersionSubminor())); + mWriter.writeTextElement(QStringLiteral("Name"), mUserAgent.getName()); + mWriter.writeTextElement(QStringLiteral("VersionMajor"), mUserAgent.getVersionMajor()); + mWriter.writeTextElement(QStringLiteral("VersionMinor"), mUserAgent.getVersionMinor()); + mWriter.writeTextElement(QStringLiteral("VersionSubminor"), mUserAgent.getVersionSubminor()); - return element; + mWriter.writeEndElement(); // UserAgent } -QDomElement StartPaos::createSupportedAPIVersionsElement() +void StartPaos::createSupportedAPIVersionsElement() { - QDomElement element = mDoc.createElement(QStringLiteral("SupportedAPIVersions")); + mWriter.writeStartElement(QStringLiteral("SupportedAPIVersions")); - element.appendChild(createTextElement(QStringLiteral("Major"), mSupportedAPI.getMajor())); - element.appendChild(createTextElement(QStringLiteral("Minor"), mSupportedAPI.getMinor())); - element.appendChild(createTextElement(QStringLiteral("Subminor"), mSupportedAPI.getSubminor())); + mWriter.writeTextElement(QStringLiteral("Major"), mSupportedAPI.getMajor()); + mWriter.writeTextElement(QStringLiteral("Minor"), mSupportedAPI.getMinor()); + mWriter.writeTextElement(QStringLiteral("Subminor"), mSupportedAPI.getSubminor()); - return element; + mWriter.writeEndElement(); // SupportedAPIVersions } -QDomElement StartPaos::createSessionIdentifierElement() +void StartPaos::createSessionIdentifierElement() { - return createTextElement(QStringLiteral("SessionIdentifier"), mSessionId); + writeTextElement(QStringLiteral("SessionIdentifier"), mSessionId); } -QDomElement StartPaos::createStartPaosElement() +void StartPaos::createBodyElement() { - QDomElement element = mDoc.createElement(QStringLiteral("StartPAOS")); - element.setAttribute(getNamespacePrefix(Namespace::DEFAULT), getNamespace(Namespace::TECHSCHEMA)); + mWriter.writeStartElement(QStringLiteral("StartPAOS")); + mWriter.writeAttribute(getNamespacePrefix(Namespace::DEFAULT), getNamespace(Namespace::TECHSCHEMA)); - element.appendChild(createSessionIdentifierElement()); - element.appendChild(createConnectionHandleElement()); - element.appendChild(createUserAgentElement()); - element.appendChild(createSupportedAPIVersionsElement()); + createSessionIdentifierElement(); + createConnectionHandleElement(); + createUserAgentElement(); + createSupportedAPIVersionsElement(); - return element; -} - - -QDomElement StartPaos::getDocumentStructure() -{ - return createEnvelopeElement(createStartPaosElement(), getRelatesTo(), getMessageId()); + mWriter.writeEndElement(); // StartPAOS } diff --git a/src/core/paos/invoke/StartPaos.h b/src/core/paos/invoke/StartPaos.h index fb706dd..1ab981b 100644 --- a/src/core/paos/invoke/StartPaos.h +++ b/src/core/paos/invoke/StartPaos.h @@ -28,13 +28,12 @@ class StartPaos const UserAgent mUserAgent; const SupportedAPI mSupportedAPI; - QDomElement createStartPaosElement(); - QDomElement createSessionIdentifierElement(); - QDomElement createConnectionHandleElement(); - QDomElement createUserAgentElement(); - QDomElement createSupportedAPIVersionsElement(); + void createSessionIdentifierElement(); + void createConnectionHandleElement(); + void createUserAgentElement(); + void createSupportedAPIVersionsElement(); - virtual QDomElement getDocumentStructure() override; + virtual void createBodyElement() override; Q_DISABLE_COPY(StartPaos) @@ -42,4 +41,4 @@ class StartPaos StartPaos(const QByteArray& pSessionId); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/paos/invoke/TransmitResponse.cpp b/src/core/paos/invoke/TransmitResponse.cpp index e40f869..711a40d 100644 --- a/src/core/paos/invoke/TransmitResponse.cpp +++ b/src/core/paos/invoke/TransmitResponse.cpp @@ -13,26 +13,20 @@ TransmitResponse::TransmitResponse() } -QDomElement TransmitResponse::getDocumentStructure() +void TransmitResponse::createBodyElement() { - return createEnvelopeElement(createTransmitResponse(), getRelatesTo(), getMessageId()); -} + mWriter.writeStartElement(QStringLiteral("TransmitResponse")); + mWriter.writeAttribute(getNamespacePrefix(Namespace::DEFAULT), getNamespace(Namespace::TECHSCHEMA)); + mWriter.writeAttribute(QStringLiteral("Profile"), getNamespace(Namespace::ECARD)); - -QDomElement TransmitResponse::createTransmitResponse() -{ - QDomElement element = mDoc.createElement(QStringLiteral("TransmitResponse")); - element.setAttribute(getNamespacePrefix(Namespace::DEFAULT), getNamespace(Namespace::TECHSCHEMA)); - element.setAttribute(QStringLiteral("Profile"), getNamespace(Namespace::ECARD)); - - element.appendChild(createResultElement(*this)); + createResultElement(*this); for (const auto& apdu : qAsConst(mOutputApdus)) { - element.appendChild(createTextElement(QStringLiteral("OutputAPDU"), apdu)); + writeTextElement(QStringLiteral("OutputAPDU"), apdu); } - return element; + mWriter.writeEndElement(); // TransmitResponse } diff --git a/src/core/paos/invoke/TransmitResponse.h b/src/core/paos/invoke/TransmitResponse.h index 60d14fd..f951e0b 100644 --- a/src/core/paos/invoke/TransmitResponse.h +++ b/src/core/paos/invoke/TransmitResponse.h @@ -23,9 +23,9 @@ class TransmitResponse private: QByteArrayList mOutputApdus; - QDomElement createTransmitResponse(); + void createTransmitResponse(); - virtual QDomElement getDocumentStructure() override; + virtual void createBodyElement() override; Q_DISABLE_COPY(TransmitResponse) @@ -35,4 +35,4 @@ class TransmitResponse void setOutputApdus(const QByteArrayList& outputApdus); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/paos/retrieve/DidAuthenticateEac1.h b/src/core/paos/retrieve/DidAuthenticateEac1.h index ceb8465..069eb63 100644 --- a/src/core/paos/retrieve/DidAuthenticateEac1.h +++ b/src/core/paos/retrieve/DidAuthenticateEac1.h @@ -15,11 +15,6 @@ #include -class test_StatePrepareChat; -class test_StatePreVerification; -class test_StateExtractCvcsFromEac1InputType; -class test_StateProcessCertificatesFromEac2; -class test_StateCertificateDescriptionCheck; namespace governikus { @@ -29,12 +24,12 @@ class DIDAuthenticateEAC1 : public PaosMessage { friend class DidAuthenticateEac1Parser; - friend class ::test_StatePrepareChat; friend class TestAuthContext; friend class ::test_StatePreVerification; friend class ::test_StateExtractCvcsFromEac1InputType; friend class ::test_StateProcessCertificatesFromEac2; friend class ::test_StateCertificateDescriptionCheck; + friend class ::test_AuthModel; private: ConnectionHandle mConnectionHandle; @@ -62,4 +57,4 @@ class DIDAuthenticateEAC1 const QString& getTransactionInfo() const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/paos/retrieve/DidAuthenticateEac1Parser.h b/src/core/paos/retrieve/DidAuthenticateEac1Parser.h index 7ebdc3b..0418402 100644 --- a/src/core/paos/retrieve/DidAuthenticateEac1Parser.h +++ b/src/core/paos/retrieve/DidAuthenticateEac1Parser.h @@ -32,4 +32,4 @@ class DidAuthenticateEac1Parser QScopedPointer mDidAuthenticateEac1; }; -} +} // namespace governikus diff --git a/src/core/paos/retrieve/DidAuthenticateEac2.h b/src/core/paos/retrieve/DidAuthenticateEac2.h index be731c2..569ca1a 100644 --- a/src/core/paos/retrieve/DidAuthenticateEac2.h +++ b/src/core/paos/retrieve/DidAuthenticateEac2.h @@ -10,7 +10,6 @@ #include "paos/element/Eac2InputType.h" #include "paos/PaosMessage.h" -class test_StateProcessCertificatesFromEac2; namespace governikus { @@ -42,4 +41,4 @@ class DIDAuthenticateEAC2 const QByteArrayList& getCvCertificatesAsBinary() const; }; -} +} // namespace governikus diff --git a/src/core/paos/retrieve/DidAuthenticateEac2Parser.h b/src/core/paos/retrieve/DidAuthenticateEac2Parser.h index 0bd6e51..f633bdf 100644 --- a/src/core/paos/retrieve/DidAuthenticateEac2Parser.h +++ b/src/core/paos/retrieve/DidAuthenticateEac2Parser.h @@ -34,4 +34,4 @@ class DidAuthenticateEac2Parser QScopedPointer mDidAuthenticateEac2; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/paos/retrieve/DidAuthenticateEacAdditional.h b/src/core/paos/retrieve/DidAuthenticateEacAdditional.h index cb0f85a..99b414b 100644 --- a/src/core/paos/retrieve/DidAuthenticateEacAdditional.h +++ b/src/core/paos/retrieve/DidAuthenticateEacAdditional.h @@ -36,4 +36,4 @@ class DIDAuthenticateEACAdditional const QString& getSignature() const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/paos/retrieve/DidAuthenticateEacAdditionalParser.cpp b/src/core/paos/retrieve/DidAuthenticateEacAdditionalParser.cpp index 96c9b4f..774840b 100644 --- a/src/core/paos/retrieve/DidAuthenticateEacAdditionalParser.cpp +++ b/src/core/paos/retrieve/DidAuthenticateEacAdditionalParser.cpp @@ -16,7 +16,6 @@ using namespace governikus; DidAuthenticateEacAdditionalParser::DidAuthenticateEacAdditionalParser() : PaosParser(QStringLiteral("DIDAuthenticate")) { - } diff --git a/src/core/paos/retrieve/DidAuthenticateEacAdditionalParser.h b/src/core/paos/retrieve/DidAuthenticateEacAdditionalParser.h index 1cc8223..6b0d348 100644 --- a/src/core/paos/retrieve/DidAuthenticateEacAdditionalParser.h +++ b/src/core/paos/retrieve/DidAuthenticateEacAdditionalParser.h @@ -34,4 +34,4 @@ class DidAuthenticateEacAdditionalParser QScopedPointer mDidAuthenticateEacAdditional; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/paos/retrieve/DidList.cpp b/src/core/paos/retrieve/DidList.cpp index d6f0ec5..77957b3 100644 --- a/src/core/paos/retrieve/DidList.cpp +++ b/src/core/paos/retrieve/DidList.cpp @@ -21,16 +21,16 @@ DIDList::DIDList(const QByteArray& xmlData) void DIDList::parse() { - QStringList expectedElements; - expectedElements.push_back(QStringLiteral("RelatesTo")); - expectedElements.push_back(QStringLiteral("MessageID")); - - expectedElements.push_back(QStringLiteral("ContextHandle")); - expectedElements.push_back(QStringLiteral("IFDName")); - expectedElements.push_back(QStringLiteral("SlotIndex")); - expectedElements.push_back(QStringLiteral("CardApplication")); - expectedElements.push_back(QStringLiteral("SlotHandle")); - expectedElements.push_back(QStringLiteral("RecognitionInfo")); + const QStringList expectedElements({ + QStringLiteral("RelatesTo"), + QStringLiteral("MessageID"), + QStringLiteral("ContextHandle"), + QStringLiteral("IFDName"), + QStringLiteral("SlotIndex"), + QStringLiteral("CardApplication"), + QStringLiteral("SlotHandle"), + QStringLiteral("RecognitionInfo") + }); detectStartElements(expectedElements); } diff --git a/src/core/paos/retrieve/DidList.h b/src/core/paos/retrieve/DidList.h index aba87a7..8a99964 100644 --- a/src/core/paos/retrieve/DidList.h +++ b/src/core/paos/retrieve/DidList.h @@ -29,4 +29,4 @@ class DIDList const ConnectionHandle& getConnectionHandle() const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/paos/retrieve/Disconnect.cpp b/src/core/paos/retrieve/Disconnect.cpp index 88beb18..d0d7a61 100644 --- a/src/core/paos/retrieve/Disconnect.cpp +++ b/src/core/paos/retrieve/Disconnect.cpp @@ -21,11 +21,12 @@ Disconnect::~Disconnect() void Disconnect::parse() { - QStringList expectedElements; - expectedElements.push_back(QStringLiteral("RelatesTo")); - expectedElements.push_back(QStringLiteral("MessageID")); + const QStringList expectedElements({ + QStringLiteral("RelatesTo"), + QStringLiteral("MessageID"), + QStringLiteral("SlotHandle") + }); - expectedElements.push_back(QStringLiteral("SlotHandle")); detectStartElements(expectedElements); } diff --git a/src/core/paos/retrieve/Disconnect.h b/src/core/paos/retrieve/Disconnect.h index 94e08b1..62c8aea 100644 --- a/src/core/paos/retrieve/Disconnect.h +++ b/src/core/paos/retrieve/Disconnect.h @@ -30,4 +30,4 @@ class Disconnect const QString& getSlotHandle() const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/paos/retrieve/InitializeFramework.cpp b/src/core/paos/retrieve/InitializeFramework.cpp index 8ded29e..daae5b2 100644 --- a/src/core/paos/retrieve/InitializeFramework.cpp +++ b/src/core/paos/retrieve/InitializeFramework.cpp @@ -19,9 +19,11 @@ InitializeFramework::InitializeFramework(const QByteArray& pXmlData) void InitializeFramework::parse() { - QStringList expectedElements; - expectedElements.push_back(QStringLiteral("RelatesTo")); - expectedElements.push_back(QStringLiteral("MessageID")); + const QStringList expectedElements({ + QStringLiteral("RelatesTo"), + QStringLiteral("MessageID") + }); + detectStartElements(expectedElements); } diff --git a/src/core/paos/retrieve/InitializeFramework.h b/src/core/paos/retrieve/InitializeFramework.h index a615637..24e5742 100644 --- a/src/core/paos/retrieve/InitializeFramework.h +++ b/src/core/paos/retrieve/InitializeFramework.h @@ -24,4 +24,4 @@ class InitializeFramework InitializeFramework(const QByteArray& pXmlData); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/paos/retrieve/PaosParser.cpp b/src/core/paos/retrieve/PaosParser.cpp index f281560..124d55d 100644 --- a/src/core/paos/retrieve/PaosParser.cpp +++ b/src/core/paos/retrieve/PaosParser.cpp @@ -9,7 +9,7 @@ using namespace governikus; PaosParser::PaosParser(const QString& pMessageName) - : ElementParser(QSharedPointer(new QXmlStreamReader())) + : ElementParser(QSharedPointer::create()) , mMessageName(pMessageName) , mMessageID() , mRelatesTo() diff --git a/src/core/paos/retrieve/PaosParser.h b/src/core/paos/retrieve/PaosParser.h index b164b5c..dbd96d4 100644 --- a/src/core/paos/retrieve/PaosParser.h +++ b/src/core/paos/retrieve/PaosParser.h @@ -35,4 +35,4 @@ class PaosParser QString mRelatesTo; }; -} +} // namespace governikus diff --git a/src/core/paos/retrieve/StartPaosResponse.cpp b/src/core/paos/retrieve/StartPaosResponse.cpp index 9bc322c..451a90e 100644 --- a/src/core/paos/retrieve/StartPaosResponse.cpp +++ b/src/core/paos/retrieve/StartPaosResponse.cpp @@ -11,19 +11,19 @@ StartPaosResponse::StartPaosResponse(const QByteArray& pXmlData) , ElementDetector(pXmlData) { parse(); - setResult(Result(mResultMajor, mResultMinor, mResultMessage, Origin::Server)); + setResult(ECardApiResult(mResultMajor, mResultMinor, mResultMessage, ECardApiResult::Origin::Server)); } void StartPaosResponse::parse() { - QStringList expectedElements; - expectedElements.push_back(QStringLiteral("RelatesTo")); - expectedElements.push_back(QStringLiteral("MessageID")); - - expectedElements.push_back(QStringLiteral("ResultMajor")); - expectedElements.push_back(QStringLiteral("ResultMinor")); - expectedElements.push_back(QStringLiteral("ResultMessage")); + const QStringList expectedElements({ + QStringLiteral("RelatesTo"), + QStringLiteral("MessageID"), + QStringLiteral("ResultMajor"), + QStringLiteral("ResultMinor"), + QStringLiteral("ResultMessage") + }); detectStartElements(expectedElements); } diff --git a/src/core/paos/retrieve/StartPaosResponse.h b/src/core/paos/retrieve/StartPaosResponse.h index 55bd8d9..22a9841 100644 --- a/src/core/paos/retrieve/StartPaosResponse.h +++ b/src/core/paos/retrieve/StartPaosResponse.h @@ -30,4 +30,4 @@ class StartPaosResponse virtual bool handleFoundElement(const QString& pElementName, const QString& pValue, const QXmlStreamAttributes& pAttributes) override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/paos/retrieve/Transmit.h b/src/core/paos/retrieve/Transmit.h index 036da65..21b2c55 100644 --- a/src/core/paos/retrieve/Transmit.h +++ b/src/core/paos/retrieve/Transmit.h @@ -51,4 +51,4 @@ class Transmit }; -} +} // namespace governikus diff --git a/src/core/paos/retrieve/TransmitParser.h b/src/core/paos/retrieve/TransmitParser.h index 5867ac0..9605b65 100644 --- a/src/core/paos/retrieve/TransmitParser.h +++ b/src/core/paos/retrieve/TransmitParser.h @@ -33,4 +33,4 @@ class TransmitParser QScopedPointer mTransmit; }; -} +} // namespace governikus diff --git a/src/core/states/AbstractGenericState.h b/src/core/states/AbstractGenericState.h index ee59ca9..ea93e4e 100644 --- a/src/core/states/AbstractGenericState.h +++ b/src/core/states/AbstractGenericState.h @@ -17,15 +17,15 @@ namespace governikus { -template +template class AbstractGenericState : public AbstractState { public: - AbstractGenericState(const QSharedPointer& pContext, bool pConnectOnCardRemoved = true) + explicit AbstractGenericState(const QSharedPointer& pContext, bool pConnectOnCardRemoved = true) : AbstractState(pContext, pConnectOnCardRemoved) { - Q_ASSERT(mContext.objectCast()); + Q_ASSERT(mContext.objectCast()); } @@ -34,12 +34,12 @@ class AbstractGenericState } - virtual QSharedPointer getContext() + virtual QSharedPointer getContext() { - return mContext.staticCast(); + return mContext.staticCast(); } }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/AbstractState.cpp b/src/core/states/AbstractState.cpp index f2d6db4..234d8d5 100644 --- a/src/core/states/AbstractState.cpp +++ b/src/core/states/AbstractState.cpp @@ -68,8 +68,9 @@ void AbstractState::onEntry(QEvent* pEvent) Q_UNUSED(pEvent); if (mConnectOnCardRemoved) { - mConnections += connect(&ReaderManager::getInstance(), &ReaderManager::fireCardRemoved, this, &AbstractState::onCardRemoved); - mConnections += connect(&ReaderManager::getInstance(), &ReaderManager::fireReaderRemoved, this, &AbstractState::onCardRemoved); + const auto readerManager = Env::getSingleton(); + mConnections += connect(readerManager, &ReaderManager::fireCardRemoved, this, &AbstractState::onCardRemoved); + mConnections += connect(readerManager, &ReaderManager::fireReaderRemoved, this, &AbstractState::onCardRemoved); } mConnections += connect(mContext.data(), &WorkflowContext::fireCancelWorkflow, this, &AbstractState::onUserCancelled); mConnections += connect(mContext.data(), &WorkflowContext::fireStateApprovedChanged, this, &AbstractState::onStateApprovedChanged); @@ -84,7 +85,8 @@ void AbstractState::onExit(QEvent* pEvent) QState::onExit(pEvent); clearConnections(); mContext->setStateApproved(false); - qCDebug(statemachine) << "Leaving state" << getStateName() << "with status:" << mContext->getStatus(); + qCDebug(statemachine) << "Leaving state" << getStateName() + << "with status: [" << mContext->getLastPaceResult() << "+" << mContext->getStatus() << "]"; } @@ -129,3 +131,13 @@ void AbstractState::updateStatus(const GlobalStatus& pStatus) mContext->setStatus(pStatus); } } + + +void AbstractState::updateStartPaosResult(const ECardApiResult& pStartPaosResult) +{ + if (!pStartPaosResult.isOk() && mContext->getStartPaosResult().isOk()) + { + mContext->setStartPaosResult(pStartPaosResult); + updateStatus(pStartPaosResult.toStatus()); + } +} diff --git a/src/core/states/AbstractState.h b/src/core/states/AbstractState.h index 95fcd03..be63253 100644 --- a/src/core/states/AbstractState.h +++ b/src/core/states/AbstractState.h @@ -7,7 +7,6 @@ #pragma once #include "context/WorkflowContext.h" -#include "Result.h" #include #include @@ -16,7 +15,6 @@ namespace governikus { - class AbstractState : public QState { @@ -29,7 +27,7 @@ class AbstractState const QSharedPointer mContext; const bool mConnectOnCardRemoved; - AbstractState(const QSharedPointer& pContext, bool pConnectOnCardRemoved); + explicit AbstractState(const QSharedPointer& pContext, bool pConnectOnCardRemoved); virtual void run() = 0; protected: @@ -40,6 +38,7 @@ class AbstractState void clearConnections(); bool isCancellationByUser(); void updateStatus(const GlobalStatus& pStatus); + void updateStartPaosResult(const ECardApiResult& pStartPaosResult); public: static QString getClassName(const char* pName); @@ -68,4 +67,4 @@ class AbstractState void onCardRemoved(const QString& pReaderName); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/CompositeStatePace.cpp b/src/core/states/CompositeStatePace.cpp new file mode 100644 index 0000000..542792f --- /dev/null +++ b/src/core/states/CompositeStatePace.cpp @@ -0,0 +1,92 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "CompositeStatePace.h" + +#include "context/WorkflowContext.h" +#include "states/CompositeStateSelectCard.h" +#include "states/StateBuilder.h" +#include "states/StateClearPacePasswords.h" +#include "states/StateDestroyPace.h" +#include "states/StateEnterPacePassword.h" +#include "states/StateEstablishPaceChannel.h" +#include "states/StateMaintainCardConnection.h" +#include "states/StatePreparePace.h" +#include "states/StateUnfortunateCardPosition.h" +#include "states/StateUpdateRetryCounter.h" +#include "states/StateVerifyRetryCounter.h" + +using namespace governikus; + + +CompositeStatePace::CompositeStatePace(const QSharedPointer& pContext) + : QState() + , mContext(pContext) +{ + auto sMaintainCardConnection = StateBuilder::createState(mContext); + auto sClearPacePasswordsOnError = StateBuilder::createState(mContext); + auto sSelectCard = new CompositeStateSelectCard(pContext); + auto sUpdateRetryCounter = StateBuilder::createState(mContext); + auto sVerifyRetryCounter = StateBuilder::createState(mContext); + auto sPreparePace = StateBuilder::createState(mContext); + auto sUnfortunateCardPosition = StateBuilder::createState(mContext); + auto sEnterPacePassword = StateBuilder::createState(mContext); + auto sEstablishPaceChannel = StateBuilder::createState(mContext); + auto sClearPacePasswordsBeforeDestroy = StateBuilder::createState(mContext); + auto sDestroyPace = StateBuilder::createState(mContext); + + sMaintainCardConnection->setParent(this); + sClearPacePasswordsOnError->setParent(this); + sSelectCard->setParent(this); + sUpdateRetryCounter->setParent(this); + sVerifyRetryCounter->setParent(this); + sPreparePace->setParent(this); + sUnfortunateCardPosition->setParent(this); + sEnterPacePassword->setParent(this); + sEstablishPaceChannel->setParent(this); + sClearPacePasswordsBeforeDestroy->setParent(this); + sDestroyPace->setParent(this); + + setInitialState(sMaintainCardConnection); + + sMaintainCardConnection->addTransition(sMaintainCardConnection, &StateMaintainCardConnection::fireContinue, sVerifyRetryCounter); + sMaintainCardConnection->addTransition(sMaintainCardConnection, &StateMaintainCardConnection::fireNoCardConnection, sSelectCard); + sMaintainCardConnection->addTransition(sMaintainCardConnection, &StateMaintainCardConnection::fireForceUpdateRetryCounter, sUpdateRetryCounter); + sMaintainCardConnection->addTransition(sMaintainCardConnection, &StateMaintainCardConnection::fireAbort, sClearPacePasswordsOnError); + + connect(sClearPacePasswordsOnError, &AbstractState::fireContinue, this, &CompositeStatePace::fireAbort); + connect(sClearPacePasswordsOnError, &AbstractState::fireAbort, this, &CompositeStatePace::fireAbort); + + sSelectCard->addTransition(sSelectCard, &CompositeStateSelectCard::fireContinue, sUpdateRetryCounter); + sSelectCard->addTransition(sSelectCard, &CompositeStateSelectCard::fireAbort, sMaintainCardConnection); + + sUpdateRetryCounter->addTransition(sUpdateRetryCounter, &AbstractState::fireContinue, sVerifyRetryCounter); + sUpdateRetryCounter->addTransition(sUpdateRetryCounter, &AbstractState::fireAbort, sMaintainCardConnection); + + sVerifyRetryCounter->addTransition(sVerifyRetryCounter, &AbstractState::fireContinue, sPreparePace); + sVerifyRetryCounter->addTransition(sVerifyRetryCounter, &AbstractState::fireAbort, sMaintainCardConnection); + + sPreparePace->addTransition(sPreparePace, &StatePreparePace::fireEnterPacePassword, sEnterPacePassword); + sPreparePace->addTransition(sPreparePace, &StatePreparePace::fireEstablishPaceChannel, sEstablishPaceChannel); + sPreparePace->addTransition(sPreparePace, &StatePreparePace::fireContinue, sMaintainCardConnection); + sPreparePace->addTransition(sPreparePace, &StatePreparePace::fireAbort, sMaintainCardConnection); + + sEnterPacePassword->addTransition(sEnterPacePassword, &AbstractState::fireContinue, sEstablishPaceChannel); + sEnterPacePassword->addTransition(sEnterPacePassword, &AbstractState::fireAbort, sMaintainCardConnection); + + sEstablishPaceChannel->addTransition(sEstablishPaceChannel, &AbstractState::fireContinue, sMaintainCardConnection); + connect(sEstablishPaceChannel, &StateEstablishPaceChannel::firePaceChannelEstablished, this, &CompositeStatePace::firePaceChannelEstablished); + sEstablishPaceChannel->addTransition(sEstablishPaceChannel, &StateEstablishPaceChannel::firePacePukEstablished, sClearPacePasswordsBeforeDestroy); + sEstablishPaceChannel->addTransition(sEstablishPaceChannel, &StateEstablishPaceChannel::fireAbortAndUnfortunateCardPosition, sUnfortunateCardPosition); + sEstablishPaceChannel->addTransition(sEstablishPaceChannel, &AbstractState::fireAbort, sMaintainCardConnection); + + sUnfortunateCardPosition->addTransition(sUnfortunateCardPosition, &AbstractState::fireContinue, sMaintainCardConnection); + sUnfortunateCardPosition->addTransition(sUnfortunateCardPosition, &AbstractState::fireAbort, sMaintainCardConnection); + + sClearPacePasswordsBeforeDestroy->addTransition(sClearPacePasswordsBeforeDestroy, &AbstractState::fireContinue, sDestroyPace); + sClearPacePasswordsBeforeDestroy->addTransition(sClearPacePasswordsBeforeDestroy, &AbstractState::fireAbort, sDestroyPace); + + connect(sDestroyPace, &AbstractState::fireContinue, this, &CompositeStatePace::firePacePukEstablished); + connect(sDestroyPace, &AbstractState::fireAbort, this, &CompositeStatePace::fireAbort); +} diff --git a/src/core/states/CompositeStatePace.h b/src/core/states/CompositeStatePace.h new file mode 100644 index 0000000..56495df --- /dev/null +++ b/src/core/states/CompositeStatePace.h @@ -0,0 +1,33 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include + + +namespace governikus +{ + +class WorkflowContext; + +class CompositeStatePace + : public QState +{ + Q_OBJECT + + const QSharedPointer mContext; + + public: + explicit CompositeStatePace(const QSharedPointer& pContext); + virtual ~CompositeStatePace() = default; + + Q_SIGNALS: + void firePaceChannelEstablished(); + void firePacePukEstablished(); + void fireAbort(); +}; + +} // namespace governikus diff --git a/src/core/states/CompositeStateProcessCvcsAndSetRights.h b/src/core/states/CompositeStateProcessCvcsAndSetRights.h index 74ff754..11fb144 100644 --- a/src/core/states/CompositeStateProcessCvcsAndSetRights.h +++ b/src/core/states/CompositeStateProcessCvcsAndSetRights.h @@ -23,7 +23,7 @@ class CompositeStateProcessCvcsAndSetRights const QSharedPointer mContext; public: - CompositeStateProcessCvcsAndSetRights(const QSharedPointer& pContext); + explicit CompositeStateProcessCvcsAndSetRights(const QSharedPointer& pContext); virtual ~CompositeStateProcessCvcsAndSetRights(); Q_SIGNALS: @@ -31,4 +31,4 @@ class CompositeStateProcessCvcsAndSetRights void fireAbort(); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/CompositeStateSelectCard.cpp b/src/core/states/CompositeStateSelectCard.cpp index a3d86d2..357fbd2 100644 --- a/src/core/states/CompositeStateSelectCard.cpp +++ b/src/core/states/CompositeStateSelectCard.cpp @@ -16,22 +16,22 @@ CompositeStateSelectCard::CompositeStateSelectCard(const QSharedPointer(mContext); - auto connectCard = StateBuilder::createState(mContext); + auto sSelectReader = StateBuilder::createState(mContext); + auto sConnectCard = StateBuilder::createState(mContext); - selectReader->setParent(this); - connectCard->setParent(this); + sSelectReader->setParent(this); + sConnectCard->setParent(this); - setInitialState(selectReader); + setInitialState(sSelectReader); - selectReader->addTransition(selectReader, &StateSelectReader::fireRetry, selectReader); - selectReader->addTransition(selectReader, &AbstractState::fireContinue, connectCard); - connect(selectReader, &AbstractState::fireAbort, this, &CompositeStateSelectCard::fireAbort); + sSelectReader->addTransition(sSelectReader, &StateSelectReader::fireRetry, sSelectReader); + sSelectReader->addTransition(sSelectReader, &AbstractState::fireContinue, sConnectCard); + connect(sSelectReader, &AbstractState::fireAbort, this, &CompositeStateSelectCard::fireAbort); - connectCard->addTransition(connectCard, &StateConnectCard::fireRetry, selectReader); - connectCard->addTransition(connectCard, &StateConnectCard::fireReaderRemoved, selectReader); - connect(connectCard, &AbstractState::fireContinue, this, &CompositeStateSelectCard::fireContinue); - connect(connectCard, &AbstractState::fireAbort, this, &CompositeStateSelectCard::fireAbort); + sConnectCard->addTransition(sConnectCard, &StateConnectCard::fireRetry, sSelectReader); + sConnectCard->addTransition(sConnectCard, &StateConnectCard::fireReaderRemoved, sSelectReader); + connect(sConnectCard, &AbstractState::fireContinue, this, &CompositeStateSelectCard::fireContinue); + connect(sConnectCard, &AbstractState::fireAbort, this, &CompositeStateSelectCard::fireAbort); } diff --git a/src/core/states/CompositeStateSelectCard.h b/src/core/states/CompositeStateSelectCard.h index a64596c..f30cce8 100644 --- a/src/core/states/CompositeStateSelectCard.h +++ b/src/core/states/CompositeStateSelectCard.h @@ -23,7 +23,7 @@ class CompositeStateSelectCard const QSharedPointer mContext; public: - CompositeStateSelectCard(const QSharedPointer& pContext); + explicit CompositeStateSelectCard(const QSharedPointer& pContext); virtual ~CompositeStateSelectCard(); Q_SIGNALS: @@ -31,4 +31,4 @@ class CompositeStateSelectCard void fireAbort(); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/FinalState.h b/src/core/states/FinalState.h index 0455129..af0389e 100644 --- a/src/core/states/FinalState.h +++ b/src/core/states/FinalState.h @@ -16,7 +16,6 @@ class FinalState : public AbstractGenericState { Q_OBJECT - friend class StateBuilder; private: virtual void run() override; @@ -30,7 +29,7 @@ class FinalState public: - FinalState(const QSharedPointer& pContext) + explicit FinalState(const QSharedPointer& pContext) : AbstractGenericState(pContext, false) { } @@ -38,4 +37,4 @@ class FinalState }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/StateBuilder.h b/src/core/states/StateBuilder.h index a3449f8..1ec71ac 100644 --- a/src/core/states/StateBuilder.h +++ b/src/core/states/StateBuilder.h @@ -31,4 +31,4 @@ class StateBuilder }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/StateCertificateDescriptionCheck.cpp b/src/core/states/StateCertificateDescriptionCheck.cpp index d4884cf..df67a09 100644 --- a/src/core/states/StateCertificateDescriptionCheck.cpp +++ b/src/core/states/StateCertificateDescriptionCheck.cpp @@ -16,9 +16,8 @@ Q_DECLARE_LOGGING_CATEGORY(developermode) StateCertificateDescriptionCheck::StateCertificateDescriptionCheck(const QSharedPointer& pContext) - : AbstractGenericState(pContext) + : AbstractGenericState(pContext, false) { - } @@ -52,7 +51,7 @@ void StateCertificateDescriptionCheck::run() if (hashCalculator.result() != hashOfDescription) { auto certificateHashError = QStringLiteral("The certificate description does not match the certificate."); - if (AppSettings::getInstance().getGeneralSettings().isDeveloperMode()) + if (Env::getSingleton()->getGeneralSettings().isDeveloperMode()) { qCCritical(developermode) << certificateHashError; } @@ -66,11 +65,22 @@ void StateCertificateDescriptionCheck::run() } // check same origin policy for TCToken URL and subject URL - const QUrl& subjectUrl = getContext()->getDidAuthenticateEac1()->getCertificateDescription()->getSubjectUrl(); - if (!UrlUtil::isMatchingSameOriginPolicy(subjectUrl, getContext()->getTcTokenUrl())) + const QString& subjectUrlString = getContext()->getDidAuthenticateEac1()->getCertificateDescription()->getSubjectUrl(); + const QUrl& tcTockenUrl = getContext()->getTcTokenUrl(); + + qDebug() << "Subject URL from AT CVC (eService certificate) description:" << subjectUrlString; + qDebug() << "TCToken URL:" << tcTockenUrl; + + if (UrlUtil::isMatchingSameOriginPolicy(QUrl(subjectUrlString), tcTockenUrl)) { + qDebug() << "SOP-Check succeeded."; + } + else + { + qDebug() << "SOP-Check failed."; + auto sameOriginPolicyError = QStringLiteral("The subject URL in the certificate description and the TCToken URL don't satisfy the same origin policy."); - if (AppSettings::getInstance().getGeneralSettings().isDeveloperMode()) + if (Env::getSingleton()->getGeneralSettings().isDeveloperMode()) { qCCritical(developermode) << sameOriginPolicyError; } diff --git a/src/core/states/StateCertificateDescriptionCheck.h b/src/core/states/StateCertificateDescriptionCheck.h index 4eb494a..9fcc0d5 100644 --- a/src/core/states/StateCertificateDescriptionCheck.h +++ b/src/core/states/StateCertificateDescriptionCheck.h @@ -7,7 +7,6 @@ #include "context/AuthContext.h" #include "states/AbstractGenericState.h" -class test_StateCertificateDescriptionCheck; namespace governikus { @@ -19,9 +18,9 @@ class StateCertificateDescriptionCheck friend class StateBuilder; friend class ::test_StateCertificateDescriptionCheck; - StateCertificateDescriptionCheck(const QSharedPointer& pContext); + explicit StateCertificateDescriptionCheck(const QSharedPointer& pContext); virtual void run() override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/StateChangePin.cpp b/src/core/states/StateChangePin.cpp index 5851d8d..63c7299 100644 --- a/src/core/states/StateChangePin.cpp +++ b/src/core/states/StateChangePin.cpp @@ -10,7 +10,7 @@ using namespace governikus; StateChangePin::StateChangePin(const QSharedPointer& pContext) - : AbstractGenericState(pContext) + : AbstractGenericState(pContext, false) { } @@ -28,6 +28,7 @@ void StateChangePin::run() void StateChangePin::onSetEidPinDone(QSharedPointer pCommand) { const CardReturnCode returnCode = pCommand->getReturnCode(); + getContext()->setLastPaceResult(returnCode); switch (returnCode) { case CardReturnCode::OK: @@ -46,7 +47,6 @@ void StateChangePin::onSetEidPinDone(QSharedPointer pCommand) break; default: - updateStatus(CardReturnCodeUtil::toGlobalStatus(returnCode)); Q_EMIT fireAbort(); break; } diff --git a/src/core/states/StateChangePin.h b/src/core/states/StateChangePin.h index cbbdb0a..5e20a47 100644 --- a/src/core/states/StateChangePin.h +++ b/src/core/states/StateChangePin.h @@ -9,6 +9,8 @@ #include "AbstractGenericState.h" #include "context/ChangePinContext.h" +class test_StateChangePin; + namespace governikus { @@ -17,8 +19,9 @@ class StateChangePin { Q_OBJECT friend class StateBuilder; + friend class ::test_StateChangePin; - StateChangePin(const QSharedPointer& pContext); + explicit StateChangePin(const QSharedPointer& pContext); virtual void run() override; private Q_SLOTS: @@ -28,4 +31,4 @@ class StateChangePin void fireInvalidPin(); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/StateCheckCertificates.cpp b/src/core/states/StateCheckCertificates.cpp index 4548d88..f26a87f 100644 --- a/src/core/states/StateCheckCertificates.cpp +++ b/src/core/states/StateCheckCertificates.cpp @@ -13,7 +13,7 @@ Q_DECLARE_LOGGING_CATEGORY(developermode) StateCheckCertificates::StateCheckCertificates(const QSharedPointer& pContext) - : AbstractGenericState(pContext) + : AbstractGenericState(pContext, false) { } @@ -30,14 +30,15 @@ void StateCheckCertificates::run() if (!TlsChecker::checkCertificate(certificate, hashAlgorithm, commCertificates)) { auto certificateDescError = QStringLiteral("Hash of certificate not in certificate description"); - if (AppSettings::getInstance().getGeneralSettings().isDeveloperMode()) + if (Env::getSingleton()->getGeneralSettings().isDeveloperMode()) { qCCritical(developermode) << certificateDescError; } else { qCritical() << certificateDescError; - updateStatus(GlobalStatus::Code::Workflow_TrustedChannel_Hash_Not_In_Description); + const auto& issuerName = TlsChecker::getCertificateIssuerName(certificate); + updateStatus(GlobalStatus(GlobalStatus::Code::Workflow_TrustedChannel_Hash_Not_In_Description, issuerName)); Q_EMIT fireAbort(); return; } diff --git a/src/core/states/StateCheckCertificates.h b/src/core/states/StateCheckCertificates.h index 52776b3..2486ad0 100644 --- a/src/core/states/StateCheckCertificates.h +++ b/src/core/states/StateCheckCertificates.h @@ -20,8 +20,8 @@ class StateCheckCertificates Q_OBJECT friend class StateBuilder; - StateCheckCertificates(const QSharedPointer& pContext); + explicit StateCheckCertificates(const QSharedPointer& pContext); virtual void run() override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/StateCheckError.h b/src/core/states/StateCheckError.h index b3b413a..6206ac4 100644 --- a/src/core/states/StateCheckError.h +++ b/src/core/states/StateCheckError.h @@ -19,8 +19,8 @@ class StateCheckError Q_OBJECT friend class StateBuilder; - StateCheckError(const QSharedPointer& pContext); + explicit StateCheckError(const QSharedPointer& pContext); virtual void run() override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/StateCheckRefreshAddress.cpp b/src/core/states/StateCheckRefreshAddress.cpp index 2835c10..2240adc 100644 --- a/src/core/states/StateCheckRefreshAddress.cpp +++ b/src/core/states/StateCheckRefreshAddress.cpp @@ -6,13 +6,13 @@ #include "AppSettings.h" #include "CertificateChecker.h" -#include "Env.h" -#include "HttpStatusCode.h" +#include "LogHandler.h" #include "NetworkManager.h" #include "StateRedirectBrowser.h" #include "TlsChecker.h" #include "UrlUtil.h" +#include #include #include #include @@ -36,11 +36,20 @@ StateCheckRefreshAddress::StateCheckRefreshAddress(const QSharedPointerdeleteLater(); + } +} + + bool StateCheckRefreshAddress::isMatchingSameOriginPolicyInDevMode() const { // Checking for same origin policy needs a special treatment in developer mode because // a tcTokenURL with the http scheme is acceptable. - if (!AppSettings::getInstance().getGeneralSettings().isDeveloperMode()) + if (!Env::getSingleton()->getGeneralSettings().isDeveloperMode()) { return false; } @@ -98,7 +107,7 @@ void StateCheckRefreshAddress::run() } if (mUrl.scheme() != QLatin1String("https")) { - if (AppSettings::getInstance().getGeneralSettings().isDeveloperMode()) + if (Env::getSingleton()->getGeneralSettings().isDeveloperMode()) { qCDebug(developermode) << refreshAddrError; } @@ -110,18 +119,18 @@ void StateCheckRefreshAddress::run() } } - qDebug() << "Current URL: " << mUrl.toString(); mSubjectUrl = determineSubjectUrl(); - qDebug() << "SubjectUrl: " << mSubjectUrl.toString(); + qDebug() << "Subject URL from AT CVC (eService certificate) description:" << mSubjectUrl.toString(); + qDebug() << "Current redirect URL:" << mUrl.toString(); - if (UrlUtil::isMatchingSameOriginPolicy(mUrl, mSubjectUrl) || isMatchingSameOriginPolicyInDevMode()) + if (UrlUtil::isMatchingSameOriginPolicy(mSubjectUrl, mUrl) || isMatchingSameOriginPolicyInDevMode()) { - qDebug() << "SOP-Check succeeded, abort process"; + qDebug() << "SOP-Check succeeded, abort process."; fetchServerCertificate(); } else { - qDebug() << "SOP-Check failed, start process"; + qDebug() << "SOP-Check failed, start process."; sendGetRequest(); } } @@ -140,7 +149,7 @@ QUrl StateCheckRefreshAddress::determineSubjectUrl() } } - if (AppSettings::getInstance().getGeneralSettings().isDeveloperMode()) + if (Env::getSingleton()->getGeneralSettings().isDeveloperMode()) { // Perform SOP-Check against TcToken-URL instead of subjectURL subjectUrl = getContext()->getTcTokenUrl(); @@ -163,7 +172,7 @@ void StateCheckRefreshAddress::sendGetRequest() mReply->deleteLater(); } - qDebug() << "Send GET request to URL: " << mUrl.toString(); + qDebug() << "Send GET request to URL:" << mUrl.toString(); QNetworkRequest request(mUrl); mReply = Env::getSingleton()->get(request); mConnections += connect(mReply.data(), &QNetworkReply::sslErrors, this, &StateCheckRefreshAddress::onSslErrors); @@ -192,7 +201,7 @@ void StateCheckRefreshAddress::reportCommunicationError(const GlobalStatus& pSta void StateCheckRefreshAddress::onSslHandshakeDone() { const auto& cfg = mReply->sslConfiguration(); - TlsChecker::logSslConfig(cfg, qInfo(network)); + TlsChecker::logSslConfig(cfg, spawnMessageLogger(network)); if (!checkSslConnectionAndSaveCertificate(cfg)) { @@ -214,17 +223,18 @@ bool StateCheckRefreshAddress::checkSslConnectionAndSaveCertificate(const QSslCo context->addCertificateData(pUrl, pCertificate); }; + const auto& issuerName = TlsChecker::getCertificateIssuerName(pSslConfiguration.peerCertificate()); switch (CertificateChecker::checkAndSaveCertificate(pSslConfiguration.peerCertificate(), mUrl, context->getDidAuthenticateEac1(), context->getDvCvc(), saveCertificateFunc)) { case CertificateChecker::CertificateStatus::Good: break; case CertificateChecker::CertificateStatus::Unsupported_Algorithm_Or_Length: - reportCommunicationError(GlobalStatus(GlobalStatus::Code::Workflow_Network_Ssl_Certificate_Unsupported_Algorithm_Or_Length)); + reportCommunicationError(GlobalStatus(GlobalStatus::Code::Workflow_Network_Ssl_Certificate_Unsupported_Algorithm_Or_Length, issuerName)); return false; case CertificateChecker::CertificateStatus::Hash_Not_In_Description: - reportCommunicationError(GlobalStatus(GlobalStatus::Code::Workflow_Nerwork_Ssl_Hash_Not_In_Certificate_Description)); + reportCommunicationError(GlobalStatus(GlobalStatus::Code::Workflow_Nerwork_Ssl_Hash_Not_In_Certificate_Description, issuerName)); return false; } @@ -240,17 +250,11 @@ bool StateCheckRefreshAddress::checkSslConnectionAndSaveCertificate(const QSslCo void StateCheckRefreshAddress::onNetworkReply() { - int statusCode = mReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - QUrl redirectUrl = mReply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); - qDebug() << "Status Code: " << statusCode << " | redirect URL: " << redirectUrl; - for (const auto& header : mReply->rawHeaderPairs()) - { - qCDebug(network).nospace() << "Header | " << header.first << ": " << header.second; - } + const auto statusCode = NetworkManager::getLoggedStatusCode(mReply, spawnMessageLogger(network)); if (mReply->error() != QNetworkReply::NoError) { - qCritical() << "An error occured: " << mReply->errorString(); + qCritical() << "An error occured:" << mReply->errorString(); switch (NetworkManager::toNetworkError(mReply.data())) { case NetworkManager::NetworkError::ServiceUnavailable: @@ -276,13 +280,14 @@ void StateCheckRefreshAddress::onNetworkReply() return; } - if (statusCode != HttpStatusCode::FOUND && statusCode != HttpStatusCode::SEE_OTHER && statusCode != HttpStatusCode::TEMPORARY_REDIRECT) + if (statusCode != HTTP_STATUS_FOUND && statusCode != HTTP_STATUS_SEE_OTHER && statusCode != HTTP_STATUS_TEMPORARY_REDIRECT) { - qCritical() << "Got unexpected status code: " << statusCode; + qCritical() << "Got unexpected status code:" << statusCode; reportCommunicationError(GlobalStatus(GlobalStatus::Code::Workflow_Network_Expected_Redirect, QString::number(statusCode))); return; } + const QUrl& redirectUrl = mReply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); if (redirectUrl.isEmpty()) { qCritical() << "Got empty redirect URL"; @@ -301,7 +306,7 @@ void StateCheckRefreshAddress::onNetworkReply() { auto httpsError = QStringLiteral("Redirect URL is not https: %1").arg(redirectUrl.toString()); - if (AppSettings::getInstance().getGeneralSettings().isDeveloperMode()) + if (Env::getSingleton()->getGeneralSettings().isDeveloperMode()) { qCCritical(developermode) << httpsError; } @@ -330,7 +335,7 @@ void StateCheckRefreshAddress::onNetworkReply() void StateCheckRefreshAddress::fetchServerCertificate() { - if (AppSettings::getInstance().getGeneralSettings().isDeveloperMode() && mUrl.scheme() == QLatin1String("http")) + if (Env::getSingleton()->getGeneralSettings().isDeveloperMode() && mUrl.scheme() == QLatin1String("http")) { qCWarning(developermode) << "Refresh URL is http only. Certificate check skipped."; doneSuccess(); @@ -370,8 +375,11 @@ void StateCheckRefreshAddress::fetchServerCertificate() void StateCheckRefreshAddress::onSslHandshakeDoneFetchingServerCertificate() { const auto& cfg = mReply->sslConfiguration(); - TlsChecker::logSslConfig(cfg, qInfo(network)); + TlsChecker::logSslConfig(cfg, spawnMessageLogger(network)); + // just establish the TLS connection but do not perform HTTP request + mCertificateFetched = true; + mReply->abort(); if (checkSslConnectionAndSaveCertificate(cfg)) { doneSuccess(); @@ -380,16 +388,13 @@ void StateCheckRefreshAddress::onSslHandshakeDoneFetchingServerCertificate() { // checkSslConnectionAndSaveCertificate already set the error } - // just establish the TLS connection but do not perform HTTP request - mCertificateFetched = true; - mReply->abort(); } void StateCheckRefreshAddress::doneSuccess() { getContext()->setRefreshUrl(mUrl); - qDebug() << "Determined RefreshUrl: " << mUrl; + qDebug() << "Determined RefreshUrl:" << mUrl; Q_EMIT fireContinue(); } @@ -400,6 +405,6 @@ void StateCheckRefreshAddress::onNetworkErrorFetchingServerCertificate(QNetworkR { return; } - qCritical() << "An error occured fetching the server certificate: " << mReply->errorString(); + qCritical() << "An error occured fetching the server certificate:" << mReply->errorString(); reportCommunicationError(GlobalStatus(GlobalStatus::Code::Workflow_Network_Empty_Redirect_Url)); } diff --git a/src/core/states/StateCheckRefreshAddress.h b/src/core/states/StateCheckRefreshAddress.h index 9881cfa..da3a00d 100644 --- a/src/core/states/StateCheckRefreshAddress.h +++ b/src/core/states/StateCheckRefreshAddress.h @@ -28,8 +28,6 @@ class StateCheckRefreshAddress friend class StateBuilder; friend class ::test_StateCheckRefreshAddress; - StateCheckRefreshAddress(const QSharedPointer& pContext); - private: QPointer mReply; QUrl mUrl; @@ -37,6 +35,8 @@ class StateCheckRefreshAddress bool mCertificateFetched; QVector mVerifiedRefreshUrlHosts; + explicit StateCheckRefreshAddress(const QSharedPointer& pContext); + bool isMatchingSameOriginPolicyInDevMode() const; virtual void run() override; @@ -55,6 +55,8 @@ class StateCheckRefreshAddress void onSslHandshakeDoneFetchingServerCertificate(); void onNetworkErrorFetchingServerCertificate(QNetworkReply::NetworkError pError); + public: + virtual ~StateCheckRefreshAddress() override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/StateCleanUpReaderManager.cpp b/src/core/states/StateCleanUpReaderManager.cpp index d4008a9..1bea3ea 100644 --- a/src/core/states/StateCleanUpReaderManager.cpp +++ b/src/core/states/StateCleanUpReaderManager.cpp @@ -2,9 +2,11 @@ * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany */ +#include "StateCleanUpReaderManager.h" + +#include "AppSettings.h" #include "context/ChangePinContext.h" #include "ReaderManager.h" -#include "StateCleanUpReaderManager.h" #include @@ -20,25 +22,26 @@ void StateCleanUpReaderManager::run() { const QSharedPointer context = getContext(); - // On a stationary AusweisApp2, do not stop scanning when a change pin workflow is completed. -#if defined(Q_OS_WIN) || defined(Q_OS_MACOS) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) || defined(Q_OS_FREEBSD) - const bool stopScanRequired = context.objectCast().isNull(); -#else - const bool stopScanRequired = true; -#endif + // Stop scanning is required to kill all connections to force deleting all pace passwords on a remote comfort reader + const auto readerManager = Env::getSingleton(); + readerManager->stopScanAll(); - if (stopScanRequired) + // On a desktop AusweisApp2, restart scanning when a change pin workflow is completed. +#if defined(Q_OS_WIN) || (defined(Q_OS_BSD4) && !defined(Q_OS_IOS)) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) + const bool changePinOnDesktop = context.objectCast(); +#else + const bool changePinOnDesktop = false; +#endif + if (changePinOnDesktop || Env::getSingleton()->isUsedAsSDK()) { - ReaderManager::getInstance().stopScanAll(); + readerManager->startScanAll(); } if (context->getCardConnection()) { qDebug() << "Going to disconnect card connection"; - context->setCardConnection(QSharedPointer()); + context->resetCardConnection(); } - qDebug() << "Going to disconnect readers"; - ReaderManager::getInstance().disconnectAllReaders(); Q_EMIT fireContinue(); } diff --git a/src/core/states/StateCleanUpReaderManager.h b/src/core/states/StateCleanUpReaderManager.h index c2f7282..91e2f4e 100644 --- a/src/core/states/StateCleanUpReaderManager.h +++ b/src/core/states/StateCleanUpReaderManager.h @@ -18,8 +18,8 @@ class StateCleanUpReaderManager Q_OBJECT friend class StateBuilder; - StateCleanUpReaderManager(const QSharedPointer& pContext); + explicit StateCleanUpReaderManager(const QSharedPointer& pContext); virtual void run() override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/StateClearPacePasswords.cpp b/src/core/states/StateClearPacePasswords.cpp new file mode 100644 index 0000000..282cfe2 --- /dev/null +++ b/src/core/states/StateClearPacePasswords.cpp @@ -0,0 +1,22 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "StateClearPacePasswords.h" + + +using namespace governikus; + + +StateClearPacePasswords::StateClearPacePasswords(const QSharedPointer& pContext) + : AbstractGenericState(pContext, false) +{ +} + + +void StateClearPacePasswords::run() +{ + getContext()->resetPacePasswords(); + + Q_EMIT fireContinue(); +} diff --git a/src/core/states/StateClearPacePasswords.h b/src/core/states/StateClearPacePasswords.h new file mode 100644 index 0000000..0a96690 --- /dev/null +++ b/src/core/states/StateClearPacePasswords.h @@ -0,0 +1,23 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/WorkflowContext.h" +#include "states/AbstractGenericState.h" + +namespace governikus +{ + +class StateClearPacePasswords + : public AbstractGenericState +{ + Q_OBJECT + friend class StateBuilder; + + explicit StateClearPacePasswords(const QSharedPointer& pContext); + virtual void run() override; +}; + +} // namespace governikus diff --git a/src/core/states/StateConnectCard.cpp b/src/core/states/StateConnectCard.cpp index 902fd28..459466d 100644 --- a/src/core/states/StateConnectCard.cpp +++ b/src/core/states/StateConnectCard.cpp @@ -4,7 +4,6 @@ #include "CardConnection.h" #include "ReaderManager.h" -#include "Result.h" #include "StateConnectCard.h" #include @@ -21,9 +20,9 @@ StateConnectCard::StateConnectCard(const QSharedPointer& pConte void StateConnectCard::run() { - qCDebug(statemachine) << "StateConnectCard::run()"; - mConnections += connect(&ReaderManager::getInstance(), &ReaderManager::fireCardInserted, this, &StateConnectCard::onCardInserted); - mConnections += connect(&ReaderManager::getInstance(), &ReaderManager::fireReaderRemoved, this, &StateConnectCard::onReaderRemoved); + const auto readerManager = Env::getSingleton(); + mConnections += connect(readerManager, &ReaderManager::fireCardInserted, this, &StateConnectCard::onCardInserted); + mConnections += connect(readerManager, &ReaderManager::fireReaderRemoved, this, &StateConnectCard::onReaderRemoved); mConnections += connect(getContext().data(), &WorkflowContext::fireAbortCardSelection, this, &StateConnectCard::onAbort); mConnections += connect(getContext().data(), &WorkflowContext::fireReaderPlugInTypesChanged, this, &StateConnectCard::fireRetry); onCardInserted(); @@ -32,13 +31,14 @@ void StateConnectCard::run() void StateConnectCard::onCardInserted() { - ReaderInfo readerInfo = ReaderManager::getInstance().getReaderInfo(getContext()->getReaderName()); + const auto readerManager = Env::getSingleton(); + ReaderInfo readerInfo = readerManager->getReaderInfo(getContext()->getReaderName()); if (readerInfo.hasEidCard()) { if (readerInfo.sufficientApduLength() && (!readerInfo.isPinDeactivated() || getContext()->isCanAllowedMode())) { qCDebug(statemachine) << "Card has been inserted, trying to connect"; - mConnections += ReaderManager::getInstance().callCreateCardConnectionCommand(readerInfo.getName(), this, &StateConnectCard::onCommandDone); + mConnections += readerManager->callCreateCardConnectionCommand(readerInfo.getName(), this, &StateConnectCard::onCommandDone); } } } @@ -49,7 +49,7 @@ void StateConnectCard::onCommandDone(QSharedPointer qCDebug(statemachine) << "Card connection command completed"; if (pCommand->getCardConnection() == nullptr) { - updateStatus(GlobalStatus::Code::Workflow_Reader_Communication_Error); + qCDebug(statemachine) << "Card connection failed"; Q_EMIT fireAbort(); return; } @@ -74,12 +74,11 @@ void StateConnectCard::onAbort() const QSharedPointer context = getContext(); Q_ASSERT(context); - ReaderManager::getInstance().stopScanAll(); - - ReaderInfo readerInfo = ReaderManager::getInstance().getReaderInfo(context->getReaderName()); + const auto readerManager = Env::getSingleton(); + ReaderInfo readerInfo = readerManager->getReaderInfo(context->getReaderName()); if (readerInfo.isConnected()) { - ReaderManager::getInstance().disconnectReader(readerInfo.getName()); + readerManager->disconnectReader(readerInfo.getName()); } Q_EMIT fireRetry(); } diff --git a/src/core/states/StateConnectCard.h b/src/core/states/StateConnectCard.h index 09f9ce1..caadbe8 100644 --- a/src/core/states/StateConnectCard.h +++ b/src/core/states/StateConnectCard.h @@ -7,6 +7,8 @@ #include "AbstractGenericState.h" #include "command/CreateCardConnectionCommand.h" +class test_StateConnectCard; + namespace governikus { @@ -15,8 +17,9 @@ class StateConnectCard { Q_OBJECT friend class StateBuilder; + friend class ::test_StateConnectCard; - StateConnectCard(const QSharedPointer& pContext); + explicit StateConnectCard(const QSharedPointer& pContext); virtual void run() override; private Q_SLOTS: @@ -31,4 +34,4 @@ class StateConnectCard }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/StateDestroyPace.h b/src/core/states/StateDestroyPace.h index 354fbf2..2ba44e1 100644 --- a/src/core/states/StateDestroyPace.h +++ b/src/core/states/StateDestroyPace.h @@ -10,20 +10,23 @@ #include "context/ChangePinContext.h" +class test_StateDestroyPace; + namespace governikus { class StateDestroyPace - : public AbstractGenericState + : public AbstractGenericState { Q_OBJECT friend class StateBuilder; + friend class ::test_StateDestroyPace; - StateDestroyPace(const QSharedPointer& pContext); + explicit StateDestroyPace(const QSharedPointer& pContext); virtual void run() override; private Q_SLOTS: void onDestroyPaceDone(QSharedPointer pCommand); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/StateDidAuthenticateEac1.cpp b/src/core/states/StateDidAuthenticateEac1.cpp index 2d50814..23861c4 100644 --- a/src/core/states/StateDidAuthenticateEac1.cpp +++ b/src/core/states/StateDidAuthenticateEac1.cpp @@ -4,7 +4,6 @@ #include "asn1/CVCertificateChainBuilder.h" #include "CardConnection.h" -#include "Result.h" #include "StateDidAuthenticateEac1.h" #include @@ -21,6 +20,10 @@ StateDidAuthenticateEac1::StateDidAuthenticateEac1(const QSharedPointergetPin().isEmpty() && "PACE passwords must be cleared as soon as possible."); + Q_ASSERT(getContext()->getCan().isEmpty() && "PACE passwords must be cleared as soon as possible."); + Q_ASSERT(getContext()->getPuk().isEmpty() && "PACE passwords must be cleared as soon as possible."); + Q_ASSERT(!getContext()->getDidAuthenticateEac1().isNull()); Q_ASSERT(getContext()->getPaceOutputData() != nullptr); Q_ASSERT(getContext()->getCardConnection()); diff --git a/src/core/states/StateDidAuthenticateEac1.h b/src/core/states/StateDidAuthenticateEac1.h index 7231caa..205ab6d 100644 --- a/src/core/states/StateDidAuthenticateEac1.h +++ b/src/core/states/StateDidAuthenticateEac1.h @@ -8,6 +8,8 @@ #include "context/AuthContext.h" #include "states/AbstractGenericState.h" +class test_StateDidAuthenticateEac1; + namespace governikus { @@ -16,8 +18,9 @@ class StateDidAuthenticateEac1 { Q_OBJECT friend class StateBuilder; + friend class ::test_StateDidAuthenticateEac1; - StateDidAuthenticateEac1(const QSharedPointer& pContext); + explicit StateDidAuthenticateEac1(const QSharedPointer& pContext); virtual void run() override; private Q_SLOTS: @@ -25,4 +28,4 @@ class StateDidAuthenticateEac1 }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/StateDidAuthenticateEac2.h b/src/core/states/StateDidAuthenticateEac2.h index d26d30b..f2e8b06 100644 --- a/src/core/states/StateDidAuthenticateEac2.h +++ b/src/core/states/StateDidAuthenticateEac2.h @@ -17,11 +17,11 @@ class StateDidAuthenticateEac2 Q_OBJECT friend class StateBuilder; - StateDidAuthenticateEac2(const QSharedPointer& pContext); + explicit StateDidAuthenticateEac2(const QSharedPointer& pContext); virtual void run() override; private Q_SLOTS: void onCardCommandDone(QSharedPointer pCommand); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/StateDidList.cpp b/src/core/states/StateDidList.cpp index 6663f8d..6e197ab 100644 --- a/src/core/states/StateDidList.cpp +++ b/src/core/states/StateDidList.cpp @@ -10,7 +10,7 @@ using namespace governikus; StateDidList::StateDidList(const QSharedPointer& pContext) - : AbstractGenericState(pContext) + : AbstractGenericState(pContext, false) { } diff --git a/src/core/states/StateDidList.h b/src/core/states/StateDidList.h index 0334327..2ef0498 100644 --- a/src/core/states/StateDidList.h +++ b/src/core/states/StateDidList.h @@ -16,8 +16,8 @@ class StateDidList Q_OBJECT friend class StateBuilder; - StateDidList(const QSharedPointer& pContext); + explicit StateDidList(const QSharedPointer& pContext); virtual void run() override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/StateEACAdditionalInputType.h b/src/core/states/StateEACAdditionalInputType.h index d1b5a56..fa2461e 100644 --- a/src/core/states/StateEACAdditionalInputType.h +++ b/src/core/states/StateEACAdditionalInputType.h @@ -16,11 +16,11 @@ class StateEACAdditionalInputType Q_OBJECT friend class StateBuilder; - StateEACAdditionalInputType(const QSharedPointer& pContext); + explicit StateEACAdditionalInputType(const QSharedPointer& pContext); virtual void run() override; Q_SIGNALS: void fireSendDidAuthenticatResponse(); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/StateEditAccessRights.cpp b/src/core/states/StateEditAccessRights.cpp index 304accf..bd92082 100644 --- a/src/core/states/StateEditAccessRights.cpp +++ b/src/core/states/StateEditAccessRights.cpp @@ -7,7 +7,7 @@ using namespace governikus; StateEditAccessRights::StateEditAccessRights(const QSharedPointer& pContext) - : AbstractGenericState(pContext) + : AbstractGenericState(pContext, false) { } diff --git a/src/core/states/StateEditAccessRights.h b/src/core/states/StateEditAccessRights.h index cc65900..728d104 100644 --- a/src/core/states/StateEditAccessRights.h +++ b/src/core/states/StateEditAccessRights.h @@ -19,8 +19,8 @@ class StateEditAccessRights Q_OBJECT friend class StateBuilder; - StateEditAccessRights(const QSharedPointer& pContext); + explicit StateEditAccessRights(const QSharedPointer& pContext); virtual void run() override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/StateEnterNewPacePin.cpp b/src/core/states/StateEnterNewPacePin.cpp new file mode 100644 index 0000000..e10ba3d --- /dev/null +++ b/src/core/states/StateEnterNewPacePin.cpp @@ -0,0 +1,20 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "StateEnterNewPacePin.h" + + +using namespace governikus; + + +StateEnterNewPacePin::StateEnterNewPacePin(const QSharedPointer& pContext) + : AbstractGenericState(pContext, false) +{ +} + + +void StateEnterNewPacePin::run() +{ + Q_EMIT fireContinue(); +} diff --git a/src/core/states/StateEnterNewPacePin.h b/src/core/states/StateEnterNewPacePin.h new file mode 100644 index 0000000..0d5f04c --- /dev/null +++ b/src/core/states/StateEnterNewPacePin.h @@ -0,0 +1,23 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/ChangePinContext.h" +#include "states/AbstractGenericState.h" + +namespace governikus +{ + +class StateEnterNewPacePin + : public AbstractGenericState +{ + Q_OBJECT + friend class StateBuilder; + + explicit StateEnterNewPacePin(const QSharedPointer& pContext); + virtual void run() override; +}; + +} // namespace governikus diff --git a/src/core/states/StateEnterPacePassword.cpp b/src/core/states/StateEnterPacePassword.cpp new file mode 100644 index 0000000..b9ad811 --- /dev/null +++ b/src/core/states/StateEnterPacePassword.cpp @@ -0,0 +1,20 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "StateEnterPacePassword.h" + + +using namespace governikus; + + +StateEnterPacePassword::StateEnterPacePassword(const QSharedPointer& pContext) + : AbstractGenericState(pContext, false) +{ +} + + +void StateEnterPacePassword::run() +{ + Q_EMIT fireContinue(); +} diff --git a/src/core/states/StateEnterPacePassword.h b/src/core/states/StateEnterPacePassword.h new file mode 100644 index 0000000..9d3b648 --- /dev/null +++ b/src/core/states/StateEnterPacePassword.h @@ -0,0 +1,23 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/WorkflowContext.h" +#include "states/AbstractGenericState.h" + +namespace governikus +{ + +class StateEnterPacePassword + : public AbstractGenericState +{ + Q_OBJECT + friend class StateBuilder; + + explicit StateEnterPacePassword(const QSharedPointer& pContext); + virtual void run() override; +}; + +} // namespace governikus diff --git a/src/core/states/StateEstablishPaceCan.cpp b/src/core/states/StateEstablishPaceCan.cpp deleted file mode 100644 index e4c9ac9..0000000 --- a/src/core/states/StateEstablishPaceCan.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/*! - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - - -#include "StateEstablishPaceCan.h" - -#include "context/AuthContext.h" - -using namespace governikus; - -StateEstablishPaceCan::StateEstablishPaceCan(const QSharedPointer& pContext) - : AbstractGenericState(pContext) -{ -} - - -void StateEstablishPaceCan::run() -{ - auto cardConnection = getContext()->getCardConnection(); - Q_ASSERT(cardConnection); - - QByteArray certificateDescription, effectiveChat; - const auto& authContext = getContext().objectCast(); - if (getContext()->isCanAllowedMode() && authContext) - { - // if PACE is performed for authentication purposes, - // the chat and certificate description need to be send - // - // in other scenarios, e.g. for changing the PIN, the data - // is not needed - Q_ASSERT(authContext->getDidAuthenticateEac1()); - Q_ASSERT(!authContext->encodeEffectiveChat().isEmpty()); - certificateDescription = authContext->getDidAuthenticateEac1()->getCertificateDescriptionAsBinary(); - effectiveChat = authContext->encodeEffectiveChat(); - } - - qDebug() << "Establish Pace connection with CAN"; - mConnections += cardConnection->callEstablishPaceChannelCommand( - this, - &StateEstablishPaceCan::onEstablishConnectionDone, - PACE_PASSWORD_ID::PACE_CAN, - getContext()->getCan(), - effectiveChat, - certificateDescription); - getContext()->setCan(QString()); -} - - -void StateEstablishPaceCan::onUserCancelled() -{ - getContext()->setLastPaceResultAndRetryCounter(CardReturnCode::CANCELLATION_BY_USER, getContext()->getCardConnection()->getReaderInfo().getRetryCounter()); - AbstractState::onUserCancelled(); -} - - -void StateEstablishPaceCan::onEstablishConnectionDone(QSharedPointer pCommand) -{ - const CardReturnCode returnCode = pCommand->getReturnCode(); - if (getContext()->isCanAllowedMode()) - { - auto paceCommand = pCommand.staticCast(); - getContext()->setPaceOutputData(paceCommand->getPaceOutput()); - } - - switch (returnCode) - { - case CardReturnCode::OK: - getContext()->setLastPaceResultAndRetryCounter(returnCode, getContext()->getCardConnection()->getReaderInfo().getRetryCounter()); - Q_EMIT fireContinue(); - break; - - case CardReturnCode::CANCELLATION_BY_USER: - getContext()->setLastPaceResultAndRetryCounter(returnCode, getContext()->getCardConnection()->getReaderInfo().getRetryCounter()); - updateStatus(CardReturnCodeUtil::toGlobalStatus(returnCode)); - Q_EMIT fireAbort(); - break; - - case CardReturnCode::INVALID_CAN: - getContext()->setLastPaceResultAndRetryCounter(returnCode, getContext()->getCardConnection()->getReaderInfo().getRetryCounter()); - Q_EMIT fireInvalidCan(); - break; - - default: - updateStatus(CardReturnCodeUtil::toGlobalStatus(returnCode)); - Q_EMIT fireAbort(); - break; - } -} diff --git a/src/core/states/StateEstablishPaceCan.h b/src/core/states/StateEstablishPaceCan.h deleted file mode 100644 index 3dde744..0000000 --- a/src/core/states/StateEstablishPaceCan.h +++ /dev/null @@ -1,34 +0,0 @@ -/*! - * \brief Controller for the step that tries to establish a PACE - * connection using the card's CAN. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "AbstractGenericState.h" -#include "context/WorkflowContext.h" - - -namespace governikus -{ - -class StateEstablishPaceCan - : public AbstractGenericState -{ - Q_OBJECT - friend class StateBuilder; - - StateEstablishPaceCan(const QSharedPointer& pContext); - virtual void run() override; - virtual void onUserCancelled() override; - - private Q_SLOTS: - void onEstablishConnectionDone(QSharedPointer pCommand); - - Q_SIGNALS: - void fireInvalidCan(); -}; - -} /* namespace governikus */ diff --git a/src/core/states/StateEstablishPaceChannel.cpp b/src/core/states/StateEstablishPaceChannel.cpp new file mode 100644 index 0000000..d4101a9 --- /dev/null +++ b/src/core/states/StateEstablishPaceChannel.cpp @@ -0,0 +1,226 @@ +/*! + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + + +#include "StateEstablishPaceChannel.h" + +#include "context/AuthContext.h" +#include "context/ChangePinContext.h" + +Q_DECLARE_LOGGING_CATEGORY(statemachine) + +using namespace governikus; + +StateEstablishPaceChannel::StateEstablishPaceChannel(const QSharedPointer& pContext) + : AbstractGenericState(pContext, false) + , mPasswordId(PacePasswordId::UNKNOWN) +{ +} + + +void StateEstablishPaceChannel::run() +{ + auto cardConnection = getContext()->getCardConnection(); + if (!cardConnection) + { + qCDebug(statemachine) << "No card connection available."; + abort(); + return; + } + + QByteArray certificateDescription, effectiveChat; + mPasswordId = getContext()->getEstablishPaceChannelType(); + Q_ASSERT(mPasswordId != PacePasswordId::UNKNOWN); + getContext()->setEstablishPaceChannelType(PacePasswordId::UNKNOWN); + + if (mPasswordId == PacePasswordId::PACE_PIN || + (mPasswordId == PacePasswordId::PACE_CAN && getContext()->isCanAllowedMode())) + { + if (auto authContext = getContext().objectCast()) + { + // if PACE is performed for authentication purposes, + // the chat and certificate description need to be send + // + // in other scenarios, e.g. for changing the PIN, the data + // is not needed + Q_ASSERT(authContext->getDidAuthenticateEac1()); + Q_ASSERT(!authContext->encodeEffectiveChat().isEmpty()); + certificateDescription = authContext->getDidAuthenticateEac1()->getCertificateDescriptionAsBinary(); + effectiveChat = authContext->encodeEffectiveChat(); + } + } + + QString password; + switch (mPasswordId) + { + case PacePasswordId::PACE_CAN: + password = getContext()->getCan(); + break; + + case PacePasswordId::PACE_PIN: + password = getContext()->getPin(); + break; + + case PacePasswordId::PACE_PUK: + password = getContext()->getPuk(); + break; + + case PacePasswordId::UNKNOWN: + case PacePasswordId::PACE_MRZ: + password = QString(); + break; + } + + if (password.isEmpty() && cardConnection->getReaderInfo().isBasicReader()) + { + qCCritical(statemachine) << "We hit an invalid state! PACE password is empty for basic reader."; + Q_ASSERT(false); + + qCDebug(statemachine) << "Reseting all PACE passwords."; + getContext()->resetPacePasswords(); + + abort(); + return; + } + + qDebug() << "Establish connection using" << mPasswordId; + Q_ASSERT(!password.isEmpty() || !cardConnection->getReaderInfo().isBasicReader()); + + if (mPasswordId == PacePasswordId::PACE_PUK) + { + mConnections += cardConnection->callUnblockPinCommand(this, &StateEstablishPaceChannel::onEstablishConnectionDone, password); + } + else + { + mConnections += cardConnection->callEstablishPaceChannelCommand(this, + &StateEstablishPaceChannel::onEstablishConnectionDone, + mPasswordId, + password, + effectiveChat, + certificateDescription); + } +} + + +void StateEstablishPaceChannel::onUserCancelled() +{ + getContext()->setLastPaceResult(CardReturnCode::CANCELLATION_BY_USER); + AbstractState::onUserCancelled(); +} + + +void StateEstablishPaceChannel::abort() +{ + getContext()->resetLastPaceResult(); + Q_EMIT fireAbort(); +} + + +void StateEstablishPaceChannel::handleNpaPosition(CardReturnCode pReturnCode) +{ + if (pReturnCode == CardReturnCode::CARD_NOT_FOUND || pReturnCode == CardReturnCode::RETRY_ALLOWED) + { + qCDebug(statemachine) << "Card vanished during PACE. Incrementing unfortunate-card-position panickiness."; + getContext()->handleWrongNpaPosition(); + return; + } + + qCDebug(statemachine) << "Clearing unfortunate-card-position panickiness. |" << pReturnCode; + getContext()->setNpaPositionVerified(); + return; +} + + +void StateEstablishPaceChannel::onEstablishConnectionDone(QSharedPointer pCommand) +{ + if (mPasswordId != PacePasswordId::PACE_PUK) + { + auto paceCommand = pCommand.staticCast(); + getContext()->setPaceOutputData(paceCommand->getPaceOutput()); + } + + CardReturnCode returnCode = pCommand->getReturnCode(); + getContext()->setLastPaceResult(returnCode); + + handleNpaPosition(returnCode); + + if (mPasswordId == PacePasswordId::PACE_PIN && returnCode == CardReturnCode::OK) + { + qCDebug(statemachine) << "PACE PIN succeeded. Setting expected retry counter to:" << 3; + getContext()->setExpectedRetryCounter(3); + } + else if (mPasswordId == PacePasswordId::PACE_PUK && returnCode == CardReturnCode::OK) + { + qCDebug(statemachine) << "PACE PUK succeeded. Resetting PACE passwords and setting expected retry counter to:" << -1; + getContext()->resetPacePasswords(); + getContext()->setExpectedRetryCounter(-1); + } + + switch (returnCode) + { + case CardReturnCode::OK: + if (mPasswordId == PacePasswordId::PACE_PIN || + (mPasswordId == PacePasswordId::PACE_CAN && getContext()->isCanAllowedMode())) + { + Q_EMIT firePaceChannelEstablished(); + return; + } + else if (mPasswordId == PacePasswordId::PACE_PUK) + { + getContext()->setLastPaceResult(CardReturnCode::OK_PUK); + + if (auto changePinContext = getContext().objectCast()) + { + changePinContext->setSuccessMessage(tr("PIN successfully unblocked")); + } + + Q_EMIT firePacePukEstablished(); + return; + } + + Q_EMIT fireContinue(); + return; + + case CardReturnCode::PUK_INOPERATIVE: + case CardReturnCode::CANCELLATION_BY_USER: + updateStatus(CardReturnCodeUtil::toGlobalStatus(returnCode)); + Q_EMIT fireAbort(); + return; + + case CardReturnCode::INVALID_PIN: + { + CardReturnCode paceResult; + switch (getContext()->getCardConnection()->getReaderInfo().getRetryCounter()) + { + case 2: + // Old retryCounter is 2: 2nd try failed + paceResult = CardReturnCode::INVALID_PIN_2; + break; + + case 1: + // Old retryCounter is 1: 3rd try failed + paceResult = CardReturnCode::INVALID_PIN_3; + break; + + default: + paceResult = CardReturnCode::INVALID_PIN; + } + getContext()->setLastPaceResult(paceResult); + Q_EMIT fireAbort(); + return; + } + + default: + if (getContext()->isNpaRepositioningRequired()) + { + Q_EMIT fireAbortAndUnfortunateCardPosition(); + return; + } + + Q_EMIT fireAbort(); + return; + } + + Q_UNREACHABLE(); +} diff --git a/src/core/states/StateEstablishPaceChannel.h b/src/core/states/StateEstablishPaceChannel.h new file mode 100644 index 0000000..cd73208 --- /dev/null +++ b/src/core/states/StateEstablishPaceChannel.h @@ -0,0 +1,45 @@ +/*! + * \brief Controller for the step that tries to establish a PACE + * connection using the card's Pin. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "AbstractGenericState.h" +#include "context/WorkflowContext.h" + +class test_StateEstablishPaceChannel; + +namespace governikus +{ + +class StateEstablishPaceChannel + : public AbstractGenericState +{ + Q_OBJECT + friend class StateBuilder; + friend class ::test_StateEstablishPaceChannel; + + private: + explicit StateEstablishPaceChannel(const QSharedPointer& pContext); + + PacePasswordId mPasswordId; + + virtual void run() override; + virtual void onUserCancelled() override; + + void abort(); + void handleNpaPosition(CardReturnCode pReturnCode); + + private Q_SLOTS: + void onEstablishConnectionDone(QSharedPointer pCommand); + + Q_SIGNALS: + void firePaceChannelEstablished(); + void firePacePukEstablished(); + void fireAbortAndUnfortunateCardPosition(); +}; + +} // namespace governikus diff --git a/src/core/states/StateEstablishPacePin.cpp b/src/core/states/StateEstablishPacePin.cpp deleted file mode 100644 index ba43115..0000000 --- a/src/core/states/StateEstablishPacePin.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/*! - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - - -#include "StateEstablishPacePin.h" - -#include "context/AuthContext.h" - - -using namespace governikus; - -StateEstablishPacePin::StateEstablishPacePin(const QSharedPointer& pContext) - : AbstractGenericState(pContext) -{ -} - - -void StateEstablishPacePin::run() -{ - auto cardConnection = getContext()->getCardConnection(); - Q_ASSERT(cardConnection); - - QByteArray certificateDescription, effectiveChat; - if (auto authContext = getContext().objectCast()) - { - // if PACE is performed for authentication purposes, - // the chat and certificate description need to be send - // - // in other scenarios, e.g. for changing the PIN, the data - // is not needed - Q_ASSERT(authContext->getDidAuthenticateEac1()); - Q_ASSERT(!authContext->encodeEffectiveChat().isEmpty()); - certificateDescription = authContext->getDidAuthenticateEac1()->getCertificateDescriptionAsBinary(); - effectiveChat = authContext->encodeEffectiveChat(); - } - - qDebug() << "Establish connection using PIN"; - mConnections += cardConnection->callEstablishPaceChannelCommand(this, - &StateEstablishPacePin::onEstablishConnectionDone, - PACE_PASSWORD_ID::PACE_PIN, - getContext()->getPin(), - effectiveChat, - certificateDescription); - getContext()->setPin(QString()); -} - - -void StateEstablishPacePin::onUserCancelled() -{ - getContext()->setLastPaceResultAndRetryCounter(CardReturnCode::CANCELLATION_BY_USER, getContext()->getCardConnection()->getReaderInfo().getRetryCounter()); - AbstractState::onUserCancelled(); -} - - -void StateEstablishPacePin::onEstablishConnectionDone(QSharedPointer pCommand) -{ - CardReturnCode returnCode = pCommand->getReturnCode(); - - auto paceCommand = pCommand.staticCast(); - getContext()->setPaceOutputData(paceCommand->getPaceOutput()); - - switch (returnCode) - { - case CardReturnCode::OK: - getContext()->setLastPaceResultAndRetryCounter(returnCode, getContext()->getCardConnection()->getReaderInfo().getRetryCounter()); - Q_EMIT fireContinue(); - break; - - case CardReturnCode::CANCELLATION_BY_USER: - getContext()->setLastPaceResultAndRetryCounter(returnCode, getContext()->getCardConnection()->getReaderInfo().getRetryCounter()); - updateStatus(CardReturnCodeUtil::toGlobalStatus(returnCode)); - Q_EMIT fireAbort(); - break; - - case CardReturnCode::INVALID_PIN: - getContext()->setLastPaceResultAndRetryCounter(returnCode, getContext()->getCardConnection()->getReaderInfo().getRetryCounter()); - Q_EMIT fireInvalidPin(); - break; - - default: - updateStatus(CardReturnCodeUtil::toGlobalStatus(returnCode)); - Q_EMIT fireAbort(); - break; - } -} diff --git a/src/core/states/StateEstablishPacePin.h b/src/core/states/StateEstablishPacePin.h deleted file mode 100644 index 1cc6660..0000000 --- a/src/core/states/StateEstablishPacePin.h +++ /dev/null @@ -1,33 +0,0 @@ -/*! - * \brief Controller for the step that tries to establish a PACE - * connection using the card's Pin. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "AbstractGenericState.h" -#include "context/WorkflowContext.h" - -namespace governikus -{ - -class StateEstablishPacePin - : public AbstractGenericState -{ - Q_OBJECT - friend class StateBuilder; - - StateEstablishPacePin(const QSharedPointer& pContext); - virtual void run() override; - virtual void onUserCancelled() override; - - private Q_SLOTS: - void onEstablishConnectionDone(QSharedPointer pCommand); - - Q_SIGNALS: - void fireInvalidPin(); -}; - -} /* namespace governikus */ diff --git a/src/core/states/StateEstablishPacePuk.cpp b/src/core/states/StateEstablishPacePuk.cpp deleted file mode 100644 index 57bd8c1..0000000 --- a/src/core/states/StateEstablishPacePuk.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/*! - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - - -#include "StateEstablishPacePuk.h" - -#include "context/ChangePinContext.h" - - -using namespace governikus; - -StateEstablishPacePuk::StateEstablishPacePuk(const QSharedPointer& pContext) - : AbstractGenericState(pContext) -{ -} - - -void StateEstablishPacePuk::run() -{ - auto cardConnection = getContext()->getCardConnection(); - - Q_ASSERT(cardConnection); - qDebug() << "Invoke unblock PIN command"; - mConnections += cardConnection->callUnblockPinCommand(this, &StateEstablishPacePuk::onEstablishConnectionDone, getContext()->getPuk()); - getContext()->setPuk(QString()); -} - - -void StateEstablishPacePuk::onUserCancelled() -{ - getContext()->setLastPaceResultAndRetryCounter(CardReturnCode::CANCELLATION_BY_USER, getContext()->getCardConnection()->getReaderInfo().getRetryCounter()); - AbstractState::onUserCancelled(); -} - - -void StateEstablishPacePuk::onEstablishConnectionDone(QSharedPointer pCommand) -{ - CardReturnCode returnCode = pCommand->getReturnCode(); - switch (returnCode) - { - case CardReturnCode::OK: - getContext()->setLastPaceResultAndRetryCounter(returnCode, getContext()->getCardConnection()->getReaderInfo().getRetryCounter()); - if (auto changePinContext = getContext().objectCast()) - { - changePinContext->setSuccessMessage(tr("PIN successfully unblocked")); - } - Q_EMIT fireContinue(); - break; - - case CardReturnCode::CANCELLATION_BY_USER: - getContext()->setLastPaceResultAndRetryCounter(returnCode, getContext()->getCardConnection()->getReaderInfo().getRetryCounter()); - updateStatus(CardReturnCodeUtil::toGlobalStatus(returnCode)); - Q_EMIT fireAbort(); - break; - - case CardReturnCode::INVALID_PUK: - getContext()->setLastPaceResultAndRetryCounter(returnCode, getContext()->getCardConnection()->getReaderInfo().getRetryCounter()); - Q_EMIT fireInvalidPuk(); - break; - - case CardReturnCode::PUK_INOPERATIVE: - getContext()->setLastPaceResultAndRetryCounter(returnCode, getContext()->getCardConnection()->getReaderInfo().getRetryCounter()); - updateStatus(CardReturnCodeUtil::toGlobalStatus(returnCode)); - Q_EMIT fireInoperativePuk(); - break; - - default: - updateStatus(CardReturnCodeUtil::toGlobalStatus(returnCode)); - Q_EMIT fireAbort(); - break; - } -} diff --git a/src/core/states/StateEstablishPacePuk.h b/src/core/states/StateEstablishPacePuk.h deleted file mode 100644 index 8c7014e..0000000 --- a/src/core/states/StateEstablishPacePuk.h +++ /dev/null @@ -1,33 +0,0 @@ -/*! - * \brief Controller for the step that tries to establish a PACE - * connection using the card's PUK. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "AbstractGenericState.h" - -namespace governikus -{ - -class StateEstablishPacePuk - : public AbstractGenericState -{ - Q_OBJECT - friend class StateBuilder; - - StateEstablishPacePuk(const QSharedPointer& pContext); - virtual void run() override; - virtual void onUserCancelled() override; - - private Q_SLOTS: - void onEstablishConnectionDone(QSharedPointer pCommand); - - Q_SIGNALS: - void fireInvalidPuk(); - void fireInoperativePuk(); -}; - -} /* namespace governikus */ diff --git a/src/core/states/StateExtractCvcsFromEac1InputType.cpp b/src/core/states/StateExtractCvcsFromEac1InputType.cpp index 5794a96..2382561 100644 --- a/src/core/states/StateExtractCvcsFromEac1InputType.cpp +++ b/src/core/states/StateExtractCvcsFromEac1InputType.cpp @@ -9,7 +9,7 @@ using namespace governikus; StateExtractCvcsFromEac1InputType::StateExtractCvcsFromEac1InputType(const QSharedPointer& pContext) - : AbstractGenericState(pContext) + : AbstractGenericState(pContext, false) { } diff --git a/src/core/states/StateExtractCvcsFromEac1InputType.h b/src/core/states/StateExtractCvcsFromEac1InputType.h index de03ee1..edf3516 100644 --- a/src/core/states/StateExtractCvcsFromEac1InputType.h +++ b/src/core/states/StateExtractCvcsFromEac1InputType.h @@ -9,7 +9,6 @@ #include "context/AuthContext.h" #include "states/AbstractGenericState.h" -class test_StateExtractCvcsFromEac1InputType; namespace governikus { @@ -21,8 +20,8 @@ class StateExtractCvcsFromEac1InputType friend class StateBuilder; friend class ::test_StateExtractCvcsFromEac1InputType; - StateExtractCvcsFromEac1InputType(const QSharedPointer& pContext); + explicit StateExtractCvcsFromEac1InputType(const QSharedPointer& pContext); virtual void run() override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/StateGenericSendReceive.cpp b/src/core/states/StateGenericSendReceive.cpp index 60bb3d4..4a77ca2 100644 --- a/src/core/states/StateGenericSendReceive.cpp +++ b/src/core/states/StateGenericSendReceive.cpp @@ -4,20 +4,25 @@ #include "StateGenericSendReceive.h" +#include "AppSettings.h" #include "CertificateChecker.h" #include "Env.h" +#include "LogHandler.h" #include "paos/PaosHandler.h" #include "TlsChecker.h" -#include +#if QT_VERSION < QT_VERSION_CHECK(5, 11, 2) + #include +#endif +Q_DECLARE_LOGGING_CATEGORY(developermode) Q_DECLARE_LOGGING_CATEGORY(network) using namespace governikus; -StateGenericSendReceive::StateGenericSendReceive(const QSharedPointer& pContext, const QVector& pTypesToReceive) - : AbstractGenericState(pContext) +StateGenericSendReceive::StateGenericSendReceive(const QSharedPointer& pContext, const QVector& pTypesToReceive, bool pConnectOnCardRemoved) + : AbstractGenericState(pContext, pConnectOnCardRemoved) , mTypesToReceive(pTypesToReceive) , mReply() { @@ -25,13 +30,18 @@ StateGenericSendReceive::StateGenericSendReceive(const QSharedPointer& pContext, PaosType pTypesToReceive) - : StateGenericSendReceive(pContext, QVector() << pTypesToReceive) + : StateGenericSendReceive(pContext, QVector + { + pTypesToReceive + }) { } void StateGenericSendReceive::setReceivedMessage(const QSharedPointer& pMessage) { + getContext()->setReceivedMessageId(pMessage->getMessageId()); + switch (pMessage->mType) { case PaosType::STARTPAOS_RESPONSE: @@ -41,23 +51,23 @@ void StateGenericSendReceive::setReceivedMessage(const QSharedPointersetInitializeFramework(pMessage.staticCast()); - getContext()->setInitializeFrameworkResponse(QSharedPointer(new InitializeFrameworkResponse())); + getContext()->setInitializeFrameworkResponse(QSharedPointer::create()); break; case PaosType::DID_LIST: getContext()->setDidList(pMessage.staticCast()); - getContext()->setDidListResponse(QSharedPointer(new DIDListResponse())); + getContext()->setDidListResponse(QSharedPointer::create()); break; case PaosType::DID_AUTHENTICATE_EAC1: getContext()->setDidAuthenticateEac1(pMessage.staticCast()); - getContext()->setDidAuthenticateResponseEac1(QSharedPointer(new DIDAuthenticateResponseEAC1())); + getContext()->setDidAuthenticateResponseEac1(QSharedPointer::create()); break; case PaosType::DID_AUTHENTICATE_EAC2: getContext()->setDidAuthenticateEac2(pMessage.staticCast()); - getContext()->setDidAuthenticateResponseEac2(QSharedPointer(new DIDAuthenticateResponseEAC2())); - getContext()->setDidAuthenticateResponseEacAdditionalInputType(QSharedPointer(new DIDAuthenticateResponseEAC2())); + getContext()->setDidAuthenticateResponseEac2(QSharedPointer::create()); + getContext()->setDidAuthenticateResponseEacAdditionalInputType(QSharedPointer::create()); break; case PaosType::DID_AUTHENTICATE_EAC_ADDITIONAL_INPUT_TYPE: @@ -67,12 +77,12 @@ void StateGenericSendReceive::setReceivedMessage(const QSharedPointeraddTransmit(pMessage.staticCast()); - getContext()->addTransmitResponse(QSharedPointer(new TransmitResponse())); + getContext()->addTransmitResponse(QSharedPointer::create()); break; case PaosType::DISCONNECT: getContext()->setDisconnect(pMessage.staticCast()); - getContext()->setDisconnectResponse(QSharedPointer(new DisconnectResponse())); + getContext()->setDisconnectResponse(QSharedPointer::create()); break; default: @@ -82,22 +92,6 @@ void StateGenericSendReceive::setReceivedMessage(const QSharedPointer& pPaosMessage) -{ - Q_ASSERT(!pPaosMessage.isNull()); - const QSharedPointer messageIdHandler = getContext()->getMessageIdHandler(); - pPaosMessage->setRelatesTo(messageIdHandler->getRemoteMsgId()); - pPaosMessage->setMessageId(messageIdHandler->createNewMsgId()); -} - - -void StateGenericSendReceive::setRemoteMessageId(const QSharedPointer& pPaosMessage) -{ - const QSharedPointer messageIdHandler = getContext()->getMessageIdHandler(); - messageIdHandler->setRemoteMsgId(pPaosMessage->getMessageId()); -} - - void StateGenericSendReceive::onSslErrors(const QList& pErrors) { if (TlsChecker::containsFatalError(mReply, pErrors)) @@ -112,14 +106,30 @@ void StateGenericSendReceive::onSslErrors(const QList& pErrors) void StateGenericSendReceive::onSslHandshakeDone() { const auto& cfg = mReply->sslConfiguration(); - TlsChecker::logSslConfig(cfg, qInfo(network)); + TlsChecker::logSslConfig(cfg, spawnMessageLogger(network)); bool abort = false; const auto statusCode = checkAndSaveCertificate(cfg.peerCertificate()); if (statusCode != GlobalStatus::Code::No_Error) { qCCritical(network) << GlobalStatus(statusCode); - updateStatus(statusCode); + + switch (statusCode) + { + case GlobalStatus::Code::Workflow_TrustedChannel_Hash_Not_In_Description: + case GlobalStatus::Code::Workflow_Nerwork_Ssl_Hash_Not_In_Certificate_Description: + case GlobalStatus::Code::Workflow_TrustedChannel_Ssl_Certificate_Unsupported_Algorithm_Or_Length: + case GlobalStatus::Code::Workflow_Network_Ssl_Certificate_Unsupported_Algorithm_Or_Length: + { + const auto& issuerName = TlsChecker::getCertificateIssuerName(cfg.peerCertificate()); + updateStatus(GlobalStatus(statusCode, issuerName)); + break; + } + + default: + updateStatus(statusCode); + break; + } abort = true; } @@ -129,9 +139,18 @@ void StateGenericSendReceive::onSslHandshakeDone() const auto& session = context->getSslSession(); if (session.isEmpty() || session != cfg.sessionTicket()) { - qCCritical(network) << "Session resumption failed"; - updateStatus(GlobalStatus::Code::Workflow_TrustedChannel_Ssl_Establishment_Error); - abort = true; + const auto& sessionFailedError = "Session resumption failed"; + + if (Env::getSingleton()->getGeneralSettings().isDeveloperMode()) + { + qCCritical(developermode) << sessionFailedError; + } + else + { + qCCritical(network) << sessionFailedError; + updateStatus(GlobalStatus::Code::Workflow_TrustedChannel_Ssl_Establishment_Error); + abort = true; + } } } @@ -143,6 +162,9 @@ void StateGenericSendReceive::onSslHandshakeDone() mReply->abort(); +#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 2) + Q_EMIT fireAbort(); +#else // We need to append the abort to the event queue, because // QNetworkAccessManager::clearConnectionCache forces sending // the request when the abort on QNetworkReply is not finished. @@ -150,6 +172,7 @@ void StateGenericSendReceive::onSslHandshakeDone() QTimer::singleShot(0, this, [this] { Q_EMIT fireAbort(); }); +#endif } } @@ -197,25 +220,30 @@ GlobalStatus::Code StateGenericSendReceive::checkAndSaveCertificate(const QSslCe void StateGenericSendReceive::run() { - setMessageId(getAsMessage()); + Q_ASSERT(getContext()->getPin().isEmpty() && "PACE passwords must be cleared as soon as possible."); + Q_ASSERT(getContext()->getCan().isEmpty() && "PACE passwords must be cleared as soon as possible."); + Q_ASSERT(getContext()->getPuk().isEmpty() && "PACE passwords must be cleared as soon as possible."); + + getAsCreator()->setRelatedMessageId(getContext()->getReceivedMessageId()); if (QSharedPointer paosResponse = getAsResponse()) { if (!getContext()->isErrorReportedToServer() && getContext()->getStatus().isError() && !getContext()->getStatus().isOriginServer()) { - if (getContext()->getTransmitResponseFailed()) + auto ctx = getContext(); + if (!ctx->getStartPaosResult().isOk()) { - paosResponse->setResult(GlobalStatus(GlobalStatus::Code::Paos_Error_AL_Unknown_Error)); + paosResponse->setResult(ctx->getStartPaosResult()); } else { - paosResponse->setResult(getContext()->getStatus()); + paosResponse->setResult(ctx->getStatus()); } - getContext()->setErrorReportedToServer(true); + ctx->setErrorReportedToServer(true); } else { - paosResponse->setResult(Result::createOk()); + paosResponse->setResult(ECardApiResult::createOk()); } } @@ -228,7 +256,7 @@ void StateGenericSendReceive::run() const QByteArray& data = getAsCreator()->marshall(); Q_ASSERT(!data.isEmpty()); - qCDebug(network) << "Try to send raw data:\n" << data; + qCDebug(network).noquote() << "Try to send raw data:\n" << data; const QByteArray& paosNamespace = PaosCreator::getNamespace(PaosCreator::Namespace::PAOS).toUtf8(); const auto& session = token->usePsk() ? QByteArray() : getContext()->getSslSession(); mReply = Env::getSingleton()->paos(request, paosNamespace, data, token->usePsk(), session); @@ -246,20 +274,17 @@ void StateGenericSendReceive::onReplyFinished() mReply.clear(); reply->deleteLater(); + const auto statusCode = NetworkManager::getLoggedStatusCode(reply, spawnMessageLogger(network)); + if (reply->error() != QNetworkReply::NoError) { - const auto& statusCode = NetworkManager::toTrustedChannelStatus(reply); - qCCritical(network) << GlobalStatus(statusCode); - updateStatus(statusCode); + const auto& channelStatus = NetworkManager::toTrustedChannelStatus(reply); + qCCritical(network) << GlobalStatus(channelStatus); + updateStatus(channelStatus); Q_EMIT fireAbort(); return; } - for (const auto& header : reply->rawHeaderPairs()) - { - qCDebug(network) << "RawHeader |" << header.first << "=" << header.second; - } - const int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if (statusCode >= 500) { qCCritical(network) << GlobalStatus(GlobalStatus::Code::Workflow_TrustedChannel_Error_From_Server); @@ -269,7 +294,7 @@ void StateGenericSendReceive::onReplyFinished() } QByteArray message = reply->readAll(); - qCDebug(network) << "Received raw data:\n" << message; + qCDebug(network).noquote() << "Received raw data:\n" << message; PaosHandler paosHandler(message); qCDebug(network) << "Received PAOS message of type:" << paosHandler.getDetectedPaosType(); @@ -298,7 +323,6 @@ void StateGenericSendReceive::onReplyFinished() return; } - setRemoteMessageId(paosHandler.getPaosMessage()); setReceivedMessage(paosHandler.getPaosMessage()); int result = mTypesToReceive.indexOf(paosHandler.getDetectedPaosType()); @@ -314,7 +338,6 @@ void StateGenericSendReceive::onReplyFinished() * 2) The detected PAOS type is unknown, so result == -1 * By adding 2, result == 1 and the next state is state error */ - result += 2; if (result == 1) diff --git a/src/core/states/StateGenericSendReceive.h b/src/core/states/StateGenericSendReceive.h index 72fcef0..d6e071b 100644 --- a/src/core/states/StateGenericSendReceive.h +++ b/src/core/states/StateGenericSendReceive.h @@ -14,6 +14,8 @@ #include #include +class test_StateGenericSendReceive; + namespace governikus { @@ -23,11 +25,10 @@ class StateGenericSendReceive Q_OBJECT private: + friend class ::test_StateGenericSendReceive; const QVector mTypesToReceive; QPointer mReply; - void setMessageId(const QSharedPointer& pPaosMessage); - void setRemoteMessageId(const QSharedPointer& pPaosMessage); void setReceivedMessage(const QSharedPointer& pMessage); GlobalStatus::Code checkAndSaveCertificate(const QSslCertificate& pCertificate); void onSslErrors(const QList& pErrors); @@ -35,8 +36,8 @@ class StateGenericSendReceive virtual void run() override; protected: - StateGenericSendReceive(const QSharedPointer& pContext, const QVector& pTypesToReceive); - StateGenericSendReceive(const QSharedPointer& pContext, PaosType pTypesToReceive); + explicit StateGenericSendReceive(const QSharedPointer& pContext, const QVector& pTypesToReceive, bool pConnectOnCardRemoved = true); + explicit StateGenericSendReceive(const QSharedPointer& pContext, PaosType pTypesToReceive); virtual QSharedPointer getAsMessage() = 0; virtual QSharedPointer getAsResponse() = 0; @@ -55,8 +56,11 @@ class StateSendStartPaos friend class StateBuilder; StateSendStartPaos(const QSharedPointer& pContext) - : StateGenericSendReceive(pContext, QVector() << PaosType::INITIALIZE_FRAMEWORK << PaosType::DID_LIST - << PaosType::DID_AUTHENTICATE_EAC1 << PaosType::STARTPAOS_RESPONSE) + : StateGenericSendReceive(pContext, QVector + { + PaosType::INITIALIZE_FRAMEWORK, PaosType::DID_LIST, + PaosType::DID_AUTHENTICATE_EAC1, PaosType::STARTPAOS_RESPONSE + }, false) { } @@ -112,7 +116,10 @@ class StateSendInitializeFrameworkResponse friend class StateBuilder; StateSendInitializeFrameworkResponse(const QSharedPointer& pContext) - : StateGenericSendReceive(pContext, QVector() << PaosType::DID_LIST << PaosType::DID_AUTHENTICATE_EAC1 << PaosType::STARTPAOS_RESPONSE) + : StateGenericSendReceive(pContext, QVector + { + PaosType::DID_LIST, PaosType::DID_AUTHENTICATE_EAC1, PaosType::STARTPAOS_RESPONSE + }, false) { } @@ -163,7 +170,10 @@ class StateSendDIDListResponse friend class StateBuilder; StateSendDIDListResponse(const QSharedPointer& pContext) - : StateGenericSendReceive(pContext, QVector() << PaosType::DID_AUTHENTICATE_EAC1 << PaosType::DISCONNECT << PaosType::STARTPAOS_RESPONSE) + : StateGenericSendReceive(pContext, QVector + { + PaosType::DID_AUTHENTICATE_EAC1, PaosType::DISCONNECT, PaosType::STARTPAOS_RESPONSE + }, false) { } @@ -213,7 +223,10 @@ class StateSendDIDAuthenticateResponseEAC1 friend class StateBuilder; StateSendDIDAuthenticateResponseEAC1(const QSharedPointer& pContext) - : StateGenericSendReceive(pContext, QVector() << PaosType::DID_AUTHENTICATE_EAC2 << PaosType::DISCONNECT << PaosType::STARTPAOS_RESPONSE) + : StateGenericSendReceive(pContext, QVector + { + PaosType::DID_AUTHENTICATE_EAC2, PaosType::DISCONNECT, PaosType::STARTPAOS_RESPONSE + }) { } @@ -263,7 +276,10 @@ class StateSendDIDAuthenticateResponseEACAdditionalInputType friend class StateBuilder; StateSendDIDAuthenticateResponseEACAdditionalInputType(const QSharedPointer& pContext) - : StateGenericSendReceive(pContext, QVector() << PaosType::DID_AUTHENTICATE_EAC_ADDITIONAL_INPUT_TYPE << PaosType::STARTPAOS_RESPONSE) + : StateGenericSendReceive(pContext, QVector + { + PaosType::DID_AUTHENTICATE_EAC_ADDITIONAL_INPUT_TYPE, PaosType::STARTPAOS_RESPONSE + }) { } @@ -308,7 +324,10 @@ class StateSendDIDAuthenticateResponseEAC2 friend class StateBuilder; StateSendDIDAuthenticateResponseEAC2(const QSharedPointer& pContext) - : StateGenericSendReceive(pContext, QVector() << PaosType::TRANSMIT << PaosType::DISCONNECT << PaosType::STARTPAOS_RESPONSE) + : StateGenericSendReceive(pContext, QVector + { + PaosType::TRANSMIT, PaosType::DISCONNECT, PaosType::STARTPAOS_RESPONSE + }) { } @@ -358,7 +377,10 @@ class StateSendTransmitResponse friend class StateBuilder; StateSendTransmitResponse(const QSharedPointer& pContext) - : StateGenericSendReceive(pContext, QVector() << PaosType::TRANSMIT << PaosType::DISCONNECT << PaosType::STARTPAOS_RESPONSE) + : StateGenericSendReceive(pContext, QVector + { + PaosType::TRANSMIT, PaosType::DISCONNECT, PaosType::STARTPAOS_RESPONSE + }) { } @@ -441,4 +463,4 @@ class StateSendDisconnectResponse }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/StateGetSelfAuthenticationData.cpp b/src/core/states/StateGetSelfAuthenticationData.cpp index b04a9c5..db2584e 100644 --- a/src/core/states/StateGetSelfAuthenticationData.cpp +++ b/src/core/states/StateGetSelfAuthenticationData.cpp @@ -5,11 +5,11 @@ #include "StateGetSelfAuthenticationData.h" #include "CertificateChecker.h" -#include "Env.h" -#include "HttpStatusCode.h" +#include "LogHandler.h" #include "SelfAuthenticationData.h" #include "TlsChecker.h" +#include #include #include @@ -58,7 +58,7 @@ void StateGetSelfAuthenticationData::reportCommunicationError(const GlobalStatus void StateGetSelfAuthenticationData::onSslHandshakeDone() { const auto& cfg = mReply->sslConfiguration(); - TlsChecker::logSslConfig(cfg, qInfo(network)); + TlsChecker::logSslConfig(cfg, spawnMessageLogger(network)); if (!checkSslConnectionAndSaveCertificate(cfg)) { @@ -79,17 +79,18 @@ bool StateGetSelfAuthenticationData::checkSslConnectionAndSaveCertificate(const context->addCertificateData(pUrl, pCertificate); }; + const auto& issuerName = TlsChecker::getCertificateIssuerName(pSslConfiguration.peerCertificate()); switch (CertificateChecker::checkAndSaveCertificate(pSslConfiguration.peerCertificate(), getContext()->getRefreshUrl(), context->getDidAuthenticateEac1(), context->getDvCvc(), saveCertificateFunc)) { case CertificateChecker::CertificateStatus::Good: break; case CertificateChecker::CertificateStatus::Unsupported_Algorithm_Or_Length: - reportCommunicationError(GlobalStatus(GlobalStatus::Code::Workflow_Network_Ssl_Certificate_Unsupported_Algorithm_Or_Length)); + reportCommunicationError(GlobalStatus(GlobalStatus::Code::Workflow_Network_Ssl_Certificate_Unsupported_Algorithm_Or_Length, issuerName)); return false; case CertificateChecker::CertificateStatus::Hash_Not_In_Description: - reportCommunicationError(GlobalStatus(GlobalStatus::Code::Workflow_Nerwork_Ssl_Hash_Not_In_Certificate_Description)); + reportCommunicationError(GlobalStatus(GlobalStatus::Code::Workflow_Nerwork_Ssl_Hash_Not_In_Certificate_Description, issuerName)); return false; } @@ -114,10 +115,8 @@ void StateGetSelfAuthenticationData::onSslErrors(const QList& pErrors void StateGetSelfAuthenticationData::onNetworkReply() { - int statusCode = mReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - qDebug() << statusCode; - - if (statusCode == HttpStatusCode::OK) + const auto statusCode = NetworkManager::getLoggedStatusCode(mReply, spawnMessageLogger(network)); + if (statusCode == HTTP_STATUS_OK) { const SelfAuthenticationData data(mReply->readAll()); if (data.isValid()) @@ -127,14 +126,14 @@ void StateGetSelfAuthenticationData::onNetworkReply() } else { - qDebug() << "Could not read data for self authentication."; + qDebug() << "No valid data of self authentication."; updateStatus(GlobalStatus::Code::Workflow_Server_Incomplete_Information_Provided); Q_EMIT fireAbort(); } } else { - qDebug() << "Could not read data for self authentication. StatusCode:" << statusCode; + qDebug() << "Could not read data for self authentication."; updateStatus(GlobalStatus::Code::Workflow_Server_Incomplete_Information_Provided); Q_EMIT fireAbort(); } diff --git a/src/core/states/StateGetSelfAuthenticationData.h b/src/core/states/StateGetSelfAuthenticationData.h index 374432c..e7068a2 100644 --- a/src/core/states/StateGetSelfAuthenticationData.h +++ b/src/core/states/StateGetSelfAuthenticationData.h @@ -8,7 +8,6 @@ #include "context/SelfAuthContext.h" #include "NetworkManager.h" -#include "Result.h" #include "states/AbstractGenericState.h" namespace governikus @@ -22,7 +21,7 @@ class StateGetSelfAuthenticationData QPointer mReply; - StateGetSelfAuthenticationData(const QSharedPointer& pContext); + explicit StateGetSelfAuthenticationData(const QSharedPointer& pContext); virtual void run() override; void reportCommunicationError(const GlobalStatus& pStatus); bool checkSslConnectionAndSaveCertificate(const QSslConfiguration& pSslConfiguration); @@ -36,4 +35,4 @@ class StateGetSelfAuthenticationData void onSslHandshakeDone(); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/StateGetTcToken.cpp b/src/core/states/StateGetTcToken.cpp index deba2fe..2a42299 100644 --- a/src/core/states/StateGetTcToken.cpp +++ b/src/core/states/StateGetTcToken.cpp @@ -5,10 +5,11 @@ #include "StateGetTcToken.h" #include "AppSettings.h" -#include "Env.h" +#include "LogHandler.h" #include "NetworkManager.h" #include "TlsChecker.h" +#include #include #include #include @@ -21,7 +22,7 @@ Q_DECLARE_LOGGING_CATEGORY(network) StateGetTcToken::StateGetTcToken(const QSharedPointer& pContext) - : AbstractGenericState(pContext) + : AbstractGenericState(pContext, false) , mReply() { } @@ -68,7 +69,7 @@ bool StateGetTcToken::isValidRedirectUrl(const QUrl& pUrl) // in contrast a HTTP error 404 must be sent, if the TCToken could not be determined const auto httpsError1 = QStringLiteral("Error while connecting to the service provider. A secure connection could not be established."); const auto httpsError2 = QStringLiteral(" The used URL is not of type HTTPS: %1").arg(pUrl.toString()); - if (AppSettings::getInstance().getGeneralSettings().isDeveloperMode()) + if (Env::getSingleton()->getGeneralSettings().isDeveloperMode()) { qCCritical(developermode) << httpsError1; qCCritical(developermode) << httpsError2; @@ -110,7 +111,7 @@ void StateGetTcToken::onSslErrors(const QList& pErrors) void StateGetTcToken::onSslHandshakeDone() { const auto& cfg = mReply->sslConfiguration(); - TlsChecker::logSslConfig(cfg, qInfo(network)); + TlsChecker::logSslConfig(cfg, spawnMessageLogger(network)); // At this point we can only check the certificate key's length. if (!TlsChecker::hasValidCertificateKeyLength(cfg.peerCertificate())) @@ -139,13 +140,7 @@ void StateGetTcToken::onSslHandshakeDone() void StateGetTcToken::onNetworkReply() { - int statusCode = mReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - QUrl redirectUrl = mReply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); - qDebug() << "Status Code: " << statusCode << " | RedirectUrl: " << redirectUrl; - for (const auto& header : mReply->rawHeaderPairs()) - { - qCDebug(network).nospace() << "Header | " << header.first << ": " << header.second; - } + const auto statusCode = NetworkManager::getLoggedStatusCode(mReply, spawnMessageLogger(network)); if (mReply->error() != QNetworkReply::NoError) { @@ -155,19 +150,20 @@ void StateGetTcToken::onNetworkReply() return; } - if (statusCode == HttpStatusCode::OK) + if (statusCode == HTTP_STATUS_OK) { parseTcToken(); return; } + const QUrl& redirectUrl = mReply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); if (!isValidRedirectUrl(redirectUrl)) { Q_EMIT fireAbort(); return; } - if (statusCode != HttpStatusCode::SEE_OTHER && statusCode != HttpStatusCode::FOUND && statusCode != HttpStatusCode::TEMPORARY_REDIRECT) + if (statusCode != HTTP_STATUS_SEE_OTHER && statusCode != HTTP_STATUS_FOUND && statusCode != HTTP_STATUS_TEMPORARY_REDIRECT) { qCritical() << "Error while connecting to the service provider. The server returns an unexpected status code:" << statusCode; updateStatus(GlobalStatus::Code::Workflow_TrustedChannel_Server_Format_Error); @@ -191,7 +187,7 @@ void StateGetTcToken::parseTcToken() Q_EMIT fireAbort(); return; } - QSharedPointer tcToken(new TcToken(data)); + const auto& tcToken = QSharedPointer::create(data); getContext()->setTcToken(tcToken); getContext()->setTcTokenNotFound(!tcToken->isSchemaConform()); diff --git a/src/core/states/StateGetTcToken.h b/src/core/states/StateGetTcToken.h index 5442f40..7c4b751 100644 --- a/src/core/states/StateGetTcToken.h +++ b/src/core/states/StateGetTcToken.h @@ -12,6 +12,8 @@ #include "context/AuthContext.h" #include "states/AbstractGenericState.h" +class test_StateGetTcToken; + namespace governikus { @@ -20,6 +22,7 @@ class StateGetTcToken { Q_OBJECT friend class StateBuilder; + friend class ::test_StateGetTcToken; QPointer mReply; @@ -28,7 +31,7 @@ class StateGetTcToken bool isValidRedirectUrl(const QUrl& pUrl); virtual void run() override; - StateGetTcToken(const QSharedPointer& pContext); + explicit StateGetTcToken(const QSharedPointer& pContext); public: virtual ~StateGetTcToken() override; @@ -39,4 +42,4 @@ class StateGetTcToken void onSslErrors(const QList& pErrors); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/StateHandleRetryCounter.cpp b/src/core/states/StateHandleRetryCounter.cpp deleted file mode 100644 index 39cdf92..0000000 --- a/src/core/states/StateHandleRetryCounter.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "StateHandleRetryCounter.h" - -#include "CardConnection.h" - - -using namespace governikus; - - -StateHandleRetryCounter::StateHandleRetryCounter(const QSharedPointer& pContext) - : AbstractGenericState(pContext) -{ -} - - -void StateHandleRetryCounter::run() -{ - auto cardConnection = getContext()->getCardConnection(); - Q_ASSERT(cardConnection != nullptr); - - switch (cardConnection->getReaderInfo().getRetryCounter()) - { - case 0: - qDebug() << "PUK required"; - Q_EMIT fireRetryCounterIsZero(); - break; - - case 1: - qDebug() << "CAN required"; - Q_EMIT fireRetryCounterIsOne(); - break; - - default: - qDebug() << "PIN allowed"; - Q_EMIT fireRetryCounterIsGTOne(); - } - -} diff --git a/src/core/states/StateHandleRetryCounter.h b/src/core/states/StateHandleRetryCounter.h deleted file mode 100644 index 6bdaf1e..0000000 --- a/src/core/states/StateHandleRetryCounter.h +++ /dev/null @@ -1,29 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "AbstractGenericState.h" - -#include "context/ChangePinContext.h" - -namespace governikus -{ - -class StateHandleRetryCounter - : public AbstractGenericState -{ - Q_OBJECT - friend class StateBuilder; - - StateHandleRetryCounter(const QSharedPointer& pContext); - virtual void run() override; - - Q_SIGNALS: - void fireRetryCounterIsZero(); - void fireRetryCounterIsOne(); - void fireRetryCounterIsGTOne(); -}; - -} /* namespace governikus */ diff --git a/src/core/states/StateInitializeFramework.cpp b/src/core/states/StateInitializeFramework.cpp index 86124d4..856546a 100644 --- a/src/core/states/StateInitializeFramework.cpp +++ b/src/core/states/StateInitializeFramework.cpp @@ -8,7 +8,7 @@ using namespace governikus; StateInitializeFramework::StateInitializeFramework(const QSharedPointer& pContext) - : AbstractGenericState(pContext) + : AbstractGenericState(pContext, false) { } diff --git a/src/core/states/StateInitializeFramework.h b/src/core/states/StateInitializeFramework.h index 9307e58..81285c8 100644 --- a/src/core/states/StateInitializeFramework.h +++ b/src/core/states/StateInitializeFramework.h @@ -18,8 +18,8 @@ class StateInitializeFramework Q_OBJECT friend class StateBuilder; - StateInitializeFramework(const QSharedPointer& pContext); + explicit StateInitializeFramework(const QSharedPointer& pContext); virtual void run() override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/StateLoadTcTokenUrl.cpp b/src/core/states/StateLoadTcTokenUrl.cpp index 44197e3..92b1d69 100644 --- a/src/core/states/StateLoadTcTokenUrl.cpp +++ b/src/core/states/StateLoadTcTokenUrl.cpp @@ -4,7 +4,6 @@ #include "AppSettings.h" -#include "Env.h" #include "states/StateLoadTcTokenUrl.h" #include "SecureStorage.h" diff --git a/src/core/states/StateLoadTcTokenUrl.h b/src/core/states/StateLoadTcTokenUrl.h index be76656..8b8fd7c 100644 --- a/src/core/states/StateLoadTcTokenUrl.h +++ b/src/core/states/StateLoadTcTokenUrl.h @@ -18,8 +18,8 @@ class StateLoadTcTokenUrl Q_OBJECT friend class StateBuilder; - StateLoadTcTokenUrl(const QSharedPointer& pContext); + explicit StateLoadTcTokenUrl(const QSharedPointer& pContext); virtual void run() override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/StateMaintainCardConnection.cpp b/src/core/states/StateMaintainCardConnection.cpp new file mode 100644 index 0000000..597d67e --- /dev/null +++ b/src/core/states/StateMaintainCardConnection.cpp @@ -0,0 +1,118 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "StateMaintainCardConnection.h" + +#include "CardReturnCode.h" + +Q_DECLARE_LOGGING_CATEGORY(statemachine) + + +using namespace governikus; + + +StateMaintainCardConnection::StateMaintainCardConnection(const QSharedPointer& pContext) + : AbstractGenericState(pContext, false) +{ +} + + +void StateMaintainCardConnection::run() +{ + auto context = getContext(); + if (context->getStatus().isError()) + { + Q_EMIT fireAbort(); + return; + } + + const CardReturnCode lastPaceResult = context->getLastPaceResult(); + qCDebug(statemachine) << "Last PACE result:" << lastPaceResult; + + switch (lastPaceResult) + { + case CardReturnCode::CANCELLATION_BY_USER: + case CardReturnCode::PUK_INOPERATIVE: + case CardReturnCode::INPUT_TIME_OUT: + case CardReturnCode::UNKNOWN: + case CardReturnCode::UNDEFINED: + case CardReturnCode::COMMAND_FAILED: + case CardReturnCode::PROTOCOL_ERROR: + case CardReturnCode::UNEXPECTED_TRANSMIT_STATUS: + { + Q_ASSERT(!CardReturnCodeUtil::equalsWrongPacePassword(lastPaceResult)); + + qCDebug(statemachine) << "Last PACE result is unrecoverable. Aborting."; + updateStatus(CardReturnCodeUtil::toGlobalStatus(lastPaceResult)); + Q_EMIT fireAbort(); + return; + } + + case CardReturnCode::INVALID_CAN: + case CardReturnCode::INVALID_PIN: + case CardReturnCode::INVALID_PIN_2: + case CardReturnCode::INVALID_PIN_3: + case CardReturnCode::INVALID_PUK: + case CardReturnCode::NEW_PIN_MISMATCH: + case CardReturnCode::NEW_PIN_INVALID_LENGTH: + case CardReturnCode::PIN_NOT_BLOCKED: + case CardReturnCode::PIN_BLOCKED: + { + Q_ASSERT(CardReturnCodeUtil::equalsWrongPacePassword(lastPaceResult)); + + qCDebug(statemachine) << "Reseting all PACE passwords."; + context->resetPacePasswords(); + + if (context->getCardConnection()) + { + qCDebug(statemachine) << "Trigger retry counter update."; + Q_EMIT fireForceUpdateRetryCounter(); + } + else + { + qCDebug(statemachine) << "No card connection available."; + Q_EMIT fireNoCardConnection(); + } + return; + } + + case CardReturnCode::RETRY_ALLOWED: + case CardReturnCode::CARD_NOT_FOUND: + { + Q_ASSERT(!CardReturnCodeUtil::equalsWrongPacePassword(lastPaceResult)); + + qCDebug(statemachine) << "Assuming the card was removed (" << lastPaceResult << ") . Resetting card connection and PACE result."; + context->resetCardConnection(); + context->resetLastPaceResult(); + break; + } + + case CardReturnCode::OK: + case CardReturnCode::OK_PUK: + { + Q_ASSERT(!CardReturnCodeUtil::equalsWrongPacePassword(lastPaceResult)); + + if (lastPaceResult == CardReturnCode::OK_PUK) + { + getContext()->resetLastPaceResult(); + + qCDebug(statemachine) << "PIN unblocked! Triggering retry counter update."; + Q_EMIT fireForceUpdateRetryCounter(); + return; + } + + break; + } + } + + if (!context->getCardConnection()) + { + qCDebug(statemachine) << "No card connection available."; + Q_EMIT fireNoCardConnection(); + return; + } + + qCDebug(statemachine) << "Card connection is fine. Proceeding."; + Q_EMIT fireContinue(); +} diff --git a/src/core/states/StateMaintainCardConnection.h b/src/core/states/StateMaintainCardConnection.h new file mode 100644 index 0000000..eb20f45 --- /dev/null +++ b/src/core/states/StateMaintainCardConnection.h @@ -0,0 +1,28 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/WorkflowContext.h" +#include "states/AbstractGenericState.h" + +namespace governikus +{ + +class StateMaintainCardConnection + : public AbstractGenericState +{ + Q_OBJECT + friend class StateBuilder; + + public: + explicit StateMaintainCardConnection(const QSharedPointer& pContext); + virtual void run() override; + + Q_SIGNALS: + void fireNoCardConnection(); + void fireForceUpdateRetryCounter(); +}; + +} // namespace governikus diff --git a/src/core/states/StateParseTcTokenUrl.cpp b/src/core/states/StateParseTcTokenUrl.cpp index a4abed7..e537d40 100644 --- a/src/core/states/StateParseTcTokenUrl.cpp +++ b/src/core/states/StateParseTcTokenUrl.cpp @@ -6,9 +6,6 @@ #include "StateParseTcTokenUrl.h" -#include "HttpStatusCode.h" -#include "Result.h" - #include using namespace governikus; diff --git a/src/core/states/StateParseTcTokenUrl.h b/src/core/states/StateParseTcTokenUrl.h index 16cd13d..f6bfbbd 100644 --- a/src/core/states/StateParseTcTokenUrl.h +++ b/src/core/states/StateParseTcTokenUrl.h @@ -18,9 +18,9 @@ class StateParseTcTokenUrl Q_OBJECT friend class StateBuilder; - StateParseTcTokenUrl(const QSharedPointer& pContext); + explicit StateParseTcTokenUrl(const QSharedPointer& pContext); virtual void run() override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/StatePreVerification.cpp b/src/core/states/StatePreVerification.cpp index 7f6f896..50efa49 100644 --- a/src/core/states/StatePreVerification.cpp +++ b/src/core/states/StatePreVerification.cpp @@ -8,17 +8,17 @@ #include "asn1/SignatureChecker.h" #include "AppSettings.h" #include "EnumHelper.h" -#include "Env.h" #include "SecureStorage.h" #include +Q_DECLARE_LOGGING_CATEGORY(developermode) using namespace governikus; StatePreVerification::StatePreVerification(const QSharedPointer& pContext) - : AbstractGenericState(pContext) + : AbstractGenericState(pContext, false) , mTrustedCvcas(CVCertificate::fromHex(SecureStorage::getInstance().getCVRootCertificates(true)) + CVCertificate::fromHex(SecureStorage::getInstance().getCVRootCertificates(false))) , mValidationDateTime(QDateTime::currentDateTime()) @@ -46,18 +46,16 @@ void StatePreVerification::run() } } - if (Env::getSingleton()->getGeneralSettings().isDeveloperMode()) + const bool developerMode = Env::getSingleton()->getGeneralSettings().isDeveloperMode(); + if (developerMode && certificateChain.isProductive()) { - if (certificateChain.isProductive()) - { - qCritical() << "Using the developer mode is only allowed in a test environment"; - updateStatus(GlobalStatus::Code::Workflow_Preverification_Developermode_Error); - Q_EMIT fireAbort(); - return; - } + qCritical() << "Using the developer mode is only allowed in a test environment"; + updateStatus(GlobalStatus::Code::Workflow_Preverification_Developermode_Error); + Q_EMIT fireAbort(); + return; } - if (!AppSettings::getInstance().getPreVerificationSettings().isEnabled()) + if (!Env::getSingleton()->getPreVerificationSettings().isEnabled()) { qInfo() << "Pre-verification is disabled"; Q_EMIT fireContinue(); @@ -80,10 +78,17 @@ void StatePreVerification::run() } else if (!isValid(certificateChain)) { - qCritical() << "Pre-verification failed: certificate not valid "; - updateStatus(GlobalStatus::Code::Workflow_Preverification_Error); - Q_EMIT fireAbort(); - return; + if (developerMode) + { + qCCritical(developermode) << "Pre-verification failed: certificate not valid"; + } + else + { + qCritical() << "Pre-verification failed: certificate not valid"; + updateStatus(GlobalStatus::Code::Workflow_Preverification_Error); + Q_EMIT fireAbort(); + return; + } } saveCvcaLinkCertificates(certificateChain); diff --git a/src/core/states/StatePreVerification.h b/src/core/states/StatePreVerification.h index 9ee9f8a..595cdb5 100644 --- a/src/core/states/StatePreVerification.h +++ b/src/core/states/StatePreVerification.h @@ -11,7 +11,6 @@ #include -class test_StatePreVerification; namespace governikus { @@ -26,11 +25,11 @@ class StatePreVerification const QVector > mTrustedCvcas; const QDateTime mValidationDateTime; - StatePreVerification(const QSharedPointer& pContext); + explicit StatePreVerification(const QSharedPointer& pContext); virtual void run() override; bool isValid(const QVector >& pCertificates); void saveCvcaLinkCertificates(const QVector >& pCertificates); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/StatePrepareChangePin.cpp b/src/core/states/StatePrepareChangePin.cpp new file mode 100644 index 0000000..055bb5e --- /dev/null +++ b/src/core/states/StatePrepareChangePin.cpp @@ -0,0 +1,29 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "StatePrepareChangePin.h" + +#include + +using namespace governikus; + +Q_DECLARE_LOGGING_CATEGORY(statemachine) + + +StatePrepareChangePin::StatePrepareChangePin(const QSharedPointer& pContext) + : AbstractGenericState(pContext, false) +{ +} + + +void StatePrepareChangePin::run() +{ + if (getContext()->getNewPin().isEmpty()) + { + Q_EMIT fireEnterNewPacePin(); + return; + } + + Q_EMIT fireContinue(); +} diff --git a/src/core/states/StatePrepareChangePin.h b/src/core/states/StatePrepareChangePin.h new file mode 100644 index 0000000..f9ab4c3 --- /dev/null +++ b/src/core/states/StatePrepareChangePin.h @@ -0,0 +1,26 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/ChangePinContext.h" +#include "states/AbstractGenericState.h" + +namespace governikus +{ + +class StatePrepareChangePin + : public AbstractGenericState +{ + Q_OBJECT + friend class StateBuilder; + + explicit StatePrepareChangePin(const QSharedPointer& pContext); + virtual void run() override; + + Q_SIGNALS: + void fireEnterNewPacePin(); +}; + +} // namespace governikus diff --git a/src/core/states/StatePreparePace.cpp b/src/core/states/StatePreparePace.cpp new file mode 100644 index 0000000..6f1c5c3 --- /dev/null +++ b/src/core/states/StatePreparePace.cpp @@ -0,0 +1,117 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "StatePreparePace.h" + +Q_DECLARE_LOGGING_CATEGORY(statemachine) + + +using namespace governikus; + + +StatePreparePace::StatePreparePace(const QSharedPointer& pContext) + : AbstractGenericState(pContext, false) +{ +} + + +void StatePreparePace::run() +{ + const QSharedPointer& cardConnection = getContext()->getCardConnection(); + if (!cardConnection) + { + qCDebug(statemachine) << "Card connection lost."; + Q_EMIT fireAbort(); + return; + } + + if (getContext()->isCanAllowedMode()) + { + qCDebug(statemachine) << "CAN allowed required"; + if (!requestPaceCanIfStillRequired()) + { + Q_ASSERT(false && "This state must not be invoked any more if PACE CAN was successful in CAN allowed mode."); + Q_EMIT fireContinue(); + } + return; + } + + const int currentRetryCounter = cardConnection->getReaderInfo().getRetryCounter(); + Q_ASSERT(currentRetryCounter != -1); + switch (currentRetryCounter) + { + case 0: + { + qCDebug(statemachine) << "PUK required"; + getContext()->setEstablishPaceChannelType(PacePasswordId::PACE_PUK); + + if (getContext()->getPuk().isEmpty()) + { + Q_EMIT fireEnterPacePassword(); + return; + } + + Q_EMIT fireEstablishPaceChannel(); + return; + } + + case 1: + { + qCDebug(statemachine) << "CAN required"; + if (requestPaceCanIfStillRequired()) + { + return; + } + } + // FALLTHROUGH + + default: + { + qCDebug(statemachine) << "PIN allowed"; + getContext()->setEstablishPaceChannelType(PacePasswordId::PACE_PIN); + + const bool pacePinDone = cardConnection->getPacePinSuccessful(); + qCDebug(statemachine) << "PACE PIN done:" << pacePinDone; + if (!pacePinDone) + { + if (getContext()->getPin().isEmpty()) + { + Q_EMIT fireEnterPacePassword(); + return; + } + + Q_EMIT fireEstablishPaceChannel(); + return; + } + + Q_UNREACHABLE(); + } + } + + Q_UNREACHABLE(); + Q_EMIT fireContinue(); +} + + +bool StatePreparePace::requestPaceCanIfStillRequired() +{ + getContext()->setEstablishPaceChannelType(PacePasswordId::PACE_CAN); + + const QSharedPointer& cardConnection = getContext()->getCardConnection(); + const bool paceCanDone = cardConnection->getPaceCanSuccessful(); + qCDebug(statemachine) << "PACE CAN done:" << paceCanDone; + if (!paceCanDone) + { + if (getContext()->getCan().isEmpty()) + { + Q_EMIT fireEnterPacePassword(); + return true; + } + + Q_EMIT fireEstablishPaceChannel(); + return true; + } + + return false; +} diff --git a/src/core/states/StatePreparePace.h b/src/core/states/StatePreparePace.h new file mode 100644 index 0000000..cb1769c --- /dev/null +++ b/src/core/states/StatePreparePace.h @@ -0,0 +1,29 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/WorkflowContext.h" +#include "states/AbstractGenericState.h" + +namespace governikus +{ + +class StatePreparePace + : public AbstractGenericState +{ + Q_OBJECT + friend class StateBuilder; + + explicit StatePreparePace(const QSharedPointer& pContext); + virtual void run() override; + + bool requestPaceCanIfStillRequired(); + + Q_SIGNALS: + void fireEnterPacePassword(); + void fireEstablishPaceChannel(); +}; + +} // namespace governikus diff --git a/src/core/states/StateProcessCertificatesFromEac2.cpp b/src/core/states/StateProcessCertificatesFromEac2.cpp index 492ae33..6e6373d 100644 --- a/src/core/states/StateProcessCertificatesFromEac2.cpp +++ b/src/core/states/StateProcessCertificatesFromEac2.cpp @@ -5,7 +5,6 @@ #include "StateProcessCertificatesFromEac2.h" #include "asn1/CVCertificateChainBuilder.h" -#include "Result.h" #include diff --git a/src/core/states/StateProcessCertificatesFromEac2.h b/src/core/states/StateProcessCertificatesFromEac2.h index 9c15e5b..d5edc64 100644 --- a/src/core/states/StateProcessCertificatesFromEac2.h +++ b/src/core/states/StateProcessCertificatesFromEac2.h @@ -9,7 +9,6 @@ #include "context/AuthContext.h" #include "states/AbstractGenericState.h" -class test_StateProcessCertificatesFromEac2; namespace governikus { @@ -21,8 +20,8 @@ class StateProcessCertificatesFromEac2 friend class StateBuilder; friend class ::test_StateProcessCertificatesFromEac2; - StateProcessCertificatesFromEac2(const QSharedPointer& pContext); + explicit StateProcessCertificatesFromEac2(const QSharedPointer& pContext); virtual void run() override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/StateProcessing.cpp b/src/core/states/StateProcessing.cpp index 3b43584..c35f7e6 100644 --- a/src/core/states/StateProcessing.cpp +++ b/src/core/states/StateProcessing.cpp @@ -21,7 +21,7 @@ void StateProcessing::run() } else { - qCritical() << "Cannot send \"Processing\" to caller: " << activationContext->getSendError(); + qCritical() << "Cannot send \"Processing\" to caller:" << activationContext->getSendError(); updateStatus(GlobalStatus(GlobalStatus::Code::Workflow_Processing_Error, activationContext->getSendError())); Q_EMIT fireAbort(); } diff --git a/src/core/states/StateProcessing.h b/src/core/states/StateProcessing.h index 26245fc..cf5ed66 100644 --- a/src/core/states/StateProcessing.h +++ b/src/core/states/StateProcessing.h @@ -18,8 +18,8 @@ class StateProcessing Q_OBJECT friend class StateBuilder; - StateProcessing(const QSharedPointer& pContext); + explicit StateProcessing(const QSharedPointer& pContext); virtual void run() override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/StateRedirectBrowser.cpp b/src/core/states/StateRedirectBrowser.cpp index 8e28c54..366b9ac 100644 --- a/src/core/states/StateRedirectBrowser.cpp +++ b/src/core/states/StateRedirectBrowser.cpp @@ -24,20 +24,33 @@ void StateRedirectBrowser::run() { if (getContext()->isTcTokenNotFound()) { - sendErrorPage(HttpStatusCode::NOT_FOUND); + sendErrorPage(HTTP_STATUS_NOT_FOUND); } else if (getContext()->getRefreshUrl().isEmpty()) { reportCommunicationError(); } - else if (sendRedirect(getContext()->getRefreshUrl(), getContext()->getStatus())) + else { - Q_EMIT fireContinue(); + bool redirectSuccess; + if (!getContext()->getStartPaosResult().isOk()) + { + redirectSuccess = sendRedirect(getContext()->getRefreshUrl(), getContext()->getStartPaosResult()); + } + else + { + redirectSuccess = sendRedirect(getContext()->getRefreshUrl(), getContext()->getStatus()); + } + + if (redirectSuccess) + { + Q_EMIT fireContinue(); + } } } -void StateRedirectBrowser::sendErrorPage(HttpStatusCode pStatus) +void StateRedirectBrowser::sendErrorPage(http_status pStatus) { auto activationContext = getContext()->getActivationContext(); if (activationContext->sendErrorPage(pStatus, getContext()->getStatus())) @@ -46,7 +59,7 @@ void StateRedirectBrowser::sendErrorPage(HttpStatusCode pStatus) } else { - qCritical() << "Cannot send error page to caller: " << activationContext->getSendError(); + qCritical() << "Cannot send error page to caller:" << activationContext->getSendError(); updateStatus(GlobalStatus(GlobalStatus::Code::Workflow_Error_Page_Transmission_Error, activationContext->getSendError())); Q_EMIT fireAbort(); } @@ -58,24 +71,24 @@ void StateRedirectBrowser::reportCommunicationError() qDebug() << "Report communication error"; if (getContext()->getTcToken() != nullptr && getContext()->getTcToken()->getCommunicationErrorAddress().isValid()) { - if (sendRedirect(getContext()->getTcToken()->getCommunicationErrorAddress(), GlobalStatus::Code::Workflow_Communication_Missing_Redirect_Url)) + if (sendRedirect(getContext()->getTcToken()->getCommunicationErrorAddress(), ECardApiResult(GlobalStatus::Code::Workflow_Communication_Missing_Redirect_Url))) { Q_EMIT fireContinue(); } } else { - sendErrorPage(HttpStatusCode::BAD_REQUEST); + sendErrorPage(HTTP_STATUS_BAD_REQUEST); } } -bool StateRedirectBrowser::sendRedirect(const QUrl& pRedirectAddress, const GlobalStatus& pStatus) +bool StateRedirectBrowser::sendRedirect(const QUrl& pRedirectAddress, const ECardApiResult& pResult) { auto activationContext = getContext()->getActivationContext(); - if (!activationContext->sendRedirect(pRedirectAddress, pStatus)) + if (!activationContext->sendRedirect(pRedirectAddress, GlobalStatus(pResult))) { - qCritical() << "Cannot send redirect to caller: " << activationContext->getSendError(); + qCritical() << "Cannot send redirect to caller:" << activationContext->getSendError(); updateStatus(GlobalStatus(GlobalStatus::Code::Workflow_Redirect_Transmission_Error, activationContext->getSendError())); Q_EMIT fireAbort(); return false; diff --git a/src/core/states/StateRedirectBrowser.h b/src/core/states/StateRedirectBrowser.h index 921d922..c6a7431 100644 --- a/src/core/states/StateRedirectBrowser.h +++ b/src/core/states/StateRedirectBrowser.h @@ -7,10 +7,11 @@ #pragma once #include "context/AuthContext.h" -#include "HttpStatusCode.h" -#include "Result.h" +#include "ECardApiResult.h" #include "states/AbstractGenericState.h" +#include + namespace governikus { @@ -20,13 +21,13 @@ class StateRedirectBrowser Q_OBJECT friend class StateBuilder; - StateRedirectBrowser(const QSharedPointer& pContext); + explicit StateRedirectBrowser(const QSharedPointer& pContext); void reportCommunicationError(); - void sendErrorPage(HttpStatusCode pStatus); - bool sendRedirect(const QUrl& pRedirectAddress, const GlobalStatus& pStatus); + void sendErrorPage(http_status pStatus); + bool sendRedirect(const QUrl& pRedirectAddress, const ECardApiResult& pResult); virtual void run() override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/StateSelectPasswordId.h b/src/core/states/StateSelectPasswordId.h index 275d1ec..0649e2e 100644 --- a/src/core/states/StateSelectPasswordId.h +++ b/src/core/states/StateSelectPasswordId.h @@ -8,6 +8,8 @@ #include "context/WorkflowContext.h" +class test_StateSelectPasswordId; + namespace governikus { @@ -16,12 +18,13 @@ class StateSelectPasswordId { Q_OBJECT friend class StateBuilder; + friend class ::test_StateSelectPasswordId; - StateSelectPasswordId(const QSharedPointer& pContext); + explicit StateSelectPasswordId(const QSharedPointer& pContext); virtual void run() override; Q_SIGNALS: void firePasswordIdCAN(); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/StateSelectReader.cpp b/src/core/states/StateSelectReader.cpp index 999789b..e13a5d2 100644 --- a/src/core/states/StateSelectReader.cpp +++ b/src/core/states/StateSelectReader.cpp @@ -4,6 +4,7 @@ #include "StateSelectReader.h" +#include "AppSettings.h" #include "FuncUtils.h" #include "ReaderManager.h" @@ -14,6 +15,12 @@ Q_DECLARE_LOGGING_CATEGORY(statemachine) using namespace governikus; +StateSelectReader::StateSelectReader(const QSharedPointer& pContext) + : AbstractGenericState(pContext, false) +{ +} + + bool StateSelectReader::requiresCard(ReaderManagerPlugInType pPlugInType) { return pPlugInType == ReaderManagerPlugInType::PCSC || @@ -21,27 +28,24 @@ bool StateSelectReader::requiresCard(ReaderManagerPlugInType pPlugInType) } -StateSelectReader::StateSelectReader(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) -{ -} - - void StateSelectReader::run() { - mConnections += connect(&ReaderManager::getInstance(), &ReaderManager::fireReaderAdded, this, &StateSelectReader::onReaderInfoChanged); - mConnections += connect(&ReaderManager::getInstance(), &ReaderManager::fireReaderRemoved, this, &StateSelectReader::onReaderInfoChanged); - mConnections += connect(&ReaderManager::getInstance(), &ReaderManager::fireCardInserted, this, &StateSelectReader::onReaderInfoChanged); - mConnections += connect(&ReaderManager::getInstance(), &ReaderManager::fireCardRemoved, this, &StateSelectReader::onReaderInfoChanged); - mConnections += connect(&ReaderManager::getInstance(), &ReaderManager::fireReaderDeviceError, this, &StateSelectReader::onReaderDeviceError); + const auto readerManager = Env::getSingleton(); + mConnections += connect(readerManager, &ReaderManager::fireReaderAdded, this, &StateSelectReader::onReaderInfoChanged); + mConnections += connect(readerManager, &ReaderManager::fireReaderRemoved, this, &StateSelectReader::onReaderInfoChanged); + mConnections += connect(readerManager, &ReaderManager::fireCardInserted, this, &StateSelectReader::onReaderInfoChanged); + mConnections += connect(readerManager, &ReaderManager::fireCardRemoved, this, &StateSelectReader::onReaderInfoChanged); + mConnections += connect(readerManager, &ReaderManager::fireReaderDeviceError, this, &StateSelectReader::onReaderDeviceError); onReaderInfoChanged(); - ReaderManager& readerManager = ReaderManager::getInstance(); - const auto& readerPlugInTypes = getContext()->getReaderPlugInTypes(); - for (const auto t : readerPlugInTypes) + if (!Env::getSingleton()->isUsedAsSDK()) { - readerManager.startScan(t); + const auto& readerPlugInTypes = getContext()->getReaderPlugInTypes(); + for (const auto t : readerPlugInTypes) + { + readerManager->startScan(t); + } } } @@ -52,7 +56,7 @@ void StateSelectReader::onReaderInfoChanged() Q_ASSERT(context); const QVector& plugInTypes = context->getReaderPlugInTypes(); - const auto allReaders = ReaderManager::getInstance().getReaderInfos(plugInTypes); + const auto allReaders = Env::getSingleton()->getReaderInfos(plugInTypes); const QVector selectableReaders = filter([](const ReaderInfo& info) { return info.isConnected() && (!requiresCard(info.getPlugInType()) || info.hasEidCard()); @@ -80,26 +84,25 @@ void StateSelectReader::onAbort() const QSharedPointer context = getContext(); Q_ASSERT(context); - ReaderManager::getInstance().stopScanAll(); - const auto& readerName = context->getReaderName(); if (!readerName.isEmpty()) { - const ReaderInfo readerInfo = ReaderManager::getInstance().getReaderInfo(readerName); + const auto readerManager = Env::getSingleton(); + const ReaderInfo readerInfo = readerManager->getReaderInfo(readerName); if (readerInfo.isConnected()) { - ReaderManager::getInstance().disconnectReader(readerName); + readerManager->disconnectReader(readerName); } } Q_EMIT fireRetry(); } -void StateSelectReader::onReaderDeviceError(DeviceError pDeviceError) +void StateSelectReader::onReaderDeviceError(const GlobalStatus pErrorCode) { - if (pDeviceError != DeviceError::DEVICE_POWERED_OFF && pDeviceError != DeviceError::DEVICE_SCAN_ERROR) + if (pErrorCode.isError() && !pErrorCode.is(GlobalStatus::Code::Workflow_Reader_Device_Scan_Error)) { - updateStatus(DeviceErrorUtil::toGlobalStatus(pDeviceError)); + updateStatus(pErrorCode); Q_EMIT fireAbort(); } } diff --git a/src/core/states/StateSelectReader.h b/src/core/states/StateSelectReader.h index 04606ea..31330cd 100644 --- a/src/core/states/StateSelectReader.h +++ b/src/core/states/StateSelectReader.h @@ -6,6 +6,8 @@ #include "AbstractGenericState.h" +class test_StateSelectReader; + namespace governikus { @@ -14,9 +16,10 @@ class StateSelectReader { Q_OBJECT friend class StateBuilder; + friend class ::test_StateSelectReader; private: - StateSelectReader(const QSharedPointer& pContext); + explicit StateSelectReader(const QSharedPointer& pContext); virtual void run() override; static bool requiresCard(ReaderManagerPlugInType pPlugInType); @@ -24,7 +27,7 @@ class StateSelectReader private Q_SLOTS: void onReaderInfoChanged(); void onAbort(); - void onReaderDeviceError(DeviceError pDeviceError); + void onReaderDeviceError(const GlobalStatus pError); public: void onEntry(QEvent* pEvent) override; @@ -33,4 +36,4 @@ class StateSelectReader void fireRetry(); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/StateSendWhitelistSurvey.cpp b/src/core/states/StateSendWhitelistSurvey.cpp new file mode 100644 index 0000000..181b35e --- /dev/null +++ b/src/core/states/StateSendWhitelistSurvey.cpp @@ -0,0 +1,65 @@ +/* + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "StateSendWhitelistSurvey.h" + +#include "AppSettings.h" +#include "ReaderManager.h" +#include "SurveyHandler.h" + +Q_DECLARE_LOGGING_CATEGORY(statemachine) + +using namespace governikus; + + +StateSendWhitelistSurvey::StateSendWhitelistSurvey(const QSharedPointer& pContext) + : AbstractGenericState(pContext, false) +{ +} + + +void StateSendWhitelistSurvey::run() +{ +#if !defined(QT_NO_DEBUG) || defined(Q_OS_ANDROID) + if (Env::getSingleton()->isUsedAsSDK()) + { + qCDebug(statemachine) << "Running as SDK. Ignoring whitelist survey."; + Q_EMIT fireContinue(); + return; + } + + if (!Env::getSingleton()->getGeneralSettings().isDeviceSurveyPending()) + { + qCDebug(statemachine) << "No survey pending."; + Q_EMIT fireContinue(); + return; + } + + Env::getSingleton()->getGeneralSettings().setDeviceSurveyPending(false); + + const auto authContext = getContext(); + if (authContext->getDidAuthenticateEac1() == nullptr || authContext->getEffectiveAccessRights().isEmpty() || + authContext->getStatus().isError()) + { + qWarning() << "Authentication was not completed successfully, cannot send survey to whitelist server."; + Q_ASSERT(false); + Q_EMIT fireAbort(); + return; + } + + const QString& readerName = authContext->getReaderName(); + if (readerName.isEmpty()) + { + qWarning() << "No reader information available, cannot send survey to whitelist server."; + Q_ASSERT(false); + Q_EMIT fireAbort(); + return; + } + + const ReaderInfo& readerInfo = Env::getSingleton()->getReaderInfo(readerName); + SurveyHandler().sendSurvey(readerInfo.getMaxApduLength()); +#endif + + Q_EMIT fireContinue(); +} diff --git a/src/core/states/StateSendWhitelistSurvey.h b/src/core/states/StateSendWhitelistSurvey.h new file mode 100644 index 0000000..16df5f5 --- /dev/null +++ b/src/core/states/StateSendWhitelistSurvey.h @@ -0,0 +1,26 @@ +/* + * \brief Allows the user to send a survey after a successful authentication + * on Android. + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/AuthContext.h" +#include "states/AbstractGenericState.h" + + +namespace governikus +{ +class StateSendWhitelistSurvey + : public AbstractGenericState +{ + Q_OBJECT + friend class StateBuilder; + + explicit StateSendWhitelistSurvey(const QSharedPointer& pContext); + virtual void run() override; +}; + +} // namespace governikus diff --git a/src/core/states/StateShowSelfInfo.cpp b/src/core/states/StateShowSelfInfo.cpp new file mode 100644 index 0000000..32afbaa --- /dev/null +++ b/src/core/states/StateShowSelfInfo.cpp @@ -0,0 +1,19 @@ +/* + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "StateShowSelfInfo.h" + +using namespace governikus; + + +StateShowSelfInfo::StateShowSelfInfo(const QSharedPointer& pContext) + : AbstractGenericState(pContext, false) +{ +} + + +void StateShowSelfInfo::run() +{ + Q_EMIT fireContinue(); +} diff --git a/src/core/states/StateShowSelfInfo.h b/src/core/states/StateShowSelfInfo.h new file mode 100644 index 0000000..22051e6 --- /dev/null +++ b/src/core/states/StateShowSelfInfo.h @@ -0,0 +1,23 @@ +/* + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/AuthContext.h" +#include "states/AbstractGenericState.h" + + +namespace governikus +{ +class StateShowSelfInfo + : public AbstractGenericState +{ + Q_OBJECT + friend class StateBuilder; + + explicit StateShowSelfInfo(const QSharedPointer& pContext); + virtual void run() override; +}; + +} // namespace governikus diff --git a/src/core/states/StateStartPaos.cpp b/src/core/states/StateStartPaos.cpp index 268f8d7..4e0a222 100644 --- a/src/core/states/StateStartPaos.cpp +++ b/src/core/states/StateStartPaos.cpp @@ -19,7 +19,7 @@ void StateStartPaos::run() Q_ASSERT(getContext()->getTcToken()); auto sessionId = getContext()->getTcToken()->getSessionIdentifier(); - getContext()->setStartPaos(QSharedPointer(new StartPaos(sessionId))); + getContext()->setStartPaos(QSharedPointer::create(sessionId)); Q_EMIT fireContinue(); } diff --git a/src/core/states/StateStartPaos.h b/src/core/states/StateStartPaos.h index 73c4b95..a9b1cb1 100644 --- a/src/core/states/StateStartPaos.h +++ b/src/core/states/StateStartPaos.h @@ -18,8 +18,8 @@ class StateStartPaos Q_OBJECT friend class StateBuilder; - StateStartPaos(const QSharedPointer& pContext); + explicit StateStartPaos(const QSharedPointer& pContext); virtual void run() override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/StateStartPaosResponse.cpp b/src/core/states/StateStartPaosResponse.cpp index fb05d46..e24e3ce 100644 --- a/src/core/states/StateStartPaosResponse.cpp +++ b/src/core/states/StateStartPaosResponse.cpp @@ -17,15 +17,14 @@ void StateStartPaosResponse::run() const QSharedPointer& startPaosResponse = getContext()->getStartPaosResponse(); Q_ASSERT(startPaosResponse); - const Result& result = startPaosResponse->getResult(); + const ECardApiResult& result = startPaosResponse->getResult(); if (result.isOk()) { Q_EMIT fireContinue(); return; } - // we override our result with the one sent from server qDebug() << "Processing server result:" << result.getMajorString() << result.getMinorString() << result.getMessage(); - getContext()->setStatus(result.toStatus(), false); + updateStartPaosResult(result); Q_EMIT fireAbort(); } diff --git a/src/core/states/StateStartPaosResponse.h b/src/core/states/StateStartPaosResponse.h index 90edeb4..75e3804 100644 --- a/src/core/states/StateStartPaosResponse.h +++ b/src/core/states/StateStartPaosResponse.h @@ -18,9 +18,9 @@ class StateStartPaosResponse Q_OBJECT friend class StateBuilder; - StateStartPaosResponse(const QSharedPointer& pContext); + explicit StateStartPaosResponse(const QSharedPointer& pContext); virtual void run() override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/StateTransmit.cpp b/src/core/states/StateTransmit.cpp index b517b65..35311ec 100644 --- a/src/core/states/StateTransmit.cpp +++ b/src/core/states/StateTransmit.cpp @@ -41,7 +41,6 @@ void StateTransmit::onCardCommandDone(QSharedPointer pCommand) QSharedPointer response(getContext()->getTransmitResponses().last()); response->setOutputApdus(transmitCommand->getOutputApduAsHex()); updateStatus(CardReturnCodeUtil::toGlobalStatus(returnCode)); // set the result to the model so it is written to the PAOS response - getContext()->setTransmitResponseFailed(true); // Return the correct return code according to the last minute test spec update. Q_EMIT fireContinue(); } else diff --git a/src/core/states/StateTransmit.h b/src/core/states/StateTransmit.h index 3dbdd07..d7f2095 100644 --- a/src/core/states/StateTransmit.h +++ b/src/core/states/StateTransmit.h @@ -10,6 +10,8 @@ #include "context/AuthContext.h" #include "states/AbstractGenericState.h" +class test_StateTransmit; + namespace governikus { @@ -19,11 +21,12 @@ class StateTransmit Q_OBJECT friend class StateBuilder; - StateTransmit(const QSharedPointer& pContext); + explicit StateTransmit(const QSharedPointer& pContext); virtual void run() override; private Q_SLOTS: + friend class ::test_StateTransmit; void onCardCommandDone(QSharedPointer pCommand); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/StateUnfortunateCardPosition.cpp b/src/core/states/StateUnfortunateCardPosition.cpp new file mode 100644 index 0000000..3c753e4 --- /dev/null +++ b/src/core/states/StateUnfortunateCardPosition.cpp @@ -0,0 +1,21 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "StateUnfortunateCardPosition.h" + + +using namespace governikus; + + +StateUnfortunateCardPosition::StateUnfortunateCardPosition(const QSharedPointer& pContext) + : AbstractGenericState(pContext, false) +{ +} + + +void StateUnfortunateCardPosition::run() +{ + getContext()->setNpaPositionVerified(); + Q_EMIT fireContinue(); +} diff --git a/src/core/states/StateUnfortunateCardPosition.h b/src/core/states/StateUnfortunateCardPosition.h new file mode 100644 index 0000000..36f7237 --- /dev/null +++ b/src/core/states/StateUnfortunateCardPosition.h @@ -0,0 +1,23 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/WorkflowContext.h" +#include "states/AbstractGenericState.h" + +namespace governikus +{ + +class StateUnfortunateCardPosition + : public AbstractGenericState +{ + Q_OBJECT + friend class StateBuilder; + + explicit StateUnfortunateCardPosition(const QSharedPointer& pContext); + virtual void run() override; +}; + +} // namespace governikus diff --git a/src/core/states/StateUpdateRetryCounter.cpp b/src/core/states/StateUpdateRetryCounter.cpp index c2b4d9c..70839a9 100644 --- a/src/core/states/StateUpdateRetryCounter.cpp +++ b/src/core/states/StateUpdateRetryCounter.cpp @@ -5,46 +5,48 @@ #include "StateUpdateRetryCounter.h" #include "CardConnection.h" -#include "Result.h" +#include + +Q_DECLARE_LOGGING_CATEGORY(statemachine) using namespace governikus; StateUpdateRetryCounter::StateUpdateRetryCounter(const QSharedPointer& pContext) - : AbstractGenericState(pContext) + : AbstractGenericState(pContext, false) { } void StateUpdateRetryCounter::run() { - qDebug() << "StateUpdateRetryCounter::run()"; - auto cardConnection = getContext()->getCardConnection(); - if (cardConnection != nullptr) + if (!cardConnection) { - mConnections += cardConnection->callUpdateRetryCounterCommand(this, &StateUpdateRetryCounter::onUpdateRetryCounterDone); - } - else - { - qDebug() << "Skipping update because there is no card connection"; - Q_EMIT fireContinue(); + qCDebug(statemachine) << "No card connection available."; + Q_EMIT fireAbort(); + return; } + + Q_ASSERT(cardConnection != nullptr); + mConnections += cardConnection->callUpdateRetryCounterCommand(this, &StateUpdateRetryCounter::onUpdateRetryCounterDone); } void StateUpdateRetryCounter::onUpdateRetryCounterDone(QSharedPointer pCommand) { - qDebug() << "StateUpdateRetryCounter::onUpdateRetryCounterDone()"; + qDebug(statemachine) << "StateUpdateRetryCounter::onUpdateRetryCounterDone()"; if (pCommand->getReturnCode() != CardReturnCode::OK) { - qCritical() << "An error occurred while communicating with the card reader, cannot determine retry counter, abort state"; - updateStatus(CardReturnCodeUtil::toGlobalStatus(pCommand->getReturnCode())); + qCritical(statemachine) << "An error occurred while communicating with the card reader, cannot determine retry counter, abort state"; + getContext()->resetCardConnection(); Q_EMIT fireAbort(); return; } + Q_ASSERT(getContext()->getCardConnection()->getReaderInfo().getRetryCounter() != -1 && "Retry counter must be intialized if command has succeeded."); + Q_EMIT fireContinue(); } diff --git a/src/core/states/StateUpdateRetryCounter.h b/src/core/states/StateUpdateRetryCounter.h index 8ffdc38..9786259 100644 --- a/src/core/states/StateUpdateRetryCounter.h +++ b/src/core/states/StateUpdateRetryCounter.h @@ -11,6 +11,8 @@ #include "context/ChangePinContext.h" +class test_StateUpdateRetryCounter; + namespace governikus { @@ -19,12 +21,13 @@ class StateUpdateRetryCounter { Q_OBJECT friend class StateBuilder; + friend class ::test_StateUpdateRetryCounter; - StateUpdateRetryCounter(const QSharedPointer& pContext); + explicit StateUpdateRetryCounter(const QSharedPointer& pContext); virtual void run() override; private Q_SLOTS: void onUpdateRetryCounterDone(QSharedPointer pCommand); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/StateVerifyRetryCounter.cpp b/src/core/states/StateVerifyRetryCounter.cpp new file mode 100644 index 0000000..a040578 --- /dev/null +++ b/src/core/states/StateVerifyRetryCounter.cpp @@ -0,0 +1,50 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "StateVerifyRetryCounter.h" + +Q_DECLARE_LOGGING_CATEGORY(statemachine) + + +using namespace governikus; + + +StateVerifyRetryCounter::StateVerifyRetryCounter(const QSharedPointer& pContext) + : AbstractGenericState(pContext, false) +{ +} + + +void StateVerifyRetryCounter::run() +{ + const QSharedPointer& cardConnection = getContext()->getCardConnection(); + if (!cardConnection) + { + qCDebug(statemachine) << "Card connection lost."; + Q_EMIT fireAbort(); + return; + } + + const int currentRetryCounter = cardConnection->getReaderInfo().getRetryCounter(); + qCDebug(statemachine) << "Retry counter | actual:" << currentRetryCounter << "/ expected:" << getContext()->getExpectedRetryCounter(); + + if (!getContext()->isExpectedReader() || getContext()->getExpectedRetryCounter() != currentRetryCounter) + { + qCDebug(statemachine) << "The reader changed or the connected card has an unexpected retry counter. Clearing PACE passwords."; + getContext()->resetPacePasswords(); + getContext()->rememberReader(); + getContext()->setExpectedRetryCounter(currentRetryCounter); + } + + if (getContext()->getExpectedRetryCounter() == -1) + { + Q_ASSERT(currentRetryCounter != -1); + + qCDebug(statemachine) << "Remembering the selected reader and initializing the expected retry counter:" << currentRetryCounter; + getContext()->rememberReader(); + getContext()->setExpectedRetryCounter(currentRetryCounter); + } + + Q_EMIT fireContinue(); +} diff --git a/src/core/states/StateVerifyRetryCounter.h b/src/core/states/StateVerifyRetryCounter.h new file mode 100644 index 0000000..8be7cb1 --- /dev/null +++ b/src/core/states/StateVerifyRetryCounter.h @@ -0,0 +1,27 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/WorkflowContext.h" +#include "states/AbstractGenericState.h" + +namespace governikus +{ + +class StateVerifyRetryCounter + : public AbstractGenericState +{ + Q_OBJECT + friend class StateBuilder; + + explicit StateVerifyRetryCounter(const QSharedPointer& pContext); + virtual void run() override; + + Q_SIGNALS: + void fireEnterPacePassword(); + void fireEstablishPaceChannel(); +}; + +} // namespace governikus diff --git a/src/core/states/StateWriteHistory.cpp b/src/core/states/StateWriteHistory.cpp index 01f1e3c..eb504e8 100644 --- a/src/core/states/StateWriteHistory.cpp +++ b/src/core/states/StateWriteHistory.cpp @@ -2,9 +2,12 @@ * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany */ -#include "AppSettings.h" #include "StateWriteHistory.h" +#include "asn1/AccessRoleAndRight.h" + +#include "AppSettings.h" + using namespace governikus; @@ -16,7 +19,7 @@ StateWriteHistory::StateWriteHistory(const QSharedPointer& pCon void StateWriteHistory::run() { - if (!AppSettings::getInstance().getHistorySettings().isEnabled()) + if (!Env::getSingleton()->getHistorySettings().isEnabled()) { qDebug() << "History disabled"; Q_EMIT fireContinue(); @@ -44,17 +47,22 @@ void StateWriteHistory::run() QString validity = tr("Validity:\n%1 - %2").arg(effectiveDate, expirationDate); QStringList requestedData; - const auto& rights = getContext()->getEffectiveAccessRights(); - for (const auto& entry : rights) + QList rights = getContext()->getEffectiveAccessRights().toList(); + std::sort(rights.begin(), rights.end()); + for (const auto& entry : qAsConst(rights)) { - requestedData += AccessRoleAndRightsUtil::toDisplayText(entry); + const auto data = AccessRoleAndRightsUtil::toTechnicalName(entry); + if (!data.isEmpty()) + { + requestedData += data; + } } if (!subjectName.isNull() && !termOfUsage.isNull()) { - HistoryInfo info(subjectName, subjectUrl, certDesc->getPurpose(), QDateTime::currentDateTime(), termOfUsage + QStringLiteral("\n\n") + validity, requestedData.join(QStringLiteral(", "))); - AppSettings::getInstance().getHistorySettings().addHistoryInfo(info); - AppSettings::getInstance().getHistorySettings().save(); + HistoryInfo info(subjectName, subjectUrl, certDesc->getPurpose(), QDateTime::currentDateTime(), termOfUsage + QStringLiteral("\n\n") + validity, requestedData); + Env::getSingleton()->getHistorySettings().addHistoryInfo(info); + Env::getSingleton()->getHistorySettings().save(); } } } diff --git a/src/core/states/StateWriteHistory.h b/src/core/states/StateWriteHistory.h index ad34663..c4b2b85 100644 --- a/src/core/states/StateWriteHistory.h +++ b/src/core/states/StateWriteHistory.h @@ -20,8 +20,8 @@ class StateWriteHistory Q_OBJECT friend class StateBuilder; - StateWriteHistory(const QSharedPointer& pContext); + explicit StateWriteHistory(const QSharedPointer& pContext); virtual void run() override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/remote_service/StateChangePinRemote.cpp b/src/core/states/remote_service/StateChangePinRemote.cpp index eaa7ed1..f29b5fc 100644 --- a/src/core/states/remote_service/StateChangePinRemote.cpp +++ b/src/core/states/remote_service/StateChangePinRemote.cpp @@ -16,100 +16,50 @@ using namespace governikus; StateChangePinRemote::StateChangePinRemote(const QSharedPointer& pContext) - : AbstractGenericState(pContext) - , mModifyPinMessage() - , mMessageHandler() + : AbstractGenericState(pContext, false) { } -StateChangePinRemote::~StateChangePinRemote() -{ -} - - -void StateChangePinRemote::onEntry(QEvent* pEvent) -{ - AbstractGenericState::onEntry(pEvent); - - const QSharedPointer& context = getContext(); - - mModifyPinMessage = context->getModifyPinMessage(); - Q_ASSERT(mModifyPinMessage); - - const QSharedPointer remoteServer = context->getRemoteServer(); - Q_ASSERT(remoteServer); - mMessageHandler = remoteServer->getMessageHandler(); - Q_ASSERT(mMessageHandler); - - auto cardConnection = context->getCardConnection(); - if (cardConnection.isNull()) - { - Q_EMIT fireContinue(); - return; - } - - mConnections += connect(context.data(), &RemoteServiceContext::fireCancelPasswordRequest, this, &StateChangePinRemote::onCancelChangePin); - mConnections += connect(cardConnection.data(), &CardConnection::fireReaderInfoChanged, this, &StateChangePinRemote::onReaderInfoChanged); - mConnections += connect(remoteServer.data(), &RemoteServer::fireConnectedChanged, this, &AbstractState::fireContinue); -} - - -void StateChangePinRemote::onExit(QEvent* pEvent) -{ - AbstractGenericState::onExit(pEvent); - - mMessageHandler.reset(); -} - - void StateChangePinRemote::run() { - const QSharedPointer& context = getContext(); + Q_ASSERT(getContext()); + Q_ASSERT(getContext()->getModifyPinMessage()); - PinModify pinModify(mModifyPinMessage->getInputData()); - const QString newPin = context->getNewPin(); - context->setPin(QString()); + const QSharedPointer& context = getContext(); + auto cardConnection = context->getCardConnection(); + if (cardConnection.isNull()) + { + context->setModifyPinMessageResponseApdu(ResponseApdu(StatusCode::EMPTY)); + Q_EMIT fireContinue(); + return; + } + + QSharedPointer modifyPinMessage = context->getModifyPinMessage(); + + PinModify pinModify(modifyPinMessage->getInputData()); const quint8 timeoutSeconds = pinModify.getTimeoutSeconds(); - auto cardConnection = context->getCardConnection(); Q_ASSERT(cardConnection); mConnections += cardConnection->callSetEidPinCommand(this, &StateChangePinRemote::onChangePinDone, - newPin, + context->getNewPin(), timeoutSeconds); } -void StateChangePinRemote::onCancelChangePin() -{ - mMessageHandler->sendModifyPinResponse(mModifyPinMessage->getSlotHandle(), ResponseApdu(StatusCode::INPUT_CANCELLED)); - Q_EMIT fireContinue(); -} - - -void StateChangePinRemote::onReaderInfoChanged(const ReaderInfo& pReaderInfo) -{ - if (!pReaderInfo.hasEidCard()) - { - mMessageHandler->sendModifyPinResponse(mModifyPinMessage->getSlotHandle(), ResponseApdu(StatusCode::EMPTY)); - Q_EMIT fireContinue(); - } -} - - void StateChangePinRemote::onChangePinDone(QSharedPointer pCommand) { - const QSharedPointer command = pCommand.dynamicCast(); + const QSharedPointer command = pCommand.objectCast(); if (command) { - mMessageHandler->sendModifyPinResponse(mModifyPinMessage->getSlotHandle(), command->getResponseApdu()); + getContext()->setModifyPinMessageResponseApdu(ResponseApdu(command->getResponseApdu())); } else { Q_ASSERT(false); qCDebug(remote_device) << "Expected a SetEidPinCommand as response!"; - mMessageHandler->sendModifyPinResponse(mModifyPinMessage->getSlotHandle(), ResponseApdu()); + getContext()->setModifyPinMessageResponseApdu(ResponseApdu()); } Q_EMIT fireContinue(); diff --git a/src/core/states/remote_service/StateChangePinRemote.h b/src/core/states/remote_service/StateChangePinRemote.h index a280287..7b4b32d 100644 --- a/src/core/states/remote_service/StateChangePinRemote.h +++ b/src/core/states/remote_service/StateChangePinRemote.h @@ -10,36 +10,24 @@ #include "context/RemoteServiceContext.h" #include "states/AbstractGenericState.h" +class test_StateChangePinRemote; + namespace governikus { - class StateChangePinRemote : public AbstractGenericState { Q_OBJECT friend class StateBuilder; + friend class ::test_StateChangePinRemote; private: - QSharedPointer mModifyPinMessage; - QSharedPointer mMessageHandler; - - StateChangePinRemote(const QSharedPointer& pContext); + explicit StateChangePinRemote(const QSharedPointer& pContext); virtual void run() override; private Q_SLOTS: - void onCancelChangePin(); - void onReaderInfoChanged(const ReaderInfo& pReaderInfo); void onChangePinDone(QSharedPointer pCommand); - - protected: - void onExit(QEvent* pEvent) override; - - public: - virtual ~StateChangePinRemote() override; - - void onEntry(QEvent* pEvent) override; - }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/remote_service/StateChangePinResponse.cpp b/src/core/states/remote_service/StateChangePinResponse.cpp new file mode 100644 index 0000000..0bd0092 --- /dev/null +++ b/src/core/states/remote_service/StateChangePinResponse.cpp @@ -0,0 +1,41 @@ +/*! + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "StateChangePinResponse.h" + +#include "PinModify.h" +#include "ServerMessageHandler.h" + +using namespace governikus; + + +StateChangePinResponse::StateChangePinResponse(const QSharedPointer& pContext) + : AbstractGenericState(pContext, false) +{ +} + + +void StateChangePinResponse::run() +{ + Q_ASSERT(getContext()->getRemoteServer()); + Q_ASSERT(getContext()->getModifyPinMessage()); + + const QSharedPointer& context = getContext(); + const auto& responseApdu = context->getModifyPinMessageResponseApdu(); + if (context->getRemoteServer() && context->getRemoteServer()->getMessageHandler()) + { + context->getRemoteServer()->getMessageHandler()->sendModifyPinResponse( + context->getModifyPinMessage()->getSlotHandle(), + responseApdu + ); + } + + if (responseApdu.getCardReturnCode() == CardReturnCode::OK) + { + Q_EMIT firePacePasswordModified(); + return; + } + + Q_EMIT fireContinue(); +} diff --git a/src/core/states/remote_service/StateChangePinResponse.h b/src/core/states/remote_service/StateChangePinResponse.h new file mode 100644 index 0000000..f97633a --- /dev/null +++ b/src/core/states/remote_service/StateChangePinResponse.h @@ -0,0 +1,30 @@ +/*! + * \brief This state executes the remote message IfdModifyPin. + * + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + + +#include "context/RemoteServiceContext.h" +#include "states/AbstractGenericState.h" + +namespace governikus +{ + +class StateChangePinResponse + : public AbstractGenericState +{ + Q_OBJECT + friend class StateBuilder; + + private: + explicit StateChangePinResponse(const QSharedPointer& pContext); + virtual void run() override; + + Q_SIGNALS: + void firePacePasswordModified(); +}; + +} // namespace governikus diff --git a/src/core/states/remote_service/StateEnterNewPacePinRemote.cpp b/src/core/states/remote_service/StateEnterNewPacePinRemote.cpp new file mode 100644 index 0000000..28a71b5 --- /dev/null +++ b/src/core/states/remote_service/StateEnterNewPacePinRemote.cpp @@ -0,0 +1,43 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "StateEnterNewPacePinRemote.h" + + +using namespace governikus; + + +StateEnterNewPacePinRemote::StateEnterNewPacePinRemote(const QSharedPointer& pContext) + : AbstractGenericState(pContext, false) +{ +} + + +void StateEnterNewPacePinRemote::run() +{ + Q_EMIT fireContinue(); +} + + +void StateEnterNewPacePinRemote::onCancelChangePin() +{ + if (getContext() && getContext()->getRemoteServer() && getContext()->getRemoteServer()->getMessageHandler()) + { + getContext()->setModifyPinMessageResponseApdu(ResponseApdu(StatusCode::INPUT_CANCELLED)); + } + Q_EMIT fireAbort(); +} + + +void StateEnterNewPacePinRemote::onEntry(QEvent* pEvent) +{ + if (getContext() && getContext()->getRemoteServer() && getContext()->getRemoteServer()->getMessageHandler()) + { + const auto& handler = getContext()->getRemoteServer()->getMessageHandler(); + mConnections += connect(handler.data(), &ServerMessageHandler::destroyed, this, &StateEnterNewPacePinRemote::onCancelChangePin); + } + + mConnections += connect(getContext().data(), &RemoteServiceContext::fireCancelPasswordRequest, this, &StateEnterNewPacePinRemote::onCancelChangePin); + AbstractGenericState::onEntry(pEvent); +} diff --git a/src/core/states/remote_service/StateEnterNewPacePinRemote.h b/src/core/states/remote_service/StateEnterNewPacePinRemote.h new file mode 100644 index 0000000..ee4babb --- /dev/null +++ b/src/core/states/remote_service/StateEnterNewPacePinRemote.h @@ -0,0 +1,30 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/RemoteServiceContext.h" +#include "states/AbstractGenericState.h" + +namespace governikus +{ + +class StateEnterNewPacePinRemote + : public AbstractGenericState +{ + Q_OBJECT + friend class StateBuilder; + + private: + explicit StateEnterNewPacePinRemote(const QSharedPointer& pContext); + virtual void run() override; + + private Q_SLOTS: + void onCancelChangePin(); + + public: + void onEntry(QEvent* pEvent) override; +}; + +} // namespace governikus diff --git a/src/core/states/remote_service/StateEnterPacePasswordRemote.cpp b/src/core/states/remote_service/StateEnterPacePasswordRemote.cpp new file mode 100644 index 0000000..db3714e --- /dev/null +++ b/src/core/states/remote_service/StateEnterPacePasswordRemote.cpp @@ -0,0 +1,46 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "StateEnterPacePasswordRemote.h" + + +using namespace governikus; + + +StateEnterPacePasswordRemote::StateEnterPacePasswordRemote(const QSharedPointer& pContext) + : AbstractGenericState(pContext, false) +{ +} + + +void StateEnterPacePasswordRemote::run() +{ + Q_EMIT fireContinue(); +} + + +void StateEnterPacePasswordRemote::onCancelEstablishPaceChannel() +{ + if (getContext() && getContext()->getRemoteServer() && getContext()->getRemoteServer()->getMessageHandler()) + { + EstablishPaceChannelOutput channelOutput = EstablishPaceChannelOutput(); + channelOutput.setPaceReturnCode(CardReturnCode::CANCELLATION_BY_USER); + getContext()->setEstablishPaceChannelOutput(channelOutput); + } + + Q_EMIT fireAbort(); +} + + +void StateEnterPacePasswordRemote::onEntry(QEvent* pEvent) +{ + if (getContext() && getContext()->getRemoteServer() && getContext()->getRemoteServer()->getMessageHandler()) + { + const auto& handler = getContext()->getRemoteServer()->getMessageHandler(); + mConnections += connect(handler.data(), &ServerMessageHandler::destroyed, this, &StateEnterPacePasswordRemote::onCancelEstablishPaceChannel); + } + + mConnections += connect(getContext().data(), &RemoteServiceContext::fireCancelPasswordRequest, this, &StateEnterPacePasswordRemote::onCancelEstablishPaceChannel); + AbstractGenericState::onEntry(pEvent); +} diff --git a/src/core/states/remote_service/StateEnterPacePasswordRemote.h b/src/core/states/remote_service/StateEnterPacePasswordRemote.h new file mode 100644 index 0000000..7dd3839 --- /dev/null +++ b/src/core/states/remote_service/StateEnterPacePasswordRemote.h @@ -0,0 +1,30 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/RemoteServiceContext.h" +#include "states/AbstractGenericState.h" + +namespace governikus +{ + +class StateEnterPacePasswordRemote + : public AbstractGenericState +{ + Q_OBJECT + friend class StateBuilder; + + private: + explicit StateEnterPacePasswordRemote(const QSharedPointer& pContext); + virtual void run() override; + + private Q_SLOTS: + void onCancelEstablishPaceChannel(); + + public: + void onEntry(QEvent* pEvent) override; +}; + +} // namespace governikus diff --git a/src/core/states/remote_service/StateEstablishPaceChannel.cpp b/src/core/states/remote_service/StateEstablishPaceChannel.cpp deleted file mode 100644 index 8b1266a..0000000 --- a/src/core/states/remote_service/StateEstablishPaceChannel.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "StateEstablishPaceChannel.h" - -#include "EstablishPACEChannelParser.h" -#include "ServerMessageHandler.h" - -#include - -Q_DECLARE_LOGGING_CATEGORY(remote_device) - - -using namespace governikus; - - -StateEstablishPaceChannel::StateEstablishPaceChannel(const QSharedPointer& pContext) - : AbstractGenericState(pContext) -{ -} - - -StateEstablishPaceChannel::~StateEstablishPaceChannel() -{ -} - - -void StateEstablishPaceChannel::onEntry(QEvent* pEvent) -{ - AbstractGenericState::onEntry(pEvent); - - const QSharedPointer& context = getContext(); - - mEstablishPaceChannelMessage = context->getEstablishPaceChannelMessage(); - Q_ASSERT(mEstablishPaceChannelMessage); - - const QSharedPointer remoteServer = context->getRemoteServer(); - Q_ASSERT(remoteServer); - mMessageHandler = remoteServer->getMessageHandler(); - Q_ASSERT(mMessageHandler); - - auto cardConnection = context->getCardConnection(); - if (!cardConnection) - { - Q_EMIT fireContinue(); - return; - } - - mConnections += connect(cardConnection.data(), &CardConnection::fireReaderInfoChanged, this, &StateEstablishPaceChannel::onReaderInfoChanged); - mConnections += connect(getContext().data(), &RemoteServiceContext::fireCancelPasswordRequest, this, &StateEstablishPaceChannel::onCancelEstablishPaceChannel); - mConnections += connect(getContext()->getRemoteServer().data(), &RemoteServer::fireConnectedChanged, this, &AbstractState::fireContinue); -} - - -void StateEstablishPaceChannel::onExit(QEvent* pEvent) -{ - AbstractGenericState::onExit(pEvent); - - mMessageHandler.reset(); -} - - -void StateEstablishPaceChannel::run() -{ - const QSharedPointer& context = getContext(); - - EstablishPACEChannelParser parser = EstablishPACEChannelParser::fromCcid(mEstablishPaceChannelMessage->getInputData()); - QString pacePassword; - switch (parser.getPasswordId()) - { - case PACE_PASSWORD_ID::PACE_CAN: - pacePassword = context->getCan(); - context->setCan(QString()); - break; - - case PACE_PASSWORD_ID::PACE_PIN: - pacePassword = context->getPin(); - context->setPin(QString()); - break; - - case PACE_PASSWORD_ID::PACE_PUK: - pacePassword = context->getPuk(); - context->setPuk(QString()); - break; - - default: - mMessageHandler->sendEstablishPaceChannelResponse(mEstablishPaceChannelMessage->getSlotHandle(), EstablishPACEChannelOutput()); - return; - } - - auto cardConnection = context->getCardConnection(); - Q_ASSERT(cardConnection); - mConnections += cardConnection->callEstablishPaceChannelCommand(this, - &StateEstablishPaceChannel::onEstablishConnectionDone, - parser.getPasswordId(), - pacePassword, - parser.getChat(), - parser.getCertificateDescription()); -} - - -void StateEstablishPaceChannel::onCancelEstablishPaceChannel() -{ - EstablishPACEChannelOutput channelOutput = EstablishPACEChannelOutput(); - channelOutput.setPaceReturnCode(CardReturnCode::CANCELLATION_BY_USER); - mMessageHandler->sendEstablishPaceChannelResponse(mEstablishPaceChannelMessage->getSlotHandle(), channelOutput); - - Q_EMIT fireContinue(); -} - - -void StateEstablishPaceChannel::onReaderInfoChanged(const ReaderInfo& pReaderInfo) -{ - if (!pReaderInfo.hasEidCard()) - { - EstablishPACEChannelOutput channelOutput = EstablishPACEChannelOutput(); - channelOutput.setPaceReturnCode(CardReturnCode::CARD_NOT_FOUND); - mMessageHandler->sendEstablishPaceChannelResponse(mEstablishPaceChannelMessage->getSlotHandle(), channelOutput); - - Q_EMIT fireContinue(); - } -} - - -void StateEstablishPaceChannel::onEstablishConnectionDone(QSharedPointer pCommand) -{ - const QSharedPointer establishPaceChannelCommand = pCommand.dynamicCast(); - if (establishPaceChannelCommand) - { - mMessageHandler->sendEstablishPaceChannelResponse(mEstablishPaceChannelMessage->getSlotHandle(), establishPaceChannelCommand->getPaceOutput()); - } - else - { - Q_ASSERT(false); - qCDebug(remote_device) << "Expected an EstablishPaceChannelCommand as response!"; - mMessageHandler->sendEstablishPaceChannelResponse(mEstablishPaceChannelMessage->getSlotHandle(), EstablishPACEChannelOutput()); - } - - Q_EMIT fireContinue(); -} diff --git a/src/core/states/remote_service/StateEstablishPaceChannel.h b/src/core/states/remote_service/StateEstablishPaceChannel.h deleted file mode 100644 index 08f3854..0000000 --- a/src/core/states/remote_service/StateEstablishPaceChannel.h +++ /dev/null @@ -1,45 +0,0 @@ -/*! - * \brief This state executes the remote message PACE channel establish. - * - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - - -#include "context/RemoteServiceContext.h" -#include "states/AbstractGenericState.h" - -namespace governikus -{ - - -class StateEstablishPaceChannel - : public AbstractGenericState -{ - Q_OBJECT - friend class StateBuilder; - - private: - QSharedPointer mEstablishPaceChannelMessage; - QSharedPointer mMessageHandler; - - StateEstablishPaceChannel(const QSharedPointer& pContext); - virtual void run() override; - - private Q_SLOTS: - void onCancelEstablishPaceChannel(); - void onReaderInfoChanged(const ReaderInfo& pReaderInfo); - void onEstablishConnectionDone(QSharedPointer pCommand); - - protected: - void onExit(QEvent* pEvent) override; - - public: - virtual ~StateEstablishPaceChannel() override; - - void onEntry(QEvent* pEvent) override; - -}; - -} /* namespace governikus */ diff --git a/src/core/states/remote_service/StateEstablishPaceChannelRemote.cpp b/src/core/states/remote_service/StateEstablishPaceChannelRemote.cpp new file mode 100644 index 0000000..8e8130b --- /dev/null +++ b/src/core/states/remote_service/StateEstablishPaceChannelRemote.cpp @@ -0,0 +1,145 @@ +/*! + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "StateEstablishPaceChannelRemote.h" + +#include "EstablishPaceChannelParser.h" +#include "ServerMessageHandler.h" + +#include + +Q_DECLARE_LOGGING_CATEGORY(statemachine) + + +using namespace governikus; + + +StateEstablishPaceChannelRemote::StateEstablishPaceChannelRemote(const QSharedPointer& pContext) + : AbstractGenericState(pContext, false) +{ +} + + +void StateEstablishPaceChannelRemote::run() +{ + Q_ASSERT(getContext()); + Q_ASSERT(getContext()->getEstablishPaceChannelMessage()); + + const QSharedPointer& context = getContext(); + auto cardConnection = context->getCardConnection(); + if (!cardConnection) + { + Q_EMIT fireContinue(); + return; + } + + QSharedPointer establishPaceChannelMessage = context->getEstablishPaceChannelMessage(); + + EstablishPaceChannelParser parser = EstablishPaceChannelParser::fromCcid(establishPaceChannelMessage->getInputData()); + mPasswordId = parser.getPasswordId(); + QString pacePassword; + switch (mPasswordId) + { + case PacePasswordId::PACE_CAN: + pacePassword = context->getCan(); + break; + + case PacePasswordId::PACE_PIN: + pacePassword = context->getPin(); + break; + + case PacePasswordId::PACE_PUK: + pacePassword = context->getPuk(); + break; + + default: + Q_EMIT fireAbort(); + return; + } + + qDebug() << "Establish connection using" << mPasswordId; + Q_ASSERT(!pacePassword.isEmpty() && cardConnection); + if (mPasswordId == PacePasswordId::PACE_PUK) + { + mConnections += cardConnection->callUnblockPinCommand(this, + &StateEstablishPaceChannelRemote::onEstablishConnectionDone, + pacePassword); + } + else + { + mConnections += cardConnection->callEstablishPaceChannelCommand(this, + &StateEstablishPaceChannelRemote::onEstablishConnectionDone, + mPasswordId, + pacePassword, + parser.getChat(), + parser.getCertificateDescription()); + } +} + + +void StateEstablishPaceChannelRemote::onReaderInfoChanged(const ReaderInfo& pReaderInfo) +{ + if (!pReaderInfo.hasEidCard()) + { + EstablishPaceChannelOutput channelOutput = EstablishPaceChannelOutput(); + channelOutput.setPaceReturnCode(CardReturnCode::CARD_NOT_FOUND); + getContext()->setEstablishPaceChannelOutput(channelOutput); + + Q_EMIT fireContinue(); + } +} + + +void StateEstablishPaceChannelRemote::onEstablishConnectionDone(QSharedPointer pCommand) +{ + const QSharedPointer establishPaceChannelCommand = pCommand.objectCast(); + const QSharedPointer unblockPinCommand = pCommand.objectCast(); + + if (establishPaceChannelCommand) + { + getContext()->setEstablishPaceChannelOutput(establishPaceChannelCommand->getPaceOutput()); + + if (mPasswordId == PacePasswordId::PACE_PIN) + { + const CardReturnCode paceReturnCode = establishPaceChannelCommand->getPaceOutput().getPaceReturnCode(); + const bool isWrongPacePassword = CardReturnCodeUtil::equalsWrongPacePassword(paceReturnCode); + + if (isWrongPacePassword) + { + const int nextExpectedCounter = getContext()->getExpectedRetryCounter() - 1; + qCDebug(statemachine) << "Wrong PACE password. Decreasing expected retry counter to" << nextExpectedCounter; + getContext()->setExpectedRetryCounter(nextExpectedCounter); + } + else if (paceReturnCode == CardReturnCode::OK) + { + const int nextExpectedCounter = 3; + qCDebug(statemachine) << "Correct PACE password. Expected retry counter is now" << nextExpectedCounter; + getContext()->setExpectedRetryCounter(nextExpectedCounter); + } + } + } + else if (unblockPinCommand) + { + const CardReturnCode returnCode = unblockPinCommand->getReturnCode(); + qCDebug(statemachine) << "PACE PUK finished with:" << returnCode; + + if (returnCode == CardReturnCode::OK || CardReturnCodeUtil::equalsWrongPacePassword(returnCode)) + { + qCDebug(statemachine) << "Resetting PACE passwords and setting expected retry counter to -1"; + getContext()->resetPacePasswords(); + getContext()->setExpectedRetryCounter(-1); + } + + EstablishPaceChannelOutput channelOutput = EstablishPaceChannelOutput(); + channelOutput.setPaceReturnCode(unblockPinCommand->getReturnCode()); + getContext()->setEstablishPaceChannelOutput(channelOutput); + } + else + { + Q_ASSERT(false); + qCDebug(statemachine) << "Expected an EstablishPaceChannelCommand as response!"; + } + + Q_EMIT fireContinue(); +} diff --git a/src/core/states/remote_service/StateEstablishPaceChannelRemote.h b/src/core/states/remote_service/StateEstablishPaceChannelRemote.h new file mode 100644 index 0000000..da36c20 --- /dev/null +++ b/src/core/states/remote_service/StateEstablishPaceChannelRemote.h @@ -0,0 +1,36 @@ +/*! + * \brief This state executes the remote message PACE channel establish. + * + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + + +#include "context/RemoteServiceContext.h" +#include "states/AbstractGenericState.h" + +class test_StateEstablishPaceChannelRemote; + +namespace governikus +{ + +class StateEstablishPaceChannelRemote + : public AbstractGenericState +{ + Q_OBJECT + friend class StateBuilder; + friend class ::test_StateEstablishPaceChannelRemote; + + private: + PacePasswordId mPasswordId; + + explicit StateEstablishPaceChannelRemote(const QSharedPointer& pContext); + virtual void run() override; + + private Q_SLOTS: + void onReaderInfoChanged(const ReaderInfo& pReaderInfo); + void onEstablishConnectionDone(QSharedPointer pCommand); +}; + +} // namespace governikus diff --git a/src/core/states/remote_service/StateEstablishPaceChannelResponse.cpp b/src/core/states/remote_service/StateEstablishPaceChannelResponse.cpp new file mode 100644 index 0000000..baa9332 --- /dev/null +++ b/src/core/states/remote_service/StateEstablishPaceChannelResponse.cpp @@ -0,0 +1,41 @@ +/*! + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "StateEstablishPaceChannelResponse.h" + +#include "ServerMessageHandler.h" + +using namespace governikus; + + +StateEstablishPaceChannelResponse::StateEstablishPaceChannelResponse(const QSharedPointer& pContext) + : AbstractGenericState(pContext, false) +{ +} + + +void StateEstablishPaceChannelResponse::run() +{ + Q_ASSERT(getContext()->getRemoteServer()); + Q_ASSERT(getContext()->getEstablishPaceChannelMessage()); + + const QSharedPointer& context = getContext(); + const auto& establishPaceChannelOutput = context->getEstablishPaceChannelOutput(); + if (context->getRemoteServer() && context->getRemoteServer()->getMessageHandler()) + { + context->getRemoteServer()->getMessageHandler()->sendEstablishPaceChannelResponse( + context->getEstablishPaceChannelMessage()->getSlotHandle(), + establishPaceChannelOutput + ); + } + + const bool isWrongPacePassword = CardReturnCodeUtil::equalsWrongPacePassword(establishPaceChannelOutput.getPaceReturnCode()); + if (isWrongPacePassword) + { + Q_EMIT fireWrongPacePassword(); + return; + } + + Q_EMIT fireContinue(); +} diff --git a/src/core/states/remote_service/StateEstablishPaceChannelResponse.h b/src/core/states/remote_service/StateEstablishPaceChannelResponse.h new file mode 100644 index 0000000..90ac022 --- /dev/null +++ b/src/core/states/remote_service/StateEstablishPaceChannelResponse.h @@ -0,0 +1,32 @@ +/*! + * \brief This state executes the remote message PACE channel establish. + * + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + + +#include "context/RemoteServiceContext.h" +#include "states/AbstractGenericState.h" + +class test_StateEstablishPaceChannelRemote; + +namespace governikus +{ + +class StateEstablishPaceChannelResponse + : public AbstractGenericState +{ + Q_OBJECT + friend class StateBuilder; + + private: + explicit StateEstablishPaceChannelResponse(const QSharedPointer& pContext); + virtual void run() override; + + Q_SIGNALS: + void fireWrongPacePassword(); +}; + +} // namespace governikus diff --git a/src/core/states/remote_service/StatePrepareChangePinRemote.cpp b/src/core/states/remote_service/StatePrepareChangePinRemote.cpp new file mode 100644 index 0000000..473499b --- /dev/null +++ b/src/core/states/remote_service/StatePrepareChangePinRemote.cpp @@ -0,0 +1,33 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "StatePrepareChangePinRemote.h" + +#include "PinModify.h" +#include "ServerMessageHandler.h" + +#include + +Q_DECLARE_LOGGING_CATEGORY(remote_device) + + +using namespace governikus; + + +StatePrepareChangePinRemote::StatePrepareChangePinRemote(const QSharedPointer& pContext) + : AbstractGenericState(pContext, false) +{ +} + + +void StatePrepareChangePinRemote::run() +{ + if (getContext()->getNewPin().isEmpty()) + { + Q_EMIT fireEnterNewPacePin(); + return; + } + + Q_EMIT fireContinue(); +} diff --git a/src/core/states/remote_service/StatePrepareChangePinRemote.h b/src/core/states/remote_service/StatePrepareChangePinRemote.h new file mode 100644 index 0000000..6c0c2fd --- /dev/null +++ b/src/core/states/remote_service/StatePrepareChangePinRemote.h @@ -0,0 +1,29 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + + +#include "context/RemoteServiceContext.h" +#include "states/AbstractGenericState.h" + + +namespace governikus +{ + +class StatePrepareChangePinRemote + : public AbstractGenericState +{ + Q_OBJECT + friend class StateBuilder; + + private: + explicit StatePrepareChangePinRemote(const QSharedPointer& pContext); + virtual void run() override; + + Q_SIGNALS: + void fireEnterNewPacePin(); +}; + +} // namespace governikus diff --git a/src/core/states/remote_service/StatePreparePaceRemote.cpp b/src/core/states/remote_service/StatePreparePaceRemote.cpp new file mode 100644 index 0000000..2330ca3 --- /dev/null +++ b/src/core/states/remote_service/StatePreparePaceRemote.cpp @@ -0,0 +1,65 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "StatePreparePaceRemote.h" + +#include "AppSettings.h" +#include "EstablishPaceChannelParser.h" + +#include + +Q_DECLARE_LOGGING_CATEGORY(statemachine) + + +using namespace governikus; + + +StatePreparePaceRemote::StatePreparePaceRemote(const QSharedPointer& pContext) + : AbstractGenericState(pContext, false) +{ +} + + +void StatePreparePaceRemote::run() +{ + const bool pinPadMode = Env::getSingleton()->getRemoteServiceSettings().getPinPadMode(); + if (pinPadMode) + { + const QSharedPointer& establishPaceChannelMessage = getContext()->getEstablishPaceChannelMessage(); + const EstablishPaceChannelParser& parser = EstablishPaceChannelParser::fromCcid(establishPaceChannelMessage->getInputData()); + + switch (parser.getPasswordId()) + { + case PacePasswordId::PACE_CAN: + if (getContext()->getCan().isEmpty()) + { + Q_EMIT fireEnterPacePassword(); + return; + } + break; + + case PacePasswordId::PACE_PIN: + if (getContext()->getPin().isEmpty()) + { + Q_EMIT fireEnterPacePassword(); + return; + } + + break; + + case PacePasswordId::PACE_PUK: + if (getContext()->getPuk().isEmpty()) + { + Q_EMIT fireEnterPacePassword(); + return; + } + break; + + default: + Q_UNREACHABLE(); + } + } + + Q_EMIT fireContinue(); +} diff --git a/src/core/states/remote_service/StatePreparePaceRemote.h b/src/core/states/remote_service/StatePreparePaceRemote.h new file mode 100644 index 0000000..7ad348c --- /dev/null +++ b/src/core/states/remote_service/StatePreparePaceRemote.h @@ -0,0 +1,29 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + + +#include "context/RemoteServiceContext.h" +#include "states/AbstractGenericState.h" + + +namespace governikus +{ + +class StatePreparePaceRemote + : public AbstractGenericState +{ + Q_OBJECT + friend class StateBuilder; + + private: + explicit StatePreparePaceRemote(const QSharedPointer& pContext); + virtual void run() override; + + Q_SIGNALS: + void fireEnterPacePassword(); +}; + +} // namespace governikus diff --git a/src/core/states/remote_service/StateProcessRemoteMessages.cpp b/src/core/states/remote_service/StateProcessRemoteMessages.cpp index 39915ac..7068b89 100644 --- a/src/core/states/remote_service/StateProcessRemoteMessages.cpp +++ b/src/core/states/remote_service/StateProcessRemoteMessages.cpp @@ -6,12 +6,15 @@ #include "ServerMessageHandler.h" +#include + +Q_DECLARE_LOGGING_CATEGORY(statemachine) using namespace governikus; StateProcessRemoteMessages::StateProcessRemoteMessages(const QSharedPointer& pContext) - : AbstractGenericState(pContext) + : AbstractGenericState(pContext, false) , mMessageConnections() { } @@ -51,17 +54,21 @@ void StateProcessRemoteMessages::onMessageHandlerAdded(const QSharedPointeronResetMessageHandler(); } @@ -70,8 +77,11 @@ void StateProcessRemoteMessages::onEstablishPaceChannel(const QSharedPointersetEstablishPaceChannelMessage(pMessage); - getContext()->setCardConnection(pConnection); + const auto& context = getContext(); + context->setEstablishPaceChannelMessage(pMessage); + context->setCardConnection(pConnection); + context->setReaderName(pConnection->getReaderInfo().getName()); + Q_EMIT fireEstablishPaceChannel(); } @@ -80,11 +90,11 @@ void StateProcessRemoteMessages::onModifyPin(const QSharedPointer context = getContext(); - Q_ASSERT(context); - + const auto& context = getContext(); context->setModifyPinMessage(pMessage); context->setCardConnection(pConnection); + context->setReaderName(pConnection->getReaderInfo().getName()); + Q_EMIT fireModifyPin(); } diff --git a/src/core/states/remote_service/StateProcessRemoteMessages.h b/src/core/states/remote_service/StateProcessRemoteMessages.h index 9212f7e..317fe68 100644 --- a/src/core/states/remote_service/StateProcessRemoteMessages.h +++ b/src/core/states/remote_service/StateProcessRemoteMessages.h @@ -11,6 +11,8 @@ #include "context/RemoteServiceContext.h" #include "states/AbstractGenericState.h" +class test_StateProcessRemoteMessages; + namespace governikus { @@ -19,11 +21,12 @@ class StateProcessRemoteMessages { Q_OBJECT friend class StateBuilder; + friend class ::test_StateProcessRemoteMessages; private: QVector mMessageConnections; - StateProcessRemoteMessages(const QSharedPointer& pContext); + explicit StateProcessRemoteMessages(const QSharedPointer& pContext); virtual void run() override; private Q_SLOTS: @@ -41,7 +44,7 @@ class StateProcessRemoteMessages Q_SIGNALS: void fireEstablishPaceChannel(); void fireModifyPin(); - + void fireSecureMessagingStopped(); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/remote_service/StateStartRemoteService.cpp b/src/core/states/remote_service/StateStartRemoteService.cpp index 7433baf..d2b1818 100644 --- a/src/core/states/remote_service/StateStartRemoteService.cpp +++ b/src/core/states/remote_service/StateStartRemoteService.cpp @@ -5,7 +5,6 @@ #include "StateStartRemoteService.h" #include "AppSettings.h" -#include "Env.h" #include "ServerMessageHandler.h" using namespace governikus; diff --git a/src/core/states/remote_service/StateStartRemoteService.h b/src/core/states/remote_service/StateStartRemoteService.h index c8ecd2a..fb87890 100644 --- a/src/core/states/remote_service/StateStartRemoteService.h +++ b/src/core/states/remote_service/StateStartRemoteService.h @@ -10,6 +10,8 @@ #include "context/RemoteServiceContext.h" #include "states/AbstractGenericState.h" +class test_StateStartRemoteService; + namespace governikus { @@ -18,8 +20,9 @@ class StateStartRemoteService { Q_OBJECT friend class StateBuilder; + friend class ::test_StateStartRemoteService; - StateStartRemoteService(const QSharedPointer& pContext); + explicit StateStartRemoteService(const QSharedPointer& pContext); virtual void run() override; @@ -31,4 +34,4 @@ class StateStartRemoteService }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/states/remote_service/StateStopRemoteService.cpp b/src/core/states/remote_service/StateStopRemoteService.cpp index cd27477..2ca53bb 100644 --- a/src/core/states/remote_service/StateStopRemoteService.cpp +++ b/src/core/states/remote_service/StateStopRemoteService.cpp @@ -34,5 +34,8 @@ void StateStopRemoteService::onExit(QEvent* pEvent) Q_ASSERT(server); server->stop(); + // Request an asynchronous update of all retry counters + Env::getSingleton()->updateRetryCounters(); + AbstractState::onExit(pEvent); } diff --git a/src/core/states/remote_service/StateStopRemoteService.h b/src/core/states/remote_service/StateStopRemoteService.h index 0b2af0b..5b56f2c 100644 --- a/src/core/states/remote_service/StateStopRemoteService.h +++ b/src/core/states/remote_service/StateStopRemoteService.h @@ -10,6 +10,8 @@ #include "context/RemoteServiceContext.h" #include "states/AbstractGenericState.h" +class test_StateStopRemoteService; + namespace governikus { @@ -18,8 +20,9 @@ class StateStopRemoteService { Q_OBJECT friend class StateBuilder; + friend class ::test_StateStopRemoteService; - StateStopRemoteService(const QSharedPointer& pContext); + explicit StateStopRemoteService(const QSharedPointer& pContext); virtual void run() override; void onExit(QEvent* pEvent) override; @@ -29,4 +32,4 @@ class StateStopRemoteService }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/core/view/UILoader.cpp b/src/core/view/UILoader.cpp deleted file mode 100644 index 88f9261..0000000 --- a/src/core/view/UILoader.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/*! - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "UILoader.h" - -#include "SingletonHelper.h" -#include "UIPlugIn.h" - -#include -#include -#include - -Q_DECLARE_LOGGING_CATEGORY(gui) - -using namespace governikus; - -defineSingleton(UILoader) - - -#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) - #define DEFAULT_UI {UIPlugInName::UIPlugInQml} -#else - #define DEFAULT_UI {UIPlugInName::UIPlugInWidgets} -#endif - - -UILoader::UILoader() - : mLoadedPlugIns() - , mDefault(DEFAULT_UI) -{ -} - - -UILoader::~UILoader() -{ -} - - -UILoader& UILoader::getInstance() -{ - return *Instance; -} - - -bool UILoader::load() -{ - bool any = false; - for (auto entry : qAsConst(mDefault)) - { - any = load(entry) || any; - } - return any; -} - - -bool UILoader::load(UIPlugInName pUi) -{ - Q_ASSERT(thread() == QThread::currentThread()); - - if (mLoadedPlugIns.contains(pUi)) - { - return true; - } - - const auto& name = getEnumName(pUi); - qCDebug(gui) << "Try to load UI plugin:" << name; - - const auto& allPlugins = QPluginLoader::staticPlugins(); - for (auto& plugin : allPlugins) - { - auto metaData = plugin.metaData(); - if (isPlugIn(metaData) && hasName(metaData, name)) - { - qCDebug(gui) << "Load plugin:" << metaData; - auto instance = qobject_cast(plugin.instance()); - if (instance) - { - mLoadedPlugIns.insert(pUi, instance); - Q_EMIT fireLoadedPlugin(instance); - return true; - } - else - { - qCWarning(gui) << "Cannot cast to plugin instance:" << plugin.instance(); - } - } - } - - qCCritical(gui) << "Cannot find UI plugin:" << name; - return false; -} - - -UIPlugIn* UILoader::getLoaded(UIPlugInName pName) const -{ - return mLoadedPlugIns.value(pName); -} - - -bool UILoader::hasName(const QJsonObject& pJson, const QString& pName) -{ - return pJson.value(QStringLiteral("className")).toString() == pName; -} - - -bool UILoader::isPlugIn(const QJsonObject& pJson) -{ - return pJson.value(QStringLiteral("IID")).toString() == QLatin1String("governikus.UIPlugIn"); -} diff --git a/src/core/view/UILoader.h b/src/core/view/UILoader.h deleted file mode 100644 index 8ef30f0..0000000 --- a/src/core/view/UILoader.h +++ /dev/null @@ -1,63 +0,0 @@ -/*! - * \brief Loader to initialize UIPlugIns. - * - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "EnumHelper.h" - -#include -#include -#include - -namespace governikus -{ - -class UIPlugIn; - - -defineEnumType(UIPlugInName, UIPlugInQml, UIPlugInCli, UIPlugInWidgets, UIPlugInJsonApi, UIPlugInWebSocket, UIPlugInAidl) - -class UILoader - : public QObject -{ - Q_OBJECT - - private: - QMap mLoadedPlugIns; - QVector mDefault; - - inline bool isPlugIn(const QJsonObject& pJson); - inline bool hasName(const QJsonObject& pJson, const QString& pName); - - protected: - UILoader(); - virtual ~UILoader(); - - public: - static UILoader& getInstance(); - - bool load(); - bool load(UIPlugInName pName); - - const QVector& getDefault() const - { - return mDefault; - } - - - void setDefault(const QVector& pDefault) - { - mDefault = pDefault; - } - - - UIPlugIn* getLoaded(UIPlugInName pName) const; - - Q_SIGNALS: - void fireLoadedPlugin(UIPlugIn* pPlugin); -}; - -} /* namespace governikus */ diff --git a/src/core/view/UIPlugIn.cpp b/src/core/view/UIPlugIn.cpp deleted file mode 100644 index afdf6f2..0000000 --- a/src/core/view/UIPlugIn.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/*! - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "UIPlugIn.h" - -using namespace governikus; - -UIPlugIn::UIPlugIn() -{ -} - - -UIPlugIn::~UIPlugIn() -{ -} - - -void UIPlugIn::onApplicationStarted() -{ - -} - - -void UIPlugIn::onShowUi(UiModule pModule) -{ - Q_UNUSED(pModule) -} - - -void UIPlugIn::onShowReaderSettings() -{ -} - - -void UIPlugIn::onSwitchToReaderSettingsRequested() -{ - Q_EMIT fireSwitchToReaderSettingsRequested(); -} - - -#ifndef QT_NO_NETWORKPROXY -void UIPlugIn::onProxyAuthenticationRequired(const QNetworkProxy& pProxy, QAuthenticator* pAuthenticator) -{ - Q_UNUSED(pProxy) - Q_UNUSED(pAuthenticator) -} - - -#endif diff --git a/src/core/view/UIPlugIn.h b/src/core/view/UIPlugIn.h deleted file mode 100644 index e9ce645..0000000 --- a/src/core/view/UIPlugIn.h +++ /dev/null @@ -1,54 +0,0 @@ -/*! - * \brief Abstract layer to UI implementations. - * - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "ActivationHandler.h" - -#include -#include - -namespace governikus -{ - -class WorkflowContext; - - -class UIPlugIn - : public QObject -{ - Q_OBJECT - - public: - UIPlugIn(); - virtual ~UIPlugIn(); - - public Q_SLOTS: - virtual void doShutdown() = 0; - virtual void onWorkflowStarted(QSharedPointer pContext) = 0; - virtual void onWorkflowFinished(QSharedPointer pContext) = 0; - virtual void onApplicationStarted(); - virtual void onShowUi(UiModule pModule); - virtual void onShowReaderSettings(); - virtual void onSwitchToReaderSettingsRequested(); -#ifndef QT_NO_NETWORKPROXY - virtual void onProxyAuthenticationRequired(const QNetworkProxy& pProxy, QAuthenticator* pAuthenticator); -#endif - - Q_SIGNALS: - void fireChangePinRequest(); - void fireSelfAuthenticationRequested(); - void fireSwitchToReaderSettingsRequested(); - void fireRemoteServiceRequested(); - void fireQuitApplicationRequest(); - void fireCloseReminderFinished(bool pDontRemindAgain); - - void fireShowUserInformation(const QString& pInformationMessage); -}; - -} /* namespace governikus */ - -Q_DECLARE_INTERFACE(governikus::UIPlugIn, "governikus.UIPlugIn") diff --git a/src/export/CMakeLists.txt b/src/export/CMakeLists.txt index f35180a..f2f51cf 100644 --- a/src/export/CMakeLists.txt +++ b/src/export/CMakeLists.txt @@ -1,3 +1,9 @@ +##################################################################### +# The module export is responsible to export certain data to other +# file formats. +# Supported: PDF +##################################################################### + ADD_PLATFORM_LIBRARY(AusweisAppExport) -TARGET_LINK_LIBRARIES(AusweisAppExport Qt5::Core Qt5::Svg AusweisAppSettings) +TARGET_LINK_LIBRARIES(AusweisAppExport Qt5::Core Qt5::Svg AusweisAppCard AusweisAppSettings) diff --git a/src/export/PdfCreator.cpp b/src/export/PdfCreator.cpp index 2b89e15..4d5be9b 100644 --- a/src/export/PdfCreator.cpp +++ b/src/export/PdfCreator.cpp @@ -28,10 +28,7 @@ PdfCreator::PdfCreator(const QString& pFilename, const QString& pTitle, const QS mPdfWriter.setPageLayout(layout); mPdfWriter.setCreator(QCoreApplication::applicationName()); mPdfWriter.setTitle(pTitle); - -#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) mPdfWriter.setPdfVersion(QPagedPaintDevice::PdfVersion_A1b); -#endif createHeader(pTitle, pHeadline); createFooter(); diff --git a/src/export/PdfCreator.h b/src/export/PdfCreator.h index 9b86559..08fa545 100644 --- a/src/export/PdfCreator.h +++ b/src/export/PdfCreator.h @@ -34,4 +34,4 @@ class PdfCreator }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/export/PdfExporter.cpp b/src/export/PdfExporter.cpp index 5856e00..657cc53 100644 --- a/src/export/PdfExporter.cpp +++ b/src/export/PdfExporter.cpp @@ -4,6 +4,7 @@ #include "PdfExporter.h" +#include "asn1/AccessRoleAndRight.h" #include "AppSettings.h" #include "LanguageLoader.h" #include "PdfCreator.h" @@ -24,7 +25,7 @@ QString getValueOrWhitespace(const QStringList& pValues, int i) } -} +} // namespace PdfExporter::PdfExporter(const QString& pFilename, bool pOpenFile, bool pFixFilename) @@ -109,14 +110,15 @@ bool PdfExporter::exportHistory() initTable(3, {180, 80}, {tr("Date"), tr("Details")}); const auto& dateTimeFormat = tr("dd.MM.yyyy hh:mm AP"); - const auto& infos = AppSettings::getInstance().getHistorySettings().getHistoryInfos(); + const auto& infos = Env::getSingleton()->getHistorySettings().getHistoryInfos(); for (const auto& entry : infos) { toggleRowColor(); const QString& dateTimeEntry = locale.toString(entry.getDateTime(), dateTimeFormat); addTableRow({dateTimeEntry, tr("Provider:"), entry.getSubjectName()}); addTableRow({QString(), tr("Purpose:"), entry.getPurpose()}); - addTableRow({QString(), tr("Data:"), entry.getRequestedData()}); + const auto& data = AccessRoleAndRightsUtil::joinFromTechnicalName(entry.getRequestedData()); + addTableRow({QString(), tr("Data:"), data}); } closeTable(); diff --git a/src/export/PdfExporter.h b/src/export/PdfExporter.h index 6a6a113..4b65f65 100644 --- a/src/export/PdfExporter.h +++ b/src/export/PdfExporter.h @@ -40,4 +40,4 @@ class PdfExporter bool exportSelfInfo(const QDateTime& pDate, const QVector >& pInfoData); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/external/CMakeLists.txt b/src/external/CMakeLists.txt index 6c7792c..633965b 100644 --- a/src/external/CMakeLists.txt +++ b/src/external/CMakeLists.txt @@ -1,6 +1,6 @@ ################################## http_parser ######################################################################### -FUNCTION(PRINT_HTTP_PARSER _include_dir _lib_dir) +FUNCTION(PARSE_HTTP_PARSER_VERSION _include_dir _version) SET(_file "${_include_dir}/http_parser.h") FILE(READ "${_file}" _file_content) @@ -8,8 +8,12 @@ FUNCTION(PRINT_HTTP_PARSER _include_dir _lib_dir) STRING(REGEX REPLACE ".*#define[\t ]+HTTP_PARSER_VERSION_MAJOR[\t ]+([0-9]+).*" "\\1" _major "${_file_content}") STRING(REGEX REPLACE ".*#define[\t ]+HTTP_PARSER_VERSION_MINOR[\t ]+([0-9]+).*" "\\1" _minor "${_file_content}") STRING(REGEX REPLACE ".*#define[\t ]+HTTP_PARSER_VERSION_PATCH[\t ]+([0-9]+).*" "\\1" _patch "${_file_content}") - SET(_version "${_major}.${_minor}.${_patch}") + SET(${_version} "${_major}.${_minor}.${_patch}" PARENT_SCOPE) ENDIF() +ENDFUNCTION() + +FUNCTION(PRINT_HTTP_PARSER _include_dir _lib_dir) + PARSE_HTTP_PARSER_VERSION("${_include_dir}" _version) IF(NOT EXISTS "${_lib_dir}") SET(_lib_dir "INTERNAL") @@ -21,9 +25,19 @@ ENDFUNCTION() IF(NOT CMAKE_VERSION VERSION_LESS "3.10") FIND_PATH(HTTP_PARSER_INCLUDE_DIR NAMES http_parser.h) FIND_LIBRARY(HTTP_PARSER_LIBRARY NAMES http_parser libhttp_parser) - INCLUDE(FindPackageHandleStandardArgs) - FIND_PACKAGE_HANDLE_STANDARD_ARGS(HTTP_PARSER REQUIRED_VARS HTTP_PARSER_INCLUDE_DIR HTTP_PARSER_LIBRARY) - MARK_AS_ADVANCED(HTTP_PARSER_INCLUDE_DIR HTTP_PARSER_LIBRARY) + + IF(HTTP_PARSER_INCLUDE_DIR) + PARSE_HTTP_PARSER_VERSION("${HTTP_PARSER_INCLUDE_DIR}" _version) + IF(_version VERSION_LESS "2.8.0") + MESSAGE(STATUS "HttpParser (system) too old: ${_version}") + UNSET(HTTP_PARSER_INCLUDE_DIR CACHE) + UNSET(HTTP_PARSER_LIBRARY CACHE) + ELSE() + INCLUDE(FindPackageHandleStandardArgs) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(HTTP_PARSER REQUIRED_VARS HTTP_PARSER_INCLUDE_DIR HTTP_PARSER_LIBRARY) + MARK_AS_ADVANCED(HTTP_PARSER_INCLUDE_DIR HTTP_PARSER_LIBRARY) + ENDIF() + ENDIF() ENDIF() IF(HTTP_PARSER_FOUND) @@ -37,20 +51,10 @@ ELSE() ADD_LIBRARY(AusweisAppExternalHttpParser ${EXTERNAL_HTTP_PARSER_FILES}) TARGET_INCLUDE_DIRECTORIES(AusweisAppExternalHttpParser INTERFACE "$") - IF("${CMAKE_CXX_FLAGS}" MATCHES "-Wcovered-switch-default") - SET_PROPERTY(TARGET AusweisAppExternalHttpParser APPEND_STRING PROPERTY COMPILE_FLAGS " -Wno-covered-switch-default") - ENDIF() - - IF("${CMAKE_CXX_FLAGS}" MATCHES "-Wconversion") - SET_PROPERTY(TARGET AusweisAppExternalHttpParser APPEND_STRING PROPERTY COMPILE_FLAGS " -Wno-conversion") - ENDIF() - - IF("${CMAKE_CXX_FLAGS}" MATCHES "-Wold-style-cast") - SET_PROPERTY(TARGET AusweisAppExternalHttpParser APPEND_STRING PROPERTY COMPILE_FLAGS " -Wno-old-style-cast") - ENDIF() - - IF(NOT MSVC) - ADD_FLAG(-Wno-gnu-statement-expression VAR AusweisAppExternalHttpParser NAME HttpParser) + IF(MSVC) + SET_PROPERTY(TARGET AusweisAppExternalHttpParser APPEND_STRING PROPERTY COMPILE_FLAGS " /w") + ELSE() + SET_PROPERTY(TARGET AusweisAppExternalHttpParser APPEND_STRING PROPERTY COMPILE_FLAGS " -w") ENDIF() ADD_LIBRARY(AusweisAppExternal::HttpParser ALIAS AusweisAppExternalHttpParser) diff --git a/src/external/http_parser/http_parser.cpp b/src/external/http_parser/http_parser.cpp index d46836a..f9991c3 100644 --- a/src/external/http_parser/http_parser.cpp +++ b/src/external/http_parser/http_parser.cpp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include @@ -283,10 +282,10 @@ enum state , s_res_HT , s_res_HTT , s_res_HTTP - , s_res_first_http_major , s_res_http_major - , s_res_first_http_minor + , s_res_http_dot , s_res_http_minor + , s_res_http_end , s_res_first_status_code , s_res_status_code , s_res_status_start @@ -313,10 +312,10 @@ enum state , s_req_http_HT , s_req_http_HTT , s_req_http_HTTP - , s_req_first_http_major , s_req_http_major - , s_req_first_http_minor + , s_req_http_dot , s_req_http_minor + , s_req_http_end , s_req_line_almost_done , s_header_field_start @@ -371,6 +370,8 @@ enum header_states , h_connection , h_content_length + , h_content_length_num + , h_content_length_ws , h_transfer_encoding , h_upgrade @@ -792,75 +793,48 @@ reexecute: case s_res_HTTP: STRICT_CHECK(ch != '/'); - UPDATE_STATE(s_res_first_http_major); + UPDATE_STATE(s_res_http_major); break; - case s_res_first_http_major: - if (UNLIKELY(ch < '0' || ch > '9')) { + case s_res_http_major: + if (UNLIKELY(!IS_NUM(ch))) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } parser->http_major = ch - '0'; - UPDATE_STATE(s_res_http_major); + UPDATE_STATE(s_res_http_dot); break; - /* major HTTP version or dot */ - case s_res_http_major: + case s_res_http_dot: { - if (ch == '.') { - UPDATE_STATE(s_res_first_http_minor); - break; - } - - if (!IS_NUM(ch)) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - parser->http_major *= 10; - parser->http_major += ch - '0'; - - if (UNLIKELY(parser->http_major > 999)) { + if (UNLIKELY(ch != '.')) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } + UPDATE_STATE(s_res_http_minor); break; } - /* first digit of minor HTTP version */ - case s_res_first_http_minor: + case s_res_http_minor: if (UNLIKELY(!IS_NUM(ch))) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } parser->http_minor = ch - '0'; - UPDATE_STATE(s_res_http_minor); + UPDATE_STATE(s_res_http_end); break; - /* minor HTTP version or end of request line */ - case s_res_http_minor: + case s_res_http_end: { - if (ch == ' ') { - UPDATE_STATE(s_res_first_status_code); - break; - } - - if (UNLIKELY(!IS_NUM(ch))) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - parser->http_minor *= 10; - parser->http_minor += ch - '0'; - - if (UNLIKELY(parser->http_minor > 999)) { + if (UNLIKELY(ch != ' ')) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } + UPDATE_STATE(s_res_first_status_code); break; } @@ -887,10 +861,9 @@ reexecute: UPDATE_STATE(s_res_status_start); break; case CR: - UPDATE_STATE(s_res_line_almost_done); - break; case LF: - UPDATE_STATE(s_header_field_start); + UPDATE_STATE(s_res_status_start); + REEXECUTE(); break; default: SET_ERRNO(HPE_INVALID_STATUS); @@ -912,19 +885,13 @@ reexecute: case s_res_status_start: { - if (ch == CR) { - UPDATE_STATE(s_res_line_almost_done); - break; - } - - if (ch == LF) { - UPDATE_STATE(s_header_field_start); - break; - } - MARK(status); UPDATE_STATE(s_res_status); parser->index = 0; + + if (ch == CR || ch == LF) + REEXECUTE(); + break; } @@ -977,7 +944,7 @@ reexecute: /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */ break; case 'R': parser->method = HTTP_REPORT; /* or REBIND */ break; - case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break; + case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH, SOURCE */ break; case 'T': parser->method = HTTP_TRACE; break; case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE, UNBIND, UNLINK */ break; default: @@ -1004,7 +971,7 @@ reexecute: UPDATE_STATE(s_req_spaces_before_url); } else if (ch == matcher[parser->index]) { ; /* nada */ - } else if (IS_ALPHA(ch)) { + } else if ((ch >= 'A' && ch <= 'Z') || ch == '-') { switch (parser->method << 16 | parser->index << 8 | ch) { #define XX(meth, pos, ch, new_meth) \ @@ -1013,31 +980,28 @@ reexecute: XX(POST, 1, 'U', PUT) XX(POST, 1, 'A', PATCH) + XX(POST, 1, 'R', PROPFIND) + XX(PUT, 2, 'R', PURGE) XX(CONNECT, 1, 'H', CHECKOUT) XX(CONNECT, 2, 'P', COPY) XX(MKCOL, 1, 'O', MOVE) XX(MKCOL, 1, 'E', MERGE) + XX(MKCOL, 1, '-', MSEARCH) XX(MKCOL, 2, 'A', MKACTIVITY) XX(MKCOL, 3, 'A', MKCALENDAR) XX(SUBSCRIBE, 1, 'E', SEARCH) + XX(SUBSCRIBE, 1, 'O', SOURCE) XX(REPORT, 2, 'B', REBIND) - XX(POST, 1, 'R', PROPFIND) XX(PROPFIND, 4, 'P', PROPPATCH) - XX(PUT, 2, 'R', PURGE) XX(LOCK, 1, 'I', LINK) XX(UNLOCK, 2, 'S', UNSUBSCRIBE) XX(UNLOCK, 2, 'B', UNBIND) XX(UNLOCK, 3, 'I', UNLINK) #undef XX - default: SET_ERRNO(HPE_INVALID_METHOD); goto error; } - } else if (ch == '-' && - parser->index == 1 && - parser->method == HTTP_MKCOL) { - parser->method = HTTP_MSEARCH; } else { SET_ERRNO(HPE_INVALID_METHOD); goto error; @@ -1150,57 +1114,41 @@ reexecute: case s_req_http_HTTP: STRICT_CHECK(ch != '/'); - UPDATE_STATE(s_req_first_http_major); - break; - - /* first digit of major HTTP version */ - case s_req_first_http_major: - if (UNLIKELY(ch < '1' || ch > '9')) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - parser->http_major = ch - '0'; UPDATE_STATE(s_req_http_major); break; - /* major HTTP version or dot */ case s_req_http_major: - { - if (ch == '.') { - UPDATE_STATE(s_req_first_http_minor); - break; - } - if (UNLIKELY(!IS_NUM(ch))) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } - parser->http_major *= 10; - parser->http_major += ch - '0'; + parser->http_major = ch - '0'; + UPDATE_STATE(s_req_http_dot); + break; - if (UNLIKELY(parser->http_major > 999)) { + case s_req_http_dot: + { + if (UNLIKELY(ch != '.')) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } + UPDATE_STATE(s_req_http_minor); break; } - /* first digit of minor HTTP version */ - case s_req_first_http_minor: + case s_req_http_minor: if (UNLIKELY(!IS_NUM(ch))) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } parser->http_minor = ch - '0'; - UPDATE_STATE(s_req_http_minor); + UPDATE_STATE(s_req_http_end); break; - /* minor HTTP version or end of request line */ - case s_req_http_minor: + case s_req_http_end: { if (ch == CR) { UPDATE_STATE(s_req_line_almost_done); @@ -1212,21 +1160,8 @@ reexecute: break; } - /* XXX allow spaces after digit? */ - - if (UNLIKELY(!IS_NUM(ch))) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - parser->http_minor *= 10; - parser->http_minor += ch - '0'; - - if (UNLIKELY(parser->http_minor > 999)) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - + SET_ERRNO(HPE_INVALID_VERSION); + goto error; break; } @@ -1473,6 +1408,7 @@ reexecute: parser->flags |= F_CONTENTLENGTH; parser->content_length = ch - '0'; + parser->header_state = h_content_length_num; break; case h_connection: @@ -1560,10 +1496,18 @@ reexecute: break; case h_content_length: + if (ch == ' ') break; + h_state = h_content_length_num; + /* FALLTHROUGH */ + + case h_content_length_num: { uint64_t t; - if (ch == ' ') break; + if (ch == ' ') { + h_state = h_content_length_ws; + break; + } if (UNLIKELY(!IS_NUM(ch))) { SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); @@ -1586,6 +1530,12 @@ reexecute: break; } + case h_content_length_ws: + if (ch == ' ') break; + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + parser->header_state = h_state; + goto error; + /* Transfer-Encoding: chunked */ case h_matching_transfer_encoding_chunked: parser->index++; @@ -1791,10 +1741,17 @@ reexecute: UPDATE_STATE(s_headers_done); /* Set this here so that on_headers_complete() callbacks can see it */ - parser->upgrade = - ((parser->flags & (F_UPGRADE | F_CONNECTION_UPGRADE)) == - (F_UPGRADE | F_CONNECTION_UPGRADE) || - parser->method == HTTP_CONNECT); + if ((parser->flags & F_UPGRADE) && + (parser->flags & F_CONNECTION_UPGRADE)) { + /* For responses, "Upgrade: foo" and "Connection: upgrade" are + * mandatory only when it is a 101 Switching Protocols response, + * otherwise it is purely informational, to announce support. + */ + parser->upgrade = + (parser->type == HTTP_REQUEST || parser->status_code == 101); + } else { + parser->upgrade = (parser->method == HTTP_CONNECT); + } /* Here we call the headers_complete callback. This is somewhat * different than other callbacks because if the user returns 1, we @@ -2427,12 +2384,27 @@ http_parser_parse_url(const char *buf, size_t buflen, int is_connect, } if (u->field_set & (1 << UF_PORT)) { - /* Don't bother with endp; we've already validated the string */ - unsigned long v = strtoul(buf + u->field_data[UF_PORT].off, NULL, 10); + uint16_t off; + uint16_t len; + const char* p; + const char* end; + unsigned long v; - /* Ports have a max value of 2^16 */ - if (v > 0xffff) { - return 1; + off = u->field_data[UF_PORT].off; + len = u->field_data[UF_PORT].len; + end = buf + off + len; + + /* NOTE: The characters are already validated and are in the [0-9] range */ + assert(off + len <= buflen && "Port number overflow"); + v = 0; + for (p = buf + off; p < end; p++) { + v *= 10; + v += *p - '0'; + + /* Ports have a max value of 2^16 */ + if (v > 0xffff) { + return 1; + } } u->port = (uint16_t) v; diff --git a/src/external/http_parser/http_parser.h b/src/external/http_parser/http_parser.h index ea26394..a0de71e 100644 --- a/src/external/http_parser/http_parser.h +++ b/src/external/http_parser/http_parser.h @@ -26,14 +26,13 @@ extern "C" { /* Also update SONAME in the Makefile whenever you change these. */ #define HTTP_PARSER_VERSION_MAJOR 2 -#define HTTP_PARSER_VERSION_MINOR 7 +#define HTTP_PARSER_VERSION_MINOR 8 #define HTTP_PARSER_VERSION_PATCH 1 -#include +#include #if defined(_WIN32) && !defined(__MINGW32__) && \ (!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__) #include -#include typedef __int8 int8_t; typedef unsigned __int8 uint8_t; typedef __int16 int16_t; @@ -90,6 +89,76 @@ typedef int (*http_data_cb) (http_parser*, const char *at, size_t length); typedef int (*http_cb) (http_parser*); +/* Status Codes */ +#define HTTP_STATUS_MAP(XX) \ + XX(100, CONTINUE, Continue) \ + XX(101, SWITCHING_PROTOCOLS, Switching Protocols) \ + XX(102, PROCESSING, Processing) \ + XX(200, OK, OK) \ + XX(201, CREATED, Created) \ + XX(202, ACCEPTED, Accepted) \ + XX(203, NON_AUTHORITATIVE_INFORMATION, Non-Authoritative Information) \ + XX(204, NO_CONTENT, No Content) \ + XX(205, RESET_CONTENT, Reset Content) \ + XX(206, PARTIAL_CONTENT, Partial Content) \ + XX(207, MULTI_STATUS, Multi-Status) \ + XX(208, ALREADY_REPORTED, Already Reported) \ + XX(226, IM_USED, IM Used) \ + XX(300, MULTIPLE_CHOICES, Multiple Choices) \ + XX(301, MOVED_PERMANENTLY, Moved Permanently) \ + XX(302, FOUND, Found) \ + XX(303, SEE_OTHER, See Other) \ + XX(304, NOT_MODIFIED, Not Modified) \ + XX(305, USE_PROXY, Use Proxy) \ + XX(307, TEMPORARY_REDIRECT, Temporary Redirect) \ + XX(308, PERMANENT_REDIRECT, Permanent Redirect) \ + XX(400, BAD_REQUEST, Bad Request) \ + XX(401, UNAUTHORIZED, Unauthorized) \ + XX(402, PAYMENT_REQUIRED, Payment Required) \ + XX(403, FORBIDDEN, Forbidden) \ + XX(404, NOT_FOUND, Not Found) \ + XX(405, METHOD_NOT_ALLOWED, Method Not Allowed) \ + XX(406, NOT_ACCEPTABLE, Not Acceptable) \ + XX(407, PROXY_AUTHENTICATION_REQUIRED, Proxy Authentication Required) \ + XX(408, REQUEST_TIMEOUT, Request Timeout) \ + XX(409, CONFLICT, Conflict) \ + XX(410, GONE, Gone) \ + XX(411, LENGTH_REQUIRED, Length Required) \ + XX(412, PRECONDITION_FAILED, Precondition Failed) \ + XX(413, PAYLOAD_TOO_LARGE, Payload Too Large) \ + XX(414, URI_TOO_LONG, URI Too Long) \ + XX(415, UNSUPPORTED_MEDIA_TYPE, Unsupported Media Type) \ + XX(416, RANGE_NOT_SATISFIABLE, Range Not Satisfiable) \ + XX(417, EXPECTATION_FAILED, Expectation Failed) \ + XX(421, MISDIRECTED_REQUEST, Misdirected Request) \ + XX(422, UNPROCESSABLE_ENTITY, Unprocessable Entity) \ + XX(423, LOCKED, Locked) \ + XX(424, FAILED_DEPENDENCY, Failed Dependency) \ + XX(426, UPGRADE_REQUIRED, Upgrade Required) \ + XX(428, PRECONDITION_REQUIRED, Precondition Required) \ + XX(429, TOO_MANY_REQUESTS, Too Many Requests) \ + XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, Request Header Fields Too Large) \ + XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, Unavailable For Legal Reasons) \ + XX(500, INTERNAL_SERVER_ERROR, Internal Server Error) \ + XX(501, NOT_IMPLEMENTED, Not Implemented) \ + XX(502, BAD_GATEWAY, Bad Gateway) \ + XX(503, SERVICE_UNAVAILABLE, Service Unavailable) \ + XX(504, GATEWAY_TIMEOUT, Gateway Timeout) \ + XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP Version Not Supported) \ + XX(506, VARIANT_ALSO_NEGOTIATES, Variant Also Negotiates) \ + XX(507, INSUFFICIENT_STORAGE, Insufficient Storage) \ + XX(508, LOOP_DETECTED, Loop Detected) \ + XX(510, NOT_EXTENDED, Not Extended) \ + XX(511, NETWORK_AUTHENTICATION_REQUIRED, Network Authentication Required) \ + +enum http_status + { +#define XX(num, name, string) HTTP_STATUS_##name = num, + HTTP_STATUS_MAP(XX) +#undef XX + }; + + /* Request Methods */ #define HTTP_METHOD_MAP(XX) \ XX(0, DELETE, DELETE) \ @@ -132,6 +201,8 @@ typedef int (*http_cb) (http_parser*); /* RFC-2068, section 19.6.1.2 */ \ XX(31, LINK, LINK) \ XX(32, UNLINK, UNLINK) \ + /* icecast */ \ + XX(33, SOURCE, SOURCE) \ enum http_method { diff --git a/src/file_provider/CMakeLists.txt b/src/file_provider/CMakeLists.txt index fb2e032..44c1a40 100644 --- a/src/file_provider/CMakeLists.txt +++ b/src/file_provider/CMakeLists.txt @@ -1,3 +1,9 @@ +##################################################################### +# The module file provider is responsible to maintain file updates +# and their dependencies. It will trigger downloads and checks +# if a download is necessary. +##################################################################### + ADD_PLATFORM_LIBRARY(AusweisAppFileProvider) TARGET_LINK_LIBRARIES(AusweisAppFileProvider Qt5::Core AusweisAppSecureStorage AusweisAppNetwork) diff --git a/src/file_provider/Downloader.cpp b/src/file_provider/Downloader.cpp index a2b80b3..a069def 100644 --- a/src/file_provider/Downloader.cpp +++ b/src/file_provider/Downloader.cpp @@ -4,12 +4,12 @@ #include "Downloader.h" -#include "Env.h" -#include "HttpStatusCode.h" +#include "LogHandler.h" #include "ScopeGuard.h" #include "SingletonHelper.h" #include "TlsChecker.h" +#include #include #include #include @@ -72,7 +72,7 @@ void Downloader::onSslErrors(const QList& pErrors) void Downloader::onSslHandshakeDone() { const auto& cfg = mCurrentReply->sslConfiguration(); - TlsChecker::logSslConfig(cfg, qInfo(network)); + TlsChecker::logSslConfig(cfg, spawnMessageLogger(network)); if (!Env::getSingleton()->checkUpdateServerCertificate(*mCurrentReply)) { @@ -87,27 +87,21 @@ void Downloader::onMetadataChanged() { const QString& fileName = mCurrentRequest->url().fileName(); - QVariant status = mCurrentReply->attribute(QNetworkRequest::Attribute::HttpStatusCodeAttribute); - if (!status.isNull() && Enum::isValue(status.toInt())) + const auto statusCode = mCurrentReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + if (statusCode == HTTP_STATUS_OK) { - HttpStatusCode statusCode = static_cast(status.toInt()); - if (statusCode != HttpStatusCode::OK) - { - qCDebug(fileprovider) << "Abort request for" << fileName << "with status" << status.toInt() << "-" << statusCode; - mCurrentReply->abort(); - return; - } - qCDebug(fileprovider) << "Continue request for" << fileName << "with status" << status.toInt() << "-" << statusCode; + qCDebug(fileprovider) << "Continue request for" << fileName; return; } - qCDebug(fileprovider) << "Unknown or missing HttpStatusCodeAttribute for" << fileName; + qCDebug(fileprovider) << "Abort request for" << fileName; + mCurrentReply->abort(); } void Downloader::onNetworkReplyFinished() { - qCDebug(fileprovider) << "Downloader::onNetworkReplyFinished()"; + qCDebug(fileprovider) << "Downloader finished:" << mCurrentReply->request().url().fileName(); const ScopeGuard guard([this] { mCurrentReply->deleteLater(); @@ -133,25 +127,27 @@ void Downloader::onNetworkReplyFinished() return; } - QDateTime lastModified = mCurrentReply->header(QNetworkRequest::KnownHeaders::LastModifiedHeader).toDateTime(); - if (!lastModified.isValid()) + const auto statusCode = NetworkManager::getLoggedStatusCode(mCurrentReply, spawnMessageLogger(network)); + switch (statusCode) { - qCWarning(fileprovider) << "Server did not provide a valid LastModifiedHeader"; - lastModified = QDateTime::currentDateTime(); - } + case HTTP_STATUS_OK: + { + QDateTime lastModified = mCurrentReply->header(QNetworkRequest::KnownHeaders::LastModifiedHeader).toDateTime(); + if (!lastModified.isValid()) + { + qCWarning(fileprovider) << "Server did not provide a valid LastModifiedHeader"; + lastModified = QDateTime::currentDateTime(); + } - const int statusCode = mCurrentReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - switch (static_cast(statusCode)) - { - case HttpStatusCode::OK: Q_EMIT fireDownloadSuccess(mCurrentRequest->url(), lastModified, mCurrentReply->readAll()); break; + } - case HttpStatusCode::NOT_MODIFIED: + case HTTP_STATUS_NOT_MODIFIED: Q_EMIT fireDownloadUnnecessary(url); break; - case HttpStatusCode::NOT_FOUND: + case HTTP_STATUS_NOT_FOUND: Q_EMIT fireDownloadFailed(url, GlobalStatus::Code::Downloader_File_Not_Found); break; @@ -159,11 +155,11 @@ void Downloader::onNetworkReplyFinished() if (mCurrentReply->error() != QNetworkReply::NoError) { qCCritical(fileprovider).nospace() << mCurrentReply->errorString() << " [" << textForLog << "]"; - Q_EMIT fireDownloadFailed(url, NetworkManager::toStatus(mCurrentReply)); + Q_EMIT fireDownloadFailed(url, NetworkManager::toStatus(mCurrentReply).getStatusCode()); } else { - qCCritical(fileprovider).nospace() << "Invalid HTTP status code: " << statusCode << " for [" << textForLog << "]"; + qCCritical(fileprovider).nospace() << "Invalid HTTP status code for [" << textForLog << "]"; Q_EMIT fireDownloadFailed(url, GlobalStatus::Code::Network_Other_Error); } } @@ -196,7 +192,7 @@ Downloader::~Downloader() void Downloader::download(const QUrl& pUpdateUrl) { qCDebug(fileprovider) << "Download:" << pUpdateUrl; - QSharedPointer request = QSharedPointer(new QNetworkRequest(pUpdateUrl)); + auto request = QSharedPointer::create(pUpdateUrl); scheduleDownload(request); } @@ -204,9 +200,8 @@ void Downloader::download(const QUrl& pUpdateUrl) void Downloader::downloadIfNew(const QUrl& pUpdateUrl, const QDateTime& pCurrentTimestamp) { - qCDebug(fileprovider) << "Download:" << pUpdateUrl; - QSharedPointer request = QSharedPointer(new QNetworkRequest(pUpdateUrl)); + auto request = QSharedPointer::create(pUpdateUrl); const QString& timeStampString = QLocale::c().toString(pCurrentTimestamp, QStringLiteral("ddd, dd MMM yyyy hh:mm:ss 'GMT'")); if (!timeStampString.isEmpty()) { diff --git a/src/file_provider/Downloader.h b/src/file_provider/Downloader.h index e2014d3..d4c85fe 100644 --- a/src/file_provider/Downloader.h +++ b/src/file_provider/Downloader.h @@ -7,6 +7,7 @@ #pragma once +#include "Env.h" #include "GlobalStatus.h" #include "NetworkManager.h" @@ -23,6 +24,7 @@ class Downloader : public QObject { Q_OBJECT + friend class Env; private: QSharedPointer mCurrentRequest; @@ -35,6 +37,7 @@ class Downloader protected: Downloader(); virtual ~Downloader(); + static Downloader& getInstance(); private Q_SLOTS: void onSslErrors(const QList& pErrors); @@ -47,12 +50,10 @@ class Downloader Q_INVOKABLE virtual void downloadIfNew(const QUrl& pUpdateUrl, const QDateTime& pCurrentTimestamp); - static Downloader& getInstance(); - Q_SIGNALS: void fireDownloadSuccess(const QUrl& pUpdateUrl, const QDateTime& pNewTimestamp, const QByteArray& pData); void fireDownloadFailed(const QUrl& pUpdateUrl, GlobalStatus::Code pErrorCode); void fireDownloadUnnecessary(const QUrl& pUpdateUrl); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/file_provider/FileProvider.cpp b/src/file_provider/FileProvider.cpp index 9554f40..7e40636 100644 --- a/src/file_provider/FileProvider.cpp +++ b/src/file_provider/FileProvider.cpp @@ -37,7 +37,7 @@ const QSharedPointer FileProvider::getFile(const QString& pSectio const QSharedPointer existingF = mUpdatableFiles.value(key, QSharedPointer()); if (existingF.isNull()) { - const QSharedPointer newF(new UpdatableFile(pSection, pName, pDefaultPath)); + const QSharedPointer newF(new UpdatableFile(pSection, pName, pDefaultPath), &QObject::deleteLater); if (!pName.isEmpty()) { mUpdatableFiles.insert(key, newF); diff --git a/src/file_provider/FileProvider.h b/src/file_provider/FileProvider.h index 6185b22..a15fdfd 100644 --- a/src/file_provider/FileProvider.h +++ b/src/file_provider/FileProvider.h @@ -6,6 +6,7 @@ #pragma once +#include "Env.h" #include "UpdatableFile.h" #include @@ -18,6 +19,7 @@ namespace governikus class FileProvider { Q_GADGET + friend class Env; private: QHash > mUpdatableFiles; @@ -26,11 +28,10 @@ class FileProvider protected: FileProvider(); ~FileProvider() = default; + static FileProvider& getInstance(); public: const QSharedPointer getFile(const QString& pSection, const QString& pName, const QString& pDefaultPath = QString()); - - static FileProvider& getInstance(); }; } // namespace governikus diff --git a/src/file_provider/UpdatableFile.cpp b/src/file_provider/UpdatableFile.cpp index dd02c10..bca3f9e 100644 --- a/src/file_provider/UpdatableFile.cpp +++ b/src/file_provider/UpdatableFile.cpp @@ -5,7 +5,6 @@ #include "UpdatableFile.h" #include "Downloader.h" -#include "Env.h" #include "SecureStorage.h" #include @@ -24,7 +23,7 @@ Q_DECLARE_LOGGING_CATEGORY(fileprovider) namespace { const QLatin1Char Sep('/'); -} +} // namespace const QString& UpdatableFile::getName() @@ -239,6 +238,11 @@ UpdatableFile::UpdatableFile(const QString& pSection, const QString& pName, cons , mUpdateUrl(updateUrl(pSection, pName)) , mUpdateRunning(false) { + if (mName.isEmpty() && mDefaultPath.isEmpty()) + { + qCWarning(fileprovider) << "Both name and default path are empty!"; + Q_ASSERT(false); + } } @@ -355,7 +359,12 @@ void UpdatableFile::clearDirty() const void UpdatableFile::markDirty() const { - if (mSectionCachePath.isEmpty() && !mName.isEmpty()) + if (mName.isEmpty()) + { + return; + } + + if (mSectionCachePath.isEmpty()) { qCCritical(fileprovider) << "Cannot mark invalid file:" << mSection << Sep << mName; diff --git a/src/global/BreakPropertyBindingDiagnosticLogFilter.cpp b/src/global/BreakPropertyBindingDiagnosticLogFilter.cpp new file mode 100644 index 0000000..ce3788b --- /dev/null +++ b/src/global/BreakPropertyBindingDiagnosticLogFilter.cpp @@ -0,0 +1,34 @@ +/* + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "BreakPropertyBindingDiagnosticLogFilter.h" + +using namespace governikus; + + +QLoggingCategory::CategoryFilter BreakPropertyBindingDiagnosticLogFilter::cOldFilterFunctionPointer = nullptr; + +BreakPropertyBindingDiagnosticLogFilter::BreakPropertyBindingDiagnosticLogFilter(QObject* pParent) + : QObject(pParent) +{ + cOldFilterFunctionPointer = QLoggingCategory::installFilter(filterFunction); + Q_ASSERT(cOldFilterFunctionPointer != filterFunction && "Created a filter loop"); +} + + +void BreakPropertyBindingDiagnosticLogFilter::filterFunction(QLoggingCategory* pCategory) +{ + const QLatin1String breakPropertyBindingDiagnostic("qt.qml.binding.removal"); + if (QLatin1String(pCategory->categoryName()) == breakPropertyBindingDiagnostic) + { + pCategory->setEnabled(QtInfoMsg, true); + return; + } + + if (cOldFilterFunctionPointer == nullptr) + { + return; + } + cOldFilterFunctionPointer(pCategory); +} diff --git a/src/global/BreakPropertyBindingDiagnosticLogFilter.h b/src/global/BreakPropertyBindingDiagnosticLogFilter.h new file mode 100644 index 0000000..7b7728c --- /dev/null +++ b/src/global/BreakPropertyBindingDiagnosticLogFilter.h @@ -0,0 +1,27 @@ +/* + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include + +namespace governikus +{ + +class BreakPropertyBindingDiagnosticLogFilter + : public QObject +{ + Q_OBJECT + + private: + static QLoggingCategory::CategoryFilter cOldFilterFunctionPointer; + + static void filterFunction(QLoggingCategory* pCategory); + + public: + explicit BreakPropertyBindingDiagnosticLogFilter(QObject* pParent); +}; + +} // namespace governikus diff --git a/src/global/BuildHelper.cpp b/src/global/BuildHelper.cpp index 62df483..df8fca3 100644 --- a/src/global/BuildHelper.cpp +++ b/src/global/BuildHelper.cpp @@ -14,9 +14,6 @@ using namespace governikus; -const char* BuildHelper::mDateTime = __DATE__ " / " __TIME__; - - #ifdef Q_OS_ANDROID namespace { @@ -46,7 +43,7 @@ QAndroidJniObject getPackageInfo(const QString& pPackageName, int pFlags = 0) } -} +} // namespace int BuildHelper::getVersionCode() @@ -88,4 +85,62 @@ QString BuildHelper::getPackageName() } +QByteArrayList BuildHelper::getAppCertificates() +{ + return getAppCertificates(getPackageName()); +} + + +QByteArrayList BuildHelper::getAppCertificates(const QString& pPackageName) +{ + const int flags = 0x00000040; // GET_SIGNATURES + const auto info = getPackageInfo(pPackageName, flags); + + if (!info.isValid()) + { + return QByteArrayList(); + } + + const auto signatures = info.getObjectField("signatures", "[Landroid/content/pm/Signature;"); + if (!signatures.isValid()) + { + return QByteArrayList(); + } + + QAndroidJniEnvironment env; + jobjectArray obj = signatures.object(); + const jsize elementCount = env->GetArrayLength(obj); + QByteArrayList list; + list.reserve(elementCount); + + for (jsize i = 0; i < elementCount; ++i) + { + QAndroidJniObject elem = env->GetObjectArrayElement(obj, i); + if (!elem.isValid()) + { + continue; + } + + auto bytes = elem.callObjectMethod("toByteArray", "()[B"); + if (!bytes.isValid()) + { + continue; + } + + jbyteArray data = bytes.object(); + const auto size = env->GetArrayLength(data); + jbyte* buffer = env->GetByteArrayElements(data, 0); + list << QByteArray(reinterpret_cast(buffer), size).toHex(); + } + + if (env->ExceptionCheck()) + { + env->ExceptionDescribe(); + env->ExceptionClear(); + } + + return list; +} + + #endif diff --git a/src/global/BuildHelper.h b/src/global/BuildHelper.h index cd937c7..7f30357 100644 --- a/src/global/BuildHelper.h +++ b/src/global/BuildHelper.h @@ -8,31 +8,32 @@ #include +#ifdef Q_OS_ANDROID +#include +#endif + namespace governikus { class BuildHelper { private: - static const char* mDateTime; BuildHelper() = delete; ~BuildHelper() = delete; public: - static const char* getDateTime() - { - return mDateTime; - } + static const char* getDateTime(); #ifdef Q_OS_ANDROID static int getVersionCode(); static int getVersionCode(const QString& pPackageName); static QString getPackageName(); - + static QByteArrayList getAppCertificates(); + static QByteArrayList getAppCertificates(const QString& pPackageName); #endif }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/global/CMakeLists.txt b/src/global/CMakeLists.txt index 22e11b3..24ce167 100644 --- a/src/global/CMakeLists.txt +++ b/src/global/CMakeLists.txt @@ -1,3 +1,8 @@ +##################################################################### +# The module global is responsible for general utilities like +# logging or loader for resources and translations. +##################################################################### + ADD_PLATFORM_LIBRARY(AusweisAppGlobal) TARGET_LINK_LIBRARIES(AusweisAppGlobal Qt5::Core OpenSSL::Crypto ${OSX_SECURITY}) diff --git a/src/global/CardReturnCode.cpp b/src/global/CardReturnCode.cpp index 788cabc..b05aa44 100644 --- a/src/global/CardReturnCode.cpp +++ b/src/global/CardReturnCode.cpp @@ -3,18 +3,24 @@ */ #include "CardReturnCode.h" + +#include "Initializer.h" #include "moc_CardReturnCode.cpp" -#include "GlobalStatus.h" - using namespace governikus; +static Initializer::Entry X([] { + qRegisterMetaType("CardReturnCode"); + }); + + GlobalStatus CardReturnCodeUtil::toGlobalStatus(CardReturnCode pCode) { switch (pCode) { case CardReturnCode::OK: + case CardReturnCode::OK_PUK: return GlobalStatus::Code::No_Error; case CardReturnCode::UNDEFINED: @@ -22,15 +28,18 @@ GlobalStatus CardReturnCodeUtil::toGlobalStatus(CardReturnCode pCode) return GlobalStatus::Code::Unknown_Error; case CardReturnCode::CARD_NOT_FOUND: + case CardReturnCode::RETRY_ALLOWED: return GlobalStatus::Code::Card_Not_Found; case CardReturnCode::COMMAND_FAILED: return GlobalStatus::Code::Card_Communication_Error; case CardReturnCode::PROTOCOL_ERROR: - case CardReturnCode::UNEXPECTED_TRANSMIT_STATUS: return GlobalStatus::Code::Card_Protocol_Error; + case CardReturnCode::UNEXPECTED_TRANSMIT_STATUS: + return GlobalStatus::Code::Card_Unexpected_Transmit_Status; + case CardReturnCode::CANCELLATION_BY_USER: return GlobalStatus::Code::Card_Cancellation_By_User; @@ -38,6 +47,8 @@ GlobalStatus CardReturnCodeUtil::toGlobalStatus(CardReturnCode pCode) return GlobalStatus::Code::Card_Input_TimeOut; case CardReturnCode::INVALID_PIN: + case CardReturnCode::INVALID_PIN_2: + case CardReturnCode::INVALID_PIN_3: return GlobalStatus::Code::Card_Invalid_Pin; case CardReturnCode::INVALID_CAN: @@ -64,3 +75,38 @@ GlobalStatus CardReturnCodeUtil::toGlobalStatus(CardReturnCode pCode) Q_UNREACHABLE(); } + + +bool CardReturnCodeUtil::equalsWrongPacePassword(CardReturnCode pCode) +{ + switch (pCode) + { + case CardReturnCode::INVALID_CAN: + case CardReturnCode::INVALID_PIN: + case CardReturnCode::INVALID_PIN_2: + case CardReturnCode::INVALID_PIN_3: + case CardReturnCode::INVALID_PUK: + case CardReturnCode::NEW_PIN_MISMATCH: + case CardReturnCode::NEW_PIN_INVALID_LENGTH: + case CardReturnCode::PIN_NOT_BLOCKED: + case CardReturnCode::PIN_BLOCKED: + return true; + + case CardReturnCode::UNDEFINED: + case CardReturnCode::RETRY_ALLOWED: + case CardReturnCode::CARD_NOT_FOUND: + case CardReturnCode::UNKNOWN: + case CardReturnCode::COMMAND_FAILED: + case CardReturnCode::PROTOCOL_ERROR: + case CardReturnCode::UNEXPECTED_TRANSMIT_STATUS: + case CardReturnCode::OK: + case CardReturnCode::OK_PUK: + case CardReturnCode::CANCELLATION_BY_USER: + case CardReturnCode::PUK_INOPERATIVE: + case CardReturnCode::INPUT_TIME_OUT: + return false; + } + + Q_UNREACHABLE(); + return true; +} diff --git a/src/global/CardReturnCode.h b/src/global/CardReturnCode.h index 3a9d91c..1c6fb9f 100644 --- a/src/global/CardReturnCode.h +++ b/src/global/CardReturnCode.h @@ -7,21 +7,23 @@ #pragma once #include "EnumHelper.h" +#include "GlobalStatus.h" namespace governikus { -class GlobalStatus; - - defineEnumType(CardReturnCode, UNDEFINED, OK, + OK_PUK, + RETRY_ALLOWED, CARD_NOT_FOUND, UNKNOWN, INPUT_TIME_OUT, INVALID_CAN, INVALID_PIN, + INVALID_PIN_2, + INVALID_PIN_3, INVALID_PUK, COMMAND_FAILED, CANCELLATION_BY_USER, @@ -39,12 +41,12 @@ class CardReturnCodeUtil private: CardReturnCodeUtil() { - } public: static GlobalStatus toGlobalStatus(CardReturnCode pCode); + static bool equalsWrongPacePassword(CardReturnCode pCode); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/global/DeviceError.cpp b/src/global/DeviceError.cpp deleted file mode 100644 index 258cc39..0000000 --- a/src/global/DeviceError.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "DeviceError.h" -#include "moc_DeviceError.cpp" - -#include "GlobalStatus.h" - -namespace governikus -{ - -static int registerDeviceError = qRegisterMetaType("DeviceError"); - -namespace DeviceErrorUtil -{ -GlobalStatus toGlobalStatus(DeviceError pDeviceError) -{ - Q_UNUSED(registerDeviceError); - - switch (pDeviceError) - { - case DeviceError::DEVICE_CONNECTION_ERROR: - return GlobalStatus::Code::Workflow_Reader_Device_Connection_Error; - - case DeviceError::DEVICE_POWERED_OFF: - return GlobalStatus::Code::No_Error; - - case DeviceError::DEVICE_SCAN_ERROR: - return GlobalStatus::Code::Workflow_Reader_Device_Scan_Error; - - case DeviceError::UNKNOWN_ERROR: - return GlobalStatus::Code::Unknown_Error; - } - - Q_UNREACHABLE(); -} - - -} -} diff --git a/src/global/DeviceError.h b/src/global/DeviceError.h deleted file mode 100644 index 23108a9..0000000 --- a/src/global/DeviceError.h +++ /dev/null @@ -1,28 +0,0 @@ -/*! - * \brief Global definitions for device error codes. - * - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "EnumHelper.h" - -namespace governikus -{ - -class GlobalStatus; - - -defineEnumType(DeviceError, - UNKNOWN_ERROR, - DEVICE_CONNECTION_ERROR, - DEVICE_POWERED_OFF, - DEVICE_SCAN_ERROR) - -namespace DeviceErrorUtil -{ -GlobalStatus toGlobalStatus(DeviceError pDeviceError); -} /* namespace DeviceErrorUtil */ - -} /* namespace governikus */ diff --git a/src/global/DeviceInfo.cpp b/src/global/DeviceInfo.cpp index 4d7492c..78c76bc 100644 --- a/src/global/DeviceInfo.cpp +++ b/src/global/DeviceInfo.cpp @@ -46,7 +46,6 @@ QString DeviceInfo::getPrettyInfo() QString DeviceInfo::getName() { - #ifdef Q_OS_ANDROID return getField("MODEL"); @@ -67,3 +66,63 @@ QString DeviceInfo::getFingerprint() #endif } + + +QString DeviceInfo::getOSBuildNumber() +{ +#ifdef Q_OS_ANDROID + return getField("DISPLAY"); + +#else + return QString(); + +#endif +} + + +QString DeviceInfo::getOSVersion() +{ + return QSysInfo::productVersion(); +} + + +QString DeviceInfo::getKernelVersion() +{ + return QSysInfo::kernelVersion(); +} + + +QString DeviceInfo::getVendor() +{ +#ifdef Q_OS_ANDROID + return getField("MANUFACTURER"); + +#else + return QString(); + +#endif +} + + +QString DeviceInfo::getModelNumber() +{ +#ifdef Q_OS_ANDROID + return getField("MODEL"); + +#else + return QString(); + +#endif +} + + +QString DeviceInfo::getModelName() +{ +#ifdef Q_OS_ANDROID + return getField("PRODUCT"); + +#else + return QString(); + +#endif +} diff --git a/src/global/DeviceInfo.h b/src/global/DeviceInfo.h index 6521c10..02a46c1 100644 --- a/src/global/DeviceInfo.h +++ b/src/global/DeviceInfo.h @@ -27,6 +27,12 @@ class DeviceInfo static QString getPrettyInfo(); static QString getName(); static QString getFingerprint(); + static QString getOSBuildNumber(); + static QString getOSVersion(); + static QString getKernelVersion(); + static QString getVendor(); + static QString getModelNumber(); + static QString getModelName(); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/global/ECardApiResult.cpp b/src/global/ECardApiResult.cpp new file mode 100644 index 0000000..efea780 --- /dev/null +++ b/src/global/ECardApiResult.cpp @@ -0,0 +1,641 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "ECardApiResult.h" + +#include "LanguageLoader.h" + +#include +#include + +using namespace governikus; + +#define RESULTMAJOR "http://www.bsi.bund.de/ecard/api/1.1/resultmajor" +#define RESULTMINOR "http://www.bsi.bund.de/ecard/api/1.1/resultminor" + +const QMap ECardApiResult::cMajorResults = { + {ECardApiResult::Major::Ok, QString::fromLatin1(RESULTMAJOR "#ok")}, + {ECardApiResult::Major::Warning, QString::fromLatin1(RESULTMAJOR "#warning")}, + {ECardApiResult::Major::Error, QString::fromLatin1(RESULTMAJOR "#error")} +}; + +// See TR-03112, section 4.2 for details about the codes +// AL -> Applicaction Layer +// DP -> Dispatcher +// IFDL -> Interface Device Layer +// IL -> Identify Layer +// KEY +// SAL -> Service Access Layer +const QMap ECardApiResult::cMinorResults = { + {ECardApiResult::Minor::AL_Unknown_Error, QString::fromLatin1(RESULTMINOR "/al/common#unknownError")}, + {ECardApiResult::Minor::AL_No_Permission, QString::fromLatin1(RESULTMINOR "/al/common#noPermission")}, + {ECardApiResult::Minor::AL_Internal_Error, QString::fromLatin1(RESULTMINOR "/al/common#internalError")}, + {ECardApiResult::Minor::AL_Parameter_Error, QString::fromLatin1(RESULTMINOR "/al/common#parameterError")}, + {ECardApiResult::Minor::AL_Unkown_API_Function, QString::fromLatin1(RESULTMINOR "/al/common#unknownAPIFunction")}, + {ECardApiResult::Minor::AL_Not_Initialized, QString::fromLatin1(RESULTMINOR "/al/common#notInitialized")}, + {ECardApiResult::Minor::AL_Warning_Connection_Disconnected, QString::fromLatin1(RESULTMINOR "/al/common#warningConnectionDisconnected")}, + {ECardApiResult::Minor::AL_Session_Terminated_Warning, QString::fromLatin1(RESULTMINOR "/al/common#SessionTerminatedWarning")}, + {ECardApiResult::Minor::AL_Communication_Error, QString::fromLatin1(RESULTMINOR "/al/common#communicationError")}, + {ECardApiResult::Minor::DP_Timeout_Error, QString::fromLatin1(RESULTMINOR "/dp#timeout")}, + {ECardApiResult::Minor::DP_Unknown_Channel_Handle, QString::fromLatin1(RESULTMINOR "/dp#unknownChannelHandle")}, + {ECardApiResult::Minor::DP_Communication_Error, QString::fromLatin1(RESULTMINOR "/dp#communicationError")}, + {ECardApiResult::Minor::DP_Trusted_Channel_Establishment_Failed, QString::fromLatin1(RESULTMINOR "/dp#trustedChannelEstablishmentFailed")}, + {ECardApiResult::Minor::DP_Unknown_Protocol, QString::fromLatin1(RESULTMINOR "/dp#unknownProtocol")}, + {ECardApiResult::Minor::DP_Unknown_Cipher_Suite, QString::fromLatin1(RESULTMINOR "/dp#unknownCipherSuite")}, + {ECardApiResult::Minor::DP_Unknown_Webservice_Binding, QString::fromLatin1(RESULTMINOR "/dp#unknownWebserviceBinding")}, + {ECardApiResult::Minor::DP_Node_Not_Reachable, QString::fromLatin1(RESULTMINOR "/dp#nodeNotReachable")}, + {ECardApiResult::Minor::IFDL_Timeout_Error, QString::fromLatin1(RESULTMINOR "/ifdl/common#timeoutError")}, + {ECardApiResult::Minor::IFDL_UnknownSlot, QString::fromLatin1(RESULTMINOR "/ifdl/terminal#unknownSlot")}, + {ECardApiResult::Minor::IFDL_InvalidSlotHandle, QString::fromLatin1(RESULTMINOR "/ifdl/common#invalidSlotHandle")}, + {ECardApiResult::Minor::IFDL_CancellationByUser, QString::fromLatin1(RESULTMINOR "/ifdl#cancellationByUser")}, + {ECardApiResult::Minor::IFDL_IFD_SharingViolation, QString::fromLatin1(RESULTMINOR "ifdl/terminal#IFDSharingViolation")}, + {ECardApiResult::Minor::IFDL_Terminal_NoCard, QString::fromLatin1(RESULTMINOR "/ifdl/terminal#noCard")}, + {ECardApiResult::Minor::IFDL_IO_RepeatedDataMismatch, QString::fromLatin1(RESULTMINOR "/ifdl/IO#repeatedDataMismatch")}, + {ECardApiResult::Minor::IFDL_IO_UnknownPINFormat, QString::fromLatin1(RESULTMINOR "/ifdl/IO#unknownPINFormat")}, + {ECardApiResult::Minor::IL_Signature_InvalidCertificatePath, QString::fromLatin1(RESULTMINOR "/il/signature#invalidCertificatePath")}, + {ECardApiResult::Minor::KEY_KeyGenerationNotPossible, QString::fromLatin1(RESULTMINOR "/il/key#keyGenerationNotPossible")}, + {ECardApiResult::Minor::SAL_Cancellation_by_User, QString::fromLatin1(RESULTMINOR "/sal#cancellationByUser")}, + {ECardApiResult::Minor::SAL_Invalid_Key, QString::fromLatin1(RESULTMINOR "/sal#invalidKey")}, + {ECardApiResult::Minor::SAL_SecurityConditionNotSatisfied, QString::fromLatin1(RESULTMINOR "/sal#securityConditionNotSatisfied")}, + {ECardApiResult::Minor::SAL_MEAC_AgeVerificationFailedWarning, QString::fromLatin1(RESULTMINOR "/sal/mEAC#AgeVerificationFailedWarning")}, + {ECardApiResult::Minor::SAL_MEAC_CommunityVerificationFailedWarning, QString::fromLatin1(RESULTMINOR "/sal/mEAC#CommunityVerificationFailedWarning")}, + {ECardApiResult::Minor::SAL_MEAC_DocumentValidityVerificationFailed, QString::fromLatin1(RESULTMINOR "/sal/mEAC#DocumentValidityVerificationFailed")}, +}; + +QMap ECardApiResult::cConversionMap1 = {}; +QMap ECardApiResult::cConversionMap2 = {}; + + +ECardApiResult ECardApiResult::fromStatus(const GlobalStatus& pStatus) +{ + if (pStatus.getStatusCode() == GlobalStatus::Code::No_Error || pStatus.getStatusCode() == GlobalStatus::Code::RemoteReader_CloseCode_NormalClose) + { + return createOk(); + } + + const QString& message = pStatus.toErrorDescription(); + const Minor minor = fromStatus((pStatus.getStatusCode())); + const Origin status = fromStatus(pStatus.getOrigin()); + + return ECardApiResult(Major::Error, minor, message, status); +} + + +ECardApiResult ECardApiResult::createOk() +{ + return ECardApiResult(Major::Ok, Minor::null); +} + + +void ECardApiResult::initConversionMaps() +{ + if (!cConversionMap1.isEmpty() || !cConversionMap2.isEmpty()) + { + return; + } + + addConversionElement(GlobalStatus::Code::Paos_Error_AL_Unknown_Error, Minor::AL_Unknown_Error); + addConversionElement(GlobalStatus::Code::Paos_Unexpected_Warning, Minor::AL_Unknown_Error); + addConversionElement(GlobalStatus::Code::Unknown_Error, Minor::AL_Unknown_Error); + addConversionElement(GlobalStatus::Code::Card_Unexpected_Transmit_Status, Minor::AL_Unknown_Error); + + addConversionElement(GlobalStatus::Code::No_Error, Minor::null); + addConversionElement(GlobalStatus::Code::RemoteReader_CloseCode_NormalClose, Minor::null); + + addConversionElement(GlobalStatus::Code::Workflow_Card_Removed, Minor::IFDL_CancellationByUser); + + addConversionElement(GlobalStatus::Code::Workflow_No_Unique_DvCvc, ECardApiResult::Minor::IL_Signature_InvalidCertificatePath); + addConversionElement(GlobalStatus::Code::Workflow_No_Unique_AtCvc, ECardApiResult::Minor::IL_Signature_InvalidCertificatePath); + addConversionElement(GlobalStatus::Code::Workflow_Preverification_Error, ECardApiResult::Minor::IL_Signature_InvalidCertificatePath); + addConversionElement(GlobalStatus::Code::Workflow_Preverification_Developermode_Error, ECardApiResult::Minor::IL_Signature_InvalidCertificatePath); + + addConversionElement(GlobalStatus::Code::Workflow_Certificate_No_Description, Minor::AL_Parameter_Error); + addConversionElement(GlobalStatus::Code::Workflow_Certificate_No_Url_In_Description, Minor::AL_Parameter_Error); + addConversionElement(GlobalStatus::Code::Workflow_Certificate_Hash_Error, Minor::AL_Parameter_Error); + addConversionElement(GlobalStatus::Code::Workflow_Certificate_Sop_Error, Minor::AL_Parameter_Error); + + addConversionElement(GlobalStatus::Code::Paos_Error_DP_Trusted_Channel_Establishment_Failed, Minor::DP_Trusted_Channel_Establishment_Failed); + addConversionElement(GlobalStatus::Code::Workflow_TrustedChannel_Establishment_Error, Minor::DP_Trusted_Channel_Establishment_Failed); + addConversionElement(GlobalStatus::Code::Workflow_TrustedChannel_Error_From_Server, Minor::DP_Trusted_Channel_Establishment_Failed); + addConversionElement(GlobalStatus::Code::Workflow_TrustedChannel_Hash_Not_In_Description, Minor::DP_Trusted_Channel_Establishment_Failed); + addConversionElement(GlobalStatus::Code::Workflow_TrustedChannel_No_Data_Received, Minor::DP_Trusted_Channel_Establishment_Failed); + addConversionElement(GlobalStatus::Code::Workflow_TrustedChannel_Ssl_Certificate_Unsupported_Algorithm_Or_Length, Minor::DP_Trusted_Channel_Establishment_Failed); + addConversionElement(GlobalStatus::Code::Workflow_TrustedChannel_ServiceUnavailable, Minor::DP_Trusted_Channel_Establishment_Failed); + addConversionElement(GlobalStatus::Code::Workflow_TrustedChannel_TimeOut, Minor::DP_Trusted_Channel_Establishment_Failed); + addConversionElement(GlobalStatus::Code::Workflow_TrustedChannel_Proxy_Error, Minor::DP_Trusted_Channel_Establishment_Failed); + addConversionElement(GlobalStatus::Code::Workflow_TrustedChannel_Ssl_Establishment_Error, Minor::DP_Trusted_Channel_Establishment_Failed); + addConversionElement(GlobalStatus::Code::Workflow_TrustedChannel_Server_Format_Error, Minor::DP_Trusted_Channel_Establishment_Failed); + addConversionElement(GlobalStatus::Code::Workflow_TrustedChannel_Other_Network_Error, Minor::DP_Trusted_Channel_Establishment_Failed); + + addConversionElement(GlobalStatus::Code::Paos_Error_SAL_Cancellation_by_User, Minor::SAL_Cancellation_by_User); + addConversionElement(GlobalStatus::Code::Workflow_Cancellation_By_User, Minor::SAL_Cancellation_by_User); + addConversionElement(GlobalStatus::Code::Card_Cancellation_By_User, Minor::SAL_Cancellation_by_User); + + addConversionElement(GlobalStatus::Code::Workflow_No_Permission_Error, Minor::AL_No_Permission); + + addConversionElement(GlobalStatus::Code::Paos_Error_AL_Communication_Error, Minor::AL_Communication_Error); + addConversionElement(GlobalStatus::Code::Workflow_Communication_Missing_Redirect_Url, Minor::AL_Communication_Error); + addConversionElement(GlobalStatus::Code::Workflow_Error_Page_Transmission_Error, Minor::AL_Communication_Error); + addConversionElement(GlobalStatus::Code::Workflow_Redirect_Transmission_Error, Minor::AL_Communication_Error); + addConversionElement(GlobalStatus::Code::Workflow_Processing_Error, Minor::AL_Communication_Error); + addConversionElement(GlobalStatus::Code::Workflow_Reader_Became_Inaccessible, Minor::AL_Communication_Error); + addConversionElement(GlobalStatus::Code::Workflow_Reader_Communication_Error, Minor::AL_Communication_Error); + addConversionElement(GlobalStatus::Code::Workflow_Reader_Device_Connection_Error, Minor::AL_Communication_Error); + addConversionElement(GlobalStatus::Code::Workflow_Reader_Device_Scan_Error, Minor::AL_Communication_Error); + addConversionElement(GlobalStatus::Code::Workflow_Server_Incomplete_Information_Provided, Minor::AL_Communication_Error); + addConversionElement(GlobalStatus::Code::Network_Ssl_Establishment_Error, Minor::AL_Communication_Error); + addConversionElement(GlobalStatus::Code::Workflow_Network_Ssl_Connection_Unsupported_Algorithm_Or_Length, Minor::AL_Communication_Error); + addConversionElement(GlobalStatus::Code::Workflow_Network_Ssl_Certificate_Unsupported_Algorithm_Or_Length, Minor::AL_Communication_Error); + addConversionElement(GlobalStatus::Code::Workflow_Nerwork_Ssl_Hash_Not_In_Certificate_Description, Minor::AL_Communication_Error); + addConversionElement(GlobalStatus::Code::Workflow_Network_Empty_Redirect_Url, Minor::AL_Communication_Error); + addConversionElement(GlobalStatus::Code::Workflow_Network_Expected_Redirect, Minor::AL_Communication_Error); + addConversionElement(GlobalStatus::Code::Workflow_Network_Invalid_Scheme, Minor::AL_Communication_Error); + addConversionElement(GlobalStatus::Code::Workflow_Network_Malformed_Redirect_Url, Minor::AL_Communication_Error); + addConversionElement(GlobalStatus::Code::Network_ServiceUnavailable, Minor::AL_Communication_Error); + addConversionElement(GlobalStatus::Code::Network_TimeOut, Minor::AL_Communication_Error); + addConversionElement(GlobalStatus::Code::Network_Proxy_Error, Minor::AL_Communication_Error); + addConversionElement(GlobalStatus::Code::Network_Other_Error, Minor::AL_Communication_Error); + addConversionElement(GlobalStatus::Code::Workflow_Wrong_Parameter_Invocation, Minor::AL_Communication_Error); + addConversionElement(GlobalStatus::Code::Card_Invalid_Pin, Minor::AL_Communication_Error); + addConversionElement(GlobalStatus::Code::Card_Invalid_Can, Minor::AL_Communication_Error); + addConversionElement(GlobalStatus::Code::Card_Invalid_Puk, Minor::AL_Communication_Error); + addConversionElement(GlobalStatus::Code::Card_Puk_Blocked, Minor::AL_Communication_Error); + + addConversionElement(GlobalStatus::Code::Card_NewPin_Invalid_Length, Minor::AL_Unknown_Error); + addConversionElement(GlobalStatus::Code::Workflow_AlreadyInProgress_Error, Minor::AL_Unknown_Error); + addConversionElement(GlobalStatus::Code::Card_Not_Found, Minor::AL_Unknown_Error); + addConversionElement(GlobalStatus::Code::Card_Communication_Error, Minor::AL_Unknown_Error); + addConversionElement(GlobalStatus::Code::Card_Input_TimeOut, Minor::AL_Unknown_Error); + addConversionElement(GlobalStatus::Code::Card_Pin_Blocked, Minor::AL_Unknown_Error); + addConversionElement(GlobalStatus::Code::Card_Pin_Not_Blocked, Minor::AL_Unknown_Error); + addConversionElement(GlobalStatus::Code::Card_NewPin_Mismatch, Minor::AL_Unknown_Error); + + addConversionElement(GlobalStatus::Code::Paos_Error_AL_Internal_Error, Minor::AL_Internal_Error); + addConversionElement(GlobalStatus::Code::Workflow_Cannot_Confirm_IdCard_Authenticity, Minor::AL_Internal_Error); + addConversionElement(GlobalStatus::Code::Workflow_Unknown_Paos_Form_EidServer, Minor::AL_Internal_Error); + addConversionElement(GlobalStatus::Code::Workflow_Unexpected_Message_From_EidServer, Minor::AL_Internal_Error); + addConversionElement(GlobalStatus::Code::Workflow_Pin_Blocked_And_Puk_Objectionable, Minor::AL_Internal_Error); + addConversionElement(GlobalStatus::Code::Card_Protocol_Error, Minor::AL_Internal_Error); + + addConversionElement(GlobalStatus::Code::Paos_Generic_Server_Error, Minor::AL_Unkown_API_Function); + addConversionElement(GlobalStatus::Code::Paos_Generic_Server_Error, Minor::AL_Not_Initialized); + addConversionElement(GlobalStatus::Code::Paos_Generic_Server_Error, Minor::AL_Warning_Connection_Disconnected); + addConversionElement(GlobalStatus::Code::Paos_Generic_Server_Error, Minor::AL_Session_Terminated_Warning); + addConversionElement(GlobalStatus::Code::Paos_Generic_Server_Error, Minor::DP_Timeout_Error); + addConversionElement(GlobalStatus::Code::Paos_Generic_Server_Error, Minor::DP_Unknown_Channel_Handle); + addConversionElement(GlobalStatus::Code::Paos_Generic_Server_Error, Minor::DP_Communication_Error); + addConversionElement(GlobalStatus::Code::Paos_Generic_Server_Error, Minor::DP_Unknown_Protocol); + addConversionElement(GlobalStatus::Code::Paos_Generic_Server_Error, Minor::DP_Unknown_Cipher_Suite); + addConversionElement(GlobalStatus::Code::Paos_Generic_Server_Error, Minor::DP_Unknown_Webservice_Binding); + addConversionElement(GlobalStatus::Code::Paos_Generic_Server_Error, Minor::DP_Node_Not_Reachable); + addConversionElement(GlobalStatus::Code::Paos_Generic_Server_Error, Minor::IFDL_Timeout_Error); + addConversionElement(GlobalStatus::Code::Paos_Generic_Server_Error, Minor::IFDL_UnknownSlot); + addConversionElement(GlobalStatus::Code::Paos_Generic_Server_Error, Minor::IFDL_InvalidSlotHandle); + addConversionElement(GlobalStatus::Code::Paos_Generic_Server_Error, Minor::IFDL_IFD_SharingViolation); + addConversionElement(GlobalStatus::Code::Paos_Generic_Server_Error, Minor::IFDL_Terminal_NoCard); + addConversionElement(GlobalStatus::Code::Paos_Generic_Server_Error, Minor::IFDL_IO_RepeatedDataMismatch); + addConversionElement(GlobalStatus::Code::Paos_Generic_Server_Error, Minor::IFDL_IO_UnknownPINFormat); + addConversionElement(GlobalStatus::Code::Paos_Generic_Server_Error, Minor::KEY_KeyGenerationNotPossible); + addConversionElement(GlobalStatus::Code::Paos_Generic_Server_Error, Minor::IL_Signature_InvalidCertificatePath); + addConversionElement(GlobalStatus::Code::Paos_Generic_Server_Error, Minor::SAL_SecurityConditionNotSatisfied); + addConversionElement(GlobalStatus::Code::Paos_Generic_Server_Error, Minor::SAL_MEAC_AgeVerificationFailedWarning); + addConversionElement(GlobalStatus::Code::Paos_Generic_Server_Error, Minor::SAL_MEAC_CommunityVerificationFailedWarning); + addConversionElement(GlobalStatus::Code::Paos_Generic_Server_Error, Minor::SAL_MEAC_DocumentValidityVerificationFailed); + + addConversionElement(GlobalStatus::Code::Paos_Error_SAL_Invalid_Key, Minor::SAL_Invalid_Key); + + addConversionElement(GlobalStatus::Code::Downloader_Data_Corrupted, Minor::AL_Unknown_Error); + addConversionElement(GlobalStatus::Code::RemoteReader_CloseCode_AbnormalClose, Minor::AL_Unknown_Error); + addConversionElement(GlobalStatus::Code::RemoteReader_CloseCode_Undefined, Minor::AL_Unknown_Error); + addConversionElement(GlobalStatus::Code::RemoteConnector_InvalidRequest, Minor::AL_Unknown_Error); + addConversionElement(GlobalStatus::Code::RemoteConnector_EmptyPassword, Minor::AL_Unknown_Error); + addConversionElement(GlobalStatus::Code::RemoteConnector_NoSupportedApiLevel, Minor::AL_Unknown_Error); + addConversionElement(GlobalStatus::Code::RemoteConnector_ConnectionTimeout, Minor::AL_Unknown_Error); + addConversionElement(GlobalStatus::Code::RemoteConnector_ConnectionError, Minor::AL_Unknown_Error); + addConversionElement(GlobalStatus::Code::RemoteConnector_RemoteHostRefusedConnection, Minor::AL_Unknown_Error); + addConversionElement(GlobalStatus::Code::Downloader_File_Not_Found, Minor::AL_Unknown_Error); + addConversionElement(GlobalStatus::Code::Downloader_Cannot_Save_File, Minor::AL_Unknown_Error); +} + + +void ECardApiResult::addConversionElement(const GlobalStatus::Code pCode, const Minor pMinor) +{ + if (!cConversionMap1.contains(pCode)) + { + cConversionMap1[pCode] = pMinor; + } + + if (!cConversionMap2.contains(pMinor)) + { + cConversionMap2[pMinor] = pCode; + } +} + + +GlobalStatus::Code ECardApiResult::toStatus(const Minor pMinor) +{ + initConversionMaps(); + + if (!cConversionMap2.contains(pMinor)) + { + qCritical() << "Critical conversion missmatch for" << pMinor; + Q_ASSERT(false); + return GlobalStatus::Code::Unknown_Error; + } + + return cConversionMap2[pMinor]; +} + + +ECardApiResult::Minor ECardApiResult::fromStatus(const GlobalStatus::Code pCode) +{ + initConversionMaps(); + + if (!cConversionMap1.contains(pCode)) + { + qCritical() << "Critical conversion missmatch for" << pCode; + Q_ASSERT(false); + return Minor::AL_Unknown_Error; + } + + return cConversionMap1[pCode]; +} + + +GlobalStatus::Origin ECardApiResult::toStatus(const ECardApiResult::Origin pSelf) +{ + switch (pSelf) + { + case governikus::ECardApiResult::Origin::Client: + return governikus::GlobalStatus::Origin::Client; + + case governikus::ECardApiResult::Origin::Server: + return governikus::GlobalStatus::Origin::Server; + } + + Q_UNREACHABLE(); +} + + +ECardApiResult::Origin ECardApiResult::fromStatus(const GlobalStatus::Origin pSelf) +{ + switch (pSelf) + { + case governikus::GlobalStatus::Origin::Client: + return governikus::ECardApiResult::Origin::Client; + + case governikus::GlobalStatus::Origin::Server: + return governikus::ECardApiResult::Origin::Server; + } + + Q_UNREACHABLE(); +} + + +ECardApiResult::Major ECardApiResult::parseMajor(const QString& pMajor) +{ + for (auto iter = cMajorResults.cbegin(); iter != cMajorResults.cend(); ++iter) + { + if (pMajor == iter.value()) + { + return iter.key(); + } + } + + if (!pMajor.isEmpty()) + { + qWarning() << "Unknown ResultMajor:" << pMajor; + } + return Major::Unknown; +} + + +ECardApiResult::Minor ECardApiResult::parseMinor(const QString& pMinor) +{ + for (auto iter = cMinorResults.cbegin(); iter != cMinorResults.cend(); ++iter) + { + if (pMinor == iter.value()) + { + return iter.key(); + } + } + + if (!pMinor.isEmpty()) + { + qWarning() << "Unknown ResultMinor:" << pMinor; + } + + return Minor::null; +} + + +bool ECardApiResult::isMajor(const QString& major) +{ + return Major::Unknown != parseMajor(major); +} + + +bool ECardApiResult::isMinor(const QString& minor) +{ + return Minor::null != parseMinor(minor); +} + + +QString ECardApiResult::getMessage(Minor pMinor) +{ + switch (pMinor) + { + case Minor::AL_Unknown_Error: + return tr("An unexpected error has occurred during processing."); + + case Minor::AL_No_Permission: + return tr("Use of the function by the client application is not permitted."); + + case Minor::AL_Internal_Error: + return tr("An internal error has occurred during processing."); + + case Minor::AL_Parameter_Error: + return tr("There was some problem with a provided or omitted parameter."); + + case Minor::AL_Unkown_API_Function: + return tr("The API function is unknown."); + + case Minor::AL_Not_Initialized: + return tr("The framework or layer has not been initialized."); + + case Minor::AL_Warning_Connection_Disconnected: + return tr("The active session has been terminated."); + + case Minor::AL_Session_Terminated_Warning: + return tr("The active session has been terminated."); + + case Minor::AL_Communication_Error: + return tr("A Communication error occurred during processing."); + + case Minor::DP_Timeout_Error: + return tr("The operation was terminated as the set time was exceeded."); + + case Minor::DP_Unknown_Channel_Handle: + return tr("The operation was aborted as an invalid channel handle was used."); + + case Minor::DP_Communication_Error: + return tr("A Communication error occurred during processing."); + + case Minor::DP_Trusted_Channel_Establishment_Failed: + return tr("A trusted channel could not be opened."); + + case Minor::DP_Unknown_Protocol: + return tr("The operation was aborted as an unknown protocol was used."); + + case Minor::DP_Unknown_Cipher_Suite: + return tr("The operation was aborted as an unknown cipher suite was used."); + + case Minor::DP_Unknown_Webservice_Binding: + return tr("The operation was aborted as an unknown web service binding was used."); + + case Minor::DP_Node_Not_Reachable: + return tr("A Communication error occurred during processing."); + + case Minor::IFDL_Timeout_Error: + return tr("The operation was terminated as the set time was exceeded."); + + case Minor::IFDL_Terminal_NoCard: + return tr("The card is missing or was removed."); + + case Minor::IFDL_IO_RepeatedDataMismatch: + return tr("The new PIN and the confirmation do not match."); + + case Minor::IFDL_IO_UnknownPINFormat: + return tr("The format of the PIN is wrong."); + + case Minor::KEY_KeyGenerationNotPossible: + return tr("Signature certificate key generation is not possible."); + + case Minor::SAL_Cancellation_by_User: + return tr("The process was cancelled by the user."); + + case Minor::IL_Signature_InvalidCertificatePath: + return tr("One or more certificate checks failed. The operation will be aborted due to security reasons."); + + case Minor::SAL_Invalid_Key: + return tr("This action cannot be performed. The online identification function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the online identification function."); + + case Minor::SAL_SecurityConditionNotSatisfied: + return tr("The authenticity of your ID card could not be verified. Please make sure that you are using a genuine ID card. Please note that test applications require the use of a test ID card."); + + case Minor::SAL_MEAC_AgeVerificationFailedWarning: + return tr("The age verification failed."); + + case Minor::SAL_MEAC_CommunityVerificationFailedWarning: + return tr("The community verification failed."); + + case Minor::SAL_MEAC_DocumentValidityVerificationFailed: + return tr("The ID card is invalid or disabled."); + + case Minor::null: + default: + return QString(); + } +} + + +ECardApiResult::ResultData::ResultData(Major pMajor, Minor pMinor, const QString& pMessage, Origin pOrigin) + : QSharedData() + , mMajor(pMajor) + , mMinor(pMinor) + , mMessage(pMessage) + , mMessageLang(LanguageLoader::getInstance().getUsedLocale().bcp47Name().left(2)) + , mOrigin(pOrigin) +{ +} + + +ECardApiResult::ECardApiResult(Major pMajor, Minor pMinor, const QString& pMessage, Origin pOrigin) + : d(new ResultData(pMajor, pMinor, pMessage, pOrigin)) +{ + const bool nullWithoutOk = pMajor != Major::Ok && pMinor == Minor::null; + const bool okWithoutNull = pMajor == Major::Ok && pMinor != Minor::null; + + if (nullWithoutOk || okWithoutNull) + { + qCritical() << "Invalid combination of Major(" << pMajor << ") and Minor (" << pMinor << ") supplied."; + d = new ResultData(Major::Error, Minor::AL_Internal_Error, pMessage, pOrigin); + } +} + + +ECardApiResult::ECardApiResult(const QString& pMajor, const QString& pMinor, const QString& pMessage, Origin pOrigin) + : d(new ResultData(parseMajor(pMajor), parseMinor(pMinor), pMessage, pOrigin)) +{ + // No parameter sanitization here, since we receive values from remote hosts. +} + + +ECardApiResult::ECardApiResult(const GlobalStatus& pStatus) + : ECardApiResult(fromStatus(pStatus)) +{ +} + + +bool ECardApiResult::operator ==(const ECardApiResult& pResult) const +{ + return *d == *pResult.d; +} + + +ECardApiResult::Major ECardApiResult::getMajor() const +{ + return d->mMajor; +} + + +ECardApiResult::Minor ECardApiResult::getMinor() const +{ + return d->mMinor; +} + + +QString ECardApiResult::getMessage() const +{ + return d->mMessage; +} + + +const QString& ECardApiResult::getMessageLang() const +{ + return d->mMessageLang; +} + + +QString ECardApiResult::getMajorString(ECardApiResult::Major pMajor) +{ + return cMajorResults.value(pMajor); +} + + +QString ECardApiResult::getMinorString(ECardApiResult::Minor pMinor) +{ + return cMinorResults.value(pMinor); +} + + +QString ECardApiResult::getMajorString() const +{ + return getMajorString(d->mMajor); +} + + +QString ECardApiResult::getMinorString() const +{ + return getMinorString(d->mMinor); +} + + +bool ECardApiResult::isValid() const +{ + switch (d->mMajor) + { + case Major::Unknown: + return false; + + case Major::Ok: + return d->mMinor == Minor::null; + + default: + return d->mMinor != Minor::null; + } +} + + +bool ECardApiResult::isOk() const +{ + return d->mMajor == Major::Ok && d->mMinor == Minor::null; +} + + +bool ECardApiResult::isOriginServer() const +{ + return d->mOrigin == Origin::Server; +} + + +GlobalStatus ECardApiResult::toStatus() const +{ + QString message = getMessage(); + if (message.isEmpty() || isOriginServer()) + { + // if the message is not set, we clearly use the result minor + // if the server sent the message, it can be in any language, so we take the result minor + message = ECardApiResult::getMessage(getMinor()); + } + + switch (getMajor()) + { + case Major::Unknown: + return GlobalStatus(GlobalStatus::Code::Unknown_Error, message); + + case Major::Ok: + if (isOk()) + { + return GlobalStatus::Code::No_Error; + } + // FALLTHROUGH + + case Major::Error: + return GlobalStatus(toStatus(getMinor()), message, toStatus(d->mOrigin)); + + case Major::Warning: + return GlobalStatus(GlobalStatus::Code::Paos_Unexpected_Warning, message); + } + + Q_UNREACHABLE(); +} + + +governikus::ECardApiResult::operator GlobalStatus() const +{ + return toStatus(); +} + + +QJsonObject ECardApiResult::toJson() const +{ + QJsonObject obj; + + obj[QLatin1String("major")] = getMajorString(); + if (getMinor() != Minor::null) + { + obj[QLatin1String("minor")] = getMinorString(); + } + + const auto& message = getMessage(); + if (!message.isEmpty()) + { + obj[QLatin1String("message")] = message; + } + + const auto& minorDesc = ECardApiResult::getMessage(getMinor()); + if (!minorDesc.isEmpty()) + { + obj[QLatin1String("description")] = minorDesc; + } + + const auto& lang = getMessageLang(); + if (!lang.isEmpty() && (!message.isEmpty() || !minorDesc.isEmpty())) + { + obj[QLatin1String("language")] = lang; + } + + return obj; +} + + +QDebug operator <<(QDebug pDbg, const governikus::ECardApiResult& pResult) +{ + const QString string = pResult.getMajorString() % QLatin1String(" | ") % pResult.getMinorString() % QLatin1String(" | ") % pResult.getMessage(); + QDebugStateSaver saver(pDbg); + pDbg.space() << "Result:"; + pDbg.quote() << string; + return pDbg; +} diff --git a/src/global/ECardApiResult.h b/src/global/ECardApiResult.h new file mode 100644 index 0000000..b4b8f48 --- /dev/null +++ b/src/global/ECardApiResult.h @@ -0,0 +1,172 @@ +/*! + * \brief Status representation according to TR-03112-6 "eCard-API-Framework" + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "GlobalStatus.h" + +#include +#include +#include +#include +#include +#include + +class test_ECardApiResult; +class test_UrlUtil; + +namespace governikus +{ + +class ECardApiResult +{ + Q_GADGET + Q_DECLARE_TR_FUNCTIONS(governikus::ECardApiResult) + + friend class RemoteMessageResponse; + friend class StartPaosResponse; + friend class ::test_ECardApiResult; + friend class ::test_UrlUtil; + + public: + enum class Major + { + Unknown, + Ok, + Warning, + Error + }; + + enum class Minor + { + null, // Used by Major::Ok and undefined state only! + AL_Unknown_Error, + AL_No_Permission, + AL_Internal_Error, + AL_Parameter_Error, + AL_Unkown_API_Function, + AL_Not_Initialized, + AL_Warning_Connection_Disconnected, + AL_Session_Terminated_Warning, + AL_Communication_Error, + DP_Timeout_Error, + DP_Unknown_Channel_Handle, + DP_Communication_Error, + DP_Trusted_Channel_Establishment_Failed, + DP_Unknown_Protocol, + DP_Unknown_Cipher_Suite, + DP_Unknown_Webservice_Binding, + DP_Node_Not_Reachable, + IFDL_Timeout_Error, + IFDL_UnknownSlot, + IFDL_InvalidSlotHandle, + IFDL_CancellationByUser, + IFDL_IFD_SharingViolation, + IFDL_Terminal_NoCard, + IFDL_IO_RepeatedDataMismatch, + IFDL_IO_UnknownPINFormat, + IL_Signature_InvalidCertificatePath, + KEY_KeyGenerationNotPossible, + SAL_Cancellation_by_User, + SAL_Invalid_Key, + SAL_SecurityConditionNotSatisfied, + SAL_MEAC_AgeVerificationFailedWarning, + SAL_MEAC_CommunityVerificationFailedWarning, + SAL_MEAC_DocumentValidityVerificationFailed, + }; + + enum class Origin + { + Server, Client + }; + + Q_ENUM(Major) + Q_ENUM(Minor) + Q_ENUM(Origin) + + private: + class ResultData + : public QSharedData + { + public: + const Major mMajor; + const Minor mMinor; + const QString mMessage; + const QString mMessageLang; + const Origin mOrigin; + + ResultData(Major pMajor, Minor pMinor, const QString& pMessage, Origin pOrigin); + + bool operator ==(const ResultData& pOther) const + { + return mMajor == pOther.mMajor && + mMinor == pOther.mMinor && + mMessage == pOther.mMessage && + mMessageLang == pOther.mMessageLang && + mOrigin == pOther.mOrigin; + } + + + }; + + static const QMap cMajorResults; + static const QMap cMinorResults; + + static QMap cConversionMap1; + static QMap cConversionMap2; + + static void initConversionMaps(); + static void addConversionElement(const GlobalStatus::Code pCode, const Minor pMinor); + static GlobalStatus::Code toStatus(const Minor pMinor); + static Minor fromStatus(const GlobalStatus::Code pCode); + + static GlobalStatus::Origin toStatus(const governikus::ECardApiResult::Origin pSelf); + static ECardApiResult::Origin fromStatus(const GlobalStatus::Origin pSelf); + + static ECardApiResult fromStatus(const GlobalStatus& pStatus); + + static Major parseMajor(const QString& pMajor); + static Minor parseMinor(const QString& pMinor); + + QSharedDataPointer d; + + ECardApiResult(Major pMajor, Minor pMinor, const QString& pMessage = QString(), Origin pOrigin = Origin::Client); + ECardApiResult(const QString& pMajor, const QString& pMinor = QString(), const QString& pMessage = QString(), Origin pOrigin = Origin::Client); + + public: + ECardApiResult(const GlobalStatus& pStatus); + + bool operator ==(const ECardApiResult& pResult) const; + + static ECardApiResult createOk(); + + static bool isMajor(const QString& pMajor); + static bool isMinor(const QString& pMinor); + static QString getMessage(Minor pMinor); + + Major getMajor() const; + Minor getMinor() const; + QString getMessage() const; + const QString& getMessageLang() const; + + static QString getMajorString(Major pMajor); + static QString getMinorString(Minor pMinor); + + QString getMajorString() const; + QString getMinorString() const; + + bool isValid() const; + bool isOk() const; + bool isOriginServer() const; + QJsonObject toJson() const; + + GlobalStatus toStatus() const; + explicit operator GlobalStatus() const; +}; + +} // namespace governikus + +QDebug operator <<(QDebug pDbg, const governikus::ECardApiResult& pResult); diff --git a/src/global/EnumHelper.h b/src/global/EnumHelper.h index 8a13ff4..3437fae 100644 --- a/src/global/EnumHelper.h +++ b/src/global/EnumHelper.h @@ -14,7 +14,6 @@ namespace governikus { - #define defineEnumOperators(enumName)\ inline QDebug operator<<(QDebug pDbg, enumName pType)\ {\ @@ -123,7 +122,7 @@ template class Enum list.reserve(metaEnum.keyCount()); for (int i = 0; i < metaEnum.keyCount(); ++i) { - list.push_back(static_cast(metaEnum.value(i))); + list << static_cast(metaEnum.value(i)); } } return list; @@ -181,4 +180,4 @@ template inline QLatin1String getEnumName(T pType) } -} /* namespace governikus */ +} // namespace governikus diff --git a/src/global/Env.cpp b/src/global/Env.cpp index 5ffe7d9..d2e8689 100644 --- a/src/global/Env.cpp +++ b/src/global/Env.cpp @@ -6,18 +6,14 @@ #include "SingletonHelper.h" -#include - using namespace governikus; defineSingleton(Env) Env::Env() -{ -} - - -Env::~Env() +#ifndef QT_NO_DEBUG + : mLock(QReadWriteLock::Recursive) +#endif { } @@ -28,65 +24,6 @@ Env& Env::getInstance() } -void Env::storeSingleton(Identifier pId, void* pObject) -{ - if (pObject) - { - qDebug() << "Add instance:" << pId; - mInstancesOwnership.remove(pId); - mInstancesUnmanaged.insert(pId, pObject); - mTypeInfo.insert(pId, Type::UNMANAGED); - } - else - { - removeStoredSingleton(pId); - } -} - - -void Env::storeSingleton(Identifier pId, std::shared_ptr pObject) -{ - if (pObject) - { - qDebug() << "Add owned instance:" << pId; - mInstancesUnmanaged.remove(pId); - mInstancesOwnership.insert(pId, pObject); - mTypeInfo.insert(pId, Type::OWNERSHIP); - } - else - { - removeStoredSingleton(pId); - } -} - - -void Env::removeStoredSingleton(Identifier pId) -{ - qDebug() << "Remove instance:" << pId; - mInstancesOwnership.remove(pId); - mInstancesUnmanaged.remove(pId); - mTypeInfo.remove(pId); -} - - -void* Env::fetchStoredSingleton(Env::Identifier pId) const -{ - switch (mTypeInfo.value(pId, Type::UNDEFINED)) - { - case Type::OWNERSHIP: - return mInstancesOwnership.value(pId).get(); - - case Type::UNMANAGED: - return mInstancesUnmanaged.value(pId); - - case Type::UNDEFINED: - return nullptr; - } - - Q_UNREACHABLE(); -} - - #ifndef QT_NO_DEBUG void Env::resetCounter() @@ -101,44 +38,52 @@ void Env::resetCounter() void Env::clear() { auto& holder = getInstance(); - holder.mInstancesOwnership.clear(); - holder.mInstancesUnmanaged.clear(); - holder.mTypeInfo.clear(); + + const QWriteLocker locker(&holder.mLock); + holder.mInstancesSingleton.clear(); holder.mInstancesCreator.clear(); + + const QWriteLocker lockerShared(&holder.mSharedInstancesLock); holder.mSharedInstances.clear(); } void Env::set(const QMetaObject& pMetaObject, void* pObject) { - Identifier className = pMetaObject.className(); - Q_ASSERT_X(!QByteArray(className).toLower().contains("mock"), "test", "Do you really want to mock a mock?"); - getInstance().storeSingleton(className, pObject); + const Identifier id = pMetaObject.className(); + Q_ASSERT_X(!QByteArray(id).toLower().contains("mock"), "test", "Do you really want to mock a mock?"); -} + auto& holder = getInstance(); + const QWriteLocker locker(&holder.mLock); - -void Env::set(const QMetaObject& pMetaObject, std::shared_ptr pObject) -{ - Identifier className = pMetaObject.className(); - Q_ASSERT_X(!QByteArray(className).toLower().contains("mock"), "test", "Do you really want to mock a mock?"); - getInstance().storeSingleton(className, pObject); + if (pObject) + { + qDebug() << "Add mock:" << id; + holder.mInstancesSingleton.insert(id, pObject); + } + else + { + qDebug() << "Remove mock:" << id; + holder.mInstancesSingleton.remove(id); + } } void Env::setShared(const QMetaObject& pMetaObject, QSharedPointer pObject) { - Identifier className = pMetaObject.className(); - auto& holder = getInstance().mSharedInstances; + const Identifier className = pMetaObject.className(); + auto& holder = getInstance(); + const QWriteLocker locker(&holder.mSharedInstancesLock); + if (pObject) { - qDebug() << "Add shared instance:" << className; - holder.insert(className, pObject.toWeakRef()); + qDebug() << "Add shared mock:" << className; + holder.mSharedInstances.insert(className, pObject.toWeakRef()); } else { - qDebug() << "Remove shared instance:" << className; - holder.remove(className); + qDebug() << "Remove shared mock:" << className; + holder.mSharedInstances.remove(className); } } diff --git a/src/global/Env.h b/src/global/Env.h index 94257cf..378b577 100644 --- a/src/global/Env.h +++ b/src/global/Env.h @@ -7,13 +7,20 @@ #pragma once #include -#include +#include + +#include +#include #include #include #include #include +#include +#include #include +#include #include +#include #ifndef QT_NO_DEBUG #include @@ -25,14 +32,18 @@ class test_Env; namespace governikus { -template T* singleton(bool& pTakeOwnership); +template T* singleton(); template T createNewObject(Args&& ... pArgs); class Env { + public: + struct ThreadSafe {}; + private: friend class ::test_Env; Q_DISABLE_COPY(Env) + using Identifier = const char*; #ifndef QT_NO_DEBUG class FuncWrapperBase @@ -84,66 +95,57 @@ class Env }; - using Wrapper = std::shared_ptr; + using Wrapper = QSharedPointer; QVector mInstancesCreator; + QMap mInstancesSingleton; + mutable QReadWriteLock mLock; #endif - - enum class Type - { - UNDEFINED, - OWNERSHIP, - UNMANAGED - }; - - using Identifier = const char*; - QMap mTypeInfo; - QMap mInstancesUnmanaged; - QMap > mInstancesOwnership; QMap > mSharedInstances; + mutable QReadWriteLock mSharedInstancesLock; static Env& getInstance(); - void storeSingleton(Identifier pId, void* pObject); - void storeSingleton(Identifier pId, std::shared_ptr pObject); - void removeStoredSingleton(Identifier pId); - void* fetchStoredSingleton(Identifier pId) const; - template - typename std::enable_if::value && std::is_destructible::value, T*>::type storeSingleton(Identifier pId) + typename std::enable_if::value && std::is_destructible::value, T*>::type fetchRealSingleton() { static_assert(std::has_virtual_destructor::value, "Destructor must be virtual"); - - bool ownership = true; - T* obj = singleton(ownership); - Q_ASSERT(obj); - if (ownership) - { - storeSingleton(pId, std::shared_ptr(obj)); - } - else - { - storeSingleton(pId, obj); - } - return obj; + return singleton(); } template - typename std::enable_if::value, T*>::type storeSingleton(Identifier pId) + typename std::enable_if::value || !std::is_destructible::value, T*>::type fetchRealSingleton() { - T* obj = &T::getInstance(); - storeSingleton(pId, obj); - return obj; + return &T::getInstance(); } template - typename std::enable_if::value, T*>::type storeSingleton(Identifier pId) + #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + inline typename std::enable_if::IsRealGadget, T*>::type checkObjectInfo(Identifier pId, T* pObject) const + + #else + inline typename std::enable_if::Value, T*>::type checkObjectInfo(Identifier pId, T* pObject) const + #endif { - auto obj = std::make_shared(); - storeSingleton(pId, obj); - return obj.get(); + Q_UNUSED(pId); + return pObject; + } + + + template + inline typename std::enable_if::Value, T*>::type checkObjectInfo(Identifier pId, T* pObject) const + { + if (!std::is_base_of() && pObject->thread() != QThread::currentThread()) + { + qWarning() << pId << "was created in" << pObject->thread()->objectName() << "but is requested by" << QThread::currentThread()->objectName(); +#ifndef QT_NO_DEBUG + Q_ASSERT(QCoreApplication::applicationName().startsWith(QLatin1String("Test_global_Env"))); +#endif + } + + return pObject; } @@ -158,15 +160,16 @@ class Env "Singletons needs to be a Q_GADGET or an QObject/Q_OBJECT"); #endif - Identifier id = T::staticMetaObject.className(); - void* obj = fetchStoredSingleton(id); - + const Identifier id = T::staticMetaObject.className(); + void* obj = nullptr; +#ifndef QT_NO_DEBUG + const QReadLocker locker(&mLock); + obj = mInstancesSingleton.value(id); if (!obj) - { - obj = storeSingleton(id); - } - - return static_cast(obj); +#endif + obj = fetchRealSingleton(); + Q_ASSERT(obj); + return checkObjectInfo(id, static_cast(obj)); } @@ -206,12 +209,19 @@ class Env T createObject(Args&& ... pArgs) const { #ifndef QT_NO_DEBUG - for (auto& mock : qAsConst(mInstancesCreator)) { - auto creator = dynamic_cast*>(mock.get()); - if (creator) + QReadLocker locker(&mLock); + + // copy QSharedPointer "mock" to increase ref-counter. Otherwise + // unlock would allow to delete the wrapper. + for (auto mock : qAsConst(mInstancesCreator)) // clazy:exclude=range-loop { - return (*creator)(std::forward(pArgs) ...); + auto creator = mock.dynamicCast >(); + if (creator) + { + locker.unlock(); + return (*creator)(std::forward(pArgs) ...); + } } } #endif @@ -222,7 +232,7 @@ class Env protected: Env(); - ~Env(); + ~Env() = default; public: template @@ -250,14 +260,23 @@ class Env "Shared class needs to be a Q_GADGET or an QObject/Q_OBJECT"); #endif - auto& holder = getInstance().mSharedInstances; - const auto* className = T::staticMetaObject.className(); + const Identifier className = T::staticMetaObject.className(); + + auto& holder = getInstance(); + holder.mSharedInstancesLock.lockForRead(); + QSharedPointer shared = qSharedPointerCast(holder.mSharedInstances.value(className)); + holder.mSharedInstancesLock.unlock(); - QSharedPointer shared = qSharedPointerCast(holder.value(className)); if (!shared) { - shared = QSharedPointer::create(); - holder.insert(className, shared.toWeakRef()); + const QWriteLocker locker(&holder.mSharedInstancesLock); + shared = qSharedPointerCast(holder.mSharedInstances.value(className)); + if (!shared) + { + qDebug() << "Spawn shared instance:" << className; + shared = QSharedPointer::create(); + holder.mSharedInstances.insert(className, shared.toWeakRef()); + } } return shared; @@ -268,21 +287,16 @@ class Env static void resetCounter(); static void clear(); static void set(const QMetaObject& pMetaObject, void* pObject = nullptr); - static void set(const QMetaObject& pMetaObject, std::shared_ptr pObject); - - template - static U* getSingleton() - { - return dynamic_cast(getSingleton()); - } - template static int getCounter() { - for (const auto& mock : qAsConst(getInstance().mInstancesCreator)) + auto& holder = getInstance(); + const QReadLocker locker(&holder.mLock); + + for (const auto& mock : qAsConst(holder.mInstancesCreator)) { - if (dynamic_cast*>(mock.get())) + if (mock.dynamicCast >()) { return mock->getCounter(); } @@ -297,21 +311,23 @@ class Env { Q_ASSERT(pFunc); - auto& holder = getInstance().mInstancesCreator; - const auto& value = Wrapper(new FuncWrapper(pFunc)); + const auto& value = QSharedPointer >::create(pFunc); - QMutableVectorIterator iter(holder); + auto& holder = getInstance(); + const QWriteLocker locker(&holder.mLock); + + QMutableVectorIterator iter(holder.mInstancesCreator); while (iter.hasNext()) { iter.next(); - if (dynamic_cast*>(iter.value().get())) + if (iter.value().dynamicCast >()) { iter.setValue(value); return; } } - holder << value; + holder.mInstancesCreator << value; } @@ -320,4 +336,4 @@ class Env }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/global/FileDestination.h b/src/global/FileDestination.h index 023f40c..c6177ab 100644 --- a/src/global/FileDestination.h +++ b/src/global/FileDestination.h @@ -21,9 +21,12 @@ class FileDestination static QString getPath() { - #ifdef Q_OS_ANDROID + #if defined(Q_OS_ANDROID) return QStringLiteral("assets:"); + #elif defined(Q_OS_MACOS) && defined(QT_NO_DEBUG) + return QCoreApplication::applicationDirPath() + QStringLiteral("/../Resources"); + #else return QCoreApplication::applicationDirPath(); @@ -46,4 +49,4 @@ class FileDestination }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/global/FuncUtils.h b/src/global/FuncUtils.h index 8d7865f..8722e40 100644 --- a/src/global/FuncUtils.h +++ b/src/global/FuncUtils.h @@ -73,4 +73,4 @@ typename std::enable_if::value, QVector >::type filter(const } -} +} // namespace governikus diff --git a/src/global/GlobalStatus.cpp b/src/global/GlobalStatus.cpp index 6ae9e5f..1ee38bf 100644 --- a/src/global/GlobalStatus.cpp +++ b/src/global/GlobalStatus.cpp @@ -8,8 +8,6 @@ #include -#define MESSAGE_SORRY "Sorry, that should not have happened! Please contact the support team." - using namespace governikus; static Initializer::Entry E([] { @@ -24,9 +22,45 @@ const QString GlobalStatus::getExternalInfo(int pIndex) const } -QString GlobalStatus::maskMessage(const QString& pMessage, const bool pMask) +bool GlobalStatus::isMessageMasked() const { - return pMask ? tr("An error occurred. Please contact our support at AusweisApp2 Support.") : pMessage; + switch (d->mStatusCode) + { + case Code::Workflow_Unknown_Paos_Form_EidServer: + case Code::Workflow_Unexpected_Message_From_EidServer: + case Code::Workflow_Preverification_Error: + case Code::Workflow_No_Unique_AtCvc: + case Code::Workflow_No_Unique_DvCvc: + case Code::Workflow_Certificate_No_Description: + case Code::Workflow_Certificate_No_Url_In_Description: + case Code::Workflow_Certificate_Hash_Error: + case Code::Workflow_Certificate_Sop_Error: + case Code::Workflow_Error_Page_Transmission_Error: + case Code::Workflow_Processing_Error: + case Code::Workflow_Redirect_Transmission_Error: + case Code::Workflow_TrustedChannel_Establishment_Error: + case Code::Workflow_TrustedChannel_Error_From_Server: + case Code::Workflow_TrustedChannel_No_Data_Received: + case Code::Network_TimeOut: + case Code::Workflow_TrustedChannel_TimeOut: + case Code::Network_Proxy_Error: + case Code::Workflow_TrustedChannel_Proxy_Error: + case Code::Workflow_TrustedChannel_Server_Format_Error: + case Code::Network_Ssl_Establishment_Error: + case Code::Workflow_TrustedChannel_Ssl_Establishment_Error: + case Code::Workflow_Wrong_Parameter_Invocation: + case Code::Network_Other_Error: + case Code::Workflow_TrustedChannel_Other_Network_Error: + case Code::Workflow_Network_Ssl_Connection_Unsupported_Algorithm_Or_Length: + case Code::Workflow_Network_Empty_Redirect_Url: + case Code::Workflow_Network_Expected_Redirect: + case Code::Workflow_Network_Invalid_Scheme: + case Code::Workflow_Network_Malformed_Redirect_Url: + return true; + + default: + return false; + } } @@ -48,13 +82,23 @@ GlobalStatus::Code GlobalStatus::getStatusCode() const } -GlobalStatus::operator GlobalStatus::Code() const +QString GlobalStatus::toErrorDescription(const bool pSimplifiedVersion) const { - return getStatusCode(); + if (pSimplifiedVersion && isMessageMasked()) + { +#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) + return tr("An error occurred. Please contact our support at AusweisApp2 Support."); + +#else + return tr("An error occurred. Please contact our support at AusweisApp2 Support or feel free to send us an email at \">support@ausweisapp.de."); + +#endif + } + return toErrorDescriptionInternal(); } -QString GlobalStatus::toErrorDescription(const bool pSimplifiedVersion) const +QString GlobalStatus::toErrorDescriptionInternal() const { switch (d->mStatusCode) { @@ -77,10 +121,10 @@ QString GlobalStatus::toErrorDescription(const bool pSimplifiedVersion) const return tr("The authenticity of your ID card could not be confirmed."); case Code::Workflow_Unknown_Paos_Form_EidServer: - return maskMessage(tr("The program received an unknown message from the server."), pSimplifiedVersion); + return tr("The program received an unknown message from the server."); case Code::Workflow_Unexpected_Message_From_EidServer: - return maskMessage(tr("The program received an unexpected message from the server."), pSimplifiedVersion); + return tr("The program received an unexpected message from the server."); case Code::Workflow_Pin_Blocked_And_Puk_Objectionable: return tr("After three wrong entries your PIN is blocked. Please use the PIN management in this app to unblock it with the help of your PUK."); @@ -89,46 +133,46 @@ QString GlobalStatus::toErrorDescription(const bool pSimplifiedVersion) const return tr("Using the developer mode is only allowed in a test environment."); case Code::Workflow_Preverification_Error: - return maskMessage(tr("Pre-verification failed."), pSimplifiedVersion); + return tr("Pre-verification failed."); case Code::Workflow_No_Unique_AtCvc: - return maskMessage(tr("No unique AT CVC"), pSimplifiedVersion); + return tr("No unique AT CVC"); case Code::Workflow_No_Unique_DvCvc: - return maskMessage(tr("No unique DV CVC"), pSimplifiedVersion); + return tr("No unique DV CVC"); case Code::Workflow_No_Permission_Error: return tr("Authentication failed."); case Code::Workflow_Certificate_No_Description: - return maskMessage(tr("No certificate description available."), pSimplifiedVersion); + return tr("No certificate description available."); case Code::Workflow_Certificate_No_Url_In_Description: - return maskMessage(tr("No subject url available in certificate description."), pSimplifiedVersion); + return tr("No subject url available in certificate description."); case Code::Workflow_Certificate_Hash_Error: - return maskMessage(tr("The certificate description does not match the certificate."), pSimplifiedVersion); + return tr("The certificate description does not match the certificate."); case Code::Workflow_Certificate_Sop_Error: - return maskMessage(tr("The subject URL in the certificate description and the TCToken URL don't satisfy the same origin policy."), pSimplifiedVersion); + return tr("The subject URL in the certificate description and the TCToken URL don't satisfy the same origin policy."); case Code::Workflow_Error_Page_Transmission_Error: case Code::Workflow_Processing_Error: case Code::Workflow_Redirect_Transmission_Error: - return maskMessage(getExternalInfo(), pSimplifiedVersion); + return getExternalInfo(); case Code::Workflow_TrustedChannel_Establishment_Error: - return maskMessage(tr("Failed to establish secure connection"), pSimplifiedVersion); + return tr("Failed to establish secure connection"); case Code::Workflow_TrustedChannel_Error_From_Server: - return maskMessage(tr("The program received an error from the server."), pSimplifiedVersion); + return tr("The program received an error from the server."); case Code::Workflow_TrustedChannel_Hash_Not_In_Description: case Code::Workflow_Nerwork_Ssl_Hash_Not_In_Certificate_Description: - return maskMessage(tr("Hash of certificate not in certificate description"), pSimplifiedVersion); + return tr("Hash of certificate not in certificate description (issuer: %1). This indicates a misconfiguration or manipulation of the certificate. Please check that your antivirus-software and firewalls are not interfering with SSL traffic.").arg(getExternalInfo()); case Code::Workflow_TrustedChannel_No_Data_Received: - return maskMessage(tr("Received no data."), pSimplifiedVersion); + return tr("Received no data."); case Code::Workflow_TrustedChannel_ServiceUnavailable: case Code::Network_ServiceUnavailable: @@ -136,25 +180,25 @@ QString GlobalStatus::toErrorDescription(const bool pSimplifiedVersion) const case Code::Network_TimeOut: case Code::Workflow_TrustedChannel_TimeOut: - return maskMessage(tr("Establishing a connection is taking too long."), pSimplifiedVersion); + return tr("Establishing a connection is taking too long."); case Code::Network_Proxy_Error: case Code::Workflow_TrustedChannel_Proxy_Error: - return maskMessage(tr("Establishing a connection via the proxy did not succeed."), pSimplifiedVersion); + return tr("Establishing a connection via the proxy did not succeed."); case Code::Workflow_TrustedChannel_Server_Format_Error: - return maskMessage(tr("It wasn't possible to connect to the server: the server sent a non-standard response."), pSimplifiedVersion); + return tr("It wasn't possible to connect to the server: the server sent a non-standard response."); case Code::Network_Ssl_Establishment_Error: case Code::Workflow_TrustedChannel_Ssl_Establishment_Error: - return maskMessage(tr("It wasn't possible to connect to the server: a secure connection could not be established."), pSimplifiedVersion); + return tr("It wasn't possible to connect to the server: a secure connection could not be established."); case Code::Workflow_Wrong_Parameter_Invocation: - return maskMessage(tr("Application was invoked with wrong parameters."), pSimplifiedVersion); + return tr("Application was invoked with wrong parameters."); case Code::Network_Other_Error: case Code::Workflow_TrustedChannel_Other_Network_Error: - return maskMessage(tr("An unknown network error occurred."), pSimplifiedVersion); + return tr("An unknown network error occurred."); case Code::Workflow_Reader_Became_Inaccessible: return tr("The selected card reader cannot be accessed anymore."); @@ -166,57 +210,36 @@ QString GlobalStatus::toErrorDescription(const bool pSimplifiedVersion) const return tr("The server provided no or incomplete information. Your personal data could not be read out."); case Code::Workflow_Network_Ssl_Connection_Unsupported_Algorithm_Or_Length: - return maskMessage(tr("Error while connecting to the service provider. The SSL connection uses an unsupported key algorithm or length."), pSimplifiedVersion); + return tr("Error while connecting to the service provider. The SSL connection uses an unsupported key algorithm or length."); case Code::Workflow_TrustedChannel_Ssl_Certificate_Unsupported_Algorithm_Or_Length: case Code::Workflow_Network_Ssl_Certificate_Unsupported_Algorithm_Or_Length: - return maskMessage(tr("Error while connecting to the server. The SSL certificate uses an unsupported key algorithm or length."), pSimplifiedVersion); + return tr("Error while connecting to the server. The SSL certificate uses an unsupported key algorithm or length. Certificate issuer: %1").arg(getExternalInfo()); case Code::Workflow_Network_Empty_Redirect_Url: - return maskMessage(tr("Empty redirect URL"), pSimplifiedVersion); + return tr("Empty redirect URL"); case Code::Workflow_Network_Expected_Redirect: - return maskMessage(tr("Expected redirect, got %1").arg(getExternalInfo()), pSimplifiedVersion); + return tr("Expected redirect, got %1").arg(getExternalInfo()); case Code::Workflow_Network_Invalid_Scheme: - return maskMessage(tr("Invalid scheme: %1").arg(getExternalInfo()), pSimplifiedVersion); + return tr("Invalid scheme: %1").arg(getExternalInfo()); case Code::Workflow_Network_Malformed_Redirect_Url: - return maskMessage(tr("Malformed redirect URL: %1").arg(getExternalInfo()), pSimplifiedVersion); + return tr("Malformed redirect URL: %1").arg(getExternalInfo()); case Code::Workflow_Cancellation_By_User: case Code::Card_Cancellation_By_User: return tr("The process was cancelled by the user."); + case Code::Paos_Generic_Server_Error: case Code::Paos_Unexpected_Warning: case Code::Paos_Error_AL_Unknown_Error: - case Code::Paos_Error_AL_No_Permission: case Code::Paos_Error_AL_Internal_Error: - case Code::Paos_Error_AL_Parameter_Error: - case Code::Paos_Error_AL_Unkown_API_Function: - case Code::Paos_Error_AL_Not_Initialized: - case Code::Paos_Error_AL_Warning_Connection_Disconnected: - case Code::Paos_Error_AL_Session_Terminated_Warning: case Code::Paos_Error_AL_Communication_Error: - case Code::Paos_Error_DP_Timeout_Error: - case Code::Paos_Error_DP_Unknown_Channel_Handle: - case Code::Paos_Error_DP_Communication_Error: case Code::Paos_Error_DP_Trusted_Channel_Establishment_Failed: - case Code::Paos_Error_DP_Unknown_Protocol: - case Code::Paos_Error_DP_Unknown_Cipher_Suite: - case Code::Paos_Error_DP_Unknown_Webservice_Binding: - case Code::Paos_Error_DP_Node_Not_Reachable: - case Code::Paos_Error_IFDL_Timeout_Error: - case Code::Paos_Error_IFDL_InvalidSlotHandle: - case Code::Paos_Error_IFDL_CancellationByUser: - case Code::Paos_Error_KEY_KeyGenerationNotPossible: case Code::Paos_Error_SAL_Cancellation_by_User: - case Code::Paos_Error_SAL_CertificateChainInterrupted: case Code::Paos_Error_SAL_Invalid_Key: - case Code::Paos_Error_SAL_SecurityConditionNotSatisfied: - case Code::Paos_Error_SAL_MEAC_AgeVerificationFailedWarning: - case Code::Paos_Error_SAL_MEAC_CommunityVerificationFailedWarning: - case Code::Paos_Error_SAL_MEAC_DocumentValidityVerificationFailed: return getExternalInfo(); case Code::Card_Input_TimeOut: @@ -229,6 +252,7 @@ QString GlobalStatus::toErrorDescription(const bool pSimplifiedVersion) const return tr("An error occurred while communicating with the ID card. Please make sure that your ID card is placed correctly on the card reader and try again."); case Code::Card_Protocol_Error: + case Code::Card_Unexpected_Transmit_Status: return QStringLiteral("%1 %3.").arg( tr("A protocol error occurred. Please make sure that your ID card is placed correctly on the card reader and try again. If the problem occurs again, please contact our support at"), tr("https://www.ausweisapp.bund.de/en/service/support/"), diff --git a/src/global/GlobalStatus.h b/src/global/GlobalStatus.h index 5c10792..8ca12c7 100644 --- a/src/global/GlobalStatus.h +++ b/src/global/GlobalStatus.h @@ -14,7 +14,6 @@ namespace governikus { - class GlobalStatus { Q_GADGET @@ -81,34 +80,14 @@ class GlobalStatus Paos_Unexpected_Warning, + Paos_Generic_Server_Error, + Paos_Error_AL_Unknown_Error, - Paos_Error_AL_No_Permission, Paos_Error_AL_Internal_Error, - Paos_Error_AL_Parameter_Error, - Paos_Error_AL_Unkown_API_Function, - Paos_Error_AL_Not_Initialized, - Paos_Error_AL_Warning_Connection_Disconnected, - Paos_Error_AL_Session_Terminated_Warning, Paos_Error_AL_Communication_Error, - Paos_Error_DP_Timeout_Error, - Paos_Error_DP_Unknown_Channel_Handle, - Paos_Error_DP_Communication_Error, Paos_Error_DP_Trusted_Channel_Establishment_Failed, - Paos_Error_DP_Unknown_Protocol, - Paos_Error_DP_Unknown_Cipher_Suite, - Paos_Error_DP_Unknown_Webservice_Binding, - Paos_Error_DP_Node_Not_Reachable, - Paos_Error_IFDL_Timeout_Error, - Paos_Error_IFDL_InvalidSlotHandle, - Paos_Error_IFDL_CancellationByUser, - Paos_Error_KEY_KeyGenerationNotPossible, Paos_Error_SAL_Cancellation_by_User, - Paos_Error_SAL_CertificateChainInterrupted, Paos_Error_SAL_Invalid_Key, - Paos_Error_SAL_SecurityConditionNotSatisfied, - Paos_Error_SAL_MEAC_AgeVerificationFailedWarning, - Paos_Error_SAL_MEAC_CommunityVerificationFailedWarning, - Paos_Error_SAL_MEAC_DocumentValidityVerificationFailed, Workflow_Reader_Device_Connection_Error, Workflow_Reader_Device_Scan_Error, @@ -116,6 +95,7 @@ class GlobalStatus Card_Not_Found, Card_Communication_Error, Card_Protocol_Error, + Card_Unexpected_Transmit_Status, Card_Cancellation_By_User, Card_Input_TimeOut, Card_Invalid_Pin, @@ -161,7 +141,6 @@ class GlobalStatus , mExternalInformation(pExternalInformation) , mOrigin(pOrigin) { - } @@ -178,20 +157,18 @@ class GlobalStatus QSharedDataPointer d; const QString getExternalInfo(int pIndex = 0) const; - static QString maskMessage(const QString& pMessage, const bool pMask); + QString toErrorDescriptionInternal() const; public: GlobalStatus(Code pStatusCode = Code::Unknown_Error, const QStringList& pExternalInformation = QStringList(), const Origin pOrigin = Origin::Client) : d(new InternalStatus(pStatusCode, pExternalInformation, pOrigin)) { - } GlobalStatus(Code pStatusCode, const QString& pExternalInformation, const Origin pOrigin = Origin::Client) : GlobalStatus(pStatusCode, QStringList(pExternalInformation), pOrigin) { - } @@ -199,7 +176,6 @@ class GlobalStatus bool is(const Code pStatusCode) const; Code getStatusCode() const; - operator GlobalStatus::Code() const; QString toErrorDescription(const bool pSimplifiedVersion = false) const; @@ -209,13 +185,14 @@ class GlobalStatus bool isNoError() const; bool isError() const; bool isCancellationByUser() const; + bool isMessageMasked() const; }; using Origin = GlobalStatus::Origin; defineEnumOperators(GlobalStatus::Code) -} /* namespace governikus */ +} // namespace governikus QDebug operator <<(QDebug pDbg, const governikus::GlobalStatus& pStatus); diff --git a/src/global/Initializer.cpp b/src/global/Initializer.cpp index c17dad0..41ebbf3 100644 --- a/src/global/Initializer.cpp +++ b/src/global/Initializer.cpp @@ -45,6 +45,6 @@ static void initialize() } -} +} // namespace Q_COREAPP_STARTUP_FUNCTION(initialize) diff --git a/src/global/Initializer.h b/src/global/Initializer.h index 36cead7..8b66e7d 100644 --- a/src/global/Initializer.h +++ b/src/global/Initializer.h @@ -45,4 +45,4 @@ class Initializer void add(const std::function& pRegister); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/global/LanguageLoader.cpp b/src/global/LanguageLoader.cpp index 4adf6c8..cabaedc 100644 --- a/src/global/LanguageLoader.cpp +++ b/src/global/LanguageLoader.cpp @@ -23,7 +23,7 @@ const QLocale::Language LanguageLoader::mFallbackLanguage = QLocale::Language::E QLocale LanguageLoader::mDefaultLanguage = QLocale::system(); LanguageLoader::LanguageLoader() - : mPath(FileDestination::getPath("translations")) + : mPath(FileDestination::getPath(QStringLiteral("translations"))) , mTranslatorList() , mComponentList( { @@ -75,10 +75,10 @@ void LanguageLoader::setPath(const QString& pPath) QSharedPointer LanguageLoader::createTranslator(const QLocale& pLocale, const QString& pComponent) { - QSharedPointer translator(new QTranslator()); + auto translator = QSharedPointer::create(); translator->setObjectName(pLocale.name() + QLatin1Char('/') + pComponent); bool loaded = translator->load(pLocale, pComponent, QStringLiteral("_"), mPath); - qCDebug(language) << "Load translation:" << pComponent << " | " << loaded; + qCDebug(language) << "Load translation:" << pComponent << '|' << loaded; if (!loaded) { diff --git a/src/global/LanguageLoader.h b/src/global/LanguageLoader.h index ca46f67..2ed4a39 100644 --- a/src/global/LanguageLoader.h +++ b/src/global/LanguageLoader.h @@ -23,7 +23,6 @@ class LanguageLoader { private: friend class ::test_LanguageLoader; - friend class ::test_ProviderParser; friend class ::test_ProviderConfigurationParser; static const QLocale::Language mFallbackLanguage; static QLocale mDefaultLanguage; @@ -60,4 +59,4 @@ class LanguageLoader QList getAvailableLocales() const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/global/LogCategories.cpp b/src/global/LogCategories.cpp index 5928731..e95d429 100644 --- a/src/global/LogCategories.cpp +++ b/src/global/LogCategories.cpp @@ -14,7 +14,6 @@ Q_LOGGING_CATEGORY(card_nfc, "card_nfc") Q_LOGGING_CATEGORY(card_remote, "card_remote") Q_LOGGING_CATEGORY(remote_device, "remote_device") Q_LOGGING_CATEGORY(card_drivers, "card_drivers") -Q_LOGGING_CATEGORY(cmdline, "cmdline") Q_LOGGING_CATEGORY(statemachine, "statemachine") Q_LOGGING_CATEGORY(paos, "paos") Q_LOGGING_CATEGORY(gui, "gui") diff --git a/src/global/LogHandler.cpp b/src/global/LogHandler.cpp index a9d0099..fb19efd 100644 --- a/src/global/LogHandler.cpp +++ b/src/global/LogHandler.cpp @@ -4,9 +4,12 @@ #include "LogHandler.h" +#include "BreakPropertyBindingDiagnosticLogFilter.h" #include "SingletonHelper.h" +#include #include +#include using namespace governikus; @@ -25,23 +28,20 @@ LogHandler::LogHandler() , mBacklogPosition(0) , mMessagePattern(QStringLiteral("%{category} %{time yyyy.MM.dd hh:mm:ss.zzz} %{if-debug} %{endif}%{if-info}I%{endif}%{if-warning}W%{endif}%{if-critical}C%{endif}%{if-fatal}F%{endif} %{function}(%{file}:%{line}) %{message}")) , mDefaultMessagePattern(QStringLiteral("%{if-category}%{category}: %{endif}%{message}")) // as defined in qlogging.cpp - , mLogFile(QDir::tempPath() + QStringLiteral("/AusweisApp2.XXXXXX.log")) // if you change value you need to adjust getOtherLogfiles() + , mLogFile(getLogFileTemplate()) , mHandler(nullptr) , mFilePrefix("/src/") , mMutex() { - mLogFile.open(); +#ifndef QT_NO_DEBUG + new BreakPropertyBindingDiagnosticLogFilter(this); +#endif } LogHandler::~LogHandler() { - const QMutexLocker mutexLocker(&mMutex); - if (mHandler) - { - qInstallMessageHandler(nullptr); - mHandler = nullptr; - } + reset(); } @@ -51,16 +51,45 @@ LogHandler& LogHandler::getInstance() } +QString LogHandler::getLogFileTemplate() +{ + // if you change value you need to adjust getOtherLogfiles() + return QDir::tempPath() % QLatin1Char('/') % QCoreApplication::applicationName() % QStringLiteral(".XXXXXX.log"); +} + + +void LogHandler::reset() +{ + const QMutexLocker mutexLocker(&mMutex); + if (isInitialized()) + { + qInstallMessageHandler(nullptr); + mHandler = nullptr; + } +} + + void LogHandler::init() { const QMutexLocker mutexLocker(&mMutex); - if (!mHandler) + if (!isInitialized()) { + if (useLogfile()) + { + mLogFile.open(); + } mHandler = qInstallMessageHandler(&LogHandler::messageHandler); + removeOldLogfiles(); } } +bool LogHandler::isInitialized() const +{ + return mHandler; +} + + void LogHandler::setAutoRemove(bool pRemove) { mLogFile.setAutoRemove(pRemove); @@ -90,26 +119,24 @@ QByteArray LogHandler::getBacklog() return backlog; } - return tr("An error occurred in log handling: %1").arg(mLogFile.errorString()).toUtf8(); + if (useLogfile()) + { + return tr("An error occurred in log handling: %1").arg(mLogFile.errorString()).toUtf8(); + } + + return QByteArray(); } QDateTime LogHandler::getFileDate(const QFileInfo& pInfo) { -#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) - const QFileInfo info(pInfo); - const auto& dateTime = info.birthTime(); + const auto& dateTime = pInfo.birthTime(); if (dateTime.isValid()) { return dateTime; } - return info.metadataChangeTime(); - -#else - return QFileInfo(pInfo).created(); - -#endif + return pInfo.metadataChangeTime(); } @@ -267,8 +294,9 @@ bool LogHandler::copy(const QString& pDest) QFileInfoList LogHandler::getOtherLogfiles() const { QDir tmpPath = QDir::temp(); + tmpPath.setSorting(QDir::Time); tmpPath.setFilter(QDir::Files); - tmpPath.setNameFilters(QStringList({QStringLiteral("AusweisApp2.*.log")})); + tmpPath.setNameFilters(QStringList({QCoreApplication::applicationName() + QStringLiteral(".*.log")})); QFileInfoList list = tmpPath.entryInfoList(); list.removeAll(mLogFile); @@ -277,13 +305,60 @@ QFileInfoList LogHandler::getOtherLogfiles() const } -void LogHandler::removeOtherLogfiles() +void LogHandler::removeOldLogfiles() +{ + const auto& threshold = QDateTime::currentDateTime().addDays(-14); + const QFileInfoList& logfileInfos = getOtherLogfiles(); + for (const QFileInfo& entry : logfileInfos) + { + if (entry.fileTime(QFileDevice::FileModificationTime) < threshold) + { + qDebug() << "Auto-remove old log file:" << entry.absoluteFilePath() << '|' << QFile::remove(entry.absoluteFilePath()); + } + } +} + + +bool LogHandler::removeOtherLogfiles() { const auto otherLogFiles = getOtherLogfiles(); for (const auto& entry : otherLogFiles) { qDebug() << "Remove old log file:" << entry.absoluteFilePath() << "|" << QFile::remove(entry.absoluteFilePath()); } + + return !otherLogFiles.isEmpty(); +} + + +void LogHandler::setLogfile(bool pEnable) +{ + const QMutexLocker mutexLocker(&mMutex); + + if (pEnable) + { + if (!mLogFile.isOpen()) + { + mLogFile.setFileTemplate(getLogFileTemplate()); + mLogFile.open(); + } + } + else + { + if (mLogFile.isOpen()) + { + mLogFile.close(); + mLogFile.remove(); + mBacklogPosition = 0; + } + mLogFile.setFileTemplate(QString()); + } +} + + +bool LogHandler::useLogfile() const +{ + return !mLogFile.fileTemplate().isNull(); } diff --git a/src/global/LogHandler.h b/src/global/LogHandler.h index a078654..5798a83 100644 --- a/src/global/LogHandler.h +++ b/src/global/LogHandler.h @@ -6,14 +6,20 @@ #pragma once +#include "Env.h" + #include #include #include +#include #include #include #include #include +#define spawnMessageLogger(category)\ + QMessageLogger(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC, category().categoryName()) + class test_LogHandler; namespace governikus @@ -23,9 +29,12 @@ class LogHandler : public QObject { Q_OBJECT + friend class Env; friend class ::test_LogHandler; private: + static QString getLogFileTemplate(); + const bool mEnvPattern; const int mFunctionFilenameSize; qint64 mBacklogPosition; @@ -43,6 +52,7 @@ class LogHandler QString getPaddedLogMsg(const QMessageLogContext& pContext, const QString& pMsg); void handleMessage(QtMsgType pType, const QMessageLogContext& pContext, const QString& pMsg); + void removeOldLogfiles(); static void messageHandler(QtMsgType pType, const QMessageLogContext& pContext, const QString& pMsg); friend QDebug operator<<(QDebug, const LogHandler&); @@ -50,9 +60,16 @@ class LogHandler protected: LogHandler(); virtual ~LogHandler(); + static LogHandler& getInstance(); + +#ifndef QT_NO_DEBUG + + public: +#endif + void reset(); + bool isInitialized() const; public: - static LogHandler& getInstance(); void init(); void setAutoRemove(bool pRemove); @@ -63,7 +80,9 @@ class LogHandler static QDateTime getFileDate(const QFileInfo& pInfo); QDateTime getCurrentLogfileDate() const; QFileInfoList getOtherLogfiles() const; - void removeOtherLogfiles(); + bool removeOtherLogfiles(); + void setLogfile(bool pEnable); + bool useLogfile() const; Q_SIGNALS: /** @@ -82,4 +101,4 @@ inline QDebug operator<<(QDebug pDbg, const governikus::LogHandler& pHandler) } -} /* namespace governikus */ +} // namespace governikus diff --git a/src/global/Randomizer.cpp b/src/global/Randomizer.cpp index ae9a3f1..6a81a1c 100644 --- a/src/global/Randomizer.cpp +++ b/src/global/Randomizer.cpp @@ -8,10 +8,7 @@ #include #include - -#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) #include -#endif #ifdef Q_OS_WIN #include @@ -50,10 +47,7 @@ template QList Randomizer::getEntropy() entropy += static_cast(std::chrono::system_clock::now().time_since_epoch().count()); entropy += std::random_device()(); entropy += static_cast(qrand()); - -#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) entropy += QRandomGenerator::securelySeeded().generate(); -#endif UniversalBuffer buffer; if (RAND_bytes(buffer.data, sizeof(buffer.data))) diff --git a/src/global/Randomizer.h b/src/global/Randomizer.h index 2b55ba1..07c4106 100644 --- a/src/global/Randomizer.h +++ b/src/global/Randomizer.h @@ -38,4 +38,4 @@ class Randomizer }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/global/ResourceLoader.h b/src/global/ResourceLoader.h index e2e62ab..0aa489d 100644 --- a/src/global/ResourceLoader.h +++ b/src/global/ResourceLoader.h @@ -30,4 +30,4 @@ class ResourceLoader bool isLoaded(); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/global/Result.cpp b/src/global/Result.cpp deleted file mode 100644 index 6c28e3b..0000000 --- a/src/global/Result.cpp +++ /dev/null @@ -1,567 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "Result.h" - -#include "LanguageLoader.h" - -#include -#include - -using namespace governikus; - -#define RESULTMAJOR "http://www.bsi.bund.de/ecard/api/1.1/resultmajor" -#define RESULTMINOR "http://www.bsi.bund.de/ecard/api/1.1/resultminor" - -const QMap Result::mMajorResults = { - {Result::Major::Ok, QString::fromLatin1(RESULTMAJOR "#ok")}, - {Result::Major::Warning, QString::fromLatin1(RESULTMAJOR "#warning")}, - {Result::Major::Error, QString::fromLatin1(RESULTMAJOR "#error")} -}; - -// See TR-03112, section 4.2 for details about the codes -// AL -> Applicaction Layer -// DP -> Dispatcher -// IFDL -> Interface Device Layer -// KEY -// SAL -> Service Access Layer -const QMap Result::mMinorResults = { - {GlobalStatus::Code::Paos_Error_AL_Unknown_Error, QString::fromLatin1(RESULTMINOR "/al/common#unknownError")}, - {GlobalStatus::Code::Paos_Error_AL_No_Permission, QString::fromLatin1(RESULTMINOR "/al/common#noPermission")}, - {GlobalStatus::Code::Paos_Error_AL_Internal_Error, QString::fromLatin1(RESULTMINOR "/al/common#internalError")}, - {GlobalStatus::Code::Paos_Error_AL_Parameter_Error, QString::fromLatin1(RESULTMINOR "/al/common#parameterError")}, - {GlobalStatus::Code::Paos_Error_AL_Unkown_API_Function, QString::fromLatin1(RESULTMINOR "/al/common#unkownAPIFunction")}, - {GlobalStatus::Code::Paos_Error_AL_Not_Initialized, QString::fromLatin1(RESULTMINOR "/al/common#notInitialized")}, - {GlobalStatus::Code::Paos_Error_AL_Warning_Connection_Disconnected, QString::fromLatin1(RESULTMINOR "/al/common#warningConnectionDisconnected")}, - {GlobalStatus::Code::Paos_Error_AL_Session_Terminated_Warning, QString::fromLatin1(RESULTMINOR "/al/common#SessionTerminatedWarning")}, - {GlobalStatus::Code::Paos_Error_AL_Communication_Error, QString::fromLatin1(RESULTMINOR "/al/common#communicationError")}, - {GlobalStatus::Code::Paos_Error_DP_Timeout_Error, QString::fromLatin1(RESULTMINOR "/dp#timeout")}, - {GlobalStatus::Code::Paos_Error_DP_Unknown_Channel_Handle, QString::fromLatin1(RESULTMINOR "/dp#unknownChannelHandle")}, - {GlobalStatus::Code::Paos_Error_DP_Communication_Error, QString::fromLatin1(RESULTMINOR "/dp#communicationError")}, - {GlobalStatus::Code::Paos_Error_DP_Trusted_Channel_Establishment_Failed, QString::fromLatin1(RESULTMINOR "/dp#trustedChannelEstablishmentFailed")}, - {GlobalStatus::Code::Paos_Error_DP_Unknown_Protocol, QString::fromLatin1(RESULTMINOR "/dp#unknownProtocol")}, - {GlobalStatus::Code::Paos_Error_DP_Unknown_Cipher_Suite, QString::fromLatin1(RESULTMINOR "/dp#unknownCipherSuite")}, - {GlobalStatus::Code::Paos_Error_DP_Unknown_Webservice_Binding, QString::fromLatin1(RESULTMINOR "/dp#unknownWebserviceBinding")}, - {GlobalStatus::Code::Paos_Error_DP_Node_Not_Reachable, QString::fromLatin1(RESULTMINOR "/dp#nodeNotReachable")}, - {GlobalStatus::Code::Paos_Error_IFDL_Timeout_Error, QString::fromLatin1(RESULTMINOR "/ifdl/common#timeoutError")}, - {GlobalStatus::Code::Paos_Error_IFDL_InvalidSlotHandle, QString::fromLatin1(RESULTMINOR "/ifdl/common#invalidSlotHandle")}, - {GlobalStatus::Code::Paos_Error_IFDL_CancellationByUser, QString::fromLatin1(RESULTMINOR "/ifdl#cancellationByUser")}, - {GlobalStatus::Code::Paos_Error_KEY_KeyGenerationNotPossible, QString::fromLatin1(RESULTMINOR "/il/key#keyGenerationNotPossible")}, - {GlobalStatus::Code::Paos_Error_SAL_Cancellation_by_User, QString::fromLatin1(RESULTMINOR "/sal#cancellationByUser")}, - {GlobalStatus::Code::Paos_Error_SAL_CertificateChainInterrupted, QString::fromLatin1(RESULTMINOR "/sal#certificateChainInterrupted")}, - {GlobalStatus::Code::Paos_Error_SAL_Invalid_Key, QString::fromLatin1(RESULTMINOR "/sal#invalidKey")}, - {GlobalStatus::Code::Paos_Error_SAL_SecurityConditionNotSatisfied, QString::fromLatin1(RESULTMINOR "/sal#securityConditionNotSatisfied")}, - {GlobalStatus::Code::Paos_Error_SAL_MEAC_AgeVerificationFailedWarning, QString::fromLatin1(RESULTMINOR "/sal/mEAC#AgeVerificationFailedWarning")}, - {GlobalStatus::Code::Paos_Error_SAL_MEAC_CommunityVerificationFailedWarning, QString::fromLatin1(RESULTMINOR "/sal/mEAC#CommunityVerificationFailedWarning")}, - {GlobalStatus::Code::Paos_Error_SAL_MEAC_DocumentValidityVerificationFailed, QString::fromLatin1(RESULTMINOR "/sal/mEAC#DocumentValidityVerificationFailed")} -}; - - -Result Result::fromStatus(const GlobalStatus& pStatus) -{ - switch (pStatus) - { - case GlobalStatus::Code::Unknown_Error: - case GlobalStatus::Code::Paos_Unexpected_Warning: - case GlobalStatus::Code::Paos_Error_AL_Unknown_Error: - return Result(Result::Major::Error, GlobalStatus::Code::Paos_Error_AL_Unknown_Error, pStatus.toErrorDescription(), pStatus.getOrigin()); - - case GlobalStatus::Code::No_Error: - return createOk(); - - case GlobalStatus::Code::Workflow_Card_Removed: - case GlobalStatus::Code::Paos_Error_IFDL_CancellationByUser: - return Result(Result::Major::Error, GlobalStatus::Code::Paos_Error_IFDL_CancellationByUser, pStatus.toErrorDescription(), pStatus.getOrigin()); - - case GlobalStatus::Code::Workflow_Preverification_Developermode_Error: - case GlobalStatus::Code::Workflow_Preverification_Error: - case GlobalStatus::Code::Workflow_No_Unique_AtCvc: - case GlobalStatus::Code::Workflow_No_Unique_DvCvc: - case GlobalStatus::Code::Paos_Error_SAL_CertificateChainInterrupted: - return Result(Result::Major::Error, GlobalStatus::Code::Paos_Error_SAL_CertificateChainInterrupted, pStatus.toErrorDescription(), pStatus.getOrigin()); - - case GlobalStatus::Code::Workflow_Certificate_No_Description: - case GlobalStatus::Code::Workflow_Certificate_No_Url_In_Description: - case GlobalStatus::Code::Workflow_Certificate_Hash_Error: - case GlobalStatus::Code::Workflow_Certificate_Sop_Error: - case GlobalStatus::Code::Paos_Error_AL_Parameter_Error: - return Result(Result::Major::Error, GlobalStatus::Code::Paos_Error_AL_Parameter_Error, pStatus.toErrorDescription(), pStatus.getOrigin()); - - case GlobalStatus::Code::Workflow_TrustedChannel_Establishment_Error: - case GlobalStatus::Code::Workflow_TrustedChannel_Error_From_Server: - case GlobalStatus::Code::Workflow_TrustedChannel_Hash_Not_In_Description: - case GlobalStatus::Code::Workflow_TrustedChannel_No_Data_Received: - case GlobalStatus::Code::Workflow_TrustedChannel_Ssl_Certificate_Unsupported_Algorithm_Or_Length: - case GlobalStatus::Code::Workflow_TrustedChannel_ServiceUnavailable: - case GlobalStatus::Code::Workflow_TrustedChannel_TimeOut: - case GlobalStatus::Code::Workflow_TrustedChannel_Proxy_Error: - case GlobalStatus::Code::Workflow_TrustedChannel_Ssl_Establishment_Error: - case GlobalStatus::Code::Workflow_TrustedChannel_Server_Format_Error: - case GlobalStatus::Code::Workflow_TrustedChannel_Other_Network_Error: - case GlobalStatus::Code::Paos_Error_DP_Trusted_Channel_Establishment_Failed: - return Result(Result::Major::Error, GlobalStatus::Code::Paos_Error_DP_Trusted_Channel_Establishment_Failed, pStatus.toErrorDescription(), pStatus.getOrigin()); - - case GlobalStatus::Code::Workflow_Cancellation_By_User: - case GlobalStatus::Code::Card_Cancellation_By_User: - case GlobalStatus::Code::Paos_Error_SAL_Cancellation_by_User: - return Result(Result::Major::Error, GlobalStatus::Code::Paos_Error_SAL_Cancellation_by_User, pStatus.toErrorDescription(), pStatus.getOrigin()); - - case GlobalStatus::Code::Workflow_No_Permission_Error: - case GlobalStatus::Code::Paos_Error_AL_No_Permission: - return Result(Result::Major::Error, GlobalStatus::Code::Paos_Error_AL_No_Permission, pStatus.toErrorDescription(), pStatus.getOrigin()); - - case GlobalStatus::Code::Workflow_Communication_Missing_Redirect_Url: - case GlobalStatus::Code::Workflow_Error_Page_Transmission_Error: - case GlobalStatus::Code::Workflow_Redirect_Transmission_Error: - case GlobalStatus::Code::Workflow_Processing_Error: - case GlobalStatus::Code::Workflow_Reader_Became_Inaccessible: - case GlobalStatus::Code::Workflow_Reader_Communication_Error: - case GlobalStatus::Code::Workflow_Reader_Device_Connection_Error: - case GlobalStatus::Code::Workflow_Reader_Device_Scan_Error: - case GlobalStatus::Code::Workflow_Server_Incomplete_Information_Provided: - case GlobalStatus::Code::Network_Ssl_Establishment_Error: - case GlobalStatus::Code::Workflow_Network_Ssl_Connection_Unsupported_Algorithm_Or_Length: - case GlobalStatus::Code::Workflow_Network_Ssl_Certificate_Unsupported_Algorithm_Or_Length: - case GlobalStatus::Code::Workflow_Nerwork_Ssl_Hash_Not_In_Certificate_Description: - case GlobalStatus::Code::Workflow_Network_Empty_Redirect_Url: - case GlobalStatus::Code::Workflow_Network_Expected_Redirect: - case GlobalStatus::Code::Workflow_Network_Invalid_Scheme: - case GlobalStatus::Code::Workflow_Network_Malformed_Redirect_Url: - case GlobalStatus::Code::Network_ServiceUnavailable: - case GlobalStatus::Code::Network_TimeOut: - case GlobalStatus::Code::Network_Proxy_Error: - case GlobalStatus::Code::Network_Other_Error: - case GlobalStatus::Code::Workflow_Wrong_Parameter_Invocation: - case GlobalStatus::Code::Card_Invalid_Pin: - case GlobalStatus::Code::Card_Invalid_Can: - case GlobalStatus::Code::Card_Invalid_Puk: - case GlobalStatus::Code::Card_Puk_Blocked: - case GlobalStatus::Code::Paos_Error_AL_Communication_Error: - return Result(Result::Major::Error, GlobalStatus::Code::Paos_Error_AL_Communication_Error, pStatus.toErrorDescription(), pStatus.getOrigin()); - - case GlobalStatus::Code::Workflow_AlreadyInProgress_Error: - case GlobalStatus::Code::Card_Not_Found: - case GlobalStatus::Code::Card_Communication_Error: - case GlobalStatus::Code::Card_Input_TimeOut: - case GlobalStatus::Code::Card_Pin_Blocked: - case GlobalStatus::Code::Card_Pin_Not_Blocked: - case GlobalStatus::Code::Card_NewPin_Mismatch: - case GlobalStatus::Code::Card_NewPin_Invalid_Length: - return Result(Result::Major::Error, GlobalStatus::Code::Paos_Error_AL_Unknown_Error, pStatus.toErrorDescription(), pStatus.getOrigin()); - - case GlobalStatus::Code::Workflow_Cannot_Confirm_IdCard_Authenticity: - case GlobalStatus::Code::Workflow_Unknown_Paos_Form_EidServer: - case GlobalStatus::Code::Workflow_Unexpected_Message_From_EidServer: - case GlobalStatus::Code::Workflow_Pin_Blocked_And_Puk_Objectionable: - case GlobalStatus::Code::Card_Protocol_Error: - case GlobalStatus::Code::Paos_Error_AL_Internal_Error: - return Result(Result::Major::Error, GlobalStatus::Code::Paos_Error_AL_Internal_Error, pStatus.toErrorDescription(), pStatus.getOrigin()); - - case GlobalStatus::Code::Paos_Error_AL_Unkown_API_Function: - return Result(Result::Major::Error, GlobalStatus::Code::Paos_Error_AL_Unkown_API_Function, pStatus.toErrorDescription(), pStatus.getOrigin()); - - case GlobalStatus::Code::Paos_Error_AL_Not_Initialized: - return Result(Result::Major::Error, GlobalStatus::Code::Paos_Error_AL_Not_Initialized, pStatus.toErrorDescription(), pStatus.getOrigin()); - - case GlobalStatus::Code::Paos_Error_AL_Warning_Connection_Disconnected: - return Result(Result::Major::Error, GlobalStatus::Code::Paos_Error_AL_Warning_Connection_Disconnected, pStatus.toErrorDescription(), pStatus.getOrigin()); - - case GlobalStatus::Code::Paos_Error_AL_Session_Terminated_Warning: - return Result(Result::Major::Error, GlobalStatus::Code::Paos_Error_AL_Session_Terminated_Warning, pStatus.toErrorDescription(), pStatus.getOrigin()); - - case GlobalStatus::Code::Paos_Error_DP_Timeout_Error: - return Result(Result::Major::Error, GlobalStatus::Code::Paos_Error_DP_Timeout_Error, pStatus.toErrorDescription(), pStatus.getOrigin()); - - case GlobalStatus::Code::Paos_Error_DP_Unknown_Channel_Handle: - return Result(Result::Major::Error, GlobalStatus::Code::Paos_Error_DP_Unknown_Channel_Handle, pStatus.toErrorDescription(), pStatus.getOrigin()); - - case GlobalStatus::Code::Paos_Error_DP_Communication_Error: - return Result(Result::Major::Error, GlobalStatus::Code::Paos_Error_DP_Communication_Error, pStatus.toErrorDescription(), pStatus.getOrigin()); - - case GlobalStatus::Code::Paos_Error_DP_Unknown_Protocol: - return Result(Result::Major::Error, GlobalStatus::Code::Paos_Error_DP_Unknown_Protocol, pStatus.toErrorDescription(), pStatus.getOrigin()); - - case GlobalStatus::Code::Paos_Error_DP_Unknown_Cipher_Suite: - return Result(Result::Major::Error, GlobalStatus::Code::Paos_Error_DP_Unknown_Cipher_Suite, pStatus.toErrorDescription(), pStatus.getOrigin()); - - case GlobalStatus::Code::Paos_Error_DP_Unknown_Webservice_Binding: - return Result(Result::Major::Error, GlobalStatus::Code::Paos_Error_DP_Unknown_Webservice_Binding, pStatus.toErrorDescription(), pStatus.getOrigin()); - - case GlobalStatus::Code::Paos_Error_DP_Node_Not_Reachable: - return Result(Result::Major::Error, GlobalStatus::Code::Paos_Error_DP_Node_Not_Reachable, pStatus.toErrorDescription(), pStatus.getOrigin()); - - case GlobalStatus::Code::Paos_Error_IFDL_Timeout_Error: - return Result(Result::Major::Error, GlobalStatus::Code::Paos_Error_IFDL_Timeout_Error, pStatus.toErrorDescription(), pStatus.getOrigin()); - - case GlobalStatus::Code::Paos_Error_IFDL_InvalidSlotHandle: - return Result(Result::Major::Error, GlobalStatus::Code::Paos_Error_IFDL_InvalidSlotHandle, pStatus.toErrorDescription(), pStatus.getOrigin()); - - case GlobalStatus::Code::Paos_Error_KEY_KeyGenerationNotPossible: - return Result(Result::Major::Error, GlobalStatus::Code::Paos_Error_KEY_KeyGenerationNotPossible, pStatus.toErrorDescription(), pStatus.getOrigin()); - - case GlobalStatus::Code::Paos_Error_SAL_Invalid_Key: - return Result(Result::Major::Error, GlobalStatus::Code::Paos_Error_SAL_Invalid_Key, pStatus.toErrorDescription(), pStatus.getOrigin()); - - case GlobalStatus::Code::Paos_Error_SAL_SecurityConditionNotSatisfied: - return Result(Result::Major::Error, GlobalStatus::Code::Paos_Error_SAL_SecurityConditionNotSatisfied, pStatus.toErrorDescription(), pStatus.getOrigin()); - - case GlobalStatus::Code::Paos_Error_SAL_MEAC_AgeVerificationFailedWarning: - return Result(Result::Major::Error, GlobalStatus::Code::Paos_Error_SAL_MEAC_AgeVerificationFailedWarning, pStatus.toErrorDescription(), pStatus.getOrigin()); - - case GlobalStatus::Code::Paos_Error_SAL_MEAC_CommunityVerificationFailedWarning: - return Result(Result::Major::Error, GlobalStatus::Code::Paos_Error_SAL_MEAC_CommunityVerificationFailedWarning, pStatus.toErrorDescription(), pStatus.getOrigin()); - - case GlobalStatus::Code::Paos_Error_SAL_MEAC_DocumentValidityVerificationFailed: - return Result(Result::Major::Error, GlobalStatus::Code::Paos_Error_SAL_MEAC_DocumentValidityVerificationFailed, pStatus.toErrorDescription(), pStatus.getOrigin()); - - case GlobalStatus::Code::RemoteReader_CloseCode_NormalClose: - return createOk(); - - case GlobalStatus::Code::RemoteReader_CloseCode_AbnormalClose: - case GlobalStatus::Code::RemoteReader_CloseCode_Undefined: - case GlobalStatus::Code::RemoteConnector_InvalidRequest: - case GlobalStatus::Code::RemoteConnector_EmptyPassword: - case GlobalStatus::Code::RemoteConnector_NoSupportedApiLevel: - case GlobalStatus::Code::RemoteConnector_ConnectionTimeout: - case GlobalStatus::Code::RemoteConnector_ConnectionError: - case GlobalStatus::Code::RemoteConnector_RemoteHostRefusedConnection: - case GlobalStatus::Code::Downloader_File_Not_Found: - case GlobalStatus::Code::Downloader_Cannot_Save_File: - case GlobalStatus::Code::Downloader_Data_Corrupted: - return Result(Result::Major::Error, GlobalStatus::Code::Paos_Error_AL_Unknown_Error, pStatus.toErrorDescription(), pStatus.getOrigin()); - } - - Q_UNREACHABLE(); -} - - -Result Result::createOk() -{ - return Result(Result::Major::Ok, GlobalStatus::Code::No_Error); -} - - -Result::Major Result::parseMajor(const QString& pMajor) -{ - for (auto iter = mMajorResults.cbegin(); iter != mMajorResults.cend(); ++iter) - { - if (pMajor == iter.value()) - { - return iter.key(); - } - } - - if (!pMajor.isEmpty()) - { - qWarning() << "Unknown ResultMajor:" << pMajor; - } - return Major::Unknown; -} - - -GlobalStatus::Code Result::parseMinor(const QString& pMinor) -{ - for (auto iter = mMinorResults.cbegin(); iter != mMinorResults.cend(); ++iter) - { - if (pMinor == iter.value()) - { - return iter.key(); - } - } - - if (!pMinor.isEmpty()) - { - qWarning() << "Unknown ResultMinor:" << pMinor; - return GlobalStatus::Code::Unknown_Error; - } - - return GlobalStatus::Code::No_Error; -} - - -bool Result::isMajor(const QString& major) -{ - return Major::Unknown != parseMajor(major); -} - - -bool Result::isMinor(const QString& minor) -{ - return GlobalStatus::Code::Unknown_Error != parseMinor(minor); -} - - -QString Result::getMessage(GlobalStatus::Code pMinor) -{ - switch (pMinor) - { - case GlobalStatus::Code::Paos_Error_AL_Unknown_Error: - return tr("An unexpected error has occurred during processing."); - - case GlobalStatus::Code::Paos_Error_AL_No_Permission: - return tr("Use of the function by the client application is not permitted."); - - case GlobalStatus::Code::Paos_Error_AL_Internal_Error: - return tr("An internal error has occurred during processing."); - - case GlobalStatus::Code::Paos_Error_AL_Parameter_Error: - return tr("There was some problem with a provided or omitted parameter."); - - case GlobalStatus::Code::Paos_Error_AL_Unkown_API_Function: - return tr("The API function is unknown."); - - case GlobalStatus::Code::Paos_Error_AL_Not_Initialized: - return tr("The framework or layer has not been initialized."); - - case GlobalStatus::Code::Paos_Error_AL_Warning_Connection_Disconnected: - return tr("The active session has been terminated."); - - case GlobalStatus::Code::Paos_Error_AL_Session_Terminated_Warning: - return tr("The active session has been terminated."); - - case GlobalStatus::Code::Paos_Error_AL_Communication_Error: - return tr("A Communication error occurred during processing."); - - case GlobalStatus::Code::Paos_Error_DP_Timeout_Error: - return tr("The operation was terminated as the set time was exceeded."); - - case GlobalStatus::Code::Paos_Error_DP_Unknown_Channel_Handle: - return tr("The operation was aborted as an invalid channel handle was used."); - - case GlobalStatus::Code::Paos_Error_DP_Communication_Error: - return tr("A Communication error occurred during processing."); - - case GlobalStatus::Code::Paos_Error_DP_Trusted_Channel_Establishment_Failed: - return tr("A trusted channel could not be opened."); - - case GlobalStatus::Code::Paos_Error_DP_Unknown_Protocol: - return tr("The operation was aborted as an unknown protocol was used."); - - case GlobalStatus::Code::Paos_Error_DP_Unknown_Cipher_Suite: - return tr("The operation was aborted as an unknown cipher suite was used."); - - case GlobalStatus::Code::Paos_Error_DP_Unknown_Webservice_Binding: - return tr("The operation was aborted as an unknown web service binding was used."); - - case GlobalStatus::Code::Paos_Error_DP_Node_Not_Reachable: - return tr("A Communication error occurred during processing."); - - case GlobalStatus::Code::Paos_Error_IFDL_Timeout_Error: - return tr("The operation was terminated as the set time was exceeded."); - - case GlobalStatus::Code::Paos_Error_KEY_KeyGenerationNotPossible: - return tr("Signature certificate key generation is not possible."); - - case GlobalStatus::Code::Paos_Error_SAL_Cancellation_by_User: - return tr("The process was cancelled by the user."); - - case GlobalStatus::Code::Paos_Error_SAL_CertificateChainInterrupted: - return tr("One or more certificate checks failed. The operation will be aborted due to security reasons."); - - case GlobalStatus::Code::Paos_Error_SAL_Invalid_Key: - return tr("This action cannot be performed. The online identification function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the online identification function."); - - case GlobalStatus::Code::Paos_Error_SAL_SecurityConditionNotSatisfied: - return tr("The authenticity of your ID card could not be verified. Please make sure that you are using a genuine ID card. Please note that test applications require the use of a test ID card."); - - case GlobalStatus::Code::Paos_Error_SAL_MEAC_AgeVerificationFailedWarning: - return tr("The age verification failed."); - - case GlobalStatus::Code::Paos_Error_SAL_MEAC_CommunityVerificationFailedWarning: - return tr("The community verification failed."); - - case GlobalStatus::Code::Paos_Error_SAL_MEAC_DocumentValidityVerificationFailed: - return tr("The ID card is invalid or disabled."); - - case GlobalStatus::Code::Unknown_Error: - default: - return QString(); - } -} - - -Result::ResultData::ResultData(Major pMajor, GlobalStatus::Code pMinor, const QString& pMessage, Origin pOrigin) - : QSharedData() - , mMajor(pMajor) - , mMinor(pMinor) - , mMessage(pMessage) - , mMessageLang(LanguageLoader::getInstance().getUsedLocale().bcp47Name().left(2)) - , mOrigin(pOrigin) -{ - if (pMinor != GlobalStatus::Code::No_Error && pMinor != GlobalStatus::Code::Unknown_Error && !mMinorResults.contains(pMinor)) - { - qCritical() << "Unknown status code in minor paos result:" << pMinor; - Q_ASSERT(false); - } -} - - -Result::Result(Major pMajor, GlobalStatus::Code pMinor, const QString& pMessage, Origin pOrigin) - : d(new ResultData(pMajor, pMinor, pMessage, pOrigin)) -{ -} - - -Result::Result(const QString& pMajor, const QString& pMinor, const QString& pMessage, Origin pOrigin) - : Result(parseMajor(pMajor), parseMinor(pMinor), pMessage, pOrigin) -{ -} - - -Result::Result(const GlobalStatus& pStatus) - : Result(fromStatus(pStatus)) -{ - -} - - -bool Result::operator ==(const Result& pResult) const -{ - return *d == *pResult.d; -} - - -Result::Major Result::getMajor() const -{ - return d->mMajor; -} - - -GlobalStatus::Code Result::getMinor() const -{ - return d->mMinor; -} - - -QString Result::getMessage() const -{ - return d->mMessage; -} - - -const QString& Result::getMessageLang() const -{ - return d->mMessageLang; -} - - -QString Result::getMajorString() const -{ - return mMajorResults.value(d->mMajor); -} - - -QString Result::getMinorString() const -{ - return mMinorResults.value(d->mMinor); -} - - -bool Result::isValid() const -{ - switch (d->mMajor) - { - case Major::Unknown: - return false; - - case Major::Ok: - return d->mMinor == GlobalStatus::Code::No_Error; - - default: - return d->mMinor != GlobalStatus::Code::Unknown_Error; - } -} - - -bool Result::isOk() const -{ - return d->mMajor == Major::Ok && d->mMinor == GlobalStatus::Code::No_Error; -} - - -bool Result::isOriginServer() const -{ - return d->mOrigin == Origin::Server; -} - - -GlobalStatus Result::toStatus() const -{ - QString message = getMessage(); - if (message.isEmpty() || isOriginServer()) - { - // if the message is not set, we clearly use the result minor - // if the server sent the message, it can be in any language, so we take the result minor - message = Result::getMessage(getMinor()); - } - - switch (getMajor()) - { - case Major::Unknown: - return GlobalStatus(GlobalStatus::Code::Unknown_Error, message); - - case Major::Ok: - if (isOk()) - { - return GlobalStatus::Code::No_Error; - } - // FALLTHROUGH - - case Major::Error: - return GlobalStatus(getMinor(), message, d->mOrigin); - - case Major::Warning: - return GlobalStatus(GlobalStatus::Code::Paos_Unexpected_Warning, message); - } - - Q_UNREACHABLE(); -} - - -governikus::Result::operator GlobalStatus() const -{ - return toStatus(); -} - - -QJsonObject Result::toJson() const -{ - QJsonObject obj; - - obj[QLatin1String("major")] = getMajorString(); - if (getMinor() != GlobalStatus::Code::No_Error) - { - obj[QLatin1String("minor")] = getMinorString(); - } - - const auto& message = getMessage(); - if (!message.isEmpty()) - { - obj[QLatin1String("message")] = message; - } - - const auto& minorDesc = Result::getMessage(getMinor()); - if (!minorDesc.isEmpty()) - { - obj[QLatin1String("description")] = minorDesc; - } - - const auto& lang = getMessageLang(); - if (!lang.isEmpty() && (!message.isEmpty() || !minorDesc.isEmpty())) - { - obj[QLatin1String("language")] = lang; - } - - return obj; -} - - -QDebug operator <<(QDebug pDbg, const governikus::Result& pResult) -{ - const QString string = pResult.getMajorString() % QLatin1String(" | ") % pResult.getMinorString() % QLatin1String(" | ") % pResult.getMessage(); - QDebugStateSaver saver(pDbg); - pDbg.space() << "Result:"; - pDbg.quote() << string; - return pDbg; -} diff --git a/src/global/Result.h b/src/global/Result.h deleted file mode 100644 index 96f373d..0000000 --- a/src/global/Result.h +++ /dev/null @@ -1,108 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "GlobalStatus.h" - -#include -#include -#include -#include -#include -#include - -class test_result; - -namespace governikus -{ - -class Result -{ - Q_GADGET - Q_DECLARE_TR_FUNCTIONS(governikus::Result) - - friend class StartPaosResponse; - friend class ::test_result; - - public: - enum class Major - { - Unknown, - Ok, - Warning, - Error - }; - - Q_ENUM(Major) - - private: - class ResultData - : public QSharedData - { - public: - const Major mMajor; - const GlobalStatus::Code mMinor; - const QString mMessage; - const QString mMessageLang; - const Origin mOrigin; - - ResultData(Major pMajor, GlobalStatus::Code pMinor, const QString& pMessage, Origin pOrigin); - - bool operator ==(const ResultData& pOther) const - { - return mMajor == pOther.mMajor && - mMinor == pOther.mMinor && - mMessage == pOther.mMessage && - mMessageLang == pOther.mMessageLang && - mOrigin == pOther.mOrigin; - } - - - }; - - static const QMap mMajorResults; - static const QMap mMinorResults; - - QSharedDataPointer d; - - static Result fromStatus(const GlobalStatus& pStatus); - - Result(Major pMajor, GlobalStatus::Code pMinor, const QString& pMessage = QString(), Origin pOrigin = Origin::Client); - Result(const QString& pMajor, const QString& pMinor = QString(), const QString& pMessage = QString(), Origin pOrigin = Origin::Client); - - public: - Result(const GlobalStatus& pStatus); - - bool operator ==(const Result& pResult) const; - - static Result createOk(); - - static Major parseMajor(const QString& pMajor); - static GlobalStatus::Code parseMinor(const QString& pMinor); - - static bool isMajor(const QString& pMajor); - static bool isMinor(const QString& pMinor); - static QString getMessage(GlobalStatus::Code pMinor); - - Major getMajor() const; - GlobalStatus::Code getMinor() const; - QString getMessage() const; - const QString& getMessageLang() const; - - QString getMajorString() const; - QString getMinorString() const; - - bool isValid() const; - bool isOk() const; - bool isOriginServer() const; - QJsonObject toJson() const; - - GlobalStatus toStatus() const; - operator GlobalStatus() const; -}; - -} - -QDebug operator <<(QDebug pDbg, const governikus::Result& pResult); diff --git a/src/global/ScopeGuard.h b/src/global/ScopeGuard.h index 0b2b50e..1489f19 100644 --- a/src/global/ScopeGuard.h +++ b/src/global/ScopeGuard.h @@ -34,4 +34,4 @@ class ScopeGuard void setEnabled(bool pEnabled = true); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/global/SingletonHelper.h b/src/global/SingletonHelper.h index 1f7a0eb..afd433f 100644 --- a/src/global/SingletonHelper.h +++ b/src/global/SingletonHelper.h @@ -22,4 +22,16 @@ \ Q_GLOBAL_STATIC(Singleton##className, instanceName) +#define defineSingletonInstanceImpl(className, instanceName, impl)\ + defineSingletonInstance(impl, instanceName)\ + namespace governikus\ + {\ + template<> className * singleton()\ + {\ + return Instance;\ + }\ + } + #define defineSingleton(className) defineSingletonInstance(className, Instance) + +#define defineSingletonImpl(className, impl) defineSingletonInstanceImpl(className, Instance, impl) diff --git a/src/global/UsbId.cpp b/src/global/UsbId.cpp index f8d72c0..1ac8f66 100644 --- a/src/global/UsbId.cpp +++ b/src/global/UsbId.cpp @@ -13,7 +13,6 @@ UsbId::UsbId(unsigned int pVendorId, unsigned int pProductId) : mVendorId(pVendorId) , mProductId(pProductId) { - } diff --git a/src/global/UsbId.h b/src/global/UsbId.h index 06ebaba..dec0966 100644 --- a/src/global/UsbId.h +++ b/src/global/UsbId.h @@ -6,7 +6,6 @@ #include -class QDebug; namespace governikus { @@ -26,7 +25,7 @@ class UsbId bool operator==(const UsbId& pOther) const; }; -} /* namespace governikus */ +} // namespace governikus Q_DECLARE_TYPEINFO(governikus::UsbId, Q_PRIMITIVE_TYPE); diff --git a/src/global/VersionInfo.cpp b/src/global/VersionInfo.cpp index a78b120..a3e4113 100644 --- a/src/global/VersionInfo.cpp +++ b/src/global/VersionInfo.cpp @@ -26,7 +26,7 @@ VERSION_NAME(IMPL_VERSION, "Implementation-Version") VERSION_NAME(SPEC_TITLE, "Specification-Title") VERSION_NAME(SPEC_VENDOR, "Specification-Vendor") VERSION_NAME(SPEC_VERSION, "Specification-Version") -} +} // namespace VersionInfo::VersionInfo(const QMap& pInfo) diff --git a/src/global/VersionInfo.h b/src/global/VersionInfo.h index cae5a61..43deeb3 100644 --- a/src/global/VersionInfo.h +++ b/src/global/VersionInfo.h @@ -42,6 +42,6 @@ class VersionInfo }; -} /* namespace governikus */ +} // namespace governikus QDebug operator<<(QDebug pDbg, const governikus::VersionInfo& pVersionInfo); diff --git a/src/global/VersionNumber.h b/src/global/VersionNumber.h index 8dcf6af..3679806 100644 --- a/src/global/VersionNumber.h +++ b/src/global/VersionNumber.h @@ -60,4 +60,4 @@ inline bool operator>(const VersionNumber& pLeft, const VersionNumber& pRight) } -} /* namespace governikus */ +} // namespace governikus diff --git a/src/jsonapi/CMakeLists.txt b/src/jsonapi/CMakeLists.txt deleted file mode 100644 index 56d2769..0000000 --- a/src/jsonapi/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -ADD_PLATFORM_LIBRARY(AusweisAppJsonApi) - -TARGET_LINK_LIBRARIES(AusweisAppJsonApi Qt5::Core AusweisAppCore AusweisAppGlobal AusweisAppActivationInternal) -TARGET_COMPILE_DEFINITIONS(AusweisAppJsonApi PRIVATE QT_STATICPLUGIN) diff --git a/src/jsonapi/MessageDispatcher.cpp b/src/jsonapi/MessageDispatcher.cpp deleted file mode 100644 index dfbe0b2..0000000 --- a/src/jsonapi/MessageDispatcher.cpp +++ /dev/null @@ -1,223 +0,0 @@ -/* - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "MessageDispatcher.h" - -#include "messages/MsgHandlerAccessRights.h" -#include "messages/MsgHandlerApiLevel.h" -#include "messages/MsgHandlerAuth.h" -#include "messages/MsgHandlerBadState.h" -#include "messages/MsgHandlerCertificate.h" -#include "messages/MsgHandlerEnterCan.h" -#include "messages/MsgHandlerEnterPin.h" -#include "messages/MsgHandlerEnterPuk.h" -#include "messages/MsgHandlerInfo.h" -#include "messages/MsgHandlerInsertCard.h" -#include "messages/MsgHandlerInternalError.h" -#include "messages/MsgHandlerInvalid.h" -#include "messages/MsgHandlerReader.h" -#include "messages/MsgHandlerReaderList.h" -#include "messages/MsgHandlerUnknownCommand.h" - -#include - -Q_DECLARE_LOGGING_CATEGORY(jsonapi) - -#define HANDLE_CURRENT_STATE(msgType, msgHandler) handleCurrentState(requestType, msgType, [&] {return msgHandler;}); - -using namespace governikus; - - -MessageDispatcher::MessageDispatcher() - : mContext() -{ -} - - -QByteArray MessageDispatcher::init(const QSharedPointer& pContext) -{ - Q_ASSERT(!mContext.isActiveWorkflow()); - - mContext.setWorkflowContext(pContext); - - if (mContext.getAuthContext()) - { - return MsgHandlerAuth().getOutput(); - } - - return QByteArray(); -} - - -QByteArray MessageDispatcher::createMsgReader(const QString& pName) const -{ - return MsgHandlerReader(pName).getOutput(); -} - - -QByteArray MessageDispatcher::finish() -{ - Q_ASSERT(mContext.isActiveWorkflow()); - - QByteArray result; - if (auto authContext = mContext.getAuthContext()) - { - result = MsgHandlerAuth(authContext).getOutput(); - } - - mContext.clear(); - return result; -} - - -QByteArray MessageDispatcher::processStateChange(const QString& pState) -{ - if (!mContext.isActiveWorkflow() || pState.isEmpty()) - { - qCritical(jsonapi) << "Unexpected condition:" << mContext.getWorkflowContext() << "|" << pState; - return MsgHandlerInternalError(QLatin1String("Unexpected condition")).getOutput(); - } - - const auto& msg = createForStateChange(MsgHandler::getStateMsgType(pState)); - mContext.addStateMsg(msg.getType()); - return msg.getOutput(); -} - - -MsgHandler MessageDispatcher::createForStateChange(MsgType pStateType) -{ - switch (pStateType) - { - case MsgType::ENTER_PIN: - return MsgHandlerEnterPin(mContext); - - case MsgType::ENTER_CAN: - return MsgHandlerEnterCan(mContext); - - case MsgType::ENTER_PUK: - return MsgHandlerEnterPuk(mContext); - - case MsgType::ACCESS_RIGHTS: - return MsgHandlerAccessRights(mContext); - - case MsgType::INSERT_CARD: - return MsgHandlerInsertCard(mContext); - - default: - mContext.getWorkflowContext()->setStateApproved(); - return MsgHandler::Void; - } -} - - -QByteArray MessageDispatcher::processCommand(const QByteArray& pMsg) -{ - QJsonParseError jsonError; - const auto& json = QJsonDocument::fromJson(pMsg, &jsonError); - if (jsonError.error != QJsonParseError::NoError) - { - return MsgHandlerInvalid(jsonError).getOutput(); - } - - const auto& obj = json.object(); - auto msg = createForCommand(obj); - msg.setRequest(obj); - return msg.getOutput(); -} - - -MsgHandler MessageDispatcher::createForCommand(const QJsonObject& pObj) -{ - const auto& cmd = pObj.value(QLatin1String("cmd")).toString(); - if (cmd.isEmpty()) - { - return MsgHandlerInvalid(QLatin1String("Command cannot be undefined")); - } - - auto requestType = Enum::fromString(cmd, MsgCmdType::UNDEFINED); - switch (requestType) - { - case MsgCmdType::UNDEFINED: - return MsgHandlerUnknownCommand(cmd); - - case MsgCmdType::CANCEL: - return cancel(); - - case MsgCmdType::ACCEPT: - return accept(); - - case MsgCmdType::GET_API_LEVEL: - return MsgHandlerApiLevel(mContext); - - case MsgCmdType::SET_API_LEVEL: - return MsgHandlerApiLevel(pObj, mContext); - - case MsgCmdType::GET_READER: - return MsgHandlerReader(pObj); - - case MsgCmdType::GET_READER_LIST: - return MsgHandlerReaderList(); - - case MsgCmdType::GET_INFO: - return MsgHandlerInfo(); - - case MsgCmdType::RUN_AUTH: - return mContext.isActiveWorkflow() ? MsgHandler(MsgHandlerBadState(requestType)) : MsgHandler(MsgHandlerAuth(pObj)); - - case MsgCmdType::GET_CERTIFICATE: - return HANDLE_CURRENT_STATE(MsgType::ACCESS_RIGHTS, MsgHandlerCertificate(mContext)); - - case MsgCmdType::SET_PIN: - return HANDLE_CURRENT_STATE(MsgType::ENTER_PIN, MsgHandlerEnterPin(pObj, mContext)); - - case MsgCmdType::SET_CAN: - return HANDLE_CURRENT_STATE(MsgType::ENTER_CAN, MsgHandlerEnterCan(pObj, mContext)); - - case MsgCmdType::SET_PUK: - return HANDLE_CURRENT_STATE(MsgType::ENTER_PUK, MsgHandlerEnterPuk(pObj, mContext)); - - case MsgCmdType::GET_ACCESS_RIGHTS: - return HANDLE_CURRENT_STATE(MsgType::ACCESS_RIGHTS, MsgHandlerAccessRights(mContext)); - - case MsgCmdType::SET_ACCESS_RIGHTS: - return HANDLE_CURRENT_STATE(MsgType::ACCESS_RIGHTS, MsgHandlerAccessRights(pObj, mContext)); - } - - return MsgHandlerInternalError(QLatin1String("Cannot process request")); -} - - -MsgHandler MessageDispatcher::handleCurrentState(MsgCmdType pCmdType, MsgType pMsgType, const std::function& pFunc) -{ - if (mContext.getLastStateMsg() == pMsgType) - { - return pFunc(); - } - - return MsgHandlerBadState(pCmdType); -} - - -MsgHandler MessageDispatcher::cancel() -{ - if (mContext.isActiveWorkflow()) - { - Q_EMIT mContext.getWorkflowContext()->fireCancelWorkflow(); - return MsgHandler::Void; - } - - return MsgHandlerBadState(MsgCmdType::CANCEL); -} - - -MsgHandler MessageDispatcher::accept() -{ - if (mContext.getLastStateMsg() == MsgType::ACCESS_RIGHTS) - { - mContext.getWorkflowContext()->setStateApproved(); - return MsgHandler::Void; - } - - return MsgHandlerBadState(MsgCmdType::ACCEPT); -} diff --git a/src/jsonapi/MessageDispatcher.h b/src/jsonapi/MessageDispatcher.h deleted file mode 100644 index 014d69e..0000000 --- a/src/jsonapi/MessageDispatcher.h +++ /dev/null @@ -1,45 +0,0 @@ -/*! - * \brief Dispatch Messages of JSON API. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "context/AuthContext.h" -#include "context/WorkflowContext.h" -#include "messages/MsgContext.h" -#include "messages/MsgHandler.h" - -#include -#include - -#include - -namespace governikus -{ - -class MessageDispatcher -{ - private: - MsgDispatcherContext mContext; - - MsgHandler createForStateChange(MsgType pStateType); - MsgHandler createForCommand(const QJsonObject& pObj); - - MsgHandler cancel(); - MsgHandler accept(); - MsgHandler handleCurrentState(MsgCmdType pCmdType, MsgType pMsgType, const std::function& pFunc); - - public: - MessageDispatcher(); - - QByteArray init(const QSharedPointer& pContext); - QByteArray finish(); - QByteArray processCommand(const QByteArray& pMsg); - QByteArray processStateChange(const QString& pState); - - QByteArray createMsgReader(const QString& pName) const; -}; - -} /* namespace governikus */ diff --git a/src/jsonapi/UIPlugInJsonApi.cpp b/src/jsonapi/UIPlugInJsonApi.cpp deleted file mode 100644 index 28658eb..0000000 --- a/src/jsonapi/UIPlugInJsonApi.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/*! - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "UIPlugInJsonApi.h" - -#include "ReaderManager.h" - -#include - - -Q_DECLARE_LOGGING_CATEGORY(jsonapi) - -using namespace governikus; - -UIPlugInJsonApi::UIPlugInJsonApi() - : UIPlugIn() - , mMessageDispatcher() -{ - connect(&ReaderManager::getInstance(), &ReaderManager::fireReaderAdded, this, &UIPlugInJsonApi::onReaderEvent); - connect(&ReaderManager::getInstance(), &ReaderManager::fireReaderRemoved, this, &UIPlugInJsonApi::onReaderEvent); - connect(&ReaderManager::getInstance(), &ReaderManager::fireCardInserted, this, &UIPlugInJsonApi::onReaderEvent); - connect(&ReaderManager::getInstance(), &ReaderManager::fireCardRemoved, this, &UIPlugInJsonApi::onReaderEvent); -} - - -UIPlugInJsonApi::~UIPlugInJsonApi() -{ -} - - -void UIPlugInJsonApi::callFireMessage(const QByteArray& pMsg) -{ - if (!pMsg.isEmpty()) - { - qCDebug(jsonapi).noquote() << "Fire message:" << pMsg; - Q_EMIT fireMessage(pMsg); - } -} - - -void UIPlugInJsonApi::onWorkflowStarted(QSharedPointer pContext) -{ - if (pContext.objectCast()) - { -#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) - pContext->setReaderPlugInTypes({ReaderManagerPlugInType::NFC}); -#else - pContext->setReaderPlugInTypes({ReaderManagerPlugInType::PCSC}); -#endif - - connect(pContext.data(), &WorkflowContext::fireStateChanged, this, &UIPlugInJsonApi::onStateChanged); - } - - callFireMessage(mMessageDispatcher.init(pContext)); -} - - -void UIPlugInJsonApi::onWorkflowFinished(QSharedPointer ) -{ - callFireMessage(mMessageDispatcher.finish()); -} - - -void UIPlugInJsonApi::onReaderEvent(const QString& pName) -{ - callFireMessage(mMessageDispatcher.createMsgReader(pName)); -} - - -void UIPlugInJsonApi::onStateChanged(const QString& pNewState) -{ - callFireMessage(mMessageDispatcher.processStateChange(pNewState)); -} - - -void UIPlugInJsonApi::doMessageProcessing(const QByteArray& pMsg) -{ - callFireMessage(mMessageDispatcher.processCommand(pMsg)); -} - - -void UIPlugInJsonApi::doShutdown() -{ -} diff --git a/src/jsonapi/UIPlugInJsonApi.h b/src/jsonapi/UIPlugInJsonApi.h deleted file mode 100644 index f2872ad..0000000 --- a/src/jsonapi/UIPlugInJsonApi.h +++ /dev/null @@ -1,46 +0,0 @@ -/*! - * \brief UIPlugIn implementation of the Json API. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "context/AuthContext.h" -#include "MessageDispatcher.h" -#include "view/UIPlugIn.h" - -namespace governikus -{ - -class UIPlugInJsonApi - : public UIPlugIn -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID "governikus.UIPlugIn" FILE "metadata.json") - Q_INTERFACES(governikus::UIPlugIn) - - private: - MessageDispatcher mMessageDispatcher; - - inline void callFireMessage(const QByteArray& pMsg); - - public: - UIPlugInJsonApi(); - virtual ~UIPlugInJsonApi() override; - - private Q_SLOTS: - virtual void doShutdown() override; - virtual void onWorkflowStarted(QSharedPointer pContext) override; - virtual void onWorkflowFinished(QSharedPointer pContext) override; - void onReaderEvent(const QString& pName); - void onStateChanged(const QString& pNewState); - - public Q_SLOTS: - void doMessageProcessing(const QByteArray& pMsg); - - Q_SIGNALS: - void fireMessage(const QByteArray& pMsg); -}; - -} /* namespace governikus */ diff --git a/src/jsonapi/messages/MsgContext.h b/src/jsonapi/messages/MsgContext.h deleted file mode 100644 index 02e8a74..0000000 --- a/src/jsonapi/messages/MsgContext.h +++ /dev/null @@ -1,51 +0,0 @@ -/*! - * \brief Context of JSON API. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "context/AuthContext.h" -#include "context/WorkflowContext.h" -#include "MsgTypes.h" - -namespace governikus -{ - -class MsgContext -{ - Q_DISABLE_COPY(MsgContext) - - protected: - MsgLevel mApiLevel; - QList mStateMessages; - QSharedPointer mContext; - - public: - MsgContext(); - - void setApiLevel(MsgLevel pApiLevel); - MsgLevel getApiLevel() const; - - MsgType getLastStateMsg() const; - - bool isActiveWorkflow() const; - - QSharedPointer getAuthContext(); - QSharedPointer getAuthContext() const; - - QSharedPointer getWorkflowContext(); - QSharedPointer getWorkflowContext() const; -}; - -class MsgDispatcherContext - : public MsgContext -{ - public: - void clear(); - void addStateMsg(MsgType pMsgType); - void setWorkflowContext(const QSharedPointer& pContext); -}; - -} /* namespace governikus */ diff --git a/src/jsonapi/messages/MsgHandler.cpp b/src/jsonapi/messages/MsgHandler.cpp deleted file mode 100644 index c9bfda4..0000000 --- a/src/jsonapi/messages/MsgHandler.cpp +++ /dev/null @@ -1,155 +0,0 @@ -/*! - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "MsgHandler.h" - -#include "states/StateEditAccessRights.h" -#include "states/StateEstablishPaceCan.h" -#include "states/StateEstablishPacePin.h" -#include "states/StateEstablishPacePuk.h" -#include "states/StateSelectReader.h" - -#include - -using namespace governikus; - -const MsgLevel MsgHandler::DEFAULT_MSG_LEVEL = MsgLevel::v1; - -const MsgHandler MsgHandler::Void = MsgHandler(); - - -MsgType MsgHandler::getStateMsgType(const QString& pState) -{ - if (AbstractState::isState(pState)) - { - return MsgType::ENTER_PIN; - } - else if (AbstractState::isState(pState)) - { - return MsgType::ENTER_CAN; - } - else if (AbstractState::isState(pState)) - { - return MsgType::ENTER_PUK; - } - else if (AbstractState::isState(pState)) - { - return MsgType::ACCESS_RIGHTS; - } - else if (AbstractState::isState(pState)) - { - return MsgType::INSERT_CARD; - } - - // indicates "do not use this" otherwise it is an internal error! - return MsgType::INTERNAL_ERROR; -} - - -MsgHandler::MsgHandler() - : MsgHandler(MsgType::INTERNAL_ERROR) -{ - setVoid(); -} - - -MsgHandler::MsgHandler(MsgType pType) - : mType(pType) - , mVoid(false) - , mJsonObject() -{ - mJsonObject[QLatin1String("msg")] = getEnumName(mType); -} - - -MsgHandler::MsgHandler(MsgType pType, const char* pKey, const QString& pValue) - : MsgHandler(pType) -{ - setValue(pKey, pValue); -} - - -MsgHandler::MsgHandler(MsgType pType, const char* pKey, const QLatin1String pValue) - : MsgHandler(pType) -{ - setValue(pKey, pValue); -} - - -QByteArray MsgHandler::toJson() const -{ - Q_ASSERT(mJsonObject[QLatin1String("msg")].isString()); - return QJsonDocument(mJsonObject).toJson(QJsonDocument::Compact); -} - - -QByteArray MsgHandler::getOutput() const -{ - if (isVoid()) - { - return QByteArray(); - } - - return toJson(); -} - - -bool MsgHandler::isVoid() const -{ - return mVoid; -} - - -MsgType MsgHandler::getType() const -{ - return mType; -} - - -void MsgHandler::setRequest(const QJsonObject& pRequest) -{ - const QLatin1String requestName("request"); - - const auto& requestValue = pRequest[requestName]; - if (!requestValue.isUndefined()) - { - mJsonObject[requestName] = requestValue; - } -} - - -void MsgHandler::setValue(const char* pKey, const QString& pValue) -{ - setValue(QLatin1String(pKey), pValue); -} - - -void MsgHandler::setValue(const QLatin1String pKey, const QLatin1String pValue) -{ - if (pValue.size()) - { - mJsonObject[pKey] = pValue; - } -} - - -void MsgHandler::setValue(const char* pKey, const QLatin1String pValue) -{ - setValue(QLatin1String(pKey), pValue); -} - - -void MsgHandler::setVoid(bool pVoid) -{ - mVoid = pVoid; -} - - -void MsgHandler::setValue(const QLatin1String pKey, const QString& pValue) -{ - if (!pValue.isEmpty()) - { - mJsonObject[pKey] = pValue; - } -} diff --git a/src/jsonapi/messages/MsgHandler.h b/src/jsonapi/messages/MsgHandler.h deleted file mode 100644 index abde282..0000000 --- a/src/jsonapi/messages/MsgHandler.h +++ /dev/null @@ -1,58 +0,0 @@ -/*! - * \brief Base of all messages of JSON API. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "MsgTypes.h" - -#include - -namespace governikus -{ -class MsgHandler -{ - private: - const MsgType mType; - bool mVoid; - - MsgHandler(); - - protected: - QJsonObject mJsonObject; - - MsgHandler(MsgType pType); - MsgHandler(MsgType pType, const char* pKey, const QString& pValue); - MsgHandler(MsgType pType, const char* pKey, const QLatin1String pValue); - - void setValue(const QLatin1String pKey, const QString& pValue); - void setValue(const char* pKey, const QString& pValue); - void setValue(const QLatin1String pKey, const QLatin1String pValue); - void setValue(const char* pKey, const QLatin1String pValue); - - void setVoid(bool pVoid = true); - - public: - static const MsgHandler Void; - static const MsgLevel DEFAULT_MSG_LEVEL; - static MsgType getStateMsgType(const QString& pState); - - QByteArray toJson() const; - QByteArray getOutput() const; - bool isVoid() const; - MsgType getType() const; - - void setRequest(const QJsonObject& pRequest); -}; - -inline QDebug operator<<(QDebug pDbg, const MsgHandler& pMsg) -{ - QDebugStateSaver saver(pDbg); - pDbg << pMsg.getType(); - return pDbg.space(); -} - - -} /* namespace governikus */ diff --git a/src/jsonapi/messages/MsgHandlerAccessRights.cpp b/src/jsonapi/messages/MsgHandlerAccessRights.cpp deleted file mode 100644 index 8a05f8e..0000000 --- a/src/jsonapi/messages/MsgHandlerAccessRights.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/*! - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "MsgHandlerAccessRights.h" - -#include - -using namespace governikus; - -MsgHandlerAccessRights::MsgHandlerAccessRights(const MsgContext& pContext) - : MsgHandler(MsgType::ACCESS_RIGHTS) -{ - Q_ASSERT(pContext.getAuthContext()); - fillAccessRights(pContext.getAuthContext()); -} - - -MsgHandlerAccessRights::MsgHandlerAccessRights(const QJsonObject& pObj, MsgContext& pContext) - : MsgHandler(MsgType::ACCESS_RIGHTS) -{ - auto ctx = pContext.getAuthContext(); - Q_ASSERT(ctx); - - const auto& jsonRaw = pObj[QLatin1String("chat")]; - if (jsonRaw.isUndefined()) - { - setError(QLatin1String("'chat' cannot be undefined")); - } - else if (!jsonRaw.isArray()) - { - setError(QLatin1String("Invalid 'chat' data")); - } - else - { - handleSetChatData(jsonRaw.toArray(), ctx); - } - - fillAccessRights(ctx); -} - - -void MsgHandlerAccessRights::handleSetChatData(const QJsonArray& pChat, const QSharedPointer& pContext) -{ - Q_ASSERT(pContext); - - QSet effectiveChat; - - if (!pContext->getOptionalAccessRights().isEmpty()) - { - for (const auto& entry : pChat) - { - if (entry.isString()) - { - const auto& func = [&](AccessRight pRight){ - if (pContext->getOptionalAccessRights().contains(pRight)) - { - effectiveChat += pRight; - } - else - { - setError(QLatin1String("Entry in 'chat' data is not available")); - } - }; - - if (!AccessRoleAndRightsUtil::fromTechnicalName(entry.toString().toLatin1().constData(), func)) - { - setError(QLatin1String("Entry in 'chat' data is invalid")); - } - } - else - { - setError(QLatin1String("Entry in 'chat' data needs to be string")); - } - } - } - else - { - setError(QLatin1String("No optional access rights available")); - } - - - if (!mJsonObject.contains(QLatin1String("error"))) - { - pContext->setEffectiveAccessRights(effectiveChat); - } -} - - -QJsonArray MsgHandlerAccessRights::getAccessRights(const QSet& pRights) const -{ - QJsonArray array; - - QList accessRights = pRights.toList(); - std::sort(accessRights.rbegin(), accessRights.rend()); - for (auto entry : qAsConst(accessRights)) - { - const QLatin1String name = AccessRoleAndRightsUtil::toTechnicalName(entry); - if (name.size()) - { - array += name; - } - } - - return array; -} - - -void MsgHandlerAccessRights::fillAccessRights(const QSharedPointer& pContext) -{ - Q_ASSERT(pContext); - - QJsonObject chat; - chat[QLatin1String("required")] = getAccessRights(pContext->getRequiredAccessRights()); - chat[QLatin1String("optional")] = getAccessRights(pContext->getOptionalAccessRights()); - chat[QLatin1String("effective")] = getAccessRights(pContext->getEffectiveAccessRights()); - - mJsonObject[QLatin1String("chat")] = chat; - const auto& transactionInfo = pContext->getDidAuthenticateEac1()->getTransactionInfo(); - if (!transactionInfo.isEmpty()) - { - mJsonObject[QLatin1String("transactionInfo")] = transactionInfo; - } - - const QJsonObject& aux = getAuxiliaryData(pContext); - if (!aux.isEmpty()) - { - mJsonObject[QLatin1String("aux")] = aux; - } -} - - -QJsonObject MsgHandlerAccessRights::getAuxiliaryData(const QSharedPointer& pContext) -{ - QJsonObject obj; - - const auto& eac1 = pContext->getDidAuthenticateEac1(); - if (eac1) - { - const auto& aux = eac1->getAuthenticatedAuxiliaryData(); - if (aux) - { - if (aux->hasAgeVerificationDate()) - { - obj[QLatin1String("ageVerificationDate")] = aux->getAgeVerificationDate().toString(Qt::ISODate); - obj[QLatin1String("requiredAge")] = aux->getRequiredAge(); - } - - if (aux->hasValidityDate()) - { - obj[QLatin1String("validityDate")] = aux->getValidityDate().toString(Qt::ISODate); - } - - if (aux->hasCommunityID()) - { - obj[QLatin1String("communityId")] = QString::fromUtf8(aux->getCommunityID()); - } - } - } - - return obj; -} - - -void MsgHandlerAccessRights::setError(const QLatin1String pError) -{ - mJsonObject[QLatin1String("error")] = pError; -} diff --git a/src/jsonapi/messages/MsgHandlerAccessRights.h b/src/jsonapi/messages/MsgHandlerAccessRights.h deleted file mode 100644 index 27b5486..0000000 --- a/src/jsonapi/messages/MsgHandlerAccessRights.h +++ /dev/null @@ -1,35 +0,0 @@ -/*! - * \brief Message MsgHandlerAccessRights of JSON API. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "context/AuthContext.h" -#include "MsgContext.h" -#include "MsgHandler.h" - -#include - -namespace governikus -{ - -class MsgHandlerAccessRights - : public MsgHandler -{ - private: - void setError(const QLatin1String pError); - - void handleSetChatData(const QJsonArray& pChat, const QSharedPointer& pContext); - QJsonArray getAccessRights(const QSet& pRights) const; - void fillAccessRights(const QSharedPointer& pContext); - QJsonObject getAuxiliaryData(const QSharedPointer& pContext); - - public: - MsgHandlerAccessRights(const MsgContext& pContext); - MsgHandlerAccessRights(const QJsonObject& pObj, MsgContext& pContext); -}; - - -} /* namespace governikus */ diff --git a/src/jsonapi/messages/MsgHandlerApiLevel.h b/src/jsonapi/messages/MsgHandlerApiLevel.h deleted file mode 100644 index f4fb846..0000000 --- a/src/jsonapi/messages/MsgHandlerApiLevel.h +++ /dev/null @@ -1,29 +0,0 @@ -/*! - * \brief Message API_LEVEL of JSON API. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "MsgContext.h" -#include "MsgHandler.h" - -namespace governikus -{ - -class MsgHandlerApiLevel - : public MsgHandler -{ - private: - void setError(const QLatin1String pError); - void setCurrentLevel(MsgLevel pLevel); - void setAvailableLevel(); - - public: - MsgHandlerApiLevel(const MsgContext& pContext); - MsgHandlerApiLevel(const QJsonObject& pObj, MsgContext& pContext); -}; - - -} /* namespace governikus */ diff --git a/src/jsonapi/messages/MsgHandlerAuth.cpp b/src/jsonapi/messages/MsgHandlerAuth.cpp deleted file mode 100644 index fd9c6b6..0000000 --- a/src/jsonapi/messages/MsgHandlerAuth.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/*! - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "MsgHandlerAuth.h" - -#include "InternalActivationHandler.h" - -#include -#include - -using namespace governikus; - - -MsgHandlerAuth::MsgHandlerAuth() - : MsgHandler(MsgType::AUTH) -{ -} - - -MsgHandlerAuth::MsgHandlerAuth(const QJsonObject& pObj) - : MsgHandlerAuth() -{ - const auto& jsonTcTokenUrl = pObj[QLatin1String("tcTokenURL")]; - if (jsonTcTokenUrl.isUndefined()) - { - setError(QLatin1String("tcTokenURL cannot be undefined")); - } - else if (!jsonTcTokenUrl.isString()) - { - setError(QLatin1String("Invalid tcTokenURL")); - } - else - { - const auto& url = createUrl(jsonTcTokenUrl.toString()); - if (url.isValid()) - { - initAuth(url); - setVoid(); - return; - } - Q_ASSERT(mJsonObject[QLatin1String("error")].isString()); - } -} - - -MsgHandlerAuth::MsgHandlerAuth(const QSharedPointer& pContext) - : MsgHandlerAuth() -{ - Q_ASSERT(pContext); - - mJsonObject[QLatin1String("result")] = Result(pContext->getStatus()).toJson(); - - QString url; - if (pContext->getRefreshUrl().isEmpty()) - { - const auto& token = pContext->getTcToken(); - if (!token.isNull() && pContext->getTcToken()->getCommunicationErrorAddress().isValid()) - { - url = pContext->getTcToken()->getCommunicationErrorAddress().toString(); - } - } - else - { - url = pContext->getRefreshUrl().toString(); - } - - setValue("url", url); -} - - -QUrl MsgHandlerAuth::createUrl(const QString& pUrl) -{ - const QUrl parsedUrl(pUrl); - if (parsedUrl.isValid() && !parsedUrl.host().isEmpty()) - { - QUrlQuery query; - query.addQueryItem(QStringLiteral("tcTokenURL"), QString::fromLatin1(QUrl::toPercentEncoding(pUrl))); - - QUrl url(QStringLiteral("http://localhost/")); // just a dummy for StateParseTcTokenUrl - url.setQuery(query); - - return url; - } - - setError(QLatin1String("Validation of tcTokenURL failed")); - return QUrl(); -} - - -void MsgHandlerAuth::initAuth(const QUrl& pTcTokenUrl) -{ - auto handler = ActivationHandler::getInstance(); - Q_ASSERT(handler); - handler->runAuthentication(QSharedPointer::create(pTcTokenUrl)); -} - - -void MsgHandlerAuth::setError(const QLatin1String pError) -{ - setValue("error", pError); -} diff --git a/src/jsonapi/messages/MsgHandlerAuth.h b/src/jsonapi/messages/MsgHandlerAuth.h deleted file mode 100644 index 0dffbde..0000000 --- a/src/jsonapi/messages/MsgHandlerAuth.h +++ /dev/null @@ -1,31 +0,0 @@ -/*! - * \brief Message Auth of JSON API. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "MsgHandler.h" - -#include "context/AuthContext.h" - -namespace governikus -{ - -class MsgHandlerAuth - : public MsgHandler -{ - private: - QUrl createUrl(const QString& pUrl); - void initAuth(const QUrl& pTcTokenUrl); - void setError(const QLatin1String pError); - - public: - MsgHandlerAuth(); - MsgHandlerAuth(const QJsonObject& pObj); - MsgHandlerAuth(const QSharedPointer& pContext); -}; - - -} /* namespace governikus */ diff --git a/src/jsonapi/messages/MsgHandlerBadState.h b/src/jsonapi/messages/MsgHandlerBadState.h deleted file mode 100644 index 5741afc..0000000 --- a/src/jsonapi/messages/MsgHandlerBadState.h +++ /dev/null @@ -1,22 +0,0 @@ -/*! - * \brief Message BadState of JSON API. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "MsgHandler.h" - -namespace governikus -{ - -class MsgHandlerBadState - : public MsgHandler -{ - public: - MsgHandlerBadState(MsgCmdType pType = MsgCmdType::UNDEFINED); -}; - - -} /* namespace governikus */ diff --git a/src/jsonapi/messages/MsgHandlerCertificate.h b/src/jsonapi/messages/MsgHandlerCertificate.h deleted file mode 100644 index 908fdbb..0000000 --- a/src/jsonapi/messages/MsgHandlerCertificate.h +++ /dev/null @@ -1,23 +0,0 @@ -/*! - * \brief Message handler for GET_CERTIFICATE of JSON API. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "MsgContext.h" -#include "MsgHandler.h" - -namespace governikus -{ - -class MsgHandlerCertificate - : public MsgHandler -{ - public: - MsgHandlerCertificate(const MsgContext& pContext); -}; - - -} /* namespace governikus */ diff --git a/src/jsonapi/messages/MsgHandlerEnterCan.cpp b/src/jsonapi/messages/MsgHandlerEnterCan.cpp deleted file mode 100644 index e9d4197..0000000 --- a/src/jsonapi/messages/MsgHandlerEnterCan.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/*! - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "MsgHandlerEnterCan.h" - -#include "context/WorkflowContext.h" - -using namespace governikus; - -MsgHandlerEnterCan::MsgHandlerEnterCan(const MsgContext& pContext) - : MsgHandlerEnterNumber(MsgType::ENTER_CAN, pContext) -{ -} - - -MsgHandlerEnterCan::MsgHandlerEnterCan(const QJsonObject& pObj, MsgContext& pContext) - : MsgHandlerEnterCan(pContext) -{ - parseValue(pObj, [&](const QString& pNumber) - { - auto ctx = pContext.getWorkflowContext(); - ctx->setCan(pNumber); - ctx->setStateApproved(); - setVoid(); - }); -} diff --git a/src/jsonapi/messages/MsgHandlerEnterCan.h b/src/jsonapi/messages/MsgHandlerEnterCan.h deleted file mode 100644 index 79dceac..0000000 --- a/src/jsonapi/messages/MsgHandlerEnterCan.h +++ /dev/null @@ -1,24 +0,0 @@ -/*! - * \brief Message EnterCan of JSON API. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "MsgContext.h" -#include "MsgHandlerEnterNumber.h" - -namespace governikus -{ - -class MsgHandlerEnterCan - : public MsgHandlerEnterNumber -{ - public: - MsgHandlerEnterCan(const MsgContext& pContext); - MsgHandlerEnterCan(const QJsonObject& pObj, MsgContext& pContext); -}; - - -} /* namespace governikus */ diff --git a/src/jsonapi/messages/MsgHandlerEnterNumber.cpp b/src/jsonapi/messages/MsgHandlerEnterNumber.cpp deleted file mode 100644 index 67b13ce..0000000 --- a/src/jsonapi/messages/MsgHandlerEnterNumber.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/*! - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "MsgHandlerEnterNumber.h" - -#include "MsgHandlerReader.h" -#include "ReaderManager.h" - -#include - -using namespace governikus; - -MsgHandlerEnterNumber::MsgHandlerEnterNumber(MsgType pType, const MsgContext& pContext) - : MsgHandler(pType) -{ - Q_ASSERT(pContext.getWorkflowContext()); - setReader(pContext.getWorkflowContext()); -} - - -void MsgHandlerEnterNumber::setError(const QString& pError) -{ - mJsonObject[QLatin1String("error")] = pError; -} - - -void MsgHandlerEnterNumber::setReader(const QSharedPointer& pContext) -{ - const auto& reader = pContext->getReaderName(); - if (!reader.isEmpty()) - { - const auto& info = ReaderManager::getInstance().getReaderInfo(reader); - if (info.isConnected()) - { - mJsonObject[QLatin1String("reader")] = MsgHandlerReader::createReaderInfo(info); - } - } -} - - -void MsgHandlerEnterNumber::parseValue(const QJsonObject& pObj, const std::function& pFunc, ushort pCount) -{ - const auto& value = pObj[QLatin1String("value")]; - if (value.isUndefined()) - { - setError(QStringLiteral("Value cannot be undefined")); - } - else if (!value.isString()) - { - setError(QStringLiteral("Invalid value")); - } - else - { - const auto& regex = QStringLiteral("^[0-9]{%1}$").arg(pCount); - const auto& number = value.toString(); - if (QRegularExpression(regex).match(number).hasMatch()) - { - pFunc(number); - } - else - { - setError(QStringLiteral("You must provide %1 digits").arg(pCount)); - } - } -} diff --git a/src/jsonapi/messages/MsgHandlerEnterNumber.h b/src/jsonapi/messages/MsgHandlerEnterNumber.h deleted file mode 100644 index f08072b..0000000 --- a/src/jsonapi/messages/MsgHandlerEnterNumber.h +++ /dev/null @@ -1,34 +0,0 @@ -/*! - * \brief Helper handler for EnterCan, EnterPin and EnterPuk of JSON API. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "context/WorkflowContext.h" -#include "MsgContext.h" -#include "MsgHandler.h" - -#include - -namespace governikus -{ - -class MsgHandlerEnterNumber - : public MsgHandler -{ - private: - void setError(const QString& pError); - void setReader(const QSharedPointer& pContext); - - protected: - MsgHandlerEnterNumber(MsgType pType, const MsgContext& pContext); - - void parseValue(const QJsonObject& pObj, - const std::function& pFunc, - ushort pCount = 6); -}; - - -} /* namespace governikus */ diff --git a/src/jsonapi/messages/MsgHandlerEnterPin.cpp b/src/jsonapi/messages/MsgHandlerEnterPin.cpp deleted file mode 100644 index 06a781f..0000000 --- a/src/jsonapi/messages/MsgHandlerEnterPin.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/*! - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "MsgHandlerEnterPin.h" - -#include "context/WorkflowContext.h" - -using namespace governikus; - -MsgHandlerEnterPin::MsgHandlerEnterPin(const MsgContext& pContext) - : MsgHandlerEnterNumber(MsgType::ENTER_PIN, pContext) -{ -} - - -MsgHandlerEnterPin::MsgHandlerEnterPin(const QJsonObject& pObj, MsgContext& pContext) - : MsgHandlerEnterPin(pContext) -{ - parseValue(pObj, [&](const QString& pNumber) - { - auto ctx = pContext.getWorkflowContext(); - ctx->setPin(pNumber); - ctx->setStateApproved(); - setVoid(); - }); -} diff --git a/src/jsonapi/messages/MsgHandlerEnterPin.h b/src/jsonapi/messages/MsgHandlerEnterPin.h deleted file mode 100644 index 967bc56..0000000 --- a/src/jsonapi/messages/MsgHandlerEnterPin.h +++ /dev/null @@ -1,24 +0,0 @@ -/*! - * \brief Message EnterPin of JSON API. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "MsgContext.h" -#include "MsgHandlerEnterNumber.h" - -namespace governikus -{ - -class MsgHandlerEnterPin - : public MsgHandlerEnterNumber -{ - public: - MsgHandlerEnterPin(const MsgContext& pContext); - MsgHandlerEnterPin(const QJsonObject& pObj, MsgContext& pContext); -}; - - -} /* namespace governikus */ diff --git a/src/jsonapi/messages/MsgHandlerEnterPuk.cpp b/src/jsonapi/messages/MsgHandlerEnterPuk.cpp deleted file mode 100644 index 5c3c096..0000000 --- a/src/jsonapi/messages/MsgHandlerEnterPuk.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/*! - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "MsgHandlerEnterPuk.h" - -#include "context/WorkflowContext.h" - -using namespace governikus; - -MsgHandlerEnterPuk::MsgHandlerEnterPuk(const MsgContext& pContext) - : MsgHandlerEnterNumber(MsgType::ENTER_PUK, pContext) -{ -} - - -MsgHandlerEnterPuk::MsgHandlerEnterPuk(const QJsonObject& pObj, MsgContext& pContext) - : MsgHandlerEnterPuk(pContext) -{ - parseValue(pObj, [&](const QString& pNumber) - { - auto ctx = pContext.getWorkflowContext(); - ctx->setPuk(pNumber); - ctx->setStateApproved(); - setVoid(); - }, 10); -} diff --git a/src/jsonapi/messages/MsgHandlerEnterPuk.h b/src/jsonapi/messages/MsgHandlerEnterPuk.h deleted file mode 100644 index ba8333e..0000000 --- a/src/jsonapi/messages/MsgHandlerEnterPuk.h +++ /dev/null @@ -1,24 +0,0 @@ -/*! - * \brief Message EnterPuk of JSON API. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "MsgContext.h" -#include "MsgHandlerEnterNumber.h" - -namespace governikus -{ - -class MsgHandlerEnterPuk - : public MsgHandlerEnterNumber -{ - public: - MsgHandlerEnterPuk(const MsgContext& pContext); - MsgHandlerEnterPuk(const QJsonObject& pObj, MsgContext& pContext); -}; - - -} /* namespace governikus */ diff --git a/src/jsonapi/messages/MsgHandlerInfo.h b/src/jsonapi/messages/MsgHandlerInfo.h deleted file mode 100644 index 266e860..0000000 --- a/src/jsonapi/messages/MsgHandlerInfo.h +++ /dev/null @@ -1,22 +0,0 @@ -/*! - * \brief Message Info of JSON API. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "MsgHandler.h" - -namespace governikus -{ - -class MsgHandlerInfo - : public MsgHandler -{ - public: - MsgHandlerInfo(); -}; - - -} /* namespace governikus */ diff --git a/src/jsonapi/messages/MsgHandlerInsertCard.cpp b/src/jsonapi/messages/MsgHandlerInsertCard.cpp deleted file mode 100644 index 3abfc71..0000000 --- a/src/jsonapi/messages/MsgHandlerInsertCard.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/*! - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "MsgHandlerInsertCard.h" - -#include "ReaderManager.h" - -using namespace governikus; - -MsgHandlerInsertCard::MsgHandlerInsertCard(MsgContext& pContext) - : MsgHandler(MsgType::INSERT_CARD) -{ - Q_ASSERT(pContext.getWorkflowContext()); - pContext.getWorkflowContext()->setStateApproved(); - - const auto& infos = ReaderManager::getInstance().getReaderInfos(); - for (const auto& entry : infos) - { - if (entry.hasEidCard()) - { - setVoid(); - break; - } - } -} diff --git a/src/jsonapi/messages/MsgHandlerInsertCard.h b/src/jsonapi/messages/MsgHandlerInsertCard.h deleted file mode 100644 index 64df3db..0000000 --- a/src/jsonapi/messages/MsgHandlerInsertCard.h +++ /dev/null @@ -1,23 +0,0 @@ -/*! - * \brief Message InsertCard of JSON API. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "MsgContext.h" -#include "MsgHandler.h" - -namespace governikus -{ - -class MsgHandlerInsertCard - : public MsgHandler -{ - public: - MsgHandlerInsertCard(MsgContext& pContext); -}; - - -} /* namespace governikus */ diff --git a/src/jsonapi/messages/MsgHandlerInternalError.h b/src/jsonapi/messages/MsgHandlerInternalError.h deleted file mode 100644 index a474cf1..0000000 --- a/src/jsonapi/messages/MsgHandlerInternalError.h +++ /dev/null @@ -1,23 +0,0 @@ -/*! - * \brief Message INTERNAL_ERROR of JSON API. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "MsgHandler.h" - -namespace governikus -{ - -class MsgHandlerInternalError - : public MsgHandler -{ - public: - MsgHandlerInternalError(const QString& pError = QString()); - MsgHandlerInternalError(const QLatin1String pError); -}; - - -} /* namespace governikus */ diff --git a/src/jsonapi/messages/MsgHandlerInvalid.h b/src/jsonapi/messages/MsgHandlerInvalid.h deleted file mode 100644 index 0d56940..0000000 --- a/src/jsonapi/messages/MsgHandlerInvalid.h +++ /dev/null @@ -1,26 +0,0 @@ -/*! - * \brief Message Invalid of JSON API. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "MsgHandler.h" - -#include - -namespace governikus -{ - -class MsgHandlerInvalid - : public MsgHandler -{ - public: - MsgHandlerInvalid(const QString& pError = QString()); - MsgHandlerInvalid(const QLatin1String pError); - MsgHandlerInvalid(const QJsonParseError& pError); -}; - - -} /* namespace governikus */ diff --git a/src/jsonapi/messages/MsgHandlerReader.cpp b/src/jsonapi/messages/MsgHandlerReader.cpp deleted file mode 100644 index e200c8d..0000000 --- a/src/jsonapi/messages/MsgHandlerReader.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/*! - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "MsgHandlerReader.h" - -#include "ReaderManager.h" - -using namespace governikus; - -MsgHandlerReader::MsgHandlerReader(const QJsonObject& pObj) - : MsgHandler(MsgType::READER) -{ - const auto& jsonName = pObj[QLatin1String("name")]; - if (jsonName.isUndefined()) - { - setError(QLatin1String("Name cannot be undefined")); - } - else if (!jsonName.isString()) - { - setError(QLatin1String("Invalid name")); - } - else - { - setReaderInfo(jsonName.toString()); - } -} - - -MsgHandlerReader::MsgHandlerReader(const QString& pName) - : MsgHandler(MsgType::READER) -{ - Q_ASSERT(!pName.isEmpty()); - setReaderInfo(pName); -} - - -void MsgHandlerReader::setError(const QLatin1String pError) -{ - mJsonObject[QLatin1String("error")] = pError; -} - - -void MsgHandlerReader::setReaderInfo(const QString& pName) -{ - setReaderInfo(mJsonObject, ReaderManager::getInstance().getReaderInfo(pName)); -} - - -QJsonObject MsgHandlerReader::createReaderInfo(const ReaderInfo& pInfo) -{ - Q_ASSERT(!pInfo.getName().isEmpty()); - QJsonObject obj; - setReaderInfo(obj, pInfo); - return obj; -} - - -void MsgHandlerReader::setReaderInfo(QJsonObject& pObj, const ReaderInfo& pInfo) -{ - pObj[QLatin1String("name")] = pInfo.getName(); - pObj[QLatin1String("attached")] = pInfo.isConnected(); - if (pInfo.isConnected()) - { - if (pInfo.hasEidCard()) - { - QJsonObject card; - card[QLatin1String("deactivated")] = pInfo.isPinDeactivated(); - card[QLatin1String("inoperative")] = pInfo.isPukInoperative(); - card[QLatin1String("retryCounter")] = pInfo.getRetryCounter(); - pObj[QLatin1String("card")] = card; - } - else - { - pObj[QLatin1String("card")] = QJsonValue::Null; - } - } -} diff --git a/src/jsonapi/messages/MsgHandlerReader.h b/src/jsonapi/messages/MsgHandlerReader.h deleted file mode 100644 index bcf130a..0000000 --- a/src/jsonapi/messages/MsgHandlerReader.h +++ /dev/null @@ -1,32 +0,0 @@ -/*! - * \brief Message Reader of JSON API. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "MsgHandler.h" -#include "ReaderInfo.h" - -namespace governikus -{ - -class MsgHandlerReader - : public MsgHandler -{ - private: - static void setReaderInfo(QJsonObject& pObj, const ReaderInfo& pInfo); - - void setError(const QLatin1String pError); - void setReaderInfo(const QString& pName); - - public: - static QJsonObject createReaderInfo(const ReaderInfo& pInfo); - - MsgHandlerReader(const QJsonObject& pObj); - MsgHandlerReader(const QString& pName); -}; - - -} /* namespace governikus */ diff --git a/src/jsonapi/messages/MsgHandlerReaderList.cpp b/src/jsonapi/messages/MsgHandlerReaderList.cpp deleted file mode 100644 index e898460..0000000 --- a/src/jsonapi/messages/MsgHandlerReaderList.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/*! - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "MsgHandlerReaderList.h" - -#include "MsgHandlerReader.h" -#include "ReaderManager.h" - -#include - -using namespace governikus; - -MsgHandlerReaderList::MsgHandlerReaderList() - : MsgHandler(MsgType::READER_LIST) -{ - QJsonArray reader; - const auto& infoList = ReaderManager::getInstance().getReaderInfos(); - for (const auto& info : infoList) - { - reader += MsgHandlerReader::createReaderInfo(info); - } - mJsonObject[QLatin1String("reader")] = reader; -} diff --git a/src/jsonapi/messages/MsgHandlerReaderList.h b/src/jsonapi/messages/MsgHandlerReaderList.h deleted file mode 100644 index 8617c0e..0000000 --- a/src/jsonapi/messages/MsgHandlerReaderList.h +++ /dev/null @@ -1,22 +0,0 @@ -/*! - * \brief Message ReaderList of JSON API. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "MsgHandler.h" - -namespace governikus -{ - -class MsgHandlerReaderList - : public MsgHandler -{ - public: - MsgHandlerReaderList(); -}; - - -} /* namespace governikus */ diff --git a/src/jsonapi/messages/MsgHandlerUnknownCommand.h b/src/jsonapi/messages/MsgHandlerUnknownCommand.h deleted file mode 100644 index 7d84454..0000000 --- a/src/jsonapi/messages/MsgHandlerUnknownCommand.h +++ /dev/null @@ -1,22 +0,0 @@ -/*! - * \brief MsgHandlerUnknownCommand of JSON API. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "MsgHandler.h" - -namespace governikus -{ - -class MsgHandlerUnknownCommand - : public MsgHandler -{ - public: - MsgHandlerUnknownCommand(const QString& pName); -}; - - -} /* namespace governikus */ diff --git a/src/jsonapi/messages/MsgTypes.h b/src/jsonapi/messages/MsgTypes.h deleted file mode 100644 index 8d9d4e8..0000000 --- a/src/jsonapi/messages/MsgTypes.h +++ /dev/null @@ -1,49 +0,0 @@ -/*! - * \brief Enumerations of message types and additional stuff. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "EnumHelper.h" - -namespace governikus -{ -defineEnumType(MsgLevel, v1 = 1) // See MsgHandler::DEFAULT_MSG_LEVEL - -defineEnumType(MsgType, - INVALID, - UNKNOWN_COMMAND, - INTERNAL_ERROR, - INFO, - API_LEVEL, - READER, - READER_LIST, - BAD_STATE, - AUTH, - CERTIFICATE, - ACCESS_RIGHTS, - INSERT_CARD, - ENTER_PIN, - ENTER_CAN, - ENTER_PUK) - -defineEnumType(MsgCmdType, - UNDEFINED, - ACCEPT, - CANCEL, - GET_INFO, - GET_API_LEVEL, - SET_API_LEVEL, - GET_READER, - GET_READER_LIST, - RUN_AUTH, - GET_CERTIFICATE, - GET_ACCESS_RIGHTS, - SET_ACCESS_RIGHTS, - SET_PIN, - SET_CAN, - SET_PUK) - -} /* namespace governikus */ diff --git a/src/main.cpp b/src/main.cpp index 9d3d60a..899010c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,25 +9,36 @@ #include "global/LogHandler.h" #include "SignalHandler.h" +#include // version API + #include +#include #include #include #include #include -#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(Q_OS_WINRT) -#include -#define QAPP QGuiApplication +#include "config.h" // use in main only! + +#ifdef ANDROID_BUILD_AAR + #include + #define QAPP QAndroidService + +#elif defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(Q_OS_WINRT) + #ifdef Q_OS_ANDROID + #include + #include + #endif + + #include + #define QAPP QGuiApplication #else -#include -#define QAPP QApplication + #include + #define QAPP QApplication #endif -// Includes for version API -#include - -#if !defined(Q_OS_WINRT) +#if !defined(Q_OS_WINRT) && !defined(ANDROID_BUILD_AAR) Q_IMPORT_PLUGIN(RemoteReaderManagerPlugIn) #endif @@ -40,9 +51,14 @@ Q_IMPORT_PLUGIN(InternalActivationHandler) #if defined(Q_OS_ANDROID) Q_IMPORT_PLUGIN(NfcReaderManagerPlugIn) + +#ifndef ANDROID_BUILD_AAR Q_IMPORT_PLUGIN(IntentActivationHandler) #endif +#endif + + #if defined(Q_OS_IOS) Q_IMPORT_PLUGIN(CustomSchemeActivationHandler) @@ -54,14 +70,9 @@ Q_IMPORT_PLUGIN(QtGraphicalEffectsPlugin) Q_IMPORT_PLUGIN(QtGraphicalEffectsPrivatePlugin) // Do not delete the comments to avoid searching for the class name -Q_IMPORT_PLUGIN(QtQuickExtrasStylesPlugin) -Q_IMPORT_PLUGIN(QtQuickControls1Plugin) //Q_IMPORT_PLUGIN(QtQuickControls2MaterialStylePlugin) //Q_IMPORT_PLUGIN(QtQuickControls2UniversalStylePlugin) Q_IMPORT_PLUGIN(QtQuickControls2Plugin) -//Q_IMPORT_PLUGIN(QtQuick2DialogsPrivatePlugin) -Q_IMPORT_PLUGIN(QtQuick2DialogsPlugin) -//Q_IMPORT_PLUGIN(QtQuickExtrasPlugin) Q_IMPORT_PLUGIN(QtQuickLayoutsPlugin) //Q_IMPORT_PLUGIN(QQmlLocalStoragePlugin) //Q_IMPORT_PLUGIN(QtQuick2ParticlesPlugin) @@ -70,7 +81,7 @@ Q_IMPORT_PLUGIN(QtQuick2WindowPlugin) Q_IMPORT_PLUGIN(QtQuick2Plugin) #endif -#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || (defined(Q_OS_LINUX) && !defined(QT_NO_DEBUG)) +#if (defined(Q_OS_ANDROID) && !defined(ANDROID_BUILD_AAR)) || defined(Q_OS_IOS) || (defined(Q_OS_LINUX) && !defined(QT_NO_DEBUG) && !defined(ANDROID_BUILD_AAR)) Q_IMPORT_PLUGIN(BluetoothReaderManagerPlugIn) #endif @@ -81,14 +92,15 @@ Q_IMPORT_PLUGIN(UIPlugInWidgets) #endif #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || !defined(QT_NO_DEBUG) -Q_IMPORT_PLUGIN(UIPlugInJsonApi) Q_IMPORT_PLUGIN(UIPlugInAidl) +#endif + +#if (defined(Q_OS_ANDROID) && !defined(ANDROID_BUILD_AAR)) || defined(Q_OS_IOS) || (!defined(QT_NO_DEBUG) && !defined(ANDROID_BUILD_AAR)) Q_IMPORT_PLUGIN(UIPlugInQml) #endif -#ifndef QT_NO_DEBUG +Q_IMPORT_PLUGIN(UIPlugInJsonApi) Q_IMPORT_PLUGIN(UIPlugInWebSocket) -#endif using namespace governikus; @@ -98,14 +110,13 @@ Q_DECLARE_LOGGING_CATEGORY(init) static inline void printInfo() { - qCDebug(init) << "Logging to" << LogHandler::getInstance(); + qCDebug(init) << "Logging to" << *Env::getSingleton(); qCInfo(init) << "##################################################"; qCInfo(init) << "### ApplicationName:" << QCoreApplication::applicationName(); qCInfo(init) << "### ApplicationVersion:" << QCoreApplication::applicationVersion(); qCInfo(init) << "### OrganizationName:" << QCoreApplication::organizationName(); qCInfo(init) << "### OrganizationDomain:" << QCoreApplication::organizationDomain(); - qCInfo(init) << "### Build:" << BuildHelper::getDateTime(); qCInfo(init) << "### System:" << QSysInfo::prettyProductName(); qCInfo(init) << "### Kernel:" << QSysInfo::kernelVersion(); qCInfo(init) << "### Architecture:" << QSysInfo::currentCpuArchitecture(); @@ -119,9 +130,14 @@ static inline void printInfo() qCInfo(init) << "### OpenSSL Version:" << QSslSocket::sslLibraryVersionString(); qCInfo(init) << "##################################################"; - if (QSslSocket::sslLibraryVersionString() != QLatin1String(SSLeay_version(0))) + #if OPENSSL_VERSION_NUMBER < 0x10100000L + #define OpenSSL_version SSLeay_version + #define OPENSSL_VERSION SSLEAY_VERSION + #endif + + if (QSslSocket::sslLibraryVersionString() != QLatin1String(OpenSSL_version(OPENSSL_VERSION))) { - qCWarning(init) << "Linked OpenSSL Version differs:" << SSLeay_version(0); + qCWarning(init) << "Linked OpenSSL Version differs:" << OpenSSL_version(OPENSSL_VERSION); } const auto libPathes = QCoreApplication::libraryPaths(); @@ -132,10 +148,9 @@ static inline void printInfo() } -#include "config.h" // use in main only! -Q_DECL_EXPORT int main(int argc, char** argv) +static inline QCoreApplication* initQt(int& argc, char** argv) { -#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) QCoreApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton); #endif @@ -143,14 +158,30 @@ Q_DECL_EXPORT int main(int argc, char** argv) QCoreApplication::setOrganizationDomain(QStringLiteral(VENDOR_DOMAIN)); QCoreApplication::setApplicationName(QStringLiteral(PRODUCT)); QCoreApplication::setApplicationVersion(QStringLiteral(VERSION)); - QGuiApplication::setQuitOnLastWindowClosed(false); - QAPP app(argc, argv); +#ifndef ANDROID_BUILD_AAR + QGuiApplication::setQuitOnLastWindowClosed(false); +#endif + +#if defined(Q_OS_ANDROID) && !defined(ANDROID_BUILD_AAR) + if (QtAndroid::androidService().isValid()) + { + return new QAndroidService(argc, argv); + } +#endif + + return new QAPP(argc, argv); +} + + +Q_DECL_EXPORT int main(int argc, char** argv) +{ + const QScopedPointer app(initQt(argc, argv)); QThread::currentThread()->setObjectName(QStringLiteral("MainThread")); - LogHandler::getInstance().init(); - SignalHandler::getInstance().init(); CommandLineParser::getInstance().parse(); + Env::getSingleton()->init(); + SignalHandler::getInstance().init(); printInfo(); AppController controller; @@ -161,5 +192,5 @@ Q_DECL_EXPORT int main(int argc, char** argv) } SignalHandler::getInstance().setController(controller); - return SignalHandler::getInstance().shouldQuit() ? EXIT_SUCCESS : app.exec(); + return SignalHandler::getInstance().shouldQuit() ? EXIT_SUCCESS : app->exec(); } diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt index 279729b..1ccc40e 100644 --- a/src/network/CMakeLists.txt +++ b/src/network/CMakeLists.txt @@ -1,3 +1,8 @@ +##################################################################### +# The module network is responsible for all raw network stuff and +# checks for TLS security. +##################################################################### + ADD_PLATFORM_LIBRARY(AusweisAppNetwork) TARGET_LINK_LIBRARIES(AusweisAppNetwork Qt5::Core Qt5::Network AusweisAppExternal::HttpParser AusweisAppGlobal AusweisAppSecureStorage AusweisAppSettings) diff --git a/src/network/DatagramHandler.h b/src/network/DatagramHandler.h index 54642f9..0bb6948 100644 --- a/src/network/DatagramHandler.h +++ b/src/network/DatagramHandler.h @@ -7,8 +7,6 @@ #pragma once #include -#include -#include class test_DatagramHandlerImpl; @@ -20,18 +18,15 @@ class DatagramHandler { Q_OBJECT - private: - friend class ::test_DatagramHandlerImpl; - public: DatagramHandler(bool pListen = true); virtual ~DatagramHandler(); virtual bool isBound() const = 0; - virtual bool send(const QJsonDocument& pData) = 0; + virtual bool send(const QByteArray& pData) = 0; Q_SIGNALS: - void fireNewMessage(const QJsonDocument& pData, const QHostAddress& pAddress); + void fireNewMessage(const QByteArray& pData, const QHostAddress& pAddress); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/network/DatagramHandlerImpl.cpp b/src/network/DatagramHandlerImpl.cpp index ab11bd9..f1eee24 100644 --- a/src/network/DatagramHandlerImpl.cpp +++ b/src/network/DatagramHandlerImpl.cpp @@ -32,15 +32,15 @@ template<> DatagramHandler* createNewObject(bool&& pList } -} /* namespace governikus */ +} // namespace governikus +quint16 DatagramHandlerImpl::cPort = PortFile::cDefaultPort; -quint16 DatagramHandlerImpl::cPort = 24727; - - -DatagramHandlerImpl::DatagramHandlerImpl(bool pListen) +DatagramHandlerImpl::DatagramHandlerImpl(bool pListen, quint16 pPort) : DatagramHandler() , mSocket(new QUdpSocket) + , mUsedPort(pPort) + , mPortFile(QStringLiteral("udp")) { #ifndef QT_NO_NETWORKPROXY mSocket->setProxy(QNetworkProxy::NoProxy); @@ -51,10 +51,20 @@ DatagramHandlerImpl::DatagramHandlerImpl(bool pListen) if (!pListen) { qCDebug(network) << "Skipping binding"; + + // If --port 0 is given we cannot use this as a client. Automatic port + // usage is only supported by a server. + if (mUsedPort == 0) + { + mUsedPort = PortFile::cDefaultPort; + qCWarning(network) << "Client port cannot be 0! Reset to default:" << mUsedPort; + } } - else if (mSocket->bind(cPort)) + else if (mSocket->bind(mUsedPort)) { - qCDebug(network) << "Bound on port:" << mSocket->localPort(); + mUsedPort = mSocket->localPort(); // if user provides 0, we need to overwrite it with real value + mPortFile.handlePort(mUsedPort); + qCDebug(network) << "Bound on port:" << mUsedPort; } else { @@ -79,26 +89,68 @@ bool DatagramHandlerImpl::isBound() const } -bool DatagramHandlerImpl::send(const QJsonDocument& pData) +bool DatagramHandlerImpl::send(const QByteArray& pData) +{ + return send(pData, 0); +} + + +bool DatagramHandlerImpl::send(const QByteArray& pData, quint16 pPort) { QVector broadcastAddresses; + const auto& interfaces = QNetworkInterface::allInterfaces(); for (const QNetworkInterface& interface : interfaces) { + bool skipFurtherIPv6AddressesOnThisInterface = false; + const auto& entries = interface.addressEntries(); for (const QNetworkAddressEntry& addressEntry : entries) { - const QHostAddress& broadcastAddr = addressEntry.broadcast(); - if (broadcastAddr.isNull()) + switch (addressEntry.ip().protocol()) { - continue; - } - if (addressEntry.ip().isEqual(QHostAddress::LocalHost, QHostAddress::TolerantConversion)) - { - continue; - } + case QAbstractSocket::NetworkLayerProtocol::IPv4Protocol: + { + const QHostAddress& broadcastAddr = addressEntry.broadcast(); + if (broadcastAddr.isNull()) + { + continue; + } + if (addressEntry.ip().isEqual(QHostAddress::LocalHost, QHostAddress::TolerantConversion)) + { + continue; + } - broadcastAddresses += broadcastAddr; + broadcastAddresses += broadcastAddr; + break; + } + + case QAbstractSocket::NetworkLayerProtocol::IPv6Protocol: + { + if (skipFurtherIPv6AddressesOnThisInterface) + { + continue; + } + + const QString& scopeId = addressEntry.ip().scopeId(); + if (scopeId.isEmpty()) + { + continue; + } + + QHostAddress scopedMulticastAddress = QHostAddress(QStringLiteral("ff02::1")); + scopedMulticastAddress.setScopeId(scopeId); + broadcastAddresses += scopedMulticastAddress; + + skipFurtherIPv6AddressesOnThisInterface = true; + break; + } + + default: + { + qCDebug(network) << "Skipping unknown protocol type:" << addressEntry.ip().protocol(); + } + } } } @@ -109,9 +161,9 @@ bool DatagramHandlerImpl::send(const QJsonDocument& pData) for (const QHostAddress& broadcastAddr : qAsConst(broadcastAddresses)) { - if (!send(pData, broadcastAddr)) + if (!send(pData, broadcastAddr, pPort)) { - qDebug() << "Broadcasting to" << broadcastAddr << "failed"; + qCDebug(network) << "Broadcasting to" << broadcastAddr << "failed"; return false; } } @@ -120,15 +172,19 @@ bool DatagramHandlerImpl::send(const QJsonDocument& pData) } -bool DatagramHandlerImpl::send(const QJsonDocument& pData, const QHostAddress& pAddress) +bool DatagramHandlerImpl::send(const QByteArray& pData, const QHostAddress& pAddress, quint16 pPort) { - const auto& data = pData.toJson(QJsonDocument::Compact); - const quint16 remotePort = cPort == 0 ? mSocket->localPort() : cPort; - if (mSocket->writeDatagram(data.constData(), pAddress, remotePort) != data.size()) + // If port is 0 we should take our own listening port as destination as other instances + // should use the same port to receive broadcasts. + const auto port = pPort > 0 ? pPort : mUsedPort; + Q_ASSERT(port > 0); + + if (mSocket->writeDatagram(pData.constData(), pAddress, port) != pData.size()) { - qCCritical(network) << "Cannot write datagram:" << mSocket->error() << '|' << mSocket->errorString(); + qCCritical(network) << "Cannot write datagram to address" << pAddress << ':' << mSocket->error() << '|' << mSocket->errorString(); return false; } + return true; } @@ -143,20 +199,6 @@ void DatagramHandlerImpl::onReadyRead() datagram.resize(static_cast(mSocket->pendingDatagramSize())); mSocket->readDatagram(datagram.data(), datagram.size(), &addr); - QJsonParseError jsonError; - const auto& json = QJsonDocument::fromJson(datagram, &jsonError); - if (jsonError.error == QJsonParseError::NoError) - { - Q_EMIT fireNewMessage(json, addr); - } - else - { - static int timesLogged = 0; - if (timesLogged < 20) - { - qCInfo(network) << "Datagram does not contain valid JSON:" << datagram; - timesLogged++; - } - } + Q_EMIT fireNewMessage(datagram, addr); } } diff --git a/src/network/DatagramHandlerImpl.h b/src/network/DatagramHandlerImpl.h index 298aca5..7692312 100644 --- a/src/network/DatagramHandlerImpl.h +++ b/src/network/DatagramHandlerImpl.h @@ -8,11 +8,12 @@ #include "DatagramHandler.h" +#include "PortFile.h" + #include #include #include -class test_DatagramHandlerImpl; namespace governikus { @@ -26,21 +27,25 @@ class DatagramHandlerImpl friend class ::test_DatagramHandlerImpl; friend struct QtSharedPointer::CustomDeleter; - static quint16 cPort; QScopedPointer mSocket; + quint16 mUsedPort; + PortFile mPortFile; - bool send(const QJsonDocument& pData, const QHostAddress& pAddress); + bool send(const QByteArray& pData, const QHostAddress& pAddress, quint16 pPort = 0); + bool send(const QByteArray& pData, quint16 pPort); public: - DatagramHandlerImpl(bool pListen = true); + static quint16 cPort; + + DatagramHandlerImpl(bool pListen = true, quint16 pPort = DatagramHandlerImpl::cPort); virtual ~DatagramHandlerImpl() override; virtual bool isBound() const override; - virtual bool send(const QJsonDocument& pData) override; + virtual bool send(const QByteArray& pData) override; private Q_SLOTS: void onReadyRead(); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/network/HttpRequest.cpp b/src/network/HttpRequest.cpp index 3bd9c97..6b95aac 100644 --- a/src/network/HttpRequest.cpp +++ b/src/network/HttpRequest.cpp @@ -12,17 +12,15 @@ Q_DECLARE_LOGGING_CATEGORY(network) #define CAST_OBJ(parser) HttpRequest* obj = static_cast(parser->data); -HttpRequest::HttpRequest(QAbstractSocket* pSocket, QObject* pParent) +HttpRequest::HttpRequest(QTcpSocket* pSocket, QObject* pParent) : QObject(pParent) , mUrl() , mHeader() , mBody() - , mSocket(pSocket, [](QAbstractSocket* s){ - s->flush(); - s->deleteLater(); - }) + , mSocket(pSocket) , mParser() , mParserSettings() + , mSocketDisconnected(false) , mFinished(false) , mCurrentHeaderField() , mCurrentHeaderValue() @@ -41,19 +39,39 @@ HttpRequest::HttpRequest(QAbstractSocket* pSocket, QObject* pParent) mParserSettings.on_url = &HttpRequest::onUrl; connect(mSocket.data(), &QAbstractSocket::readyRead, this, &HttpRequest::onReadyRead); - connect(mSocket.data(), &QAbstractSocket::disconnected, this, &HttpRequest::deleteLater); + connect(mSocket.data(), &QAbstractSocket::disconnected, this, &HttpRequest::onSocketDisconnected, Qt::QueuedConnection); + connect(mSocket.data(), &QAbstractSocket::disconnected, this, &HttpRequest::deleteLater, Qt::QueuedConnection); onReadyRead(); } +QTcpSocket* HttpRequest::take() +{ + disconnect(mSocket.data(), &QAbstractSocket::readyRead, this, &HttpRequest::onReadyRead); + disconnect(mSocket.data(), &QAbstractSocket::disconnected, this, &HttpRequest::onSocketDisconnected); + disconnect(mSocket.data(), &QAbstractSocket::disconnected, this, &HttpRequest::deleteLater); + return mSocket.take(); +} + + +void HttpRequest::onSocketDisconnected() +{ + mSocketDisconnected = true; +} + + HttpRequest::~HttpRequest() { + if (mSocket && !mSocketDisconnected) + { + mSocket->flush(); + } } bool HttpRequest::isConnected() const { - return mSocket->state() == QAbstractSocket::ConnectedState; + return mSocket && mSocket->state() == QAbstractSocket::ConnectedState; } @@ -95,6 +113,11 @@ const QByteArray& HttpRequest::getBody() const bool HttpRequest::send(const HttpResponse& pResponse) { + if (!mSocket) + { + return false; + } + const auto& msg = pResponse.getMessage(); if (mSocket->write(msg) != msg.size()) { @@ -107,6 +130,11 @@ bool HttpRequest::send(const HttpResponse& pResponse) void HttpRequest::onReadyRead() { + if (!mSocket) + { + return; + } + while (mSocket->bytesAvailable()) { const auto& buffer = mSocket->readAll(); @@ -125,7 +153,7 @@ void HttpRequest::onReadyRead() { disconnect(mSocket.data(), &QAbstractSocket::readyRead, this, &HttpRequest::onReadyRead); disconnect(mSocket.data(), &QAbstractSocket::disconnected, this, &HttpRequest::deleteLater); - Q_EMIT fireMessageComplete(this, mSocket); + Q_EMIT fireMessageComplete(this); } } @@ -150,7 +178,7 @@ int HttpRequest::onHeadersComplete(http_parser* pParser) { CAST_OBJ(pParser) obj->insertHeader(); - qCDebug(network) << obj->getMethod() << " |" << obj->getUrl(); + qCDebug(network) << obj->getMethod() << '|' << obj->getUrl(); qCDebug(network) << "Header completed"; return 0; } diff --git a/src/network/HttpRequest.h b/src/network/HttpRequest.h index 27f9aba..b9653f6 100644 --- a/src/network/HttpRequest.h +++ b/src/network/HttpRequest.h @@ -6,17 +6,18 @@ #pragma once -#include "http_parser.h" #include "HttpResponse.h" -#include +#include #include #include #include -#include +#include +#include #include class test_WebserviceActivationHandler; +class test_WebserviceActivationContext; namespace governikus { @@ -28,6 +29,8 @@ class HttpRequest private: friend class ::test_WebserviceActivationHandler; + friend class ::test_WebserviceActivationContext; + friend class HttpServer; static int onMessageBegin(http_parser* pParser); static int onMessageComplete(http_parser* pParser); @@ -46,10 +49,11 @@ class HttpRequest QByteArray mUrl; QMap mHeader; QByteArray mBody; - QSharedPointer mSocket; + QScopedPointer mSocket; http_parser mParser; http_parser_settings mParserSettings; + bool mSocketDisconnected; bool mFinished; QByteArray mCurrentHeaderField; QByteArray mCurrentHeaderValue; @@ -57,7 +61,7 @@ class HttpRequest void insertHeader(); public: - HttpRequest(QAbstractSocket* pSocket, QObject* pParent = nullptr); + HttpRequest(QTcpSocket* pSocket, QObject* pParent = nullptr); virtual ~HttpRequest(); bool isConnected() const; @@ -71,11 +75,14 @@ class HttpRequest bool send(const HttpResponse& pResponse); + QTcpSocket* take(); + private Q_SLOTS: void onReadyRead(); + void onSocketDisconnected(); Q_SIGNALS: - void fireMessageComplete(HttpRequest* pSelf, QSharedPointer pSocket); + void fireMessageComplete(HttpRequest* pSelf); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/network/HttpResponse.cpp b/src/network/HttpResponse.cpp index 5cddf88..4984e9f 100644 --- a/src/network/HttpResponse.cpp +++ b/src/network/HttpResponse.cpp @@ -4,6 +4,8 @@ #include "HttpResponse.h" +#include "NetworkManager.h" + #include #include #include @@ -22,11 +24,11 @@ HEADER_NAME(HEADER_CONTENT_LENGTH, "Content-Length") HEADER_NAME(HEADER_CONTENT_TYPE, "Content-Type") HEADER_NAME(HEADER_SERVER, "Server") HEADER_NAME(HEADER_DATE, "Date") -} +} // namespace Q_DECLARE_LOGGING_CATEGORY(network) -HttpResponse::HttpResponse(HttpStatusCode pStatus, const QByteArray& pBody, const QByteArray& pContentType) +HttpResponse::HttpResponse(http_status pStatus, const QByteArray& pBody, const QByteArray& pContentType) : mStatus(pStatus) , mHeader() , mBody() @@ -45,17 +47,6 @@ HttpResponse::HttpResponse(HttpStatusCode pStatus, const QByteArray& pBody, cons } -HttpResponse::~HttpResponse() -{ -} - - -bool HttpResponse::isValid() const -{ - return mStatus != HttpStatusCode::UNDEFINED; -} - - QByteArray HttpResponse::getHeader(const QByteArray& pKey) const { return mHeader.value(pKey); @@ -74,13 +65,13 @@ void HttpResponse::setHeader(const QByteArray& pKey, const QByteArray& pValue) } -HttpStatusCode HttpResponse::getStatus() const +http_status HttpResponse::getStatus() const { return mStatus; } -void HttpResponse::setStatus(HttpStatusCode pStatus) +void HttpResponse::setStatus(http_status pStatus) { mStatus = pStatus; } @@ -108,16 +99,17 @@ void HttpResponse::setBody(const QByteArray& pBody, const QByteArray& pContentTy } +QByteArray HttpResponse::getStatusMessage() const +{ + return NetworkManager::getStatusMessage(mStatus); +} + + QByteArray HttpResponse::getMessage() const { - Q_ASSERT(mStatus != HttpStatusCode::UNDEFINED); - static const QByteArray CR_LF = QByteArrayLiteral("\r\n"); QByteArrayList list; - - const auto& statusCode = QByteArray::number(static_cast(mStatus)); - QByteArray statusMsg(getEnumName(mStatus).data()); - list += QByteArrayLiteral("HTTP/1.0 ") % statusCode % ' ' % statusMsg.replace('_', ' '); + list += QByteArrayLiteral("HTTP/1.0 ") % QByteArray::number(mStatus) % ' ' % getStatusMessage(); const auto& end = mHeader.constEnd(); for (auto iter = mHeader.constBegin(); iter != end; ++iter) diff --git a/src/network/HttpResponse.h b/src/network/HttpResponse.h index cfc5da1..c654f48 100644 --- a/src/network/HttpResponse.h +++ b/src/network/HttpResponse.h @@ -6,8 +6,7 @@ #pragma once -#include "HttpStatusCode.h" - +#include #include #include @@ -17,21 +16,23 @@ namespace governikus class HttpResponse { private: - HttpStatusCode mStatus; + http_status mStatus; QMap mHeader; QByteArray mBody; + QByteArray getStatusMessage() const; + public: - HttpResponse(HttpStatusCode pStatus = HttpStatusCode::UNDEFINED, const QByteArray& pBody = QByteArray(), const QByteArray& pContentType = QByteArray()); - virtual ~HttpResponse(); - bool isValid() const; + HttpResponse(http_status pStatus = HTTP_STATUS_INTERNAL_SERVER_ERROR, + const QByteArray& pBody = QByteArray(), + const QByteArray& pContentType = QByteArray()); QByteArray getHeader(const QByteArray& pKey) const; const QMap& getHeaders() const; void setHeader(const QByteArray& pKey, const QByteArray& pValue); - HttpStatusCode getStatus() const; - void setStatus(HttpStatusCode pStatus); + http_status getStatus() const; + void setStatus(http_status pStatus); const QByteArray& getBody() const; void setBody(const QByteArray& pBody, const QByteArray& pContentType = QByteArray()); @@ -39,4 +40,4 @@ class HttpResponse QByteArray getMessage() const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/network/HttpServer.cpp b/src/network/HttpServer.cpp index 166d3bb..5e52f1f 100644 --- a/src/network/HttpServer.cpp +++ b/src/network/HttpServer.cpp @@ -5,21 +5,26 @@ #include "HttpServer.h" #include +#include using namespace governikus; Q_DECLARE_LOGGING_CATEGORY(network) -quint16 HttpServer::cPort = 24727; + +quint16 HttpServer::cPort = PortFile::cDefaultPort; + HttpServer::HttpServer(quint16 pPort) : QObject() , mServer(new QTcpServer) + , mPortFile() { connect(mServer.data(), &QTcpServer::newConnection, this, &HttpServer::onNewConnection); if (mServer->listen(QHostAddress::LocalHost, pPort)) { + mPortFile.handlePort(mServer->serverPort()); qCDebug(network) << "Listening on port:" << mServer->serverPort(); } else @@ -63,29 +68,55 @@ void HttpServer::onNewConnection() } -void HttpServer::onMessageComplete(HttpRequest* pRequest, QSharedPointer pSocket) +bool HttpServer::checkReceiver(const QMetaMethod& pSignal, HttpRequest* pRequest) +{ + if (isSignalConnected(pSignal)) + { + return true; + } + + qCDebug(network) << "No registration found:" << pSignal.name(); + pRequest->send(HTTP_STATUS_SERVICE_UNAVAILABLE); + pRequest->deleteLater(); + return false; +} + + +void HttpServer::onMessageComplete(HttpRequest* pRequest) { pRequest->setParent(nullptr); if (pRequest->isUpgrade()) { - pRequest->deleteLater(); - pSocket->rollbackTransaction(); - if (pRequest->getHeader(QByteArrayLiteral("upgrade")).toLower() == QByteArrayLiteral("websocket")) { qCDebug(network) << "Upgrade to websocket requested"; - Q_EMIT fireNewWebSocketRequest(pSocket); + + static const QMetaMethod signal = QMetaMethod::fromSignal(&HttpServer::fireNewWebSocketRequest); + if (!checkReceiver(signal, pRequest)) + { + return; + } + + pRequest->mSocket->rollbackTransaction(); + Q_EMIT fireNewWebSocketRequest(QSharedPointer(pRequest, &QObject::deleteLater)); } else { qCWarning(network) << "Unknown upgrade requested"; - pRequest->send(HttpStatusCode::NOT_FOUND); + pRequest->send(HTTP_STATUS_NOT_FOUND); + pRequest->deleteLater(); } } else { - pSocket->commitTransaction(); + static const QMetaMethod signal = QMetaMethod::fromSignal(&HttpServer::fireNewHttpRequest); + if (!checkReceiver(signal, pRequest)) + { + return; + } + + pRequest->mSocket->commitTransaction(); Q_EMIT fireNewHttpRequest(QSharedPointer(pRequest, &QObject::deleteLater)); } } diff --git a/src/network/HttpServer.h b/src/network/HttpServer.h index 575fcd3..db58632 100644 --- a/src/network/HttpServer.h +++ b/src/network/HttpServer.h @@ -7,11 +7,12 @@ #pragma once #include "HttpRequest.h" +#include "PortFile.h" +#include #include #include #include -#include namespace governikus { @@ -23,6 +24,9 @@ class HttpServer private: QScopedPointer mServer; + PortFile mPortFile; + + bool checkReceiver(const QMetaMethod& pSignal, HttpRequest* pRequest); public: static quint16 cPort; @@ -35,11 +39,11 @@ class HttpServer private Q_SLOTS: void onNewConnection(); - void onMessageComplete(HttpRequest* pRequest, QSharedPointer pSocket); + void onMessageComplete(HttpRequest* pRequest); Q_SIGNALS: void fireNewHttpRequest(const QSharedPointer& pRequest); - void fireNewWebSocketRequest(const QSharedPointer& pSocket); + void fireNewWebSocketRequest(const QSharedPointer& pRequest); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/network/HttpServerRequestor.cpp b/src/network/HttpServerRequestor.cpp index 89f876c..9d3ae0e 100644 --- a/src/network/HttpServerRequestor.cpp +++ b/src/network/HttpServerRequestor.cpp @@ -2,7 +2,6 @@ * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany */ -#include "Env.h" #include "HttpServerRequestor.h" #include "NetworkManager.h" @@ -32,7 +31,7 @@ HttpServerRequestor::~HttpServerRequestor() } -QUrl HttpServerRequestor::createUrl(const QString& pQuery, int pPort, const QHostAddress& pHost, const QString& pPath) +QUrl HttpServerRequestor::createUrl(const QString& pQuery, quint16 pPort, const QHostAddress& pHost, const QString& pPath) { QUrl url; url.setScheme(QStringLiteral("http")); diff --git a/src/network/HttpServerRequestor.h b/src/network/HttpServerRequestor.h index b4eb134..654768d 100644 --- a/src/network/HttpServerRequestor.h +++ b/src/network/HttpServerRequestor.h @@ -31,10 +31,10 @@ class HttpServerRequestor virtual ~HttpServerRequestor(); QPointer request(const QUrl& pUrl, int pTimeOut = 2000); - static QUrl createUrl(const QString& pQuery, int pPort, const QHostAddress& pHost = QHostAddress::LocalHost, const QString& pPath = QStringLiteral("/eID-Client")); + static QUrl createUrl(const QString& pQuery, quint16 pPort, const QHostAddress& pHost = QHostAddress::LocalHost, const QString& pPath = QStringLiteral("/eID-Client")); private Q_SLOTS: void finished(); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/network/HttpServerStatusParser.cpp b/src/network/HttpServerStatusParser.cpp index 990b363..d8a62d4 100644 --- a/src/network/HttpServerStatusParser.cpp +++ b/src/network/HttpServerStatusParser.cpp @@ -4,8 +4,10 @@ #include "HttpServerStatusParser.h" -#include "HttpStatusCode.h" +#include "LogHandler.h" +#include "NetworkManager.h" +#include #include using namespace governikus; @@ -14,7 +16,7 @@ using namespace governikus; Q_DECLARE_LOGGING_CATEGORY(network) -HttpServerStatusParser::HttpServerStatusParser(int pPort, const QHostAddress& pHost) +HttpServerStatusParser::HttpServerStatusParser(quint16 pPort, const QHostAddress& pHost) : QObject() , mUrl(HttpServerRequestor::createUrl(QStringLiteral("Status"), pPort, pHost)) , mServerHeader() @@ -69,8 +71,8 @@ bool HttpServerStatusParser::parseReply(const QPointer& pReply) } } - int statusCode = pReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - if (statusCode == HttpStatusCode::OK) + const auto statusCode = NetworkManager::getLoggedStatusCode(pReply, spawnMessageLogger(network)); + if (statusCode == HTTP_STATUS_OK) { Q_ASSERT_X(pReply->isFinished(), "HttpServerStatusParser::parseReply", "Not all data available for reply"); mVersionInfo = VersionInfo::fromText(QString::fromUtf8(pReply->readAll())); @@ -79,7 +81,7 @@ bool HttpServerStatusParser::parseReply(const QPointer& pReply) } else { - qCDebug(network) << "Cannot get status information! Got bad http status code:" << statusCode; + qCDebug(network) << "Cannot get status information! Got bad http status code."; } return false; diff --git a/src/network/HttpServerStatusParser.h b/src/network/HttpServerStatusParser.h index c7b0661..9f63452 100644 --- a/src/network/HttpServerStatusParser.h +++ b/src/network/HttpServerStatusParser.h @@ -30,7 +30,7 @@ class HttpServerStatusParser bool parseReply(const QPointer& pReply); public: - HttpServerStatusParser(int pPort, const QHostAddress& pHost = QHostAddress::LocalHost); + HttpServerStatusParser(quint16 pPort, const QHostAddress& pHost = QHostAddress::LocalHost); virtual ~HttpServerStatusParser(); bool request(); @@ -38,4 +38,4 @@ class HttpServerStatusParser const VersionInfo& getVersionInfo() const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/network/HttpStatusCode.cpp b/src/network/HttpStatusCode.cpp deleted file mode 100644 index 60c7589..0000000 --- a/src/network/HttpStatusCode.cpp +++ /dev/null @@ -1,9 +0,0 @@ -/*! - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "HttpStatusCode.h" - -using namespace governikus; - -#include "moc_HttpStatusCode.cpp" diff --git a/src/network/HttpStatusCode.h b/src/network/HttpStatusCode.h deleted file mode 100644 index 28fa813..0000000 --- a/src/network/HttpStatusCode.h +++ /dev/null @@ -1,61 +0,0 @@ -/*! - * \brief Defines an enumeration of HTTP Status Codes as they are not defined by Qt. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "EnumHelper.h" - -namespace governikus -{ - -// see https://www.ietf.org/rfc/rfc2616.txt -defineEnumType(HttpStatusCode, - UNDEFINED = 0, - CONTINUE = 100, - SWITCH_PROTOCOLS = 101, - PROCESSING = 102, - OK = 200, - CREATED = 201, - ACCEPTED = 202, - NON_AUTHORITATIVE_INFORMATION = 203, - NO_CONTENT = 204, - RESET_CONTENT = 205, - PARTIAL_CONTENT = 206, - MULTIPLE_CHOICES = 300, - MOVED_PERMANENTLY = 301, - FOUND = 302, - SEE_OTHER = 303, - NOT_MODIFIED = 304, - USE_PROXY = 305, - TEMPORARY_REDIRECT = 307, - BAD_REQUEST = 400, - UNAUTHORIZED = 401, - PAYMENT_REQUIRED = 402, - FORBIDDEN = 403, - NOT_FOUND = 404, - METHOD_NOT_ALLOWED = 405, - NOT_ACCEPTABLE = 406, - PROXY_AUTHENTICATION_REQUIRED = 407, - REQUEST_TIMEOUT = 408, - CONFLICT = 409, - GONE = 410, - LENGTH_REQUIRED = 411, - PRECONDITION_FAILED = 412, - REQUEST_ENTITY_TOO_LARGE = 413, - REQUEST_URI_TOO_LONG = 414, - REQUEST_UNSUPPORTED_MEDIA_TYPE = 415, - REQUESTED_RANGE_NOT_SATISFIABLE = 416, - EXPECTATION_FAILED = 417, - INTERNAL_SERVER_ERROR = 500, - NOT_IMPLEMENTED = 501, - BAD_GATEWAY = 502, - SERVICE_UNAVAILABLE = 503, - GATEWAY_TIMEOUT = 504, - HTTP_VERSION_NOT_SUPPORTED = 505, - NETWORK_AUTHENTICATION_REQUIRED = 511 - ) - -} /* namespace governikus */ diff --git a/src/network/NetworkManager.cpp b/src/network/NetworkManager.cpp index d685d94..f69be19 100644 --- a/src/network/NetworkManager.cpp +++ b/src/network/NetworkManager.cpp @@ -4,13 +4,13 @@ #include "NetworkManager.h" -#include "Env.h" #include "NetworkReplyError.h" #include "NetworkReplyTimeout.h" #include "SecureStorage.h" #include "SingletonHelper.h" #include "VersionInfo.h" +#include #include #include #include @@ -29,7 +29,8 @@ bool NetworkManager::mLockProxy = false; NetworkManager::NetworkManager() : QObject() , mApplicationExitInProgress(false) - , mOpenConnectionCount(0) + , mTrackedConnectionsMutex() + , mTrackedConnections() , mNetAccessManager(new QNetworkAccessManager()) { #ifndef QT_NO_NETWORKPROXY @@ -51,7 +52,9 @@ NetworkManager& NetworkManager::getInstance() int NetworkManager::getOpenConnectionCount() { - return mOpenConnectionCount; + QMutexLocker locker(&mTrackedConnectionsMutex); + + return mTrackedConnections.size(); } @@ -111,6 +114,27 @@ QNetworkReply* NetworkManager::get(QNetworkRequest& pRequest, } +QNetworkReply* NetworkManager::post(QNetworkRequest& pRequest, + const QByteArray& pData, + int pTimeoutInMilliSeconds) +{ + if (mApplicationExitInProgress) + { + return new NetworkReplyError(pRequest); + } + + pRequest.setHeader(QNetworkRequest::UserAgentHeader, getUserAgentHeader()); + pRequest.setHeader(QNetworkRequest::ContentLengthHeader, QString::number(pData.size())); + + auto cfg = SecureStorage::getInstance().getTlsConfig(SecureStorage::TlsSuite::DEFAULT).getConfiguration(); + pRequest.setSslConfiguration(cfg); + QNetworkReply* response = mNetAccessManager->post(pRequest, pData); + + trackConnection(response, pTimeoutInMilliSeconds); + return response; +} + + bool NetworkManager::checkUpdateServerCertificate(const QNetworkReply& pReply) { const QVector& trustedCertificates = SecureStorage::getInstance().getUpdateCertificates(); @@ -217,15 +241,59 @@ void NetworkManager::trackConnection(QNetworkReply* pResponse, const int pTimeou { Q_ASSERT(pResponse); - mOpenConnectionCount++; - connect(pResponse, &QNetworkReply::finished, [&] { - --mOpenConnectionCount; + addTrackedConnection(pResponse); + + connect(pResponse, &QObject::destroyed, this, [ = ] { + this->removeTrackedConnection(pResponse); }); NetworkReplyTimeout::setTimeout(pResponse, pTimeoutInMilliSeconds); } +void NetworkManager::addTrackedConnection(QNetworkReply* pResponse) +{ + QMutexLocker locker(&mTrackedConnectionsMutex); + + mTrackedConnections.insert(pResponse); +} + + +void NetworkManager::removeTrackedConnection(QNetworkReply* pResponse) +{ + QMutexLocker locker(&mTrackedConnectionsMutex); + + mTrackedConnections.remove(pResponse); +} + + +QByteArray NetworkManager::getStatusMessage(int pStatus) +{ + switch (pStatus) + { + #define XX(num, name, string) case num:\ + return QByteArrayLiteral(#string); + + HTTP_STATUS_MAP(XX) + #undef XX + } + + return QByteArray(); +} + + +int NetworkManager::getLoggedStatusCode(const QNetworkReply* const pReply, const QMessageLogger& pLogger) +{ + const int statusCode = pReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + pLogger.debug() << "Status Code:" << statusCode << getStatusMessage(statusCode); + for (const auto& header : pReply->rawHeaderPairs()) + { + pLogger.debug().nospace().noquote() << "Header | " << header.first << ": " << header.second; + } + return statusCode; +} + + #ifndef QT_NO_NETWORKPROXY namespace { @@ -276,7 +344,7 @@ class SystemProxyFactory }; -} +} // namespace void NetworkManager::setApplicationProxyFactory() { diff --git a/src/network/NetworkManager.h b/src/network/NetworkManager.h index 36c5ee2..b588bca 100644 --- a/src/network/NetworkManager.h +++ b/src/network/NetworkManager.h @@ -6,11 +6,14 @@ #pragma once +#include "Env.h" #include "GlobalStatus.h" #include #include #include +#include +#include #include #include #include @@ -23,11 +26,15 @@ class NetworkManager : public QObject { Q_OBJECT + friend class Env; private: bool mApplicationExitInProgress; - QAtomicInt mOpenConnectionCount; + QMutex mTrackedConnectionsMutex; + QSet mTrackedConnections; void trackConnection(QNetworkReply* pResponse, const int pTimeoutInMilliSeconds); + void addTrackedConnection(QNetworkReply* pResponse); + void removeTrackedConnection(QNetworkReply* pResponse); static bool mLockProxy; QScopedPointer mNetAccessManager; @@ -40,6 +47,7 @@ class NetworkManager protected: NetworkManager(); virtual ~NetworkManager(); + static NetworkManager& getInstance(); public: enum class NetworkError @@ -59,11 +67,12 @@ class NetworkManager } - static NetworkManager& getInstance(); + static int getLoggedStatusCode(const QNetworkReply* const pReply, const QMessageLogger& pLogger); static NetworkError toNetworkError(const QNetworkReply* const pNetworkReply); static GlobalStatus toTrustedChannelStatus(const QNetworkReply* const pNetworkReply); static GlobalStatus toStatus(const QNetworkReply* const pNetworkReply); static QString getTlsVersionString(QSsl::SslProtocol pProtocol); + static QByteArray getStatusMessage(int pStatus); virtual void clearConnections(); virtual QNetworkReply* paos(QNetworkRequest& pRequest, @@ -76,6 +85,10 @@ class NetworkManager const QByteArray& pSslSession = QByteArray(), int pTimeoutInMilliSeconds = 30000); + QNetworkReply* post(QNetworkRequest& pRequest, + const QByteArray& pData, + int pTimeoutInMilliSeconds = 30000); + virtual bool checkUpdateServerCertificate(const QNetworkReply& pReply); int getOpenConnectionCount(); @@ -85,6 +98,6 @@ class NetworkManager void fireShutdown(); }; -} /* namespace governikus */ +} // namespace governikus QDebug operator <<(QDebug pDbg, QSsl::SslProtocol pProtocol); diff --git a/src/network/NetworkReplyError.cpp b/src/network/NetworkReplyError.cpp index 97bc455..efa9bf9 100644 --- a/src/network/NetworkReplyError.cpp +++ b/src/network/NetworkReplyError.cpp @@ -33,5 +33,5 @@ NetworkReplyError::NetworkReplyError(QNetworkRequest const& pRequest, QObject* p setRequest(pRequest); setError(NetworkError::OperationCanceledError, QStringLiteral("Application shutting down")); - QMetaObject::invokeMethod(this, "onErrorSignals", Qt::QueuedConnection); + QMetaObject::invokeMethod(this, &NetworkReplyError::onErrorSignals, Qt::QueuedConnection); } diff --git a/src/network/NetworkReplyError.h b/src/network/NetworkReplyError.h index 07e5f9a..b3bc2e1 100644 --- a/src/network/NetworkReplyError.h +++ b/src/network/NetworkReplyError.h @@ -25,4 +25,4 @@ class NetworkReplyError explicit NetworkReplyError(const QNetworkRequest& pRequest, QObject* pParent = nullptr); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/network/NetworkReplyTimeout.cpp b/src/network/NetworkReplyTimeout.cpp index 611a321..7d74a0f 100644 --- a/src/network/NetworkReplyTimeout.cpp +++ b/src/network/NetworkReplyTimeout.cpp @@ -4,7 +4,6 @@ #include "NetworkReplyTimeout.h" -#include "Env.h" #include "NetworkManager.h" #include diff --git a/src/network/NetworkReplyTimeout.h b/src/network/NetworkReplyTimeout.h index 31f44a6..796cd52 100644 --- a/src/network/NetworkReplyTimeout.h +++ b/src/network/NetworkReplyTimeout.h @@ -33,4 +33,4 @@ class NetworkReplyTimeout static void setTimeout(QNetworkReply* pReply, const int pTimeoutMilliSeconds); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/network/PortFile.cpp b/src/network/PortFile.cpp new file mode 100644 index 0000000..b7f702d --- /dev/null +++ b/src/network/PortFile.cpp @@ -0,0 +1,46 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "PortFile.h" + +#include +#include + +using namespace governikus; + +const quint16 PortFile::cDefaultPort = 24727; + +QString PortFile::getPortFilename(const QString& pUsage, qint64 pPid, const QString& pApp) +{ + const QLatin1Char sep('.'); + const auto& usage = pUsage.isEmpty() ? pUsage : sep % pUsage; + + return QDir::tempPath() % QDir::separator() % pApp % sep % QString::number(pPid) % usage % QStringLiteral(".port"); +} + + +PortFile::PortFile(const QString& pUsage, quint16 pDefaultPort) + : mDefaultPort(pDefaultPort) + , mPortFile(getPortFilename(pUsage)) +{ +} + + +void PortFile::handlePort(quint16 pCurrentPort) +{ + if (pCurrentPort != mDefaultPort && mPortFile.open(QIODevice::WriteOnly) && mPortFile.isWritable()) + { + mPortFile.write(QByteArray::number(pCurrentPort)); + mPortFile.close(); + } +} + + +PortFile::~PortFile() +{ + if (mPortFile.exists()) + { + mPortFile.remove(); + } +} diff --git a/src/network/PortFile.h b/src/network/PortFile.h new file mode 100644 index 0000000..262fc54 --- /dev/null +++ b/src/network/PortFile.h @@ -0,0 +1,33 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include +#include + +namespace governikus +{ + +class PortFile +{ + private: + quint16 mDefaultPort; + QFile mPortFile; + + public: + static const quint16 cDefaultPort; + + static QString getPortFilename(const QString& pUsage = QString(), + qint64 pPid = QCoreApplication::applicationPid(), + const QString& pApp = QCoreApplication::applicationName()); + + PortFile(const QString& pUsage = QString(), quint16 pDefaultPort = cDefaultPort); + ~PortFile(); + + void handlePort(quint16 pCurrentPort); +}; + +} // namespace governikus diff --git a/src/network/TlsChecker.cpp b/src/network/TlsChecker.cpp index bfe5084..5a012b2 100644 --- a/src/network/TlsChecker.cpp +++ b/src/network/TlsChecker.cpp @@ -5,7 +5,6 @@ #include "TlsChecker.h" #include "AppSettings.h" -#include "Env.h" #include "SecureStorage.h" #include @@ -45,6 +44,12 @@ bool TlsChecker::checkCertificate(const QSslCertificate& pCertificate, bool TlsChecker::hasValidCertificateKeyLength(const QSslCertificate& pCertificate) { + if (pCertificate.isNull()) + { + qDebug() << "Certificate is null"; + return false; + } + auto keyLength = pCertificate.publicKey().length(); auto keyAlgorithm = pCertificate.publicKey().algorithm(); qDebug() << "Check certificate key of type" << TlsChecker::toString(keyAlgorithm) << "and key size" << keyLength; @@ -59,6 +64,7 @@ bool TlsChecker::hasValidEphemeralKeyLength(const QSslKey& pEphemeralServerKey) if (keyAlgorithm == QSsl::Opaque) { + qWarning() << "Qt failed to determine algorithm... fallback to internal handling"; // try do determine key algorithm and length if (auto key = static_cast(pEphemeralServerKey.handle())) { @@ -87,6 +93,17 @@ bool TlsChecker::hasValidEphemeralKeyLength(const QSslKey& pEphemeralServerKey) } +QString TlsChecker::getCertificateIssuerName(const QSslCertificate& pCertificate) +{ + const auto& issuerNameList = pCertificate.issuerInfo(QSslCertificate::CommonName); + if (!issuerNameList.isEmpty()) + { + return issuerNameList.first(); + } + return QString(); +} + + bool TlsChecker::isValidKeyLength(int pKeyLength, QSsl::KeyAlgorithm pKeyAlgorithm, bool pIsEphemeral) { const auto& secureStorage = SecureStorage::getInstance(); @@ -100,12 +117,12 @@ bool TlsChecker::isValidKeyLength(int pKeyLength, QSsl::KeyAlgorithm pKeyAlgorit auto keySizeError = QStringLiteral("%1 key with insufficient key size found %2").arg(toString(pKeyAlgorithm)).arg(pKeyLength); if (Env::getSingleton()->getGeneralSettings().isDeveloperMode()) { - qCWarning(developermode) << keySizeError; + qCWarning(developermode).noquote() << keySizeError; sufficient = true; } else { - qWarning() << keySizeError; + qWarning().noquote() << keySizeError; } } return sufficient; @@ -149,6 +166,26 @@ QString TlsChecker::toString(QSsl::SslProtocol pProtocol) case QSsl::SslProtocol::TlsV1_2OrLater: return QStringLiteral("TlsV1_2OrLater"); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) + case QSsl::SslProtocol::TlsV1_3: + return QStringLiteral("TlsV1_3"); + + case QSsl::SslProtocol::TlsV1_3OrLater: + return QStringLiteral("TlsV1_3OrLater"); + + case QSsl::SslProtocol::DtlsV1_0: + return QStringLiteral("DtlsV1_0"); + + case QSsl::SslProtocol::DtlsV1_0OrLater: + return QStringLiteral("DtlsV1_0OrLater"); + + case QSsl::SslProtocol::DtlsV1_2: + return QStringLiteral("DtlsV1_2"); + + case QSsl::SslProtocol::DtlsV1_2OrLater: + return QStringLiteral("DtlsV1_2OrLater"); + +#endif case QSsl::SslProtocol::UnknownProtocol: return QStringLiteral("UnknownProtocol"); } @@ -161,6 +198,9 @@ QString TlsChecker::toString(QSsl::KeyAlgorithm pKeyAlgorithm) { switch (pKeyAlgorithm) { + case QSsl::KeyAlgorithm::Opaque: + return QStringLiteral("Opaque"); + case QSsl::KeyAlgorithm::Dsa: return QStringLiteral("Dsa"); @@ -169,21 +209,21 @@ QString TlsChecker::toString(QSsl::KeyAlgorithm pKeyAlgorithm) case QSsl::KeyAlgorithm::Ec: return QStringLiteral("Ec"); - - default: - return QStringLiteral("Unknown (%1)").arg(pKeyAlgorithm); } + + return QStringLiteral("Unknown (%1)").arg(pKeyAlgorithm); } QStringList TlsChecker::getFatalErrors(const QList& pErrors) { static const QSet fatalErrors( - {QSslError::CertificateSignatureFailed, QSslError::CertificateNotYetValid, QSslError::CertificateExpired, - QSslError::InvalidNotBeforeField, QSslError::InvalidNotAfterField, QSslError::CertificateRevoked, QSslError::InvalidCaCertificate, - QSslError::CertificateRejected, QSslError::SubjectIssuerMismatch, QSslError::HostNameMismatch, - QSslError::UnspecifiedError, QSslError::NoSslSupport, QSslError::CertificateBlacklisted} - ); + { + QSslError::CertificateSignatureFailed, QSslError::CertificateNotYetValid, QSslError::CertificateExpired, + QSslError::InvalidNotBeforeField, QSslError::InvalidNotAfterField, QSslError::CertificateRevoked, QSslError::InvalidCaCertificate, + QSslError::CertificateRejected, QSslError::SubjectIssuerMismatch, QSslError::HostNameMismatch, + QSslError::UnspecifiedError, QSslError::NoSslSupport, QSslError::CertificateBlacklisted + }); QStringList fatalErrorStrings; for (const auto& error : pErrors) @@ -191,7 +231,7 @@ QStringList TlsChecker::getFatalErrors(const QList& pErrors) const auto& msg = error.errorString(); if (fatalErrors.contains(error.error())) { - if (AppSettings::getInstance().getGeneralSettings().isDeveloperMode()) + if (Env::getSingleton()->getGeneralSettings().isDeveloperMode()) { qCWarning(developermode) << msg; if (!error.certificate().isNull()) @@ -211,7 +251,7 @@ QStringList TlsChecker::getFatalErrors(const QList& pErrors) } else { - qCDebug(network) << "(ignored) " << msg; + qCDebug(network) << "(ignored)" << msg; } } return fatalErrorStrings; @@ -220,7 +260,12 @@ QStringList TlsChecker::getFatalErrors(const QList& pErrors) bool TlsChecker::containsFatalError(QNetworkReply* pReply, const QList& pErrors) { - Q_ASSERT(pReply); + if (pReply == nullptr) + { + qCCritical(network) << "Reply should not be nullptr"; + Q_ASSERT(pReply); + return false; + } if (getFatalErrors(pErrors).isEmpty()) { @@ -233,14 +278,13 @@ bool TlsChecker::containsFatalError(QNetworkReply* pReply, const QList +#include #include #include #include @@ -24,7 +25,7 @@ class TlsChecker static bool isValidKeyLength(int pKeyLength, QSsl::KeyAlgorithm pKeyAlgorithm, bool pIsEphemeral); public: - static void logSslConfig(const QSslConfiguration pCfg, QDebug pDebug); + static void logSslConfig(const QSslConfiguration pCfg, const QMessageLogger& pLogger); static QString toString(QSsl::SslProtocol pProtocol); static QString toString(QSsl::KeyAlgorithm pKeyAlgorithm); @@ -47,6 +48,11 @@ class TlsChecker * Checks, whether the length of the ephemeral key is of sufficient length. */ static bool hasValidEphemeralKeyLength(const QSslKey& pEphemeralServerKey); + + /*! + * This method is only needed until QSslCertificate provides its own method issuerDisplayName in Qt 5.12 + */ + static QString getCertificateIssuerName(const QSslCertificate& pCertificate); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/network/UrlUtil.cpp b/src/network/UrlUtil.cpp index b0e437f..f389df2 100644 --- a/src/network/UrlUtil.cpp +++ b/src/network/UrlUtil.cpp @@ -34,6 +34,7 @@ QUrl UrlUtil::getUrlOrigin(const QUrl& pUrl) origin.setScheme(scheme); origin.setHost(pUrl.host()); origin.setPort(pUrl.port(defaultPort)); + qDebug() << "Normalized URL from" << pUrl << "to" << origin; return origin; } @@ -54,34 +55,49 @@ QString UrlUtil::removePrefix(QString pStr) } +QString UrlUtil::getSuffix(ECardApiResult::Minor pMinor) +{ + return removePrefix(ECardApiResult::getMinorString(pMinor)); +} + + QUrl UrlUtil::addMajorMinor(const QUrl& pOriginUrl, const GlobalStatus& pStatus) { - const Result result(pStatus); - const QString& major = removePrefix(result.getMajorString()); - QUrlQuery q; q.setQuery(pOriginUrl.query()); + + const ECardApiResult::Major majorEnumVal = pStatus.isError() ? ECardApiResult::Major::Error : ECardApiResult::Major::Ok; + QString major = removePrefix(ECardApiResult::getMajorString(majorEnumVal)); q.addQueryItem(QStringLiteral("ResultMajor"), major); - if (result.getMinor() != GlobalStatus::Code::No_Error) + if (pStatus.isError()) { QString minor; - - if (result.isOriginServer()) + if (pStatus.isOriginServer()) { minor = QStringLiteral("serverError"); } - else if (result.getMinor() == GlobalStatus::Code::Paos_Error_AL_Communication_Error || - result.getMinor() == GlobalStatus::Code::Paos_Error_DP_Trusted_Channel_Establishment_Failed || - result.getMinor() == GlobalStatus::Code::Paos_Error_SAL_Cancellation_by_User) - { - minor = removePrefix(result.getMinorString()); - } else { - minor = QStringLiteral("clientError"); - } + switch (pStatus.getStatusCode()) + { + case GlobalStatus::Code::Paos_Error_AL_Communication_Error: + minor = getSuffix(ECardApiResult::Minor::AL_Communication_Error); + break; + case GlobalStatus::Code::Paos_Error_DP_Trusted_Channel_Establishment_Failed: + minor = getSuffix(ECardApiResult::Minor::DP_Trusted_Channel_Establishment_Failed); + break; + + case GlobalStatus::Code::Paos_Error_SAL_Cancellation_by_User: + minor = getSuffix(ECardApiResult::Minor::SAL_Cancellation_by_User); + break; + + default: + minor = QStringLiteral("clientError"); + break; + } + } q.addQueryItem(QStringLiteral("ResultMinor"), minor); } diff --git a/src/network/UrlUtil.h b/src/network/UrlUtil.h index bb6c712..708613d 100644 --- a/src/network/UrlUtil.h +++ b/src/network/UrlUtil.h @@ -5,7 +5,7 @@ #pragma once -#include "Result.h" +#include "ECardApiResult.h" #include #include @@ -25,6 +25,7 @@ class UrlUtil ~UrlUtil() = delete; static inline QString removePrefix(QString pStr); + static inline QString getSuffix(ECardApiResult::Minor pMinor); public: /*! @@ -44,4 +45,4 @@ class UrlUtil }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/network/WifiInfo.cpp b/src/network/WifiInfo.cpp index e0c0eae..62f4980 100644 --- a/src/network/WifiInfo.cpp +++ b/src/network/WifiInfo.cpp @@ -21,7 +21,7 @@ bool WifiInfo::isPrivateIp(const QHostAddress& pAddress) } -bool WifiInfo::hasPrivateIpAddress() const +bool WifiInfo::hasPrivateIpAddress() { const auto& interfaces = QNetworkInterface::allInterfaces(); for (const QNetworkInterface& interface : interfaces) diff --git a/src/network/WifiInfo.h b/src/network/WifiInfo.h index a535c91..45ef35d 100644 --- a/src/network/WifiInfo.h +++ b/src/network/WifiInfo.h @@ -27,9 +27,10 @@ class WifiInfo int mWifiEnableWaitCounter; static bool isPrivateIp(const QHostAddress& pAddress); + static bool hasPrivateIpAddress(); + bool getCurrentWifiEnabled(); bool shouldWifiEnabledBeCalled(); - bool hasPrivateIpAddress() const; protected: void timerEvent(QTimerEvent* pEvent) override; @@ -47,4 +48,4 @@ class WifiInfo }; -} +} // namespace governikus diff --git a/src/network/WifiInfo_android.cpp b/src/network/WifiInfo_android.cpp index c80ae2f..a54519f 100644 --- a/src/network/WifiInfo_android.cpp +++ b/src/network/WifiInfo_android.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include @@ -74,7 +73,6 @@ void WifiInfo::enableWifi() "(Landroid/content/Context;)Z", context.object()); - qCDebug(qml) << "enableWifi() returned:" << (jSuccess == JNI_TRUE); if (env->ExceptionCheck()) diff --git a/src/network/WifiInfo_generic.cpp b/src/network/WifiInfo_generic.cpp index c240bec..e2b37cb 100644 --- a/src/network/WifiInfo_generic.cpp +++ b/src/network/WifiInfo_generic.cpp @@ -4,8 +4,8 @@ #include "WifiInfo.h" +#include #include -#include #include Q_DECLARE_LOGGING_CATEGORY(qml) @@ -31,7 +31,13 @@ bool WifiInfo::getCurrentWifiEnabled() void WifiInfo::enableWifi() { - qCWarning(qml) << "NOT IMPLEMENTED"; +#ifndef QT_NO_DEBUG + if (!QCoreApplication::applicationName().startsWith(QLatin1String("QmlTest"))) + +#endif + { + qCWarning(qml) << "NOT IMPLEMENTED"; + } } @@ -43,7 +49,13 @@ void WifiInfo::timerEvent(QTimerEvent* pEvent) bool WifiInfo::isWifiEnabled() { - qCWarning(qml) << "NOT IMPLEMENTED"; +#ifndef QT_NO_DEBUG + if (!QCoreApplication::applicationName().startsWith(QLatin1String("QmlTest"))) + +#endif + { + qCWarning(qml) << "NOT IMPLEMENTED"; + } return mWifiEnabled; } diff --git a/src/network/WifiInfo_ios.mm b/src/network/WifiInfo_ios.mm index 79cc175..0e1fcd4 100644 --- a/src/network/WifiInfo_ios.mm +++ b/src/network/WifiInfo_ios.mm @@ -66,7 +66,8 @@ void WifiInfo::timerEvent(QTimerEvent* pEvent) if (pEvent->timerId() == mWifiCheckTimerId) { pEvent->accept(); - const bool currentEnabled = getCurrentWifiEnabled(); + + const bool currentEnabled = getCurrentWifiEnabled() || hasPrivateIpAddress(); if (mWifiEnabled != currentEnabled) { mWifiEnabled = currentEnabled; diff --git a/src/qml/ApplicationModel.cpp b/src/qml/ApplicationModel.cpp deleted file mode 100644 index ab942e4..0000000 --- a/src/qml/ApplicationModel.cpp +++ /dev/null @@ -1,230 +0,0 @@ -/*! - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "ApplicationModel.h" - -#include "context/AuthContext.h" -#include "context/ChangePinContext.h" -#include "context/SelfAuthContext.h" -#include "Env.h" -#include "ReaderInfo.h" -#include "ReaderManager.h" - -#if (defined(Q_OS_LINUX) && !defined(QT_NO_DEBUG)) || defined(Q_OS_ANDROID) || defined(Q_OS_IOS) -#include -#endif - -#ifdef Q_OS_ANDROID -#include -#include -#include -#endif - -using namespace governikus; - - -void ApplicationModel::onStatusChanged(const ReaderManagerPlugInInfo& pInfo) -{ - if (pInfo.getPlugInType() == ReaderManagerPlugInType::BLUETOOTH) - { - Q_EMIT fireBluetoothEnabledChanged(); - Q_EMIT fireBluetoothRespondingChanged(); - } - else if (pInfo.getPlugInType() == ReaderManagerPlugInType::NFC) - { - Q_EMIT fireNfcEnabledChanged(); - } -} - - -ApplicationModel::ApplicationModel(QObject* pParent) - : QObject(pParent) - , mContext() - , mWifiInfo() - , mBluetoothResponding(true) -{ - connect(&ReaderManager::getInstance(), &ReaderManager::fireReaderAdded, this, &ApplicationModel::fireBluetoothReaderChanged); - connect(&ReaderManager::getInstance(), &ReaderManager::fireReaderRemoved, this, &ApplicationModel::fireBluetoothReaderChanged); - connect(&ReaderManager::getInstance(), &ReaderManager::fireStatusChanged, this, &ApplicationModel::onStatusChanged); - connect(&ReaderManager::getInstance(), &ReaderManager::fireStatusChanged, this, &ApplicationModel::fireBluetoothReaderChanged); - connect(&ReaderManager::getInstance(), &ReaderManager::fireReaderAdded, this, &ApplicationModel::fireSelectedReaderChanged); - connect(&ReaderManager::getInstance(), &ReaderManager::fireReaderRemoved, this, &ApplicationModel::fireSelectedReaderChanged); - connect(&mWifiInfo, &WifiInfo::fireWifiEnabledChanged, this, &ApplicationModel::onWifiEnabledChanged); - - onWifiEnabledChanged(); - - const QSharedPointer& remoteClient = Env::getSingleton()->getRemoteClient(); - connect(remoteClient.data(), &RemoteClient::fireCertificateRemoved, this, &ApplicationModel::fireCertificateRemoved); -} - - -ApplicationModel::~ApplicationModel() -{ -} - - -void ApplicationModel::resetContext(const QSharedPointer& pContext) -{ - if (mContext) - { - disconnect(mContext.data(), &WorkflowContext::fireReaderPlugInTypesChanged, this, &ApplicationModel::fireSelectedReaderChanged); - } - - if ((mContext = pContext)) - { - connect(mContext.data(), &WorkflowContext::fireReaderPlugInTypesChanged, this, &ApplicationModel::fireSelectedReaderChanged); - } - Q_EMIT fireCurrentWorkflowChanged(); -} - - -ReaderManagerPlugInInfo ApplicationModel::getFirstPlugInInfo(ReaderManagerPlugInType pType) const -{ - const auto pluginInfos = ReaderManager::getInstance().getPlugInInfos(); - for (const auto& info : pluginInfos) - { - if (info.getPlugInType() == pType) - { - return info; - } - } - return ReaderManagerPlugInInfo(); -} - - -bool ApplicationModel::isNfcAvailable() const -{ -#if !defined(QT_NO_DEBUG) && !defined(Q_OS_IOS) && !defined(Q_OS_ANDROID) && !defined(Q_OS_WINRT) - return getFirstPlugInInfo(ReaderManagerPlugInType::PCSC).isAvailable(); - -#else - return getFirstPlugInInfo(ReaderManagerPlugInType::NFC).isAvailable(); - -#endif -} - - -bool ApplicationModel::isNfcEnabled() const -{ -#if !defined(QT_NO_DEBUG) && !defined(Q_OS_IOS) && !defined(Q_OS_ANDROID) && !defined(Q_OS_WINRT) - return getFirstPlugInInfo(ReaderManagerPlugInType::PCSC).isEnabled(); - -#else - return getFirstPlugInInfo(ReaderManagerPlugInType::NFC).isEnabled(); - -#endif -} - - -bool ApplicationModel::isBluetoothAvailable() const -{ - return getFirstPlugInInfo(ReaderManagerPlugInType::BLUETOOTH).isAvailable(); -} - - -bool ApplicationModel::isBluetoothResponding() const -{ - return getFirstPlugInInfo(ReaderManagerPlugInType::BLUETOOTH).isResponding(); -} - - -bool ApplicationModel::isBluetoothEnabled() const -{ - return getFirstPlugInInfo(ReaderManagerPlugInType::BLUETOOTH).isEnabled(); -} - - -void ApplicationModel::setBluetoothEnabled(bool pEnabled) -{ -#if (defined(Q_OS_LINUX) && !defined(QT_NO_DEBUG)) || defined(Q_OS_ANDROID) || defined(Q_OS_IOS) - if (pEnabled) - { - QBluetoothLocalDevice localDevice; - localDevice.powerOn(); - qDebug() << "Bluetooth enabled"; - } - else - { - qWarning() << "Cannot disable Bluetooth: not supported"; - } -#else - qWarning() << (pEnabled ? "Enabling" : "Disabling") << "Bluetooth not supported on this platform"; -#endif -} - - -bool ApplicationModel::locationPermissionRequired() const -{ -#ifdef Q_OS_ANDROID - if (QtAndroid::androidSdkVersion() < 23) - { - return false; - } - - QAndroidJniEnvironment env; - const int PERMISSION_GRANTED = 0; - const auto& permission = QAndroidJniObject::fromString(QStringLiteral("android.permission.ACCESS_COARSE_LOCATION")); - - const auto result = QAndroidJniObject::callStaticMethod( - "org/qtproject/qt5/android/QtNative", - "checkSelfPermission", - "(Ljava/lang/String;)I", - permission.object()); - - if (env->ExceptionCheck()) - { - env->ExceptionClear(); - qWarning() << "Exception occurred while checking location permission"; - return false; - } - - return (result != PERMISSION_GRANTED) && ReaderManager::getInstance().getReaderInfos(ReaderManagerPlugInType::BLUETOOTH).isEmpty(); - -#else - return false; - -#endif -} - - -QString ApplicationModel::getCurrentWorkflow() const -{ - if (mContext.objectCast()) - { - return QStringLiteral("changepin"); - } - if (mContext.objectCast()) - { - return QStringLiteral("selfauthentication"); - } - if (mContext.objectCast()) - { - return QStringLiteral("authentication"); - } - return QString(); -} - - -bool ApplicationModel::foundSelectedReader() const -{ - if (!mContext) - { - return false; - } - - return ReaderManager::getInstance().getReaderInfos(mContext->getReaderPlugInTypes()).size() > 0; -} - - -void ApplicationModel::onWifiEnabledChanged() -{ - mWifiEnabled = mWifiInfo.isWifiEnabled(); - Q_EMIT fireWifiEnabledChanged(); -} - - -Q_INVOKABLE void ApplicationModel::enableWifi() -{ - mWifiInfo.enableWifi(); -} diff --git a/src/qml/ApplicationModel.h b/src/qml/ApplicationModel.h deleted file mode 100644 index 24f77ef..0000000 --- a/src/qml/ApplicationModel.h +++ /dev/null @@ -1,88 +0,0 @@ -/*! - * \brief Model implementation for the application. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "ReaderManagerPlugInInfo.h" - -#include "ReaderInfo.h" -#include "WifiInfo.h" - -#include -#include -#include - -namespace governikus -{ - -class WorkflowContext; - -class ApplicationModel - : public QObject -{ - Q_OBJECT - - Q_PROPERTY(bool nfcEnabled READ isNfcEnabled NOTIFY fireNfcEnabledChanged) - Q_PROPERTY(bool nfcAvailable READ isNfcAvailable CONSTANT) - - Q_PROPERTY(bool bluetoothEnabled READ isBluetoothEnabled WRITE setBluetoothEnabled NOTIFY fireBluetoothEnabledChanged) - Q_PROPERTY(bool bluetoothResponding READ isBluetoothResponding NOTIFY fireBluetoothRespondingChanged) - Q_PROPERTY(bool bluetoothAvailable READ isBluetoothAvailable CONSTANT) - Q_PROPERTY(bool locationPermissionRequired READ locationPermissionRequired NOTIFY fireBluetoothReaderChanged) - - Q_PROPERTY(bool wifiEnabled MEMBER mWifiEnabled NOTIFY fireWifiEnabledChanged) - - Q_PROPERTY(QString currentWorkflow READ getCurrentWorkflow NOTIFY fireCurrentWorkflowChanged) - Q_PROPERTY(bool foundSelectedReader READ foundSelectedReader NOTIFY fireSelectedReaderChanged) - - QSharedPointer mContext; - - void onStatusChanged(const ReaderManagerPlugInInfo& pInfo); - ReaderManagerPlugInInfo getFirstPlugInInfo(ReaderManagerPlugInType pType) const; - - private: - WifiInfo mWifiInfo; - bool mWifiEnabled; - bool mBluetoothResponding; - - private Q_SLOTS: - void onWifiEnabledChanged(); - - public: - ApplicationModel(QObject* pParent = nullptr); - virtual ~ApplicationModel(); - void resetContext(const QSharedPointer& pContext = QSharedPointer()); - - bool isNfcAvailable() const; - bool isNfcEnabled() const; - - bool isBluetoothAvailable() const; - bool isBluetoothResponding() const; - bool isBluetoothEnabled() const; - void setBluetoothEnabled(bool pEnabled); - bool locationPermissionRequired() const; - - QString getCurrentWorkflow() const; - bool foundSelectedReader() const; - - Q_INVOKABLE void enableWifi(); - - Q_SIGNALS: - void fireNfcEnabledChanged(); - - void fireBluetoothEnabledChanged(); - void fireBluetoothRespondingChanged(); - void fireBluetoothReaderChanged(); - - void fireCurrentWorkflowChanged(); - void fireSelectedReaderChanged(); - - void fireWifiEnabledChanged(); - void fireCertificateRemoved(QString pDeviceName); -}; - - -} /* namespace governikus */ diff --git a/src/qml/AuthModel.cpp b/src/qml/AuthModel.cpp deleted file mode 100644 index 46418e3..0000000 --- a/src/qml/AuthModel.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/*! - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "AuthModel.h" - -#include "context/AuthContext.h" -#include "ReaderManagerPlugInInfo.h" - -using namespace governikus; - - -AuthModel::AuthModel(QObject* pParent) - : WorkflowModel(pParent) - , mContext() - , mTransactionInfo() -{ -} - - -AuthModel::~AuthModel() -{ -} - - -void AuthModel::resetContext(const QSharedPointer& pContext) -{ - mContext = pContext; - WorkflowModel::resetContext(pContext); - - if (mContext) - { - connect(mContext.data(), &AuthContext::fireDidAuthenticateEac1Changed, this, &AuthModel::onDidAuthenticateEac1Changed); - } - - if (!mTransactionInfo.isEmpty()) - { - mTransactionInfo.clear(); - - Q_EMIT fireTransactionInfoChanged(); - } -} - - -const QString& AuthModel::getTransactionInfo() const -{ - return mTransactionInfo; -} - - -void AuthModel::onDidAuthenticateEac1Changed() -{ - if (mContext) - { - const QSharedPointer& didAuthenticateEAC1 = mContext->getDidAuthenticateEac1(); - const QString newTransactionInfo = didAuthenticateEAC1.isNull() ? QString() : didAuthenticateEAC1->getTransactionInfo(); - if (newTransactionInfo != mTransactionInfo) - { - mTransactionInfo = newTransactionInfo; - - Q_EMIT fireTransactionInfoChanged(); - } - } -} diff --git a/src/qml/AuthModel.h b/src/qml/AuthModel.h deleted file mode 100644 index 0ef479c..0000000 --- a/src/qml/AuthModel.h +++ /dev/null @@ -1,47 +0,0 @@ -/*! - * \brief Model implementation for the authentication action. - * - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "WorkflowModel.h" - -#include -#include -#include - - -namespace governikus -{ - -class AuthContext; - -class AuthModel - : public WorkflowModel -{ - Q_OBJECT - - Q_PROPERTY(QString transactionInfo READ getTransactionInfo NOTIFY fireTransactionInfoChanged) - - QSharedPointer mContext; - QString mTransactionInfo; - - public: - AuthModel(QObject* pParent = nullptr); - virtual ~AuthModel() override; - - void resetContext(const QSharedPointer& pContext = QSharedPointer()); - - const QString& getTransactionInfo() const; - - public Q_SLOTS: - void onDidAuthenticateEac1Changed(); - - Q_SIGNALS: - void fireTransactionInfoChanged(); -}; - - -} /* namespace governikus */ diff --git a/src/qml/CMakeLists.txt b/src/qml/CMakeLists.txt deleted file mode 100644 index beaada7..0000000 --- a/src/qml/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -ADD_PLATFORM_LIBRARY(AusweisAppQml) - -TARGET_LINK_LIBRARIES(AusweisAppQml Qt5::Core Qt5::Svg Qt5::Qml Qt5::Quick Qt5::QuickControls2 AusweisAppCore AusweisAppGlobal AusweisAppRemoteDevice) -IF(DESKTOP) - TARGET_LINK_LIBRARIES(AusweisAppQml AusweisAppExport) -ENDIF() - -IF(TARGET Qt5::Bluetooth) - TARGET_LINK_LIBRARIES(AusweisAppQml Qt5::Bluetooth) -ENDIF() - -TARGET_COMPILE_DEFINITIONS(AusweisAppQml PRIVATE QT_STATICPLUGIN) - -STRING(REPLACE "\\" "/" RES_DIR ${RESOURCES_DIR}) -IF(CMAKE_GENERATOR STREQUAL Xcode OR CMAKE_VERSION VERSION_LESS "3.8") - TARGET_COMPILE_DEFINITIONS(AusweisAppQml PRIVATE $<$:RES_DIR="\\"${RES_DIR}\\"">) -ELSE() - SET_PROPERTY(SOURCE "UIPlugInQml.cpp" APPEND PROPERTY COMPILE_FLAGS $<$:-DRES_DIR="\\"${RES_DIR}\\"">) -ENDIF() diff --git a/src/qml/CertificateDescriptionModel.cpp b/src/qml/CertificateDescriptionModel.cpp deleted file mode 100644 index 0d08a56..0000000 --- a/src/qml/CertificateDescriptionModel.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/*! - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "asn1/CertificateDescription.h" -#include "AppSettings.h" -#include "context/AuthContext.h" -#include "context/SelfAuthContext.h" -#include "CertificateDescriptionModel.h" -#include "Env.h" -#include "SecureStorage.h" - - -using namespace governikus; - - -QSharedPointer CertificateDescriptionModel::getCertificateDescription() const -{ - if (mContext && mContext->getDidAuthenticateEac1()) - { - return mContext->getDidAuthenticateEac1()->getCertificateDescription(); - } - - return QSharedPointer(); -} - - -void CertificateDescriptionModel::onDidAuthenticateEac1Changed() -{ - beginResetModel(); - mData.clear(); - if (const auto& certDescr = getCertificateDescription()) - { - initModelData(certDescr); - } - endResetModel(); - - Q_EMIT fireChanged(); -} - - -void CertificateDescriptionModel::initModelData(const QSharedPointer& pCertDescription) -{ - const QString& serviceProviderAddress = pCertDescription->getServiceProviderAddress(); - const QString& purpose = getPurpose(); - const QString& dataSecurityOfficer = pCertDescription->getDataSecurityOfficer(); - const QString& termsOfUsage = pCertDescription->getTermsOfUsage(); - const bool showDetailedProviderInfo = !(serviceProviderAddress.isEmpty() || purpose.isEmpty() || dataSecurityOfficer.isEmpty()); - - mData += QPair(tr("Service provider"), getSubjectName() + QLatin1Char('\n') + getSubjectUrl()); - mData += QPair(tr("Certificate issuer"), pCertDescription->getIssuerName() + QLatin1Char('\n') + pCertDescription->getIssuerUrl()); - if (showDetailedProviderInfo) - { - mData += QPair(tr("Name, address and mail address of the service provider"), serviceProviderAddress); - mData += QPair(tr("Purpose"), purpose); - mData += QPair(tr("Indication of the bodies responsible for the service provider, " - "that verify the compliance with data security regulations"), dataSecurityOfficer); - } - else if (!termsOfUsage.isEmpty()) - { - mData += QPair(tr("Service provider information"), termsOfUsage); - } - if (!getValidity().isEmpty()) - { - mData += QPair(tr("Validity"), getValidity()); - } -} - - -CertificateDescriptionModel::CertificateDescriptionModel(QObject* pParent) - : QAbstractListModel(pParent) - , mData() - , mContext() -{ - resetContext(); - connect(&AppSettings::getInstance(), &AppSettings::fireSettingsChanged, this, &CertificateDescriptionModel::onDidAuthenticateEac1Changed); - connect(&AppSettings::getInstance().getGeneralSettings(), &GeneralSettings::fireSettingsChanged, this, [this]() - { - beginResetModel(); - onDidAuthenticateEac1Changed(); - endResetModel(); - }); -} - - -void CertificateDescriptionModel::resetContext(const QSharedPointer& pContext) -{ - mContext = pContext; - if (mContext) - { - connect(mContext.data(), &AuthContext::fireDidAuthenticateEac1Changed, this, &CertificateDescriptionModel::onDidAuthenticateEac1Changed); - } - - onDidAuthenticateEac1Changed(); -} - - -QString CertificateDescriptionModel::getSubjectName() const -{ - const auto& certificateDescription = getCertificateDescription(); - return certificateDescription ? getCertificateDescription()->getSubjectName() : QString(); -} - - -QString CertificateDescriptionModel::getSubjectUrl() const -{ - const auto& certificateDescription = getCertificateDescription(); - return certificateDescription ? getCertificateDescription()->getSubjectUrl() : QString(); -} - - -QString CertificateDescriptionModel::getPurpose() const -{ - const auto& certificateDescription = getCertificateDescription(); - return certificateDescription ? getCertificateDescription()->getPurpose() : QString(); -} - - -QString CertificateDescriptionModel::getValidity() const -{ - if (mContext && mContext->getDidAuthenticateEac1() && !mContext->getDidAuthenticateEac1()->getCvCertificates().isEmpty()) - { - const CVCertificateBody body = mContext->getDidAuthenticateEac1()->getCvCertificates().at(0)->getBody(); - const QString effectiveDate = body.getCertificateEffectiveDate().toString(Qt::DefaultLocaleShortDate); - const QString expirationDate = body.getCertificateExpirationDate().toString(Qt::DefaultLocaleShortDate); - - return QStringLiteral("%1 - %2").arg(effectiveDate, expirationDate); - } - return QString(); -} - - -int CertificateDescriptionModel::rowCount(const QModelIndex&) const -{ - return mData.size(); -} - - -QVariant CertificateDescriptionModel::data(const QModelIndex& pIndex, int pRole) const -{ - if (pIndex.isValid() && pIndex.row() < rowCount()) - { - auto entry = mData[pIndex.row()]; - if (pRole == LABEL) - { - return entry.first; - } - if (pRole == TEXT) - { - return entry.second; - } - } - return QVariant(); -} - - -QHash CertificateDescriptionModel::roleNames() const -{ - QHash roles = QAbstractListModel::roleNames(); - roles.insert(LABEL, "label"); - roles.insert(TEXT, "text"); - return roles; -} diff --git a/src/qml/CertificateDescriptionModel.h b/src/qml/CertificateDescriptionModel.h deleted file mode 100644 index 0becc1c..0000000 --- a/src/qml/CertificateDescriptionModel.h +++ /dev/null @@ -1,65 +0,0 @@ -/*! - * \brief Model implementation for the CV certificate description. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace governikus -{ - -class AuthContext; -struct CertificateDescription; -struct cvcertificate_st; - - -class CertificateDescriptionModel - : public QAbstractListModel -{ - Q_OBJECT - Q_PROPERTY(QString subjectName READ getSubjectName NOTIFY fireChanged) - Q_PROPERTY(QString purpose READ getPurpose NOTIFY fireChanged) - - QVector > mData; - QSharedPointer mContext; - - enum UserRoles - { - LABEL = Qt::UserRole + 1, - TEXT - }; - - - inline QSharedPointer getCertificateDescription() const; - inline QString getValidity() const; - void initModelData(const QSharedPointer& pCertDescription); - - private Q_SLOTS: - void onDidAuthenticateEac1Changed(); - - public: - CertificateDescriptionModel(QObject* pParent = nullptr); - - void resetContext(const QSharedPointer& pContext = QSharedPointer()); - - QString getSubjectName() const; - QString getSubjectUrl() const; - QString getPurpose() const; - - int rowCount(const QModelIndex& = QModelIndex()) const override; - QVariant data(const QModelIndex& pIndex, int pRole = Qt::DisplayRole) const override; - QHash roleNames() const override; - - Q_SIGNALS: - void fireChanged(); -}; - - -} /* namespace governikus */ diff --git a/src/qml/ChangePinModel.cpp b/src/qml/ChangePinModel.cpp deleted file mode 100644 index eb499e1..0000000 --- a/src/qml/ChangePinModel.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/*! - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "ChangePinModel.h" - -#include "context/ChangePinContext.h" -#include "ReaderManager.h" - - -using namespace governikus; - - -ChangePinModel::ChangePinModel(QObject* pParent) - : WorkflowModel(pParent) -{ -} - - -ChangePinModel::~ChangePinModel() -{ -} - - -void ChangePinModel::resetContext(const QSharedPointer& pContext) -{ - mContext = pContext; - WorkflowModel::resetContext(pContext); - - if (mContext) - { - connect(mContext.data(), &ChangePinContext::fireSuccessMessageChanged, this, &WorkflowModel::fireResultChanged); - - Q_EMIT fireResultChanged(); - Q_EMIT fireNewContextSet(); - } -} - - -QString ChangePinModel::getResultString() const -{ - if (!mContext) - { - return QString(); - } - - return isError() ? WorkflowModel::getResultString() : mContext->getSuccessMessage(); -} diff --git a/src/qml/ChangePinModel.h b/src/qml/ChangePinModel.h deleted file mode 100644 index fd04b82..0000000 --- a/src/qml/ChangePinModel.h +++ /dev/null @@ -1,41 +0,0 @@ -/*! - * \brief Model implementation for the PIN action. - * - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "WorkflowModel.h" - -#include -#include -#include - - -namespace governikus -{ - -class ChangePinContext; - -class ChangePinModel - : public WorkflowModel -{ - Q_OBJECT - - QSharedPointer mContext; - - public: - ChangePinModel(QObject* pParent = nullptr); - virtual ~ChangePinModel() override; - - void resetContext(const QSharedPointer& pContext = QSharedPointer()); - - virtual QString getResultString() const override; - - Q_SIGNALS: - void fireNewContextSet(); -}; - - -} /* namespace governikus */ diff --git a/src/qml/ChatModel.cpp b/src/qml/ChatModel.cpp deleted file mode 100644 index b315db6..0000000 --- a/src/qml/ChatModel.cpp +++ /dev/null @@ -1,224 +0,0 @@ -/*! - * \brief Model implementation for the chat. - * - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "ChatModel.h" - -#include "asn1/AccessRoleAndRight.h" -#include "asn1/CVCertificate.h" -#include "AppSettings.h" -#include "context/SelfAuthContext.h" - - -using namespace governikus; - - -ChatModel::ChatModel(QObject* pParent) - : QAbstractListModel(pParent) - , mAuthContext() - , mAllRights() - , mOptionalRights() - , mSelectedRights() - , mFilterOptionalModel() - , mFilterRequiredModel() -{ - resetContext(QSharedPointer()); - - initFilterModel(mFilterOptionalModel, QStringLiteral("true")); - initFilterModel(mFilterRequiredModel, QStringLiteral("false")); - - connect(&AppSettings::getInstance().getGeneralSettings(), &GeneralSettings::fireSettingsChanged, this, [this]() - { - beginResetModel(); - endResetModel(); - }); -} - - -void ChatModel::initFilterModel(QSortFilterProxyModel& pModel, const QString& pFilter) -{ - pModel.setSourceModel(this); - pModel.setFilterRole(ChatRoles::OPTIONAL_ROLE); - pModel.setFilterRegExp(pFilter); -} - - -void ChatModel::resetContext(const QSharedPointer& pContext) -{ - mAuthContext = pContext; - - if (pContext.objectCast()) - { - /* nothing to do, access rights are static */ - } - else if (!pContext.isNull() /* it's an AuthContext */) - { - beginResetModel(); - - mAllRights.clear(); - mOptionalRights.clear(); - mSelectedRights.clear(); - - endResetModel(); - - connect(mAuthContext.data(), &AuthContext::fireAuthenticationDataChanged, this, &ChatModel::onAuthenticationDataChanged); - } - else - { - /* set static access rights according to selfAuthentication*/ - Q_ASSERT(pContext.isNull()); - - beginResetModel(); - - mAllRights.clear(); - mAllRights += AccessRight::READ_DG05; - mAllRights += AccessRight::READ_DG13; - mAllRights += AccessRight::READ_DG04; - mAllRights += AccessRight::READ_DG07; - mAllRights += AccessRight::READ_DG08; - mAllRights += AccessRight::READ_DG09; - mAllRights += AccessRight::READ_DG17; - mAllRights += AccessRight::READ_DG01; - mAllRights += AccessRight::READ_DG10; - mAllRights += AccessRight::READ_DG06; - mAllRights += AccessRight::READ_DG02; - mAllRights += AccessRight::READ_DG19; - - mOptionalRights.clear(); - mSelectedRights = mAllRights.toSet(); - - endResetModel(); - } -} - - -void ChatModel::onAuthenticationDataChanged() -{ - beginResetModel(); - - mAllRights.clear(); - mOptionalRights.clear(); - mSelectedRights.clear(); - - if (!mAuthContext->getRequiredAccessRights().isEmpty()) - { - setOrderedAllRights(mAuthContext->getRequiredAccessRights()); - mSelectedRights += mAuthContext->getRequiredAccessRights(); - } - - if (!mAuthContext->getOptionalAccessRights().isEmpty()) - { - mOptionalRights += mAuthContext->getOptionalAccessRights(); - setOrderedAllRights(mAuthContext->getOptionalAccessRights()); - mSelectedRights += mAuthContext->getOptionalAccessRights(); - } - - endResetModel(); -} - - -void ChatModel::setOrderedAllRights(const QSet& pAllRights) -{ - for (auto right : AccessRoleAndRightsUtil::allDisplayedOrderedRights()) - { - if (pAllRights.contains(right)) - { - mAllRights += right; - } - } -} - - -int ChatModel::rowCount(const QModelIndex&) const -{ - return mAllRights.size(); -} - - -QVariant ChatModel::data(const QModelIndex& pIndex, int pRole) const -{ - if (pIndex.isValid() && pIndex.row() < rowCount()) - { - auto right = mAllRights.at(pIndex.row()); - if (pRole == Qt::DisplayRole || pRole == NAME_ROLE) - { - QString displayText = AccessRoleAndRightsUtil::toDisplayText(right); - if (right == AccessRight::AGE_VERIFICATION) - { - displayText += QStringLiteral(" (%1)").arg(mAuthContext->getRequiredAge()); - } - return displayText; - } - if (pRole == OPTIONAL_ROLE) - { - return mOptionalRights.contains(right); - } - if (pRole == SELECTED_ROLE) - { - return mSelectedRights.contains(right); - } - } - return QVariant(); -} - - -QHash ChatModel::roleNames() const -{ - QHash roles = QAbstractListModel::roleNames(); - roles.insert(NAME_ROLE, "name"); - roles.insert(OPTIONAL_ROLE, "optional"); - roles.insert(SELECTED_ROLE, "selected"); - return roles; -} - - -bool ChatModel::setData(const QModelIndex& pIndex, const QVariant& pValue, int pRole) -{ - if (pRole == SELECTED_ROLE) - { - auto right = mAllRights.at(pIndex.row()); - - if (pValue.toBool()) // is selected? - { - if (mSelectedRights.contains(right)) - { - return false; - } - else - { - mSelectedRights += right; - } - } - else if (!mSelectedRights.remove(right)) - { - return false; - } - - Q_EMIT dataChanged(pIndex, pIndex, QVector({SELECTED_ROLE})); - return true; - } - - return false; -} - - -void ChatModel::transferAccessRights() -{ - Q_ASSERT(mAuthContext); - - mAuthContext->setEffectiveAccessRights(mSelectedRights); -} - - -QSortFilterProxyModel* ChatModel::getFilterOptionalModel() -{ - return &mFilterOptionalModel; -} - - -QSortFilterProxyModel* ChatModel::getFilterRequiredModel() -{ - return &mFilterRequiredModel; -} diff --git a/src/qml/ChatModel.h b/src/qml/ChatModel.h deleted file mode 100644 index 2eb7383..0000000 --- a/src/qml/ChatModel.h +++ /dev/null @@ -1,65 +0,0 @@ -/*! - * \brief Model implementation for the chat. - * - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include -#include -#include -#include - -#include "context/AuthContext.h" - -namespace governikus -{ - -struct cvcertificate_st; - -class ChatModel - : public QAbstractListModel -{ - Q_OBJECT - Q_PROPERTY(QSortFilterProxyModel * optional READ getFilterOptionalModel CONSTANT) - Q_PROPERTY(QSortFilterProxyModel * required READ getFilterRequiredModel CONSTANT) - - QSharedPointer mAuthContext; - QList mAllRights; - QSet mOptionalRights, mSelectedRights; - QSortFilterProxyModel mFilterOptionalModel; - QSortFilterProxyModel mFilterRequiredModel; - - enum ChatRoles - { - NAME_ROLE = Qt::UserRole + 1, - OPTIONAL_ROLE, - SELECTED_ROLE - }; - - private: - void initFilterModel(QSortFilterProxyModel& pModel, const QString& pFilter); - void setOrderedAllRights(const QSet& pAllRights); - - private Q_SLOTS: - void onAuthenticationDataChanged(); - - public: - ChatModel(QObject* pParent = nullptr); - - void resetContext(const QSharedPointer& pContext = QSharedPointer()); - - int rowCount(const QModelIndex& = QModelIndex()) const override; - QVariant data(const QModelIndex& pIndex, int pRole = Qt::DisplayRole) const override; - bool setData(const QModelIndex& pIndex, const QVariant& pValue, int pRole) override; - QHash roleNames() const override; - - Q_INVOKABLE void transferAccessRights(); - Q_INVOKABLE QSortFilterProxyModel* getFilterOptionalModel(); - Q_INVOKABLE QSortFilterProxyModel* getFilterRequiredModel(); -}; - - -} /* namespace governikus */ diff --git a/src/qml/ConnectivityManager.cpp b/src/qml/ConnectivityManager.cpp deleted file mode 100644 index 330a4a6..0000000 --- a/src/qml/ConnectivityManager.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/*! - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "ConnectivityManager.h" - -#include -#include -#include - - -using namespace governikus; - - -Q_DECLARE_LOGGING_CATEGORY(network) - - -ConnectivityManager::ConnectivityManager(QObject* pParent) - : QObject(pParent) - , mTimerId(0) - , mActive(false) -{ -} - - -ConnectivityManager::~ConnectivityManager() -{ - if (mTimerId) - { - killTimer(mTimerId); - } -} - - -void ConnectivityManager::setActive(bool pActive, const QString& pInterfaceName) -{ - if (mActive != pActive) - { - if (pActive) - { - qCDebug(network) << "Found active network interface" << pInterfaceName; - } - else - { - qCDebug(network) << "Found no active network interface"; - } - mActive = pActive; - Q_EMIT fireNetworkInterfaceActiveChanged(mActive); - } -} - - -void ConnectivityManager::updateConnectivity() -{ - const auto& allInterfaces = QNetworkInterface::allInterfaces(); - for (const auto& iface : allInterfaces) - { - if (iface.flags().testFlag(QNetworkInterface::IsUp) - && iface.flags().testFlag(QNetworkInterface::IsRunning) - && !iface.flags().testFlag(QNetworkInterface::IsLoopBack) - && !iface.addressEntries().isEmpty()) - { - setActive(true, iface.name()); - return; - } - } - setActive(false); -} - - -void ConnectivityManager::timerEvent(QTimerEvent* pTimerEvent) -{ - if (pTimerEvent->timerId() == mTimerId) - { - updateConnectivity(); - } -} - - -bool ConnectivityManager::isNetworkInterfaceActive() const -{ - return mActive; -} - - -void ConnectivityManager::startWatching() -{ - if (mTimerId) - { - qCWarning(network) << "Already started, skip"; - return; - } - mTimerId = startTimer(1000); - updateConnectivity(); -} - - -void ConnectivityManager::stopWatching() -{ - if (!mTimerId) - { - qCWarning(network) << "Already stopped, skip"; - return; - } - killTimer(mTimerId); - mTimerId = 0; -} diff --git a/src/qml/ConnectivityManager.h b/src/qml/ConnectivityManager.h deleted file mode 100644 index 2986ab7..0000000 --- a/src/qml/ConnectivityManager.h +++ /dev/null @@ -1,44 +0,0 @@ -/*! - * \brief Utility class providing information about network connectivity status. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - - -#include - - -namespace governikus -{ - -class ConnectivityManager - : public QObject -{ - Q_OBJECT - Q_PROPERTY(bool networkInterfaceActive READ isNetworkInterfaceActive NOTIFY fireNetworkInterfaceActiveChanged) - - private: - int mTimerId; - bool mActive; - void setActive(bool pActive, const QString& pInterfaceName = QString()); - void updateConnectivity(); - - protected: - void timerEvent(QTimerEvent* pEvent) override; - - public: - ConnectivityManager(QObject* pParent = nullptr); - virtual ~ConnectivityManager() override; - - bool isNetworkInterfaceActive() const; - void startWatching(); - void stopWatching(); - - Q_SIGNALS: - void fireNetworkInterfaceActiveChanged(bool pActive); -}; - - -} /* namespace governikus */ diff --git a/src/qml/DpiCalculator.h b/src/qml/DpiCalculator.h deleted file mode 100644 index 710a112..0000000 --- a/src/qml/DpiCalculator.h +++ /dev/null @@ -1,23 +0,0 @@ -/*! - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include - -namespace governikus -{ - -class DpiCalculator -{ - DpiCalculator() - { - } - - - public: - static qreal getDpi(); -}; - -} /* namespace governikus */ diff --git a/src/qml/DpiCalculator_generic.cpp b/src/qml/DpiCalculator_generic.cpp deleted file mode 100644 index a9140d9..0000000 --- a/src/qml/DpiCalculator_generic.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "DpiCalculator.h" - -#include -#include -#include - -using namespace governikus; - - -Q_DECLARE_LOGGING_CATEGORY(qml) - - -qreal DpiCalculator::getDpi() -{ - qreal dpi = 0.0; - -#if defined(Q_OS_ANDROID) - auto qtDpi = qgetenv("QT_ANDROID_THEME_DISPLAY_DPI"); - bool ok; - dpi = qtDpi.toFloat(&ok); - if (!ok) - { - qCCritical(qml) << "Cannot get screen dpi"; - return -1.0; - } - -#elif defined(Q_OS_IOS) - qCCritical(qml) << "Determination of dpi on iOS not supported"; - return -1.0; - -#else - auto app = static_cast(QCoreApplication::instance()); - auto screen = app->primaryScreen(); - dpi = screen->logicalDotsPerInch(); -#endif - qCInfo(qml) << "Determined dpi" << dpi; - -#ifndef Q_NO_DEBUG - const char* overrideDpi = "OVERRIDE_DPI"; - if (!qEnvironmentVariableIsEmpty(overrideDpi)) - { - dpi = qEnvironmentVariableIntValue(overrideDpi); - qCDebug(qml) << "Override DPI:" << dpi; - } -#endif - - return dpi; - -} diff --git a/src/qml/HistoryModel.cpp b/src/qml/HistoryModel.cpp deleted file mode 100644 index a2a8fb3..0000000 --- a/src/qml/HistoryModel.cpp +++ /dev/null @@ -1,395 +0,0 @@ -/*! - * \brief Model implementation for the history entries. - * - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "HistoryModel.h" - -#include "Env.h" -#include "ProviderConfiguration.h" -#include "ProviderModel.h" - -#include -#include - -using namespace governikus; - - -namespace -{ -bool matchProviderWithSubjectUrl(const ProviderConfigurationInfo& pProvider, const QString& pSubjectUrl) -{ - const QString subjectUrlHost = QUrl(pSubjectUrl).host(); - - // Check provider address host. - if (QUrl(pProvider.getAddress()).host() == subjectUrlHost) - { - return true; - } - - // Check subject urls. - for (const auto& subjectUrl : pProvider.getSubjectUrls()) - { - if (QUrl(subjectUrl).host() == subjectUrlHost) - { - return true; - } - } - - return false; -} - - -} - - -HistoryProxyModel::HistoryProxyModel() -{ -} - - -HistoryProxyModel::~HistoryProxyModel() -{ -} - - -bool HistoryProxyModel::removeRows(int pRow, int pCount, const QModelIndex& pParent) -{ - return QSortFilterProxyModel::removeRows(pRow, pCount, pParent); -} - - -bool ProviderNameFilterModel::filterAcceptsRow(int pSourceRow, const QModelIndex& /* pSourceParent */) const -{ - HistoryModel* const dataSourceModel = qobject_cast(sourceModel()); - if (dataSourceModel == nullptr) - { - return false; - } - - auto entry = mHistorySettings->getHistoryInfos()[pSourceRow]; - - return matchProviderWithSubjectUrl(mProvider, entry.getSubjectUrl()); -} - - -ProviderNameFilterModel::ProviderNameFilterModel(HistorySettings* pHistorySettings) - : mHistorySettings(pHistorySettings) -{ -} - - -ProviderNameFilterModel::~ProviderNameFilterModel() -{ -} - - -void ProviderNameFilterModel::setProviderAddress(const QString& pProviderAddress) -{ - if (mProvider.getAddress() != pProviderAddress) - { - const auto& providers = Env::getSingleton()->getProviderConfigurationInfos(); - for (const auto& provider : providers) - { - if (provider.getAddress() == pProviderAddress) - { - mProvider = provider; - - invalidateFilter(); - - return; - } - } - - // If we get here, no provider for pProviderAddress was found in the settings. - qWarning() << "Cannot select provider with address" << pProviderAddress; - } -} - - -HistoryModel::HistoryModel(HistorySettings* pHistorySettings, QObject* pParent) - : QAbstractListModel(pParent) - , mHistorySettings(pHistorySettings) - , mFilterModel() - , mNameFilterModel(pHistorySettings) -{ - updateConnections(); - mFilterModel.setSourceModel(this); - mFilterModel.setFilterCaseSensitivity(Qt::CaseInsensitive); - mNameFilterModel.setSourceModel(this); - mHistoryModelSearchFilter.setSourceModel(this); - connect(mHistorySettings, &HistorySettings::fireHistoryInfosChanged, this, &HistoryModel::onHistoryEntriesChanged); - connect(mHistorySettings, &HistorySettings::fireEnabledChanged, this, &HistoryModel::fireEnabledChanged); - connect(Env::getSingleton(), &ProviderConfiguration::fireUpdated, this, &HistoryModel::onProvidersChanged); -} - - -HistoryModel::~HistoryModel() -{ -} - - -void HistoryModel::updateConnections() -{ - for (const auto& connection : qAsConst(mConnections)) - { - disconnect(connection); - } - mConnections.clear(); - - const auto& historyEntries = mHistorySettings->getHistoryInfos(); - mConnections.reserve(historyEntries.size() * 2); - for (int i = 0; i < historyEntries.size(); i++) - { - const auto& provider = determineProviderFor(historyEntries.at(i)); - const QModelIndex& modelIndex = createIndex(i, 0); - - mConnections += connect(provider.getIcon().data(), &UpdatableFile::fireUpdated, [ = ] { - Q_EMIT dataChanged(modelIndex, modelIndex, {PROVIDER_ICON}); - }); - mConnections += connect(provider.getImage().data(), &UpdatableFile::fireUpdated, [ = ] { - Q_EMIT dataChanged(modelIndex, modelIndex, {PROVIDER_IMAGE}); - }); - } -} - - -void HistoryModel::onHistoryEntriesChanged() -{ - beginResetModel(); - updateConnections(); - endResetModel(); -} - - -void HistoryModel::onProvidersChanged() -{ - const static QVector PROVIDER_ROLES({PROVIDER_CATEGORY, - PROVIDER_SHORTNAME, - PROVIDER_LONGNAME, - PROVIDER_SHORTDESCRIPTION, - PROVIDER_LONGDESCRIPTION, - PROVIDER_ADDRESS, - PROVIDER_ADDRESS_DOMAIN, - PROVIDER_HOMEPAGE, - PROVIDER_HOMEPAGE_BASE, - PROVIDER_PHONE, - PROVIDER_PHONE_COST, - PROVIDER_EMAIL, - PROVIDER_POSTALADDRESS, - PROVIDER_ICON, - PROVIDER_IMAGE}); - Q_EMIT dataChanged(index(0), index(rowCount() - 1), PROVIDER_ROLES); -} - - -int HistoryModel::rowCount(const QModelIndex&) const -{ - return mHistorySettings->getHistoryInfos().size(); -} - - -QVariant HistoryModel::data(const QModelIndex& pIndex, int pRole) const -{ - if (pIndex.isValid() && pIndex.row() < rowCount()) - { - auto entry = mHistorySettings->getHistoryInfos()[pIndex.row()]; - if (pRole == Qt::DisplayRole || pRole == SUBJECT) - { - return entry.getSubjectName(); - } - if (pRole == PURPOSE) - { - return entry.getPurpose(); - } - if (pRole == DATETIME) - { - return entry.getDateTime(); - } - if (pRole == TERMSOFUSAGE) - { - return entry.getTermOfUsage(); - } - if (pRole == REQUESTEDDATA) - { - return entry.getRequestedData(); - } - if (pRole == PROVIDER_CATEGORY) - { - auto provider = determineProviderFor(entry); - return provider.getCategory(); - } - if (pRole == PROVIDER_SHORTNAME) - { - auto provider = determineProviderFor(entry); - return provider.getShortName().toString(); - } - if (pRole == PROVIDER_LONGNAME) - { - auto provider = determineProviderFor(entry); - return provider.getLongName().toString(); - } - if (pRole == PROVIDER_SHORTDESCRIPTION) - { - auto provider = determineProviderFor(entry); - return provider.getShortDescription().toString(); - } - if (pRole == PROVIDER_LONGDESCRIPTION) - { - auto provider = determineProviderFor(entry); - return provider.getLongDescription().toString(); - } - if (pRole == PROVIDER_ADDRESS) - { - auto provider = determineProviderFor(entry); - return provider.getAddress(); - } - if (pRole == PROVIDER_ADDRESS_DOMAIN) - { - auto provider = determineProviderFor(entry); - return provider.getAddressDomain(); - } - if (pRole == PROVIDER_HOMEPAGE) - { - auto provider = determineProviderFor(entry); - return provider.getHomepage(); - } - if (pRole == PROVIDER_HOMEPAGE_BASE) - { - auto provider = determineProviderFor(entry); - return provider.getHomepageBase(); - } - if (pRole == PROVIDER_PHONE) - { - auto provider = determineProviderFor(entry); - return provider.getPhone(); - } - if (pRole == PROVIDER_PHONE_COST) - { - const auto& provider = determineProviderFor(entry); - const auto& cost = Env::getSingleton()->getCallCost(provider); - return ProviderModel::createCostString(cost); - } - if (pRole == PROVIDER_EMAIL) - { - auto provider = determineProviderFor(entry); - return provider.getEMail(); - } - if (pRole == PROVIDER_POSTALADDRESS) - { - auto provider = determineProviderFor(entry); - return provider.getPostalAddress(); - } - if (pRole == PROVIDER_ICON) - { - auto provider = determineProviderFor(entry); - return provider.getIcon()->lookupUrl(); - } - if (pRole == PROVIDER_IMAGE) - { - auto provider = determineProviderFor(entry); - return provider.getImage()->lookupUrl(); - } - } - return QVariant(); -} - - -ProviderConfigurationInfo HistoryModel::determineProviderFor(const HistoryInfo& pHistoryInfo) const -{ - QVector matchingProviders; - const auto& providers = Env::getSingleton()->getProviderConfigurationInfos(); - for (const auto& provider : providers) - { - if (matchProviderWithSubjectUrl(provider, pHistoryInfo.getSubjectUrl())) - { - matchingProviders += provider; - } - } - if (matchingProviders.size() == 1) - { - return matchingProviders.at(0); - } - return ProviderConfigurationInfo(); -} - - -bool HistoryModel::isEnabled() const -{ - return mHistorySettings->isEnabled(); -} - - -void HistoryModel::setEnabled(bool pEnabled) -{ - if (pEnabled != isEnabled()) - { - mHistorySettings->setEnabled(pEnabled); - mHistorySettings->save(); - } -} - - -QHash HistoryModel::roleNames() const -{ - QHash roles = QAbstractListModel::roleNames(); - roles.insert(SUBJECT, "subject"); - roles.insert(PURPOSE, "purpose"); - roles.insert(DATETIME, "dateTime"); - roles.insert(TERMSOFUSAGE, "termsOfUsage"); - roles.insert(REQUESTEDDATA, "requestedData"); - roles.insert(PROVIDER_CATEGORY, "providerCategory"); - roles.insert(PROVIDER_SHORTNAME, "providerShortName"); - roles.insert(PROVIDER_LONGNAME, "providerLongName"); - roles.insert(PROVIDER_SHORTDESCRIPTION, "providerShortDescription"); - roles.insert(PROVIDER_LONGDESCRIPTION, "providerLongDescription"); - roles.insert(PROVIDER_ADDRESS, "providerAddress"); - roles.insert(PROVIDER_ADDRESS_DOMAIN, "providerAddressDomain"); - roles.insert(PROVIDER_HOMEPAGE, "providerHomepage"); - roles.insert(PROVIDER_HOMEPAGE_BASE, "providerHomepageBase"); - roles.insert(PROVIDER_PHONE, "providerPhone"); - roles.insert(PROVIDER_PHONE_COST, "providerPhoneCost"); - roles.insert(PROVIDER_EMAIL, "providerEmail"); - roles.insert(PROVIDER_POSTALADDRESS, "providerPostalAddress"); - roles.insert(PROVIDER_ICON, "providerIcon"); - roles.insert(PROVIDER_IMAGE, "providerImage"); - return roles; -} - - -bool HistoryModel::removeRows(int pRow, int pCount, const QModelIndex& pParent) -{ - beginRemoveRows(pParent, pRow, pRow + pCount - 1); - - auto entries = mHistorySettings->getHistoryInfos(); - entries.remove(pRow, pCount); - - // disconnect the signal, otherwise this model gets reset - disconnect(mHistorySettings, &HistorySettings::fireHistoryInfosChanged, this, &HistoryModel::onHistoryEntriesChanged); - mHistorySettings->setHistoryInfos(entries); - connect(mHistorySettings, &HistorySettings::fireHistoryInfosChanged, this, &HistoryModel::onHistoryEntriesChanged); - - mHistorySettings->save(); - - endRemoveRows(); - return true; -} - - -HistoryProxyModel* HistoryModel::getFilterModel() -{ - return &mFilterModel; -} - - -ProviderNameFilterModel* HistoryModel::getNameFilterModel() -{ - return &mNameFilterModel; -} - - -HistoryModelSearchFilter* HistoryModel::getHistoryModelSearchFilter() -{ - return &mHistoryModelSearchFilter; -} diff --git a/src/qml/HistoryModel.h b/src/qml/HistoryModel.h deleted file mode 100644 index dc75f98..0000000 --- a/src/qml/HistoryModel.h +++ /dev/null @@ -1,125 +0,0 @@ -/*! - * \brief Model implementation for the history entries. - * - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "HistoryModelSearchFilter.h" -#include "HistorySettings.h" -#include "ProviderConfigurationInfo.h" - -#include -#include -#include - -namespace governikus -{ - -class HistoryProxyModel - : public QSortFilterProxyModel -{ - Q_OBJECT - - public: - Q_INVOKABLE bool removeRows(int pRow, int pCount, const QModelIndex& pParent = QModelIndex()) override; - - HistoryProxyModel(); - - virtual ~HistoryProxyModel() override; -}; - - -class ProviderNameFilterModel - : public QSortFilterProxyModel -{ - Q_OBJECT - - private: - QPointer mHistorySettings; - - ProviderConfigurationInfo mProvider; - - protected: - bool filterAcceptsRow(int pSourceRow, const QModelIndex& pSourceParent) const override; - - public: - ProviderNameFilterModel(HistorySettings* pHistorySettings); - - virtual ~ProviderNameFilterModel() override; - - Q_INVOKABLE void setProviderAddress(const QString& pProviderAddress); - -}; - - -class HistoryModel - : public QAbstractListModel -{ - Q_OBJECT - Q_PROPERTY(HistoryProxyModel * filter READ getFilterModel CONSTANT) - Q_PROPERTY(ProviderNameFilterModel * nameFilter READ getNameFilterModel CONSTANT) - Q_PROPERTY(HistoryModelSearchFilter * searchFilter READ getHistoryModelSearchFilter CONSTANT) - Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY fireEnabledChanged) - - QPointer mHistorySettings; - HistoryProxyModel mFilterModel; - ProviderNameFilterModel mNameFilterModel; - HistoryModelSearchFilter mHistoryModelSearchFilter; - - private: - QVector mConnections; - - ProviderConfigurationInfo determineProviderFor(const HistoryInfo& pHistoryInfo) const; - - bool isEnabled() const; - void setEnabled(bool pEnabled); - void updateConnections(); - - private Q_SLOTS: - void onHistoryEntriesChanged(); - void onProvidersChanged(); - - Q_SIGNALS: - void fireEnabledChanged(bool pValue); - - public: - HistoryModel(HistorySettings* pHistorySettings, QObject* pParent = nullptr); - virtual ~HistoryModel() override; - - enum HistoryRoles - { - SUBJECT = Qt::UserRole + 1, - PURPOSE, - DATETIME, - TERMSOFUSAGE, - REQUESTEDDATA, - PROVIDER_CATEGORY, - PROVIDER_SHORTNAME, - PROVIDER_LONGNAME, - PROVIDER_SHORTDESCRIPTION, - PROVIDER_LONGDESCRIPTION, - PROVIDER_ADDRESS, - PROVIDER_ADDRESS_DOMAIN, - PROVIDER_HOMEPAGE, - PROVIDER_HOMEPAGE_BASE, - PROVIDER_PHONE, - PROVIDER_PHONE_COST, - PROVIDER_EMAIL, - PROVIDER_POSTALADDRESS, - PROVIDER_ICON, - PROVIDER_IMAGE - }; - - int rowCount(const QModelIndex& = QModelIndex()) const override; - QVariant data(const QModelIndex& pIndex, int pRole = Qt::DisplayRole) const override; - QHash roleNames() const override; - Q_INVOKABLE bool removeRows(int pRow, int pCount, const QModelIndex& pParent = QModelIndex()) override; - - Q_INVOKABLE HistoryProxyModel* getFilterModel(); - Q_INVOKABLE ProviderNameFilterModel* getNameFilterModel(); - HistoryModelSearchFilter* getHistoryModelSearchFilter(); -}; - -} /* namespace governikus */ diff --git a/src/qml/HistoryModelSearchFilter.h b/src/qml/HistoryModelSearchFilter.h deleted file mode 100644 index dd04dcb..0000000 --- a/src/qml/HistoryModelSearchFilter.h +++ /dev/null @@ -1,31 +0,0 @@ -/*! - * \brief A filter to search the history model - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include -#include - -namespace governikus -{ - -class HistoryModelSearchFilter - : public QSortFilterProxyModel -{ - Q_OBJECT - - private: - QString mFilterString; - - protected: - bool filterAcceptsRow(int pSourceRow, const QModelIndex&) const override; - - public: - Q_INVOKABLE void setFilterString(const QString& pFilterString); -}; - -} diff --git a/src/qml/NumberModel.cpp b/src/qml/NumberModel.cpp deleted file mode 100644 index e943f53..0000000 --- a/src/qml/NumberModel.cpp +++ /dev/null @@ -1,264 +0,0 @@ -/*! - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "NumberModel.h" - -#include "context/ChangePinContext.h" -#include "context/RemoteServiceContext.h" -#include "context/WorkflowContext.h" -#include "ReaderManager.h" - - -using namespace governikus; - - -NumberModel::NumberModel(QObject* pParent) - : QObject(pParent) -{ - connect(&ReaderManager::getInstance(), &ReaderManager::fireReaderPropertiesUpdated, this, &NumberModel::onReaderInfoChanged); - connect(&ReaderManager::getInstance(), &ReaderManager::fireCardRetryCounterChanged, this, &NumberModel::onReaderInfoChanged); - connect(&ReaderManager::getInstance(), &ReaderManager::fireReaderRemoved, this, &NumberModel::onReaderInfoChanged); - connect(&ReaderManager::getInstance(), &ReaderManager::fireCardRemoved, this, &NumberModel::onReaderInfoChanged); - connect(&ReaderManager::getInstance(), &ReaderManager::fireCardInserted, this, &NumberModel::onReaderInfoChanged); -} - - -NumberModel::~NumberModel() -{ -} - - -void NumberModel::resetContext(const QSharedPointer& pContext) -{ - mContext = pContext; - if (mContext) - { - connect(mContext.data(), &WorkflowContext::fireCanChanged, this, &NumberModel::fireCanChanged); - connect(mContext.data(), &WorkflowContext::firePinChanged, this, &NumberModel::firePinChanged); - connect(mContext.data(), &WorkflowContext::fireCanAllowedModeChanged, this, &NumberModel::fireCanAllowedModeChanged); - - const auto changePinContext = mContext.objectCast(); - if (changePinContext) - { - connect(changePinContext.data(), &ChangePinContext::fireNewPinChanged, this, &NumberModel::fireNewPinChanged); - connect(changePinContext.data(), &ChangePinContext::firePukChanged, this, &NumberModel::firePukChanged); - } - - connect(mContext.data(), &WorkflowContext::fireCardConnectionChanged, this, &NumberModel::onCardConnectionChanged); - connect(mContext.data(), &WorkflowContext::fireReaderNameChanged, this, &NumberModel::fireReaderInfoChanged); - connect(mContext.data(), &WorkflowContext::fireLastPaceResultChanged, this, &NumberModel::fireInputErrorChanged); - } - - Q_EMIT fireCanChanged(); - Q_EMIT firePinChanged(); - Q_EMIT fireNewPinChanged(); - Q_EMIT firePukChanged(); - Q_EMIT fireInputErrorChanged(); - Q_EMIT fireReaderInfoChanged(); - Q_EMIT fireCanAllowedModeChanged(); -} - - -void NumberModel::continueWorkflow() -{ - if (mContext) - { - mContext->setStateApproved(); - } -} - - -QString NumberModel::getCan() const -{ - return mContext ? mContext->getCan() : QString(); -} - - -void NumberModel::setCan(const QString& pCan) -{ - if (mContext) - { - mContext->setCan(pCan); - } -} - - -QString NumberModel::getPin() const -{ - return mContext ? mContext->getPin() : QString(); -} - - -void NumberModel::setPin(const QString& pPin) -{ - if (mContext) - { - mContext->setPin(pPin); - } -} - - -QString NumberModel::getNewPin() const -{ - const auto changePinContext = mContext.objectCast(); - - return changePinContext ? changePinContext->getNewPin() : QString(); -} - - -void NumberModel::setNewPin(const QString& pNewPin) -{ - const auto changePinContext = mContext.objectCast(); - if (changePinContext) - { - changePinContext->setNewPin(pNewPin); - } - - const auto remoteServiceContext = mContext.objectCast(); - if (remoteServiceContext) - { - remoteServiceContext->setNewPin(pNewPin); - } -} - - -QString NumberModel::getPuk() const -{ - const auto changePinContext = mContext.objectCast(); - return changePinContext ? changePinContext->getPuk() : QString(); -} - - -void NumberModel::setPuk(const QString& pPuk) -{ - const auto changePinContext = mContext.objectCast(); - if (changePinContext) - { - changePinContext->setPuk(pPuk); - } -} - - -bool NumberModel::hasError() -{ - return !getInputError().isEmpty() || isExtendedLengthApdusUnsupported() || isPinDeactivated(); -} - - -QString NumberModel::getInputError() const -{ - if (mContext.isNull() - || mContext->getLastPaceResult() == CardReturnCode::OK - || mContext->getLastPaceResult() == CardReturnCode::CANCELLATION_BY_USER - || mContext->getCardConnection().isNull()) - { - return QString(); - } - - CardReturnCode paceResult = mContext->getLastPaceResult(); - if (paceResult == CardReturnCode::INVALID_PIN) - { - int oldRetryCounter = mContext->getOldRetryCounter(); - if (oldRetryCounter > 2) - { - return tr("The given PIN is not correct. You have 2 tries to enter the correct PIN."); - } - if (oldRetryCounter == 2) - { - return tr("You have entered the wrong PIN twice. " - "Prior to a third attempt, you have to enter your six-digit card access number first. " - "You can find your card access number on the front of your ID card."); - } - if (oldRetryCounter == 1) - { - return tr("You have entered a wrong PIN three times. " - "Your PIN is now blocked. " - "You have to enter the PUK now for unblocking."); - } - } - if (paceResult == CardReturnCode::INVALID_CAN) - { - return tr("You have entered a wrong CAN, please try again."); - } - if (paceResult == CardReturnCode::INVALID_PUK) - { - return tr("You have entered a wrong PUK. " - "Please try again."); - } - return CardReturnCodeUtil::toGlobalStatus(paceResult).toErrorDescription(true); -} - - -void NumberModel::onCardConnectionChanged() -{ - Q_ASSERT(mContext); - if (auto cardConnection = mContext->getCardConnection()) - { - connect(cardConnection.data(), &CardConnection::fireReaderInfoChanged, this, &NumberModel::fireReaderInfoChanged); - } - Q_EMIT fireReaderInfoChanged(); -} - - -int NumberModel::getRetryCounter() const -{ - if (mContext.isNull() || mContext->getCardConnection().isNull()) - { - return -1; - } - else - { - return mContext->getCardConnection()->getReaderInfo().getRetryCounter(); - } -} - - -bool NumberModel::isExtendedLengthApdusUnsupported() const -{ - if (mContext && !mContext->getReaderName().isEmpty()) - { - ReaderInfo readerInfo = ReaderManager::getInstance().getReaderInfo(mContext->getReaderName()); - return !readerInfo.sufficientApduLength(); - } - return false; -} - - -bool NumberModel::isPinDeactivated() const -{ - if (mContext && !mContext->getReaderName().isEmpty()) - { - return ReaderManager::getInstance().getReaderInfo(mContext->getReaderName()).isPinDeactivated(); - } - return false; -} - - -bool NumberModel::isCardConnected() const -{ - if (mContext && !mContext->getReaderName().isEmpty()) - { - return ReaderManager::getInstance().getReaderInfo(mContext->getReaderName()).hasCard(); - } - return false; -} - - -bool NumberModel::isCanAllowedMode() -{ - if (mContext) - { - return mContext->isCanAllowedMode(); - } - return false; -} - - -void NumberModel::onReaderInfoChanged(const QString& pReaderName) -{ - if (mContext && pReaderName == mContext->getReaderName()) - { - Q_EMIT fireReaderInfoChanged(); - } -} diff --git a/src/qml/NumberModel.h b/src/qml/NumberModel.h deleted file mode 100644 index 890a016..0000000 --- a/src/qml/NumberModel.h +++ /dev/null @@ -1,86 +0,0 @@ -/*! - * \brief Model for accessing PIN, CAN, PUK, according to the - * currently active workflow. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include - -namespace governikus -{ - -class WorkflowContext; - -class NumberModel - : public QObject -{ - Q_OBJECT - Q_PROPERTY(QString can READ getCan WRITE setCan NOTIFY fireCanChanged) - Q_PROPERTY(QString pin READ getPin WRITE setPin NOTIFY firePinChanged) - Q_PROPERTY(QString newPin READ getNewPin WRITE setNewPin NOTIFY fireNewPinChanged) - Q_PROPERTY(QString puk READ getPuk WRITE setPuk NOTIFY firePukChanged) - Q_PROPERTY(bool hasError READ hasError NOTIFY fireReaderInfoChanged) - Q_PROPERTY(QString inputError READ getInputError NOTIFY fireInputErrorChanged) - Q_PROPERTY(int retryCounter READ getRetryCounter NOTIFY fireReaderInfoChanged) - Q_PROPERTY(bool extendedLengthApdusUnsupported READ isExtendedLengthApdusUnsupported NOTIFY fireReaderInfoChanged) - Q_PROPERTY(bool pinDeactivated READ isPinDeactivated NOTIFY fireReaderInfoChanged) - Q_PROPERTY(bool cardConnected READ isCardConnected NOTIFY fireReaderInfoChanged) - Q_PROPERTY(bool isCanAllowedMode READ isCanAllowedMode NOTIFY fireCanAllowedModeChanged) - - QSharedPointer mContext; - - private Q_SLOTS: - void onCardConnectionChanged(); - - public: - NumberModel(QObject* pParent = nullptr); - virtual ~NumberModel(); - - void resetContext(const QSharedPointer& pContext = QSharedPointer()); - Q_INVOKABLE void continueWorkflow(); - - QString getCan() const; - void setCan(const QString& pCan); - - QString getPin() const; - void setPin(const QString& pPin); - - QString getNewPin() const; - void setNewPin(const QString& pNewPin); - - QString getPuk() const; - void setPuk(const QString& pPuk); - - bool hasError(); - - QString getInputError() const; - - int getRetryCounter() const; - - bool isExtendedLengthApdusUnsupported() const; - - bool isPinDeactivated() const; - - bool isCardConnected() const; - - bool isCanAllowedMode(); - - private Q_SLOTS: - void onReaderInfoChanged(const QString& pReaderName); - - Q_SIGNALS: - void fireCanChanged(); - void firePinChanged(); - void fireNewPinChanged(); - void firePukChanged(); - void fireInputErrorChanged(); - void fireReaderInfoChanged(); - void fireCanAllowedModeChanged(); -}; - - -} /* namespace governikus */ diff --git a/src/qml/ProviderCategoryFilterModel.cpp b/src/qml/ProviderCategoryFilterModel.cpp deleted file mode 100644 index 27491f7..0000000 --- a/src/qml/ProviderCategoryFilterModel.cpp +++ /dev/null @@ -1,177 +0,0 @@ -/*! - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "ProviderCategoryFilterModel.h" - - -using namespace governikus; - -namespace -{ -const QStringList& getCategories() -{ - static QStringList cats({QStringLiteral("citizen"), QStringLiteral("insurance"), QStringLiteral("finance"), QStringLiteral("other")}); - return cats; -} - - -} - - -QString ProviderCategoryFilterModel::getSearchString() const -{ - return mSearchString; -} - - -void ProviderCategoryFilterModel::updateSearchString(const QString& pSearchString) -{ - const QString& newSearchString = pSearchString.trimmed(); - if (mSearchString != newSearchString) - { - mSearchString = newSearchString; - invalidateFilter(); - Q_EMIT fireCriteriaChanged(); - } -} - - -QStringList ProviderCategoryFilterModel::getSelectedCategories() const -{ - return mSelectedCategories.toList(); -} - - -int ProviderCategoryFilterModel::getAdditionalResultCount() const -{ - int results = 0; - for (const QString& p : getCategories()) - { - results += matchesForExcludedCategory(p); - } - return results; -} - - -int ProviderCategoryFilterModel::matchesForExcludedCategory(const QString& pCategory) const -{ - if (mSearchString.isEmpty() || mSelectedCategories.isEmpty() || mSelectedCategories.contains(pCategory)) - { - return 0; - } - - QAbstractItemModel* const model = sourceModel(); - const int count = model->rowCount(); - int matchCount = 0; - for (int sourceRow = 0; sourceRow < count; ++sourceRow) - { - const QModelIndex idx = model->index(sourceRow, 0, QModelIndex()); - const QString dt = model->data(idx, Qt::DisplayRole).toString(); - if (!dt.contains(mSearchString, Qt::CaseInsensitive)) - { - continue; - } - - if (pCategory.toLower() == model->data(idx, ProviderModel::CATEGORY).toString().toLower()) - { - matchCount++; - } - } - - return matchCount; -} - - -bool ProviderCategoryFilterModel::filterAcceptsRow(int pSourceRow, const QModelIndex& pSourceParent) const -{ - QAbstractItemModel* const model = sourceModel(); - Q_ASSERT(model != nullptr); - const QModelIndex idx = model->index(pSourceRow, 0, pSourceParent); - - if (!mSearchString.isEmpty()) - { - const QString dt = model->data(idx, Qt::DisplayRole).toString(); - if (!dt.contains(mSearchString, Qt::CaseInsensitive)) - { - return false; - } - } - - return mSelectedCategories.isEmpty() || mSelectedCategories.contains(QStringLiteral("all")) || - mSelectedCategories.contains(model->data(idx, ProviderModel::CATEGORY).toString().toLower()); -} - - -ProviderCategoryFilterModel::ProviderCategoryFilterModel() : - mProviderModel() -{ - QSortFilterProxyModel::setSourceModel(&mProviderModel); - - QSortFilterProxyModel::sort(0); - sortByCategoryFirst(false); - setSortCaseSensitivity(Qt::CaseInsensitive); -} - - -ProviderCategoryFilterModel::~ProviderCategoryFilterModel() -{ -} - - -void ProviderCategoryFilterModel::sortByCategoryFirst(bool pEnabled) -{ - setSortRole(pEnabled ? ProviderModel::SORT_ROLE : ProviderModel::SHORTNAME); -} - - -void ProviderCategoryFilterModel::setCategorySelection(const QString& pCategory) -{ - mSelectedCategories.clear(); - - if (!pCategory.isEmpty()) - { - mSelectedCategories.insert(pCategory.toLower()); - } - invalidateFilter(); - Q_EMIT fireCriteriaChanged(); -} - - -void ProviderCategoryFilterModel::updateCategorySelection(const QString& pCategory, bool pSelected) -{ - const int categoryCount = mSelectedCategories.count(); - if (pSelected) - { - mSelectedCategories.insert(pCategory.toLower()); - } - else - { - mSelectedCategories.remove(pCategory.toLower()); - } - - if (mSelectedCategories.count() != categoryCount) - { - invalidateFilter(); - Q_EMIT fireCriteriaChanged(); - } -} - - -void ProviderCategoryFilterModel::addAdditionalResultCategories() -{ - bool needUpdate = false; - for (const QString& p : getCategories()) - { - if (matchesForExcludedCategory(p) > 0) - { - needUpdate = true; - mSelectedCategories += p; - } - } - if (needUpdate) - { - invalidateFilter(); - Q_EMIT fireCriteriaChanged(); - } -} diff --git a/src/qml/ProviderCategoryFilterModel.h b/src/qml/ProviderCategoryFilterModel.h deleted file mode 100644 index 6d996ac..0000000 --- a/src/qml/ProviderCategoryFilterModel.h +++ /dev/null @@ -1,58 +0,0 @@ -/*! - * \brief Model implementation for the providers. - * - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "ProviderModel.h" - -#include -#include -#include - - -namespace governikus -{ - -class ProviderCategoryFilterModel - : public QSortFilterProxyModel -{ - Q_OBJECT - Q_PROPERTY(QString searchString READ getSearchString WRITE updateSearchString NOTIFY fireCriteriaChanged) - Q_PROPERTY(QStringList categories READ getSelectedCategories NOTIFY fireCriteriaChanged) - Q_PROPERTY(int rowCount READ rowCount NOTIFY fireCriteriaChanged) - Q_PROPERTY(int additionalResultCount READ getAdditionalResultCount NOTIFY fireCriteriaChanged) - - private: - QString mSearchString; - QSet mSelectedCategories; - - ProviderModel mProviderModel; - - QString getSearchString() const; - void updateSearchString(const QString& pSearchString); - QStringList getSelectedCategories() const; - int getAdditionalResultCount() const; - int matchesForExcludedCategory(const QString& pCategory) const; - - protected: - bool filterAcceptsRow(int pSourceRow, const QModelIndex& pSourceParent) const override; - - public: - ProviderCategoryFilterModel(); - virtual ~ProviderCategoryFilterModel() override; - - Q_INVOKABLE void sortByCategoryFirst(bool pEnabled); - Q_INVOKABLE void setCategorySelection(const QString& pCategory); - Q_INVOKABLE void updateCategorySelection(const QString& pCategory, bool pSelected); - Q_INVOKABLE void addAdditionalResultCategories(); - - Q_SIGNALS: - void fireCriteriaChanged(); - -}; - - -} /* namespace governikus */ diff --git a/src/qml/ProviderModel.cpp b/src/qml/ProviderModel.cpp deleted file mode 100644 index 607c2ed..0000000 --- a/src/qml/ProviderModel.cpp +++ /dev/null @@ -1,212 +0,0 @@ -/*! - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "ProviderModel.h" - -#include "Env.h" -#include "ProviderConfiguration.h" - - -using namespace governikus; - - -QString ProviderModel::createCostString(double pCostsPerMinute, double pCostsPerCall) -{ - if (pCostsPerMinute > 0.0) - { - return tr("%1/min").arg(createAmountString(pCostsPerMinute)); - } - if (pCostsPerCall > 0.0) - { - return tr("%1/call").arg(createAmountString(pCostsPerCall)); - } - return QString(); -} - - -QString ProviderModel::createAmountString(double pCents) -{ - return pCents > 100 ? tr("%1 EUR").arg(pCents / 100.0) : tr("%1 ct").arg(pCents); -} - - -void ProviderModel::updateConnections() -{ - for (const auto& connection : qAsConst(mConnections)) - { - disconnect(connection); - } - mConnections.clear(); - - const auto& providerConfigurationInfos = Env::getSingleton()->getProviderConfigurationInfos(); - mConnections.reserve(providerConfigurationInfos.size() * 2); - for (int i = 0; i < providerConfigurationInfos.size(); i++) - { - const auto& provider = providerConfigurationInfos.at(i); - const QModelIndex& modelIndex = createIndex(i, 0); - - mConnections += connect(provider.getIcon().data(), &UpdatableFile::fireUpdated, [ = ] { - Q_EMIT dataChanged(modelIndex, modelIndex, {ProviderRoles::ICON}); - }); - mConnections += connect(provider.getImage().data(), &UpdatableFile::fireUpdated, [ = ] { - Q_EMIT dataChanged(modelIndex, modelIndex, {ProviderRoles::IMAGE}); - }); - } -} - - -void ProviderModel::onProvidersChanged() -{ - beginResetModel(); - updateConnections(); - endResetModel(); -} - - -ProviderModel::ProviderModel(QObject* pParent) - : QAbstractListModel(pParent) -{ - updateConnections(); - connect(Env::getSingleton(), &ProviderConfiguration::fireUpdated, this, &ProviderModel::onProvidersChanged); -} - - -ProviderModel::~ProviderModel() -{ -} - - -int ProviderModel::rowCount(const QModelIndex&) const -{ - return Env::getSingleton()->getProviderConfigurationInfos().size(); -} - - -QVariant ProviderModel::data(const QModelIndex& pIndex, int pRole) const -{ - const auto& providerConfiguration = Env::getSingleton(); - - if (pIndex.isValid()) - { - auto provider = providerConfiguration->getProviderConfigurationInfos().at(pIndex.row()); - - if (pRole == Qt::DisplayRole) - { - auto longName = provider.getLongName(); - return longName.isEmpty() ? provider.getShortName().toString() : longName.toString(); - } - - if (pRole == CATEGORY) - { - return provider.getCategory(); - } - if (pRole == SHORTNAME) - { - return provider.getShortName().toString(); - } - if (pRole == LONGNAME) - { - return provider.getLongName().toString(); - } - if (pRole == SHORTDESCRIPTION) - { - return provider.getShortDescription().toString(); - } - if (pRole == LONGDESCRIPTION) - { - return provider.getLongDescription().toString(); - } - if (pRole == ADDRESS) - { - return provider.getAddress(); - } - if (pRole == ADDRESS_DOMAIN) - { - return provider.getAddressDomain(); - } - if (pRole == HOMEPAGE) - { - return provider.getHomepage(); - } - if (pRole == HOMEPAGE_BASE) - { - return provider.getHomepageBase(); - } - if (pRole == PHONE) - { - return provider.getPhone(); - } - if (pRole == PHONE_COST) - { - const auto& cost = providerConfiguration->getCallCost(provider); - return createCostString(cost); - } - if (pRole == EMAIL) - { - return provider.getEMail(); - } - if (pRole == POSTALADDRESS) - { - return provider.getPostalAddress(); - } - if (pRole == ICON) - { - return provider.getIcon()->lookupUrl(); - } - if (pRole == IMAGE) - { - return provider.getImage()->lookupUrl(); - } - if (pRole == SORT_ROLE) - { - auto value = provider.getLongName(); - - return provider.getCategory() + (value.isEmpty() ? provider.getShortName() : value); - } - } - - return QVariant(); -} - - -QHash ProviderModel::roleNames() const -{ - QHash roles = QAbstractListModel::roleNames(); - roles.insert(CATEGORY, "providerCategory"); - roles.insert(SHORTNAME, "providerShortName"); - roles.insert(LONGNAME, "providerLongName"); - roles.insert(SHORTDESCRIPTION, "providerShortDescription"); - roles.insert(LONGDESCRIPTION, "providerLongDescription"); - roles.insert(ADDRESS, "providerAddress"); - roles.insert(ADDRESS_DOMAIN, "providerAddressDomain"); - roles.insert(HOMEPAGE, "providerHomepage"); - roles.insert(HOMEPAGE_BASE, "providerHomepageBase"); - roles.insert(PHONE, "providerPhone"); - roles.insert(PHONE_COST, "providerPhoneCost"); - roles.insert(EMAIL, "providerEmail"); - roles.insert(POSTALADDRESS, "providerPostalAddress"); - roles.insert(ICON, "providerIcon"); - roles.insert(IMAGE, "providerImage"); - - return roles; -} - - -QString ProviderModel::createCostString(const CallCost& pCosts) -{ - if (pCosts.isNull()) - { - return QString(); - } - - QString msg; - if (pCosts.getFreeSeconds() > 0) - { - msg += tr("%1 seconds free, afterwards ").arg(pCosts.getFreeSeconds()); - } - msg += tr("landline costs %1; ").arg(createCostString(pCosts.getLandlineCentsPerMinute(), pCosts.getLandlineCentsPerCall())); - const auto mobileCosts = createCostString(pCosts.getMobileCentsPerMinute(), pCosts.getMobileCentsPerCall()); - msg += mobileCosts.isEmpty() ? tr("mobile costs may vary.") : tr("mobile costs %1").arg(mobileCosts); - return msg; -} diff --git a/src/qml/ProviderModel.h b/src/qml/ProviderModel.h deleted file mode 100644 index e4d864f..0000000 --- a/src/qml/ProviderModel.h +++ /dev/null @@ -1,71 +0,0 @@ -/*! - * \brief Model implementation for the providers. - * - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "CallCost.h" - -#include -#include - - -class test_ProviderModel; - - -namespace governikus -{ - -class ProviderModel - : public QAbstractListModel -{ - friend class ::test_ProviderModel; - - Q_OBJECT - - static QString createCostString(double pCostsPerMinute, double pCostsPerCall); - static QString createAmountString(double pCents); - - private: - QVector mConnections; - - void updateConnections(); - - private Q_SLOTS: - void onProvidersChanged(); - - public: - enum ProviderRoles - { - CATEGORY = Qt::UserRole + 1, - SHORTNAME, - LONGNAME, - SHORTDESCRIPTION, - LONGDESCRIPTION, - ADDRESS, - ADDRESS_DOMAIN, - HOMEPAGE, - HOMEPAGE_BASE, - PHONE, - PHONE_COST, - EMAIL, - POSTALADDRESS, - ICON, - IMAGE, - SORT_ROLE - }; - - ProviderModel(QObject* pParent = nullptr); - virtual ~ProviderModel() override; - - int rowCount(const QModelIndex&) const override; - QVariant data(const QModelIndex& pIndex, int pRole = Qt::DisplayRole) const override; - QHash roleNames() const override; - - static QString createCostString(const CallCost& pCosts); -}; - - -} /* namespace governikus */ diff --git a/src/qml/QmlExtension.h b/src/qml/QmlExtension.h deleted file mode 100644 index 0bba89a..0000000 --- a/src/qml/QmlExtension.h +++ /dev/null @@ -1,29 +0,0 @@ -/*! - * \brief Utility for sharing text. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include - -namespace governikus -{ - -class QmlExtension - : public QObject -{ - Q_OBJECT - - public: - Q_INVOKABLE void showSettings(const QString& pAction); - Q_INVOKABLE void shareText(const QString& pText, const QString& pChooserTitle); - Q_INVOKABLE void showFeedback(const QString& pMessage); - Q_INVOKABLE bool exportHistory(const QString& pPdfUrl) const; - Q_INVOKABLE void mailLog(const QString& pEmail, const QString& pSubject, const QString& pMsg); - Q_INVOKABLE void keepScreenOn(bool pActive); -}; - -} diff --git a/src/qml/QmlExtension_android.cpp b/src/qml/QmlExtension_android.cpp deleted file mode 100644 index 06d441f..0000000 --- a/src/qml/QmlExtension_android.cpp +++ /dev/null @@ -1,212 +0,0 @@ -/*! - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "QmlExtension.h" - -#include "LogHandler.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -Q_DECLARE_LOGGING_CATEGORY(qml) - - -using namespace governikus; - -void QmlExtension::showSettings(const QString& pAction) -{ - QAndroidJniEnvironment env; - - const QAndroidJniObject& jAction = QAndroidJniObject::fromString(pAction); - QAndroidJniObject intent("android/content/Intent", "(Ljava/lang/String;)V", jAction.object()); - const jint flag = QAndroidJniObject::getStaticField("android/content/Intent", "FLAG_ACTIVITY_NEW_TASK"); - intent.callObjectMethod("setFlags", "(I)V", flag); - - if (intent.isValid()) - { - qCCritical(qml) << "Call action:" << pAction; - QtAndroid::startActivity(intent, 0); - } - - if (env->ExceptionCheck()) - { - qCCritical(qml) << "Cannot call an action as activity:" << pAction; - env->ExceptionDescribe(); - env->ExceptionClear(); - } -} - - -void QmlExtension::shareText(const QString& pText, const QString& pChooserTitle) -{ - QAndroidJniEnvironment env; - const QAndroidJniObject javaActivity(QtAndroid::androidActivity()); - if (!javaActivity.isValid()) - { - qCCritical(qml) << "Cannot determine android activity."; - return; - } - - const QAndroidJniObject& jText = QAndroidJniObject::fromString(pText); - const QAndroidJniObject& jTitle = QAndroidJniObject::fromString(pChooserTitle); - QAndroidJniObject::callStaticMethod("com/governikus/ausweisapp2/ShareUtil", - "shareText", - "(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)V", - javaActivity.object(), - jText.object(), - jTitle.object()); - - if (env->ExceptionCheck()) - { - qCCritical(qml) << "Cannot call ShareUtil.shareText()"; - env->ExceptionDescribe(); - env->ExceptionClear(); - return; - } -} - - -void QmlExtension::showFeedback(const QString& pMessage) -{ - // Wait for toast activation synchronously so that the app can not be deactivated - // in the meantime and all used Java objects are still alive when accessed. - QtAndroid::runOnAndroidThreadSync([pMessage](){ - QAndroidJniEnvironment env; - - const QAndroidJniObject& jMessage = QAndroidJniObject::fromString(pMessage); - const QAndroidJniObject& toast = QAndroidJniObject::callStaticObjectMethod( - "android/widget/Toast", - "makeText", - "(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;", - QtAndroid::androidActivity().object(), - jMessage.object(), - jint(1)); - toast.callMethod("show"); - - if (env->ExceptionCheck()) - { - qCCritical(qml) << "Suppressing an unexpected exception."; - env->ExceptionDescribe(); - env->ExceptionClear(); - // The toast was probably not displayed (e.g. DeadObjectException). We halt on error - // since it is used to display information to the user as required by the TR. - Q_ASSERT(false); - } - }); -} - - -/* - * Calling - * QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation).last() - * does the same but we don't want to rely on Qt internals, so we do it on our own. - */ -QString getPublicLogFileName(QAndroidJniEnvironment& env, const QAndroidJniObject& javaActivity) -{ - const auto& jEmptyString = QAndroidJniObject::fromString(QLatin1String("")); - const auto& jExternalFilesDir = javaActivity.callObjectMethod("getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;", jEmptyString.object()); - if (env->ExceptionCheck()) - { - qCCritical(qml) << "Exception determining externalFilesDir"; - env->ExceptionDescribe(); - env->ExceptionClear(); - return QString(); - } - if (!jExternalFilesDir.isValid()) - { - qCCritical(qml) << "Cannot determine externalFilesDir"; - return QString(); - } - - const auto& jExternalFilesDirPath = jExternalFilesDir.callObjectMethod("getAbsolutePath", "()Ljava/lang/String;"); - if (env->ExceptionCheck()) - { - qCCritical(qml) << "Exception determining externalFilesDir absolute path"; - env->ExceptionDescribe(); - env->ExceptionClear(); - return QString(); - } - if (!jExternalFilesDirPath.isValid()) - { - qCCritical(qml) << "Cannot determine externalFilesDir absolute path"; - return QString(); - } - - const auto& nowAsISO = QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd'T'HH:mm:ss-t")); - return QStringLiteral("%1/AusweisApp2-%2.log").arg(jExternalFilesDirPath.toString(), nowAsISO); -} - - -void QmlExtension::mailLog(const QString& pEmail, const QString& pSubject, const QString& pMsg) -{ - QAndroidJniEnvironment env; - const QAndroidJniObject javaActivity(QtAndroid::androidActivity()); - if (!javaActivity.isValid()) - { - qCCritical(qml) << "Cannot determine android activity"; - return; - } - - const auto& jEmail = QAndroidJniObject::fromString(pEmail); - const auto& jSubject = QAndroidJniObject::fromString(pSubject); - const auto& jMsg = QAndroidJniObject::fromString(pMsg); - const auto& jChooserTitle = QAndroidJniObject::fromString(tr("Send application log per email...")); - const auto& publicLogFile = getPublicLogFileName(env, javaActivity); - const auto& jPublicLogFile = QAndroidJniObject::fromString(publicLogFile); - - qCDebug(qml) << "Copy log file to" << publicLogFile; - if (!LogHandler::getInstance().copy(publicLogFile)) - { - qCCritical(qml) << "Cannot copy log file to" << publicLogFile; - return; - } - - QAndroidJniObject::callStaticMethod("com/governikus/ausweisapp2/ShareUtil", - "shareLog", - "(Landroid/app/Activity;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", - javaActivity.object(), - jEmail.object(), - jSubject.object(), - jMsg.object(), - jPublicLogFile.object(), - jChooserTitle.object()); - if (env->ExceptionCheck()) - { - qCCritical(qml) << "Exception calling ShareUtil.shareLog()"; - env->ExceptionDescribe(); - env->ExceptionClear(); - } -} - - -bool QmlExtension::exportHistory(const QString&) const -{ - qCWarning(qml) << "NOT IMPLEMENTED"; - return false; -} - - -void QmlExtension::keepScreenOn(bool pActive) -{ - QtAndroid::runOnAndroidThread([pActive](){ - QtAndroid::androidActivity().callMethod("keepScreenOn", "(Z)V", pActive); - QAndroidJniEnvironment env; - if (env->ExceptionCheck()) - { - qCCritical(qml) << "Exception calling java native function."; - env->ExceptionDescribe(); - env->ExceptionClear(); - } - }); -} - - -#include "moc_QmlExtension.cpp" diff --git a/src/qml/QmlExtension_generic.cpp b/src/qml/QmlExtension_generic.cpp deleted file mode 100644 index c593f2a..0000000 --- a/src/qml/QmlExtension_generic.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/*! - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "QmlExtension.h" - -#ifndef Q_OS_WINRT -#include "PdfExporter.h" -#endif - -#include -#include - -Q_DECLARE_LOGGING_CATEGORY(qml) - -using namespace governikus; - - -void QmlExtension::showSettings(const QString&) -{ - qCWarning(qml) << "NOT IMPLEMENTED"; -} - - -void QmlExtension::shareText(const QString&, const QString&) -{ - qCWarning(qml) << "NOT IMPLEMENTED"; -} - - -void QmlExtension::showFeedback(const QString&) -{ - qCWarning(qml) << "NOT IMPLEMENTED"; -} - - -void QmlExtension::mailLog(const QString&, const QString&, const QString&) -{ - qCWarning(qml) << "NOT IMPLEMENTED"; -} - - -bool QmlExtension::exportHistory(const QString& pPdfUrl) const -{ -#ifdef Q_OS_WINRT - return false; - -#else - PdfExporter exporter(QUrl(pPdfUrl).toLocalFile()); - return exporter.exportHistory(); - -#endif -} - - -void QmlExtension::keepScreenOn(bool) -{ - qCWarning(qml) << "NOT IMPLEMENTED"; -} - - -#include "moc_QmlExtension.cpp" diff --git a/src/qml/QmlExtension_ios.mm b/src/qml/QmlExtension_ios.mm deleted file mode 100644 index 2db9b0c..0000000 --- a/src/qml/QmlExtension_ios.mm +++ /dev/null @@ -1,72 +0,0 @@ -/*! - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "QmlExtension.h" - -#include -#import - -Q_DECLARE_LOGGING_CATEGORY(qml) - -using namespace governikus; - - -void QmlExtension::showSettings(const QString&) -{ - qCWarning(qml) << "NOT IMPLEMENTED"; -} - - -void QmlExtension::shareText(const QString& pText, const QString& pChooserTitle) -{ - Q_UNUSED(pChooserTitle); - NSMutableArray* shareItemArray = [NSMutableArray new]; - [shareItemArray addObject : pText.toNSString()]; - UIViewController* rootController = [[UIApplication sharedApplication].keyWindow rootViewController]; - UIActivityViewController* activityController = [[UIActivityViewController alloc] initWithActivityItems:shareItemArray applicationActivities:nil]; - if ([activityController respondsToSelector : @selector(popoverPresentationController)]) - { - activityController.popoverPresentationController.sourceView = rootController.view; - } - [rootController presentViewController : activityController animated : YES completion : nil]; -} - - -void QmlExtension::mailLog(const QString&, const QString&, const QString&) -{ - qCWarning(qml) << "NOT IMPLEMENTED"; -} - - -void QmlExtension::showFeedback(const QString& pMessage) -{ - NSString* msg = pMessage.toNSString(); - - UIAlertController* alert = [UIAlertController - alertControllerWithTitle:msg - message:@"" - preferredStyle:UIAlertControllerStyleAlert]; - - UIViewController* rootController = [[UIApplication sharedApplication].keyWindow rootViewController]; - [rootController presentViewController:alert animated:YES completion:nil]; - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, static_cast(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - [alert dismissViewControllerAnimated:YES completion:nil]; - }); -} - - -bool QmlExtension::exportHistory(const QString&) const -{ - qCWarning(qml) << "NOT IMPLEMENTED"; - return false; -} - - -void QmlExtension::keepScreenOn(bool) -{ - qCWarning(qml) << "NOT IMPLEMENTED"; -} - - -#include "moc_QmlExtension.cpp" diff --git a/src/qml/RemoteServiceModel.cpp b/src/qml/RemoteServiceModel.cpp deleted file mode 100644 index e6e2cc1..0000000 --- a/src/qml/RemoteServiceModel.cpp +++ /dev/null @@ -1,339 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "RemoteServiceModel.h" - -#include "AppSettings.h" -#include "Env.h" -#include "EstablishPACEChannelParser.h" -#include "RemoteClientImpl.h" -#include "RemoteServiceSettings.h" - -using namespace governikus; - -RemoteServiceModel::RemoteServiceModel() - : mContext() - , mWifiInfo() - , mRunnable(false) - , mCanEnableNfc(false) - , mErrorMessage() - , mPsk() - , mAvailableRemoteDevices(this, false, true) - , mKnownDevices(this, true, false) - , mConnectedClientDeviceName() - , mConnectedServerDeviceNames() -{ - connect(&ReaderManager::getInstance(), &ReaderManager::firePluginAdded, this, &RemoteServiceModel::onEnvironmentChanged); - connect(&ReaderManager::getInstance(), &ReaderManager::fireStatusChanged, this, &RemoteServiceModel::onEnvironmentChanged); - connect(&ReaderManager::getInstance(), &ReaderManager::fireReaderAdded, this, &RemoteServiceModel::onEnvironmentChanged); - connect(&ReaderManager::getInstance(), &ReaderManager::fireReaderRemoved, this, &RemoteServiceModel::onEnvironmentChanged); - connect(&mWifiInfo, &WifiInfo::fireWifiEnabledChanged, this, &RemoteServiceModel::onEnvironmentChanged); - - const QSharedPointer& remoteClient = ReaderManager::getInstance().getRemoteClient(); - connect(remoteClient.data(), &RemoteClient::fireDetectionChanged, this, &RemoteServiceModel::fireDetectionChanged); - connect(remoteClient.data(), &RemoteClient::fireNewRemoteDispatcher, this, &RemoteServiceModel::onConnectedDevicesChanged); - connect(remoteClient.data(), &RemoteClient::fireDispatcherDestroyed, this, &RemoteServiceModel::onConnectedDevicesChanged); - onEnvironmentChanged(); -} - - -void RemoteServiceModel::onEnvironmentChanged() -{ - bool nfcPluginAvailable = false; - bool nfcPluginEnabled = false; - const auto& allPlugins = ReaderManager::getInstance().getPlugInInfos(); - for (const auto& pluginInfo : allPlugins) - { - if (pluginInfo.getPlugInType() != ReaderManagerPlugInType::NFC) - { - // At this time no bluetooth basic reader available so we can skip - continue; - } - - nfcPluginAvailable |= pluginInfo.isAvailable(); - nfcPluginEnabled |= pluginInfo.isEnabled(); - } - - bool readerAvailable = false; - const auto& allReader = ReaderManager::getInstance().getReaderInfos(); - for (const auto& readerInfo : allReader) - { - readerAvailable |= readerInfo.isBasicReader(); - } - - const bool wifiEnabled = mWifiInfo.isWifiEnabled(); - - const bool runnable = readerAvailable && wifiEnabled; - const bool canEnableNfc = nfcPluginAvailable && !nfcPluginEnabled; - const QString errorMessage = getErrorMessage(nfcPluginAvailable, nfcPluginEnabled, wifiEnabled); - if (mRunnable != runnable || mCanEnableNfc != canEnableNfc || mErrorMessage != errorMessage) - { - mRunnable = runnable; - mCanEnableNfc = canEnableNfc; - mErrorMessage = errorMessage; - - Q_EMIT fireEnvironmentChanged(); - } - - if (!runnable && isRunning()) - { - setRunning(false); - } -} - - -bool RemoteServiceModel::isRunning() const -{ - return mContext ? mContext->isRunning() : false; -} - - -QString RemoteServiceModel::getCurrentState() const -{ - return mContext ? mContext->getCurrentState() : QString(); -} - - -void RemoteServiceModel::setRunning(bool pState) -{ - if (isRunning() == pState) - { - return; - } - - if (isRunning() && mContext) - { - Q_EMIT mContext->fireCancelWorkflow(); - } - else - { - Q_EMIT fireStartWorkflow(); - } - - Q_EMIT fireIsRunningChanged(); -} - - -QString RemoteServiceModel::getReaderPlugInType() const -{ - if (mContext) - { - return getEnumName(mContext->getReaderPlugInTypes().at(0)); - } - - return QString(); -} - - -void RemoteServiceModel::setReaderPlugInType(const QString& pReaderPlugInType) -{ - if (mContext) - { - mContext->setReaderPlugInTypes({Enum::fromString(pReaderPlugInType, ReaderManagerPlugInType::UNKNOWN)}); - } -} - - -RemoteDeviceModel* RemoteServiceModel::getAvailableRemoteDevices() -{ - return &mAvailableRemoteDevices; -} - - -RemoteDeviceModel* RemoteServiceModel::getKnownDevices() -{ - return &mKnownDevices; -} - - -void RemoteServiceModel::setDetectRemoteDevices(bool pNewStatus) -{ - if (pNewStatus) - { - mAvailableRemoteDevices.onWidgetShown(); - mKnownDevices.onWidgetShown(); - } - else - { - mAvailableRemoteDevices.onWidgetHidden(); - mKnownDevices.onWidgetHidden(); - } -} - - -bool RemoteServiceModel::detectRemoteDevices() -{ - const QSharedPointer& remoteClient = ReaderManager::getInstance().getRemoteClient(); - return remoteClient->isDetecting(); -} - - -void RemoteServiceModel::connectToServer(const QString& pDeviceId, const QString& pServerPsk) -{ - if (!pServerPsk.isEmpty()) - { - const QSharedPointer& remoteClient = Env::getSingleton()->getRemoteClient(); - connect(remoteClient.data(), &RemoteClient::fireEstablishConnectionDone, this, &RemoteServiceModel::onEstablishConnectionDone); - - remoteClient->establishConnection(mAvailableRemoteDevices.getRemoteDeviceListEntry(pDeviceId), pServerPsk); - } -} - - -void RemoteServiceModel::onEstablishConnectionDone(const QSharedPointer& pEntry, const GlobalStatus& pStatus) -{ - Q_UNUSED(pEntry); - const QSharedPointer& remoteClient = Env::getSingleton()->getRemoteClient(); - disconnect(remoteClient.data(), &RemoteClient::fireEstablishConnectionDone, this, &RemoteServiceModel::onEstablishConnectionDone); - if (pStatus.isError()) - { - Q_EMIT firePairingFailed(); - } -} - - -void RemoteServiceModel::onClientConnectedChanged(bool pConnected) -{ - const RemoteServiceSettings& settings = Env::getSingleton()->getRemoteServiceSettings(); - const QString peerName = settings.getRemoteInfo(getCurrentFingerprint()).getName(); - mConnectedClientDeviceName = peerName; - Q_EMIT fireConnectedClientDeviceNameChanged(); - Q_EMIT fireConnectedChanged(pConnected); -} - - -void RemoteServiceModel::resetContext(const QSharedPointer& pContext) -{ - mPsk.clear(); - - mContext = pContext; - if (mContext) - { - connect(mContext.data(), &WorkflowContext::fireStateChanged, this, &RemoteServiceModel::fireCurrentStateChanged); - connect(mContext.data(), &WorkflowContext::fireStateChanged, this, &RemoteServiceModel::fireIsRunningChanged); - connect(mContext->getRemoteServer().data(), &RemoteServer::firePskChanged, this, [this](const QByteArray& pPsk){ - mPsk = pPsk; - }); - connect(mContext->getRemoteServer().data(), &RemoteServer::firePskChanged, this, &RemoteServiceModel::firePskChanged); - connect(mContext->getRemoteServer().data(), &RemoteServer::fireConnectedChanged, this, &RemoteServiceModel::onClientConnectedChanged); - } - - Q_EMIT fireConnectedChanged(isConnected()); -} - - -void RemoteServiceModel::setPairing(bool pEnabled) -{ - if (mContext) - { - mContext->getRemoteServer()->setPairing(pEnabled); - } -} - - -QString RemoteServiceModel::getCurrentFingerprint() const -{ - if (mContext && mContext->getRemoteServer()->isConnected()) - { - return RemoteServiceSettings::generateFingerprint(mContext->getRemoteServer()->getCurrentCertificate()); - } - - return QString(); -} - - -bool RemoteServiceModel::isConnected() const -{ - if (mContext) - { - return mContext->getRemoteServer()->isConnected(); - } - - return false; -} - - -bool RemoteServiceModel::pinPadModeOn() -{ - return Env::getSingleton()->getRemoteServiceSettings().getPinPadMode(); -} - - -QString RemoteServiceModel::getPacePasswordId() const -{ - if (mContext.isNull()) - { - return QString(); - } - - const QSharedPointer establishPaceChannelMessage = mContext->getEstablishPaceChannelMessage(); - if (establishPaceChannelMessage.isNull()) - { - return QString(); - } - - const EstablishPACEChannelParser parser = EstablishPACEChannelParser::fromCcid(establishPaceChannelMessage->getInputData()); - switch (parser.getPasswordId()) - { - case PACE_PASSWORD_ID::PACE_CAN: - return QStringLiteral("CAN"); - - case PACE_PASSWORD_ID::PACE_PIN: - return QStringLiteral("PIN"); - - case PACE_PASSWORD_ID::PACE_PUK: - return QStringLiteral("PUK"); - - default: - return QString(); - } -} - - -QString RemoteServiceModel::getErrorMessage(bool pNfcPluginAvailable, bool pNfcPluginEnabled, bool pWifiEnabled) const -{ - if (!pNfcPluginAvailable) - { - return tr("NFC is not available on your device."); - } - if (!pNfcPluginEnabled) - { - return tr("Please enable NFC to use the remote service."); - } - if (!pWifiEnabled) - { - return tr("Please connect your WiFi to use the remote service."); - } - - return QString(); -} - - -void RemoteServiceModel::forgetDevice(const QString& pId) -{ - mKnownDevices.forgetDevice(pId); -} - - -void RemoteServiceModel::cancelPasswordRequest() -{ - if (mContext) - { - Q_EMIT mContext->fireCancelPasswordRequest(); - } -} - - -void RemoteServiceModel::onConnectedDevicesChanged() -{ - const QSharedPointer& remoteClient = Env::getSingleton()->getRemoteClient(); - const auto deviceInfos = remoteClient->getConnectedDeviceInfos(); - QStringList deviceNames; - for (const auto& info : deviceInfos) - { - deviceNames.append(QLatin1Char('"') + info.getName() + QLatin1Char('"')); - } - mConnectedServerDeviceNames = deviceNames.join(QLatin1String(", ")); - Q_EMIT fireConnectedServerDeviceNamesChanged(); -} diff --git a/src/qml/RemoteServiceModel.h b/src/qml/RemoteServiceModel.h deleted file mode 100644 index e8a5e8b..0000000 --- a/src/qml/RemoteServiceModel.h +++ /dev/null @@ -1,101 +0,0 @@ -/*! - * \brief Model implementation for the remote service component - * - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "context/RemoteServiceContext.h" -#include "ReaderManager.h" -#include "RemoteDeviceModel.h" -#include "WifiInfo.h" - -#include - -namespace governikus -{ - - -class RemoteServiceModel - : public QObject -{ - Q_OBJECT - - Q_PROPERTY(QString currentState READ getCurrentState NOTIFY fireCurrentStateChanged) - Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY fireIsRunningChanged) - Q_PROPERTY(bool runnable MEMBER mRunnable NOTIFY fireEnvironmentChanged) - Q_PROPERTY(bool canEnableNfc MEMBER mCanEnableNfc NOTIFY fireEnvironmentChanged) - Q_PROPERTY(QString errorMessage MEMBER mErrorMessage NOTIFY fireEnvironmentChanged) - Q_PROPERTY(QByteArray psk MEMBER mPsk NOTIFY firePskChanged) - Q_PROPERTY(QString currentFingerprint READ getCurrentFingerprint NOTIFY fireConnectedChanged) - Q_PROPERTY(bool connected READ isConnected NOTIFY fireConnectedChanged) - Q_PROPERTY(QString connectedClientDeviceName MEMBER mConnectedClientDeviceName NOTIFY fireConnectedClientDeviceNameChanged) - Q_PROPERTY(QString connectedServerDeviceNames MEMBER mConnectedServerDeviceNames NOTIFY fireConnectedServerDeviceNamesChanged) - Q_PROPERTY(QString readerPlugInType READ getReaderPlugInType WRITE setReaderPlugInType NOTIFY fireReaderPlugInTypeChanged) - Q_PROPERTY(RemoteDeviceModel * availableRemoteDevices READ getAvailableRemoteDevices CONSTANT) - Q_PROPERTY(RemoteDeviceModel * knownDevices READ getKnownDevices CONSTANT) - Q_PROPERTY(bool detectRemoteDevices READ detectRemoteDevices WRITE setDetectRemoteDevices NOTIFY fireDetectionChanged) - - private: - QSharedPointer mContext; - WifiInfo mWifiInfo; - bool mRunnable; - bool mCanEnableNfc; - QString mErrorMessage; - QByteArray mPsk; - RemoteDeviceModel mAvailableRemoteDevices; - RemoteDeviceModel mKnownDevices; - QString mConnectedClientDeviceName; - QString mConnectedServerDeviceNames; - - void onEnvironmentChanged(); - QString getErrorMessage(bool pNfcPluginAvailable, bool pNfcPluginEnabled, bool pWifiEnabled) const; - - private Q_SLOTS: - void onEstablishConnectionDone(const QSharedPointer& pEntry, const GlobalStatus& pStatus); - void onClientConnectedChanged(bool pConnected); - void onConnectedDevicesChanged(); - - public: - RemoteServiceModel(); - - QString getCurrentState() const; - bool isRunning() const; - void setRunning(bool pState); - - QString getReaderPlugInType() const; - void setReaderPlugInType(const QString& pReaderPlugInType); - - RemoteDeviceModel* getAvailableRemoteDevices(); - RemoteDeviceModel* getKnownDevices(); - void setDetectRemoteDevices(bool pNewStatus); - bool detectRemoteDevices(); - Q_INVOKABLE void connectToServer(const QString& pDeviceId, const QString& pServerPsk); - - void resetContext(const QSharedPointer& pContext = QSharedPointer()); - Q_INVOKABLE void setPairing(bool pEnabled = true); - QString getCurrentFingerprint() const; - bool isConnected() const; - Q_INVOKABLE bool pinPadModeOn(); - Q_INVOKABLE QString getPacePasswordId() const; - Q_INVOKABLE void forgetDevice(const QString& pId); - Q_INVOKABLE void cancelPasswordRequest(); - - Q_SIGNALS: - void fireStartWorkflow(); - void fireCurrentStateChanged(const QString& pState); - void fireIsRunningChanged(); - void fireEnvironmentChanged(); - void firePskChanged(const QByteArray& pPsk); - void fireConnectedChanged(bool pConnected); - void fireReaderPlugInTypeChanged(); - void fireServerPskChanged(); - void fireDetectionChanged(); - void firePairingFailed(); - void fireConnectedClientDeviceNameChanged(); - void fireConnectedServerDeviceNamesChanged(); -}; - - -} /* namespace governikus */ diff --git a/src/qml/SelfAuthenticationModel.cpp b/src/qml/SelfAuthenticationModel.cpp deleted file mode 100644 index c58020a..0000000 --- a/src/qml/SelfAuthenticationModel.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/*! - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "SelfAuthenticationModel.h" - -#include "context/SelfAuthContext.h" -#include "LanguageLoader.h" - -using namespace governikus; - - -void SelfAuthenticationModel::onSelfAuthenticationDataChanged() -{ - beginResetModel(); - mSelfData.clear(); - - if (mContext && mContext->getSelfAuthenticationData().isValid()) - { - const auto& selfdata = mContext->getSelfAuthenticationData().getOrderedSelfData(); - for (const auto& entry : selfdata) - { - if (entry.first.isEmpty()) - { - Q_ASSERT(!mSelfData.isEmpty()); - const auto& previous = mSelfData.takeLast(); - mSelfData << qMakePair(previous.first, previous.second + QStringLiteral("
") + entry.second); - } - else - { - mSelfData << entry; - } - } - } - - endResetModel(); -} - - -SelfAuthenticationModel::SelfAuthenticationModel(QObject* pParent) - : QAbstractListModel(pParent) - , mContext() - , mSelfData() -{ - onSelfAuthenticationDataChanged(); -} - - -void SelfAuthenticationModel::resetContext(const QSharedPointer& pContext) -{ - mContext = pContext; - if (mContext) - { - connect(mContext.data(), &SelfAuthContext::fireSelfAuthenticationDataChanged, this, &SelfAuthenticationModel::onSelfAuthenticationDataChanged); - } - onSelfAuthenticationDataChanged(); -} - - -void SelfAuthenticationModel::startWorkflow() -{ - Q_EMIT fireStartWorkflow(); -} - - -void SelfAuthenticationModel::cancelWorkflow() -{ - if (mContext) - { - Q_EMIT mContext->fireCancelWorkflow(); - } -} - - -bool SelfAuthenticationModel::isBasicReader() -{ - if (mContext) - { - return mContext->getCardConnection()->getReaderInfo().isBasicReader(); - } - - return true; -} - - -int SelfAuthenticationModel::rowCount(const QModelIndex&) const -{ - return mSelfData.size(); -} - - -QVariant SelfAuthenticationModel::data(const QModelIndex& pIndex, int pRole) const -{ - if (pIndex.isValid() && pIndex.row() < rowCount()) - { - auto pair = mSelfData.at(pIndex.row()); - if (pRole == Qt::DisplayRole || pRole == NAME) - { - return pair.first; - } - if (pRole == VALUE) - { - return pair.second; - } - } - return QVariant(); -} - - -QHash SelfAuthenticationModel::roleNames() const -{ - QHash roles = QAbstractListModel::roleNames(); - roles.insert(NAME, "name"); - roles.insert(VALUE, "value"); - return roles; -} - - -bool SelfAuthenticationModel::event(QEvent* pEvent) -{ - if (pEvent->type() == QEvent::LanguageChange) - { - onSelfAuthenticationDataChanged(); - } - return QAbstractListModel::event(pEvent); -} diff --git a/src/qml/SelfAuthenticationModel.h b/src/qml/SelfAuthenticationModel.h deleted file mode 100644 index 5e26c62..0000000 --- a/src/qml/SelfAuthenticationModel.h +++ /dev/null @@ -1,58 +0,0 @@ -/*! - * \brief Model implementation for the self authentication workflow. - * - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "SelfAuthenticationData.h" - -#include -#include -#include -#include - -namespace governikus -{ - -class SelfAuthContext; - -class SelfAuthenticationModel - : public QAbstractListModel -{ - Q_OBJECT - - QSharedPointer mContext; - SelfAuthenticationData::OrderedSelfData mSelfData; - - enum DataRoles - { - NAME = Qt::UserRole + 1, - VALUE - }; - - private Q_SLOTS: - void onSelfAuthenticationDataChanged(); - - public: - SelfAuthenticationModel(QObject* pParent = nullptr); - void resetContext(const QSharedPointer& pContext = QSharedPointer()); - - Q_INVOKABLE void startWorkflow(); - Q_INVOKABLE void cancelWorkflow(); - Q_INVOKABLE bool isBasicReader(); - - int rowCount(const QModelIndex& = QModelIndex()) const override; - QVariant data(const QModelIndex& pIndex, int pRole = Qt::DisplayRole) const override; - QHash roleNames() const override; - - virtual bool event(QEvent* pEvent) override; - - Q_SIGNALS: - void fireStartWorkflow(); - -}; - - -} /* namespace governikus */ diff --git a/src/qml/SettingsModel.cpp b/src/qml/SettingsModel.cpp deleted file mode 100644 index 5945108..0000000 --- a/src/qml/SettingsModel.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/*! - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "SettingsModel.h" - -#include "AppSettings.h" -#include "Env.h" -#include "HistorySettings.h" -#include "LanguageLoader.h" - -using namespace governikus; - - -SettingsModel::SettingsModel() -{ - const HistorySettings& settings = Env::getSingleton()->getHistorySettings(); - connect(&settings, &HistorySettings::fireEnabledChanged, this, &SettingsModel::fireHistoryEnabledChanged); -} - - -QString SettingsModel::getEmptyString() -{ - return QString(); -} - - -QString SettingsModel::getLanguage() const -{ - return LanguageLoader::getInstance().getUsedLocale().bcp47Name(); -} - - -void SettingsModel::setLanguage(const QString& pLanguage) -{ - GeneralSettings& settings = Env::getSingleton()->getGeneralSettings(); - settings.setLanguage(QLocale(pLanguage).language()); - settings.save(); - - Q_EMIT fireLanguageChanged(); -} - - -bool SettingsModel::isDeveloperMode() const -{ - return Env::getSingleton()->getGeneralSettings().isDeveloperMode(); -} - - -void SettingsModel::setDeveloperMode(bool pEnable) -{ - if (isDeveloperMode() == pEnable) - { - return; - } - - Env::getSingleton()->getGeneralSettings().setDeveloperMode(pEnable); - Env::getSingleton()->getGeneralSettings().save(); - Q_EMIT fireDeveloperModeChanged(); -} - - -bool SettingsModel::useSelfauthenticationTestUri() const -{ - return AppSettings::getInstance().getGeneralSettings().useSelfAuthTestUri(); -} - - -void SettingsModel::setUseSelfauthenticationTestUri(bool pUse) -{ - if (useSelfauthenticationTestUri() == pUse) - { - return; - } - - Env::getSingleton()->getGeneralSettings().setUseSelfauthenticationTestUri(pUse); - Env::getSingleton()->getGeneralSettings().save(); - Q_EMIT fireUseSelfauthenticationTestUriChanged(); -} - - -QString SettingsModel::getServerName() const -{ - return Env::getSingleton()->getRemoteServiceSettings().getServerName(); -} - - -bool SettingsModel::isValidServerName(const QString& name) const -{ - return !name.isEmpty(); -} - - -void SettingsModel::setServerName(const QString& name) -{ - RemoteServiceSettings& settings = Env::getSingleton()->getRemoteServiceSettings(); - settings.setServerName(name); - settings.save(); -} - - -void SettingsModel::removeTrustedCertificate(const QString& pFingerprint) -{ - Env::getSingleton()->getRemoteServiceSettings().removeTrustedCertificate(pFingerprint); -} - - -bool SettingsModel::getPinPadMode() const -{ - return Env::getSingleton()->getRemoteServiceSettings().getPinPadMode(); -} - - -void SettingsModel::setPinPadMode(bool pPinPadMode) -{ - RemoteServiceSettings& settings = Env::getSingleton()->getRemoteServiceSettings(); - settings.setPinPadMode(pPinPadMode); - settings.save(); -} - - -bool SettingsModel::isHistoryEnabled() const -{ - const HistorySettings& settings = Env::getSingleton()->getHistorySettings(); - return settings.isEnabled(); -} - - -void SettingsModel::setHistoryEnabled(bool pEnabled) -{ - HistorySettings& settings = Env::getSingleton()->getHistorySettings(); - settings.setEnabled(pEnabled); - settings.save(); -} - - -int SettingsModel::removeHistory(const QString& pPeriodToRemove) -{ - HistorySettings& settings = Env::getSingleton()->getHistorySettings(); - int removedItemCount = settings.deleteSettings(Enum::fromString(pPeriodToRemove, TimePeriod::UNKNOWN)); - settings.save(); - return removedItemCount; -} diff --git a/src/qml/SettingsModel.h b/src/qml/SettingsModel.h deleted file mode 100644 index 50b48d9..0000000 --- a/src/qml/SettingsModel.h +++ /dev/null @@ -1,61 +0,0 @@ -/*! - * \brief Model implementation for the settings. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include - -namespace governikus -{ - -class SettingsModel - : public QObject -{ - Q_OBJECT - Q_PROPERTY(QString translationTrigger READ getEmptyString NOTIFY fireLanguageChanged) - Q_PROPERTY(QString language READ getLanguage WRITE setLanguage NOTIFY fireLanguageChanged) - Q_PROPERTY(bool developerMode READ isDeveloperMode WRITE setDeveloperMode NOTIFY fireDeveloperModeChanged) - Q_PROPERTY(bool useSelfauthenticationTestUri READ useSelfauthenticationTestUri WRITE setUseSelfauthenticationTestUri NOTIFY fireUseSelfauthenticationTestUriChanged) - Q_PROPERTY(bool pinPadMode READ getPinPadMode WRITE setPinPadMode NOTIFY firePinPadModeChanged) - Q_PROPERTY(QString serverName READ getServerName WRITE setServerName NOTIFY fireDeviceNameChanged) - Q_PROPERTY(bool historyEnabled READ isHistoryEnabled WRITE setHistoryEnabled NOTIFY fireHistoryEnabledChanged) - - public: - SettingsModel(); - - QString getEmptyString(); - QString getLanguage() const; - void setLanguage(const QString& pLanguage); - - bool isDeveloperMode() const; - void setDeveloperMode(bool pEnabled); - - bool useSelfauthenticationTestUri() const; - void setUseSelfauthenticationTestUri(bool pUse); - - QString getServerName() const; - Q_INVOKABLE bool isValidServerName(const QString& name) const; - void setServerName(const QString& name); - - Q_INVOKABLE void removeTrustedCertificate(const QString& pFingerprint); - Q_INVOKABLE int removeHistory(const QString& pPeriodToRemove); - - bool getPinPadMode() const; - void setPinPadMode(bool pPinPadMode); - - bool isHistoryEnabled() const; - void setHistoryEnabled(bool pEnabled); - - Q_SIGNALS: - void fireLanguageChanged(); - void fireDeveloperModeChanged(); - void fireUseSelfauthenticationTestUriChanged(); - void fireDeviceNameChanged(); - void firePinPadModeChanged(); - void fireHistoryEnabledChanged(); -}; - -} /* namespace governikus */ diff --git a/src/qml/ShareUtil.java b/src/qml/ShareUtil.java deleted file mode 100644 index 6c70b33..0000000 --- a/src/qml/ShareUtil.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -package com.governikus.ausweisapp2; - -import java.io.File; - -import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.util.Log; - - -public final class ShareUtil -{ - private static final String LOG_TAG = AusweisApp2Service.LOG_TAG; - - private ShareUtil() - { - } - - - public static void shareText(Context ctx, final String text, final String chooserTitle) - { - Intent shareData = new Intent(); - shareData.setType("text/plain"); - shareData.setAction(Intent.ACTION_SEND); - shareData.putExtra(Intent.EXTRA_TEXT, text); - ctx.startActivity(Intent.createChooser(shareData, chooserTitle)); - } - - - public static void shareLog(Activity activity, final String email, final String subject, final String msg, final String logFilePath, final String chooserTitle) - { - try - { - Intent shareData = new Intent(); - shareData.setType("message/rfc822"); - shareData.setAction(Intent.ACTION_SEND); - shareData.putExtra(Intent.EXTRA_EMAIL, new String[] {email}); - shareData.putExtra(Intent.EXTRA_SUBJECT, subject); - shareData.putExtra(Intent.EXTRA_TEXT, msg); - shareData.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(logFilePath))); - activity.startActivity(Intent.createChooser(shareData, chooserTitle)); - } - catch (Exception e) - { - Log.e(LOG_TAG, "Error sharing log file", e); - } - } - - -} diff --git a/src/qml/StatusBarUtil.h b/src/qml/StatusBarUtil.h deleted file mode 100644 index 4971d90..0000000 --- a/src/qml/StatusBarUtil.h +++ /dev/null @@ -1,27 +0,0 @@ -/*! - * \brief Utility for changing the color of the status bar. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include - -namespace governikus -{ - -class StatusBarUtil - : public QObject -{ - Q_OBJECT - - private: - bool catchJavaExceptions() const; - - public: - Q_INVOKABLE void setStatusBarColor(const QString& pColor); -}; - -} diff --git a/src/qml/UIPlugInQml.cpp b/src/qml/UIPlugInQml.cpp deleted file mode 100644 index 87f3783..0000000 --- a/src/qml/UIPlugInQml.cpp +++ /dev/null @@ -1,287 +0,0 @@ -/*! - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "UIPlugInQml.h" - -#include "AppSettings.h" -#include "context/AuthContext.h" -#include "context/ChangePinContext.h" -#include "context/SelfAuthContext.h" -#include "DpiCalculator.h" -#include "Env.h" -#include "FileDestination.h" -#include "Service.h" - -#ifdef Q_OS_ANDROID -#include -#include -#endif - -#include -#include -#include -#include -#include -#include -#include - -#if defined(Q_OS_ANDROID) -#include -#endif - -Q_DECLARE_LOGGING_CATEGORY(qml) - -using namespace governikus; - -UIPlugInQml::UIPlugInQml() - : mEngine() - , mDpi(DpiCalculator::getDpi()) - , mProviderModel() - , mHistoryModel(&AppSettings::getInstance().getHistorySettings()) - , mChangePinModel() - , mAuthModel() - , mVersionInformationModel() - , mQmlExtension() - , mSelfAuthenticationModel() - , mSettingsModel() - , mCertificateDescriptionModel() - , mChatModel() - , mNumberModel() - , mApplicationModel() - , mExplicitPlatformStyle(getPlatformSelectors()) - , mStatusBarUtil() - , mConnectivityManager() - , mRemoteServiceModel() -{ -#if defined(Q_OS_ANDROID) - QGuiApplication::setFont(QFont("Roboto")); -#endif - - connect(&mChangePinModel, &ChangePinModel::fireStartWorkflow, this, &UIPlugIn::fireChangePinRequest); - connect(&mSelfAuthenticationModel, &SelfAuthenticationModel::fireStartWorkflow, this, &UIPlugIn::fireSelfAuthenticationRequested); - connect(&mRemoteServiceModel, &RemoteServiceModel::fireStartWorkflow, this, &UIPlugIn::fireRemoteServiceRequested); - connect(this, &UIPlugIn::fireShowUserInformation, this, &UIPlugInQml::onShowUserInformation); - init(); -} - - -UIPlugInQml::~UIPlugInQml() -{ -} - - -void UIPlugInQml::init() -{ -#if !defined(QT_NO_DEBUG) && !defined(Q_OS_IOS) && !defined(Q_OS_ANDROID) && !defined(Q_OS_WINRT) - qputenv("QML_DISABLE_DISK_CACHE", "true"); -#endif - - mEngine.reset(new QQmlApplicationEngine()); - - mEngine->rootContext()->setContextProperty(QStringLiteral("plugin"), this); - QQmlFileSelector::get(mEngine.data())->setExtraSelectors(mExplicitPlatformStyle.split(QLatin1Char(','))); - - mEngine->rootContext()->setContextProperty(QStringLiteral("screenDpi"), mDpi); - mEngine->rootContext()->setContextProperty(QStringLiteral("providerModel"), &mProviderModel); - mEngine->rootContext()->setContextProperty(QStringLiteral("historyModel"), &mHistoryModel); - mEngine->rootContext()->setContextProperty(QStringLiteral("changePinModel"), &mChangePinModel); - mEngine->rootContext()->setContextProperty(QStringLiteral("authModel"), &mAuthModel); - mEngine->rootContext()->setContextProperty(QStringLiteral("versionInformationModel"), &mVersionInformationModel); - mEngine->rootContext()->setContextProperty(QStringLiteral("qmlExtension"), &mQmlExtension); - mEngine->rootContext()->setContextProperty(QStringLiteral("selfAuthenticationModel"), &mSelfAuthenticationModel); - mEngine->rootContext()->setContextProperty(QStringLiteral("settingsModel"), &mSettingsModel); - mEngine->rootContext()->setContextProperty(QStringLiteral("certificateDescriptionModel"), &mCertificateDescriptionModel); - mEngine->rootContext()->setContextProperty(QStringLiteral("chatModel"), &mChatModel); - mEngine->rootContext()->setContextProperty(QStringLiteral("numberModel"), &mNumberModel); - mEngine->rootContext()->setContextProperty(QStringLiteral("applicationModel"), &mApplicationModel); - mEngine->rootContext()->setContextProperty(QStringLiteral("statusBarUtil"), &mStatusBarUtil); - mEngine->rootContext()->setContextProperty(QStringLiteral("connectivityManager"), &mConnectivityManager); - mEngine->rootContext()->setContextProperty(QStringLiteral("remoteServiceModel"), &mRemoteServiceModel); - - mEngine->addImportPath(getPath(QStringLiteral("qml/")).toString()); - mEngine->load(getPath(QStringLiteral("qml/main.qml"))); - - Env::getSingleton()->updateConfigurations(); -} - - -QString UIPlugInQml::getPlatformSelectors() const -{ -#ifndef Q_NO_DEBUG - const char* overrideSelector = "OVERRIDE_PLATFORM_SELECTOR"; - if (!qEnvironmentVariableIsEmpty(overrideSelector)) - { - const auto& platform = QString::fromLocal8Bit(qgetenv(overrideSelector)); - qCDebug(qml) << "Override platform selector:" << platform; - return platform; - } -#endif - -#if defined(Q_OS_ANDROID) - const jboolean result = QtAndroid::androidActivity().callMethod("isTablet", "()Z"); - const bool isTablet = result != JNI_FALSE; -#else - // A device with a screen diagnonal above 6 inches is considered a tablet. - static const double MAX_SMARTPHONE_DIAGONAL_IN = 6.0; - static const double MAX_SMARTPHONE_DIAGONAL_MM = 25.4 * MAX_SMARTPHONE_DIAGONAL_IN; - - const QList screens = QGuiApplication::screens(); - - Q_ASSERT(!screens.isEmpty()); - - QScreen* const mainScreen = screens.first(); - Q_ASSERT(mainScreen != nullptr); - - const QSizeF size = mainScreen->physicalSize(); - const double width = size.width(); - const double height = size.height(); - const double diagonal = sqrt(width * width + height * height); - const bool isTablet = diagonal > MAX_SMARTPHONE_DIAGONAL_MM; -#endif - -#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) - const QString platform = QGuiApplication::platformName(); -#else - const QString platform = QStringLiteral("android"); -#endif - - return isTablet ? QStringLiteral("tablet,") + platform : platform; -} - - -void UIPlugInQml::onWorkflowStarted(QSharedPointer pContext) -{ -#if defined(Q_OS_ANDROID) - mQmlExtension.keepScreenOn(true); -#endif - - mApplicationModel.resetContext(pContext); - mNumberModel.resetContext(pContext); - - if (auto changePinContext = pContext.objectCast()) - { - mChangePinModel.resetContext(changePinContext); - } - - if (auto authContext = pContext.objectCast()) - { - mConnectivityManager.startWatching(); - mAuthModel.resetContext(authContext); - mCertificateDescriptionModel.resetContext(authContext); - mChatModel.resetContext(authContext); - } - - if (auto authContext = pContext.objectCast()) - { - mSelfAuthenticationModel.resetContext(authContext); - } - - if (auto remoteServiceContext = pContext.objectCast()) - { - mRemoteServiceModel.resetContext(remoteServiceContext); - } -} - - -void UIPlugInQml::onWorkflowFinished(QSharedPointer pContext) -{ -#if defined(Q_OS_ANDROID) - mQmlExtension.keepScreenOn(false); -#endif - - mApplicationModel.resetContext(); - mNumberModel.resetContext(); - - if (pContext.objectCast()) - { - mChangePinModel.resetContext(); - } - - if (pContext.objectCast()) - { - mConnectivityManager.stopWatching(); - mAuthModel.resetContext(); - mCertificateDescriptionModel.resetContext(); - mChatModel.resetContext(); - } - - if (pContext.objectCast()) - { - mSelfAuthenticationModel.resetContext(); - } - - if (pContext.objectCast()) - { - mRemoteServiceModel.resetContext(); - } -} - - -void UIPlugInQml::onShowUserInformation(const QString& pMessage) -{ - mQmlExtension.showFeedback(pMessage); -} - - -void UIPlugInQml::doShutdown() -{ - -} - - -QUrl UIPlugInQml::getPath(const QString& pRelativePath) -{ -#if !defined(QT_NO_DEBUG) && !defined(Q_OS_IOS) && !defined(Q_OS_ANDROID) && !defined(Q_OS_WINRT) - const QString ressourceFolderPath(QStringLiteral(RES_DIR) + QLatin1Char('/') + pRelativePath); - if (QFile::exists(ressourceFolderPath)) - { - return QUrl::fromLocalFile(ressourceFolderPath); - } - -#endif - -#if defined(Q_OS_IOS) || defined(Q_OS_ANDROID) - return QStringLiteral("qrc:///") + pRelativePath; - -#else - const QString path = FileDestination::getPath(pRelativePath); - return QUrl::fromLocalFile(path); - -#endif -} - - -void UIPlugInQml::doRefresh() -{ - qCDebug(qml) << "Reload qml files"; - QMetaObject::invokeMethod(this, "init", Qt::QueuedConnection); -} - - -QString UIPlugInQml::getPlatformStyle() const -{ - return mExplicitPlatformStyle; -} - - -void UIPlugInQml::applyPlatformStyle(const QString& pPlatformStyle) -{ - if (mExplicitPlatformStyle != pPlatformStyle) - { - mExplicitPlatformStyle = pPlatformStyle; - doRefresh(); - } -} - - -bool UIPlugInQml::isDeveloperBuild() const -{ -#ifndef QT_NO_DEBUG - return true; - -#else - return false; - -#endif -} diff --git a/src/qml/UIPlugInQml.h b/src/qml/UIPlugInQml.h deleted file mode 100644 index 88e7c08..0000000 --- a/src/qml/UIPlugInQml.h +++ /dev/null @@ -1,83 +0,0 @@ -/*! - * \brief UIPlugIn implementation of QML. - * - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "view/UIPlugIn.h" - -#include "ApplicationModel.h" -#include "AuthModel.h" -#include "CertificateDescriptionModel.h" -#include "ChangePinModel.h" -#include "ChatModel.h" -#include "ConnectivityManager.h" -#include "HistoryModel.h" -#include "NumberModel.h" -#include "ProviderCategoryFilterModel.h" -#include "QmlExtension.h" -#include "RemoteServiceModel.h" -#include "SelfAuthenticationModel.h" -#include "SettingsModel.h" -#include "StatusBarUtil.h" -#include "VersionInformationModel.h" - -#include -#include - -namespace governikus -{ - -class UIPlugInQml - : public UIPlugIn -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID "governikus.UIPlugIn" FILE "metadata.json") - Q_INTERFACES(governikus::UIPlugIn) - Q_PROPERTY(QString platformStyle READ getPlatformStyle CONSTANT) - Q_PROPERTY(bool developerBuild READ isDeveloperBuild CONSTANT) - - private: - QScopedPointer mEngine; - qreal mDpi; - ProviderCategoryFilterModel mProviderModel; - HistoryModel mHistoryModel; - ChangePinModel mChangePinModel; - AuthModel mAuthModel; - VersionInformationModel mVersionInformationModel; - QmlExtension mQmlExtension; - SelfAuthenticationModel mSelfAuthenticationModel; - SettingsModel mSettingsModel; - CertificateDescriptionModel mCertificateDescriptionModel; - ChatModel mChatModel; - NumberModel mNumberModel; - ApplicationModel mApplicationModel; - QString mExplicitPlatformStyle; - StatusBarUtil mStatusBarUtil; - ConnectivityManager mConnectivityManager; - RemoteServiceModel mRemoteServiceModel; - - QString getPlatformSelectors() const; - static QUrl getPath(const QString& pRelativePath); - - public: - UIPlugInQml(); - virtual ~UIPlugInQml() override; - QString getPlatformStyle() const; - Q_INVOKABLE void applyPlatformStyle(const QString& pPlatformStyle); - Q_INVOKABLE bool isDeveloperBuild() const; - Q_INVOKABLE void init(); - - private Q_SLOTS: - virtual void doShutdown() override; - virtual void onWorkflowStarted(QSharedPointer pContext) override; - virtual void onWorkflowFinished(QSharedPointer pContext) override; - void onShowUserInformation(const QString& pMessage); - - public Q_SLOTS: - void doRefresh(); -}; - -} /* namespace governikus */ diff --git a/src/qml/VersionInformationModel.cpp b/src/qml/VersionInformationModel.cpp deleted file mode 100644 index 574b8cf..0000000 --- a/src/qml/VersionInformationModel.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/*! - * \brief Model implementation for version information. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "VersionInformationModel.h" - -#include "AppSettings.h" -#include "BuildHelper.h" -#include "DeviceInfo.h" - -#include -#include -#include -#include - -#include - - -using namespace governikus; - - -void VersionInformationModel::init() -{ - mData.clear(); - mData += QPair(tr("Application Name"), QCoreApplication::applicationName()); - mData += QPair(tr("Application Version"), QCoreApplication::applicationVersion()); - mData += QPair(tr("Organization"), QCoreApplication::organizationName()); - mData += QPair(tr("Organization domain"), QCoreApplication::organizationDomain()); - mData += QPair(tr("Build date"), QString::fromLatin1(BuildHelper::getDateTime())); -#ifdef Q_OS_ANDROID - mData += QPair(tr("VersionCode"), QString::number(BuildHelper::getVersionCode())); -#endif - mData += QPair(tr("System version"), QSysInfo::prettyProductName()); - mData += QPair(tr("Kernel"), QSysInfo::kernelVersion()); - mData += QPair(tr("Architecture"), QSysInfo::currentCpuArchitecture()); -#ifdef Q_OS_ANDROID - mData += QPair(tr("Compiled architecture"), QSysInfo::buildCpuArchitecture()); - mData += QPair(tr("Device"), DeviceInfo::getPrettyInfo()); -#endif - mData += QPair(tr("Qt Version"), QString::fromLatin1(qVersion())); - mData += QPair(tr("OpenSSL Version"), QSslSocket::sslLibraryVersionString()); -} - - -VersionInformationModel::VersionInformationModel(QObject* pParent) - : QAbstractListModel(pParent) - , mData() -{ - init(); - - connect(&AppSettings::getInstance().getGeneralSettings(), &GeneralSettings::fireSettingsChanged, this, [this]() - { - beginResetModel(); - init(); - endResetModel(); - }); -} - - -int VersionInformationModel::rowCount(const QModelIndex&) const -{ - return mData.size(); -} - - -QVariant VersionInformationModel::data(const QModelIndex& pIndex, int pRole) const -{ - if (pIndex.isValid() && pIndex.row() < rowCount()) - { - auto entry = mData[pIndex.row()]; - if (pRole == LABEL) - { - return entry.first; - } - if (pRole == TEXT) - { - return entry.second; - } - } - return QVariant(); -} - - -QHash VersionInformationModel::roleNames() const -{ - QHash roles = QAbstractListModel::roleNames(); - roles.insert(LABEL, "label"); - roles.insert(TEXT, "text"); - return roles; -} diff --git a/src/qml/VersionInformationModel.h b/src/qml/VersionInformationModel.h deleted file mode 100644 index f554449..0000000 --- a/src/qml/VersionInformationModel.h +++ /dev/null @@ -1,38 +0,0 @@ -/*! - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include -#include -#include - -namespace governikus -{ - -class VersionInformationModel - : public QAbstractListModel -{ - Q_OBJECT - - private: - enum HistoryRoles - { - LABEL = Qt::UserRole + 1, - TEXT - }; - QVector > mData; - - void init(); - - public: - VersionInformationModel(QObject* pParent = nullptr); - - int rowCount(const QModelIndex& = QModelIndex()) const override; - QVariant data(const QModelIndex& pIndex, int pRole = Qt::DisplayRole) const override; - QHash roleNames() const override; -}; - -} diff --git a/src/qml/WorkflowModel.cpp b/src/qml/WorkflowModel.cpp deleted file mode 100644 index 4e0444d..0000000 --- a/src/qml/WorkflowModel.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/*! - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "WorkflowModel.h" - -#include "AppSettings.h" -#include "context/AuthContext.h" -#include "GeneralSettings.h" - -using namespace governikus; - - -WorkflowModel::WorkflowModel(QObject* pParent) - : QObject(pParent) - , mContext() -{ -} - - -WorkflowModel::~WorkflowModel() -{ -} - - -void WorkflowModel::resetContext(const QSharedPointer& pContext) -{ - mContext = pContext; - if (mContext) - { - connect(mContext.data(), &WorkflowContext::fireStateChanged, this, &WorkflowModel::fireCurrentStateChanged); - connect(mContext.data(), &WorkflowContext::fireResultChanged, this, &WorkflowModel::fireResultChanged); - connect(mContext.data(), &WorkflowContext::fireReaderPlugInTypesChanged, this, &WorkflowModel::fireReaderPlugInTypeChanged); - connect(mContext.data(), &WorkflowContext::fireCardConnectionChanged, this, &WorkflowModel::fireIsBasicReaderChanged); - } - - /* - * Only this state change is emitted when the context is reset, i.e. after the end of the workflow - */ - Q_EMIT fireCurrentStateChanged(getCurrentState()); -} - - -QString WorkflowModel::getCurrentState() const -{ - return mContext ? mContext->getCurrentState() : QString(); -} - - -QString WorkflowModel::getResultString() const -{ - return mContext ? mContext->getStatus().toErrorDescription(true) : QString(); -} - - -bool WorkflowModel::isError() const -{ - return mContext && mContext->getStatus().isError(); -} - - -QString WorkflowModel::getReaderPlugInType() const -{ - if (mContext && !mContext->getReaderPlugInTypes().isEmpty()) - { - return getEnumName(mContext->getReaderPlugInTypes().at(0)); - } - - return QString(); -} - - -void WorkflowModel::setReaderPlugInType(const QString& pReaderPlugInType) -{ - setReaderPlugInType(Enum::fromString(pReaderPlugInType, ReaderManagerPlugInType::UNKNOWN)); -} - - -void WorkflowModel::setReaderPlugInType(const ReaderManagerPlugInType pReaderPlugInType) -{ - if (!mContext) - { - return; - } - mContext->setReaderPlugInTypes({pReaderPlugInType}); - - GeneralSettings& settings = AppSettings::getInstance().getGeneralSettings(); - settings.setLastReaderPluginType(getEnumName(pReaderPlugInType)); - settings.save(); -} - - -void WorkflowModel::continueWorkflow() -{ - if (mContext) - { - mContext->setStateApproved(); - } -} - - -void WorkflowModel::startWorkflow() -{ - Q_EMIT fireStartWorkflow(); -} - - -void WorkflowModel::cancelWorkflow() -{ - if (mContext) - { - Q_EMIT mContext->fireCancelWorkflow(); - } -} - - -void WorkflowModel::cancelWorkflowOnPinBlocked() -{ - if (mContext) - { - mContext->setStatus(GlobalStatus::Code::Workflow_Pin_Blocked_And_Puk_Objectionable); - Q_EMIT mContext->fireCancelWorkflow(); - } -} - - -bool WorkflowModel::isBasicReader() -{ - if (mContext && mContext->getCardConnection()) - { - return mContext->getCardConnection()->getReaderInfo().isBasicReader(); - } - - return true; -} - - -void WorkflowModel::setInitialPluginType() -{ - const GeneralSettings& settings = AppSettings::getInstance().getGeneralSettings(); - - const QString& lastReaderPluginTypeString = settings.getLastReaderPluginType(); - const auto& lastReaderPluginType = Enum::fromString(lastReaderPluginTypeString, ReaderManagerPlugInType::UNKNOWN); - - if (lastReaderPluginType == ReaderManagerPlugInType::UNKNOWN) - { -#if defined(Q_OS_ANDROID) - setReaderPlugInType(ReaderManagerPlugInType::NFC); -#elif defined(Q_OS_IOS) - setReaderPlugInType(ReaderManagerPlugInType::BLUETOOTH); -#else - setReaderPlugInType(ReaderManagerPlugInType::PCSC); -#endif - return; - } - setReaderPlugInType(lastReaderPluginType); -} diff --git a/src/qml/WorkflowModel.h b/src/qml/WorkflowModel.h deleted file mode 100644 index b7f45f1..0000000 --- a/src/qml/WorkflowModel.h +++ /dev/null @@ -1,65 +0,0 @@ -/*! - * \brief Model implementation for the authentication action. - * - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "ReaderManagerPlugInInfo.h" - -#include -#include -#include - -namespace governikus -{ - -class WorkflowContext; - -class WorkflowModel - : public QObject -{ - Q_OBJECT - Q_PROPERTY(QString currentState READ getCurrentState NOTIFY fireCurrentStateChanged) - Q_PROPERTY(QString resultString READ getResultString NOTIFY fireResultChanged) - Q_PROPERTY(bool error READ isError NOTIFY fireResultChanged) - Q_PROPERTY(QString readerPlugInType READ getReaderPlugInType WRITE setReaderPlugInType NOTIFY fireReaderPlugInTypeChanged) - Q_PROPERTY(bool isBasicReader READ isBasicReader NOTIFY fireIsBasicReaderChanged) - - private: - QSharedPointer mContext; - - void setReaderPlugInType(const ReaderManagerPlugInType pReaderPlugInType); - - public: - WorkflowModel(QObject* pParent = nullptr); - virtual ~WorkflowModel(); - - void resetContext(const QSharedPointer& pContext = QSharedPointer()); - - QString getCurrentState() const; - virtual QString getResultString() const; - bool isError() const; - - QString getReaderPlugInType() const; - void setReaderPlugInType(const QString& pReaderPlugInType); - - bool isBasicReader(); - - Q_INVOKABLE void startWorkflow(); - Q_INVOKABLE void cancelWorkflow(); - Q_INVOKABLE void cancelWorkflowOnPinBlocked(); - Q_INVOKABLE void continueWorkflow(); - Q_INVOKABLE void setInitialPluginType(); - - Q_SIGNALS: - void fireStartWorkflow(); - void fireCurrentStateChanged(const QString& pState); - void fireResultChanged(); - void fireReaderPlugInTypeChanged(); - void fireIsBasicReaderChanged(); -}; - - -} /* namespace governikus */ diff --git a/src/remote_device/CMakeLists.txt b/src/remote_device/CMakeLists.txt index b785a86..e58ea4b 100644 --- a/src/remote_device/CMakeLists.txt +++ b/src/remote_device/CMakeLists.txt @@ -1,3 +1,11 @@ +##################################################################### +# The module remote devices is responsible for the feature +# "smartphone as card reader". It provides an interface to control +# pairing and connection. Also it implements a ReaderManagerPlugin +# to provide a wrapper of a card reader. +##################################################################### + ADD_PLATFORM_LIBRARY(AusweisAppRemoteDevice) TARGET_LINK_LIBRARIES(AusweisAppRemoteDevice Qt5::Core Qt5::WebSockets AusweisAppCard AusweisAppGlobal AusweisAppSecureStorage AusweisAppNetwork AusweisAppSettings) +TARGET_COMPILE_DEFINITIONS(AusweisAppRemoteDevice PRIVATE QT_STATICPLUGIN) diff --git a/src/remote_device/DataChannel.cpp b/src/remote_device/DataChannel.cpp index 55c6741..b44d6ae 100644 --- a/src/remote_device/DataChannel.cpp +++ b/src/remote_device/DataChannel.cpp @@ -16,11 +16,3 @@ DataChannel::DataChannel() DataChannel::~DataChannel() { } - - -const QString& DataChannel::getId() const -{ - static const QString EMPTY_STRING; - - return EMPTY_STRING; -} diff --git a/src/remote_device/DataChannel.h b/src/remote_device/DataChannel.h index 5a06563..6930e23 100644 --- a/src/remote_device/DataChannel.h +++ b/src/remote_device/DataChannel.h @@ -25,11 +25,11 @@ class DataChannel Q_INVOKABLE virtual void send(const QByteArray& pDataBlock) = 0; Q_INVOKABLE virtual void close() = 0; - virtual const QString& getId() const; + virtual const QString& getId() const = 0; Q_SIGNALS: void fireReceived(const QByteArray& pDataBlock); void fireClosed(GlobalStatus::Code pCloseCode); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/card/base/RemoteClient.cpp b/src/remote_device/RemoteClient.cpp similarity index 100% rename from src/card/base/RemoteClient.cpp rename to src/remote_device/RemoteClient.cpp diff --git a/src/remote_device/RemoteClient.h b/src/remote_device/RemoteClient.h new file mode 100644 index 0000000..d540279 --- /dev/null +++ b/src/remote_device/RemoteClient.h @@ -0,0 +1,55 @@ +/*! + * \brief An interface for RemoteClientImpl, meant to omit the + * dependency between card_base and remote_device. + * + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "Env.h" +#include "GlobalStatus.h" +#include "RemoteDeviceList.h" +#include "RemoteDispatcherClient.h" +#include "RemoteServiceSettings.h" + +#include +#include + +namespace governikus +{ + +class RemoteClient + : public QObject + , private Env::ThreadSafe +{ + Q_OBJECT + + Q_SIGNALS: + void fireDeviceAppeared(const QSharedPointer& pEntry); + void fireDeviceVanished(const QSharedPointer& pEntry); + void fireEstablishConnectionDone(const QSharedPointer& pEntry, GlobalStatus pStatus); + + void fireNewRemoteDispatcher(const QSharedPointer& pRemoteDispatcher); + void fireRemoteDevicesInfo(const QVector >& pRemoteDevices); + void fireDispatcherDestroyed(GlobalStatus::Code pCloseCode, const QString& pId); + void fireDetectionChanged(); + void fireCertificateRemoved(QString pDeviceName); + + public: + RemoteClient() = default; + virtual ~RemoteClient(); + + Q_INVOKABLE virtual void startDetection() = 0; + Q_INVOKABLE virtual void stopDetection() = 0; + Q_INVOKABLE virtual bool isDetecting() = 0; + + Q_INVOKABLE virtual void establishConnection(const QSharedPointer& pEntry, const QString& pPsk) = 0; + + virtual QVector > getRemoteDevices() const; + Q_INVOKABLE virtual void requestRemoteDevices(); + virtual QVector getConnectedDeviceInfos() = 0; +}; + + +} // namespace governikus diff --git a/src/remote_device/RemoteClientImpl.cpp b/src/remote_device/RemoteClientImpl.cpp index 199dd69..3cc3468 100644 --- a/src/remote_device/RemoteClientImpl.cpp +++ b/src/remote_device/RemoteClientImpl.cpp @@ -5,9 +5,9 @@ #include "RemoteClientImpl.h" #include "AppSettings.h" -#include "Env.h" -#include "messages/RemoteMessageParser.h" +#include "messages/Discovery.h" #include "RemoteConnectorImpl.h" +#include "SingletonHelper.h" #include #include @@ -17,17 +17,7 @@ Q_DECLARE_LOGGING_CATEGORY(remote_device) using namespace governikus; -namespace governikus -{ - -template<> RemoteClient* createNewObject() -{ - return new RemoteClientImpl; -} - - -} - +defineSingletonImpl(RemoteClient, RemoteClientImpl) RemoteClientImpl::RemoteClientImpl() : mDatagramHandler() @@ -75,8 +65,8 @@ void RemoteClientImpl::shutdownRemoteConnectorThread() qCDebug(remote_device) << "Shutdown RemoteConnector..."; mRemoteConnectorThread.requestInterruption(); // do not try to stop AGAIN from dtor mRemoteConnectorThread.quit(); - mRemoteConnectorThread.wait(2500); - qCDebug(remote_device) << "RemoteConnector:" << (mRemoteConnectorThread.isRunning() ? "still running" : "stopped"); + mRemoteConnectorThread.wait(5000); + qCDebug(remote_device).noquote() << mRemoteConnectorThread.objectName() << "stopped:" << !mRemoteConnectorThread.isRunning(); Q_ASSERT(!mRemoteConnectorThread.isRunning()); } @@ -89,7 +79,7 @@ QSharedPointer RemoteClientImpl::mapToAndTakeRemoteConnec while (i.hasNext()) { QSharedPointer entry = i.next(); - if (entry->contains(pRemoteDeviceDescriptor)) + if (entry->containsEquivalent(pRemoteDeviceDescriptor)) { i.remove(); return entry; @@ -101,38 +91,32 @@ QSharedPointer RemoteClientImpl::mapToAndTakeRemoteConnec } -void RemoteClientImpl::onNewMessage(const QJsonDocument& pData, const QHostAddress& pAddress) +void RemoteClientImpl::onNewMessage(const QByteArray& pData, const QHostAddress& pAddress) { - bool isIPv4; - pAddress.toIPv4Address(&isIPv4); - if (!isIPv4) + QJsonObject obj; { - static int timesLogged = 0; - if (timesLogged < 20) - { - qCCritical(remote_device) << "IPv6 not supported" << pAddress; - timesLogged++; - } - return; + obj = RemoteMessage::parseByteArray(pData); } - const QSharedPointer& discovery = RemoteMessageParser().parseDiscovery(pData); - if (discovery.isNull()) + + const Discovery discovery(obj); + if (discovery.isIncomplete()) { - static int timesLogged = 0; - if (timesLogged < 20) - { - qCDebug(remote_device) << "Discarding unparsable message"; - timesLogged++; - } + qCDebug(remote_device) << "Discarding unparsable message"; return; } const RemoteDeviceDescriptor remoteDeviceDescriptor(discovery, pAddress); + if (remoteDeviceDescriptor.isNull()) + { + qCCritical(remote_device) << "Dropping message from unparsable address:" << pAddress; + return; + } + mRemoteDeviceList->update(remoteDeviceDescriptor); } -void RemoteClientImpl::onRemoteDispatcherCreated(const RemoteDeviceDescriptor& pRemoteDeviceDescriptor, const QSharedPointer& pDispatcher) +void RemoteClientImpl::onRemoteDispatcherCreated(const RemoteDeviceDescriptor& pRemoteDeviceDescriptor, const QSharedPointer& pDispatcher) { mErrorCounter[pRemoteDeviceDescriptor.getIfdId()] = 0; const QSharedPointer& entry = mapToAndTakeRemoteConnectorPending(pRemoteDeviceDescriptor); @@ -144,7 +128,7 @@ void RemoteClientImpl::onRemoteDispatcherCreated(const RemoteDeviceDescriptor& p } mConnectedDeviceIds.append(entry->getRemoteDeviceDescriptor().getIfdId()); - connect(pDispatcher.data(), &RemoteDispatcher::fireClosed, this, &RemoteClientImpl::onDispatcherDestroyed); + connect(pDispatcher.data(), &RemoteDispatcherClient::fireClosed, this, &RemoteClientImpl::onDispatcherDestroyed); Q_EMIT fireEstablishConnectionDone(entry, GlobalStatus::Code::No_Error); Q_EMIT fireNewRemoteDispatcher(pDispatcher); @@ -167,7 +151,7 @@ void RemoteClientImpl::onRemoteDispatcherError(const RemoteDeviceDescriptor& pRe if (mErrorCounter[pRemoteDeviceDescriptor.getIfdId()] >= 7) { qCCritical(remote_device) << "Remote device refused connection seven times, removing certificate with fingerprint:" << pRemoteDeviceDescriptor.getIfdId(); - RemoteServiceSettings& settings = AppSettings::getInstance().getRemoteServiceSettings(); + RemoteServiceSettings& settings = Env::getSingleton()->getRemoteServiceSettings(); QString deviceName; const auto& infos = settings.getRemoteInfos(); for (const auto& remoteInfo : infos) @@ -239,9 +223,10 @@ void RemoteClientImpl::establishConnection(const QSharedPointergetRemoteDeviceDescriptor()), - Q_ARG(QString, pPsk)); + const auto& localCopy = mRemoteConnector; + QMetaObject::invokeMethod(localCopy.data(), [localCopy, pEntry, pPsk] { + localCopy->onConnectRequest(pEntry->getRemoteDeviceDescriptor(), pPsk); + }, Qt::QueuedConnection); } @@ -259,7 +244,7 @@ void RemoteClientImpl::requestRemoteDevices() QVector RemoteClientImpl::getConnectedDeviceInfos() { - RemoteServiceSettings& settings = AppSettings::getInstance().getRemoteServiceSettings(); + RemoteServiceSettings& settings = Env::getSingleton()->getRemoteServiceSettings(); auto remoteInfos = settings.getRemoteInfos(); QVector result; for (const auto& info : remoteInfos) @@ -276,11 +261,8 @@ QVector RemoteClientImpl::getConnectedDeviceI } -void RemoteClientImpl::onDispatcherDestroyed(GlobalStatus::Code pCloseCode, const QSharedPointer& pRemoteDispatcher) +void RemoteClientImpl::onDispatcherDestroyed(GlobalStatus::Code pCloseCode, const QString& pId) { - if (pRemoteDispatcher) - { - mConnectedDeviceIds.removeAll(pRemoteDispatcher->getId()); - } - Q_EMIT fireDispatcherDestroyed(pCloseCode, pRemoteDispatcher); + mConnectedDeviceIds.removeAll(pId); + Q_EMIT fireDispatcherDestroyed(pCloseCode, pId); } diff --git a/src/remote_device/RemoteClientImpl.h b/src/remote_device/RemoteClientImpl.h index ab1ee3d..f679246 100644 --- a/src/remote_device/RemoteClientImpl.h +++ b/src/remote_device/RemoteClientImpl.h @@ -9,6 +9,7 @@ #include "RemoteClient.h" #include "DatagramHandler.h" +#include "Env.h" #include "RemoteConnector.h" #include "RemoteDeviceList.h" @@ -17,6 +18,8 @@ #include #include +class test_RemoteClient; + namespace governikus { @@ -26,6 +29,9 @@ class RemoteClientImpl Q_OBJECT private: + friend ::test_RemoteClient; + friend RemoteClient* singleton(); + QSharedPointer mDatagramHandler; QScopedPointer mRemoteDeviceList; QMap mErrorCounter; @@ -39,16 +45,17 @@ class RemoteClientImpl void shutdownRemoteConnectorThread(); QSharedPointer mapToAndTakeRemoteConnectorPending(const RemoteDeviceDescriptor& pRemoteDeviceDescriptor); - private Q_SLOTS: - void onNewMessage(const QJsonDocument& pData, const QHostAddress& pAddress); - void onRemoteDispatcherCreated(const RemoteDeviceDescriptor& pRemoteDeviceDescriptor, const QSharedPointer& pAdapter); - void onRemoteDispatcherError(const RemoteDeviceDescriptor& pRemoteDeviceDescriptor, RemoteErrorCode pErrorCode); - void onDispatcherDestroyed(GlobalStatus::Code pCloseCode, const QSharedPointer& pRemoteDispatcher); - - public: + protected: RemoteClientImpl(); virtual ~RemoteClientImpl() override; + private Q_SLOTS: + void onNewMessage(const QByteArray& pData, const QHostAddress& pAddress); + void onRemoteDispatcherCreated(const RemoteDeviceDescriptor& pRemoteDeviceDescriptor, const QSharedPointer& pDispatcher); + void onRemoteDispatcherError(const RemoteDeviceDescriptor& pRemoteDeviceDescriptor, RemoteErrorCode pErrorCode); + void onDispatcherDestroyed(GlobalStatus::Code pCloseCode, const QString& pId); + + public: Q_INVOKABLE virtual void startDetection() override; Q_INVOKABLE virtual void stopDetection() override; Q_INVOKABLE virtual bool isDetecting() override; @@ -61,4 +68,4 @@ class RemoteClientImpl }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/remote_device/RemoteConnector.h b/src/remote_device/RemoteConnector.h index 1e952ee..a9979f5 100644 --- a/src/remote_device/RemoteConnector.h +++ b/src/remote_device/RemoteConnector.h @@ -9,7 +9,7 @@ #include "EnumHelper.h" #include "messages/RemoteMessage.h" #include "RemoteDeviceDescriptor.h" -#include "RemoteDispatcher.h" +#include "RemoteDispatcherClient.h" #include @@ -39,9 +39,9 @@ class RemoteConnector virtual void onConnectRequest(const RemoteDeviceDescriptor& pRemoteDeviceDescriptor, const QString& pPsk) = 0; Q_SIGNALS: - void fireRemoteDispatcherCreated(const RemoteDeviceDescriptor& pRemoteDeviceDescriptor, const QSharedPointer& pAdapter); + void fireRemoteDispatcherCreated(const RemoteDeviceDescriptor& pRemoteDeviceDescriptor, const QSharedPointer& pClientDispatcher); void fireRemoteDispatcherError(const RemoteDeviceDescriptor& pRemoteDeviceDescriptor, RemoteErrorCode pErrorCode); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/remote_device/RemoteConnectorImpl.cpp b/src/remote_device/RemoteConnectorImpl.cpp index aab10c9..0b297d4 100644 --- a/src/remote_device/RemoteConnectorImpl.cpp +++ b/src/remote_device/RemoteConnectorImpl.cpp @@ -6,8 +6,8 @@ #include "AppSettings.h" #include "Env.h" -#include "RemoteDispatcherImpl.h" -#include "RemoteHelper.h" +#include "LogHandler.h" +#include "RemoteDispatcher.h" #include "SecureStorage.h" #include "TlsChecker.h" #include "WebSocketChannel.h" @@ -63,8 +63,7 @@ class ConnectRequest void fireConnectionTimeout(const RemoteDeviceDescriptor& pRemoteDeviceDescriptor); }; - -} +} // namespace governikus using namespace governikus; @@ -75,7 +74,7 @@ void ConnectRequest::onConnected() mTimer.stop(); const auto& cfg = mSocket->sslConfiguration(); - TlsChecker::logSslConfig(cfg, qInfo(remote_device)); + TlsChecker::logSslConfig(cfg, spawnMessageLogger(remote_device)); if (!TlsChecker::hasValidCertificateKeyLength(cfg.peerCertificate()) || (!cfg.ephemeralServerKey().isNull() && !TlsChecker::hasValidEphemeralKeyLength(cfg.ephemeralServerKey()))) @@ -109,12 +108,7 @@ void ConnectRequest::onConnected() void ConnectRequest::onError(QAbstractSocket::SocketError pError) { - static int timesLogged = 0; - if (timesLogged < 20) - { - qCWarning(remote_device) << "Connection error:" << pError; - timesLogged++; - } + qCWarning(remote_device) << "Connection error:" << pError; mTimer.stop(); if (pError == QAbstractSocket::SocketError::RemoteHostClosedError) @@ -182,18 +176,19 @@ ConnectRequest::ConnectRequest(const RemoteDeviceDescriptor& pRemoteDeviceDescri , mSocket(new QWebSocket(), &QObject::deleteLater) , mTimer() { - if (!RemoteHelper::checkAndGenerateKey()) + auto& remoteServiceSettings = Env::getSingleton()->getRemoteServiceSettings(); + const bool invalidLength = !TlsChecker::hasValidCertificateKeyLength(remoteServiceSettings.getCertificate()); + if (!remoteServiceSettings.checkAndGenerateKey(invalidLength)) { qCCritical(remote_device) << "Cannot get required key/certificate for tls"; return; } - auto& settings = Env::getSingleton()->getRemoteServiceSettings(); QSslConfiguration config; if (mPsk.isEmpty()) { config = SecureStorage::getInstance().getTlsConfigRemote().getConfiguration(); - config.setCaCertificates(settings.getTrustedCertificates()); + config.setCaCertificates(remoteServiceSettings.getTrustedCertificates()); qCCritical(remote_device) << "Start reconnect to server"; } else @@ -201,8 +196,8 @@ ConnectRequest::ConnectRequest(const RemoteDeviceDescriptor& pRemoteDeviceDescri config = SecureStorage::getInstance().getTlsConfigRemote(SecureStorage::TlsSuite::PSK).getConfiguration(); qCCritical(remote_device) << "Start pairing to server"; } - config.setPrivateKey(settings.getKey()); - config.setLocalCertificate(settings.getCertificate()); + config.setPrivateKey(remoteServiceSettings.getKey()); + config.setLocalCertificate(remoteServiceSettings.getCertificate()); config.setPeerVerifyMode(QSslSocket::VerifyPeer); mSocket->setSslConfiguration(config); @@ -259,7 +254,8 @@ void RemoteConnectorImpl::onConnectionCreated(const RemoteDeviceDescriptor& pRem const QSharedPointer& pWebSocket) { const QSharedPointer channel(new WebSocketChannel(pWebSocket), &QObject::deleteLater); - const QSharedPointer dispatcher(Env::create(channel), &QObject::deleteLater); + const IfdVersion::Version latestSupportedVersion = IfdVersion::selectLatestSupported(pRemoteDeviceDescriptor.getApiVersions()); + const QSharedPointer dispatcher(Env::create(latestSupportedVersion, channel), &QObject::deleteLater); removeRequest(pRemoteDeviceDescriptor); diff --git a/src/remote_device/RemoteConnectorImpl.h b/src/remote_device/RemoteConnectorImpl.h index 3276f7e..b68d7f4 100644 --- a/src/remote_device/RemoteConnectorImpl.h +++ b/src/remote_device/RemoteConnectorImpl.h @@ -13,6 +13,7 @@ namespace governikus { + class ConnectRequest; class RemoteConnectorImpl @@ -39,4 +40,4 @@ class RemoteConnectorImpl }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/remote_device/RemoteDeviceDescriptor.cpp b/src/remote_device/RemoteDeviceDescriptor.cpp index 319c0b3..eba8522 100644 --- a/src/remote_device/RemoteDeviceDescriptor.cpp +++ b/src/remote_device/RemoteDeviceDescriptor.cpp @@ -5,8 +5,6 @@ #include "RemoteDeviceDescriptor.h" #include "Initializer.h" -#include "messages/Discovery.h" - using namespace governikus; @@ -18,57 +16,25 @@ static Initializer::Entry E([] { namespace { -QHostAddress convertAddressProtocol(const QHostAddress& pAddress, QAbstractSocket::NetworkLayerProtocol pRequiredProtocol) + +QUrl urlFromMsgAndHost(const Discovery& pDiscovery, + const QHostAddress& pHostAddress) { - const QAbstractSocket::NetworkLayerProtocol actualProtocol = pAddress.protocol(); - if (actualProtocol == pRequiredProtocol) - { - return pAddress; - } - - switch (pRequiredProtocol) - { - case QAbstractSocket::IPv4Protocol: - { - bool conversionOK = false; - const QHostAddress convertedAddress(pAddress.toIPv4Address(&conversionOK)); - return conversionOK ? convertedAddress : QHostAddress(); - } - - case QAbstractSocket::IPv6Protocol: - return QHostAddress(pAddress.toIPv6Address()); - - default: - return QHostAddress(); - } -} - - -QUrl urlFromMsgAndHost(const QSharedPointer& pMsg, - const QHostAddress& pHostAddress, - QAbstractSocket::NetworkLayerProtocol pRequiredProtocol) -{ - if (pMsg.isNull()) - { - return QUrl(); - } - - const QHostAddress convertedHostAddress = convertAddressProtocol(pHostAddress, pRequiredProtocol); - if (convertedHostAddress.isNull()) + if (pDiscovery.isIncomplete()) { return QUrl(); } QUrl url; url.setScheme(QStringLiteral("wss")); - url.setHost(convertedHostAddress.toString()); - url.setPort(pMsg->getPort()); + url.setHost(pHostAddress.toString()); + url.setPort(pDiscovery.getPort()); return url; } -} +} // namespace RemoteDeviceDescriptor::RemoteDeviceDescriptorData::RemoteDeviceDescriptorData(const QString& pIfdName, @@ -90,35 +56,39 @@ RemoteDeviceDescriptor::RemoteDeviceDescriptorData::~RemoteDeviceDescriptorData( bool RemoteDeviceDescriptor::RemoteDeviceDescriptorData::operator==(const RemoteDeviceDescriptorData& pOther) const { - return mIfdName == pOther.mIfdName && - mIfdId == pOther.mIfdId && - mApiVersions == pOther.mApiVersions && + return isEquivalent(pOther) && mUrl == pOther.mUrl; } -RemoteDeviceDescriptor::RemoteDeviceDescriptorData* RemoteDeviceDescriptor::createRemoteDeviceDescriptorData(const QSharedPointer& pMsg, - const QHostAddress& pHostAddress, - QAbstractSocket::NetworkLayerProtocol pRequiredProtocol) +bool RemoteDeviceDescriptor::RemoteDeviceDescriptorData::isEquivalent(const RemoteDeviceDescriptorData& pOther) const { - const QUrl url = urlFromMsgAndHost(pMsg, pHostAddress, pRequiredProtocol); - if (url.isEmpty()) + return mIfdName == pOther.mIfdName && + mIfdId == pOther.mIfdId && + mApiVersions == pOther.mApiVersions; +} + + +RemoteDeviceDescriptor::RemoteDeviceDescriptorData* RemoteDeviceDescriptor::createRemoteDeviceDescriptorData(const Discovery& pDiscovery, + const QHostAddress& pHostAddress) +{ + const QUrl url = urlFromMsgAndHost(pDiscovery, pHostAddress); + if (url.isEmpty() || url.host().isEmpty()) { return nullptr; } - const QString& ifdName = pMsg->getIfdName(); - const QString& ifdId = pMsg->getIfdId(); - const QVector& supportedApis = pMsg->getSupportedApis(); + const QString& ifdName = pDiscovery.getIfdName(); + const QString& ifdId = pDiscovery.getIfdId(); + const QVector& supportedApis = pDiscovery.getSupportedApis(); return new RemoteDeviceDescriptorData(ifdName, ifdId, supportedApis, url); } -RemoteDeviceDescriptor::RemoteDeviceDescriptor(const QSharedPointer& pDiscovery, - const QHostAddress& pHostAddress, - QAbstractSocket::NetworkLayerProtocol pRequiredProtocol) - : d(createRemoteDeviceDescriptorData(pDiscovery, pHostAddress, pRequiredProtocol)) +RemoteDeviceDescriptor::RemoteDeviceDescriptor(const Discovery& pDiscovery, + const QHostAddress& pHostAddress) + : d(createRemoteDeviceDescriptorData(pDiscovery, pHostAddress)) { } @@ -149,7 +119,7 @@ const QVector& RemoteDeviceDescriptor::getApiVersions() con bool RemoteDeviceDescriptor::isSupported() const { - return IfdVersion::selectLatestSupported(getApiVersions()).isValid(); + return IfdVersion(IfdVersion::selectLatestSupported(getApiVersions())).isValid(); } @@ -173,3 +143,11 @@ bool RemoteDeviceDescriptor::operator==(const RemoteDeviceDescriptor& pOther) co (d.data() == nullptr && pOther.d.data() == nullptr) || (d.data() != nullptr && pOther.d.data() != nullptr && *d == *(pOther.d)); } + + +bool RemoteDeviceDescriptor::isEquivalent(const RemoteDeviceDescriptor& pOther) const +{ + return this == &pOther || + (d.data() == nullptr && pOther.d.data() == nullptr) || + (d.data() != nullptr && pOther.d.data() != nullptr && d->isEquivalent(*(pOther.d))); +} diff --git a/src/remote_device/RemoteDeviceDescriptor.h b/src/remote_device/RemoteDeviceDescriptor.h index 2683ee3..60492cb 100644 --- a/src/remote_device/RemoteDeviceDescriptor.h +++ b/src/remote_device/RemoteDeviceDescriptor.h @@ -7,7 +7,7 @@ #pragma once -#include "messages/IfdVersion.h" +#include "messages/Discovery.h" #include #include @@ -17,8 +17,6 @@ namespace governikus { -class Discovery; - class RemoteDeviceDescriptor { @@ -39,20 +37,19 @@ class RemoteDeviceDescriptor const QVector mApiVersions; const QUrl mUrl; bool operator==(const RemoteDeviceDescriptorData& pOther) const; + bool isEquivalent(const RemoteDeviceDescriptorData& pOther) const; }; - static RemoteDeviceDescriptorData* createRemoteDeviceDescriptorData(const QSharedPointer& pMsg, - const QHostAddress& pHostAddress, - QAbstractSocket::NetworkLayerProtocol pRequiredProtocol); + static RemoteDeviceDescriptorData* createRemoteDeviceDescriptorData(const Discovery& pDiscovery, + const QHostAddress& pHostAddress); const QSharedDataPointer d; public: RemoteDeviceDescriptor() = default; - RemoteDeviceDescriptor(const QSharedPointer& pDiscovery, - const QHostAddress& pHostAddress, - QAbstractSocket::NetworkLayerProtocol pRequiredProtocol = QAbstractSocket::IPv4Protocol); + RemoteDeviceDescriptor(const Discovery& pDiscovery, + const QHostAddress& pHostAddress); ~RemoteDeviceDescriptor() = default; @@ -64,6 +61,7 @@ class RemoteDeviceDescriptor bool isNull() const; bool operator==(const RemoteDeviceDescriptor& pOther) const; + bool isEquivalent(const RemoteDeviceDescriptor& pOther) const; }; @@ -78,4 +76,4 @@ inline QDebug operator<<(QDebug pDbg, const RemoteDeviceDescriptor& pRemoteDevic } -} /* namespace governikus */ +} // namespace governikus diff --git a/src/remote_device/RemoteDeviceList.cpp b/src/remote_device/RemoteDeviceList.cpp index 036c4b7..a8c4ae6 100644 --- a/src/remote_device/RemoteDeviceList.cpp +++ b/src/remote_device/RemoteDeviceList.cpp @@ -31,7 +31,7 @@ template<> RemoteDeviceList* createNewObject(int&& } -} /* namespace governikus */ +} // namespace governikus RemoteDeviceListEntry::RemoteDeviceListEntry(const RemoteDeviceDescriptor& pRemoteDeviceDescriptor) @@ -47,9 +47,9 @@ void RemoteDeviceListEntry::setLastSeenToNow() } -bool RemoteDeviceListEntry::contains(const RemoteDeviceDescriptor& pRemoteDeviceDescriptor) const +bool RemoteDeviceListEntry::containsEquivalent(const RemoteDeviceDescriptor& pRemoteDeviceDescriptor) const { - return mRemoteDeviceDescriptor == pRemoteDeviceDescriptor; + return mRemoteDeviceDescriptor.isEquivalent(pRemoteDeviceDescriptor); } @@ -75,13 +75,11 @@ const RemoteDeviceDescriptor& RemoteDeviceListEntry::getRemoteDeviceDescriptor() RemoteDeviceList::RemoteDeviceList(int, int) { - } RemoteDeviceList::~RemoteDeviceList() { - } @@ -112,14 +110,14 @@ void RemoteDeviceListImpl::update(const RemoteDeviceDescriptor& pDescriptor) { for (const QSharedPointer& entry : qAsConst(mList)) { - if (entry->contains(pDescriptor)) + if (entry->containsEquivalent(pDescriptor)) { entry->setLastSeenToNow(); return; } } - const QSharedPointer newDevice(new RemoteDeviceListEntry(pDescriptor)); + const auto& newDevice = QSharedPointer::create(pDescriptor); mList.append(newDevice); if (!mTimer.isActive()) diff --git a/src/remote_device/RemoteDeviceList.h b/src/remote_device/RemoteDeviceList.h index 91c4350..17cd251 100644 --- a/src/remote_device/RemoteDeviceList.h +++ b/src/remote_device/RemoteDeviceList.h @@ -28,7 +28,7 @@ class RemoteDeviceListEntry void setLastSeenToNow(); - bool contains(const RemoteDeviceDescriptor& pRemoteDeviceDescriptor) const; + bool containsEquivalent(const RemoteDeviceDescriptor& pRemoteDeviceDescriptor) const; bool isEqual(const RemoteDeviceListEntry* const pOther) const; const QTime& getLastSeen() const; @@ -79,4 +79,4 @@ class RemoteDeviceListImpl }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/remote_device/RemoteDeviceModel.cpp b/src/remote_device/RemoteDeviceModel.cpp index 10ad935..7d4b9f1 100644 --- a/src/remote_device/RemoteDeviceModel.cpp +++ b/src/remote_device/RemoteDeviceModel.cpp @@ -5,9 +5,8 @@ #include "RemoteDeviceModel.h" #include "AppSettings.h" -#include "Env.h" #include "LanguageLoader.h" -#include "ReaderManager.h" +#include "RemoteClient.h" #include @@ -23,7 +22,6 @@ RemoteDeviceModelEntry::RemoteDeviceModelEntry(const QString pDeviceName, const , mLastConnected() , mRemoteDeviceListEntry(pRemoteDeviceListEntry) { - } @@ -36,7 +34,6 @@ RemoteDeviceModelEntry::RemoteDeviceModelEntry(const QString pDeviceName, const , mLastConnected(pLastConnected) , mRemoteDeviceListEntry(nullptr) { - } @@ -49,7 +46,6 @@ RemoteDeviceModelEntry::RemoteDeviceModelEntry(const QString pDeviceName) , mLastConnected() , mRemoteDeviceListEntry(nullptr) { - } @@ -126,13 +122,13 @@ RemoteDeviceModel::RemoteDeviceModel(QObject* pParent, bool pShowPairedReaders, , mShowPairedReaders(pShowPairedReaders) , mShowUnpairedReaders(pShowUnpairedReaders) { - RemoteServiceSettings& settings = AppSettings::getInstance().getRemoteServiceSettings(); + RemoteServiceSettings& settings = Env::getSingleton()->getRemoteServiceSettings(); connect(&settings, &RemoteServiceSettings::fireTrustedRemoteInfosChanged, this, &RemoteDeviceModel::onKnownRemoteReadersChanged); onKnownRemoteReadersChanged(); - const QSharedPointer& remoteClient = Env::getSingleton()->getRemoteClient(); - connect(remoteClient.data(), &RemoteClient::fireDeviceAppeared, this, &RemoteDeviceModel::constructReaderList); - connect(remoteClient.data(), &RemoteClient::fireDeviceVanished, this, &RemoteDeviceModel::constructReaderList); + const auto& remoteClient = Env::getSingleton(); + connect(remoteClient, &RemoteClient::fireDeviceAppeared, this, &RemoteDeviceModel::constructReaderList); + connect(remoteClient, &RemoteClient::fireDeviceVanished, this, &RemoteDeviceModel::constructReaderList); } @@ -230,7 +226,7 @@ QVariant RemoteDeviceModel::data(const QModelIndex& pIndex, int pRole) const case LAST_CONNECTED: { const auto& locale = LanguageLoader::getInstance().getUsedLocale(); - const auto& dateTimeFormat = tr("dd.MM.YYYY hh:mm AP"); + const auto& dateTimeFormat = tr("dd.MM.yyyy hh:mm AP"); return locale.toString(reader.getLastConnected(), dateTimeFormat); } @@ -296,7 +292,7 @@ void RemoteDeviceModel::onWidgetShown() } qDebug() << "Starting Remote Device Detection"; - Env::getSingleton()->getRemoteClient()->startDetection(); + Env::getSingleton()->startDetection(); constructReaderList(); } @@ -309,7 +305,7 @@ void RemoteDeviceModel::onWidgetHidden() } qDebug() << "Stopping Remote Device Detection"; - Env::getSingleton()->getRemoteClient()->stopDetection(); + Env::getSingleton()->stopDetection(); constructReaderList(); } @@ -318,7 +314,7 @@ void RemoteDeviceModel::onKnownRemoteReadersChanged() { mPairedReaders.clear(); - RemoteServiceSettings& settings = AppSettings::getInstance().getRemoteServiceSettings(); + RemoteServiceSettings& settings = Env::getSingleton()->getRemoteServiceSettings(); auto pairedReaders = settings.getRemoteInfos(); for (const auto& reader : pairedReaders) { @@ -334,27 +330,24 @@ void RemoteDeviceModel::constructReaderList() beginResetModel(); mAllRemoteReaders.clear(); - const QSharedPointer& remoteClient = Env::getSingleton()->getRemoteClient(); + RemoteClient* const remoteClient = Env::getSingleton(); if (mShowPairedReaders) { + const QVector >& foundDevices = remoteClient->getRemoteDevices(); + for (const auto& pairedReader : qAsConst(mPairedReaders)) { bool found = false; bool supported = true; - if (remoteClient) + for (const auto& foundDevice : foundDevices) { - const QVector >& foundDevices = remoteClient->getRemoteDevices(); - - for (const auto& foundDevice : foundDevices) + if (foundDevice && foundDevice->getRemoteDeviceDescriptor().getIfdId() == pairedReader.getFingerprint()) { - if (foundDevice && foundDevice->getRemoteDeviceDescriptor().getIfdId() == pairedReader.getFingerprint()) - { - found = true; - supported = foundDevice->getRemoteDeviceDescriptor().isSupported(); + found = true; + supported = foundDevice->getRemoteDeviceDescriptor().isSupported(); - break; - } + break; } } @@ -368,7 +361,7 @@ void RemoteDeviceModel::constructReaderList() } } - if (mShowUnpairedReaders && remoteClient) + if (mShowUnpairedReaders) { const QVector >& remoteDevices = remoteClient->getRemoteDevices(); @@ -402,14 +395,14 @@ void RemoteDeviceModel::forgetDevice(const QModelIndex& pIndex) void RemoteDeviceModel::forgetDevice(const QString& pDeviceId) { - RemoteServiceSettings& settings = AppSettings::getInstance().getRemoteServiceSettings(); + RemoteServiceSettings& settings = Env::getSingleton()->getRemoteServiceSettings(); settings.removeTrustedCertificate(pDeviceId); } -void RemoteDeviceModel::onDeviceDisconnected(GlobalStatus::Code pCloseCode, const QSharedPointer& pRemoteDispatcher) +void RemoteDeviceModel::onDeviceDisconnected(GlobalStatus::Code pCloseCode, const QString& pId) { Q_UNUSED(pCloseCode); - Q_UNUSED(pRemoteDispatcher); + Q_UNUSED(pId); constructReaderList(); } diff --git a/src/remote_device/RemoteDeviceModel.h b/src/remote_device/RemoteDeviceModel.h index 82b6cf6..93ffdc5 100644 --- a/src/remote_device/RemoteDeviceModel.h +++ b/src/remote_device/RemoteDeviceModel.h @@ -18,6 +18,7 @@ #include #include +class test_RemoteDeviceModel; namespace governikus { @@ -25,6 +26,7 @@ namespace governikus class RemoteDeviceModelEntry { private: + friend class ::test_RemoteDeviceModel; QString mDeviceName; QString mId; bool mPaired; @@ -59,6 +61,8 @@ class RemoteDeviceModel Q_OBJECT private: + friend class ::test_RemoteDeviceModel; + const int NUMBER_OF_COLUMNS = 2; QMap mPairedReaders; @@ -104,7 +108,7 @@ class RemoteDeviceModel void onWidgetShown(); void onWidgetHidden(); void onKnownRemoteReadersChanged(); - void onDeviceDisconnected(GlobalStatus::Code pCloseCode, const QSharedPointer& pRemoteDispatcher); + void onDeviceDisconnected(GlobalStatus::Code pCloseCode, const QString& pId); Q_SIGNALS: void fireModelChanged(); @@ -112,4 +116,4 @@ class RemoteDeviceModel }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/remote_device/RemoteDispatcher.cpp b/src/remote_device/RemoteDispatcher.cpp new file mode 100644 index 0000000..8895ca9 --- /dev/null +++ b/src/remote_device/RemoteDispatcher.cpp @@ -0,0 +1,130 @@ +/*! + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "RemoteDispatcher.h" + +#include "AppSettings.h" +#include "Initializer.h" +#include "messages/IfdError.h" + +#include + + +Q_DECLARE_LOGGING_CATEGORY(remote_device) + + +using namespace governikus; + + +static Initializer::Entry E([] { + qRegisterMetaType("RemoteCardMessageType"); + }); + + +RemoteDispatcher::RemoteDispatcher(IfdVersion::Version pVersion, const QSharedPointer& pDataChannel) + : QObject() + , mDataChannel(pDataChannel) + , mVersion(pVersion) + , mContextHandle() +{ + connect(mDataChannel.data(), &DataChannel::fireReceived, this, &RemoteDispatcher::onReceived); + connect(mDataChannel.data(), &DataChannel::fireClosed, this, &RemoteDispatcher::onClosed); +} + + +RemoteDispatcher::~RemoteDispatcher() +{ + disconnect(mDataChannel.data(), &DataChannel::fireReceived, this, &RemoteDispatcher::onReceived); + disconnect(mDataChannel.data(), &DataChannel::fireClosed, this, &RemoteDispatcher::onClosed); + + close(); +} + + +void RemoteDispatcher::onReceived(const QByteArray& pDataBlock) +{ + const auto& msgObject = RemoteMessage::parseByteArray(pDataBlock); + const RemoteMessage remoteMessage(msgObject); + const RemoteCardMessageType messageType = remoteMessage.getType(); + + if (messageType == RemoteCardMessageType::UNDEFINED) + { + const auto& errorMessage = QSharedPointer::create(QString(), ECardApiResult::Minor::AL_Unkown_API_Function); + send(errorMessage); + return; + } + + if (processContext(messageType, msgObject)) + { + return; + } + + const auto& contextHandle = remoteMessage.getContextHandle(); + if (contextHandle.isEmpty() || mContextHandle != contextHandle) + { + qCWarning(remote_device) << "Invalid context handle received. Expecting:" << mContextHandle << "but got:" << pDataBlock; + return; + } + + qCDebug(remote_device) << "Received message type:" << messageType; + Q_EMIT fireReceived(messageType, msgObject, getId()); +} + + +void RemoteDispatcher::onClosed(GlobalStatus::Code pCloseCode) +{ + qCDebug(remote_device) << "Connection closed"; + Q_EMIT fireClosed(pCloseCode, getId()); +} + + +QString RemoteDispatcher::getId() const +{ + if (!mDataChannel) + { + return QString(); + } + + return mDataChannel->getId(); +} + + +const QString& RemoteDispatcher::getContextHandle() const +{ + return mContextHandle; +} + + +void RemoteDispatcher::saveRemoteNameInSettings(const QString& pName) +{ + RemoteServiceSettings& settings = Env::getSingleton()->getRemoteServiceSettings(); + auto info = settings.getRemoteInfo(getId()); + info.setName(pName); + settings.updateRemoteInfo(info); +} + + +void RemoteDispatcher::send(const QSharedPointer& pMessage) +{ + Q_ASSERT(mDataChannel); + + const RemoteCardMessageType messageType = pMessage->getType(); + qCDebug(remote_device) << "Send message of type:" << messageType << "with context handle:" << mContextHandle; + + Q_ASSERT(!mContextHandle.isEmpty() + || messageType == RemoteCardMessageType::IFDError + || messageType == RemoteCardMessageType::IFDEstablishContext + || messageType == RemoteCardMessageType::IFDEstablishContextResponse); + + mDataChannel->send(pMessage->toByteArray(mContextHandle)); +} + + +void RemoteDispatcher::close() +{ + if (mDataChannel) + { + mDataChannel->close(); + } +} diff --git a/src/remote_device/RemoteDispatcher.h b/src/remote_device/RemoteDispatcher.h new file mode 100644 index 0000000..5feea39 --- /dev/null +++ b/src/remote_device/RemoteDispatcher.h @@ -0,0 +1,57 @@ +/*! + * + * \brief An interface for RemoteHandleImpl, meant to omit the + * dependency between card_base and remote_device. + * + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "DataChannel.h" +#include "GlobalStatus.h" +#include "messages/IfdVersion.h" +#include "messages/RemoteMessage.h" + +#include +#include + + +namespace governikus +{ + +class RemoteDispatcher + : public QObject +{ + Q_OBJECT + + private: + const QSharedPointer mDataChannel; + + virtual bool processContext(RemoteCardMessageType pMsgType, const QJsonObject& pMsgObject) = 0; + + private Q_SLOTS: + void onReceived(const QByteArray& pDataBlock); + void onClosed(GlobalStatus::Code pCloseCode); + + protected: + const IfdVersion::Version mVersion; + QString mContextHandle; + + public: + explicit RemoteDispatcher(IfdVersion::Version pVersion, const QSharedPointer& pDataChannel = QSharedPointer()); + ~RemoteDispatcher(); + + virtual QString getId() const; + virtual const QString& getContextHandle() const; + void saveRemoteNameInSettings(const QString& pName); + + void close(); + Q_INVOKABLE virtual void send(const QSharedPointer& pMessage); + + Q_SIGNALS: + void fireReceived(RemoteCardMessageType pMessageType, const QJsonObject& pJsonObject, const QString& pId); + void fireClosed(GlobalStatus::Code pCloseCode, const QString& pId); +}; + +} // namespace governikus diff --git a/src/remote_device/RemoteDispatcherClient.cpp b/src/remote_device/RemoteDispatcherClient.cpp new file mode 100644 index 0000000..c0747ab --- /dev/null +++ b/src/remote_device/RemoteDispatcherClient.cpp @@ -0,0 +1,72 @@ +/*! + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "RemoteDispatcherClient.h" + +#include "AppSettings.h" +#include "Initializer.h" +#include "messages/IfdEstablishContext.h" +#include "messages/IfdEstablishContextResponse.h" + +#include + + +Q_DECLARE_LOGGING_CATEGORY(remote_device) + + +namespace governikus +{ +template<> RemoteDispatcherClient* createNewObject&>(IfdVersion::Version&& pVersion, const QSharedPointer& pChannel) +{ + return new RemoteDispatcherClient(pVersion, pChannel); +} + + +} // namespace governikus + + +using namespace governikus; + + +static Initializer::Entry E([] { + qRegisterMetaType >("QSharedPointer"); + }); + + +RemoteDispatcherClient::RemoteDispatcherClient(IfdVersion::Version pVersion, const QSharedPointer& pDataChannel) + : RemoteDispatcher(pVersion, pDataChannel) +{ +} + + +bool RemoteDispatcherClient::processContext(RemoteCardMessageType pMsgType, const QJsonObject& pMsgObject) +{ + if (pMsgType != RemoteCardMessageType::IFDEstablishContextResponse) + { + return false; + } + + IfdEstablishContextResponse establishContextResponse(pMsgObject); + if (establishContextResponse.resultHasError()) + { + qCWarning(remote_device) << "Establish context failed with result minor:" << establishContextResponse.getResultMinor(); + } + else + { + mContextHandle = establishContextResponse.getContextHandle(); + qCDebug(remote_device) << "Received new ContextHandle:" << mContextHandle; + Q_EMIT fireContextEstablished(establishContextResponse.getIfdName(), getId()); + } + return true; +} + + +void RemoteDispatcherClient::sendEstablishContext() +{ + qCDebug(remote_device) << "Try to establish context with version" << mVersion; + + RemoteServiceSettings& settings = Env::getSingleton()->getRemoteServiceSettings(); + const auto& establishContext = QSharedPointer::create(mVersion, settings.getServerName()); + send(establishContext); +} diff --git a/src/remote_device/RemoteDispatcherClient.h b/src/remote_device/RemoteDispatcherClient.h new file mode 100644 index 0000000..3ebbec8 --- /dev/null +++ b/src/remote_device/RemoteDispatcherClient.h @@ -0,0 +1,32 @@ +/*! + * \brief Class that dispatches incoming and outgoing remote messages. + * + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "RemoteDispatcher.h" + + +namespace governikus +{ + +class RemoteDispatcherClient + : public RemoteDispatcher +{ + Q_OBJECT + + private: + virtual bool processContext(RemoteCardMessageType pMsgType, const QJsonObject& pMsgObject) override; + + public: + RemoteDispatcherClient(IfdVersion::Version pVersion, const QSharedPointer& pDataChannel); + + Q_INVOKABLE virtual void sendEstablishContext(); + + Q_SIGNALS: + void fireContextEstablished(const QString& pIfdName, const QString& pId); +}; + +} // namespace governikus diff --git a/src/remote_device/RemoteDispatcherImpl.cpp b/src/remote_device/RemoteDispatcherImpl.cpp deleted file mode 100644 index 2658107..0000000 --- a/src/remote_device/RemoteDispatcherImpl.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "RemoteDispatcherImpl.h" - -#include "AppSettings.h" -#include "Env.h" -#include "messages/IfdError.h" -#include "messages/IfdEstablishContext.h" -#include "messages/IfdEstablishContextResponse.h" - -#include -#include - - -Q_DECLARE_LOGGING_CATEGORY(remote_device) - - -namespace governikus -{ -template<> RemoteDispatcher* createNewObject&>(const QSharedPointer& pChannel) -{ - return new RemoteDispatcherImpl(pChannel); -} - - -} /* namespace governikus */ - - -using namespace governikus; - - -void RemoteDispatcherImpl::createAndSendContext(const QJsonObject& pMessageObject) -{ - QString fail; - - if (!mContextHandle.isEmpty()) - { - qCWarning(remote_device) << "Context already established. Additional contexts are not supported"; - fail = QStringLiteral("/al/common#unknownError"); - } - - IfdEstablishContext establishContext(pMessageObject); - if (!establishContext.getProtocol().isSupported()) - { - qCWarning(remote_device) << "Unsupported API protocol requested:" << establishContext.getProtocolRaw(); - fail = QStringLiteral("/al/common#unknownError"); - } - - const auto& settings = AppSettings::getInstance().getRemoteServiceSettings(); - const QString& serverName = settings.getServerName(); - if (!fail.isEmpty()) - { - qCDebug(remote_device) << "Cannot create a new ContextHandle:" << fail; - IfdEstablishContextResponse response(serverName, fail); - mDataChannel->send(response.toJson(QUuid::createUuid().toString()).toJson(QJsonDocument::Compact)); - return; - } - - IfdEstablishContextResponse response(serverName); - mContextHandle = QUuid::createUuid().toString(); - qCDebug(remote_device) << "Creating new ContextHandle:" << mContextHandle; - mDataChannel->send(response.toJson(mContextHandle).toJson(QJsonDocument::Compact)); -} - - -void RemoteDispatcherImpl::saveRemoteNameInSettings(const QString& pName) -{ - RemoteServiceSettings& settings = AppSettings::getInstance().getRemoteServiceSettings(); - auto info = settings.getRemoteInfo(getId()); - info.setName(pName); - settings.updateRemoteInfo(info); -} - - -void RemoteDispatcherImpl::onReceived(const QByteArray& pDataBlock) -{ - const auto& msgObject = RemoteMessage::parseByteArray(pDataBlock); - RemoteMessage remoteMessage(msgObject); - - if (remoteMessage.getType() == RemoteCardMessageType::UNDEFINED) - { - qCWarning(remote_device) << "Invalid message received:" << pDataBlock; - - const QSharedPointer errorMessage(new IfdError(QString(), QStringLiteral("/al/common#unknownAPIFunction"))); - send(errorMessage); - - return; - } - - if (remoteMessage.getType() == RemoteCardMessageType::IFDEstablishContext) - { - IfdEstablishContext establishContext(msgObject); - - saveRemoteNameInSettings(establishContext.getUdName()); - - createAndSendContext(msgObject); - return; - } - - const auto& contextHandle = remoteMessage.getContextHandle(); - if (remoteMessage.getType() == RemoteCardMessageType::IFDEstablishContextResponse) - { - IfdEstablishContextResponse establishContextResponse(msgObject); - if (establishContextResponse.resultHasError()) - { - qCWarning(remote_device) << "Establish context failed with result minor:" << establishContextResponse.getResultMinor(); - return; - } - - mContextHandle = establishContextResponse.getContextHandle(); - qCDebug(remote_device) << "Received new ContextHandle:" << mContextHandle; - } - - if (contextHandle.isEmpty() || mContextHandle != contextHandle) - { - qCWarning(remote_device) << "Invalid context handle received. Expecting:" << mContextHandle << "but got:" << pDataBlock; - return; - } - - qCDebug(remote_device) << "Received message type:" << remoteMessage.getType(); - Q_EMIT fireReceived(mParser.parse(pDataBlock), sharedFromThis()); -} - - -void RemoteDispatcherImpl::close() -{ - mDataChannel->close(); -} - - -void RemoteDispatcherImpl::onClosed(GlobalStatus::Code pCloseCode) -{ - qCDebug(remote_device) << "Connection closed"; - - Q_EMIT fireClosed(pCloseCode, sharedFromThis()); -} - - -RemoteDispatcherImpl::RemoteDispatcherImpl(const QSharedPointer& pDataChannel) - : RemoteDispatcher() - , mDataChannel(pDataChannel) - , mParser() -{ - connect(mDataChannel.data(), &DataChannel::fireClosed, this, &RemoteDispatcherImpl::onClosed); - connect(mDataChannel.data(), &DataChannel::fireReceived, this, &RemoteDispatcherImpl::onReceived); -} - - -RemoteDispatcherImpl::~RemoteDispatcherImpl() -{ - disconnect(mDataChannel.data(), &DataChannel::fireClosed, this, &RemoteDispatcherImpl::onClosed); - disconnect(mDataChannel.data(), &DataChannel::fireReceived, this, &RemoteDispatcherImpl::onReceived); - - mDataChannel->close(); -} - - -const QString& RemoteDispatcherImpl::getId() const -{ - return mDataChannel->getId(); -} - - -const QString& RemoteDispatcherImpl::getContextHandle() const -{ - return mContextHandle; -} - - -void RemoteDispatcherImpl::send(const QSharedPointer& pMessage) -{ - const RemoteCardMessageType messageType = pMessage->getType(); - - qCDebug(remote_device) << "Send message of type:" << messageType; - Q_ASSERT(messageType == RemoteCardMessageType::IFDError || messageType == RemoteCardMessageType::IFDEstablishContext || !mContextHandle.isEmpty()); - - mDataChannel->send(pMessage->toJson(mContextHandle).toJson(QJsonDocument::Compact)); -} diff --git a/src/remote_device/RemoteDispatcherImpl.h b/src/remote_device/RemoteDispatcherImpl.h deleted file mode 100644 index 4e86a40..0000000 --- a/src/remote_device/RemoteDispatcherImpl.h +++ /dev/null @@ -1,43 +0,0 @@ -/*! - * \brief Class that dispatches incoming and outgoing remote messages. - * - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "DataChannel.h" -#include "messages/RemoteMessageParser.h" -#include "RemoteDispatcher.h" - - -namespace governikus -{ -class RemoteDispatcherImpl - : public RemoteDispatcher -{ - Q_OBJECT - - private: - const QSharedPointer mDataChannel; - const RemoteMessageParser mParser; - QString mContextHandle; - - void createAndSendContext(const QJsonObject& pMessageObject); - void saveRemoteNameInSettings(const QString& pName); - - private Q_SLOTS: - void onReceived(const QByteArray& pDataBlock); - void onClosed(GlobalStatus::Code pCloseCode); - - public: - RemoteDispatcherImpl(const QSharedPointer& pDataChannel); - virtual ~RemoteDispatcherImpl() override; - - virtual const QString& getId() const override; - virtual const QString& getContextHandle() const override; - Q_INVOKABLE virtual void close() override; - Q_INVOKABLE virtual void send(const QSharedPointer& pMessage) override; -}; - -} /* namespace governikus */ diff --git a/src/remote_device/RemoteDispatcherServer.cpp b/src/remote_device/RemoteDispatcherServer.cpp new file mode 100644 index 0000000..c3fc82f --- /dev/null +++ b/src/remote_device/RemoteDispatcherServer.cpp @@ -0,0 +1,89 @@ +/*! + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "RemoteDispatcherServer.h" + +#include "AppSettings.h" +#include "Initializer.h" +#include "messages/IfdError.h" +#include "messages/IfdEstablishContext.h" +#include "messages/IfdEstablishContextResponse.h" + +#include +#include + + +Q_DECLARE_LOGGING_CATEGORY(remote_device) + + +namespace governikus +{ +template<> RemoteDispatcherServer* createNewObject&>(const QSharedPointer& pChannel) +{ + return new RemoteDispatcherServer(pChannel); +} + + +} // namespace governikus + + +using namespace governikus; + + +static Initializer::Entry E([] { + qRegisterMetaType >("QSharedPointer"); + }); + + +RemoteDispatcherServer::RemoteDispatcherServer(const QSharedPointer& pDataChannel) + : RemoteDispatcher(IfdVersion::Version::Unknown, pDataChannel) +{ +} + + +void RemoteDispatcherServer::createAndSendContext(const QJsonObject& pMessageObject) +{ + ECardApiResult::Minor fail = ECardApiResult::Minor::null; + + if (!mContextHandle.isEmpty()) + { + qCWarning(remote_device) << "Context already established. Additional contexts are not supported"; + fail = ECardApiResult::Minor::AL_Unknown_Error; + } + + IfdEstablishContext establishContext(pMessageObject); + if (!establishContext.getProtocol().isSupported()) + { + qCWarning(remote_device) << "Unsupported API protocol requested:" << establishContext.getProtocolRaw(); + fail = ECardApiResult::Minor::AL_Unknown_Error; + } + + const auto& settings = Env::getSingleton()->getRemoteServiceSettings(); + const QString& serverName = settings.getServerName(); + if (fail != ECardApiResult::Minor::null) + { + qCDebug(remote_device) << "Cannot create a new ContextHandle:" << fail; + send(QSharedPointer::create(serverName, fail)); + return; + } + + mContextHandle = QUuid::createUuid().toString(); + qCDebug(remote_device) << "Creating new ContextHandle:" << mContextHandle; + send(QSharedPointer::create(serverName)); + Q_EMIT fireContextEstablished(); +} + + +bool RemoteDispatcherServer::processContext(RemoteCardMessageType pMsgType, const QJsonObject& pMsgObject) +{ + if (pMsgType != RemoteCardMessageType::IFDEstablishContext) + { + return false; + } + + IfdEstablishContext establishContext(pMsgObject); + saveRemoteNameInSettings(establishContext.getUdName()); + createAndSendContext(pMsgObject); + return true; +} diff --git a/src/remote_device/RemoteDispatcherServer.h b/src/remote_device/RemoteDispatcherServer.h new file mode 100644 index 0000000..7772a33 --- /dev/null +++ b/src/remote_device/RemoteDispatcherServer.h @@ -0,0 +1,30 @@ +/*! + * \brief Class that dispatches incoming and outgoing remote messages. + * + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "RemoteDispatcher.h" + + +namespace governikus +{ +class RemoteDispatcherServer + : public RemoteDispatcher +{ + Q_OBJECT + + private: + void createAndSendContext(const QJsonObject& pMessageObject); + virtual bool processContext(RemoteCardMessageType pMsgType, const QJsonObject& pMsgObject) override; + + public: + explicit RemoteDispatcherServer(const QSharedPointer& pDataChannel); + + Q_SIGNALS: + void fireContextEstablished(); +}; + +} // namespace governikus diff --git a/src/remote_device/RemoteHelper.cpp b/src/remote_device/RemoteHelper.cpp deleted file mode 100644 index 625a50e..0000000 --- a/src/remote_device/RemoteHelper.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "RemoteHelper.h" - -#include "AppSettings.h" -#include "Env.h" -#include "KeyPair.h" -#include "TlsChecker.h" - -using namespace governikus; - -bool RemoteHelper::checkAndGenerateKey() -{ - auto& settings = Env::getSingleton()->getRemoteServiceSettings(); - - if (settings.getKey().isNull() - || settings.getCertificate().isNull() - || settings.getCertificate().expiryDate() < QDateTime::currentDateTime() - || !TlsChecker::hasValidCertificateKeyLength(settings.getCertificate())) - { - qDebug() << "Generate local keypair..."; - const auto& pair = KeyPair::generate(); - if (pair.isValid()) - { - settings.setKey(pair.getKey()); - settings.setCertificate(pair.getCertificate()); - return true; - } - - return false; - } - - return true; -} diff --git a/src/remote_device/RemoteHelper.h b/src/remote_device/RemoteHelper.h deleted file mode 100644 index 13fc940..0000000 --- a/src/remote_device/RemoteHelper.h +++ /dev/null @@ -1,22 +0,0 @@ -/*! - * \brief Helper for some miscellaneous methods. - * - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -namespace governikus -{ - -class RemoteHelper -{ - private: - RemoteHelper() = delete; - - public: - static bool checkAndGenerateKey(); -}; - - -} /* namespace governikus */ diff --git a/src/remote_device/RemoteReaderAdvertiser.cpp b/src/remote_device/RemoteReaderAdvertiser.cpp index 949b394..6e7f555 100644 --- a/src/remote_device/RemoteReaderAdvertiser.cpp +++ b/src/remote_device/RemoteReaderAdvertiser.cpp @@ -30,7 +30,7 @@ template<> RemoteReaderAdvertiser* createNewObject(false)) , mTimerId(startTimer(pTimerInterval)) - , mDiscovery(Discovery(pIfdName, pIfdId, pPort, {IfdVersion::supported()}).toJson()) + , mDiscovery(Discovery(pIfdName, pIfdId, pPort, {IfdVersion::supported()}).toByteArray()) { qCDebug(remote_device) << "Start advertising every" << pTimerInterval << "msecs"; } diff --git a/src/remote_device/RemoteReaderAdvertiser.h b/src/remote_device/RemoteReaderAdvertiser.h index 8faf29b..4c93751 100644 --- a/src/remote_device/RemoteReaderAdvertiser.h +++ b/src/remote_device/RemoteReaderAdvertiser.h @@ -8,7 +8,8 @@ #pragma once -#include +#include "DatagramHandler.h" + #include #include @@ -16,7 +17,6 @@ namespace governikus { - class RemoteReaderAdvertiser : public QObject { @@ -29,10 +29,6 @@ class RemoteReaderAdvertiser virtual ~RemoteReaderAdvertiser(); }; - -class DatagramHandler; - - class RemoteReaderAdvertiserImpl : public RemoteReaderAdvertiser { @@ -40,7 +36,7 @@ class RemoteReaderAdvertiserImpl const QScopedPointer mHandler; const int mTimerId; - const QJsonDocument mDiscovery; + const QByteArray mDiscovery; void timerEvent(QTimerEvent* pEvent) override; @@ -50,4 +46,4 @@ class RemoteReaderAdvertiserImpl }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/remote_device/RemoteServer.cpp b/src/remote_device/RemoteServer.cpp index f0baeb2..780e0da 100644 --- a/src/remote_device/RemoteServer.cpp +++ b/src/remote_device/RemoteServer.cpp @@ -6,106 +6,10 @@ #include "AppSettings.h" #include "Env.h" -#include "RemoteReaderAdvertiser.h" -#include "RemoteWebSocketServer.h" - using namespace governikus; -namespace governikus -{ - -template<> RemoteServer* createNewObject() -{ - return new RemoteServerImpl; -} - - -} - - RemoteServer::~RemoteServer() { } - - -void RemoteServerImpl::onConnectedChanged(bool pConnected) -{ - if (pConnected) - { - mRemoteReaderAdvertiser.reset(); - return; - } - - if (isRunning() && !pConnected) - { - const auto& ifdName = mWebSocketServer->getServerName(); - const auto& remoteServiceSettings = Env::getSingleton()->getRemoteServiceSettings(); - const auto& ifdId = RemoteServiceSettings::generateFingerprint(remoteServiceSettings.getCertificate()); - quint16 port = mWebSocketServer->getServerPort(); - mRemoteReaderAdvertiser.reset(Env::create(ifdName, ifdId, port)); - return; - } -} - - -RemoteServerImpl::RemoteServerImpl() - : RemoteServer() - , mRemoteReaderAdvertiser() - , mWebSocketServer(Env::create()) -{ - connect(mWebSocketServer.data(), &RemoteWebSocketServer::fireConnectedChanged, this, &RemoteServerImpl::onConnectedChanged); - connect(mWebSocketServer.data(), &RemoteWebSocketServer::fireConnectedChanged, this, &RemoteServerImpl::fireConnectedChanged); - connect(mWebSocketServer.data(), &RemoteWebSocketServer::fireMessageHandlerAdded, this, &RemoteServer::fireMessageHandlerAdded); - connect(mWebSocketServer.data(), &RemoteWebSocketServer::firePskChanged, this, &RemoteServer::firePskChanged); -} - - -bool RemoteServerImpl::isRunning() const -{ - return mWebSocketServer->isListening(); -} - - -bool RemoteServerImpl::start(const QString& pServerName) -{ - bool success = mWebSocketServer->listen(pServerName); - if (success) - { - onConnectedChanged(false); - } - return success; -} - - -void RemoteServerImpl::stop() -{ - mRemoteReaderAdvertiser.reset(); - mWebSocketServer->close(); -} - - -void RemoteServerImpl::setPairing(bool pEnable) -{ - mWebSocketServer->setPairing(pEnable); -} - - -bool RemoteServerImpl::isConnected() const -{ - return mWebSocketServer->isConnected(); -} - - -QSslCertificate RemoteServerImpl::getCurrentCertificate() const -{ - return mWebSocketServer->getCurrentCertificate(); -} - - -const QSharedPointer& RemoteServerImpl::getMessageHandler() const -{ - Q_ASSERT(mWebSocketServer); - return mWebSocketServer->getMessageHandler(); -} diff --git a/src/remote_device/RemoteServer.h b/src/remote_device/RemoteServer.h index 5b1b772..fe9d943 100644 --- a/src/remote_device/RemoteServer.h +++ b/src/remote_device/RemoteServer.h @@ -7,17 +7,17 @@ #pragma once +#include "RemoteWebSocketServer.h" +#include "ServerMessageHandler.h" + #include #include #include #include -#include namespace governikus { -class ServerMessageHandler; - class RemoteServer : public QObject { @@ -43,33 +43,4 @@ class RemoteServer void fireConnectedChanged(bool pConnected); }; - -class RemoteReaderAdvertiser; -class RemoteWebSocketServer; - - -class RemoteServerImpl - : public RemoteServer -{ - Q_OBJECT - - private: - QScopedPointer mRemoteReaderAdvertiser; - QScopedPointer mWebSocketServer; - - private Q_SLOTS: - void onConnectedChanged(bool pConnected); - - public: - RemoteServerImpl(); - - virtual bool isRunning() const override; - virtual bool start(const QString& pServerName) override; - virtual void stop() override; - virtual void setPairing(bool pEnable = true) override; - virtual bool isConnected() const override; - virtual QSslCertificate getCurrentCertificate() const override; - virtual const QSharedPointer& getMessageHandler() const override; -}; - -} /* namespace governikus */ +} // namespace governikus diff --git a/src/remote_device/RemoteServerImpl.cpp b/src/remote_device/RemoteServerImpl.cpp new file mode 100644 index 0000000..cadd5a5 --- /dev/null +++ b/src/remote_device/RemoteServerImpl.cpp @@ -0,0 +1,102 @@ +/*! + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "RemoteServerImpl.h" + +#include "AppSettings.h" +#include "Env.h" + +using namespace governikus; + + +namespace governikus +{ + +template<> RemoteServer* createNewObject() +{ + return new RemoteServerImpl(); +} + + +} // namespace governikus + +void RemoteServerImpl::onConnectedChanged(bool pConnected) +{ + if (pConnected) + { + mRemoteReaderAdvertiser.reset(); + return; + } + + if (isRunning() && !pConnected) + { + const auto& ifdName = mWebSocketServer->getServerName(); + const auto& remoteServiceSettings = Env::getSingleton()->getRemoteServiceSettings(); + const auto& ifdId = RemoteServiceSettings::generateFingerprint(remoteServiceSettings.getCertificate()); + quint16 port = mWebSocketServer->getServerPort(); + mRemoteReaderAdvertiser.reset(Env::create(ifdName, ifdId, port)); + return; + } +} + + +RemoteServerImpl::RemoteServerImpl() + : RemoteServer() + , mRemoteReaderAdvertiser() + , mWebSocketServer(Env::create()) +{ + connect(mWebSocketServer.data(), &RemoteWebSocketServer::fireConnectedChanged, this, &RemoteServerImpl::onConnectedChanged); + connect(mWebSocketServer.data(), &RemoteWebSocketServer::fireConnectedChanged, this, &RemoteServerImpl::fireConnectedChanged); + connect(mWebSocketServer.data(), &RemoteWebSocketServer::fireMessageHandlerAdded, this, &RemoteServer::fireMessageHandlerAdded); + connect(mWebSocketServer.data(), &RemoteWebSocketServer::firePskChanged, this, &RemoteServer::firePskChanged); +} + + +bool RemoteServerImpl::isRunning() const +{ + return mWebSocketServer->isListening(); +} + + +bool RemoteServerImpl::start(const QString& pServerName) +{ + bool success = mWebSocketServer->listen(pServerName); + if (success) + { + onConnectedChanged(false); + } + return success; +} + + +void RemoteServerImpl::stop() +{ + mRemoteReaderAdvertiser.reset(); + mWebSocketServer->close(); +} + + +void RemoteServerImpl::setPairing(bool pEnable) +{ + mWebSocketServer->setPairing(pEnable); +} + + +bool RemoteServerImpl::isConnected() const +{ + return mWebSocketServer->isConnected(); +} + + +QSslCertificate RemoteServerImpl::getCurrentCertificate() const +{ + return mWebSocketServer->getCurrentCertificate(); +} + + +const QSharedPointer& RemoteServerImpl::getMessageHandler() const +{ + Q_ASSERT(mWebSocketServer); + return mWebSocketServer->getMessageHandler(); +} diff --git a/src/remote_device/RemoteServerImpl.h b/src/remote_device/RemoteServerImpl.h new file mode 100644 index 0000000..e833708 --- /dev/null +++ b/src/remote_device/RemoteServerImpl.h @@ -0,0 +1,40 @@ +/*! + * \brief Remote server service to offer remote readers. + * This class controls the advertising over UDP as well as the Websocket connection management. + * + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "RemoteReaderAdvertiser.h" +#include "RemoteServer.h" + +namespace governikus +{ + +class RemoteServerImpl + : public RemoteServer +{ + Q_OBJECT + + private: + QScopedPointer mRemoteReaderAdvertiser; + QScopedPointer mWebSocketServer; + + private Q_SLOTS: + void onConnectedChanged(bool pConnected); + + public: + RemoteServerImpl(); + + virtual bool isRunning() const override; + virtual bool start(const QString& pServerName) override; + virtual void stop() override; + virtual void setPairing(bool pEnable = true) override; + virtual bool isConnected() const override; + virtual QSslCertificate getCurrentCertificate() const override; + virtual const QSharedPointer& getMessageHandler() const override; +}; + +} // namespace governikus diff --git a/src/remote_device/RemoteTlsServer.cpp b/src/remote_device/RemoteTlsServer.cpp index 80d3e4c..26e05b7 100644 --- a/src/remote_device/RemoteTlsServer.cpp +++ b/src/remote_device/RemoteTlsServer.cpp @@ -5,9 +5,8 @@ #include "RemoteTlsServer.h" #include "AppSettings.h" -#include "Env.h" +#include "LogHandler.h" #include "Randomizer.h" -#include "RemoteHelper.h" #include "SecureStorage.h" #include "TlsChecker.h" @@ -39,7 +38,9 @@ bool RemoteTlsServer::listen() return false; } - if (!RemoteHelper::checkAndGenerateKey()) + auto& remoteServiceSettings = Env::getSingleton()->getRemoteServiceSettings(); + const bool invalidLength = !TlsChecker::hasValidCertificateKeyLength(remoteServiceSettings.getCertificate()); + if (!remoteServiceSettings.checkAndGenerateKey(invalidLength)) { qCCritical(remote_device) << "Cannot get required key/certificate for tls"; return false; @@ -143,7 +144,7 @@ void RemoteTlsServer::onSslErrors(const QList& pErrors) void RemoteTlsServer::onEncrypted() { const auto& cfg = mSocket->sslConfiguration(); - TlsChecker::logSslConfig(cfg, qInfo(remote_device)); + TlsChecker::logSslConfig(cfg, spawnMessageLogger(remote_device)); if (!TlsChecker::hasValidCertificateKeyLength(cfg.peerCertificate())) { diff --git a/src/remote_device/RemoteTlsServer.h b/src/remote_device/RemoteTlsServer.h index fddc71e..4ba8ee1 100644 --- a/src/remote_device/RemoteTlsServer.h +++ b/src/remote_device/RemoteTlsServer.h @@ -43,4 +43,4 @@ class RemoteTlsServer void firePskChanged(const QByteArray& pPsk); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/remote_device/RemoteWebSocketServer.cpp b/src/remote_device/RemoteWebSocketServer.cpp index 659f553..47483c3 100644 --- a/src/remote_device/RemoteWebSocketServer.cpp +++ b/src/remote_device/RemoteWebSocketServer.cpp @@ -26,7 +26,7 @@ template<> RemoteWebSocketServer* createNewObject() } -} /* namespace governikus */ +} // namespace governikus using namespace governikus; @@ -67,12 +67,7 @@ void RemoteWebSocketServerImpl::onConnectionClosed() void RemoteWebSocketServerImpl::onServerError(QWebSocketProtocol::CloseCode pCloseCode) { - static int timesLogged = 0; - if (timesLogged < 20) - { - qCCritical(remote_device) << pCloseCode; - timesLogged++; - } + qCCritical(remote_device) << pCloseCode; } diff --git a/src/remote_device/RemoteWebSocketServer.h b/src/remote_device/RemoteWebSocketServer.h index 10db6d6..031e9cf 100644 --- a/src/remote_device/RemoteWebSocketServer.h +++ b/src/remote_device/RemoteWebSocketServer.h @@ -21,7 +21,6 @@ namespace governikus { - class RemoteWebSocketServer : public QObject { @@ -79,4 +78,4 @@ class RemoteWebSocketServerImpl virtual const QSharedPointer& getMessageHandler() const override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/remote_device/ServerMessageHandler.cpp b/src/remote_device/ServerMessageHandler.cpp index 1135531..c5b28af 100644 --- a/src/remote_device/ServerMessageHandler.cpp +++ b/src/remote_device/ServerMessageHandler.cpp @@ -6,7 +6,7 @@ #include "AppSettings.h" #include "Env.h" -#include "messages/GetIfdStatus.h" +#include "FuncUtils.h" #include "messages/IfdConnect.h" #include "messages/IfdConnectResponse.h" #include "messages/IfdDisconnect.h" @@ -15,6 +15,7 @@ #include "messages/IfdEstablishContext.h" #include "messages/IfdEstablishPaceChannel.h" #include "messages/IfdEstablishPaceChannelResponse.h" +#include "messages/IfdGetStatus.h" #include "messages/IfdModifyPin.h" #include "messages/IfdModifyPinResponse.h" #include "messages/IfdStatus.h" @@ -22,7 +23,6 @@ #include "messages/IfdTransmitResponse.h" #include "PinModifyOutput.h" #include "ReaderManager.h" -#include "RemoteDispatcher.h" #include @@ -46,29 +46,33 @@ ServerMessageHandler::~ServerMessageHandler() ServerMessageHandlerImpl::ServerMessageHandlerImpl(const QSharedPointer& pDataChannel) : ServerMessageHandler() - , MessageReceiver() , mReaderManager(Env::getSingleton()) - , mRemoteDispatcher(Env::create(pDataChannel), &QObject::deleteLater) + , mRemoteDispatcher(Env::create(pDataChannel), &QObject::deleteLater) , mCardConnections() { - connect(mRemoteDispatcher.data(), &RemoteDispatcher::fireReceived, this, &ServerMessageHandlerImpl::onReceived); - connect(mRemoteDispatcher.data(), &RemoteDispatcher::fireClosed, this, &ServerMessageHandlerImpl::onClosed); + connect(mRemoteDispatcher.data(), &RemoteDispatcherServer::fireReceived, this, &ServerMessageHandlerImpl::onRemoteMessage); + connect(mRemoteDispatcher.data(), &RemoteDispatcherServer::fireClosed, this, &ServerMessageHandlerImpl::onClosed); - connect(&ReaderManager::getInstance(), &ReaderManager::fireReaderAdded, this, &ServerMessageHandlerImpl::onReaderChanged); - connect(&ReaderManager::getInstance(), &ReaderManager::fireReaderRemoved, this, &ServerMessageHandlerImpl::onReaderRemoved); - connect(&ReaderManager::getInstance(), &ReaderManager::fireReaderPropertiesUpdated, this, &ServerMessageHandlerImpl::onReaderChanged); - connect(&ReaderManager::getInstance(), &ReaderManager::fireCardInserted, this, &ServerMessageHandlerImpl::onReaderChanged); - connect(&ReaderManager::getInstance(), &ReaderManager::fireCardRemoved, this, &ServerMessageHandlerImpl::onReaderChanged); - connect(&ReaderManager::getInstance(), &ReaderManager::fireCardRetryCounterChanged, this, &ServerMessageHandlerImpl::onReaderChanged); + connect(mRemoteDispatcher.data(), &RemoteDispatcherServer::fireContextEstablished, this, [this] { + const auto readerManager = Env::getSingleton(); + connect(readerManager, &ReaderManager::fireReaderAdded, this, &ServerMessageHandlerImpl::onReaderChanged, Qt::UniqueConnection); + connect(readerManager, &ReaderManager::fireReaderRemoved, this, &ServerMessageHandlerImpl::onReaderRemoved, Qt::UniqueConnection); + connect(readerManager, &ReaderManager::fireReaderPropertiesUpdated, this, &ServerMessageHandlerImpl::onReaderChanged, Qt::UniqueConnection); + connect(readerManager, &ReaderManager::fireCardInserted, this, &ServerMessageHandlerImpl::onReaderChanged, Qt::UniqueConnection); + connect(readerManager, &ReaderManager::fireCardRemoved, this, &ServerMessageHandlerImpl::onReaderChanged, Qt::UniqueConnection); + connect(readerManager, &ReaderManager::fireCardRetryCounterChanged, this, &ServerMessageHandlerImpl::onReaderChanged, Qt::UniqueConnection); + }); } -void ServerMessageHandlerImpl::process(const QSharedPointer& pMessage) +void ServerMessageHandlerImpl::handleIfdGetStatus(const QJsonObject& pJsonObject) { - if (!pMessage->getSlotName().isEmpty()) + const IfdGetStatus ifdGetStatus(pJsonObject); + + if (!ifdGetStatus.getSlotName().isEmpty()) { - const auto& readerInfo = mReaderManager->getReaderInfo(pMessage->getSlotName()); - const QSharedPointer ifdStatusMsg(new IfdStatus(readerInfo)); + const auto& readerInfo = mReaderManager->getReaderInfo(ifdGetStatus.getSlotName()); + const auto& ifdStatusMsg = QSharedPointer::create(readerInfo); mRemoteDispatcher->send(ifdStatusMsg); return; @@ -82,41 +86,47 @@ void ServerMessageHandlerImpl::process(const QSharedPointer& continue; } - const QSharedPointer ifdStatusMsg(new IfdStatus(readerInfo)); + const auto& ifdStatusMsg = QSharedPointer::create(readerInfo); mRemoteDispatcher->send(ifdStatusMsg); } } -void ServerMessageHandlerImpl::process(const QSharedPointer& pMessage) +void ServerMessageHandlerImpl::handleIfdConnect(const QJsonObject& pJsonObject) { - const auto& info = mReaderManager->getReaderInfo(pMessage->getSlotName()); + const IfdConnect ifdConnect(pJsonObject); + + const auto& info = mReaderManager->getReaderInfo(ifdConnect.getSlotName()); if (!info.isConnected()) { - qCWarning(remote_device) << "Unknown reader" << pMessage->getSlotName(); - const QSharedPointer response(new IfdConnectResponse(pMessage->getSlotName(), QStringLiteral("/ifdl/terminal#unknownSlot"))); + qCWarning(remote_device) << "Unknown reader" << ifdConnect.getSlotName(); + const auto& response = QSharedPointer::create(ifdConnect.getSlotName(), ECardApiResult::Minor::IFDL_UnknownSlot); mRemoteDispatcher->send(response); return; } if (!info.hasEidCard()) { - qCWarning(remote_device) << "Cannot determine eID card for reader" << pMessage->getSlotName(); - const QSharedPointer response(new IfdConnectResponse(pMessage->getSlotName(), QStringLiteral("/al/common#unknownError"))); + qCWarning(remote_device) << "Cannot determine eID card for reader" << ifdConnect.getSlotName(); + const auto& response = QSharedPointer::create(ifdConnect.getSlotName(), ECardApiResult::Minor::AL_Unknown_Error); mRemoteDispatcher->send(response); return; } - if (mCardConnections.contains(pMessage->getSlotName())) + const auto& connections = mCardConnections.values(); + const auto& slotNames = map, QString>([](const QSharedPointer& c){ + return c ? c->getReaderInfo().getName() : QString(); + }, connections); + if (slotNames.contains(ifdConnect.getSlotName())) { - qCWarning(remote_device) << "Card is already connected" << pMessage->getSlotName(); - const QSharedPointer response(new IfdConnectResponse(pMessage->getSlotName(), QStringLiteral("/al/common#unknownError"))); + qCWarning(remote_device) << "Card is already connected" << ifdConnect.getSlotName(); + const auto& response = QSharedPointer::create(ifdConnect.getSlotName(), ECardApiResult::Minor::IFDL_IFD_SharingViolation); mRemoteDispatcher->send(response); return; } - qCDebug(remote_device) << "Connect card" << pMessage->getSlotName(); - mReaderManager->callCreateCardConnectionCommand(pMessage->getSlotName(), this, &ServerMessageHandlerImpl::onCreateCardConnectionCommandDone); + qCDebug(remote_device) << "Connect card" << ifdConnect.getSlotName(); + mReaderManager->callCreateCardConnectionCommand(ifdConnect.getSlotName(), this, &ServerMessageHandlerImpl::onCreateCardConnectionCommandDone); } @@ -126,7 +136,7 @@ void ServerMessageHandlerImpl::onCreateCardConnectionCommandDone(QSharedPointer< if (pCommand->getCardConnection() == nullptr) { qCWarning(remote_device) << "Cannot connect card" << pCommand->getReaderName(); - const QSharedPointer response(new IfdConnectResponse(pCommand->getReaderName(), QStringLiteral("/al/common#unknownError"))); + const auto& response = QSharedPointer::create(pCommand->getReaderName(), ECardApiResult::Minor::AL_Unknown_Error); mRemoteDispatcher->send(response); return; } @@ -135,63 +145,76 @@ void ServerMessageHandlerImpl::onCreateCardConnectionCommandDone(QSharedPointer< qCInfo(remote_device) << "Card successfully connected" << pCommand->getReaderName() << ", using handle " << slotHandle; mCardConnections.insert(slotHandle, pCommand->getCardConnection()); - const QSharedPointer response(new IfdConnectResponse(slotHandle)); + const auto& response = QSharedPointer::create(slotHandle); mRemoteDispatcher->send(response); } -QString ServerMessageHandlerImpl::convertSlotHandleBackwardsCompatibility(const QString& pSlotHandle) +QString ServerMessageHandlerImpl::slotHandleForReaderName(const QString& pReaderName) const { - if (!mCardConnections.contains(pSlotHandle)) + const auto& slotHandles = mCardConnections.keys(); + for (const auto& slotHandle : slotHandles) { - const auto& slotHandles = mCardConnections.keys(); - for (const auto& slotHandle : slotHandles) + if (mCardConnections[slotHandle]->getReaderInfo().getName() == pReaderName) { - if (mCardConnections[slotHandle]->getReaderInfo().getName() == pSlotHandle) - { - return slotHandle; - } + return slotHandle; } } - return pSlotHandle; + return QString(); } -void ServerMessageHandlerImpl::process(const QSharedPointer& pMessage) +QString ServerMessageHandlerImpl::convertSlotHandleBackwardsCompatibility(const QString& pReaderName) { - QString slotHandle = pMessage->getSlotHandle(); + if (!mCardConnections.contains(pReaderName)) + { + const QString& slotHandle = slotHandleForReaderName(pReaderName); + if (!slotHandle.isEmpty()) + { + return slotHandle; + } + } + return pReaderName; +} + + +void ServerMessageHandlerImpl::handleIfdDisconnect(const QJsonObject& pJsonObject) +{ + const IfdDisconnect ifdDisconnect(pJsonObject); + QString slotHandle = ifdDisconnect.getSlotHandle(); slotHandle = convertSlotHandleBackwardsCompatibility(slotHandle); if (!mCardConnections.contains(slotHandle)) { qCWarning(remote_device) << "Card is not connected" << slotHandle; - const QSharedPointer response(new IfdDisconnectResponse(slotHandle, QStringLiteral("/ifdl/common#invalidSlotHandle"))); + const auto& response = QSharedPointer::create(slotHandle, ECardApiResult::Minor::IFDL_InvalidSlotHandle); mRemoteDispatcher->send(response); return; } mCardConnections.remove(slotHandle); qCInfo(remote_device) << "Card successfully disconnected" << slotHandle; - const QSharedPointer response(new IfdDisconnectResponse(slotHandle)); + const auto& response = QSharedPointer::create(slotHandle); mRemoteDispatcher->send(response); } -void ServerMessageHandlerImpl::process(const QSharedPointer& pMessage) +void ServerMessageHandlerImpl::handleIfdTransmit(const QJsonObject& pJsonObject) { - QString slotHandle = pMessage->getSlotHandle(); + const IfdTransmit ifdTransmit(pJsonObject); + QString slotHandle = ifdTransmit.getSlotHandle(); slotHandle = convertSlotHandleBackwardsCompatibility(slotHandle); if (!mCardConnections.contains(slotHandle)) { qCWarning(remote_device) << "Card is not connected" << slotHandle; - const QSharedPointer response(new IfdTransmitResponse(slotHandle, QByteArray(), QStringLiteral("/ifdl/common#invalidSlotHandle"))); + const auto& response = QSharedPointer::create(slotHandle, QByteArray(), ECardApiResult::Minor::IFDL_InvalidSlotHandle); mRemoteDispatcher->send(response); return; } const QSharedPointer& cardConnection = mCardConnections.value(slotHandle); - const auto& commandApdu = pMessage->getInputApdu(); + const auto& commandApdu = ifdTransmit.getInputApdu(); const bool pinPadMode = Env::getSingleton()->getRemoteServiceSettings().getPinPadMode(); if (pinPadMode && CommandApdu::isSecureMessaging(commandApdu)) @@ -200,67 +223,77 @@ void ServerMessageHandlerImpl::process(const QSharedPointer& if (stopped) { qCDebug(remote_device) << "The eService has established Secure Messaging. Stopping local Secure Messaging."; + Q_EMIT fireSecureMessagingStopped(); } } qCDebug(remote_device) << "Transmit card APDU for" << slotHandle; - InputAPDUInfo inputApduInfo(commandApdu, MSEBuilder::isUpdateRetryCounterCommand(commandApdu)); + InputAPDUInfo inputApduInfo(commandApdu); cardConnection->callTransmitCommand(this, &ServerMessageHandlerImpl::onTransmitCardCommandDone, {inputApduInfo}, slotHandle); } -void ServerMessageHandlerImpl::process(const QSharedPointer& pMessage) +void ServerMessageHandlerImpl::handleIfdEstablishPaceChannel(const QJsonObject& pJsonObject) { - QString slotHandle = pMessage->getSlotHandle(); + const auto& ifdEstablishPaceChannel = QSharedPointer::create(pJsonObject); + QString slotHandle = ifdEstablishPaceChannel->getSlotHandle(); slotHandle = convertSlotHandleBackwardsCompatibility(slotHandle); - const bool pinPadMode = Env::getSingleton()->getRemoteServiceSettings().getPinPadMode(); - if (!pinPadMode) - { - qCWarning(remote_device) << "EstablishPaceChannel is only available in pin pad mode."; - const QSharedPointer response(new IfdEstablishPaceChannelResponse(slotHandle, QByteArray(), QStringLiteral("/al/common#unknownError"))); - mRemoteDispatcher->send(response); - return; - } - if (!mCardConnections.contains(slotHandle)) { qCWarning(remote_device) << "Card is not connected" << slotHandle; - const QSharedPointer response(new IfdEstablishPaceChannelResponse(slotHandle, QByteArray(), QStringLiteral("/ifdl/common#invalidSlotHandle"))); + const auto& response = QSharedPointer::create(slotHandle, QByteArray(), ECardApiResult::Minor::IFDL_InvalidSlotHandle); mRemoteDispatcher->send(response); return; } - QSharedPointer connection = mCardConnections[slotHandle]; - Q_EMIT fireEstablishPaceChannel(pMessage, connection); + QSharedPointer cardConnection = mCardConnections[slotHandle]; + + const bool isBasicReader = cardConnection->getReaderInfo().isBasicReader(); + const bool pinPadMode = Env::getSingleton()->getRemoteServiceSettings().getPinPadMode(); + if (isBasicReader && !pinPadMode) + { + qCWarning(remote_device) << "EstablishPaceChannel is only available in pin pad mode."; + const auto& response = QSharedPointer::create(slotHandle, QByteArray(), ECardApiResult::Minor::AL_Unknown_Error); + mRemoteDispatcher->send(response); + return; + } + + Q_EMIT fireEstablishPaceChannel(ifdEstablishPaceChannel, cardConnection); } -void ServerMessageHandlerImpl::sendEstablishPaceChannelResponse(const QString& pSlotHandle, const EstablishPACEChannelOutput& pChannelOutput) +void ServerMessageHandlerImpl::sendEstablishPaceChannelResponse(const QString& pSlotHandle, const EstablishPaceChannelOutput& pChannelOutput) { const QByteArray& ccid = pChannelOutput.toCcid(); if (pChannelOutput.getPaceReturnCode() == CardReturnCode::UNKNOWN) { - const QSharedPointer response(new IfdEstablishPaceChannelResponse(pSlotHandle, ccid, QStringLiteral("/al/common#unknownError"))); + const auto& response = QSharedPointer::create(pSlotHandle, ccid, ECardApiResult::Minor::AL_Unknown_Error); mRemoteDispatcher->send(response); return; } - const QSharedPointer response(new IfdEstablishPaceChannelResponse(pSlotHandle, ccid)); + if (pChannelOutput.getPaceReturnCode() == CardReturnCode::CARD_NOT_FOUND) + { + const auto& response = QSharedPointer::create(pSlotHandle, ccid, ECardApiResult::Minor::IFDL_Terminal_NoCard); + mRemoteDispatcher->send(response); + return; + } + + const auto& response = QSharedPointer::create(pSlotHandle, ccid); mRemoteDispatcher->send(response); } -void ServerMessageHandlerImpl::process(const QSharedPointer& pMessage) +void ServerMessageHandlerImpl::handleIfdModifyPIN(const QJsonObject& pJsonObject) { - QString slotHandle = pMessage->getSlotHandle(); - slotHandle = convertSlotHandleBackwardsCompatibility(slotHandle); - + const auto& ifdModifyPin = QSharedPointer::create(pJsonObject); + const QString slotHandle = ifdModifyPin->getSlotHandle(); const bool pinPadMode = Env::getSingleton()->getRemoteServiceSettings().getPinPadMode(); if (!pinPadMode) { qCWarning(remote_device) << "ModifyPin is only available in pin pad mode."; - const QSharedPointer response(new IfdModifyPinResponse(slotHandle, QByteArray(), QStringLiteral("/al/common#unknownError"))); + const auto& response = QSharedPointer::create(slotHandle, QByteArray(), ECardApiResult::Minor::AL_Unknown_Error); mRemoteDispatcher->send(response); return; } @@ -268,12 +301,12 @@ void ServerMessageHandlerImpl::process(const QSharedPointer& if (!mCardConnections.contains(slotHandle)) { qCWarning(remote_device) << "Card is not connected" << slotHandle; - const QSharedPointer response(new IfdModifyPinResponse(slotHandle, QByteArray(), QStringLiteral("/ifdl/common#invalidSlotHandle"))); + const auto& response = QSharedPointer::create(slotHandle, QByteArray(), ECardApiResult::Minor::IFDL_InvalidSlotHandle); mRemoteDispatcher->send(response); return; } - Q_EMIT fireModifyPin(pMessage, mCardConnections[slotHandle]); + Q_EMIT fireModifyPin(ifdModifyPin, mCardConnections[slotHandle]); } @@ -282,37 +315,37 @@ void ServerMessageHandlerImpl::sendModifyPinResponse(const QString& pSlotHandle, PinModifyOutput pinModifyOutput(pResponseApdu); const QByteArray& ccid = pinModifyOutput.toCcid(); - QString minor; + ECardApiResult::Minor minor = ECardApiResult::Minor::null; switch (pResponseApdu.getReturnCode()) { case StatusCode::SUCCESS: break; case StatusCode::EMPTY: - minor = QStringLiteral("/ifdl/terminal#noCard"); + minor = ECardApiResult::Minor::IFDL_Terminal_NoCard; break; case StatusCode::INPUT_TIMEOUT: - minor = QStringLiteral("/ifdl/common#timeoutError"); + minor = ECardApiResult::Minor::IFDL_Timeout_Error; break; case StatusCode::INPUT_CANCELLED: - minor = QStringLiteral("/ifdl#cancellationByUser"); + minor = ECardApiResult::Minor::IFDL_CancellationByUser; break; case StatusCode::PASSWORDS_DIFFER: - minor = QStringLiteral("/ifdl/IO#repeatedDataMismatch"); + minor = ECardApiResult::Minor::IFDL_IO_RepeatedDataMismatch; break; case StatusCode::PASSWORD_OUTOF_RANGE: - minor = QStringLiteral("/ifdl/IO#unknownPINFormat"); + minor = ECardApiResult::Minor::IFDL_IO_UnknownPINFormat; break; default: - minor = QStringLiteral("/al/common#unknownError"); + minor = ECardApiResult::Minor::AL_Unknown_Error; } - const QSharedPointer response(new IfdModifyPinResponse(pSlotHandle, ccid, minor)); + const auto& response = QSharedPointer::create(pSlotHandle, ccid, minor); mRemoteDispatcher->send(response); } @@ -326,7 +359,7 @@ void ServerMessageHandlerImpl::onTransmitCardCommandDone(QSharedPointergetReturnCode() != CardReturnCode::OK) { qCWarning(remote_device) << "Card transmit for" << slotHandle << "failed" << transmitCommand->getReturnCode(); - QSharedPointer response(new IfdTransmitResponse(slotHandle, QByteArray(), QStringLiteral("/al/common#unknownError"))); + const auto& response = QSharedPointer::create(slotHandle, QByteArray(), ECardApiResult::Minor::AL_Unknown_Error); mRemoteDispatcher->send(response); return; } @@ -338,29 +371,11 @@ void ServerMessageHandlerImpl::onTransmitCardCommandDone(QSharedPointergetOutputApduAsHex().first()); } qCInfo(remote_device) << "Card transmit succeeded" << slotHandle; - QSharedPointer response(new IfdTransmitResponse(slotHandle, responseApdu)); + const auto& response = QSharedPointer::create(slotHandle, responseApdu); mRemoteDispatcher->send(response); } -void ServerMessageHandlerImpl::unprocessed(const QSharedPointer& pMessage) -{ - unexpectedMessage(pMessage); -} - - -void ServerMessageHandlerImpl::unexpectedMessage(const QSharedPointer& pMessage, bool pSendMessage) -{ - qCWarning(remote_device) << "Received an unexpected message of type:" << pMessage->getType(); - - if (pSendMessage) - { - const QSharedPointer errorMessage(new IfdError(QString(), QStringLiteral("/al/common#unknownAPIFunction"))); - QMetaObject::invokeMethod(mRemoteDispatcher.data(), "send", Qt::QueuedConnection, Q_ARG(QSharedPointer, errorMessage)); - } -} - - void ServerMessageHandlerImpl::onClosed() { mCardConnections.clear(); @@ -369,44 +384,79 @@ void ServerMessageHandlerImpl::onClosed() } -void ServerMessageHandlerImpl::onReceived(const QSharedPointer& pMessage) +void ServerMessageHandlerImpl::onRemoteMessage(RemoteCardMessageType pMessageType, const QJsonObject pJsonObject) { - const QVector serverMessageTypes({ - RemoteCardMessageType::IFDStatus, - RemoteCardMessageType::IFDConnectResponse, - RemoteCardMessageType::IFDDisconnectResponse, - RemoteCardMessageType::IFDTransmitResponse, - RemoteCardMessageType::IFDEstablishPACEChannelResponse, - RemoteCardMessageType::IFDModifyPINResponse - }); - - if (serverMessageTypes.contains(pMessage->getType())) + switch (pMessageType) { - unexpectedMessage(pMessage, true); - return; - } + case RemoteCardMessageType::IFDError: + case RemoteCardMessageType::UNDEFINED: + case RemoteCardMessageType::IFDEstablishContext: + case RemoteCardMessageType::IFDEstablishContextResponse: + break; - receive(pMessage); + case RemoteCardMessageType::IFDStatus: + case RemoteCardMessageType::IFDConnectResponse: + case RemoteCardMessageType::IFDDisconnectResponse: + case RemoteCardMessageType::IFDTransmitResponse: + case RemoteCardMessageType::IFDEstablishPACEChannelResponse: + case RemoteCardMessageType::IFDModifyPINResponse: + { + qCWarning(remote_device) << "Received an unexpected message of type:" << pMessageType; + const auto& localCopy = mRemoteDispatcher; + QMetaObject::invokeMethod(localCopy.data(), [localCopy] { + const auto& errorMessage = QSharedPointer::create(QString(), ECardApiResult::Minor::AL_Unkown_API_Function); + localCopy->send(errorMessage); + }, Qt::QueuedConnection); + break; + } + + case RemoteCardMessageType::IFDGetStatus: + handleIfdGetStatus(pJsonObject); + break; + + case RemoteCardMessageType::IFDConnect: + handleIfdConnect(pJsonObject); + break; + + case RemoteCardMessageType::IFDTransmit: + handleIfdTransmit(pJsonObject); + break; + + case RemoteCardMessageType::IFDDisconnect: + handleIfdDisconnect(pJsonObject); + break; + + case RemoteCardMessageType::IFDEstablishPACEChannel: + handleIfdEstablishPaceChannel(pJsonObject); + break; + + case RemoteCardMessageType::IFDModifyPIN: + handleIfdModifyPIN(pJsonObject); + break; + } } void ServerMessageHandlerImpl::onReaderChanged(const QString& pReaderName) { ReaderInfo info = mReaderManager->getReaderInfo(pReaderName); - if (!info.hasEidCard() && mCardConnections.contains(pReaderName)) + if (!info.hasEidCard()) { - mCardConnections.remove(pReaderName); - qCInfo(remote_device) << "Removed CardConnection for" << pReaderName; + const QString& slotHandle = slotHandleForReaderName(pReaderName); + if (mCardConnections.remove(slotHandle) > 0) + { + qCInfo(remote_device) << "Removed CardConnection for" << slotHandle; + } } - mRemoteDispatcher->send(QSharedPointer(new IfdStatus(info))); + mRemoteDispatcher->send(QSharedPointer::create(info)); } void ServerMessageHandlerImpl::onReaderRemoved(const QString& pReaderName) { - mRemoteDispatcher->send(QSharedPointer(new IfdStatus(pReaderName))); + mRemoteDispatcher->send(QSharedPointer::create(pReaderName)); } -} /* namespace governikus */ +} // namespace governikus diff --git a/src/remote_device/ServerMessageHandler.h b/src/remote_device/ServerMessageHandler.h index 53efd1a..a418844 100644 --- a/src/remote_device/ServerMessageHandler.h +++ b/src/remote_device/ServerMessageHandler.h @@ -6,11 +6,13 @@ #pragma once +#include "command/CreateCardConnectionCommand.h" #include "CardConnection.h" #include "DataChannel.h" -#include "messages/MessageReceiver.h" +#include "messages/IfdEstablishPaceChannel.h" +#include "messages/IfdModifyPin.h" #include "ReaderManager.h" -#include "RemoteDispatcher.h" +#include "RemoteDispatcherServer.h" #include #include @@ -20,8 +22,6 @@ namespace governikus { -class CreateCardConnectionCommand; - class ServerMessageHandler : public QObject { @@ -33,53 +33,51 @@ class ServerMessageHandler public: virtual ~ServerMessageHandler(); - virtual void sendEstablishPaceChannelResponse(const QString& pSlotHandle, const EstablishPACEChannelOutput&) = 0; + virtual void sendEstablishPaceChannelResponse(const QString& pSlotHandle, const EstablishPaceChannelOutput&) = 0; virtual void sendModifyPinResponse(const QString& pSlotHandle, const ResponseApdu& pResponseApdu) = 0; Q_SIGNALS: void fireEstablishPaceChannel(const QSharedPointer& pMessage, const QSharedPointer& pConnection); void fireModifyPin(const QSharedPointer& pMessage, const QSharedPointer& pConnection); void fireClosed(); + void fireSecureMessagingStopped(); }; class ServerMessageHandlerImpl : public ServerMessageHandler - , public MessageReceiver { Q_OBJECT private: QPointer mReaderManager; - const QSharedPointer mRemoteDispatcher; + const QSharedPointer mRemoteDispatcher; QMap > mCardConnections; - QString convertSlotHandleBackwardsCompatibility(const QString& pSlotHandle); + QString slotHandleForReaderName(const QString& pReaderName) const; + QString convertSlotHandleBackwardsCompatibility(const QString& pReaderName); - virtual void process(const QSharedPointer& pMessage) override; - virtual void process(const QSharedPointer& pMessage) override; - virtual void process(const QSharedPointer& pMessage) override; - virtual void process(const QSharedPointer& pMessage) override; - virtual void process(const QSharedPointer& pMessage) override; - virtual void process(const QSharedPointer& pMessage) override; - - virtual void unprocessed(const QSharedPointer& pMessage) override; - void unexpectedMessage(const QSharedPointer& pMessage, bool pSendMessage = false); + void handleIfdGetStatus(const QJsonObject& pJsonObject); + void handleIfdConnect(const QJsonObject& pJsonObject); + void handleIfdDisconnect(const QJsonObject& pJsonObject); + void handleIfdTransmit(const QJsonObject& pJsonObject); + void handleIfdEstablishPaceChannel(const QJsonObject& pJsonObject); + void handleIfdModifyPIN(const QJsonObject& pJsonObject); private Q_SLOTS: void onCreateCardConnectionCommandDone(QSharedPointer pCommand); void onTransmitCardCommandDone(QSharedPointer pCommand); void onClosed(); - void onReceived(const QSharedPointer& pMessage); + void onRemoteMessage(RemoteCardMessageType pMessageType, const QJsonObject pJsonObject); void onReaderChanged(const QString& pReaderName); void onReaderRemoved(const QString& pReaderName); public: ServerMessageHandlerImpl(const QSharedPointer& pDataChannel); - virtual void sendEstablishPaceChannelResponse(const QString& pSlotHandle, const EstablishPACEChannelOutput& pChannelOutput) override; + virtual void sendEstablishPaceChannelResponse(const QString& pSlotHandle, const EstablishPaceChannelOutput& pChannelOutput) override; virtual void sendModifyPinResponse(const QString& pSlotHandle, const ResponseApdu& pResponseApdu) override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/remote_device/WebSocketChannel.cpp b/src/remote_device/WebSocketChannel.cpp index ecd139a..b64ab19 100644 --- a/src/remote_device/WebSocketChannel.cpp +++ b/src/remote_device/WebSocketChannel.cpp @@ -18,7 +18,7 @@ using namespace governikus; namespace { const int PING_PONG_TIMEOUT_MS = 5000; -} +} // namespace QString WebSocketChannel::makeConnectionId(const QSharedPointer& pConnection) diff --git a/src/remote_device/WebSocketChannel.h b/src/remote_device/WebSocketChannel.h index a260f8f..de5a4b9 100644 --- a/src/remote_device/WebSocketChannel.h +++ b/src/remote_device/WebSocketChannel.h @@ -43,4 +43,4 @@ class WebSocketChannel void onPongTimeout(); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/remote_device/messages/Discovery.cpp b/src/remote_device/messages/Discovery.cpp index 52c1c24..c247f44 100644 --- a/src/remote_device/messages/Discovery.cpp +++ b/src/remote_device/messages/Discovery.cpp @@ -18,13 +18,24 @@ Q_DECLARE_LOGGING_CATEGORY(remote_device) using namespace governikus; +namespace +{ +VALUE_NAME(MSG_TYPE, "msg") +VALUE_NAME(IFD_NAME, "IFDName") +VALUE_NAME(IFD_ID, "IFDID") +VALUE_NAME(PORT, "port") +VALUE_NAME(SUPPORTED_API, "SupportedAPI") +} // namespace + + static Initializer::Entry E([] { qRegisterMetaType >("QSharedPointer"); }); Discovery::Discovery(const QString& pIfdName, const QString& pIfdId, quint16 pPort, const QVector& pSupportedApis) - : mIfdName(pIfdName) + : RemoteMessage(RemoteCardMessageType::UNDEFINED) + , mIfdName(pIfdName) , mIfdId(pIfdId) , mPort(pPort) , mSupportedApis(pSupportedApis) @@ -32,6 +43,54 @@ Discovery::Discovery(const QString& pIfdName, const QString& pIfdId, quint16 pPo } +Discovery::Discovery(const QJsonObject& pMessageObject) + : RemoteMessage(RemoteCardMessageType::UNDEFINED) + , mIfdName() + , mIfdId() + , mPort() + , mSupportedApis() +{ + if (getStringValue(pMessageObject, MSG_TYPE()) != QLatin1String("REMOTE_IFD")) + { + markIncomplete(QStringLiteral("The value of msg should be REMOTE_IFD")); + } + + mIfdName = getStringValue(pMessageObject, IFD_NAME()); + mIfdId = getStringValue(pMessageObject, IFD_ID()); + mPort = static_cast(getIntValue(pMessageObject, PORT())); + + if (!pMessageObject.contains(SUPPORTED_API())) + { + missingValue(SUPPORTED_API()); + return; + } + + const auto& value = pMessageObject.value(SUPPORTED_API()); + if (!value.isArray()) + { + invalidType(SUPPORTED_API(), QLatin1String("array")); + return; + } + + const auto& array = value.toArray(); + for (const auto& entry : array) + { + if (entry.isString()) + { + mSupportedApis += IfdVersion(entry.toString()).getVersion(); + continue; + } + + invalidType(SUPPORTED_API(), QLatin1String("string array")); + } +} + + +Discovery::~Discovery() +{ +} + + const QString& Discovery::getIfdName() const { return mIfdName; @@ -56,21 +115,21 @@ const QVector& Discovery::getSupportedApis() const } -QJsonDocument Discovery::toJson() const +QByteArray Discovery::toByteArray(const QString&) const { QJsonObject result; - result[QLatin1String("IFDName")] = mIfdName; - result[QLatin1String("IFDID")] = mIfdId; - result[QLatin1String("msg")] = QStringLiteral("REMOTE_IFD"); - result[QLatin1String("port")] = mPort; + result[IFD_NAME()] = mIfdName; + result[IFD_ID()] = mIfdId; + result[MSG_TYPE()] = QStringLiteral("REMOTE_IFD"); + result[PORT()] = mPort; QJsonArray levels; for (const auto& level : qAsConst(mSupportedApis)) { levels += IfdVersion(level).toString(); } - result[QLatin1String("SupportedAPI")] = levels; + result[SUPPORTED_API()] = levels; - return QJsonDocument(result); + return RemoteMessage::toByteArray(result); } diff --git a/src/remote_device/messages/Discovery.h b/src/remote_device/messages/Discovery.h index a822e23..e59947c 100644 --- a/src/remote_device/messages/Discovery.h +++ b/src/remote_device/messages/Discovery.h @@ -13,23 +13,26 @@ namespace governikus { class Discovery + : public RemoteMessage { private: - const QString mIfdName; - const QString mIfdId; - const quint16 mPort; - const QVector mSupportedApis; + QString mIfdName; + QString mIfdId; + quint16 mPort; + QVector mSupportedApis; public: Discovery(const QString& pIfdName, const QString& pIfdId, quint16 pPort, const QVector& pSupportedApis); - ~Discovery() = default; + Discovery(const QJsonObject& pMessageObject); + virtual ~Discovery() override; const QString& getIfdName() const; const QString& getIfdId() const; quint16 getPort() const; const QVector& getSupportedApis() const; - QJsonDocument toJson() const; + + virtual QByteArray toByteArray(const QString& pContextHandle = QString()) const override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/remote_device/messages/GetIfdStatus.cpp b/src/remote_device/messages/GetIfdStatus.cpp deleted file mode 100644 index 5f5e483..0000000 --- a/src/remote_device/messages/GetIfdStatus.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - - -#include "GetIfdStatus.h" - -#include -#include - - -Q_DECLARE_LOGGING_CATEGORY(remote_device) - - -using namespace governikus; - - -namespace -{ -VALUE_NAME(SLOT_NAME, "SlotName") -} - - -GetIfdStatus::GetIfdStatus(const QString& pSlotName) - : RemoteMessage(RemoteCardMessageType::IFDGetStatus) - , mSlotName(pSlotName) -{ -} - - -GetIfdStatus::GetIfdStatus(const QJsonObject& pMessageObject) - : RemoteMessage(pMessageObject) - , mSlotName() -{ - mSlotName = getStringValue(pMessageObject, SLOT_NAME()); -} - - -const QString& GetIfdStatus::getSlotName() const -{ - return mSlotName; -} - - -QJsonDocument GetIfdStatus::toJson(const QString& pContextHandle) const -{ - QJsonObject result = createMessageBody(pContextHandle); - - result[SLOT_NAME()] = mSlotName; - - return QJsonDocument(result); -} diff --git a/src/remote_device/messages/GetIfdStatus.h b/src/remote_device/messages/GetIfdStatus.h deleted file mode 100644 index 12f7929..0000000 --- a/src/remote_device/messages/GetIfdStatus.h +++ /dev/null @@ -1,30 +0,0 @@ -/*! - * \brief Classes that model the GetIFDStatus message. - * - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "RemoteMessage.h" - - -namespace governikus -{ -class GetIfdStatus - : public RemoteMessage -{ - private: - QString mSlotName; - - public: - GetIfdStatus(const QString& pSlotName = QString()); - GetIfdStatus(const QJsonObject& pMessageObject); - virtual ~GetIfdStatus() override = default; - - const QString& getSlotName() const; - virtual QJsonDocument toJson(const QString& pContextHandle) const override; -}; - - -} /* namespace governikus */ diff --git a/src/remote_device/messages/IfdConnect.cpp b/src/remote_device/messages/IfdConnect.cpp index ac4f321..d509f2a 100644 --- a/src/remote_device/messages/IfdConnect.cpp +++ b/src/remote_device/messages/IfdConnect.cpp @@ -19,7 +19,7 @@ namespace { VALUE_NAME(SLOT_NAME, "SlotName") VALUE_NAME(EXCLUSIVE, "exclusive") -} +} // namespace IfdConnect::IfdConnect(const QString& pSlotName, bool pExclusive) @@ -37,6 +37,11 @@ IfdConnect::IfdConnect(const QJsonObject& pMessageObject) { mSlotName = getStringValue(pMessageObject, SLOT_NAME()); mExclusive = getBoolValue(pMessageObject, EXCLUSIVE()); + + if (getType() != RemoteCardMessageType::IFDConnect) + { + markIncomplete(QStringLiteral("The value of msg should be IFDConnect")); + } } @@ -52,12 +57,12 @@ bool IfdConnect::isExclusive() const } -QJsonDocument IfdConnect::toJson(const QString& pContextHandle) const +QByteArray IfdConnect::toByteArray(const QString& pContextHandle) const { QJsonObject result = createMessageBody(pContextHandle); result[SLOT_NAME()] = mSlotName; result[EXCLUSIVE()] = mExclusive; - return QJsonDocument(result); + return RemoteMessage::toByteArray(result); } diff --git a/src/remote_device/messages/IfdConnect.h b/src/remote_device/messages/IfdConnect.h index b8ba257..0d8c7c3 100644 --- a/src/remote_device/messages/IfdConnect.h +++ b/src/remote_device/messages/IfdConnect.h @@ -23,8 +23,8 @@ class IfdConnect const QString& getSlotName() const; bool isExclusive() const; - virtual QJsonDocument toJson(const QString& pContextHandle) const override; + virtual QByteArray toByteArray(const QString& pContextHandle) const override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/remote_device/messages/IfdConnectResponse.cpp b/src/remote_device/messages/IfdConnectResponse.cpp index 1fe5198..7e6cb64 100644 --- a/src/remote_device/messages/IfdConnectResponse.cpp +++ b/src/remote_device/messages/IfdConnectResponse.cpp @@ -18,10 +18,10 @@ using namespace governikus; namespace { VALUE_NAME(SLOT_HANDLE, "SlotHandle") -} +} // namespace -IfdConnectResponse::IfdConnectResponse(const QString& pSlotHandle, const QString& pResultMinor) +IfdConnectResponse::IfdConnectResponse(const QString& pSlotHandle, ECardApiResult::Minor pResultMinor) : RemoteMessageResponse(RemoteCardMessageType::IFDConnectResponse, pResultMinor) , mSlotHandle(pSlotHandle) { @@ -34,6 +34,11 @@ IfdConnectResponse::IfdConnectResponse(const QJsonObject& pMessageObject) { mSlotHandle = getStringValue(pMessageObject, SLOT_HANDLE()); mError = pMessageObject.value(QLatin1String("error")).toString(); + + if (getType() != RemoteCardMessageType::IFDConnectResponse) + { + markIncomplete(QStringLiteral("The value of msg should be IFDConnectResponse")); + } } @@ -43,11 +48,11 @@ const QString& IfdConnectResponse::getSlotHandle() const } -QJsonDocument IfdConnectResponse::toJson(const QString& pContextHandle) const +QByteArray IfdConnectResponse::toByteArray(const QString& pContextHandle) const { QJsonObject result = createMessageBody(pContextHandle); result[SLOT_HANDLE()] = mSlotHandle; - return QJsonDocument(result); + return RemoteMessage::toByteArray(result); } diff --git a/src/remote_device/messages/IfdConnectResponse.h b/src/remote_device/messages/IfdConnectResponse.h index b17cf8c..dfa95e2 100644 --- a/src/remote_device/messages/IfdConnectResponse.h +++ b/src/remote_device/messages/IfdConnectResponse.h @@ -17,13 +17,13 @@ class IfdConnectResponse QString mError; public: - IfdConnectResponse(const QString& pSlotHandle, const QString& pResultMinor = QString()); + IfdConnectResponse(const QString& pSlotHandle, ECardApiResult::Minor pResultMinor = ECardApiResult::Minor::null); IfdConnectResponse(const QJsonObject& pMessageObject); virtual ~IfdConnectResponse() override = default; const QString& getSlotHandle() const; - virtual QJsonDocument toJson(const QString& pContextHandle) const override; + virtual QByteArray toByteArray(const QString& pContextHandle) const override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/remote_device/messages/IfdDisconnect.cpp b/src/remote_device/messages/IfdDisconnect.cpp index fcf3d0c..bcb2e9f 100644 --- a/src/remote_device/messages/IfdDisconnect.cpp +++ b/src/remote_device/messages/IfdDisconnect.cpp @@ -18,7 +18,7 @@ using namespace governikus; namespace { VALUE_NAME(SLOT_HANDLE, "SlotHandle") -} +} // namespace IfdDisconnect::IfdDisconnect(const QString& pSlotHandle) @@ -33,6 +33,11 @@ IfdDisconnect::IfdDisconnect(const QJsonObject& pMessageObject) , mSlotHandle() { mSlotHandle = getStringValue(pMessageObject, SLOT_HANDLE()); + + if (getType() != RemoteCardMessageType::IFDDisconnect) + { + markIncomplete(QStringLiteral("The value of msg should be IFDDisconnect")); + } } @@ -42,11 +47,11 @@ const QString& IfdDisconnect::getSlotHandle() const } -QJsonDocument IfdDisconnect::toJson(const QString& pContextHandle) const +QByteArray IfdDisconnect::toByteArray(const QString& pContextHandle) const { QJsonObject result = createMessageBody(pContextHandle); result[SLOT_HANDLE()] = mSlotHandle; - return QJsonDocument(result); + return RemoteMessage::toByteArray(result); } diff --git a/src/remote_device/messages/IfdDisconnect.h b/src/remote_device/messages/IfdDisconnect.h index 092510d..516d3be 100644 --- a/src/remote_device/messages/IfdDisconnect.h +++ b/src/remote_device/messages/IfdDisconnect.h @@ -21,8 +21,8 @@ class IfdDisconnect virtual ~IfdDisconnect() override = default; const QString& getSlotHandle() const; - virtual QJsonDocument toJson(const QString& pContextHandle) const override; + virtual QByteArray toByteArray(const QString& pContextHandle) const override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/remote_device/messages/IfdDisconnectResponse.cpp b/src/remote_device/messages/IfdDisconnectResponse.cpp index bfebb7a..1d76777 100644 --- a/src/remote_device/messages/IfdDisconnectResponse.cpp +++ b/src/remote_device/messages/IfdDisconnectResponse.cpp @@ -18,10 +18,10 @@ using namespace governikus; namespace { VALUE_NAME(SLOT_HANDLE, "SlotHandle") -} +} // namespace -IfdDisconnectResponse::IfdDisconnectResponse(const QString& pSlotHandle, const QString& pResultMinor) +IfdDisconnectResponse::IfdDisconnectResponse(const QString& pSlotHandle, ECardApiResult::Minor pResultMinor) : RemoteMessageResponse(RemoteCardMessageType::IFDDisconnectResponse, pResultMinor) , mSlotHandle(pSlotHandle) { @@ -33,6 +33,11 @@ IfdDisconnectResponse::IfdDisconnectResponse(const QJsonObject& pMessageObject) , mSlotHandle() { mSlotHandle = getStringValue(pMessageObject, SLOT_HANDLE()); + + if (getType() != RemoteCardMessageType::IFDDisconnectResponse) + { + markIncomplete(QStringLiteral("The value of msg should be IFDDisconnectResponse")); + } } @@ -42,11 +47,11 @@ const QString& IfdDisconnectResponse::getSlotHandle() const } -QJsonDocument IfdDisconnectResponse::toJson(const QString& pContextHandle) const +QByteArray IfdDisconnectResponse::toByteArray(const QString& pContextHandle) const { QJsonObject result = createMessageBody(pContextHandle); result[SLOT_HANDLE()] = mSlotHandle; - return QJsonDocument(result); + return RemoteMessage::toByteArray(result); } diff --git a/src/remote_device/messages/IfdDisconnectResponse.h b/src/remote_device/messages/IfdDisconnectResponse.h index 35dd22e..01a37b0 100644 --- a/src/remote_device/messages/IfdDisconnectResponse.h +++ b/src/remote_device/messages/IfdDisconnectResponse.h @@ -16,13 +16,13 @@ class IfdDisconnectResponse QString mSlotHandle; public: - IfdDisconnectResponse(const QString& pSlotHandle, const QString& pResultMinor = QString()); + IfdDisconnectResponse(const QString& pSlotHandle, ECardApiResult::Minor pResultMinor = ECardApiResult::Minor::null); IfdDisconnectResponse(const QJsonObject& pMessageObject); virtual ~IfdDisconnectResponse() override = default; const QString& getSlotHandle() const; - virtual QJsonDocument toJson(const QString& pContextHandle) const override; + virtual QByteArray toByteArray(const QString& pContextHandle) const override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/remote_device/messages/IfdError.cpp b/src/remote_device/messages/IfdError.cpp index 899cd84..45d1ea8 100644 --- a/src/remote_device/messages/IfdError.cpp +++ b/src/remote_device/messages/IfdError.cpp @@ -18,10 +18,10 @@ using namespace governikus; namespace { VALUE_NAME(SLOT_HANDLE, "SlotHandle") -} +} // namespace -IfdError::IfdError(const QString& pSlotHandle, const QString& pResultMinor) +IfdError::IfdError(const QString& pSlotHandle, ECardApiResult::Minor pResultMinor) : RemoteMessageResponse(RemoteCardMessageType::IFDError, pResultMinor) , mSlotHandle(pSlotHandle) { @@ -33,6 +33,11 @@ IfdError::IfdError(const QJsonObject& pMessageObject) , mSlotHandle() { mSlotHandle = getStringValue(pMessageObject, SLOT_HANDLE()); + + if (getType() != RemoteCardMessageType::IFDError) + { + markIncomplete(QStringLiteral("The value of msg should be IFDError")); + } } @@ -42,11 +47,11 @@ const QString& IfdError::getSlotHandle() const } -QJsonDocument IfdError::toJson(const QString& pContextHandle) const +QByteArray IfdError::toByteArray(const QString& pContextHandle) const { QJsonObject result = createMessageBody(pContextHandle); result[SLOT_HANDLE()] = mSlotHandle; - return QJsonDocument(result); + return RemoteMessage::toByteArray(result); } diff --git a/src/remote_device/messages/IfdError.h b/src/remote_device/messages/IfdError.h index 376e514..22b1765 100644 --- a/src/remote_device/messages/IfdError.h +++ b/src/remote_device/messages/IfdError.h @@ -16,13 +16,13 @@ class IfdError QString mSlotHandle; public: - IfdError(const QString& pSlotHandle, const QString& pResultMinor = QString()); + IfdError(const QString& pSlotHandle, ECardApiResult::Minor pResultMinor = ECardApiResult::Minor::null); IfdError(const QJsonObject& pMessageObject); virtual ~IfdError() override = default; const QString& getSlotHandle() const; - virtual QJsonDocument toJson(const QString& pContextHandle) const override; + virtual QByteArray toByteArray(const QString& pContextHandle) const override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/remote_device/messages/IfdEstablishContext.cpp b/src/remote_device/messages/IfdEstablishContext.cpp index fa3f44c..4107850 100644 --- a/src/remote_device/messages/IfdEstablishContext.cpp +++ b/src/remote_device/messages/IfdEstablishContext.cpp @@ -19,7 +19,7 @@ namespace { VALUE_NAME(PROTOCOL, "Protocol") VALUE_NAME(UD_NAME, "UDName") -} +} // namespace IfdEstablishContext::IfdEstablishContext(const IfdVersion& pProtocol, const QString& pUdName) @@ -36,6 +36,10 @@ IfdEstablishContext::IfdEstablishContext(const QJsonObject& pMessageObject) , mProtocol(IfdVersion(mProtocolRaw)) , mUdName(getStringValue(pMessageObject, UD_NAME())) { + if (getType() != RemoteCardMessageType::IFDEstablishContext) + { + markIncomplete(QStringLiteral("The value of msg should be IFDEstablishContext")); + } } @@ -57,10 +61,12 @@ const QString& IfdEstablishContext::getUdName() const } -QJsonDocument IfdEstablishContext::toJson(const QString& pContextHandle) const +QByteArray IfdEstablishContext::toByteArray(const QString& pContextHandle) const { QJsonObject result = createMessageBody(pContextHandle); + result[PROTOCOL()] = mProtocol.toString(); result[UD_NAME()] = mUdName; - return QJsonDocument(result); + + return RemoteMessage::toByteArray(result); } diff --git a/src/remote_device/messages/IfdEstablishContext.h b/src/remote_device/messages/IfdEstablishContext.h index 8dedcb4..6921545 100644 --- a/src/remote_device/messages/IfdEstablishContext.h +++ b/src/remote_device/messages/IfdEstablishContext.h @@ -30,8 +30,8 @@ class IfdEstablishContext const IfdVersion& getProtocol() const; const QString& getProtocolRaw() const; const QString& getUdName() const; - virtual QJsonDocument toJson(const QString& pContextHandle) const override; + virtual QByteArray toByteArray(const QString& pContextHandle) const override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/remote_device/messages/IfdEstablishContextResponse.cpp b/src/remote_device/messages/IfdEstablishContextResponse.cpp index f1615a6..73b5241 100644 --- a/src/remote_device/messages/IfdEstablishContextResponse.cpp +++ b/src/remote_device/messages/IfdEstablishContextResponse.cpp @@ -17,9 +17,9 @@ using namespace governikus; namespace { VALUE_NAME(IFD_NAME, "IFDName") -} +} // namespace -IfdEstablishContextResponse::IfdEstablishContextResponse(const QString& pIfdName, const QString& pResultMinor) +IfdEstablishContextResponse::IfdEstablishContextResponse(const QString& pIfdName, ECardApiResult::Minor pResultMinor) : RemoteMessageResponse(RemoteCardMessageType::IFDEstablishContextResponse, pResultMinor) , mIfdName(pIfdName) { @@ -31,14 +31,21 @@ IfdEstablishContextResponse::IfdEstablishContextResponse(const QJsonObject& pMes , mIfdName() { mIfdName = getStringValue(pMessageObject, IFD_NAME()); + + if (getType() != RemoteCardMessageType::IFDEstablishContextResponse) + { + markIncomplete(QStringLiteral("The value of msg should be IFDEstablishContextResponse")); + } } -QJsonDocument IfdEstablishContextResponse::toJson(const QString& pContextHandle) const +QByteArray IfdEstablishContextResponse::toByteArray(const QString& pContextHandle) const { QJsonObject result = createMessageBody(pContextHandle); + result[IFD_NAME()] = mIfdName; - return QJsonDocument(result); + + return RemoteMessage::toByteArray(result); } diff --git a/src/remote_device/messages/IfdEstablishContextResponse.h b/src/remote_device/messages/IfdEstablishContextResponse.h index 5d65657..13c6a18 100644 --- a/src/remote_device/messages/IfdEstablishContextResponse.h +++ b/src/remote_device/messages/IfdEstablishContextResponse.h @@ -19,13 +19,13 @@ class IfdEstablishContextResponse QString mIfdName; public: - IfdEstablishContextResponse(const QString& pIfdName, const QString& pResultMinor = QString()); + IfdEstablishContextResponse(const QString& pIfdName, ECardApiResult::Minor pResultMinor = ECardApiResult::Minor::null); IfdEstablishContextResponse(const QJsonObject& pMessageObject); virtual ~IfdEstablishContextResponse() override = default; const QString& getIfdName() const; - virtual QJsonDocument toJson(const QString& pContextHandle) const override; + virtual QByteArray toByteArray(const QString& pContextHandle) const override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/remote_device/messages/IfdEstablishPaceChannel.cpp b/src/remote_device/messages/IfdEstablishPaceChannel.cpp index 9347af8..3a47877 100644 --- a/src/remote_device/messages/IfdEstablishPaceChannel.cpp +++ b/src/remote_device/messages/IfdEstablishPaceChannel.cpp @@ -4,8 +4,6 @@ #include "IfdEstablishPaceChannel.h" -#include "MessageReceiver.h" - #include #include @@ -20,7 +18,7 @@ namespace { VALUE_NAME(SLOT_HANDLE, "SlotHandle") VALUE_NAME(INPUT_DATA, "InputData") -} +} // namespace IfdEstablishPaceChannel::IfdEstablishPaceChannel(const QString& pSlotHandle, const QByteArray& pInputData) @@ -28,7 +26,6 @@ IfdEstablishPaceChannel::IfdEstablishPaceChannel(const QString& pSlotHandle, con , mSlotHandle(pSlotHandle) , mInputData(pInputData) { - } @@ -41,6 +38,11 @@ IfdEstablishPaceChannel::IfdEstablishPaceChannel(const QJsonObject& pMessageObje const QString& inputData = getStringValue(pMessageObject, INPUT_DATA()); mInputData = QByteArray::fromHex(inputData.toUtf8()); + + if (getType() != RemoteCardMessageType::IFDEstablishPACEChannel) + { + markIncomplete(QStringLiteral("The value of msg should be IFDEstablishPACEChannel")); + } } @@ -56,12 +58,12 @@ const QByteArray& IfdEstablishPaceChannel::getInputData() const } -QJsonDocument IfdEstablishPaceChannel::toJson(const QString& pContextHandle) const +QByteArray IfdEstablishPaceChannel::toByteArray(const QString& pContextHandle) const { QJsonObject result = createMessageBody(pContextHandle); result[SLOT_HANDLE()] = mSlotHandle; result[INPUT_DATA()] = QString::fromLatin1(mInputData.toHex()); - return QJsonDocument(result); + return RemoteMessage::toByteArray(result); } diff --git a/src/remote_device/messages/IfdEstablishPaceChannel.h b/src/remote_device/messages/IfdEstablishPaceChannel.h index 6aa229e..7ef4692 100644 --- a/src/remote_device/messages/IfdEstablishPaceChannel.h +++ b/src/remote_device/messages/IfdEstablishPaceChannel.h @@ -4,14 +4,12 @@ #pragma once -#include "MessageReceiver.h" #include "RemoteMessage.h" namespace governikus { - class IfdEstablishPaceChannel : public RemoteMessage { @@ -26,8 +24,8 @@ class IfdEstablishPaceChannel const QString& getSlotHandle() const; const QByteArray& getInputData() const; - virtual QJsonDocument toJson(const QString& pContextHandle) const override; + virtual QByteArray toByteArray(const QString& pContextHandle) const override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/remote_device/messages/IfdEstablishPaceChannelResponse.cpp b/src/remote_device/messages/IfdEstablishPaceChannelResponse.cpp index 8eabbde..d8698fb 100644 --- a/src/remote_device/messages/IfdEstablishPaceChannelResponse.cpp +++ b/src/remote_device/messages/IfdEstablishPaceChannelResponse.cpp @@ -18,10 +18,10 @@ namespace { VALUE_NAME(SLOT_HANDLE, "SlotHandle") VALUE_NAME(OUTPUT_DATA, "OutputData") -} +} // namespace -IfdEstablishPaceChannelResponse::IfdEstablishPaceChannelResponse(const QString& pSlotHandle, const QByteArray& pOutputData, const QString& pResultMinor) +IfdEstablishPaceChannelResponse::IfdEstablishPaceChannelResponse(const QString& pSlotHandle, const QByteArray& pOutputData, ECardApiResult::Minor pResultMinor) : RemoteMessageResponse(RemoteCardMessageType::IFDEstablishPACEChannelResponse, pResultMinor) , mSlotHandle(pSlotHandle) , mOutputData(pOutputData) @@ -38,6 +38,11 @@ IfdEstablishPaceChannelResponse::IfdEstablishPaceChannelResponse(const QJsonObje const QString& inputData = getStringValue(pMessageObject, OUTPUT_DATA()); mOutputData = QByteArray::fromHex(inputData.toUtf8()); + + if (getType() != RemoteCardMessageType::IFDEstablishPACEChannelResponse) + { + markIncomplete(QStringLiteral("The value of msg should be IFDEstablishPACEChannelResponse")); + } } @@ -53,12 +58,12 @@ const QByteArray& IfdEstablishPaceChannelResponse::getOutputData() const } -QJsonDocument IfdEstablishPaceChannelResponse::toJson(const QString& pContextHandle) const +QByteArray IfdEstablishPaceChannelResponse::toByteArray(const QString& pContextHandle) const { QJsonObject result = createMessageBody(pContextHandle); result[SLOT_HANDLE()] = mSlotHandle; result[OUTPUT_DATA()] = QString::fromLatin1(mOutputData.toHex()); - return QJsonDocument(result); + return RemoteMessage::toByteArray(result); } diff --git a/src/remote_device/messages/IfdEstablishPaceChannelResponse.h b/src/remote_device/messages/IfdEstablishPaceChannelResponse.h index ba36c48..bd1f5f0 100644 --- a/src/remote_device/messages/IfdEstablishPaceChannelResponse.h +++ b/src/remote_device/messages/IfdEstablishPaceChannelResponse.h @@ -4,9 +4,7 @@ #pragma once -#include "EstablishPACEChannel.h" #include "RemoteMessageResponse.h" -#include "SmartCardDefinitions.h" namespace governikus @@ -19,14 +17,14 @@ class IfdEstablishPaceChannelResponse QByteArray mOutputData; public: - IfdEstablishPaceChannelResponse(const QString& pSlotHandle, const QByteArray& pOutputData, const QString& pResultMinor = QString()); + IfdEstablishPaceChannelResponse(const QString& pSlotHandle, const QByteArray& pOutputData, ECardApiResult::Minor pResultMinor = ECardApiResult::Minor::null); IfdEstablishPaceChannelResponse(const QJsonObject& pMessageObject); virtual ~IfdEstablishPaceChannelResponse() override = default; const QString& getSlotHandle() const; const QByteArray& getOutputData() const; - virtual QJsonDocument toJson(const QString& pContextHandle) const override; + virtual QByteArray toByteArray(const QString& pContextHandle) const override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/remote_device/messages/IfdGetStatus.cpp b/src/remote_device/messages/IfdGetStatus.cpp new file mode 100644 index 0000000..e8cb23c --- /dev/null +++ b/src/remote_device/messages/IfdGetStatus.cpp @@ -0,0 +1,57 @@ +/*! + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + + +#include "IfdGetStatus.h" + +#include +#include + + +Q_DECLARE_LOGGING_CATEGORY(remote_device) + + +using namespace governikus; + + +namespace +{ +VALUE_NAME(SLOT_NAME, "SlotName") +} // namespace + + +IfdGetStatus::IfdGetStatus(const QString& pSlotName) + : RemoteMessage(RemoteCardMessageType::IFDGetStatus) + , mSlotName(pSlotName) +{ +} + + +IfdGetStatus::IfdGetStatus(const QJsonObject& pMessageObject) + : RemoteMessage(pMessageObject) + , mSlotName() +{ + mSlotName = getStringValue(pMessageObject, SLOT_NAME()); + + if (getType() != RemoteCardMessageType::IFDGetStatus) + { + markIncomplete(QStringLiteral("The value of msg should be IFDGetStatus")); + } +} + + +const QString& IfdGetStatus::getSlotName() const +{ + return mSlotName; +} + + +QByteArray IfdGetStatus::toByteArray(const QString& pContextHandle) const +{ + QJsonObject result = createMessageBody(pContextHandle); + + result[SLOT_NAME()] = mSlotName; + + return RemoteMessage::toByteArray(result); +} diff --git a/src/remote_device/messages/IfdGetStatus.h b/src/remote_device/messages/IfdGetStatus.h new file mode 100644 index 0000000..8fad5ff --- /dev/null +++ b/src/remote_device/messages/IfdGetStatus.h @@ -0,0 +1,28 @@ +/*! + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "RemoteMessage.h" + + +namespace governikus +{ +class IfdGetStatus + : public RemoteMessage +{ + private: + QString mSlotName; + + public: + IfdGetStatus(const QString& pSlotName = QString()); + IfdGetStatus(const QJsonObject& pMessageObject); + virtual ~IfdGetStatus() override = default; + + const QString& getSlotName() const; + virtual QByteArray toByteArray(const QString& pContextHandle) const override; +}; + + +} // namespace governikus diff --git a/src/remote_device/messages/IfdModifyPin.cpp b/src/remote_device/messages/IfdModifyPin.cpp index 51791d0..758bef4 100644 --- a/src/remote_device/messages/IfdModifyPin.cpp +++ b/src/remote_device/messages/IfdModifyPin.cpp @@ -18,7 +18,7 @@ namespace { VALUE_NAME(SLOT_HANDLE, "SlotHandle") VALUE_NAME(INPUT_DATA, "InputData") -} +} // namespace IfdModifyPin::IfdModifyPin(const QString& pSlotHandle, const QByteArray& pInputData) @@ -26,7 +26,6 @@ IfdModifyPin::IfdModifyPin(const QString& pSlotHandle, const QByteArray& pInputD , mSlotHandle(pSlotHandle) , mInputData(pInputData) { - } @@ -39,6 +38,11 @@ IfdModifyPin::IfdModifyPin(const QJsonObject& pMessageObject) const QString& inputData = getStringValue(pMessageObject, INPUT_DATA()); mInputData = QByteArray::fromHex(inputData.toUtf8()); + + if (getType() != RemoteCardMessageType::IFDModifyPIN) + { + markIncomplete(QStringLiteral("The value of msg should be IFDModifyPIN")); + } } @@ -54,12 +58,12 @@ const QByteArray& IfdModifyPin::getInputData() const } -QJsonDocument IfdModifyPin::toJson(const QString& pContextHandle) const +QByteArray IfdModifyPin::toByteArray(const QString& pContextHandle) const { QJsonObject result = createMessageBody(pContextHandle); result[SLOT_HANDLE()] = mSlotHandle; result[INPUT_DATA()] = QString::fromLatin1(mInputData.toHex()); - return QJsonDocument(result); + return RemoteMessage::toByteArray(result); } diff --git a/src/remote_device/messages/IfdModifyPin.h b/src/remote_device/messages/IfdModifyPin.h index dafc0c3..3ba435b 100644 --- a/src/remote_device/messages/IfdModifyPin.h +++ b/src/remote_device/messages/IfdModifyPin.h @@ -10,7 +10,6 @@ namespace governikus { - class IfdModifyPin : public RemoteMessage { @@ -25,8 +24,8 @@ class IfdModifyPin const QString& getSlotHandle() const; const QByteArray& getInputData() const; - virtual QJsonDocument toJson(const QString& pContextHandle) const override; + virtual QByteArray toByteArray(const QString& pContextHandle) const override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/remote_device/messages/IfdModifyPinResponse.cpp b/src/remote_device/messages/IfdModifyPinResponse.cpp index da88d33..ab7b202 100644 --- a/src/remote_device/messages/IfdModifyPinResponse.cpp +++ b/src/remote_device/messages/IfdModifyPinResponse.cpp @@ -18,10 +18,10 @@ namespace { VALUE_NAME(SLOT_HANDLE, "SlotHandle") VALUE_NAME(OUTPUT_DATA, "OutputData") -} +} // namespace -IfdModifyPinResponse::IfdModifyPinResponse(const QString& pSlotHandle, const QByteArray& pOutputData, const QString& pResultMinor) +IfdModifyPinResponse::IfdModifyPinResponse(const QString& pSlotHandle, const QByteArray& pOutputData, ECardApiResult::Minor pResultMinor) : RemoteMessageResponse(RemoteCardMessageType::IFDModifyPINResponse, pResultMinor) , mSlotHandle(pSlotHandle) , mOutputData(pOutputData) @@ -38,6 +38,11 @@ IfdModifyPinResponse::IfdModifyPinResponse(const QJsonObject& pMessageObject) const QString& inputData = getStringValue(pMessageObject, OUTPUT_DATA()); mOutputData = QByteArray::fromHex(inputData.toUtf8()); + + if (getType() != RemoteCardMessageType::IFDModifyPINResponse) + { + markIncomplete(QStringLiteral("The value of msg should be IFDModifyPINResponse")); + } } @@ -60,38 +65,41 @@ CardReturnCode IfdModifyPinResponse::getReturnCode() const return CardReturnCode::OK; } - const auto& minor = getResultMinor(); - if (minor == QLatin1String("http://www.bsi.bund.de/ecard/api/1.1/resultminor/ifdl/common#timeoutError")) + switch (getResultMinor()) { - return CardReturnCode::INPUT_TIME_OUT; - } - if (minor == QLatin1String("http://www.bsi.bund.de/ecard/api/1.1/resultminor/ifdl#cancellationByUser")) - { - return CardReturnCode::CANCELLATION_BY_USER; - } - if (minor == QLatin1String("http://www.bsi.bund.de/ecard/api/1.1/resultminor/ifdl/IO#repeatedDataMismatch")) - { - return CardReturnCode::NEW_PIN_MISMATCH; - } - if (minor == QLatin1String("http://www.bsi.bund.de/ecard/api/1.1/resultminor/ifdl/IO#unknownPINFormat")) - { - return CardReturnCode::NEW_PIN_INVALID_LENGTH; - } - if (minor == QLatin1String("http://www.bsi.bund.de/ecard/api/1.1/resultminor/al/common#unknownError")) - { - return CardReturnCode::UNKNOWN; - } + case ECardApiResult::Minor::IFDL_Timeout_Error: + return CardReturnCode::INPUT_TIME_OUT; - return CardReturnCode::COMMAND_FAILED; + case ECardApiResult::Minor::IFDL_CancellationByUser: + return CardReturnCode::CANCELLATION_BY_USER; + + case ECardApiResult::Minor::IFDL_IO_RepeatedDataMismatch: + return CardReturnCode::NEW_PIN_MISMATCH; + + case ECardApiResult::Minor::IFDL_IO_UnknownPINFormat: + return CardReturnCode::NEW_PIN_INVALID_LENGTH; + + case ECardApiResult::Minor::IFDL_Terminal_NoCard: + return CardReturnCode::CARD_NOT_FOUND; + + case ECardApiResult::Minor::AL_Unkown_API_Function: + return CardReturnCode::PROTOCOL_ERROR; + + case ECardApiResult::Minor::AL_Unknown_Error: + return CardReturnCode::UNKNOWN; + + default: + return CardReturnCode::COMMAND_FAILED; + } } -QJsonDocument IfdModifyPinResponse::toJson(const QString& pContextHandle) const +QByteArray IfdModifyPinResponse::toByteArray(const QString& pContextHandle) const { QJsonObject result = createMessageBody(pContextHandle); result[SLOT_HANDLE()] = mSlotHandle; result[OUTPUT_DATA()] = QString::fromLatin1(mOutputData.toHex()); - return QJsonDocument(result); + return RemoteMessage::toByteArray(result); } diff --git a/src/remote_device/messages/IfdModifyPinResponse.h b/src/remote_device/messages/IfdModifyPinResponse.h index 0d41af3..98e6d7a 100644 --- a/src/remote_device/messages/IfdModifyPinResponse.h +++ b/src/remote_device/messages/IfdModifyPinResponse.h @@ -18,15 +18,16 @@ class IfdModifyPinResponse QByteArray mOutputData; public: - IfdModifyPinResponse(const QString& pSlotHandle, const QByteArray& pOutputData, const QString& pResultMinor = QString()); + IfdModifyPinResponse(const QString& pSlotHandle, const QByteArray& pOutputData, ECardApiResult::Minor pResultMinor = ECardApiResult::Minor::null); IfdModifyPinResponse(const QJsonObject& pMessageObject); virtual ~IfdModifyPinResponse() override = default; const QString& getSlotHandle() const; const QByteArray& getOutputData() const; CardReturnCode getReturnCode() const; - virtual QJsonDocument toJson(const QString& pContextHandle) const override; + + virtual QByteArray toByteArray(const QString& pContextHandle) const override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/remote_device/messages/IfdStatus.cpp b/src/remote_device/messages/IfdStatus.cpp index f6a85f2..ecdae08 100644 --- a/src/remote_device/messages/IfdStatus.cpp +++ b/src/remote_device/messages/IfdStatus.cpp @@ -6,9 +6,7 @@ #include "IfdStatus.h" #include "AppSettings.h" -#include "Env.h" -#include #include @@ -31,7 +29,7 @@ VALUE_NAME(CONNECTED_READER, "ConnectedReader") VALUE_NAME(CARD_AVAILABLE, "CardAvailable") VALUE_NAME(EF_ATR, "EFATR") VALUE_NAME(EF_DIR, "EFDIR") -} +} // namespace PaceCapabilities::PaceCapabilities(bool pPace, bool pEId, bool pESign, bool pDestroy) @@ -78,13 +76,27 @@ QJsonValue PaceCapabilities::toJson() const } -IfdStatus::IfdStatus(const ReaderInfo& pReaderInfo) +IfdStatus::IfdStatus(const QString& pSlotName, + const PaceCapabilities& pPaceCapabilities, + int pMaxApduLength, + bool pConnected, + bool pCardAvailable) : RemoteMessage(RemoteCardMessageType::IFDStatus) - , mSlotName(pReaderInfo.getName()) - , mPaceCapabilities(Env::getSingleton()->getRemoteServiceSettings().getPinPadMode()) - , mMaxApduLength(pReaderInfo.getMaxApduLength()) - , mConnectedReader(pReaderInfo.isConnected()) - , mCardAvailable(pReaderInfo.hasCard()) + , mSlotName(pSlotName) + , mPaceCapabilities(pPaceCapabilities) + , mMaxApduLength(pMaxApduLength) + , mConnectedReader(pConnected) + , mCardAvailable(pCardAvailable) +{ +} + + +IfdStatus::IfdStatus(const ReaderInfo& pReaderInfo) + : IfdStatus(pReaderInfo.getName() + , pReaderInfo.isBasicReader() ? Env::getSingleton()->getRemoteServiceSettings().getPinPadMode() : true + , pReaderInfo.getMaxApduLength() + , pReaderInfo.isConnected() + , pReaderInfo.hasCard()) { } @@ -124,6 +136,11 @@ IfdStatus::IfdStatus(const QJsonObject& pMessageObject) mMaxApduLength = getIntValue(pMessageObject, MAX_APDU_LENGTH()); mConnectedReader = getBoolValue(pMessageObject, CONNECTED_READER()); mCardAvailable = getBoolValue(pMessageObject, CARD_AVAILABLE()); + + if (getType() != RemoteCardMessageType::IFDStatus) + { + markIncomplete(QStringLiteral("The value of msg should be IFDStatus")); + } } @@ -157,7 +174,7 @@ bool IfdStatus::getCardAvailable() const } -QJsonDocument IfdStatus::toJson(const QString& pContextHandle) const +QByteArray IfdStatus::toByteArray(const QString& pContextHandle) const { QJsonObject result = createMessageBody(pContextHandle); @@ -169,5 +186,5 @@ QJsonDocument IfdStatus::toJson(const QString& pContextHandle) const result[EF_ATR()] = QJsonValue(); result[EF_DIR()] = QJsonValue(); - return QJsonDocument(result); + return RemoteMessage::toByteArray(result); } diff --git a/src/remote_device/messages/IfdStatus.h b/src/remote_device/messages/IfdStatus.h index bc70c67..25edd94 100644 --- a/src/remote_device/messages/IfdStatus.h +++ b/src/remote_device/messages/IfdStatus.h @@ -1,6 +1,4 @@ /*! - * \brief Classes that model the IFDStatus message. - * * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany */ @@ -12,8 +10,16 @@ #include +class test_RemoteReaderManagerPlugIn; +class test_ServerMessageHandler; +class test_IfdStatus; + + namespace governikus { +class MockRemoteDispatcher; + + class PaceCapabilities { // PACECapabilities according to TR-03119, sec. D.1.1. @@ -40,12 +46,23 @@ class IfdStatus : public RemoteMessage { private: + friend MockRemoteDispatcher; + friend ::test_RemoteReaderManagerPlugIn; + friend ::test_ServerMessageHandler; + friend ::test_IfdStatus; + QString mSlotName; PaceCapabilities mPaceCapabilities; int mMaxApduLength; bool mConnectedReader; bool mCardAvailable; + IfdStatus(const QString& pSlotName, + const PaceCapabilities& pPaceCapabilities, + int pMaxApduLength, + bool pConnected, + bool pCardAvailable = false); + public: IfdStatus(const ReaderInfo& pReaderInfo); IfdStatus(const QJsonObject& pMessageObject); @@ -56,8 +73,8 @@ class IfdStatus int getMaxApduLength() const; bool getConnectedReader() const; bool getCardAvailable() const; - virtual QJsonDocument toJson(const QString& pContextHandle) const override; + virtual QByteArray toByteArray(const QString& pContextHandle) const override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/remote_device/messages/IfdTransmit.cpp b/src/remote_device/messages/IfdTransmit.cpp index d7469a8..849e9dd 100644 --- a/src/remote_device/messages/IfdTransmit.cpp +++ b/src/remote_device/messages/IfdTransmit.cpp @@ -22,14 +22,14 @@ VALUE_NAME(SLOT_HANDLE, "SlotHandle") VALUE_NAME(COMMAND_APDUS, "CommandAPDUs") VALUE_NAME(INPUT_APDU, "InputAPDU") VALUE_NAME(ACCEPTABLE_STATUS_CODES, "AcceptableStatusCodes") -} +} // namespace void IfdTransmit::parseCommandApdu(QJsonValue pEntry) { if (!pEntry.isObject()) { - invalidType(COMMAND_APDUS(), QLatin1String("object")); + invalidType(COMMAND_APDUS(), QLatin1String("object array")); return; } @@ -74,6 +74,11 @@ IfdTransmit::IfdTransmit(const QJsonObject& pMessageObject) { missingValue(COMMAND_APDUS()); } + + if (getType() != RemoteCardMessageType::IFDTransmit) + { + markIncomplete(QStringLiteral("The value of msg should be IFDTransmit")); + } } @@ -89,7 +94,7 @@ const QByteArray& IfdTransmit::getInputApdu() const } -QJsonDocument IfdTransmit::toJson(const QString& pContextHandle) const +QByteArray IfdTransmit::toByteArray(const QString& pContextHandle) const { QJsonObject result = createMessageBody(pContextHandle); @@ -102,5 +107,5 @@ QJsonDocument IfdTransmit::toJson(const QString& pContextHandle) const commandApdus += commandApdu; result[COMMAND_APDUS()] = commandApdus; - return QJsonDocument(result); + return RemoteMessage::toByteArray(result); } diff --git a/src/remote_device/messages/IfdTransmit.h b/src/remote_device/messages/IfdTransmit.h index 80a9c90..15e6ba6 100644 --- a/src/remote_device/messages/IfdTransmit.h +++ b/src/remote_device/messages/IfdTransmit.h @@ -27,8 +27,8 @@ class IfdTransmit const QString& getSlotHandle() const; const QByteArray& getInputApdu() const; - virtual QJsonDocument toJson(const QString& pContextHandle) const override; + virtual QByteArray toByteArray(const QString& pContextHandle) const override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/remote_device/messages/IfdTransmitResponse.cpp b/src/remote_device/messages/IfdTransmitResponse.cpp index 6d0f2ed..18c7c7f 100644 --- a/src/remote_device/messages/IfdTransmitResponse.cpp +++ b/src/remote_device/messages/IfdTransmitResponse.cpp @@ -20,10 +20,10 @@ namespace { VALUE_NAME(SLOT_HANDLE, "SlotHandle") VALUE_NAME(RESPONSE_APDUS, "ResponseAPDUs") -} +} // namespace -IfdTransmitResponse::IfdTransmitResponse(const QString& pSlotHandle, const QByteArray& pResponseApdu, const QString& pResultMinor) +IfdTransmitResponse::IfdTransmitResponse(const QString& pSlotHandle, const QByteArray& pResponseApdu, ECardApiResult::Minor pResultMinor) : RemoteMessageResponse(RemoteCardMessageType::IFDTransmitResponse, pResultMinor) , mSlotHandle(pSlotHandle) , mResponseApdu(pResponseApdu) @@ -52,6 +52,10 @@ IfdTransmitResponse::IfdTransmitResponse(const QJsonObject& pMessageObject) { invalidType(RESPONSE_APDUS(), QLatin1String("string array")); } + if (value.toArray().size() > 1) + { + qCDebug(remote_device) << "Only using the first ResponseAPDU. Command chaining ist not supported yet"; + } } else { @@ -62,6 +66,11 @@ IfdTransmitResponse::IfdTransmitResponse(const QJsonObject& pMessageObject) { missingValue(RESPONSE_APDUS()); } + + if (getType() != RemoteCardMessageType::IFDTransmitResponse) + { + markIncomplete(QStringLiteral("The value of msg should be IFDTransmitResponse")); + } } @@ -77,7 +86,7 @@ const QByteArray& IfdTransmitResponse::getResponseApdu() const } -QJsonDocument IfdTransmitResponse::toJson(const QString& pContextHandle) const +QByteArray IfdTransmitResponse::toByteArray(const QString& pContextHandle) const { QJsonObject result = createMessageBody(pContextHandle); @@ -87,5 +96,5 @@ QJsonDocument IfdTransmitResponse::toJson(const QString& pContextHandle) const responseApdus += QString::fromLatin1(mResponseApdu.toHex()); result[RESPONSE_APDUS()] = responseApdus; - return QJsonDocument(result); + return RemoteMessage::toByteArray(result); } diff --git a/src/remote_device/messages/IfdTransmitResponse.h b/src/remote_device/messages/IfdTransmitResponse.h index 20212b4..bd81a4f 100644 --- a/src/remote_device/messages/IfdTransmitResponse.h +++ b/src/remote_device/messages/IfdTransmitResponse.h @@ -19,14 +19,14 @@ class IfdTransmitResponse QByteArray mResponseApdu; public: - IfdTransmitResponse(const QString& pSlotHandle, const QByteArray& pResponseApdu = QByteArray(), const QString& pResultMinor = QString()); + IfdTransmitResponse(const QString& pSlotHandle, const QByteArray& pResponseApdu = QByteArray(), ECardApiResult::Minor pResultMinor = ECardApiResult::Minor::null); IfdTransmitResponse(const QJsonObject& pMessageObject); virtual ~IfdTransmitResponse() override = default; const QString& getSlotHandle() const; const QByteArray& getResponseApdu() const; - virtual QJsonDocument toJson(const QString& pContextHandle) const override; + virtual QByteArray toByteArray(const QString& pContextHandle) const override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/remote_device/messages/IfdVersion.cpp b/src/remote_device/messages/IfdVersion.cpp index f9f989a..bba7feb 100644 --- a/src/remote_device/messages/IfdVersion.cpp +++ b/src/remote_device/messages/IfdVersion.cpp @@ -16,7 +16,7 @@ IfdVersion::IfdVersion(IfdVersion::Version pVersion) IfdVersion::IfdVersion(const QString& pVersionString) - : IfdVersion(fromString(pVersionString).getVersion()) + : IfdVersion(fromString(pVersionString)) { } @@ -42,39 +42,44 @@ QString IfdVersion::toString() const case IfdVersion::Version::v0: return QStringLiteral("IFDInterface_WebSocket_v0"); + +#ifndef QT_NO_DEBUG + case IfdVersion::Version::v_test: + return QStringLiteral("IFDInterface_WebSocket_v_test"); + +#endif } Q_UNREACHABLE(); } -IfdVersion IfdVersion::fromString(const QString& pVersionString) +IfdVersion::Version IfdVersion::fromString(const QString& pVersionString) { const IfdVersion& v0 = Version::v0; if (pVersionString == v0.toString()) { - return v0; + return Version::v0; } +#ifndef QT_NO_DEBUG + if (pVersionString == IfdVersion(Version::v_test).toString()) + { + return Version::v_test; + } +#endif + return Version::Unknown; } -IfdVersion IfdVersion::latest() -{ - return Version::v0; -} - - QVector IfdVersion::supported() { - return { - Version::v0 - }; + return QVector({Version::v0}); } -IfdVersion IfdVersion::selectLatestSupported(const QVector& pVersions) +IfdVersion::Version IfdVersion::selectLatestSupported(const QVector& pVersions) { QVector supported; supported += IfdVersion::Version::Unknown; diff --git a/src/remote_device/messages/IfdVersion.h b/src/remote_device/messages/IfdVersion.h index 3a144df..594ffb5 100644 --- a/src/remote_device/messages/IfdVersion.h +++ b/src/remote_device/messages/IfdVersion.h @@ -17,6 +17,9 @@ class IfdVersion { Unknown = -1, v0 +#ifndef QT_NO_DEBUG + , v_test +#endif }; private: @@ -34,11 +37,10 @@ class IfdVersion bool operator!=(const IfdVersion& pOther) const; QString toString() const; - static IfdVersion fromString(const QString& pVersionString); + static Version fromString(const QString& pVersionString); - static IfdVersion latest(); static QVector supported(); - static IfdVersion selectLatestSupported(const QVector& pVersions); + static Version selectLatestSupported(const QVector& pVersions); }; diff --git a/src/remote_device/messages/MessageReceiver.cpp b/src/remote_device/messages/MessageReceiver.cpp deleted file mode 100644 index 6d7b34b..0000000 --- a/src/remote_device/messages/MessageReceiver.cpp +++ /dev/null @@ -1,242 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "MessageReceiver.h" - -#include "Discovery.h" -#include "GetIfdStatus.h" -#include "IfdConnect.h" -#include "IfdConnectResponse.h" -#include "IfdDisconnect.h" -#include "IfdDisconnectResponse.h" -#include "IfdError.h" -#include "IfdEstablishContext.h" -#include "IfdEstablishContextResponse.h" -#include "IfdEstablishPaceChannel.h" -#include "IfdEstablishPaceChannelResponse.h" -#include "IfdModifyPin.h" -#include "IfdModifyPinResponse.h" -#include "IfdStatus.h" -#include "IfdTransmit.h" -#include "IfdTransmitResponse.h" - - -using namespace governikus; - - -void MessageReceiver::process(const QSharedPointer& pMessage) -{ - unprocessed(pMessage); -} - - -void MessageReceiver::process(const QSharedPointer& pMessage) -{ - unprocessed(pMessage); -} - - -void MessageReceiver::process(const QSharedPointer& pMessage) -{ - unprocessed(pMessage); -} - - -void MessageReceiver::process(const QSharedPointer& pMessage) -{ - unprocessed(pMessage); -} - - -void MessageReceiver::process(const QSharedPointer& pMessage) -{ - unprocessed(pMessage); -} - - -void MessageReceiver::process(const QSharedPointer& pMessage) -{ - unprocessed(pMessage); -} - - -void MessageReceiver::process(const QSharedPointer& pMessage) -{ - unprocessed(pMessage); -} - - -void MessageReceiver::process(const QSharedPointer& pMessage) -{ - unprocessed(pMessage); -} - - -void MessageReceiver::process(const QSharedPointer& pMessage) -{ - unprocessed(pMessage); -} - - -void MessageReceiver::process(const QSharedPointer& pMessage) -{ - unprocessed(pMessage); -} - - -void MessageReceiver::process(const QSharedPointer& pMessage) -{ - unprocessed(pMessage); -} - - -void MessageReceiver::process(const QSharedPointer& pMessage) -{ - unprocessed(pMessage); -} - - -void MessageReceiver::process(const QSharedPointer& pMessage) -{ - unprocessed(pMessage); -} - - -void MessageReceiver::process(const QSharedPointer& pMessage) -{ - unprocessed(pMessage); -} - - -void MessageReceiver::process(const QSharedPointer& pMessage) -{ - unprocessed(pMessage); -} - - -void MessageReceiver::unprocessed(const QSharedPointer& /*pMessage*/) -{ -} - - -void MessageReceiver::receive(const QSharedPointer& pMessage, const QSharedPointer& pRemoteDispatcher) -{ - Q_ASSERT(pMessage); - Q_UNUSED(pRemoteDispatcher); - - switch (pMessage->getType()) - { - case RemoteCardMessageType::IFDEstablishContext: - { - const QSharedPointer castMessage = pMessage.dynamicCast(); - process(castMessage); - break; - } - - case RemoteCardMessageType::IFDEstablishContextResponse: - { - const QSharedPointer castMessage = pMessage.dynamicCast(); - process(castMessage); - break; - } - - case RemoteCardMessageType::IFDGetStatus: - { - const QSharedPointer castMessage = pMessage.dynamicCast(); - process(castMessage); - break; - } - - case RemoteCardMessageType::IFDStatus: - { - const QSharedPointer castMessage = pMessage.dynamicCast(); - process(castMessage); - break; - } - - - case RemoteCardMessageType::IFDConnect: - { - const QSharedPointer castMessage = pMessage.dynamicCast(); - process(castMessage); - break; - } - - case RemoteCardMessageType::IFDConnectResponse: - { - const QSharedPointer castMessage = pMessage.dynamicCast(); - process(castMessage); - break; - } - - case RemoteCardMessageType::IFDDisconnect: - { - const QSharedPointer castMessage = pMessage.dynamicCast(); - process(castMessage); - break; - } - - case RemoteCardMessageType::IFDDisconnectResponse: - { - const QSharedPointer castMessage = pMessage.dynamicCast(); - process(castMessage); - break; - } - - case RemoteCardMessageType::IFDError: - { - const QSharedPointer castMessage = pMessage.dynamicCast(); - process(castMessage); - break; - } - - case RemoteCardMessageType::IFDTransmit: - { - const QSharedPointer castMessage = pMessage.dynamicCast(); - process(castMessage); - break; - } - - case RemoteCardMessageType::IFDTransmitResponse: - { - const QSharedPointer castMessage = pMessage.dynamicCast(); - process(castMessage); - break; - } - - case RemoteCardMessageType::IFDEstablishPACEChannel: - { - const QSharedPointer castMessage = pMessage.dynamicCast(); - process(castMessage); - break; - } - - case RemoteCardMessageType::IFDEstablishPACEChannelResponse: - { - const QSharedPointer castMessage = pMessage.dynamicCast(); - process(castMessage); - break; - } - - case RemoteCardMessageType::IFDModifyPIN: - { - const QSharedPointer castMessage = pMessage.dynamicCast(); - process(castMessage); - break; - } - - case RemoteCardMessageType::IFDModifyPINResponse: - { - const QSharedPointer castMessage = pMessage.dynamicCast(); - process(castMessage); - break; - } - - case RemoteCardMessageType::UNDEFINED: - { - unprocessed(pMessage); - break; - } - } -} diff --git a/src/remote_device/messages/MessageReceiver.h b/src/remote_device/messages/MessageReceiver.h deleted file mode 100644 index daf2f29..0000000 --- a/src/remote_device/messages/MessageReceiver.h +++ /dev/null @@ -1,61 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include - - -namespace governikus -{ -class RemoteMessage; -class IfdEstablishContext; -class IfdEstablishContextResponse; -class GetIfdStatus; -class ReaderListMsg; -class IfdStatus; -class IfdConnect; -class IfdConnectResponse; -class IfdDisconnect; -class IfdDisconnectResponse; -class IfdError; -class IfdTransmit; -class IfdTransmitResponse; -class IfdEstablishPaceChannel; -class IfdEstablishPaceChannelResponse; -class IfdModifyPin; -class IfdModifyPinResponse; -class RemoteDispatcher; - -class MessageReceiver -{ - Q_DISABLE_COPY(MessageReceiver) - - public: - MessageReceiver() = default; - virtual ~MessageReceiver() = default; - - virtual void process(const QSharedPointer& pMessage); - virtual void process(const QSharedPointer& pMessage); - virtual void process(const QSharedPointer& pMessage); - virtual void process(const QSharedPointer& pMessage); - virtual void process(const QSharedPointer& pMessage); - virtual void process(const QSharedPointer& pMessage); - virtual void process(const QSharedPointer& pMessage); - virtual void process(const QSharedPointer& pMessage); - virtual void process(const QSharedPointer& pMessage); - virtual void process(const QSharedPointer& pMessage); - virtual void process(const QSharedPointer& pMessage); - virtual void process(const QSharedPointer& pMessage); - virtual void process(const QSharedPointer& pMessage); - virtual void process(const QSharedPointer& pMessage); - virtual void process(const QSharedPointer& pMessage); - - virtual void unprocessed(const QSharedPointer& pMessage); - - void receive(const QSharedPointer& pMessage, const QSharedPointer& pRemoteDispatcher = QSharedPointer()); -}; - - -} /* namespace governikus */ diff --git a/src/remote_device/messages/RemoteMessage.cpp b/src/remote_device/messages/RemoteMessage.cpp index 5bc454b..929fda8 100644 --- a/src/remote_device/messages/RemoteMessage.cpp +++ b/src/remote_device/messages/RemoteMessage.cpp @@ -7,6 +7,9 @@ #include "Initializer.h" +#ifndef QT_NO_DEBUG + #include +#endif #include @@ -20,7 +23,7 @@ namespace { VALUE_NAME(MSG_TYPE, "msg") VALUE_NAME(CONTEXT_HANDLE, "ContextHandle") -} +} // namespace static Initializer::Entry E([] { @@ -38,7 +41,7 @@ QJsonObject RemoteMessage::createMessageBody(const QString& pContextHandle) cons } else { - Q_ASSERT(!pContextHandle.isEmpty() || mMessageType == RemoteCardMessageType::IFDError); + Q_ASSERT(!pContextHandle.isEmpty() || mMessageType == RemoteCardMessageType::IFDError || mMessageType == RemoteCardMessageType::IFDEstablishContextResponse); } messageBody[CONTEXT_HANDLE()] = pContextHandle; @@ -46,17 +49,39 @@ QJsonObject RemoteMessage::createMessageBody(const QString& pContextHandle) cons } +QByteArray RemoteMessage::toByteArray(const QJsonObject& pJsonObject) +{ +#ifndef QT_NO_DEBUG + if (QCoreApplication::applicationName().startsWith(QLatin1String("Test"))) + { + return QJsonDocument(pJsonObject).toJson(QJsonDocument::Indented); + } +#endif + + return QJsonDocument(pJsonObject).toJson(QJsonDocument::Compact); +} + + +void RemoteMessage::markIncomplete(const QString& pLogMessage) +{ + Q_ASSERT(!pLogMessage.isEmpty()); + + qCCritical(remote_device) << pLogMessage; + mIncomplete = true; +} + + void RemoteMessage::missingValue(const QLatin1String& pName) { qCCritical(remote_device) << "Missing value" << pName; - mIsValid = false; + mIncomplete = true; } void RemoteMessage::invalidType(const QLatin1String& pName, const QLatin1String& pExpectedType) { qCCritical(remote_device) << "The value of" << pName << "should be of type" << pExpectedType; - mIsValid = false; + mIncomplete = true; } @@ -137,7 +162,7 @@ QJsonObject RemoteMessage::parseByteArray(const QByteArray& pMessage) RemoteMessage::RemoteMessage(RemoteCardMessageType pMessageType) - : mIsValid(true) + : mIncomplete(false) , mMessageType(pMessageType) , mContextHandle() { @@ -145,12 +170,18 @@ RemoteMessage::RemoteMessage(RemoteCardMessageType pMessageType) RemoteMessage::RemoteMessage(const QJsonObject& pMessageObject) - : mIsValid(true) + : mIncomplete(false) , mMessageType(RemoteCardMessageType::UNDEFINED) , mContextHandle() { - const QString& msg = getStringValue(pMessageObject, MSG_TYPE()); - mMessageType = Enum::fromString(msg, RemoteCardMessageType::UNDEFINED); + const QString& msgType = getStringValue(pMessageObject, MSG_TYPE()); + mMessageType = Enum::fromString(msgType, RemoteCardMessageType::UNDEFINED); + + if (mMessageType == RemoteCardMessageType::UNDEFINED) + { + qCWarning(remote_device) << "Invalid messageType received:" << msgType; + mIncomplete = true; + } if (mMessageType != RemoteCardMessageType::IFDEstablishContext) { @@ -159,9 +190,9 @@ RemoteMessage::RemoteMessage(const QJsonObject& pMessageObject) } -bool RemoteMessage::isValid() const +bool RemoteMessage::isIncomplete() const { - return mIsValid; + return mIncomplete; } @@ -177,12 +208,12 @@ const QString& RemoteMessage::getContextHandle() const } -QJsonDocument RemoteMessage::toJson(const QString& pContextHandle) const +QByteArray RemoteMessage::toByteArray(const QString& pContextHandle) const { Q_ASSERT(false); qCCritical(remote_device) << "Unable to convert an untyped RemoteMessage to json:" << pContextHandle; - return QJsonDocument(); + return QByteArray(); } diff --git a/src/remote_device/messages/RemoteMessage.h b/src/remote_device/messages/RemoteMessage.h index fdf23bb..c41b765 100644 --- a/src/remote_device/messages/RemoteMessage.h +++ b/src/remote_device/messages/RemoteMessage.h @@ -1,6 +1,4 @@ /*! - * \brief Classes that model remote card reader messages. - * * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany */ @@ -43,12 +41,15 @@ defineEnumType(RemoteCardMessageType, class RemoteMessage { private: - bool mIsValid; + bool mIncomplete; RemoteCardMessageType mMessageType; QString mContextHandle; protected: virtual QJsonObject createMessageBody(const QString& pContextHandle) const; + static QByteArray toByteArray(const QJsonObject& pJsonObject); + + void markIncomplete(const QString& pLogMessage); void missingValue(const QLatin1String& pName); void invalidType(const QLatin1String& pName, const QLatin1String& pExpectedType); bool getBoolValue(const QJsonObject& pJsonObject, const QLatin1String& pName); @@ -62,12 +63,12 @@ class RemoteMessage RemoteMessage(const QJsonObject& pMessageObject); virtual ~RemoteMessage() = default; - bool isValid() const; + bool isIncomplete() const; RemoteCardMessageType getType() const; const QString& getContextHandle() const; - virtual QJsonDocument toJson(const QString& pContextHandle) const; + virtual QByteArray toByteArray(const QString& pContextHandle = QString()) const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/remote_device/messages/RemoteMessageParser.cpp b/src/remote_device/messages/RemoteMessageParser.cpp deleted file mode 100644 index 9aa8cd7..0000000 --- a/src/remote_device/messages/RemoteMessageParser.cpp +++ /dev/null @@ -1,235 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - - -#include "RemoteMessageParser.h" - -#include "messages/Discovery.h" -#include "messages/GetIfdStatus.h" -#include "messages/IfdConnect.h" -#include "messages/IfdConnectResponse.h" -#include "messages/IfdDisconnect.h" -#include "messages/IfdDisconnectResponse.h" -#include "messages/IfdError.h" -#include "messages/IfdEstablishContext.h" -#include "messages/IfdEstablishContextResponse.h" -#include "messages/IfdEstablishPaceChannel.h" -#include "messages/IfdEstablishPaceChannelResponse.h" -#include "messages/IfdModifyPin.h" -#include "messages/IfdModifyPinResponse.h" -#include "messages/IfdStatus.h" -#include "messages/IfdTransmit.h" -#include "messages/IfdTransmitResponse.h" - -#include -#include -#include - - -Q_DECLARE_LOGGING_CATEGORY(remote_device) - - -using namespace governikus; - - -static QStringList parseArrayOfString(const QJsonArray& pArray) -{ - QStringList result; - for (const QJsonValue& v : pArray) - { - if (!v.isString()) - { - qCWarning(remote_device) << "Expected string but found" << v.type(); - continue; - } - result += v.toString(); - } - - return result; -} - - -static QSharedPointer fail(const QString& pLogMessage) -{ - qCWarning(remote_device) << pLogMessage; - - return QSharedPointer(); -} - - -static QSharedPointer parseMessage(const QJsonObject& pJsonObject) -{ - const RemoteMessage remoteMessage(pJsonObject); - const RemoteCardMessageType messageType = remoteMessage.getType(); - switch (messageType) - { - case RemoteCardMessageType::IFDEstablishContext: - return QSharedPointer(new IfdEstablishContext(pJsonObject)); - - case RemoteCardMessageType::IFDEstablishContextResponse: - return QSharedPointer(new IfdEstablishContextResponse(pJsonObject)); - - case RemoteCardMessageType::IFDGetStatus: - return QSharedPointer(new GetIfdStatus(pJsonObject)); - - case RemoteCardMessageType::IFDConnect: - return QSharedPointer(new IfdConnect(pJsonObject)); - - case RemoteCardMessageType::IFDDisconnect: - return QSharedPointer(new IfdDisconnect(pJsonObject)); - - case RemoteCardMessageType::IFDError: - return QSharedPointer(new IfdError(pJsonObject)); - - case RemoteCardMessageType::IFDTransmit: - return QSharedPointer(new IfdTransmit(pJsonObject)); - - case RemoteCardMessageType::IFDStatus: - return QSharedPointer(new IfdStatus(pJsonObject)); - - case RemoteCardMessageType::IFDConnectResponse: - return QSharedPointer(new IfdConnectResponse(pJsonObject)); - - case RemoteCardMessageType::IFDDisconnectResponse: - return QSharedPointer(new IfdDisconnectResponse(pJsonObject)); - - case RemoteCardMessageType::IFDTransmitResponse: - return QSharedPointer(new IfdTransmitResponse(pJsonObject)); - - case RemoteCardMessageType::IFDEstablishPACEChannel: - return QSharedPointer(new IfdEstablishPaceChannel(pJsonObject)); - - case RemoteCardMessageType::IFDEstablishPACEChannelResponse: - return QSharedPointer(new IfdEstablishPaceChannelResponse(pJsonObject)); - - case RemoteCardMessageType::IFDModifyPIN: - return QSharedPointer(new IfdModifyPin(pJsonObject)); - - case RemoteCardMessageType::IFDModifyPINResponse: - return QSharedPointer(new IfdModifyPinResponse(pJsonObject)); - - case RemoteCardMessageType::UNDEFINED: - return fail(QStringLiteral("Unknown RemoteMessage received")); - } - - Q_UNREACHABLE(); -} - - -RemoteMessageParser::RemoteMessageParser() -{ -} - - -RemoteMessageParser::~RemoteMessageParser() -{ -} - - -QSharedPointer RemoteMessageParser::parseDiscovery(const QJsonDocument& pJsonDocument) const -{ - const QJsonObject& rootObject = pJsonDocument.object(); - if (rootObject.isEmpty()) - { - qCWarning(remote_device) << "Expected object at top level"; - return QSharedPointer(); - } - - const QJsonValue& ifdNameValue = rootObject.value(QLatin1String("IFDName")); - if (!ifdNameValue.isString()) - { - qCWarning(remote_device) << "The value of \"IFDName\" should be of type string"; - return QSharedPointer(); - } - const QString& idfName = ifdNameValue.toString(); - - const QJsonValue& ifdIdValue = rootObject.value(QLatin1String("IFDID")); - if (!ifdIdValue.isString()) - { - qCWarning(remote_device) << "The value of \"IFDID\" should be of type string"; - return QSharedPointer(); - } - const QString& ifdId = ifdIdValue.toString(); - - const QJsonValue& msgValue = rootObject.value(QLatin1String("msg")); - if (!msgValue.isString()) - { - qCWarning(remote_device) << "The value of \"msg\" should be of type string"; - return QSharedPointer(); - } - else if (msgValue.toString() != QLatin1String("REMOTE_IFD")) - { - qCWarning(remote_device) << "The value of \"msg\" should be \"REMOTE_IFD\""; - return QSharedPointer(); - } - // This field is not saved in the C++ object. - - const QJsonValue& portValue = rootObject.value(QLatin1String("port")); - if (!portValue.isDouble()) - { - qCWarning(remote_device) << "The value of \"port\" should be of type double"; - return QSharedPointer(); - } - const long portLong = portValue.toInt(); - if (portLong <= 0 || portLong > static_cast(USHRT_MAX)) - { - qCWarning(remote_device) << "Expected unsigned integer number (16 bit) for port number"; - return QSharedPointer(); - } - const quint16 port = static_cast(portLong); - - const QJsonValue& supportedApisValue = rootObject.value(QLatin1String("SupportedAPI")); - if (!supportedApisValue.isArray()) - { - qCWarning(remote_device) << "The value of \"availableApiLevels\" should be of type string array"; - return QSharedPointer(); - } - const QStringList& availableApiLevelStrings = parseArrayOfString(supportedApisValue.toArray()); - - QVector availableApiLevels; - for (const QString& levelString : availableApiLevelStrings) - { - const IfdVersion& level = IfdVersion::fromString(levelString); - if (level.isValid()) - { - availableApiLevels += level.getVersion(); - } - } - - return QSharedPointer(new Discovery(idfName, ifdId, port, availableApiLevels)); -} - - -QSharedPointer RemoteMessageParser::parse(const QByteArray& pJsonData) const -{ - QJsonParseError error; - const QJsonDocument& doc = QJsonDocument::fromJson(pJsonData, &error); - if (error.error != QJsonParseError::NoError) - { - qCWarning(remote_device) << pJsonData; - - return fail(QStringLiteral("Json parsing failed. Error at offset %1: %2").arg(error.offset).arg(error.errorString())); - } - - return parse(doc); -} - - -QSharedPointer RemoteMessageParser::parse(const QJsonDocument& pJsonDocument) const -{ - const QJsonObject& rootObject = pJsonDocument.object(); - if (rootObject.isEmpty()) - { - return fail(QStringLiteral("Expected object at top level")); - } - - if (rootObject.contains(QStringLiteral("msg"))) - { - return parseMessage(rootObject); - } - else - { - return fail(QStringLiteral("Root object has no messageType")); - } -} diff --git a/src/remote_device/messages/RemoteMessageParser.h b/src/remote_device/messages/RemoteMessageParser.h deleted file mode 100644 index 3e42125..0000000 --- a/src/remote_device/messages/RemoteMessageParser.h +++ /dev/null @@ -1,31 +0,0 @@ -/*! - * \brief Class for parsing JSON data to remote card message objects. - * - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include -#include -#include - - -namespace governikus -{ -class Discovery; -class RemoteMessage; - -class RemoteMessageParser -{ - public: - RemoteMessageParser(); - ~RemoteMessageParser(); - - QSharedPointer parseDiscovery(const QJsonDocument& pJsonDocument) const; - QSharedPointer parse(const QByteArray& pJsonData) const; - QSharedPointer parse(const QJsonDocument& pJsonDocument) const; -}; - -} /* namespace governikus */ diff --git a/src/remote_device/messages/RemoteMessageResponse.cpp b/src/remote_device/messages/RemoteMessageResponse.cpp index 27e0c4d..fe705c4 100644 --- a/src/remote_device/messages/RemoteMessageResponse.cpp +++ b/src/remote_device/messages/RemoteMessageResponse.cpp @@ -14,58 +14,54 @@ Q_DECLARE_LOGGING_CATEGORY(remote_device) using namespace governikus; -#define RESULTMAJOR "http://www.bsi.bund.de/ecard/api/1.1/resultmajor" - - namespace { VALUE_NAME(RESULT_MAJOR, "ResultMajor") VALUE_NAME(RESULT_MINOR, "ResultMinor") -} +} // namespace QJsonObject RemoteMessageResponse::createMessageBody(const QString& pContextHandle) const { QJsonObject result = RemoteMessage::createMessageBody(pContextHandle); - result[RESULT_MAJOR()] = mResultMajor; - result[RESULT_MINOR()] = mResultMinor.isEmpty() ? QJsonValue() : mResultMinor; + const ECardApiResult eCardApiResult(mResultMajor, mResultMinor); + result[RESULT_MAJOR()] = eCardApiResult.getMajorString(); + result[RESULT_MINOR()] = mResultMinor == ECardApiResult::Minor::null ? QJsonValue() : eCardApiResult.getMinorString(); return result; } -RemoteMessageResponse::RemoteMessageResponse(RemoteCardMessageType pMessageType, const QString& pResultMinor) +RemoteMessageResponse::RemoteMessageResponse(RemoteCardMessageType pMessageType, ECardApiResult::Minor pResultMinor) : RemoteMessage(pMessageType) - , mResultMajor(QStringLiteral(RESULTMAJOR "#ok")) - , mResultMinor() + , mResultMajor(pResultMinor == ECardApiResult::Minor::null ? ECardApiResult::Major::Ok : ECardApiResult::Major::Error) + , mResultMinor(pResultMinor) { - if (!pResultMinor.isEmpty()) - { - mResultMajor = QStringLiteral(RESULTMAJOR "#error"); - mResultMinor = QStringLiteral("http://www.bsi.bund.de/ecard/api/1.1/resultminor") + pResultMinor; - } } RemoteMessageResponse::RemoteMessageResponse(const QJsonObject& pMessageObject) : RemoteMessage(pMessageObject) - , mResultMajor() - , mResultMinor() + , mResultMajor(ECardApiResult::Major::Ok) + , mResultMinor(ECardApiResult::Minor::null) { - mResultMajor = getStringValue(pMessageObject, RESULT_MAJOR()); + const auto& major = getStringValue(pMessageObject, RESULT_MAJOR()); + mResultMajor = ECardApiResult::parseMajor(major); + if (resultHasError()) { - mResultMinor = getStringValue(pMessageObject, RESULT_MINOR()); + const auto& minor = getStringValue(pMessageObject, RESULT_MINOR()); + mResultMinor = ECardApiResult::parseMinor(minor); } } bool RemoteMessageResponse::resultHasError() const { - return mResultMajor != QLatin1String(RESULTMAJOR "#ok"); + return mResultMajor != ECardApiResult::Major::Ok; } -const QString& RemoteMessageResponse::getResultMinor() const +ECardApiResult::Minor RemoteMessageResponse::getResultMinor() const { return mResultMinor; } diff --git a/src/remote_device/messages/RemoteMessageResponse.h b/src/remote_device/messages/RemoteMessageResponse.h index abbabc2..d4e620e 100644 --- a/src/remote_device/messages/RemoteMessageResponse.h +++ b/src/remote_device/messages/RemoteMessageResponse.h @@ -1,11 +1,10 @@ /*! - * \brief Classes that model remote card reader response messages. - * * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany */ #pragma once +#include "ECardApiResult.h" #include "RemoteMessage.h" @@ -15,20 +14,20 @@ class RemoteMessageResponse : public RemoteMessage { private: - QString mResultMajor; - QString mResultMinor; + ECardApiResult::Major mResultMajor; + ECardApiResult::Minor mResultMinor; protected: virtual QJsonObject createMessageBody(const QString& pContextHandle) const override; public: - RemoteMessageResponse(RemoteCardMessageType pType, const QString& pResultMinor = QString()); + RemoteMessageResponse(RemoteCardMessageType pType, ECardApiResult::Minor pResultMinor); RemoteMessageResponse(const QJsonObject& pMessageObject); virtual ~RemoteMessageResponse() override = default; bool resultHasError() const; - const QString& getResultMinor() const; + ECardApiResult::Minor getResultMinor() const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/remote_device/plugin/RemoteCard.cpp b/src/remote_device/plugin/RemoteCard.cpp new file mode 100644 index 0000000..6cc2d80 --- /dev/null +++ b/src/remote_device/plugin/RemoteCard.cpp @@ -0,0 +1,260 @@ +/*! + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "RemoteCard.h" + +#include "EstablishPaceChannel.h" +#include "messages/IfdConnect.h" +#include "messages/IfdConnectResponse.h" +#include "messages/IfdDisconnect.h" +#include "messages/IfdDisconnectResponse.h" +#include "messages/IfdEstablishPaceChannel.h" +#include "messages/IfdEstablishPaceChannelResponse.h" +#include "messages/IfdModifyPin.h" +#include "messages/IfdModifyPinResponse.h" +#include "messages/IfdTransmit.h" +#include "messages/IfdTransmitResponse.h" +#include "PinModify.h" +#include "PinModifyOutput.h" + +#include +#include + + +using namespace governikus; + + +Q_DECLARE_LOGGING_CATEGORY(card_remote) + + +bool RemoteCard::sendMessage(const QSharedPointer& pMessage, RemoteCardMessageType pExpectedAnswer, unsigned long pTimeout) +{ + // mResponseAvailable is locked by the constructor, to revert the mutex behavior. + // Locking this is a requirement for QWaitCondition. + + mWaitingForAnswer = true; + mExpectedAnswerType = pExpectedAnswer; + + const auto& connectionMR = QObject::connect(mRemoteDispatcher.data(), &RemoteDispatcher::fireReceived, this, &RemoteCard::onMessageReceived, Qt::DirectConnection); + const auto& connectionDC = QObject::connect(mRemoteDispatcher.data(), &RemoteDispatcherClient::fireClosed, this, &RemoteCard::onDispatcherClosed, Qt::DirectConnection); + const auto& localCopy = mRemoteDispatcher; + QMetaObject::invokeMethod(localCopy.data(), [localCopy, pMessage] { + localCopy->send(pMessage); + }, Qt::QueuedConnection); + + mWaitCondition.wait(&mResponseAvailable, pTimeout); + QObject::disconnect(connectionMR); + QObject::disconnect(connectionDC); + + QMutexLocker locker(&mProcessResponse); + + if (mWaitingForAnswer) + { + mWaitingForAnswer = false; + return false; + } + + return true; +} + + +void RemoteCard::onMessageReceived(RemoteCardMessageType pMessageTpe, const QJsonObject pJsonObject) +{ + QMutexLocker locker(&mProcessResponse); + + if (!mWaitingForAnswer) + { + return; + } + + if (pMessageTpe == mExpectedAnswerType || pMessageTpe == RemoteCardMessageType::IFDError) + { + mResponse = pJsonObject; + mWaitingForAnswer = false; + mWaitCondition.wakeOne(); + } +} + + +void RemoteCard::onDispatcherClosed(GlobalStatus::Code pCloseCode, const QString& pId) +{ + Q_UNUSED(pId); + QMutexLocker locker(&mProcessResponse); + + if (mWaitingForAnswer) + { + qCWarning(card_remote) << "RemoteDispatcher was closed while waiting for an answer:" << pCloseCode; + + mResponse = QJsonObject(); + mWaitingForAnswer = false; + mWaitCondition.wakeOne(); + } +} + + +RemoteCard::RemoteCard(const QSharedPointer& pRemoteDispatcher, const QString& pReaderName) + : Card() + , mWaitingForAnswer(false) + , mWaitCondition() + , mResponseAvailable() + , mProcessResponse() + , mExpectedAnswerType() + , mResponse() + , mRemoteDispatcher(pRemoteDispatcher) + , mReaderName(pReaderName) + , mConnected(false) +{ + Q_ASSERT(mRemoteDispatcher); + + mResponseAvailable.lock(); + const QString& contextHandle = mRemoteDispatcher->getContextHandle(); + mReaderName.remove(contextHandle); +} + + +RemoteCard::~RemoteCard() +{ + mResponseAvailable.unlock(); +} + + +CardReturnCode RemoteCard::connect() +{ + const auto& connectMsg = QSharedPointer::create(mReaderName); + if (sendMessage(connectMsg, RemoteCardMessageType::IFDConnectResponse, 5000)) + { + const IfdConnectResponse response(mResponse); + if (!response.isIncomplete()) + { + if (!response.resultHasError()) + { + mConnected = true; + mSlotHandle = response.getSlotHandle(); + return CardReturnCode::OK; + } + qCWarning(card_remote) << response.getResultMinor(); + } + + return CardReturnCode::COMMAND_FAILED; + } + + return CardReturnCode::INPUT_TIME_OUT; +} + + +CardReturnCode RemoteCard::disconnect() +{ + const auto& disconnectCmd = QSharedPointer::create(mSlotHandle); + if (sendMessage(disconnectCmd, RemoteCardMessageType::IFDDisconnectResponse, 5000)) + { + const IfdDisconnectResponse response(mResponse); + if (!response.isIncomplete()) + { + if (!response.resultHasError()) + { + mConnected = false; + return CardReturnCode::OK; + } + qCWarning(card_remote) << response.getResultMinor(); + } + + return CardReturnCode::COMMAND_FAILED; + } + + return CardReturnCode::INPUT_TIME_OUT; +} + + +bool RemoteCard::isConnected() +{ + return mConnected; +} + + +CardReturnCode RemoteCard::transmit(const CommandApdu& pCommand, ResponseApdu& pResponse) +{ + const auto& transmitCmd = QSharedPointer::create(mSlotHandle, pCommand.getBuffer()); + if (sendMessage(transmitCmd, RemoteCardMessageType::IFDTransmitResponse, 5000)) + { + const IfdTransmitResponse response(mResponse); + if (!response.isIncomplete()) + { + if (!response.resultHasError()) + { + pResponse.setBuffer(response.getResponseApdu()); + return CardReturnCode::OK; + } + qCWarning(card_remote) << response.getResultMinor(); + } + + return CardReturnCode::COMMAND_FAILED; + } + + return CardReturnCode::INPUT_TIME_OUT; +} + + +CardReturnCode RemoteCard::establishPaceChannel(PacePasswordId pPasswordId, const QByteArray& pChat, const QByteArray& pCertificateDescription, EstablishPaceChannelOutput& pChannelOutput, quint8 pTimeoutSeconds) +{ + EstablishPaceChannel builder; + builder.setPasswordId(pPasswordId); + builder.setChat(pChat); + builder.setCertificateDescription(pCertificateDescription); + const QByteArray inputData = builder.createCommandDataCcid().getBuffer(); + + const auto& message = QSharedPointer::create(mSlotHandle, inputData); + if (sendMessage(message, RemoteCardMessageType::IFDEstablishPACEChannelResponse, pTimeoutSeconds * 1000)) + { + const IfdEstablishPaceChannelResponse response(mResponse); + if (!response.isIncomplete()) + { + if (response.getResultMinor() == ECardApiResult::Minor::IFDL_Terminal_NoCard) + { + return CardReturnCode::CARD_NOT_FOUND; + } + + if (!response.resultHasError()) + { + pChannelOutput.parseFromCcid(response.getOutputData(), pPasswordId); + return pChannelOutput.getPaceReturnCode(); + } + qCWarning(card_remote) << response.getResultMinor(); + } + + return CardReturnCode::COMMAND_FAILED; + } + + return CardReturnCode::INPUT_TIME_OUT; +} + + +CardReturnCode RemoteCard::setEidPin(quint8 pTimeoutSeconds, ResponseApdu& pResponseApdu) +{ + PinModify pinModify(pTimeoutSeconds); + const QByteArray inputData = pinModify.createCcidForRemote(); + + const auto& message = QSharedPointer::create(mSlotHandle, inputData); + if (sendMessage(message, RemoteCardMessageType::IFDModifyPINResponse, pTimeoutSeconds * 1000)) + { + const IfdModifyPinResponse response(mResponse); + if (response.resultHasError()) + { + return response.getReturnCode(); + } + + if (!response.isIncomplete()) + { + PinModifyOutput output(response.getOutputData()); + pResponseApdu.setBuffer(output.getResponseApdu().getBuffer()); + if (!response.resultHasError()) + { + return output.getReturnCode(); + } + } + + return CardReturnCode::COMMAND_FAILED; + } + + return CardReturnCode::INPUT_TIME_OUT; +} diff --git a/src/remote_device/plugin/RemoteCard.h b/src/remote_device/plugin/RemoteCard.h new file mode 100644 index 0000000..dc95aa2 --- /dev/null +++ b/src/remote_device/plugin/RemoteCard.h @@ -0,0 +1,63 @@ +/*! + * \brief Implementation of \ref Card for remote reader. + * + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "Card.h" +#include "messages/RemoteMessage.h" +#include "RemoteDispatcherClient.h" + +#include +#include +#include +#include + + +namespace governikus +{ + +class RemoteCard + : public Card +{ + Q_OBJECT + + private: + bool mWaitingForAnswer; + QWaitCondition mWaitCondition; + QMutex mResponseAvailable, mProcessResponse; + + RemoteCardMessageType mExpectedAnswerType; + QJsonObject mResponse; + const QSharedPointer mRemoteDispatcher; + QString mReaderName; + QString mSlotHandle; + bool mConnected; + + bool sendMessage(const QSharedPointer& pMessage, RemoteCardMessageType pExpectedAnswer, unsigned long pTimeout); + + private Q_SLOTS: + void onMessageReceived(RemoteCardMessageType pMessageTpe, const QJsonObject pJsonObject); + void onDispatcherClosed(GlobalStatus::Code pCloseCode, const QString& pId); + + Q_SIGNALS: + void fireCardRemoved(); + + public: + RemoteCard(const QSharedPointer& pRemoteDispatcher, const QString& pReaderName); + virtual ~RemoteCard() override; + + virtual CardReturnCode connect() override; + virtual CardReturnCode disconnect() override; + virtual bool isConnected() override; + + virtual CardReturnCode transmit(const CommandApdu& pCmd, ResponseApdu& pRes) override; + + virtual CardReturnCode establishPaceChannel(PacePasswordId pPasswordId, const QByteArray& pChat, const QByteArray& pCertificateDescription, EstablishPaceChannelOutput& pChannelOutput, quint8 pTimeoutSeconds = 60) override; + + virtual CardReturnCode setEidPin(quint8 pTimeoutSeconds, ResponseApdu& pResponseApdu) override; +}; + +} // namespace governikus diff --git a/src/remote_device/plugin/RemoteReader.cpp b/src/remote_device/plugin/RemoteReader.cpp new file mode 100644 index 0000000..284024d --- /dev/null +++ b/src/remote_device/plugin/RemoteReader.cpp @@ -0,0 +1,79 @@ +/*! + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "RemoteReader.h" + +#include "CardConnectionWorker.h" + +#include +#include + +using namespace governikus; + +Q_DECLARE_LOGGING_CATEGORY(card_remote) + + +RemoteReader::RemoteReader(const QString& pReaderName, const QSharedPointer& pRemoteDispatcher, const IfdStatus& pIfdStatus) + : Reader(ReaderManagerPlugInType::REMOTE, pReaderName) + , mRemoteDispatcher(pRemoteDispatcher) +{ + mReaderInfo.setBasicReader(!pIfdStatus.getPaceCapabilities().getPace()); + mReaderInfo.setConnected(true); + + update(pIfdStatus); +} + + +RemoteReader::~RemoteReader() +{ + mCard.reset(); +} + + +Card* RemoteReader::getCard() const +{ + return mCard.data(); +} + + +Reader::CardEvent RemoteReader::updateCard() +{ + return CardEvent::NONE; +} + + +void RemoteReader::update(const IfdStatus& pIfdStatus) +{ + if (mReaderInfo.getMaxApduLength() != pIfdStatus.getMaxApduLength()) + { + mReaderInfo.setMaxApduLength(pIfdStatus.getMaxApduLength()); + Q_EMIT fireReaderPropertiesUpdated(getName()); + + if (!mReaderInfo.sufficientApduLength()) + { + qCDebug(card_remote) << "ExtendedLengthApduSupport missing. maxAPDULength:" << mReaderInfo.getMaxApduLength(); + } + } + + if (mCard) + { + if (!pIfdStatus.getCardAvailable()) + { + qCDebug(card_remote) << "Card removed"; + mReaderInfo.setCardInfo(CardInfo(CardType::NONE)); + mCard.reset(); + Q_EMIT fireCardRemoved(getName()); + } + return; + } + + if (pIfdStatus.getCardAvailable()) + { + qCDebug(card_remote) << "Card inserted"; + mCard.reset(new RemoteCard(mRemoteDispatcher, getName())); + QSharedPointer cardConnection = createCardConnectionWorker(); + CardInfoFactory::create(cardConnection, mReaderInfo); + Q_EMIT fireCardInserted(getName()); + } +} diff --git a/src/remote_device/plugin/RemoteReader.h b/src/remote_device/plugin/RemoteReader.h new file mode 100644 index 0000000..dd112de --- /dev/null +++ b/src/remote_device/plugin/RemoteReader.h @@ -0,0 +1,41 @@ +/*! + * \brief Implementation of \ref Reader for remote reader. + * + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "messages/IfdStatus.h" +#include "Reader.h" +#include "RemoteCard.h" +#include "RemoteDispatcherClient.h" + +#include +#include + + +namespace governikus +{ + +class RemoteReader + : public Reader +{ + Q_OBJECT + + private: + QScopedPointer mCard; + const QSharedPointer mRemoteDispatcher; + + virtual CardEvent updateCard() override; + + public: + RemoteReader(const QString& pReaderName, const QSharedPointer& pRemoteDispatcher, const IfdStatus& pIfdStatus); + virtual ~RemoteReader() override; + + virtual Card* getCard() const override; + + void update(const IfdStatus& pIfdStatus); +}; + +} // namespace governikus diff --git a/src/remote_device/plugin/RemoteReaderManagerPlugIn.cpp b/src/remote_device/plugin/RemoteReaderManagerPlugIn.cpp new file mode 100644 index 0000000..ce7c5a3 --- /dev/null +++ b/src/remote_device/plugin/RemoteReaderManagerPlugIn.cpp @@ -0,0 +1,296 @@ +/*! + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "RemoteReaderManagerPlugIn.h" + +#include "AppSettings.h" +#include "messages/IfdError.h" +#include "messages/IfdEstablishContext.h" +#include "messages/IfdEstablishContextResponse.h" +#include "messages/IfdGetStatus.h" +#include "messages/IfdStatus.h" +#include "RemoteClient.h" +#include "RemoteDeviceList.h" +#include "RemoteReader.h" + +#include + + +using namespace governikus; + + +Q_DECLARE_LOGGING_CATEGORY(card_remote) + + +void RemoteReaderManagerPlugIn::removeDispatcher(const QString& pId) +{ + const auto& remoteReader = mReadersForDispatcher.values(pId); + for (const auto& readerName : remoteReader) + { + if (readerName.isEmpty()) + { + continue; + } + Q_EMIT fireReaderRemoved(readerName); + delete mReaderList.take(readerName); + mReadersForDispatcher.remove(pId, readerName); + } + + const auto remoteDispatcher = mDispatcherList[pId].data(); + disconnect(remoteDispatcher, &RemoteDispatcherClient::fireContextEstablished, this, &RemoteReaderManagerPlugIn::onContextEstablished); + disconnect(remoteDispatcher, &RemoteDispatcherClient::fireReceived, this, &RemoteReaderManagerPlugIn::onRemoteMessage); + disconnect(remoteDispatcher, &RemoteDispatcherClient::fireClosed, this, &RemoteReaderManagerPlugIn::onDispatcherClosed); + + mDispatcherList.remove(pId); +} + + +void RemoteReaderManagerPlugIn::removeAllDispatchers() +{ + for (const auto& dispatcher : qAsConst(mDispatcherList)) + { + QMetaObject::invokeMethod(dispatcher.data(), &RemoteDispatcherClient::close, Qt::QueuedConnection); + } +} + + +void RemoteReaderManagerPlugIn::connectToPairedReaders() +{ + if (!mConnectToPairedReaders || mConnectionCheckInProgress) + { + return; + } + + mConnectionCheckInProgress = true; + const auto remoteClient = Env::getSingleton(); + connect(remoteClient, &RemoteClient::fireRemoteDevicesInfo, this, &RemoteReaderManagerPlugIn::continueConnectToPairedReaders); + QMetaObject::invokeMethod(remoteClient, &RemoteClient::requestRemoteDevices, Qt::QueuedConnection); +} + + +void RemoteReaderManagerPlugIn::handleIFDStatus(const QJsonObject& pJsonObject, const QString& pId) +{ + IfdStatus ifdStatus(pJsonObject); + const auto& remoteDispatcher = mDispatcherList[pId]; + + const QString& contextHandle = remoteDispatcher->getContextHandle(); + const QString& readerName = ifdStatus.getSlotName() + contextHandle; + + if (mReaderList.contains(readerName)) + { + if (ifdStatus.getConnectedReader()) + { + static_cast(mReaderList[readerName])->update(ifdStatus); + } + else + { + qCDebug(card_remote) << "Removed reader" << readerName; + Q_EMIT fireReaderRemoved(readerName); + delete mReaderList.take(readerName); + mReadersForDispatcher.remove(pId, readerName); + } + return; + } + + if (ifdStatus.getConnectedReader()) + { + auto reader = new RemoteReader(readerName, remoteDispatcher, ifdStatus); + + connect(reader, &RemoteReader::fireCardInserted, this, &RemoteReaderManagerPlugIn::fireCardInserted); + connect(reader, &RemoteReader::fireCardRemoved, this, &RemoteReaderManagerPlugIn::fireCardRemoved); + connect(reader, &RemoteReader::fireCardRetryCounterChanged, this, &RemoteReaderManagerPlugIn::fireCardRetryCounterChanged); + connect(reader, &RemoteReader::fireReaderPropertiesUpdated, this, &RemoteReaderManagerPlugIn::fireReaderPropertiesUpdated); + + mReaderList.insert(readerName, reader); + mReadersForDispatcher.insert(pId, readerName); + qCDebug(card_remote) << "Add reader" << readerName; + Q_EMIT fireReaderAdded(readerName); + + // Also update card + reader->update(ifdStatus); + } +} + + +void RemoteReaderManagerPlugIn::continueConnectToPairedReaders(const QVector >& pRemoteDevices) +{ + const auto remoteClient = Env::getSingleton(); + + disconnect(remoteClient, &RemoteClient::fireRemoteDevicesInfo, this, &RemoteReaderManagerPlugIn::continueConnectToPairedReaders); + + const RemoteServiceSettings& remoteServiceSettings = Env::getSingleton()->getRemoteServiceSettings(); + for (const QSharedPointer& remoteDevice : pRemoteDevices) + { + if (!remoteDevice->getRemoteDeviceDescriptor().isSupported()) + { + continue; + } + + const QString ifdId = remoteDevice->getRemoteDeviceDescriptor().getIfdId(); + + // If already connected: skip. + if (mDispatcherList.contains(ifdId)) + { + continue; + } + + const RemoteServiceSettings::RemoteInfo remoteInfo = remoteServiceSettings.getRemoteInfo(ifdId); + // If we find a remote info for this fingerprint (IfdId), then the remote device is paired. + if (remoteInfo.getFingerprint() == ifdId) + { + QMetaObject::invokeMethod(remoteClient, [remoteClient, remoteDevice] { + remoteClient->establishConnection(remoteDevice, QString()); + }, Qt::QueuedConnection); + } + } + mConnectionCheckInProgress = false; +} + + +void RemoteReaderManagerPlugIn::onContextEstablished(const QString& pIfdName, const QString& pId) +{ + RemoteServiceSettings& settings = Env::getSingleton()->getRemoteServiceSettings(); + auto info = settings.getRemoteInfo(pId); + bool initialPairing = false; + if (info.getName().isEmpty()) + { + initialPairing = true; + } + info.setName(pIfdName); + settings.updateRemoteInfo(info); + + const auto& remoteDispatcher = mDispatcherList[pId]; + if (initialPairing) + { + QMetaObject::invokeMethod(remoteDispatcher.data(), &RemoteDispatcherClient::close, Qt::QueuedConnection); + } + else + { + QMetaObject::invokeMethod(remoteDispatcher.data(), [remoteDispatcher] { + const auto& ifdGetStatus = QSharedPointer::create(); + remoteDispatcher->send(ifdGetStatus); + }, Qt::QueuedConnection); + } +} + + +void RemoteReaderManagerPlugIn::onRemoteMessage(RemoteCardMessageType pMessageType, const QJsonObject& pJsonObject, const QString& pId) +{ + switch (pMessageType) + { + case RemoteCardMessageType::IFDError: + case RemoteCardMessageType::UNDEFINED: + case RemoteCardMessageType::IFDEstablishContextResponse: + case RemoteCardMessageType::IFDConnectResponse: + case RemoteCardMessageType::IFDTransmitResponse: + case RemoteCardMessageType::IFDDisconnectResponse: + case RemoteCardMessageType::IFDEstablishPACEChannelResponse: + case RemoteCardMessageType::IFDModifyPINResponse: + break; + + case RemoteCardMessageType::IFDEstablishContext: + case RemoteCardMessageType::IFDGetStatus: + case RemoteCardMessageType::IFDConnect: + case RemoteCardMessageType::IFDDisconnect: + case RemoteCardMessageType::IFDTransmit: + case RemoteCardMessageType::IFDEstablishPACEChannel: + case RemoteCardMessageType::IFDModifyPIN: + { + qCWarning(card_remote) << "Received an unexpected message of type:" << pMessageType; + const auto& dispatcher = mDispatcherList[pId]; + QMetaObject::invokeMethod(dispatcher.data(), [dispatcher] { + const auto& errorMessage = QSharedPointer::create(QString(), ECardApiResult::Minor::AL_Unkown_API_Function); + dispatcher->send(errorMessage); + }, Qt::QueuedConnection); + break; + } + + case RemoteCardMessageType::IFDStatus: + handleIFDStatus(pJsonObject, pId); + break; + } +} + + +void RemoteReaderManagerPlugIn::onDispatcherClosed(GlobalStatus::Code pCloseCode, const QString& pId) +{ + qCDebug(card_remote) << "RemoteDispatcherClient was closed with:" << pCloseCode; + removeDispatcher(pId); +} + + +RemoteReaderManagerPlugIn::RemoteReaderManagerPlugIn() + : ReaderManagerPlugIn(ReaderManagerPlugInType::REMOTE, true) + , mScanTimer() + , mReaderList() + , mConnectToPairedReaders(true) + , mConnectionCheckInProgress(false) +{ + mScanTimer.setInterval(1000); + connect(&mScanTimer, &QTimer::timeout, this, &RemoteReaderManagerPlugIn::connectToPairedReaders); +} + + +RemoteReaderManagerPlugIn::~RemoteReaderManagerPlugIn() +{ + mScanTimer.stop(); + removeAllDispatchers(); +} + + +void RemoteReaderManagerPlugIn::init() +{ + const auto remoteClient = Env::getSingleton(); + connect(remoteClient, &RemoteClient::fireNewRemoteDispatcher, this, &RemoteReaderManagerPlugIn::addRemoteDispatcher); +} + + +QList RemoteReaderManagerPlugIn::getReaders() const +{ + return mReaderList.values(); +} + + +void RemoteReaderManagerPlugIn::addRemoteDispatcher(const QSharedPointer& pRemoteDispatcher) +{ + Q_ASSERT(pRemoteDispatcher); + + mDispatcherList.insert(pRemoteDispatcher->getId(), pRemoteDispatcher); + + connect(pRemoteDispatcher.data(), &RemoteDispatcherClient::fireContextEstablished, this, &RemoteReaderManagerPlugIn::onContextEstablished); + connect(pRemoteDispatcher.data(), &RemoteDispatcherClient::fireReceived, this, &RemoteReaderManagerPlugIn::onRemoteMessage); + connect(pRemoteDispatcher.data(), &RemoteDispatcherClient::fireClosed, this, &RemoteReaderManagerPlugIn::onDispatcherClosed); + + QMetaObject::invokeMethod(pRemoteDispatcher.data(), [pRemoteDispatcher] { + const auto& establishContext = QSharedPointer::create(QStringLiteral("IFDInterface_WebSocket_v0"), DeviceInfo::getName()); + pRemoteDispatcher->send(establishContext); + }, Qt::QueuedConnection); +} + + +void RemoteReaderManagerPlugIn::startScan(bool pAutoConnect) +{ + mConnectToPairedReaders = pAutoConnect; + if (!mConnectToPairedReaders) + { + removeAllDispatchers(); + } + + const auto remoteClient = Env::getSingleton(); + connect(remoteClient, &RemoteClient::fireDeviceAppeared, this, &RemoteReaderManagerPlugIn::connectToPairedReaders, Qt::UniqueConnection); + + mScanTimer.start(); + QMetaObject::invokeMethod(remoteClient, &RemoteClient::startDetection, Qt::QueuedConnection); + connectToPairedReaders(); +} + + +void RemoteReaderManagerPlugIn::stopScan() +{ + const auto remoteClient = Env::getSingleton(); + disconnect(remoteClient, &RemoteClient::fireDeviceAppeared, this, &RemoteReaderManagerPlugIn::connectToPairedReaders); + mScanTimer.stop(); + QMetaObject::invokeMethod(remoteClient, &RemoteClient::stopDetection, Qt::QueuedConnection); + removeAllDispatchers(); +} diff --git a/src/remote_device/plugin/RemoteReaderManagerPlugIn.h b/src/remote_device/plugin/RemoteReaderManagerPlugIn.h new file mode 100644 index 0000000..0c7dde1 --- /dev/null +++ b/src/remote_device/plugin/RemoteReaderManagerPlugIn.h @@ -0,0 +1,63 @@ +/*! + * \brief Implementation of \ref ReaderManagerPlugIn for remote reader. + * + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "messages/RemoteMessage.h" +#include "Reader.h" +#include "ReaderManagerPlugIn.h" + +#include +#include +#include +#include + +namespace governikus +{ +class RemoteDispatcherClient; +class RemoteDeviceListEntry; + +class RemoteReaderManagerPlugIn + : public ReaderManagerPlugIn +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "governikus.ReaderManagerPlugIn" FILE "metadata.json") + Q_INTERFACES(governikus::ReaderManagerPlugIn) + + private: + QTimer mScanTimer; + QMultiMap mReadersForDispatcher; + QMap > mDispatcherList; + QMap mReaderList; + bool mConnectToPairedReaders; + bool mConnectionCheckInProgress; + + void removeDispatcher(const QString& pId); + void removeAllDispatchers(); + + void handleIFDStatus(const QJsonObject& pJsonObject, const QString& pId); + + private Q_SLOTS: + void onContextEstablished(const QString& pIfdName, const QString& pId); + void onRemoteMessage(RemoteCardMessageType pMessageType, const QJsonObject& pJsonObject, const QString& pId); + void onDispatcherClosed(GlobalStatus::Code pCloseCode, const QString& pId); + void addRemoteDispatcher(const QSharedPointer& pRemoteDispatcher); + void connectToPairedReaders(); + void continueConnectToPairedReaders(const QVector >& pRemoteDevices); + + public: + RemoteReaderManagerPlugIn(); + virtual ~RemoteReaderManagerPlugIn() override; + + void init() override; + virtual QList getReaders() const override; + + virtual void startScan(bool pAutoConnect) override; + virtual void stopScan() override; + +}; + +} // namespace governikus diff --git a/src/card/remote/metadata.json b/src/remote_device/plugin/metadata.json similarity index 100% rename from src/card/remote/metadata.json rename to src/remote_device/plugin/metadata.json diff --git a/src/secure_storage/CMakeLists.txt b/src/secure_storage/CMakeLists.txt index 6c9b2a6..723b855 100644 --- a/src/secure_storage/CMakeLists.txt +++ b/src/secure_storage/CMakeLists.txt @@ -1,3 +1,9 @@ +##################################################################### +# The module secure storage is responsible for persistent +# configuration of the released application. It is delivered as a +# read-only file. +##################################################################### + ADD_PLATFORM_LIBRARY(AusweisAppSecureStorage) TARGET_LINK_LIBRARIES(AusweisAppSecureStorage Qt5::Core Qt5::Network AusweisAppGlobal) diff --git a/src/secure_storage/SecureStorage.cpp b/src/secure_storage/SecureStorage.cpp index dcf115d..42441d7 100644 --- a/src/secure_storage/SecureStorage.cpp +++ b/src/secure_storage/SecureStorage.cpp @@ -8,8 +8,8 @@ #include "FileDestination.h" #include "SingletonHelper.h" - #include +#include #include #include #include @@ -49,10 +49,13 @@ CONFIG_NAME(CONFIGURATION_NAME_SELF_AUTHENTICATION_TEST_URL, "testUrl") CONFIG_NAME(CONFIGURATION_GROUP_NAME_UPDATE_SERVER, "updateServer") CONFIG_NAME(CONFIGURATION_NAME_UPDATE_SERVER_BASE_URL, "baseUrl") +CONFIG_NAME(CONFIGURATION_GROUP_NAME_WHITELIST_SERVER, "whitelistServer") +CONFIG_NAME(CONFIGURATION_NAME_WHITELIST_SERVER_BASE_URL, "baseUrl") + CONFIG_NAME(CONFIGURATION_GROUP_NAME_UPDATES, "updates") CONFIG_NAME(CONFIGURATION_NAME_APPCAST_UPDATE_URL, "release") CONFIG_NAME(CONFIGURATION_NAME_APPCAST_BETA_UPDATE_URL, "beta") -} +} // namespace defineSingleton(SecureStorage) @@ -65,6 +68,7 @@ SecureStorage::SecureStorage() , mSelfAuthenticationUrl() , mSelfAuthenticationTestUrl() , mUpdateServerBaseUrl() + , mWhitelistServerBaseUrl() , mAppcastUpdateUrl() , mAppcastBetaUpdateUrl() , mTlsConfig() @@ -91,7 +95,7 @@ SecureStorage& SecureStorage::getInstance() void SecureStorage::load() { - const auto& path = FileDestination::getPath("config.json"); + const auto& path = FileDestination::getPath(QStringLiteral("config.json")); if (!QFile::exists(path)) { @@ -117,7 +121,7 @@ void SecureStorage::load() configFile.close(); if (parseError.error != QJsonParseError::NoError) { - qCCritical(securestorage) << "Parse error while reading SecureStorage on position " << parseError.offset << ": " << parseError.errorString(); + qCCritical(securestorage) << "Parse error while reading SecureStorage on position" << parseError.offset << ':' << parseError.errorString(); return; } QJsonObject config = document.object(); @@ -127,6 +131,7 @@ void SecureStorage::load() mCvcasTest.clear(); readByteArrayList(mCvcasTest, config, CONFIGURATION_GROUP_NAME_CV_ROOT_CERTIFICATE_TEST()); + mCvcasTest = loadTestCvcsFromAppDir() + mCvcasTest; QByteArrayList certificates; readByteArrayList(certificates, config, CONFIGURATION_GROUP_NAME_UPDATE_CERTIFICATES()); @@ -168,11 +173,11 @@ void SecureStorage::load() mMinStaticKeySizes = readKeySizes(config, CONFIGURATION_GROUP_NAME_MIN_STATIC_KEY_SIZES()); mMinEphemeralKeySizes = readKeySizes(config, CONFIGURATION_GROUP_NAME_MIN_EPHEMERAL_KEY_SIZES()); - mSelfAuthenticationUrl = readGroup(config, CONFIGURATION_GROUP_NAME_SELF_AUTHENTICATION(), CONFIGURATION_NAME_SELF_AUTHENTICATION_URL()); mSelfAuthenticationTestUrl = readGroup(config, CONFIGURATION_GROUP_NAME_SELF_AUTHENTICATION(), CONFIGURATION_NAME_SELF_AUTHENTICATION_TEST_URL()); mUpdateServerBaseUrl = readGroup(config, CONFIGURATION_GROUP_NAME_UPDATE_SERVER(), CONFIGURATION_NAME_UPDATE_SERVER_BASE_URL()); + mWhitelistServerBaseUrl = readGroup(config, CONFIGURATION_GROUP_NAME_WHITELIST_SERVER(), CONFIGURATION_NAME_WHITELIST_SERVER_BASE_URL()); mAppcastUpdateUrl = readGroup(config, CONFIGURATION_GROUP_NAME_UPDATES(), CONFIGURATION_NAME_APPCAST_UPDATE_URL()); mAppcastBetaUpdateUrl = readGroup(config, CONFIGURATION_GROUP_NAME_UPDATES(), CONFIGURATION_NAME_APPCAST_BETA_UPDATE_URL()); @@ -182,6 +187,44 @@ void SecureStorage::load() } +QByteArrayList SecureStorage::loadTestCvcsFromAppDir() +{ + QByteArrayList testCvcs; + const QDir appDir(QCoreApplication::applicationDirPath()); + const QStringList& dirEntries = appDir.entryList({QStringLiteral("*.cvcert.hex")}, QDir::Files); + for (QString cvcFilePath : dirEntries) + { + cvcFilePath = appDir.absolutePath() + QDir::separator() + cvcFilePath; + const QByteArray& hex = loadTestCvc(cvcFilePath); + if (hex.isEmpty()) + { + qWarning() << "Can not load CVC from" << cvcFilePath; + continue; + } + + qDebug() << "Adding CVC from" << cvcFilePath << ':' << hex; + testCvcs += hex; + } + return testCvcs; +} + + +QByteArray SecureStorage::loadTestCvc(const QString& pPath) +{ + QFile cvcFile(pPath); + const int TEN_MEGA_BYTE = 10 * 1024 * 1024; + if (cvcFile.size() > TEN_MEGA_BYTE) + { + return QByteArray(); + } + if (!cvcFile.open(QFile::ReadOnly | QFile::Text)) + { + return QByteArray(); + } + return cvcFile.readAll(); +} + + const QByteArrayList& SecureStorage::getCVRootCertificates(bool pProductive) const { return pProductive ? mCvcas : mCvcasTest; @@ -206,6 +249,12 @@ const QUrl& SecureStorage::getUpdateServerBaseUrl() const } +const QUrl& SecureStorage::getWhitelistServerBaseUrl() const +{ + return mWhitelistServerBaseUrl; +} + + const QUrl& SecureStorage::getAppcastUpdateUrl() const { return mAppcastUpdateUrl; diff --git a/src/secure_storage/SecureStorage.h b/src/secure_storage/SecureStorage.h index 3798a0b..310248b 100644 --- a/src/secure_storage/SecureStorage.h +++ b/src/secure_storage/SecureStorage.h @@ -40,6 +40,7 @@ class SecureStorage QUrl mSelfAuthenticationUrl; QUrl mSelfAuthenticationTestUrl; QUrl mUpdateServerBaseUrl; + QUrl mWhitelistServerBaseUrl; QUrl mAppcastUpdateUrl; QUrl mAppcastBetaUpdateUrl; @@ -54,6 +55,9 @@ class SecureStorage void load(); + QByteArrayList loadTestCvcsFromAppDir(); + QByteArray loadTestCvc(const QString& pPath); + protected: SecureStorage(); virtual ~SecureStorage(); @@ -70,6 +74,7 @@ class SecureStorage const QVector& getUpdateCertificates() const; const QUrl& getSelfAuthenticationUrl(bool pTest = false) const; const QUrl& getUpdateServerBaseUrl() const; + const QUrl& getWhitelistServerBaseUrl() const; const QUrl& getAppcastUpdateUrl() const; const QUrl& getAppcastBetaUpdateUrl() const; const TlsConfiguration& getTlsConfig(TlsSuite pTlsSuite = TlsSuite::DEFAULT) const; diff --git a/src/secure_storage/TlsConfiguration.cpp b/src/secure_storage/TlsConfiguration.cpp index cc63cb4..e5e095b 100644 --- a/src/secure_storage/TlsConfiguration.cpp +++ b/src/secure_storage/TlsConfiguration.cpp @@ -9,7 +9,6 @@ #include #include - using namespace governikus; const QLatin1String SETTINGS_NAME_SSL_PROTOCOL_VERSION("protocolVersion"); @@ -94,8 +93,8 @@ void TlsConfiguration::load(const QJsonObject& pConfig) mConfiguration.setCiphers(ciphers); mConfiguration.setEllipticCurves(ellipticCurves); -#ifdef GOVERNIKUS_QT - mConfiguration.setSignatureAndHashAlgorithms(signatureAlgorithms); +#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + mConfiguration.setBackendConfigurationOption(QByteArrayLiteral("SignatureAlgorithms"), signatureAlgorithms.join(':')); #else Q_UNUSED(signatureAlgorithms) #endif @@ -120,15 +119,16 @@ QVector TlsConfiguration::getEllipticCurves() const } -QVector TlsConfiguration::getSignatureAlgorithms() const +QByteArrayList TlsConfiguration::getSignatureAlgorithms() const { -#ifdef GOVERNIKUS_QT - return mConfiguration.signatureAndHashAlgorithms(); - -#else - return QVector(); - +#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + const auto signatureAlgorithms = mConfiguration.backendConfiguration().value(QByteArrayLiteral("SignatureAlgorithms")).toByteArray(); + if (!signatureAlgorithms.isEmpty()) + { + return signatureAlgorithms.split(':'); + } #endif + return QByteArrayList(); } @@ -177,64 +177,43 @@ QSsl::SslProtocol TlsConfiguration::readSslProtocol(const QJsonObject& pConfig, { return QSsl::SslProtocol::TlsV1_2OrLater; } + if (value == QLatin1String("TlsV1_2")) + { + return QSsl::SslProtocol::TlsV1_2; + } qCritical() << pName << ": Unsupported TLS protocol version detected" << value; } return QSsl::SslProtocol::SecureProtocols; } -QVector TlsConfiguration::readSignatureAlgorithms(const QJsonObject& pConfig, const QLatin1String pKey) +QByteArrayList TlsConfiguration::readSignatureAlgorithms(const QJsonObject& pConfig, const QLatin1String pKey) { const QJsonValue& tmp = pConfig[pKey]; if (tmp.isUndefined()) { qDebug() << pKey << "undefined, using default"; - return QVector(); + return QByteArrayList(); } if (!tmp.isArray()) { qCritical() << pKey << "is malformed"; - return QVector(); + return QByteArrayList(); } const QJsonArray& array = tmp.toArray(); - QVector algorithms; + QByteArrayList algorithms; for (const QJsonValue& line : array) { - const auto& parts = line.toString().split(QLatin1Char('+')); - if (parts.size() != 2) + const auto& value = line.toString(); + if (value.count(QStringLiteral("+")) != 1) { qCritical() << pKey << "has malformed item" << line; - return QVector(); + return QByteArrayList(); } - static const auto& hashMetaEnum = QMetaEnum::fromType(); - bool hashConversionSuccessfull; - const int hashInt = hashMetaEnum.keyToValue(parts[1].toLatin1().constData(), &hashConversionSuccessfull); - if (!hashConversionSuccessfull) - { - qCritical() << "Not a hash algorithm" << parts[1]; - return QVector(); - } - auto hash = static_cast(hashInt); - - if (parts[0] == QLatin1String("Rsa")) - { - algorithms += SignatureAlgorithmPair(QSsl::KeyAlgorithm::Rsa, hash); - } - else if (parts[0] == QLatin1String("Dsa")) - { - algorithms += SignatureAlgorithmPair(QSsl::KeyAlgorithm::Dsa, hash); - } - else if (parts[0] == QLatin1String("Ec")) - { - algorithms += SignatureAlgorithmPair(QSsl::KeyAlgorithm::Ec, hash); - } - else - { - qCritical() << "Not a signature algorithm" << parts[0]; - return QVector(); - } + algorithms += value.toUtf8(); } + return algorithms; } diff --git a/src/secure_storage/TlsConfiguration.h b/src/secure_storage/TlsConfiguration.h index 22816a5..852a82b 100644 --- a/src/secure_storage/TlsConfiguration.h +++ b/src/secure_storage/TlsConfiguration.h @@ -6,6 +6,7 @@ #pragma once +#include #include #include #include @@ -22,9 +23,7 @@ class test_TlsConfiguration; namespace governikus { - class SecureStorage; -using SignatureAlgorithmPair = QPair; class SslCipherList @@ -57,7 +56,7 @@ class TlsConfiguration final bool readJsonArray(QJsonArray& pArray, const QJsonObject& pConfig, const QLatin1String pName); QSsl::SslProtocol readSslProtocol(const QJsonObject& pConfig, const QLatin1String pName); - QVector readSignatureAlgorithms(const QJsonObject& pConfig, const QLatin1String pKey); + QByteArrayList readSignatureAlgorithms(const QJsonObject& pConfig, const QLatin1String pKey); public: void load(const QJsonObject& pConfig); @@ -65,7 +64,7 @@ class TlsConfiguration final QSsl::SslProtocol getProtocolVersion() const; QList getCiphers() const; QVector getEllipticCurves() const; - QVector getSignatureAlgorithms() const; + QByteArrayList getSignatureAlgorithms() const; const QSslConfiguration& getConfiguration() const; }; diff --git a/src/services/AppUpdateData.cpp b/src/services/AppUpdateData.cpp index 50d3024..3efb3f7 100644 --- a/src/services/AppUpdateData.cpp +++ b/src/services/AppUpdateData.cpp @@ -5,7 +5,6 @@ #include "AppUpdateData.h" #include "AppSettings.h" -#include "Env.h" #include "VersionNumber.h" #include diff --git a/src/services/AppUpdateData.h b/src/services/AppUpdateData.h index 0e472d0..2a5dfc7 100644 --- a/src/services/AppUpdateData.h +++ b/src/services/AppUpdateData.h @@ -64,4 +64,4 @@ class AppUpdateData }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/services/AppUpdater.cpp b/src/services/AppUpdater.cpp index fd46697..0e7e2c8 100644 --- a/src/services/AppUpdater.cpp +++ b/src/services/AppUpdater.cpp @@ -6,9 +6,9 @@ #include "AppSettings.h" #include "Downloader.h" -#include "Env.h" #include "ProviderConfiguration.h" #include "SecureStorage.h" +#include "SingletonHelper.h" #include "VersionNumber.h" #if defined(Q_OS_WIN) || defined(Q_OS_MACOS) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) @@ -19,6 +19,8 @@ using namespace governikus; +defineSingleton(AppUpdater) + Q_DECLARE_LOGGING_CATEGORY(appupdate) AppUpdater::AppUpdater() @@ -32,6 +34,12 @@ AppUpdater::AppUpdater() } +AppUpdater& AppUpdater::getInstance() +{ + return *Instance; +} + + void AppUpdater::checkAppUpdate(bool pIgnoreNextVersionskip) { mIgnoreNextVersionskip = pIgnoreNextVersionskip; @@ -104,7 +112,6 @@ void AppUpdater::onUpdateDownloadFinished(const QUrl& pUpdateUrl, const QDateTim Q_EMIT fireAppUpdateCheckFinished(true, GlobalStatus::Code::No_Error); clearDownloaderConnection(); } - } diff --git a/src/services/AppUpdater.h b/src/services/AppUpdater.h index 3ea01a5..daa946a 100644 --- a/src/services/AppUpdater.h +++ b/src/services/AppUpdater.h @@ -5,11 +5,14 @@ #pragma once #include "AppUpdateData.h" +#include "Env.h" #include "GlobalStatus.h" #include #include +class test_AppUpdater; + namespace governikus { class AppUpdater @@ -18,17 +21,20 @@ class AppUpdater Q_OBJECT private: + friend class Env; + friend class ::test_AppUpdater; bool mIgnoreNextVersionskip; QUrl mAppUpdateJsonUrl; AppUpdateData mAppUpdateData; void clearDownloaderConnection(); - public: + protected: AppUpdater(); virtual ~AppUpdater() = default; static AppUpdater& getInstance(); + public: void checkAppUpdate(bool pIgnoreNextVersionskip = false); const AppUpdateData& getUpdateData() const; void skipVersion(const QString& pVersion); @@ -42,4 +48,4 @@ class AppUpdater void fireAppUpdateCheckFinished(bool pUpdateAvailable, const GlobalStatus& pError); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/services/CMakeLists.txt b/src/services/CMakeLists.txt index 1b4e456..69f12e4 100644 --- a/src/services/CMakeLists.txt +++ b/src/services/CMakeLists.txt @@ -1,7 +1,9 @@ +##################################################################### +# The module services is responsible to trigger periodic background +# services like checkes for appplication updates or updates for +# configuration files. +##################################################################### + ADD_PLATFORM_LIBRARY(AusweisAppServices) -TARGET_LINK_LIBRARIES(AusweisAppServices Qt5::Core AusweisAppCard AusweisAppNetwork) - -IF(DESKTOP) - TARGET_LINK_LIBRARIES(AusweisAppServices AusweisAppConfiguration) -ENDIF() +TARGET_LINK_LIBRARIES(AusweisAppServices Qt5::Core AusweisAppConfiguration AusweisAppFileProvider) diff --git a/src/services/Service.cpp b/src/services/Service.cpp index 665ffc9..d926f14 100644 --- a/src/services/Service.cpp +++ b/src/services/Service.cpp @@ -6,7 +6,6 @@ #include "AppSettings.h" #include "AppUpdateData.h" -#include "Env.h" #include "ProviderConfiguration.h" #include "SingletonHelper.h" @@ -24,10 +23,9 @@ Service::Service() : mTimer(this) , mUpdateScheduled(true) , mExplicitSuccessMessage(true) - , mAppUpdater() { connect(&mTimer, &QTimer::timeout, this, &Service::onTimedUpdateTriggered); - connect(&mAppUpdater, &AppUpdater::fireAppUpdateCheckFinished, this, &Service::onAppUpdateFinished); + connect(Env::getSingleton(), &AppUpdater::fireAppUpdateCheckFinished, this, &Service::onAppUpdateFinished); mTimer.setSingleShot(true); mTimer.start(mOneDayInMs); @@ -42,16 +40,17 @@ Service& Service::getInstance() void Service::updateConfigurations() { - QMetaObject::invokeMethod(this, "doConfigurationsUpdate", Qt::QueuedConnection); + QMetaObject::invokeMethod(this, &Service::doConfigurationsUpdate, Qt::QueuedConnection); } void Service::updateApp(bool pIgnoreNextVersionskip) { - mUpdateScheduled = false; mExplicitSuccessMessage = pIgnoreNextVersionskip; mTimer.start(mOneDayInMs); - QMetaObject::invokeMethod(this, "doAppUpdate", Qt::QueuedConnection, Q_ARG(bool, pIgnoreNextVersionskip)); + QMetaObject::invokeMethod(this, [ = ] { + doAppUpdate(pIgnoreNextVersionskip); + }, Qt::QueuedConnection); } @@ -67,7 +66,7 @@ void Service::doConfigurationsUpdate() void Service::doAppUpdate(bool pIgnoreNextVersionskip) { - mAppUpdater.checkAppUpdate(pIgnoreNextVersionskip); + Env::getSingleton()->checkAppUpdate(pIgnoreNextVersionskip); } @@ -88,6 +87,7 @@ void Service::runUpdateIfNeeded() { if (mUpdateScheduled) { + mUpdateScheduled = false; updateConfigurations(); if (Env::getSingleton()->getGeneralSettings().isAutoUpdateCheck()) { @@ -99,7 +99,7 @@ void Service::runUpdateIfNeeded() const AppUpdateData& Service::getUpdateData() const { - return mAppUpdater.getUpdateData(); + return Env::getSingleton()->getUpdateData(); } diff --git a/src/services/Service.h b/src/services/Service.h index 8bd6651..eb1903f 100644 --- a/src/services/Service.h +++ b/src/services/Service.h @@ -5,6 +5,7 @@ #pragma once #include "AppUpdater.h" +#include "Env.h" #include @@ -14,17 +15,18 @@ class Service : public QObject { Q_OBJECT + friend class Env; private: QTimer mTimer; bool mUpdateScheduled; bool mExplicitSuccessMessage; - AppUpdater mAppUpdater; const int mOneDayInMs = 1000 * 60 * 60 * 24; protected: Service(); - ~Service() = default; + virtual ~Service() = default; + static Service& getInstance(); private Q_SLOTS: void doConfigurationsUpdate(); @@ -33,7 +35,6 @@ class Service void onAppUpdateFinished(bool pUpdateAvailable, const GlobalStatus& pError); public: - static Service& getInstance(); void updateConfigurations(); void updateApp(bool pIgnoreNextVersionskip = false); bool isUpdateScheduled(); @@ -45,4 +46,4 @@ class Service void fireUpdateScheduled(); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/settings/AbstractSettings.cpp b/src/settings/AbstractSettings.cpp index 6d2783b..cc227fa 100644 --- a/src/settings/AbstractSettings.cpp +++ b/src/settings/AbstractSettings.cpp @@ -9,9 +9,6 @@ #ifdef Q_OS_MACOS #include #endif -#ifdef Q_OS_ANDROID - #include -#endif using namespace governikus; @@ -52,6 +49,7 @@ void AbstractSettings::createLegacyFileMapping() AbstractSettings::AbstractSettings() + : QObject() { } @@ -73,20 +71,8 @@ QSharedPointer AbstractSettings::getStore() Q_ASSERT(mTestDir->isValid()); } QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, mTestDir->path()); - return QSharedPointer(new QSettings(QSettings::IniFormat, QSettings::UserScope, QCoreApplication::organizationName(), QCoreApplication::applicationName())); + return QSharedPointer::create(QSettings::IniFormat, QSettings::UserScope, QCoreApplication::organizationName(), QCoreApplication::applicationName()); } #endif - return QSharedPointer(new QSettings()); -} - - -bool AbstractSettings::appIsBackgroundService() const -{ -#ifdef Q_OS_ANDROID - if (QtAndroid::androidService().isValid()) - { - return true; - } -#endif - return false; + return QSharedPointer::create(); } diff --git a/src/settings/AbstractSettings.h b/src/settings/AbstractSettings.h index a0a5f5c..18649cf 100644 --- a/src/settings/AbstractSettings.h +++ b/src/settings/AbstractSettings.h @@ -42,11 +42,9 @@ class AbstractSettings virtual void save() = 0; - bool appIsBackgroundService() const; - Q_SIGNALS: void fireSettingsChanged(); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/settings/AppSettings.cpp b/src/settings/AppSettings.cpp index 916af6d..e559ef6 100644 --- a/src/settings/AppSettings.cpp +++ b/src/settings/AppSettings.cpp @@ -6,6 +6,10 @@ #include "SingletonHelper.h" +#ifdef Q_OS_ANDROID + #include +#endif + using namespace governikus; defineSingleton(AppSettings) @@ -13,11 +17,19 @@ defineSingleton(AppSettings) AppSettings::AppSettings() : AbstractSettings() + , mUsedAsSdk(false) , mGeneralSettings() , mPreVerificationSettings() , mHistorySettings() , mRemoteReaderSettings() { +#ifdef Q_OS_ANDROID + if (QtAndroid::androidService().isValid()) + { + mUsedAsSdk = true; + } +#endif + connect(&mGeneralSettings, &AbstractSettings::fireSettingsChanged, this, &AbstractSettings::fireSettingsChanged); connect(&mPreVerificationSettings, &AbstractSettings::fireSettingsChanged, this, &AbstractSettings::fireSettingsChanged); connect(&mHistorySettings, &AbstractSettings::fireSettingsChanged, this, &AbstractSettings::fireSettingsChanged); @@ -36,6 +48,18 @@ AppSettings& AppSettings::getInstance() } +bool AppSettings::isUsedAsSDK() const +{ + return mUsedAsSdk; +} + + +void AppSettings::setUsedAsSDK(bool pSdk) +{ + mUsedAsSdk = pSdk; +} + + void AppSettings::save() { mGeneralSettings.save(); diff --git a/src/settings/AppSettings.h b/src/settings/AppSettings.h index 5fc77c7..4cb511d 100644 --- a/src/settings/AppSettings.h +++ b/src/settings/AppSettings.h @@ -7,6 +7,7 @@ #pragma once #include "AbstractSettings.h" +#include "Env.h" #include "GeneralSettings.h" #include "HistorySettings.h" #include "PreVerificationSettings.h" @@ -24,11 +25,14 @@ namespace governikus */ class AppSettings : public AbstractSettings + , private Env::ThreadSafe { Q_OBJECT + friend class Env; friend class ::test_AppSettings; private: + bool mUsedAsSdk; GeneralSettings mGeneralSettings; PreVerificationSettings mPreVerificationSettings; HistorySettings mHistorySettings; @@ -37,10 +41,12 @@ class AppSettings protected: AppSettings(); virtual ~AppSettings() override; + static AppSettings& getInstance(); public: - static AppSettings& getInstance(); virtual void save() override; + bool isUsedAsSDK() const; + void setUsedAsSDK(bool pSdk); virtual GeneralSettings& getGeneralSettings(); virtual PreVerificationSettings& getPreVerificationSettings(); @@ -49,4 +55,4 @@ class AppSettings }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/settings/AutoStart.h b/src/settings/AutoStart.h index 0284037..049f75e 100644 --- a/src/settings/AutoStart.h +++ b/src/settings/AutoStart.h @@ -8,13 +8,13 @@ namespace governikus { - class AutoStart { public: static bool enabled(); + static bool isSetByAdmin(); static void set(bool pEnabled); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/settings/AutoStart_generic.cpp b/src/settings/AutoStart_generic.cpp index 7b5170c..d1507b4 100644 --- a/src/settings/AutoStart_generic.cpp +++ b/src/settings/AutoStart_generic.cpp @@ -17,6 +17,12 @@ bool AutoStart::enabled() } +bool AutoStart::isSetByAdmin() +{ + return false; +} + + void AutoStart::set(bool pEnabled) { if (pEnabled) diff --git a/src/settings/AutoStart_osx.cpp b/src/settings/AutoStart_osx.cpp index 5da987a..f440f8f 100644 --- a/src/settings/AutoStart_osx.cpp +++ b/src/settings/AutoStart_osx.cpp @@ -8,6 +8,7 @@ #include #include +#import #import @@ -15,13 +16,11 @@ using namespace governikus; Q_DECLARE_LOGGING_CATEGORY(settings) -static bool checkAndRemoveAutoStart(bool pRemove) +static void cleanupOldAutoStart() { - qCDebug(settings) << "Loading OSX login items"; - - QRegularExpression regex("/Contents/Resources$"); - NSString* appPath = QCoreApplication::applicationDirPath().remove(regex).toNSString(); - CFURLRef url = static_cast([NSURL fileURLWithPath: appPath]); + const QRegularExpression regex(QStringLiteral("/Contents/Resources$")); + const QRegularExpression regex2(QStringLiteral("/Contents/MacOS$")); + NSString* appPath = QCoreApplication::applicationDirPath().remove(regex).remove(regex2).toNSString(); // Create a reference to the shared file list. LSSharedFileListRef loginItems = LSSharedFileListCreate(NULL, @@ -36,18 +35,15 @@ static bool checkAndRemoveAutoStart(bool pRemove) { LSSharedFileListItemRef itemRef = static_cast([loginItemsArray objectAtIndex:i]); //Resolve the item with URL - if (LSSharedFileListItemResolve(itemRef, 0, static_cast(&url), NULL) == noErr) + CFURLRef url = LSSharedFileListItemCopyResolvedURL(itemRef, 0, nullptr); + if (url) { NSURL* nsUrl = static_cast(url); NSString* urlPath = [nsUrl path]; if ([urlPath compare : appPath] == NSOrderedSame) { - if (pRemove) - { - LSSharedFileListItemRemove(loginItems, itemRef); - } - - return true; + LSSharedFileListItemRemove(loginItems, itemRef); + qCDebug(settings) << "Removed old autostart entry"; } } else @@ -66,37 +62,55 @@ static bool checkAndRemoveAutoStart(bool pRemove) } [loginItemsArray release]; } - - return false; } bool AutoStart::enabled() { - return checkAndRemoveAutoStart(false); + cleanupOldAutoStart(); + CFStringRef autostartBundleName = CFSTR("com.governikus.AusweisApp2.AutostartHelper"); + CFStringRef dictionaryKey = CFSTR("Label"); + CFArrayRef jobDictioniaries = SMCopyAllJobDictionaries(kSMDomainUserLaunchd); + if (jobDictioniaries == nullptr) + { + qCCritical(settings) << "Getting autostart entries failed"; + return false; + } + for (int i = 0; i < CFArrayGetCount(jobDictioniaries); ++i) + { + CFDictionaryRef jobDictionary = static_cast(CFArrayGetValueAtIndex(jobDictioniaries, i)); + if (jobDictionary == nullptr) + { + continue; + } + + CFStringRef jobLabel = static_cast(CFDictionaryGetValue(jobDictionary, dictionaryKey)); + if (jobLabel != nullptr && CFStringCompare(jobLabel, autostartBundleName, 0) == kCFCompareEqualTo) + { + qCDebug(settings) << "Autostart entry found"; + return true; + } + } + qCDebug(settings) << "No autostart entry found"; + return false; +} + + +bool AutoStart::isSetByAdmin() +{ + return false; } void AutoStart::set(bool pEnabled) { - if (pEnabled) + CFStringRef autostartBundleName = CFSTR("com.governikus.AusweisApp2.AutostartHelper"); + if (SMLoginItemSetEnabled(autostartBundleName, pEnabled)) { - QRegularExpression regex("/Contents/Resources$"); - NSString* path = QCoreApplication::applicationDirPath().remove(regex).toNSString(); - CFURLRef url = static_cast([NSURL fileURLWithPath: path]); - - LSSharedFileListRef loginItems = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL); - if (loginItems) - { - LSSharedFileListItemRef item = LSSharedFileListInsertItemURL(loginItems, kLSSharedFileListItemLast, NULL, NULL, url, NULL, NULL); - if (item) - { - CFRelease(item); - return; - } - } - return; + qCCritical(settings) << "Setting autostart succeded:" << pEnabled; + } + else + { + qCCritical(settings) << "Setting autostart failed"; } - - checkAndRemoveAutoStart(true); } diff --git a/src/settings/AutoStart_win.cpp b/src/settings/AutoStart_win.cpp index 5950a43..3579ad2 100644 --- a/src/settings/AutoStart_win.cpp +++ b/src/settings/AutoStart_win.cpp @@ -28,10 +28,15 @@ static QString registryPath() } -} +} // namespace bool AutoStart::enabled() { + if (isSetByAdmin()) + { + return true; + } + QSettings windowsBootUpSettings(registryPath(), QSettings::NativeFormat); if (!windowsBootUpSettings.contains(QCoreApplication::applicationName())) { @@ -49,16 +54,23 @@ bool AutoStart::enabled() } +bool AutoStart::isSetByAdmin() +{ + QSettings settings(QStringLiteral("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\Run"), QSettings::NativeFormat); + return settings.contains(QCoreApplication::applicationName()); +} + + void AutoStart::set(bool pEnabled) { QSettings windowsBootUpSettings(registryPath(), QSettings::NativeFormat); - if (pEnabled) - { - windowsBootUpSettings.setValue(QCoreApplication::applicationName(), appPath()); - } - else + if (isSetByAdmin() || !pEnabled) { windowsBootUpSettings.remove(QCoreApplication::applicationName()); } + else + { + windowsBootUpSettings.setValue(QCoreApplication::applicationName(), appPath()); + } } diff --git a/src/settings/CMakeLists.txt b/src/settings/CMakeLists.txt index 923a0f9..275c138 100644 --- a/src/settings/CMakeLists.txt +++ b/src/settings/CMakeLists.txt @@ -1,9 +1,17 @@ +##################################################################### +# The module settings is responsible for user settings. +# They will be stored with QSettings using platform specific format. +# +# Any user changeable settings will be saved and loaded by this +# module. +##################################################################### + ADD_PLATFORM_LIBRARY(AusweisAppSettings) -TARGET_LINK_LIBRARIES(AusweisAppSettings Qt5::Core Qt5::Network OpenSSL::Crypto AusweisAppGlobal) +TARGET_LINK_LIBRARIES(AusweisAppSettings Qt5::Core Qt5::Network Qt5::Concurrent OpenSSL::Crypto AusweisAppGlobal) IF(MAC) - TARGET_LINK_LIBRARIES(AusweisAppSettings ${OSX_APPKIT}) + TARGET_LINK_LIBRARIES(AusweisAppSettings ${OSX_APPKIT} ${OSX_SERVICEMANAGEMENT}) ENDIF() IF(ANDROID) diff --git a/src/settings/GeneralSettings.cpp b/src/settings/GeneralSettings.cpp index 0c5ac16..134da3d 100644 --- a/src/settings/GeneralSettings.cpp +++ b/src/settings/GeneralSettings.cpp @@ -6,7 +6,9 @@ #include "GeneralSettings.h" +#include "AppSettings.h" #include "AutoStart.h" +#include "Env.h" #include #include @@ -28,12 +30,18 @@ SETTINGS_NAME(SETTINGS_NAME_TRANSPORT_PIN_REMINDER, "transportPinReminder") SETTINGS_NAME(SETTINGS_NAME_DEVELOPER_MODE, "developerMode") SETTINGS_NAME(SETTINGS_NAME_USE_SELF_AUTH_TEST_URI, "selfauthTestUri") SETTINGS_NAME(SETTINGS_NAME_LANGUAGE, "language") +SETTINGS_NAME(SETTINGS_NAME_SELECTED_UI, "selectedUi") +SETTINGS_NAME(SETTINGS_NAME_DEVICE_SURVEY_PENDING, "deviceSurveyPending") +SETTINGS_NAME(SETTINGS_NAME_LAST_READER_PLUGIN_TYPE, "lastTechnology") + +#if !defined(Q_OS_IOS) +SETTINGS_NAME(SETTINGS_NAME_REQUEST_STORE_FEEDBACK, "requestStoreFeedback") +#endif SETTINGS_NAME(SETTINGS_GROUP_NAME_COMMON, "common") SETTINGS_NAME(SETTINGS_NAME_AUTO, "autoUpdateCheck") SETTINGS_NAME(SETTINGS_NAME_KEYLESS_PASSWORD, "keylessPassword") -SETTINGS_NAME(SETTINGS_NAME_LAST_READER_PLUGIN_TYPE, "lastTechnology") -} +} // namespace GeneralSettings::GeneralSettings() : AbstractSettings() @@ -63,12 +71,11 @@ GeneralSettings::GeneralSettings() // Check if the key "autoCloseWindow" (introduced in changeset 199210b0b20c) // does not yet exist to detect a new installation. This key was the first one // in the settings general group. - const bool isNewInstallation = !mStoreGeneral->allKeys().contains(SETTINGS_NAME_AUTO_CLOSE_WINDOW()); + const bool isNewInstallation = getPersistentSettingsVersion().isEmpty(); if (isNewInstallation) { - mStoreGeneral->setValue(SETTINGS_NAME_AUTO_CLOSE_WINDOW(), true); + mStoreGeneral->setValue(SETTINGS_NAME_PERSISTENT_SETTINGS_VERSION(), QCoreApplication::applicationVersion()); setAutoStart(GENERAL_SETTINGS_DEFAULT_AUTOSTART); - setTransportPinReminder(true); mStoreGeneral->sync(); } @@ -101,6 +108,12 @@ bool GeneralSettings::isAutoStart() const } +bool GeneralSettings::autoStartIsSetByAdmin() const +{ + return AutoStart::isSetByAdmin(); +} + + void GeneralSettings::setAutoStart(bool pAutoStart) { if (pAutoStart != mAutoStart) @@ -185,11 +198,7 @@ void GeneralSettings::setRemindUserToClose(bool pRemindUser) bool GeneralSettings::isTransportPinReminder() const { - // The standard value should be true but we need to set it to false for backwards - // compatibility. It is set to true in the constructor if we have a new - // installation. The the standard value used in this function will only be - // used if the user upgrades from AusweisApp 1.6.3 or an older version. - return mStoreGeneral->value(SETTINGS_NAME_TRANSPORT_PIN_REMINDER(), false).toBool(); + return mStoreGeneral->value(SETTINGS_NAME_TRANSPORT_PIN_REMINDER(), true).toBool(); } @@ -206,9 +215,9 @@ void GeneralSettings::setTransportPinReminder(bool pTransportPinReminder) bool GeneralSettings::isDeveloperMode() const { const bool developerMode = mStoreGeneral->value(SETTINGS_NAME_DEVELOPER_MODE(), false).toBool(); - if (developerMode && appIsBackgroundService()) + if (developerMode && Env::getSingleton()->isUsedAsSDK()) { - qCDebug(settings) << "Running as a background service. Developer mode is disallowed."; + qCDebug(settings) << "Running as SDK. Developer mode is disallowed."; return false; } @@ -264,38 +273,75 @@ void GeneralSettings::setLanguage(const QLocale::Language pLanguage) } -bool GeneralSettings::isAutoUpdateCheck() const +QString GeneralSettings::getSelectedUi() const { - return mStoreCommon->value(SETTINGS_NAME_AUTO(), true).toBool(); + return mStoreGeneral->value(SETTINGS_NAME_SELECTED_UI(), QStringLiteral(DEFAULT_UI)).toString(); } -void GeneralSettings::setAutoUpdateCheck(bool pAutoCheck) +void GeneralSettings::setSelectedUi(const QString& pSelectedUi) { - if (pAutoCheck != isAutoUpdateCheck()) + if (pSelectedUi != getSelectedUi()) { - mStoreCommon->setValue(SETTINGS_NAME_AUTO(), pAutoCheck); + mStoreGeneral->setValue(SETTINGS_NAME_SELECTED_UI(), pSelectedUi); Q_EMIT fireSettingsChanged(); } } -bool GeneralSettings::isUseScreenKeyboard() const +bool GeneralSettings::askForDeviceSurvey() const { - return mStoreCommon->value(SETTINGS_NAME_KEYLESS_PASSWORD(), false).toBool(); + return !mStoreGeneral->contains(SETTINGS_NAME_DEVICE_SURVEY_PENDING()); } -void GeneralSettings::setUseScreenKeyboard(bool pKeylessPassword) +bool GeneralSettings::isDeviceSurveyPending() const { - if (pKeylessPassword != isUseScreenKeyboard()) + return mStoreGeneral->value(SETTINGS_NAME_DEVICE_SURVEY_PENDING(), false).toBool(); +} + + +void GeneralSettings::setDeviceSurveyPending(bool pDeviceSurveyPending) +{ + if (askForDeviceSurvey() || pDeviceSurveyPending != isDeviceSurveyPending()) { - mStoreCommon->setValue(SETTINGS_NAME_KEYLESS_PASSWORD(), pKeylessPassword); + mStoreGeneral->setValue(SETTINGS_NAME_DEVICE_SURVEY_PENDING(), pDeviceSurveyPending); Q_EMIT fireSettingsChanged(); } } +bool GeneralSettings::isRequestStoreFeedback() const +{ +#if defined(Q_OS_IOS) + qCWarning(settings) << "NOT IMPLEMENTED"; + return false; + +#else + + return mStoreGeneral->value(SETTINGS_NAME_REQUEST_STORE_FEEDBACK(), true).toBool(); + +#endif +} + + +void GeneralSettings::setRequestStoreFeedback(bool pRequest) +{ +#if defined(Q_OS_IOS) + Q_UNUSED(pRequest); + qCWarning(settings) << "NOT IMPLEMENTED"; + return; + +#else + if (pRequest != isRequestStoreFeedback()) + { + mStoreGeneral->setValue(SETTINGS_NAME_REQUEST_STORE_FEEDBACK(), pRequest); + Q_EMIT fireSettingsChanged(); + } +#endif +} + + QString GeneralSettings::getLastReaderPluginType() const { return mStoreGeneral->value(SETTINGS_NAME_LAST_READER_PLUGIN_TYPE(), QString()).toString(); @@ -310,3 +356,53 @@ void GeneralSettings::setLastReaderPluginType(const QString& pLastReaderPluginTy Q_EMIT fireSettingsChanged(); } } + + +bool GeneralSettings::isAutoUpdateCheck() const +{ + if (autoUpdateCheckIsSetByAdmin()) + { + mStoreCommon->remove(SETTINGS_NAME_AUTO()); + } + + return mStoreCommon->value(SETTINGS_NAME_AUTO(), true).toBool(); +} + + +bool GeneralSettings::autoUpdateCheckIsSetByAdmin() const +{ +#ifdef Q_OS_MACOS + QSettings settings(QSettings::Scope::SystemScope, QCoreApplication::organizationDomain(), QCoreApplication::applicationName()); +#else + QSettings settings(QSettings::Scope::SystemScope, QCoreApplication::organizationName(), QCoreApplication::applicationName()); +#endif + + settings.beginGroup(SETTINGS_GROUP_NAME_COMMON()); + return settings.contains(SETTINGS_NAME_AUTO()); +} + + +void GeneralSettings::setAutoUpdateCheck(bool pAutoUpdateCheck) +{ + if (!autoUpdateCheckIsSetByAdmin() && pAutoUpdateCheck != isAutoUpdateCheck()) + { + mStoreCommon->setValue(SETTINGS_NAME_AUTO(), pAutoUpdateCheck); + Q_EMIT fireSettingsChanged(); + } +} + + +bool GeneralSettings::isUseScreenKeyboard() const +{ + return mStoreCommon->value(SETTINGS_NAME_KEYLESS_PASSWORD(), false).toBool(); +} + + +void GeneralSettings::setUseScreenKeyboard(bool pUseScreenKeyboard) +{ + if (pUseScreenKeyboard != isUseScreenKeyboard()) + { + mStoreCommon->setValue(SETTINGS_NAME_KEYLESS_PASSWORD(), pUseScreenKeyboard); + Q_EMIT fireSettingsChanged(); + } +} diff --git a/src/settings/GeneralSettings.h b/src/settings/GeneralSettings.h index 1bfab5b..70ffb12 100644 --- a/src/settings/GeneralSettings.h +++ b/src/settings/GeneralSettings.h @@ -23,6 +23,12 @@ namespace governikus #define GENERAL_SETTINGS_DEFAULT_AUTOSTART false #endif +#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) + #define DEFAULT_UI "qml" +#else + #define DEFAULT_UI "widgets" +#endif + class GeneralSettings : public AbstractSettings @@ -45,6 +51,7 @@ class GeneralSettings virtual void save() override; bool isAutoStart() const; + bool autoStartIsSetByAdmin() const; void setAutoStart(bool pAutoStart); const QString getPersistentSettingsVersion() const; @@ -73,15 +80,26 @@ class GeneralSettings QLocale::Language getLanguage() const; void setLanguage(const QLocale::Language pLanguage); + QString getSelectedUi() const; + void setSelectedUi(const QString& pSelectedUi); + + bool askForDeviceSurvey() const; + bool isDeviceSurveyPending() const; + void setDeviceSurveyPending(bool pDeviceSurveyPending); + + bool isRequestStoreFeedback() const; + void setRequestStoreFeedback(bool pRequest); + + QString getLastReaderPluginType() const; + void setLastReaderPluginType(const QString& pLastReaderPluginType); + bool isAutoUpdateCheck() const; + bool autoUpdateCheckIsSetByAdmin() const; void setAutoUpdateCheck(bool pAutoUpdateCheck); bool isUseScreenKeyboard() const; void setUseScreenKeyboard(bool pUseScreenKeyboard); - - QString getLastReaderPluginType() const; - void setLastReaderPluginType(const QString& pLastReaderPluginType); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/settings/HistoryInfo.cpp b/src/settings/HistoryInfo.cpp index 25e5ed7..ca13025 100644 --- a/src/settings/HistoryInfo.cpp +++ b/src/settings/HistoryInfo.cpp @@ -11,7 +11,7 @@ using namespace governikus; -HistoryInfo::HistoryInfo(const QString& pSubjectName, const QString& pSubjectUrl, const QString& pUsage, const QDateTime& pDateTime, const QString& pTermOfUsage, const QString& pRequestedData) +HistoryInfo::HistoryInfo(const QString& pSubjectName, const QString& pSubjectUrl, const QString& pUsage, const QDateTime& pDateTime, const QString& pTermOfUsage, const QStringList& pRequestedData) : mSubjectName(pSubjectName) , mSubjectUrl(pSubjectUrl) , mPurpose(pUsage) @@ -30,7 +30,7 @@ QDateTime HistoryInfo::roundToSeconds(const QDateTime& pDateTime) } -const QString& HistoryInfo::getRequestedData() const +const QStringList& HistoryInfo::getRequestedData() const { return mRequestedData; } diff --git a/src/settings/HistoryInfo.h b/src/settings/HistoryInfo.h index e4aab61..ada9918 100644 --- a/src/settings/HistoryInfo.h +++ b/src/settings/HistoryInfo.h @@ -51,7 +51,7 @@ class HistoryInfo /*! * The requested data fields during authentication */ - QString mRequestedData; + QStringList mRequestedData; public: HistoryInfo() @@ -59,7 +59,7 @@ class HistoryInfo } - HistoryInfo(const QString& pSubjectName, const QString& pSubjectUrl, const QString& pUsage, const QDateTime& pDateTime, const QString& pTermOfUsage, const QString& pRequestedData); + HistoryInfo(const QString& pSubjectName, const QString& pSubjectUrl, const QString& pUsage, const QDateTime& pDateTime, const QString& pTermOfUsage, const QStringList& pRequestedData); bool operator==(const HistoryInfo& pOther) const { @@ -79,8 +79,8 @@ class HistoryInfo const QString& getPurpose() const; const QDateTime& getDateTime() const; const QString& getTermOfUsage() const; - const QString& getRequestedData() const; + const QStringList& getRequestedData() const; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/settings/HistorySettings.cpp b/src/settings/HistorySettings.cpp index bd2daa6..feedd8f 100644 --- a/src/settings/HistorySettings.cpp +++ b/src/settings/HistorySettings.cpp @@ -4,8 +4,10 @@ #include "HistorySettings.h" +#include "AppSettings.h" +#include "Env.h" + #include -#include namespace { @@ -18,7 +20,7 @@ SETTINGS_NAME(SETTINGS_NAME_CHRONIC_USAGE, "usage") SETTINGS_NAME(SETTINGS_NAME_CHRONIC_DATETIME, "dateTime") SETTINGS_NAME(SETTINGS_NAME_CHRONIC_TOU, "termOfUsage") SETTINGS_NAME(SETTINGS_NAME_CHRONIC_REQUESTED_DATA, "requestedData") -} +} // namespace using namespace governikus; @@ -73,7 +75,7 @@ QVector HistorySettings::getHistoryInfos() const const QString usage = mStore->value(SETTINGS_NAME_CHRONIC_USAGE(), QString()).toString(); const QDateTime dateTime = QDateTime::fromString(mStore->value(SETTINGS_NAME_CHRONIC_DATETIME(), QString()).toString(), Qt::ISODate); const QString termsOfUsage = mStore->value(SETTINGS_NAME_CHRONIC_TOU(), QString()).toString(); - const QString requestData = mStore->value(SETTINGS_NAME_CHRONIC_REQUESTED_DATA(), QString()).toString(); + const QStringList requestData = mStore->value(SETTINGS_NAME_CHRONIC_REQUESTED_DATA(), QStringList()).toStringList(); historyInfos += HistoryInfo(subjectName, subjectUrl, usage, dateTime, termsOfUsage, requestData); } @@ -109,9 +111,9 @@ void HistorySettings::setHistoryInfos(const QVector& pHistoryInfos) void HistorySettings::addHistoryInfo(const HistoryInfo& pHistoryInfo) { - if (appIsBackgroundService()) + if (Env::getSingleton()->isUsedAsSDK()) { - qCDebug(settings) << "Running as a background service. Ignoring save request for history."; + qCDebug(settings) << "Running as SDK. Ignoring save request for history."; return; } @@ -160,7 +162,7 @@ int HistorySettings::deleteSettings(const TimePeriod& pPeriodToRemove) break; case TimePeriod::ALL_HISTORY: - latestToKeep = QDateTime(); + latestToKeep = QDateTime::fromMSecsSinceEpoch(1); break; case TimePeriod::UNKNOWN: diff --git a/src/settings/HistorySettings.h b/src/settings/HistorySettings.h index 41bc795..f19f0ee 100644 --- a/src/settings/HistorySettings.h +++ b/src/settings/HistorySettings.h @@ -60,4 +60,4 @@ class HistorySettings }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/settings/KeyPair.cpp b/src/settings/KeyPair.cpp index 5ac3327..59592cd 100644 --- a/src/settings/KeyPair.cpp +++ b/src/settings/KeyPair.cpp @@ -8,8 +8,10 @@ #include "Randomizer.h" #include +#include #include #include +#include #include #include @@ -54,7 +56,7 @@ struct OpenSslCustomDeleter }; -} +} // namespace KeyPair::KeyPair(const QSslKey& pKey, const QSslCertificate& pCert) : mKey(pKey) @@ -144,10 +146,15 @@ QSharedPointer KeyPair::createCertificate(EVP_PKEY* pPkey) std::uniform_int_distribution uni_long(1); std::uniform_int_distribution uni_qulonglong(1); + #if OPENSSL_VERSION_NUMBER < 0x10100000L + #define X509_getm_notBefore X509_get_notBefore + #define X509_getm_notAfter X509_get_notAfter + #endif + ASN1_INTEGER_set(X509_get_serialNumber(x509.data()), uni_long(randomizer)); // see: https://tools.ietf.org/html/rfc5280#section-4.1.2.5 - ASN1_TIME_set_string(X509_get_notBefore(x509.data()), "19700101000000Z"); - ASN1_TIME_set_string(X509_get_notAfter(x509.data()), "99991231235959Z"); + ASN1_TIME_set_string(X509_getm_notBefore(x509.data()), "19700101000000Z"); + ASN1_TIME_set_string(X509_getm_notAfter(x509.data()), "99991231235959Z"); X509_set_pubkey(x509.data(), pPkey); auto randomSerial = QByteArray::number(uni_qulonglong(randomizer)); diff --git a/src/settings/KeyPair.h b/src/settings/KeyPair.h index bbf4e28..db074ce 100644 --- a/src/settings/KeyPair.h +++ b/src/settings/KeyPair.h @@ -39,4 +39,4 @@ class KeyPair }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/settings/PreVerificationSettings.cpp b/src/settings/PreVerificationSettings.cpp index 519e947..7585ccb 100644 --- a/src/settings/PreVerificationSettings.cpp +++ b/src/settings/PreVerificationSettings.cpp @@ -12,7 +12,7 @@ SETTINGS_NAME(SETTINGS_GROUP_NAME_PREVERIFICATION, "preverification") SETTINGS_NAME(SETTINGS_NAME_ENABLED, "enabled") SETTINGS_NAME(SETTINGS_NAME_LINKCERTIFICATES, "linkcertificates") SETTINGS_NAME(SETTINGS_NAME_LINKCERTIFICATE, "linkcertificate") -} +} // namespace PreVerificationSettings::PreVerificationSettings() : AbstractSettings() diff --git a/src/settings/PreVerificationSettings.h b/src/settings/PreVerificationSettings.h index 323fa7f..1ff6df5 100644 --- a/src/settings/PreVerificationSettings.h +++ b/src/settings/PreVerificationSettings.h @@ -23,7 +23,6 @@ class PreVerificationSettings friend class AppSettings; friend class ::test_PreVerificationSettings; - friend class ::test_StatePreVerification; private: QSharedPointer mStore; @@ -43,4 +42,4 @@ class PreVerificationSettings }; -} /* namespace governikus */ +} // namespace governikus diff --git a/src/settings/RemoteServiceSettings.cpp b/src/settings/RemoteServiceSettings.cpp index 4e85b77..dd14d55 100644 --- a/src/settings/RemoteServiceSettings.cpp +++ b/src/settings/RemoteServiceSettings.cpp @@ -5,15 +5,19 @@ #include "RemoteServiceSettings.h" #include "DeviceInfo.h" +#include "KeyPair.h" #include #include #include #include +#include #include using namespace governikus; +Q_DECLARE_LOGGING_CATEGORY(settings) + namespace { SETTINGS_NAME(SETTINGS_GROUP_NAME_REMOTEREADER, "remotereader") @@ -24,7 +28,7 @@ SETTINGS_NAME(SETTINGS_NAME_TRUSTED_CERTIFICATE_ITEM, "certificate") SETTINGS_NAME(SETTINGS_NAME_TRUSTED_REMOTE_INFO, "trustedRemoteInfo") SETTINGS_NAME(SETTINGS_NAME_KEY, "key") SETTINGS_NAME(SETTINGS_NAME_CERTIFICATE, "certificate") -} +} // namespace QString RemoteServiceSettings::generateFingerprint(const QSslCertificate& pCert) @@ -173,6 +177,29 @@ void RemoteServiceSettings::removeTrustedCertificate(const QString& pFingerprint } +bool RemoteServiceSettings::checkAndGenerateKey(bool pForceGeneration) +{ + if (getKey().isNull() + || getCertificate().isNull() + || getCertificate().expiryDate() < QDateTime::currentDateTime() + || pForceGeneration) + { + qCDebug(settings) << "Generate local keypair..."; + const auto& pair = KeyPair::generate(); + if (pair.isValid()) + { + setKey(pair.getKey()); + setCertificate(pair.getCertificate()); + return true; + } + + return false; + } + + return true; +} + + QSslCertificate RemoteServiceSettings::getCertificate() const { return QSslCertificate(mStore->value(SETTINGS_NAME_CERTIFICATE(), QByteArray()).toByteArray()); diff --git a/src/settings/RemoteServiceSettings.h b/src/settings/RemoteServiceSettings.h index a512d51..6e31428 100644 --- a/src/settings/RemoteServiceSettings.h +++ b/src/settings/RemoteServiceSettings.h @@ -92,6 +92,8 @@ class RemoteServiceSettings void removeTrustedCertificate(const QSslCertificate& pCertificate); void removeTrustedCertificate(const QString& pFingerprint); + bool checkAndGenerateKey(bool pForceGeneration = false); + QSslCertificate getCertificate() const; void setCertificate(const QSslCertificate& pCert) const; @@ -109,6 +111,6 @@ class RemoteServiceSettings }; -} /* namespace governikus */ +} // namespace governikus Q_DECLARE_TYPEINFO(governikus::RemoteServiceSettings::RemoteInfo, Q_MOVABLE_TYPE); diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt new file mode 100644 index 0000000..3eef3e0 --- /dev/null +++ b/src/ui/CMakeLists.txt @@ -0,0 +1,27 @@ +##################################################################### +# The module ui provides a lean interface that will be implemented +# by different plugins. It provides a generic API that will be used +# for communication between core and ui. +##################################################################### + +ADD_SUBDIRECTORY(base) +ADD_SUBDIRECTORY(common) + +# Use this if we can use QSvgPlugin without Widgets +# IF(TARGET Qt5::Widgets) +# https://bugreports.qt.io/browse/QTBUG-41884 +IF(DESKTOP) + ADD_SUBDIRECTORY(widget) +ENDIF() + +IF(DESKTOP) + ADD_SUBDIRECTORY(cli) +ENDIF() + +ADD_SUBDIRECTORY(jsonapi) +ADD_SUBDIRECTORY(aidl) +ADD_SUBDIRECTORY(websocket) + +IF(TARGET Qt5::Qml) + ADD_SUBDIRECTORY(qml) +ENDIF() diff --git a/src/ui/aidl/AidlBinder.java b/src/ui/aidl/AidlBinder.java new file mode 100644 index 0000000..8f9b6c8 --- /dev/null +++ b/src/ui/aidl/AidlBinder.java @@ -0,0 +1,162 @@ +/* + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +package com.governikus.ausweisapp2; + +import org.qtproject.qt5.android.QtNative; + +import android.content.Intent; +import android.nfc.NfcAdapter; +import android.nfc.Tag; +import android.os.DeadObjectException; +import android.os.IBinder; +import android.util.Log; + + +class AidlBinder extends IAusweisApp2Sdk.Stub +{ + private static final String LOG_TAG = AusweisApp2Service.LOG_TAG; + private IAusweisApp2SdkCallback mCallback; + private String mCallbackSessionId; + + private void cleanUpDeadCallback() + { + if (mCallback == null) + { + return; + } + + IBinder binder = mCallback.asBinder(); + if (!binder.isBinderAlive() || !binder.pingBinder()) + { + Log.i(LOG_TAG, "Android service: Removing dead callback."); + mCallback = null; + } + } + + + private void handleClientException(Throwable pException) + { + Log.w(LOG_TAG, "Android service: Connected client sent an exception. Dropping client.", pException); + mCallback = null; + } + + + public synchronized boolean connectSdk(IAusweisApp2SdkCallback pCallback) + { + if (pCallback == null) + { + Log.w(LOG_TAG, "Android service: Supplied callback is null."); + return false; + } + + cleanUpDeadCallback(); + if (mCallback != null) + { + Log.i(LOG_TAG, "Android service: A client is already connected. Dropping previous callback."); + try + { + mCallbackSessionId = null; + mCallback.sdkDisconnected(); + } + catch (Throwable t) + { + handleClientException(t); + } + } + + mCallbackSessionId = resetValidSessionID(); + if (mCallbackSessionId.isEmpty()) + { + return false; + } + + mCallback = pCallback; + final boolean sessionIdIsSecure = isSecureRandomPsk(); + Log.i(LOG_TAG, "Android service: Callback connected."); + + try + { + mCallback.sessionIdGenerated(sessionIdIsSecure ? mCallbackSessionId : null, sessionIdIsSecure); + } + catch (Throwable t) + { + handleClientException(t); + } + return sessionIdIsSecure; + } + + + private boolean isValidSessionId(String pSessionId) + { + if (mCallback == null || pSessionId.compareTo(mCallbackSessionId) != 0) + { + Log.w(LOG_TAG, "Android service: Invalid sessiond ID!"); + return false; + } + return true; + } + + + public synchronized boolean send(String pSessionId, String pMessageFromClient) + { + Log.d(LOG_TAG, "Android service: Received JSON from client"); + + if (!isValidSessionId(pSessionId)) + { + return false; + } + + aidlSend(pMessageFromClient); + return true; + } + + + public synchronized boolean updateNfcTag(String pSessionId, Tag pTag) + { + Log.d(LOG_TAG, "Android service: Received nfc tag from client"); + + if (!isValidSessionId(pSessionId)) + { + return false; + } + + Intent newIntent = new Intent(); + newIntent.putExtra(NfcAdapter.EXTRA_TAG, pTag); + QtNative.onNewIntent(newIntent); + + return true; + } + + + public synchronized void aidlReceive(String pMessageToClient) + { + Log.d(LOG_TAG, "Android service: Passing JSON to client"); + + if (mCallback == null) + { + Log.d(LOG_TAG, "Android service: Callback not connected."); + return; + } + + try + { + mCallback.receive(pMessageToClient); + } + catch (DeadObjectException e) + { + Log.w(LOG_TAG, "Android service: Connected client is already dead."); + mCallback = null; + } + catch (Throwable t) + { + handleClientException(t); + } + } + + + private native String resetValidSessionID(); + private native boolean isSecureRandomPsk(); + private native void aidlSend(String pMessageFromClient); +} diff --git a/src/ui/aidl/CMakeLists.txt b/src/ui/aidl/CMakeLists.txt new file mode 100644 index 0000000..f71a279 --- /dev/null +++ b/src/ui/aidl/CMakeLists.txt @@ -0,0 +1,10 @@ +##################################################################### +# The aidl plugin implements the ui interface for Android's AIDL. +# +# It provides a background service for the SDK. +##################################################################### + +ADD_PLATFORM_LIBRARY(AusweisAppUiAidl) + +TARGET_LINK_LIBRARIES(AusweisAppUiAidl Qt5::Core AusweisAppUiJsonApi AusweisAppGlobal) +TARGET_COMPILE_DEFINITIONS(AusweisAppUiAidl PRIVATE QT_STATICPLUGIN) diff --git a/src/aidl/PskManager.cpp b/src/ui/aidl/PskManager.cpp similarity index 100% rename from src/aidl/PskManager.cpp rename to src/ui/aidl/PskManager.cpp diff --git a/src/ui/aidl/PskManager.h b/src/ui/aidl/PskManager.h new file mode 100644 index 0000000..bf56201 --- /dev/null +++ b/src/ui/aidl/PskManager.h @@ -0,0 +1,29 @@ +/*! + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include + +namespace governikus +{ + +class PskManager +{ + private: + QByteArray mPsk; + bool mSecureRandomPsk; + QMutex mPskMutex; + + public: + static PskManager& getInstance(); + + PskManager(); + QByteArray generatePsk(); + QByteArray getPsk(); + bool isSecureRandomPsk(); +}; + +} // namespace governikus diff --git a/src/ui/aidl/UIPlugInAidl.cpp b/src/ui/aidl/UIPlugInAidl.cpp new file mode 100644 index 0000000..99179a9 --- /dev/null +++ b/src/ui/aidl/UIPlugInAidl.cpp @@ -0,0 +1,215 @@ +/*! + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "UIPlugInAidl.h" + +#include "UILoader.h" +#ifdef Q_OS_ANDROID +#include "PskManager.h" +#include +#endif + +#include +#include +#include +#include +#include + +#ifdef Q_OS_ANDROID +#include +#include +#include +#endif + + +Q_DECLARE_LOGGING_CATEGORY(aidl) + +using namespace governikus; + + +QAtomicPointer UIPlugInAidl::instance = nullptr; + +UIPlugInAidl::UIPlugInAidl() + : UIPlugIn() + , mJsonApi(nullptr) + , mContext() + , mWorkflowIsActive() + , mInitializationSuccessfull(false) +{ + if (UILoader::getInstance().load(UIPlugInName::UIPlugInJsonApi)) + { + mJsonApi = qobject_cast(UILoader::getInstance().getLoaded(UIPlugInName::UIPlugInJsonApi)); + Q_ASSERT(mJsonApi); + connect(mJsonApi, &UIPlugInJsonApi::fireMessage, this, &UIPlugInAidl::onToSend, Qt::QueuedConnection); + + mJsonApi->setEnabled(); + mInitializationSuccessfull = true; + } + else + { + qWarning(aidl) << "Cannot start AIDL because JSON-API is missing"; + } + + instance = this; +} + + +UIPlugInAidl::~UIPlugInAidl() +{ +} + + +UIPlugInAidl* UIPlugInAidl::getInstance(bool pBlock) +{ + // The Java interface thread is ready before our core has booted. + // Hence we delay access to the UIPlugInAidl. + if (pBlock) + { + while (instance.load() == nullptr) + { + QThread::msleep(100); + } + } + + return instance.load(); +} + + +bool UIPlugInAidl::isSuccessfullInitialized() +{ + return mInitializationSuccessfull; +} + + +void UIPlugInAidl::onWorkflowStarted(QSharedPointer pContext) +{ + mWorkflowIsActive.lock(); + pContext->setReaderPlugInTypes({ReaderManagerPlugInType::NFC}); + mContext = pContext; +} + + +void UIPlugInAidl::onWorkflowFinished(QSharedPointer pContext) +{ + Q_UNUSED(pContext); + + mContext.clear(); + mJsonApi->blockSignals(false); + mWorkflowIsActive.unlock(); +} + + +void UIPlugInAidl::onReceived(const QByteArray& pMessage) +{ + mJsonApi->doMessageProcessing(pMessage); +} + + +bool UIPlugInAidl::waitForWorkflowToFinish() +{ + const int fiveSeconds = 5000; + bool success = mWorkflowIsActive.tryLock(fiveSeconds); + if (success) + { + mWorkflowIsActive.unlock(); + } + return success; +} + + +void UIPlugInAidl::reset() +{ + if (mContext) + { + mJsonApi->blockSignals(true); + Q_EMIT mContext->fireCancelWorkflow(); + } +} + + +void UIPlugInAidl::onToSend(const QByteArray& pMessage) +{ +#ifdef Q_OS_ANDROID + const QString json = QString::fromUtf8(pMessage); + QAndroidJniObject jsonAndroidString = QAndroidJniObject::fromString(json); + + QAndroidJniObject aidlBinder = QtAndroid::androidService().callObjectMethod("getAidlBinder", "()Lcom/governikus/ausweisapp2/AidlBinder;"); + aidlBinder.callMethod("aidlReceive", "(Ljava/lang/String;)V", jsonAndroidString.object()); +#else + Q_UNUSED(pMessage); +#endif +} + + +void UIPlugInAidl::doShutdown() +{ +} + + +#ifdef Q_OS_ANDROID +extern "C" +{ + +// These functions need to be explicitly exported so that the JVM can bind to them. +// At the moment only the Q_Plugins seem to be appropriate locations. + +JNIEXPORT jstring JNICALL Java_com_governikus_ausweisapp2_AidlBinder_resetValidSessionID(JNIEnv* pEnv, jobject pObj) +{ + Q_UNUSED(pObj); + + UIPlugInAidl* plugin = UIPlugInAidl::getInstance(); + if (!plugin->isSuccessfullInitialized()) + { + qCCritical(aidl) << "Cannot call AIDL plugin"; + return pEnv->NewStringUTF(""); + } + QMetaObject::invokeMethod(plugin, &UIPlugInAidl::reset, Qt::QueuedConnection); + if (!plugin->waitForWorkflowToFinish()) + { + qCCritical(aidl) << "Cannot acquire workflow mutex"; + return pEnv->NewStringUTF(""); + } + + const auto& finalPsk = PskManager::getInstance().generatePsk(); + return pEnv->NewStringUTF(finalPsk.constData()); +} + + +JNIEXPORT jboolean JNICALL Java_com_governikus_ausweisapp2_AidlBinder_isSecureRandomPsk(JNIEnv* pEnv, jobject pObj) +{ + Q_UNUSED(pEnv); + Q_UNUSED(pObj); + + return PskManager::getInstance().isSecureRandomPsk(); +} + + +} + +extern "C" +{ + +JNIEXPORT void JNICALL Java_com_governikus_ausweisapp2_AidlBinder_aidlSend(JNIEnv* pEnv, jobject pObj, jstring pJson) +{ + Q_UNUSED(pObj); + + const char* nativeString = pEnv->GetStringUTFChars(pJson, 0); + const QString json = QString::fromUtf8(nativeString); + pEnv->ReleaseStringUTFChars(pJson, nativeString); + + UIPlugInAidl* plugin = UIPlugInAidl::getInstance(); + if (!plugin->isSuccessfullInitialized()) + { + qCritical(aidl) << "Cannot call AIDL plugin"; + return; + } + + QMetaObject::invokeMethod(plugin, [ = ] { + plugin->onReceived(json.toUtf8()); + }, Qt::QueuedConnection); +} + + +} +#endif diff --git a/src/ui/aidl/UIPlugInAidl.h b/src/ui/aidl/UIPlugInAidl.h new file mode 100644 index 0000000..b2577b8 --- /dev/null +++ b/src/ui/aidl/UIPlugInAidl.h @@ -0,0 +1,53 @@ +/*! + * \brief UIPlugIn implementation of the AIDL UI. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "UIPlugIn.h" +#include "UIPlugInJsonApi.h" + +#include +#include + +namespace governikus +{ + +class UIPlugInAidl + : public UIPlugIn +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "governikus.UIPlugIn" FILE "metadata.json") + Q_INTERFACES(governikus::UIPlugIn) + + private: + UIPlugInJsonApi* mJsonApi; + QSharedPointer mContext; + QMutex mWorkflowIsActive; + + static QAtomicPointer instance; + bool mInitializationSuccessfull; + + public: + UIPlugInAidl(); + virtual ~UIPlugInAidl() override; + + static UIPlugInAidl* getInstance(bool pBlock = true); + bool isSuccessfullInitialized(); + Q_INVOKABLE void onReceived(const QByteArray& pMessage); + bool waitForWorkflowToFinish(); + + public Q_SLOTS: + void reset(); + + private Q_SLOTS: + virtual void doShutdown() override; + virtual void onWorkflowStarted(QSharedPointer pContext) override; + virtual void onWorkflowFinished(QSharedPointer pContext) override; + + void onToSend(const QByteArray& pMessage); +}; + +} // namespace governikus diff --git a/src/aidl/metadata.json b/src/ui/aidl/metadata.json similarity index 100% rename from src/aidl/metadata.json rename to src/ui/aidl/metadata.json diff --git a/src/ui/base/CMakeLists.txt b/src/ui/base/CMakeLists.txt new file mode 100644 index 0000000..d3855d0 --- /dev/null +++ b/src/ui/base/CMakeLists.txt @@ -0,0 +1,3 @@ +ADD_PLATFORM_LIBRARY(AusweisAppUi) + +TARGET_LINK_LIBRARIES(AusweisAppUi Qt5::Core Qt5::Network AusweisAppGlobal AusweisAppSettings) diff --git a/src/ui/base/UILoader.cpp b/src/ui/base/UILoader.cpp new file mode 100644 index 0000000..c021d6c --- /dev/null +++ b/src/ui/base/UILoader.cpp @@ -0,0 +1,188 @@ +/*! + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "UILoader.h" + +#include "AppSettings.h" +#include "SingletonHelper.h" + +#include +#include +#include + + +Q_DECLARE_LOGGING_CATEGORY(gui) + + +using namespace governikus; + + +defineSingleton(UILoader) + + +namespace +{ +QString getPrefixUi() +{ + return QStringLiteral("UIPlugIn"); +} + + +} // namespace + + +UILoader::UILoader() + : mLoadedPlugIns() + , mDefault() +{ + QStringList list({Env::getSingleton()->getGeneralSettings().getSelectedUi()}); + +#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) + list << getName(UIPlugInName::UIPlugInWebSocket); +#endif + + setDefault(list); +} + + +UILoader::~UILoader() +{ +} + + +UILoader& UILoader::getInstance() +{ + return *Instance; +} + + +bool UILoader::load() +{ + bool any = false; + for (auto entry : qAsConst(mDefault)) + { + any = load(entry) || any; + } + return any; +} + + +bool UILoader::load(UIPlugInName pUi) +{ + Q_ASSERT(QObject::thread() == QThread::currentThread()); + + if (mLoadedPlugIns.contains(pUi)) + { + return true; + } + + const auto& name = getEnumName(pUi); + qCDebug(gui) << "Try to load UI plugin:" << name; + + const auto& allPlugins = QPluginLoader::staticPlugins(); + for (auto& plugin : allPlugins) + { + auto metaData = plugin.metaData(); + if (isPlugIn(metaData) && hasName(metaData, name)) + { + qCDebug(gui) << "Load plugin:" << metaData; + auto instance = qobject_cast(plugin.instance()); + if (instance) + { + mLoadedPlugIns.insert(pUi, instance); + Q_EMIT fireLoadedPlugin(instance); + return true; + } + else + { + qCWarning(gui) << "Cannot cast to plugin instance:" << plugin.instance(); + } + } + } + + qCCritical(gui) << "Cannot find UI plugin:" << name; + return false; +} + + +const QStringList UILoader::getDefault() const +{ + QStringList list; + for (auto entry : qAsConst(mDefault)) + { + list << getName(entry); + } + return list; +} + + +void UILoader::setDefault(const QStringList& pDefault) +{ + QVector selectedPlugins; + const auto& availablePlugins = Enum::getList(); + + for (const auto& parsedUiOption : pDefault) + { + for (auto availablePluginEntry : availablePlugins) + { + if (parsedUiOption.compare(QString(getEnumName(availablePluginEntry)).remove(getPrefixUi()), Qt::CaseInsensitive) == 0) + { + selectedPlugins << availablePluginEntry; + } + } + } + + if (!selectedPlugins.isEmpty()) + { + mDefault = selectedPlugins; + } +} + + +UIPlugIn* UILoader::getLoaded(UIPlugInName pName) const +{ + return mLoadedPlugIns.value(pName); +} + + +void UILoader::shutdown() +{ + const QList keys = mLoadedPlugIns.keys(); + for (UIPlugInName key : keys) + { + UIPlugIn* const plugin = mLoadedPlugIns.value(key); + + connect(plugin, &QObject::destroyed, this, + [ = ](){ + mLoadedPlugIns.remove(key); + if (mLoadedPlugIns.isEmpty()) + { + Q_EMIT fireShutdownComplete(); + } + }, + Qt::QueuedConnection); + + // Plugins and therefore their members are not auto destructed due to a bug in Qt. + // https://bugreports.qt.io/browse/QTBUG-17458 + plugin->deleteLater(); + } +} + + +bool UILoader::hasName(const QJsonObject& pJson, const QString& pName) +{ + return pJson.value(QStringLiteral("className")).toString() == pName; +} + + +QString UILoader::getName(UIPlugInName pPlugin) const +{ + return QString(getEnumName(pPlugin)).remove(getPrefixUi()); +} + + +bool UILoader::isPlugIn(const QJsonObject& pJson) +{ + return pJson.value(QStringLiteral("IID")).toString() == QLatin1String("governikus.UIPlugIn"); +} diff --git a/src/ui/base/UILoader.h b/src/ui/base/UILoader.h new file mode 100644 index 0000000..3b7b109 --- /dev/null +++ b/src/ui/base/UILoader.h @@ -0,0 +1,56 @@ +/*! + * \brief Loader to initialize UIPlugIns. + * + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "EnumHelper.h" +#include "UIPlugIn.h" + +#include +#include +#include + +namespace governikus +{ + +defineEnumType(UIPlugInName, UIPlugInQml, UIPlugInCli, UIPlugInWidgets, UIPlugInJsonApi, UIPlugInWebSocket, UIPlugInAidl) + +class UILoader + : public QObject +{ + Q_OBJECT + + private: + QMap mLoadedPlugIns; + QVector mDefault; + + inline QString getName(UIPlugInName pPlugin) const; + inline bool isPlugIn(const QJsonObject& pJson); + inline bool hasName(const QJsonObject& pJson, const QString& pName); + + protected: + UILoader(); + virtual ~UILoader(); + + public: + static UILoader& getInstance(); + + bool load(); + bool load(UIPlugInName pName); + + const QStringList getDefault() const; + void setDefault(const QStringList& pDefault); + + UIPlugIn* getLoaded(UIPlugInName pName) const; + + Q_INVOKABLE void shutdown(); + + Q_SIGNALS: + void fireLoadedPlugin(UIPlugIn* pPlugin); + void fireShutdownComplete(); +}; + +} // namespace governikus diff --git a/src/ui/base/UIPlugIn.cpp b/src/ui/base/UIPlugIn.cpp new file mode 100644 index 0000000..ad613fb --- /dev/null +++ b/src/ui/base/UIPlugIn.cpp @@ -0,0 +1,61 @@ +/*! + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "UIPlugIn.h" + +using namespace governikus; + +UIPlugIn::UIPlugIn() +{ +} + + +UIPlugIn::~UIPlugIn() +{ +} + + +void UIPlugIn::onApplicationStarted() +{ +} + + +void UIPlugIn::onShowUi(UiModule pModule) +{ + Q_UNUSED(pModule) +} + + +void UIPlugIn::onHideUi() +{ +} + + +void UIPlugIn::onShowReaderSettings() +{ +} + + +#ifndef QT_NO_NETWORKPROXY +void UIPlugIn::onProxyAuthenticationRequired(const QNetworkProxy& pProxy, QAuthenticator* pAuthenticator) +{ + Q_UNUSED(pProxy) + Q_UNUSED(pAuthenticator) +} + + +void UIPlugIn::onUiDomination(const UIPlugIn* pUi, const QString& pInformation, bool pAccepted) +{ + Q_UNUSED(pUi) + Q_UNUSED(pInformation) + Q_UNUSED(pAccepted) +} + + +void UIPlugIn::onUiDominationReleased() +{ +} + + +#endif diff --git a/src/ui/base/UIPlugIn.h b/src/ui/base/UIPlugIn.h new file mode 100644 index 0000000..e2a6bf9 --- /dev/null +++ b/src/ui/base/UIPlugIn.h @@ -0,0 +1,67 @@ +/*! + * \brief Abstract layer to UI implementations. + * + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "EnumHelper.h" + +#include +#include + +namespace governikus +{ + +class WorkflowContext; + +/*! + * UI modules that can be requested to show. + */ +defineEnumType(UiModule, + CURRENT, + DEFAULT, + IDENTIFY, + SETTINGS, + PINMANAGEMENT + ) + +class UIPlugIn + : public QObject +{ + Q_OBJECT + + public: + UIPlugIn(); + virtual ~UIPlugIn(); + + public Q_SLOTS: + virtual void doShutdown() = 0; + virtual void onWorkflowStarted(QSharedPointer pContext) = 0; + virtual void onWorkflowFinished(QSharedPointer pContext) = 0; + virtual void onApplicationStarted(); + virtual void onShowUi(UiModule pModule); + virtual void onHideUi(); + virtual void onShowReaderSettings(); +#ifndef QT_NO_NETWORKPROXY + virtual void onProxyAuthenticationRequired(const QNetworkProxy& pProxy, QAuthenticator* pAuthenticator); +#endif + virtual void onUiDomination(const UIPlugIn* pUi, const QString& pInformation, bool pAccepted); + virtual void onUiDominationReleased(); + + Q_SIGNALS: + void fireChangePinRequest(); + void fireSelfAuthenticationRequested(); + void fireRemoteServiceRequested(); + void fireQuitApplicationRequest(); + void fireCloseReminderFinished(bool pDontRemindAgain); + + void fireShowUserInformation(const QString& pInformationMessage); + void fireUiDominationRequest(const UIPlugIn* pUi, const QString& pInformation); + void fireUiDominationRelease(); +}; + +} // namespace governikus + +Q_DECLARE_INTERFACE(governikus::UIPlugIn, "governikus.UIPlugIn") diff --git a/src/ui/cli/CMakeLists.txt b/src/ui/cli/CMakeLists.txt new file mode 100644 index 0000000..3032e8f --- /dev/null +++ b/src/ui/cli/CMakeLists.txt @@ -0,0 +1,12 @@ +##################################################################### +# The cli plugin implements the ui interface for a command line. +# +# It is an experimental interface that can be controled by a +# terminal/console. It is deprecated and should be replaced by +# the WebSocket plugin. +##################################################################### + +ADD_PLATFORM_LIBRARY(AusweisAppUiCli) + +TARGET_LINK_LIBRARIES(AusweisAppUiCli Qt5::Core AusweisAppGlobal AusweisAppCore AusweisAppUi AusweisAppActivationWebservice) +TARGET_COMPILE_DEFINITIONS(AusweisAppUiCli PRIVATE QT_STATICPLUGIN) diff --git a/src/cli/ConsoleReader.cpp b/src/ui/cli/ConsoleReader.cpp similarity index 100% rename from src/cli/ConsoleReader.cpp rename to src/ui/cli/ConsoleReader.cpp diff --git a/src/ui/cli/ConsoleReader.h b/src/ui/cli/ConsoleReader.h new file mode 100644 index 0000000..f2cc06c --- /dev/null +++ b/src/ui/cli/ConsoleReader.h @@ -0,0 +1,74 @@ +/*! + * \brief Helper to read stdin in non-blocking mode. + * + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include + +#if defined(Q_OS_WIN) +#include + +#include +#include +#else +#include +#endif + + +namespace governikus +{ + +#if defined(Q_OS_WIN) + +class ConsoleInputThread + : public QThread +{ + Q_OBJECT + + public: + void run() Q_DECL_OVERRIDE; + virtual ~ConsoleInputThread(); + + Q_SIGNALS: + void fireText(const QString& pData); +}; + +#endif + + +class ConsoleReader + : public QObject +{ + Q_OBJECT + + private: +#if defined(Q_OS_WIN) + QScopedPointer mConsoleInputThread; +#else + QScopedPointer mNotifier; + + bool mInputOpen; + + private Q_SLOTS: + void onData(); +#endif + + public: + ConsoleReader(QObject* pParent = nullptr); + void init(); + void shutdown(); + bool isInputOpen() const; + + QString readText(); + + Q_SIGNALS: + void fireShutdown(); + void fireText(const QString& pData); +}; + + +} // namespace governikus diff --git a/src/ui/cli/UIPlugInCli.cpp b/src/ui/cli/UIPlugInCli.cpp new file mode 100644 index 0000000..bb8d58c --- /dev/null +++ b/src/ui/cli/UIPlugInCli.cpp @@ -0,0 +1,234 @@ +/*! + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "UIPlugInCli.h" + +#include "states/StateEnterPacePassword.h" + +#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) +#include "Env.h" +#include "HttpServer.h" +#endif + +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(stdinput) +Q_DECLARE_LOGGING_CATEGORY(cli) + +using namespace governikus; + +UIPlugInCli::UIPlugInCli() + : mReader() + , mAvailableCommands() +{ + addCommand(QStringLiteral("cancel"), &UIPlugInCli::handleCancelWorkflow); + addCommand(QStringLiteral("changepin"), &UIPlugInCli::handleChangePin); + addCommand(QStringLiteral("enter-pin"), &UIPlugInCli::handleEnterPin); + addCommand(QStringLiteral("help"), &UIPlugInCli::handleHelp); + addCommand(QStringLiteral("ping"), &UIPlugInCli::handlePing); + addCommand(QStringLiteral("port"), &UIPlugInCli::handlePort); + addCommand(QStringLiteral("quit"), &UIPlugInCli::handleQuit); + + connect(&mReader, &ConsoleReader::fireText, this, &UIPlugInCli::doInput); + mReader.init(); +} + + +UIPlugInCli::~UIPlugInCli() +{ +} + + +void UIPlugInCli::onApplicationStarted() +{ + qCInfo(cli) << "ready"; +} + + +void UIPlugInCli::doShutdown() +{ + mReader.shutdown(); +} + + +void UIPlugInCli::onWorkflowStarted(QSharedPointer pContext) +{ + mContext = pContext; + if (!mContext.isNull()) + { + connect(mContext.data(), &WorkflowContext::fireStateChanged, + this, &UIPlugInCli::onStateChanged); + mContext->setReaderPlugInTypes({ReaderManagerPlugInType::PCSC}); + } +} + + +void UIPlugInCli::onWorkflowFinished(QSharedPointer pContext) +{ + Q_UNUSED(pContext); + if (!mContext.isNull()) + { + mContext->disconnect(this); + mContext.reset(); + } +} + + +void UIPlugInCli::onStateChanged(const QString& pState) +{ + Q_UNUSED(pState); + + bool userInteractionRequired = false; + + if (AbstractState::isState(pState) && mContext->getEstablishPaceChannelType() == PacePasswordId::PACE_PIN) + { + userInteractionRequired = true; + qCInfo(cli) << "enter-pin"; + } + + mContext->setStateApproved(!userInteractionRequired); +} + + +void UIPlugInCli::doInput(const QString& pData) +{ + qCInfo(stdinput) << pData; + + const QStringList chunks = pData.split(QLatin1Char(' ')); + auto func = mAvailableCommands.value(chunks.at(0).toLower()); + if (func) + { + mCurrentCommandArgs = chunks.mid(1); + func(); + } + else + { + qCWarning(cli) << "Unknown command:" << pData; + } +} + + +void UIPlugInCli::handleChangePin() +{ + mOldPin.clear(); + mNewPin.clear(); + + qCDebug(cli) << "Change PIN requested"; + qCInfo(cli) << "Please enter old PIN"; + + disconnect(&mReader, &ConsoleReader::fireText, this, &UIPlugInCli::doInput); + connect(&mReader, &ConsoleReader::fireText, this, &UIPlugInCli::handleOldPinEntered); +} + + +void UIPlugInCli::handleOldPinEntered(const QString& pLine) +{ + const QRegularExpression regexOldPin(QStringLiteral("^[0-9]{5,6}$")); + if (regexOldPin.match(pLine).hasMatch() && mReader.isInputOpen()) + { + mOldPin = pLine; + qCInfo(cli) << "Please enter new PIN"; + disconnect(&mReader, &ConsoleReader::fireText, this, &UIPlugInCli::handleOldPinEntered); + connect(&mReader, &ConsoleReader::fireText, this, &UIPlugInCli::handleNewPinEntered); + } + else + { + qCInfo(cli) << "Please enter old PIN"; + } +} + + +void UIPlugInCli::handleNewPinEntered(const QString& pLine) +{ + const QRegularExpression regexNewPin(QStringLiteral("^[0-9]{6}$")); + if (regexNewPin.match(pLine).hasMatch() && mReader.isInputOpen()) + { + mNewPin = pLine; + qCInfo(cli) << "Please enter new PIN again"; + disconnect(&mReader, &ConsoleReader::fireText, this, &UIPlugInCli::handleNewPinEntered); + connect(&mReader, &ConsoleReader::fireText, this, &UIPlugInCli::handleNewPinEnteredAgain); + } + else + { + qCInfo(cli) << "Please enter new PIN"; + } +} + + +void UIPlugInCli::handleNewPinEnteredAgain(const QString& pLine) +{ + if (mNewPin != pLine) + { + qCInfo(cli) << "PINs were not equal"; + qCInfo(cli) << "Please enter new PIN"; + + disconnect(&mReader, &ConsoleReader::fireText, this, &UIPlugInCli::handleNewPinEnteredAgain); + connect(&mReader, &ConsoleReader::fireText, this, &UIPlugInCli::handleNewPinEntered); + } + else if (mReader.isInputOpen()) + { + qCDebug(cli) << "Start"; + Q_EMIT fireChangePinRequest(); + connect(&mReader, &ConsoleReader::fireText, this, &UIPlugInCli::doInput); + } +} + + +void UIPlugInCli::handleCancelWorkflow() +{ + if (mContext.isNull()) + { + qCInfo(cli) << "error"; + } + else + { + Q_EMIT mContext->fireCancelWorkflow(); + qCInfo(cli) << "ok"; + } +} + + +void UIPlugInCli::handleEnterPin() +{ + if (mCurrentCommandArgs.size() != 1 || mContext.isNull()) + { + qCInfo(cli) << "error"; + } + else + { + mContext->setPin(mCurrentCommandArgs.at(0)); + mContext->setStateApproved(true); + qCInfo(cli) << "ok"; + } +} + + +void UIPlugInCli::handleHelp() +{ + qCInfo(cli) << "Available commands:" << mAvailableCommands.keys().join(QStringLiteral(" | ")); +} + + +void UIPlugInCli::handlePing() +{ + qCInfo(cli) << "Pong!"; +} + + +void UIPlugInCli::handlePort() +{ +#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) + qCInfo(cli) << "Port:" << Env::getShared()->getServerPort(); +#else + qCInfo(cli) << "Port is undefined"; +#endif +} + + +void UIPlugInCli::handleQuit() +{ + qCInfo(cli) << "Shutdown application..."; + Q_EMIT fireQuitApplicationRequest(); +} diff --git a/src/ui/cli/UIPlugInCli.h b/src/ui/cli/UIPlugInCli.h new file mode 100644 index 0000000..defb94a --- /dev/null +++ b/src/ui/cli/UIPlugInCli.h @@ -0,0 +1,72 @@ +/*! + * \brief UIPlugIn implementation of CLI. + * + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/WorkflowContext.h" +#include "ConsoleReader.h" +#include "UIPlugIn.h" + +#include +#include + +#include + +namespace governikus +{ + +class UIPlugInCli + : public UIPlugIn +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "governikus.UIPlugIn" FILE "metadata.json") + Q_INTERFACES(governikus::UIPlugIn) + + using MemberFunc = void (UIPlugInCli::*)(); + + private: + QString mOldPin; + QString mNewPin; + ConsoleReader mReader; + QMap > mAvailableCommands; + QSharedPointer mContext; + QStringList mCurrentCommandArgs; + + void addCommand(const QString& pCmd, MemberFunc pFunc) + { + mAvailableCommands.insert(pCmd, std::bind(pFunc, this)); + } + + + void handleCancelWorkflow(); + void handleChangePin(); + void handleEnterPin(); + void handleHelp(); + void handlePing(); + void handlePort(); + void handleQuit(); + + public: + UIPlugInCli(); + virtual ~UIPlugInCli() override; + + public Q_SLOTS: + virtual void onApplicationStarted() override; + + virtual void doShutdown() override; + + private Q_SLOTS: + void doInput(const QString& pData); + virtual void onWorkflowStarted(QSharedPointer pContext) override; + virtual void onWorkflowFinished(QSharedPointer pContext) override; + void onStateChanged(const QString& pState); + + void handleOldPinEntered(const QString& pLine); + void handleNewPinEntered(const QString& pLine); + void handleNewPinEnteredAgain(const QString& pLine); +}; + +} // namespace governikus diff --git a/src/cli/metadata.json b/src/ui/cli/metadata.json similarity index 100% rename from src/cli/metadata.json rename to src/ui/cli/metadata.json diff --git a/src/ui/common/CMakeLists.txt b/src/ui/common/CMakeLists.txt new file mode 100644 index 0000000..d33b64c --- /dev/null +++ b/src/ui/common/CMakeLists.txt @@ -0,0 +1,15 @@ +##################################################################### +# Common UI functions used by multiple UI implementations +##################################################################### + +ADD_PLATFORM_LIBRARY(AusweisAppUiCommon) + +TARGET_LINK_LIBRARIES(AusweisAppUiCommon Qt5::Core Qt5::Gui AusweisAppGlobal) + +IF(DESKTOP) + TARGET_LINK_LIBRARIES(AusweisAppUiCommon Qt5::Widgets) +ENDIF() + +IF(MAC) + TARGET_LINK_LIBRARIES(AusweisAppUiCommon ${OSX_APPKIT}) +ENDIF() diff --git a/src/ui/common/HelpAction.cpp b/src/ui/common/HelpAction.cpp new file mode 100644 index 0000000..5e5e6f2 --- /dev/null +++ b/src/ui/common/HelpAction.cpp @@ -0,0 +1,125 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "HelpAction.h" + +#include "LanguageLoader.h" +#include "SingletonHelper.h" +#include "VersionNumber.h" + +#include +#include +#include +#include +#include +#include +#include + + +using namespace governikus; + +defineSingleton(HelpAction) + +Q_DECLARE_LOGGING_CATEGORY(gui) + +//Mapping object name to help file, \see AppQtMainWidget::onContentActionClicked() +const QMap HelpAction::mHelpMapping = { + {QStringLiteral("setupAssistant"), QStringLiteral("wizard-info.html")}, + {QStringLiteral("setupAssistantSetupCompleted"), QString()}, + {QStringLiteral("ausweisenPage"), QStringLiteral("identify.html")}, + {QStringLiteral("providerPage"), QStringLiteral("provider.html")}, + {QStringLiteral("historyPage"), QStringLiteral("history.html")}, + {QStringLiteral("generalTab"), QStringLiteral("settings-general.html")}, + {QStringLiteral("pinTab"), QStringLiteral("settings-pin-management.html")}, + {QStringLiteral("readerDeviceTab"), QStringLiteral("settings-reader-detection.html")}, + {QStringLiteral("stepChooseCardGui"), QStringLiteral("settings-reader-detection.html")} +}; + + +HelpAction& HelpAction::getInstance() +{ + return *Instance; +} + + +QString HelpAction::getHelpPath(QLocale::Language pLang) const +{ + const QString langDir = QCoreApplication::applicationDirPath() % QStringLiteral("/help/") % QLocale(pLang).bcp47Name().mid(0, 2) % QLatin1Char('/'); + + if (QDir(langDir).exists()) + { + return langDir; + } + + return QString(); +} + + +QLocale::Language HelpAction::getExistingHelpLanguage() const +{ + QLocale::Language lang = LanguageLoader::getInstance().getUsedLocale().language(); + if (!getHelpPath(lang).isNull()) + { + return lang; + } + + lang = LanguageLoader::getInstance().getFallbackLanguage(); + if (!getHelpPath(lang).isNull()) + { + return lang; + } + + return QLocale::AnyLanguage; +} + + +QString HelpAction::getContextMapping(const QString& pObjectName) const +{ + if (mHelpMapping.contains(pObjectName)) + { + return mHelpMapping.value(pObjectName); + } + else + { + qCWarning(gui) << "Cannot find help mapping:" << pObjectName; + } + + return QStringLiteral("index.html"); +} + + +QString HelpAction::getHelpUrl(const QString& pObjectName) const +{ + QLocale::Language lang = getExistingHelpLanguage(); + if (lang == QLocale::AnyLanguage) + { + return getOnlineUrl(); + } + + return QUrl::fromLocalFile(getHelpPath(lang)).toString() + getContextMapping(pObjectName); +} + + +QString HelpAction::getOnlineUrl(const QString& pObjectName) +{ +#ifdef Q_OS_MACOS + const QLatin1String osPath("macOS"); +#else + const QLatin1String osPath("Windows"); +#endif + + const auto& appVersion = VersionNumber::getApplicationVersion().getVersionNumber(); + const QString ver = QString::number(appVersion.majorVersion()) % QLatin1Char('.') % QString::number(appVersion.minorVersion()); + const QString locale = QLocale(LanguageLoader::getInstance().getUsedLocale().language()).bcp47Name().mid(0, 2); + const QString mapping = getInstance().getContextMapping(pObjectName); + return QStringLiteral("https://www.ausweisapp.bund.de/ausweisapp2/handbuch/") % ver % QLatin1Char('/') % locale % QLatin1Char('/') % osPath % QLatin1Char('/') % mapping; +} + + +void HelpAction::openContextHelp(const QString& pObjectName) +{ + const auto& url = QUrl(getOnlineUrl(pObjectName)); + qCDebug(gui) << "Open online help:" << pObjectName << '|' << url; + QDesktopServices::openUrl(url); +} diff --git a/src/ui/common/HelpAction.h b/src/ui/common/HelpAction.h new file mode 100644 index 0000000..c34ae14 --- /dev/null +++ b/src/ui/common/HelpAction.h @@ -0,0 +1,43 @@ +/*! + * \brief Helper class for mapping object name from f1 widget to help file. + * \see AppQtMainWidget::onContentActionClicked() + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include +#include + +class test_HelpAction; + +namespace governikus +{ + +class HelpAction +{ + private: + friend class ::test_HelpAction; + + static const QMap mHelpMapping; + + Q_DISABLE_COPY(HelpAction) + + QLocale::Language getExistingHelpLanguage() const; + QString getContextMapping(const QString& pObjectName) const; + QString getHelpPath(QLocale::Language pLang) const; + QString getHelpUrl(const QString& pObjectName) const; + + protected: + static HelpAction& getInstance(); + HelpAction() = default; + ~HelpAction() = default; + + public: + static QString getOnlineUrl(const QString& pObjectName = QString()); + static void openContextHelp(const QString& pObjectName = QStringLiteral("applicationPage")); +}; + +} // namespace governikus diff --git a/src/ui/common/PlatformTools.h b/src/ui/common/PlatformTools.h new file mode 100644 index 0000000..d30203a --- /dev/null +++ b/src/ui/common/PlatformTools.h @@ -0,0 +1,18 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + + +namespace governikus +{ + +class PlatformTools +{ + public: + static void hideFromTaskbar(); + static void restoreToTaskbar(); +}; + +} // namespace governikus diff --git a/src/ui/common/PlatformTools_generic.cpp b/src/ui/common/PlatformTools_generic.cpp new file mode 100644 index 0000000..9f0e02b --- /dev/null +++ b/src/ui/common/PlatformTools_generic.cpp @@ -0,0 +1,18 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "PlatformTools.h" + + +using namespace governikus; + + +void PlatformTools::hideFromTaskbar() +{ +} + + +void PlatformTools::restoreToTaskbar() +{ +} diff --git a/src/ui/common/PlatformTools_osx.cpp b/src/ui/common/PlatformTools_osx.cpp new file mode 100644 index 0000000..4509c11 --- /dev/null +++ b/src/ui/common/PlatformTools_osx.cpp @@ -0,0 +1,29 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "PlatformTools.h" + +#import + + +using namespace governikus; + + +void PlatformTools::hideFromTaskbar() +{ + ProcessSerialNumber psn = { + 0, kCurrentProcess + }; + TransformProcessType(&psn, kProcessTransformToBackgroundApplication); +} + + +void PlatformTools::restoreToTaskbar() +{ + ProcessSerialNumber psn = { + 0, kCurrentProcess + }; + TransformProcessType(&psn, kProcessTransformToForegroundApplication); + [NSApp activateIgnoringOtherApps: YES]; +} diff --git a/src/ui/common/TrayIcon.cpp b/src/ui/common/TrayIcon.cpp new file mode 100644 index 0000000..c4e13a2 --- /dev/null +++ b/src/ui/common/TrayIcon.cpp @@ -0,0 +1,105 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "TrayIcon.h" + +#include + +#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) + #include +#endif + + +using namespace governikus; + + +#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) +void TrayIcon::onActivated(QSystemTrayIcon::ActivationReason pReason) +{ +#ifdef Q_OS_MACOS + Q_UNUSED(pReason) +#else + if (pReason == QSystemTrayIcon::Trigger) + { + Q_EMIT fireShow(); + } +#endif +} + + +#endif + + +TrayIcon::TrayIcon() + : QObject() + , mIcon(QStringLiteral(":/images/npa.svg")) +#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) + , mTrayIcon(nullptr) +#endif +{ +} + + +TrayIcon::~TrayIcon() +{ +#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) + if (mTrayIcon != nullptr) + { + QMenu* menu = mTrayIcon->contextMenu(); + if (menu != nullptr) + { + qDeleteAll(menu->actions()); + delete menu; + } + + delete mTrayIcon; + mTrayIcon = nullptr; + } +#endif +} + + +void TrayIcon::create() +{ +#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) + if (!QSystemTrayIcon::isSystemTrayAvailable()) + { + return; + } + + const auto trayIconMenu = new QMenu(nullptr); + +#if defined(Q_OS_MACOS) + QAction* showApplicationAction = new QAction(tr("Open"), trayIconMenu); + connect(showApplicationAction, &QAction::triggered, this, &TrayIcon::fireShow); + trayIconMenu->addAction(showApplicationAction); + trayIconMenu->addSeparator(); +#endif + + const auto quitAction = new QAction(tr("Exit AusweisApp2"), trayIconMenu); + connect(quitAction, &QAction::triggered, this, &TrayIcon::fireQuit); + trayIconMenu->addAction(quitAction); + + mTrayIcon = new QSystemTrayIcon(mIcon); + connect(mTrayIcon, &QSystemTrayIcon::activated, this, &TrayIcon::onActivated); + connect(mTrayIcon, &QSystemTrayIcon::messageClicked, this, &TrayIcon::fireShow); + + mTrayIcon->setContextMenu(trayIconMenu); + mTrayIcon->setToolTip(QCoreApplication::applicationName()); + + mTrayIcon->show(); + mTrayIcon->showMessage(QString(), tr("AusweisApp2 was started."), mIcon, 3000); +#endif +} + + +void TrayIcon::hide() +{ +#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) + if (mTrayIcon) + { + mTrayIcon->hide(); + } +#endif +} diff --git a/src/ui/common/TrayIcon.h b/src/ui/common/TrayIcon.h new file mode 100644 index 0000000..45cb279 --- /dev/null +++ b/src/ui/common/TrayIcon.h @@ -0,0 +1,45 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + + +#include +#include + +#if defined(Q_OS_WIN) || (defined(Q_OS_BSD4) && !defined(Q_OS_IOS)) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) + #include +#endif + + +namespace governikus +{ + +class TrayIcon + : public QObject +{ + Q_OBJECT + + private: + QIcon mIcon; +#if defined(Q_OS_WIN) || (defined(Q_OS_BSD4) && !defined(Q_OS_IOS)) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) + QSystemTrayIcon* mTrayIcon; + + private Q_SLOTS: + void onActivated(QSystemTrayIcon::ActivationReason pReason); +#endif + + public: + TrayIcon(); + ~TrayIcon(); + + void create(); + void hide(); + + Q_SIGNALS: + void fireShow(); + void fireQuit(); +}; + +} // namespace governikus diff --git a/src/ui/jsonapi/CMakeLists.txt b/src/ui/jsonapi/CMakeLists.txt new file mode 100644 index 0000000..4dfe294 --- /dev/null +++ b/src/ui/jsonapi/CMakeLists.txt @@ -0,0 +1,12 @@ +##################################################################### +# The jsonapi plugin implements the ui interface for internal usage. +# +# This plugin will be used by other ui plugins like AIDL or WebSocket +# to provide the SDK functionality. It provides no possibility for +# direct user control. +##################################################################### + +ADD_PLATFORM_LIBRARY(AusweisAppUiJsonApi) + +TARGET_LINK_LIBRARIES(AusweisAppUiJsonApi Qt5::Core AusweisAppCore AusweisAppGlobal AusweisAppActivationInternal) +TARGET_COMPILE_DEFINITIONS(AusweisAppUiJsonApi PRIVATE QT_STATICPLUGIN) diff --git a/src/ui/jsonapi/MessageDispatcher.cpp b/src/ui/jsonapi/MessageDispatcher.cpp new file mode 100644 index 0000000..8c0546c --- /dev/null +++ b/src/ui/jsonapi/MessageDispatcher.cpp @@ -0,0 +1,231 @@ +/* + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "MessageDispatcher.h" + +#include "messages/MsgHandlerAccessRights.h" +#include "messages/MsgHandlerApiLevel.h" +#include "messages/MsgHandlerAuth.h" +#include "messages/MsgHandlerBadState.h" +#include "messages/MsgHandlerCertificate.h" +#include "messages/MsgHandlerEnterCan.h" +#include "messages/MsgHandlerEnterPin.h" +#include "messages/MsgHandlerEnterPuk.h" +#include "messages/MsgHandlerInfo.h" +#include "messages/MsgHandlerInsertCard.h" +#include "messages/MsgHandlerInternalError.h" +#include "messages/MsgHandlerInvalid.h" +#include "messages/MsgHandlerReader.h" +#include "messages/MsgHandlerReaderList.h" +#include "messages/MsgHandlerUnknownCommand.h" + +#include + +Q_DECLARE_LOGGING_CATEGORY(jsonapi) + +#define HANDLE_CURRENT_STATE(msgType, msgHandler) handleCurrentState(requestType, msgType, [&] {return msgHandler;}); + +using namespace governikus; + + +MessageDispatcher::MessageDispatcher() + : mContext() +{ +} + + +QByteArray MessageDispatcher::init(const QSharedPointer& pContext) +{ + Q_ASSERT(!mContext.isActiveWorkflow()); + + reset(); + mContext.setWorkflowContext(pContext); + + if (mContext.getAuthContext()) + { + return MsgHandlerAuth().getOutput(); + } + + return QByteArray(); +} + + +void MessageDispatcher::reset() +{ + mContext.clear(); +} + + +QByteArray MessageDispatcher::createMsgReader(const QString& pName) const +{ + return MsgHandlerReader(pName).getOutput(); +} + + +QByteArray MessageDispatcher::finish() +{ + Q_ASSERT(mContext.isActiveWorkflow()); + + QByteArray result; + if (auto authContext = mContext.getAuthContext()) + { + result = MsgHandlerAuth(authContext).getOutput(); + } + + reset(); + return result; +} + + +QByteArray MessageDispatcher::processStateChange(const QString& pState) +{ + if (!mContext.isActiveWorkflow() || pState.isEmpty()) + { + qCritical(jsonapi) << "Unexpected condition:" << mContext.getWorkflowContext() << "|" << pState; + return MsgHandlerInternalError(QLatin1String("Unexpected condition")).getOutput(); + } + + const auto& msg = createForStateChange(MsgHandler::getStateMsgType(pState, mContext.getWorkflowContext()->getEstablishPaceChannelType())); + mContext.addStateMsg(msg.getType()); + return msg.getOutput(); +} + + +MsgHandler MessageDispatcher::createForStateChange(MsgType pStateType) +{ + switch (pStateType) + { + case MsgType::ENTER_PIN: + return MsgHandlerEnterPin(mContext); + + case MsgType::ENTER_CAN: + return MsgHandlerEnterCan(mContext); + + case MsgType::ENTER_PUK: + return MsgHandlerEnterPuk(mContext); + + case MsgType::ACCESS_RIGHTS: + return MsgHandlerAccessRights(mContext); + + case MsgType::INSERT_CARD: + return MsgHandlerInsertCard(mContext); + + default: + mContext.getWorkflowContext()->setStateApproved(); + return MsgHandler::Void; + } +} + + +QByteArray MessageDispatcher::processCommand(const QByteArray& pMsg) +{ + QJsonParseError jsonError; + const auto& json = QJsonDocument::fromJson(pMsg, &jsonError); + if (jsonError.error != QJsonParseError::NoError) + { + return MsgHandlerInvalid(jsonError).getOutput(); + } + + const auto& obj = json.object(); + auto msg = createForCommand(obj); + msg.setRequest(obj); + return msg.getOutput(); +} + + +MsgHandler MessageDispatcher::createForCommand(const QJsonObject& pObj) +{ + const auto& cmd = pObj.value(QLatin1String("cmd")).toString(); + if (cmd.isEmpty()) + { + return MsgHandlerInvalid(QLatin1String("Command cannot be undefined")); + } + + auto requestType = Enum::fromString(cmd, MsgCmdType::UNDEFINED); + qDebug(jsonapi) << "Process type:" << requestType; + switch (requestType) + { + case MsgCmdType::UNDEFINED: + return MsgHandlerUnknownCommand(cmd); + + case MsgCmdType::CANCEL: + return cancel(); + + case MsgCmdType::ACCEPT: + return accept(); + + case MsgCmdType::GET_API_LEVEL: + return MsgHandlerApiLevel(mContext); + + case MsgCmdType::SET_API_LEVEL: + return MsgHandlerApiLevel(pObj, mContext); + + case MsgCmdType::GET_READER: + return MsgHandlerReader(pObj); + + case MsgCmdType::GET_READER_LIST: + return MsgHandlerReaderList(); + + case MsgCmdType::GET_INFO: + return MsgHandlerInfo(); + + case MsgCmdType::RUN_AUTH: + return mContext.isActiveWorkflow() ? MsgHandler(MsgHandlerBadState(requestType)) : MsgHandler(MsgHandlerAuth(pObj)); + + case MsgCmdType::GET_CERTIFICATE: + return HANDLE_CURRENT_STATE(MsgType::ACCESS_RIGHTS, MsgHandlerCertificate(mContext)); + + case MsgCmdType::SET_PIN: + return HANDLE_CURRENT_STATE(MsgType::ENTER_PIN, MsgHandlerEnterPin(pObj, mContext)); + + case MsgCmdType::SET_CAN: + return HANDLE_CURRENT_STATE(MsgType::ENTER_CAN, MsgHandlerEnterCan(pObj, mContext)); + + case MsgCmdType::SET_PUK: + return HANDLE_CURRENT_STATE(MsgType::ENTER_PUK, MsgHandlerEnterPuk(pObj, mContext)); + + case MsgCmdType::GET_ACCESS_RIGHTS: + return HANDLE_CURRENT_STATE(MsgType::ACCESS_RIGHTS, MsgHandlerAccessRights(mContext)); + + case MsgCmdType::SET_ACCESS_RIGHTS: + return HANDLE_CURRENT_STATE(MsgType::ACCESS_RIGHTS, MsgHandlerAccessRights(pObj, mContext)); + } + + return MsgHandlerInternalError(QLatin1String("Cannot process request")); +} + + +MsgHandler MessageDispatcher::handleCurrentState(MsgCmdType pCmdType, MsgType pMsgType, const std::function& pFunc) +{ + if (mContext.getLastStateMsg() == pMsgType) + { + return pFunc(); + } + + return MsgHandlerBadState(pCmdType); +} + + +MsgHandler MessageDispatcher::cancel() +{ + if (mContext.isActiveWorkflow()) + { + Q_EMIT mContext.getWorkflowContext()->fireCancelWorkflow(); + return MsgHandler::Void; + } + + return MsgHandlerBadState(MsgCmdType::CANCEL); +} + + +MsgHandler MessageDispatcher::accept() +{ + if (mContext.getLastStateMsg() == MsgType::ACCESS_RIGHTS) + { + mContext.getWorkflowContext()->setStateApproved(); + return MsgHandler::Void; + } + + return MsgHandlerBadState(MsgCmdType::ACCEPT); +} diff --git a/src/ui/jsonapi/MessageDispatcher.h b/src/ui/jsonapi/MessageDispatcher.h new file mode 100644 index 0000000..19c014c --- /dev/null +++ b/src/ui/jsonapi/MessageDispatcher.h @@ -0,0 +1,50 @@ +/*! + * \brief Dispatch Messages of JSON API. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/AuthContext.h" +#include "context/WorkflowContext.h" +#include "messages/MsgContext.h" +#include "messages/MsgHandler.h" + +#include +#include + +#include + +class test_Message; + +namespace governikus +{ + +class MessageDispatcher +{ + private: + friend class ::test_Message; + + MsgDispatcherContext mContext; + + MsgHandler createForStateChange(MsgType pStateType); + MsgHandler createForCommand(const QJsonObject& pObj); + + MsgHandler cancel(); + MsgHandler accept(); + MsgHandler handleCurrentState(MsgCmdType pCmdType, MsgType pMsgType, const std::function& pFunc); + + public: + MessageDispatcher(); + + QByteArray init(const QSharedPointer& pContext); + QByteArray finish(); + void reset(); + QByteArray processCommand(const QByteArray& pMsg); + QByteArray processStateChange(const QString& pState); + + QByteArray createMsgReader(const QString& pName) const; +}; + +} // namespace governikus diff --git a/src/ui/jsonapi/UIPlugInJsonApi.cpp b/src/ui/jsonapi/UIPlugInJsonApi.cpp new file mode 100644 index 0000000..e28b46d --- /dev/null +++ b/src/ui/jsonapi/UIPlugInJsonApi.cpp @@ -0,0 +1,115 @@ +/*! + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "UIPlugInJsonApi.h" + +#include "ReaderManager.h" + +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(jsonapi) + +using namespace governikus; + +UIPlugInJsonApi::UIPlugInJsonApi() + : UIPlugIn() + , mMessageDispatcher() + , mEnabled(false) +{ +} + + +void UIPlugInJsonApi::setEnabled(bool pEnable) +{ + mEnabled = pEnable; + const auto readerManager = Env::getSingleton(); + + if (mEnabled) + { + connect(readerManager, &ReaderManager::fireReaderAdded, this, &UIPlugInJsonApi::onReaderEvent); + connect(readerManager, &ReaderManager::fireReaderRemoved, this, &UIPlugInJsonApi::onReaderEvent); + connect(readerManager, &ReaderManager::fireCardInserted, this, &UIPlugInJsonApi::onReaderEvent); + connect(readerManager, &ReaderManager::fireCardRemoved, this, &UIPlugInJsonApi::onReaderEvent); + } + else + { + disconnect(readerManager, &ReaderManager::fireReaderAdded, this, &UIPlugInJsonApi::onReaderEvent); + disconnect(readerManager, &ReaderManager::fireReaderRemoved, this, &UIPlugInJsonApi::onReaderEvent); + disconnect(readerManager, &ReaderManager::fireCardInserted, this, &UIPlugInJsonApi::onReaderEvent); + disconnect(readerManager, &ReaderManager::fireCardRemoved, this, &UIPlugInJsonApi::onReaderEvent); + } +} + + +bool UIPlugInJsonApi::isEnabled() const +{ + return mEnabled; +} + + +void UIPlugInJsonApi::callFireMessage(const QByteArray& pMsg) +{ + if (!pMsg.isEmpty()) + { + qCDebug(jsonapi).noquote() << "Fire message:" << pMsg; + Q_EMIT fireMessage(pMsg); + } +} + + +void UIPlugInJsonApi::onWorkflowStarted(QSharedPointer pContext) +{ + if (!mEnabled) + { + return; + } + + if (pContext.objectCast()) + { + connect(pContext.data(), &WorkflowContext::fireStateChanged, this, &UIPlugInJsonApi::onStateChanged); + } + + callFireMessage(mMessageDispatcher.init(pContext)); +} + + +void UIPlugInJsonApi::onWorkflowFinished(QSharedPointer ) +{ + if (!mEnabled) + { + mMessageDispatcher.reset(); + return; + } + + callFireMessage(mMessageDispatcher.finish()); +} + + +void UIPlugInJsonApi::onReaderEvent(const QString& pName) +{ + callFireMessage(mMessageDispatcher.createMsgReader(pName)); +} + + +void UIPlugInJsonApi::onStateChanged(const QString& pNewState) +{ + callFireMessage(mMessageDispatcher.processStateChange(pNewState)); +} + + +void UIPlugInJsonApi::doMessageProcessing(const QByteArray& pMsg) +{ + if (!mEnabled) + { + return; + } + + callFireMessage(mMessageDispatcher.processCommand(pMsg)); +} + + +void UIPlugInJsonApi::doShutdown() +{ +} diff --git a/src/ui/jsonapi/UIPlugInJsonApi.h b/src/ui/jsonapi/UIPlugInJsonApi.h new file mode 100644 index 0000000..1ec1121 --- /dev/null +++ b/src/ui/jsonapi/UIPlugInJsonApi.h @@ -0,0 +1,50 @@ +/*! + * \brief UIPlugIn implementation of the Json API. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/AuthContext.h" +#include "MessageDispatcher.h" +#include "UIPlugIn.h" + +namespace governikus +{ + +class UIPlugInJsonApi + : public UIPlugIn +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "governikus.UIPlugIn" FILE "metadata.json") + Q_INTERFACES(governikus::UIPlugIn) + + private: + MessageDispatcher mMessageDispatcher; + bool mEnabled; + + inline void callFireMessage(const QByteArray& pMsg); + + public: + UIPlugInJsonApi(); + virtual ~UIPlugInJsonApi() override = default; + + void setEnabled(bool pEnable = true); + bool isEnabled() const; + + private Q_SLOTS: + virtual void doShutdown() override; + virtual void onWorkflowStarted(QSharedPointer pContext) override; + virtual void onWorkflowFinished(QSharedPointer pContext) override; + void onReaderEvent(const QString& pName); + void onStateChanged(const QString& pNewState); + + public Q_SLOTS: + void doMessageProcessing(const QByteArray& pMsg); + + Q_SIGNALS: + void fireMessage(const QByteArray& pMsg); +}; + +} // namespace governikus diff --git a/src/jsonapi/messages/MsgContext.cpp b/src/ui/jsonapi/messages/MsgContext.cpp similarity index 100% rename from src/jsonapi/messages/MsgContext.cpp rename to src/ui/jsonapi/messages/MsgContext.cpp diff --git a/src/ui/jsonapi/messages/MsgContext.h b/src/ui/jsonapi/messages/MsgContext.h new file mode 100644 index 0000000..a9ba41d --- /dev/null +++ b/src/ui/jsonapi/messages/MsgContext.h @@ -0,0 +1,51 @@ +/*! + * \brief Context of JSON API. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/AuthContext.h" +#include "context/WorkflowContext.h" +#include "MsgTypes.h" + +namespace governikus +{ + +class MsgContext +{ + Q_DISABLE_COPY(MsgContext) + + protected: + MsgLevel mApiLevel; + QList mStateMessages; + QSharedPointer mContext; + + public: + MsgContext(); + + void setApiLevel(MsgLevel pApiLevel); + MsgLevel getApiLevel() const; + + MsgType getLastStateMsg() const; + + bool isActiveWorkflow() const; + + QSharedPointer getAuthContext(); + QSharedPointer getAuthContext() const; + + QSharedPointer getWorkflowContext(); + QSharedPointer getWorkflowContext() const; +}; + +class MsgDispatcherContext + : public MsgContext +{ + public: + void clear(); + void addStateMsg(MsgType pMsgType); + void setWorkflowContext(const QSharedPointer& pContext); +}; + +} // namespace governikus diff --git a/src/ui/jsonapi/messages/MsgHandler.cpp b/src/ui/jsonapi/messages/MsgHandler.cpp new file mode 100644 index 0000000..2f28590 --- /dev/null +++ b/src/ui/jsonapi/messages/MsgHandler.cpp @@ -0,0 +1,156 @@ +/*! + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "MsgHandler.h" + +#include "states/StateEditAccessRights.h" +#include "states/StateEnterPacePassword.h" +#include "states/StateSelectReader.h" + +#include + +using namespace governikus; + +const MsgLevel MsgHandler::DEFAULT_MSG_LEVEL = MsgLevel::v1; + +const MsgHandler MsgHandler::Void = MsgHandler(); + + +MsgType MsgHandler::getStateMsgType(const QString& pState, PacePasswordId pPasswordId) +{ + if (AbstractState::isState(pState)) + { + if (pPasswordId == PacePasswordId::PACE_PIN) + { + return MsgType::ENTER_PIN; + } + else if (pPasswordId == PacePasswordId::PACE_CAN) + { + return MsgType::ENTER_CAN; + } + else if (pPasswordId == PacePasswordId::PACE_PUK) + { + return MsgType::ENTER_PUK; + } + } + else if (AbstractState::isState(pState)) + { + return MsgType::ACCESS_RIGHTS; + } + else if (AbstractState::isState(pState)) + { + return MsgType::INSERT_CARD; + } + + // indicates "do not use this" otherwise it is an internal error! + return MsgType::INTERNAL_ERROR; +} + + +MsgHandler::MsgHandler() + : MsgHandler(MsgType::INTERNAL_ERROR) +{ + setVoid(); +} + + +MsgHandler::MsgHandler(MsgType pType) + : mType(pType) + , mVoid(false) + , mJsonObject() +{ + mJsonObject[QLatin1String("msg")] = getEnumName(mType); +} + + +MsgHandler::MsgHandler(MsgType pType, const char* pKey, const QString& pValue) + : MsgHandler(pType) +{ + setValue(pKey, pValue); +} + + +MsgHandler::MsgHandler(MsgType pType, const char* pKey, const QLatin1String pValue) + : MsgHandler(pType) +{ + setValue(pKey, pValue); +} + + +QByteArray MsgHandler::toJson() const +{ + Q_ASSERT(mJsonObject[QLatin1String("msg")].isString()); + return QJsonDocument(mJsonObject).toJson(QJsonDocument::Compact); +} + + +QByteArray MsgHandler::getOutput() const +{ + if (isVoid()) + { + return QByteArray(); + } + + return toJson(); +} + + +bool MsgHandler::isVoid() const +{ + return mVoid; +} + + +MsgType MsgHandler::getType() const +{ + return mType; +} + + +void MsgHandler::setRequest(const QJsonObject& pRequest) +{ + const QLatin1String requestName("request"); + + const auto& requestValue = pRequest[requestName]; + if (!requestValue.isUndefined()) + { + mJsonObject[requestName] = requestValue; + } +} + + +void MsgHandler::setValue(const char* pKey, const QString& pValue) +{ + setValue(QLatin1String(pKey), pValue); +} + + +void MsgHandler::setValue(const QLatin1String pKey, const QLatin1String pValue) +{ + if (pValue.size()) + { + mJsonObject[pKey] = pValue; + } +} + + +void MsgHandler::setValue(const char* pKey, const QLatin1String pValue) +{ + setValue(QLatin1String(pKey), pValue); +} + + +void MsgHandler::setVoid(bool pVoid) +{ + mVoid = pVoid; +} + + +void MsgHandler::setValue(const QLatin1String pKey, const QString& pValue) +{ + if (!pValue.isEmpty()) + { + mJsonObject[pKey] = pValue; + } +} diff --git a/src/ui/jsonapi/messages/MsgHandler.h b/src/ui/jsonapi/messages/MsgHandler.h new file mode 100644 index 0000000..2cffeb8 --- /dev/null +++ b/src/ui/jsonapi/messages/MsgHandler.h @@ -0,0 +1,59 @@ +/*! + * \brief Base of all messages of JSON API. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "MsgTypes.h" +#include "SmartCardDefinitions.h" + +#include + +namespace governikus +{ +class MsgHandler +{ + private: + const MsgType mType; + bool mVoid; + + MsgHandler(); + + protected: + QJsonObject mJsonObject; + + MsgHandler(MsgType pType); + MsgHandler(MsgType pType, const char* pKey, const QString& pValue); + MsgHandler(MsgType pType, const char* pKey, const QLatin1String pValue); + + void setValue(const QLatin1String pKey, const QString& pValue); + void setValue(const char* pKey, const QString& pValue); + void setValue(const QLatin1String pKey, const QLatin1String pValue); + void setValue(const char* pKey, const QLatin1String pValue); + + void setVoid(bool pVoid = true); + + public: + static const MsgHandler Void; + static const MsgLevel DEFAULT_MSG_LEVEL; + static MsgType getStateMsgType(const QString& pState, PacePasswordId pPasswordId); + + QByteArray toJson() const; + QByteArray getOutput() const; + bool isVoid() const; + MsgType getType() const; + + void setRequest(const QJsonObject& pRequest); +}; + +inline QDebug operator<<(QDebug pDbg, const MsgHandler& pMsg) +{ + QDebugStateSaver saver(pDbg); + pDbg << pMsg.getType(); + return pDbg.space(); +} + + +} // namespace governikus diff --git a/src/ui/jsonapi/messages/MsgHandlerAccessRights.cpp b/src/ui/jsonapi/messages/MsgHandlerAccessRights.cpp new file mode 100644 index 0000000..21ea8b9 --- /dev/null +++ b/src/ui/jsonapi/messages/MsgHandlerAccessRights.cpp @@ -0,0 +1,167 @@ +/*! + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "MsgHandlerAccessRights.h" + +#include + +using namespace governikus; + +MsgHandlerAccessRights::MsgHandlerAccessRights(const MsgContext& pContext) + : MsgHandler(MsgType::ACCESS_RIGHTS) +{ + Q_ASSERT(pContext.getAuthContext()); + fillAccessRights(pContext.getAuthContext()); +} + + +MsgHandlerAccessRights::MsgHandlerAccessRights(const QJsonObject& pObj, MsgContext& pContext) + : MsgHandler(MsgType::ACCESS_RIGHTS) +{ + auto ctx = pContext.getAuthContext(); + Q_ASSERT(ctx); + + const auto& jsonRaw = pObj[QLatin1String("chat")]; + if (jsonRaw.isUndefined()) + { + setError(QLatin1String("'chat' cannot be undefined")); + } + else if (!jsonRaw.isArray()) + { + setError(QLatin1String("Invalid 'chat' data")); + } + else + { + handleSetChatData(jsonRaw.toArray(), ctx); + } + + fillAccessRights(ctx); +} + + +void MsgHandlerAccessRights::handleSetChatData(const QJsonArray& pChat, const QSharedPointer& pContext) +{ + Q_ASSERT(pContext); + + QSet effectiveChat; + + if (!pContext->getOptionalAccessRights().isEmpty()) + { + for (const auto& entry : pChat) + { + if (entry.isString()) + { + const auto& func = [&](AccessRight pRight){ + if (pContext->getOptionalAccessRights().contains(pRight)) + { + effectiveChat += pRight; + } + else + { + setError(QLatin1String("Entry in 'chat' data is not available")); + } + }; + + if (!AccessRoleAndRightsUtil::fromTechnicalName(entry.toString(), func)) + { + setError(QLatin1String("Entry in 'chat' data is invalid")); + } + } + else + { + setError(QLatin1String("Entry in 'chat' data needs to be string")); + } + } + } + else + { + setError(QLatin1String("No optional access rights available")); + } + + if (!mJsonObject.contains(QLatin1String("error"))) + { + pContext->setEffectiveAccessRights(effectiveChat); + } +} + + +QJsonArray MsgHandlerAccessRights::getAccessRights(const QSet& pRights) const +{ + QJsonArray array; + + QList accessRights = pRights.toList(); + std::sort(accessRights.rbegin(), accessRights.rend()); + for (auto entry : qAsConst(accessRights)) + { + const QLatin1String name = AccessRoleAndRightsUtil::toTechnicalName(entry); + if (name.size()) + { + array += name; + } + } + + return array; +} + + +void MsgHandlerAccessRights::fillAccessRights(const QSharedPointer& pContext) +{ + Q_ASSERT(pContext); + + QJsonObject chat; + chat[QLatin1String("required")] = getAccessRights(pContext->getRequiredAccessRights()); + chat[QLatin1String("optional")] = getAccessRights(pContext->getOptionalAccessRights()); + chat[QLatin1String("effective")] = getAccessRights(pContext->getEffectiveAccessRights()); + + mJsonObject[QLatin1String("chat")] = chat; + const auto& transactionInfo = pContext->getDidAuthenticateEac1()->getTransactionInfo(); + if (!transactionInfo.isEmpty()) + { + mJsonObject[QLatin1String("transactionInfo")] = transactionInfo; + } + + const QJsonObject& aux = getAuxiliaryData(pContext); + if (!aux.isEmpty()) + { + mJsonObject[QLatin1String("aux")] = aux; + } +} + + +QJsonObject MsgHandlerAccessRights::getAuxiliaryData(const QSharedPointer& pContext) +{ + QJsonObject obj; + + const auto& eac1 = pContext->getDidAuthenticateEac1(); + if (eac1) + { + const auto& aux = eac1->getAuthenticatedAuxiliaryData(); + if (aux) + { + if (aux->hasAgeVerificationDate()) + { + obj[QLatin1String("ageVerificationDate")] = aux->getAgeVerificationDate().toString(Qt::ISODate); + obj[QLatin1String("requiredAge")] = aux->getRequiredAge(); + } + + if (aux->hasValidityDate()) + { + obj[QLatin1String("validityDate")] = aux->getValidityDate().toString(Qt::ISODate); + } + + if (aux->hasCommunityID()) + { + obj[QLatin1String("communityId")] = QString::fromUtf8(aux->getCommunityID()); + } + } + } + + return obj; +} + + +void MsgHandlerAccessRights::setError(const QLatin1String pError) +{ + mJsonObject[QLatin1String("error")] = pError; +} diff --git a/src/ui/jsonapi/messages/MsgHandlerAccessRights.h b/src/ui/jsonapi/messages/MsgHandlerAccessRights.h new file mode 100644 index 0000000..1ad542a --- /dev/null +++ b/src/ui/jsonapi/messages/MsgHandlerAccessRights.h @@ -0,0 +1,35 @@ +/*! + * \brief Message MsgHandlerAccessRights of JSON API. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/AuthContext.h" +#include "MsgContext.h" +#include "MsgHandler.h" + +#include + +namespace governikus +{ + +class MsgHandlerAccessRights + : public MsgHandler +{ + private: + void setError(const QLatin1String pError); + + void handleSetChatData(const QJsonArray& pChat, const QSharedPointer& pContext); + QJsonArray getAccessRights(const QSet& pRights) const; + void fillAccessRights(const QSharedPointer& pContext); + QJsonObject getAuxiliaryData(const QSharedPointer& pContext); + + public: + MsgHandlerAccessRights(const MsgContext& pContext); + MsgHandlerAccessRights(const QJsonObject& pObj, MsgContext& pContext); +}; + + +} // namespace governikus diff --git a/src/jsonapi/messages/MsgHandlerApiLevel.cpp b/src/ui/jsonapi/messages/MsgHandlerApiLevel.cpp similarity index 100% rename from src/jsonapi/messages/MsgHandlerApiLevel.cpp rename to src/ui/jsonapi/messages/MsgHandlerApiLevel.cpp diff --git a/src/ui/jsonapi/messages/MsgHandlerApiLevel.h b/src/ui/jsonapi/messages/MsgHandlerApiLevel.h new file mode 100644 index 0000000..13b6c69 --- /dev/null +++ b/src/ui/jsonapi/messages/MsgHandlerApiLevel.h @@ -0,0 +1,29 @@ +/*! + * \brief Message API_LEVEL of JSON API. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "MsgContext.h" +#include "MsgHandler.h" + +namespace governikus +{ + +class MsgHandlerApiLevel + : public MsgHandler +{ + private: + void setError(const QLatin1String pError); + void setCurrentLevel(MsgLevel pLevel); + void setAvailableLevel(); + + public: + MsgHandlerApiLevel(const MsgContext& pContext); + MsgHandlerApiLevel(const QJsonObject& pObj, MsgContext& pContext); +}; + + +} // namespace governikus diff --git a/src/ui/jsonapi/messages/MsgHandlerAuth.cpp b/src/ui/jsonapi/messages/MsgHandlerAuth.cpp new file mode 100644 index 0000000..1402783 --- /dev/null +++ b/src/ui/jsonapi/messages/MsgHandlerAuth.cpp @@ -0,0 +1,102 @@ +/*! + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "MsgHandlerAuth.h" + +#include "InternalActivationHandler.h" + +#include +#include + +using namespace governikus; + + +MsgHandlerAuth::MsgHandlerAuth() + : MsgHandler(MsgType::AUTH) +{ +} + + +MsgHandlerAuth::MsgHandlerAuth(const QJsonObject& pObj) + : MsgHandlerAuth() +{ + const auto& jsonTcTokenUrl = pObj[QLatin1String("tcTokenURL")]; + if (jsonTcTokenUrl.isUndefined()) + { + setError(QLatin1String("tcTokenURL cannot be undefined")); + } + else if (!jsonTcTokenUrl.isString()) + { + setError(QLatin1String("Invalid tcTokenURL")); + } + else + { + const auto& url = createUrl(jsonTcTokenUrl.toString()); + if (url.isValid()) + { + initAuth(url); + setVoid(); + return; + } + Q_ASSERT(mJsonObject[QLatin1String("error")].isString()); + } +} + + +MsgHandlerAuth::MsgHandlerAuth(const QSharedPointer& pContext) + : MsgHandlerAuth() +{ + Q_ASSERT(pContext); + + mJsonObject[QLatin1String("result")] = ECardApiResult(pContext->getStatus()).toJson(); + + QString url; + if (pContext->getRefreshUrl().isEmpty()) + { + const auto& token = pContext->getTcToken(); + if (!token.isNull() && pContext->getTcToken()->getCommunicationErrorAddress().isValid()) + { + url = pContext->getTcToken()->getCommunicationErrorAddress().toString(); + } + } + else + { + url = pContext->getRefreshUrl().toString(); + } + + setValue("url", url); +} + + +QUrl MsgHandlerAuth::createUrl(const QString& pUrl) +{ + const QUrl parsedUrl(pUrl); + if (parsedUrl.isValid() && !parsedUrl.host().isEmpty()) + { + QUrlQuery query; + query.addQueryItem(QStringLiteral("tcTokenURL"), QString::fromLatin1(QUrl::toPercentEncoding(pUrl))); + + QUrl url(QStringLiteral("http://localhost/")); // just a dummy for StateParseTcTokenUrl + url.setQuery(query); + + return url; + } + + setError(QLatin1String("Validation of tcTokenURL failed")); + return QUrl(); +} + + +void MsgHandlerAuth::initAuth(const QUrl& pTcTokenUrl) +{ + auto handler = ActivationHandler::getInstance(); + Q_ASSERT(handler); + handler->runAuthentication(QSharedPointer::create(pTcTokenUrl)); +} + + +void MsgHandlerAuth::setError(const QLatin1String pError) +{ + setValue("error", pError); +} diff --git a/src/ui/jsonapi/messages/MsgHandlerAuth.h b/src/ui/jsonapi/messages/MsgHandlerAuth.h new file mode 100644 index 0000000..486d72a --- /dev/null +++ b/src/ui/jsonapi/messages/MsgHandlerAuth.h @@ -0,0 +1,31 @@ +/*! + * \brief Message Auth of JSON API. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "MsgHandler.h" + +#include "context/AuthContext.h" + +namespace governikus +{ + +class MsgHandlerAuth + : public MsgHandler +{ + private: + QUrl createUrl(const QString& pUrl); + void initAuth(const QUrl& pTcTokenUrl); + void setError(const QLatin1String pError); + + public: + MsgHandlerAuth(); + MsgHandlerAuth(const QJsonObject& pObj); + MsgHandlerAuth(const QSharedPointer& pContext); +}; + + +} // namespace governikus diff --git a/src/jsonapi/messages/MsgHandlerBadState.cpp b/src/ui/jsonapi/messages/MsgHandlerBadState.cpp similarity index 100% rename from src/jsonapi/messages/MsgHandlerBadState.cpp rename to src/ui/jsonapi/messages/MsgHandlerBadState.cpp diff --git a/src/ui/jsonapi/messages/MsgHandlerBadState.h b/src/ui/jsonapi/messages/MsgHandlerBadState.h new file mode 100644 index 0000000..6588a11 --- /dev/null +++ b/src/ui/jsonapi/messages/MsgHandlerBadState.h @@ -0,0 +1,22 @@ +/*! + * \brief Message BadState of JSON API. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "MsgHandler.h" + +namespace governikus +{ + +class MsgHandlerBadState + : public MsgHandler +{ + public: + MsgHandlerBadState(MsgCmdType pType = MsgCmdType::UNDEFINED); +}; + + +} // namespace governikus diff --git a/src/jsonapi/messages/MsgHandlerCertificate.cpp b/src/ui/jsonapi/messages/MsgHandlerCertificate.cpp similarity index 100% rename from src/jsonapi/messages/MsgHandlerCertificate.cpp rename to src/ui/jsonapi/messages/MsgHandlerCertificate.cpp diff --git a/src/ui/jsonapi/messages/MsgHandlerCertificate.h b/src/ui/jsonapi/messages/MsgHandlerCertificate.h new file mode 100644 index 0000000..459598b --- /dev/null +++ b/src/ui/jsonapi/messages/MsgHandlerCertificate.h @@ -0,0 +1,23 @@ +/*! + * \brief Message handler for GET_CERTIFICATE of JSON API. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "MsgContext.h" +#include "MsgHandler.h" + +namespace governikus +{ + +class MsgHandlerCertificate + : public MsgHandler +{ + public: + MsgHandlerCertificate(const MsgContext& pContext); +}; + + +} // namespace governikus diff --git a/src/ui/jsonapi/messages/MsgHandlerEnterCan.cpp b/src/ui/jsonapi/messages/MsgHandlerEnterCan.cpp new file mode 100644 index 0000000..1f6606f --- /dev/null +++ b/src/ui/jsonapi/messages/MsgHandlerEnterCan.cpp @@ -0,0 +1,27 @@ +/*! + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "MsgHandlerEnterCan.h" + +#include "context/WorkflowContext.h" + +using namespace governikus; + +MsgHandlerEnterCan::MsgHandlerEnterCan(const MsgContext& pContext) + : MsgHandlerEnterNumber(MsgType::ENTER_CAN, pContext) +{ +} + + +MsgHandlerEnterCan::MsgHandlerEnterCan(const QJsonObject& pObj, MsgContext& pContext) + : MsgHandlerEnterCan(pContext) +{ + parseValue(pObj, pContext, [&](const QString& pNumber) + { + auto ctx = pContext.getWorkflowContext(); + ctx->setCan(pNumber); + ctx->setStateApproved(); + setVoid(); + }); +} diff --git a/src/ui/jsonapi/messages/MsgHandlerEnterCan.h b/src/ui/jsonapi/messages/MsgHandlerEnterCan.h new file mode 100644 index 0000000..4575511 --- /dev/null +++ b/src/ui/jsonapi/messages/MsgHandlerEnterCan.h @@ -0,0 +1,24 @@ +/*! + * \brief Message EnterCan of JSON API. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "MsgContext.h" +#include "MsgHandlerEnterNumber.h" + +namespace governikus +{ + +class MsgHandlerEnterCan + : public MsgHandlerEnterNumber +{ + public: + MsgHandlerEnterCan(const MsgContext& pContext); + MsgHandlerEnterCan(const QJsonObject& pObj, MsgContext& pContext); +}; + + +} // namespace governikus diff --git a/src/ui/jsonapi/messages/MsgHandlerEnterNumber.cpp b/src/ui/jsonapi/messages/MsgHandlerEnterNumber.cpp new file mode 100644 index 0000000..c25653c --- /dev/null +++ b/src/ui/jsonapi/messages/MsgHandlerEnterNumber.cpp @@ -0,0 +1,91 @@ +/*! + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "MsgHandlerEnterNumber.h" + +#include "MsgHandlerReader.h" +#include "ReaderManager.h" + +#include + +using namespace governikus; + +MsgHandlerEnterNumber::MsgHandlerEnterNumber(MsgType pType, const MsgContext& pContext) + : MsgHandler(pType) +{ + Q_ASSERT(pContext.getWorkflowContext()); + setReader(pContext.getWorkflowContext()); +} + + +void MsgHandlerEnterNumber::setError(const QString& pError) +{ + mJsonObject[QLatin1String("error")] = pError; +} + + +void MsgHandlerEnterNumber::setReader(const QSharedPointer& pContext) +{ + const auto& reader = pContext->getReaderName(); + if (!reader.isEmpty()) + { + const auto& info = Env::getSingleton()->getReaderInfo(reader); + if (info.isConnected()) + { + mJsonObject[QLatin1String("reader")] = MsgHandlerReader::createReaderInfo(info); + } + } +} + + +void MsgHandlerEnterNumber::parseValue(const QJsonObject& pObj, + const MsgContext& pContext, + const std::function& pFunc, ushort pCount) +{ + const auto& reader = pContext.getWorkflowContext()->getReaderName(); + if (reader.isEmpty()) + { + setError(QStringLiteral("No card inserted")); + return; + } + + const auto& value = pObj[QLatin1String("value")]; + + if (Env::getSingleton()->getReaderInfo(reader).isBasicReader()) + { + if (value.isUndefined()) + { + setError(QStringLiteral("Value cannot be undefined")); + return; + } + + if (!value.isString()) + { + setError(QStringLiteral("Invalid value")); + return; + } + + const auto& regex = QStringLiteral("^[0-9]{%1}$").arg(pCount); + const auto& number = value.toString(); + if (QRegularExpression(regex).match(number).hasMatch()) + { + pFunc(number); + } + else + { + setError(QStringLiteral("You must provide %1 digits").arg(pCount)); + } + } + else + { + if (value.isUndefined()) + { + pFunc(QString()); + } + else + { + setError(QStringLiteral("Value cannot be defined")); + } + } +} diff --git a/src/ui/jsonapi/messages/MsgHandlerEnterNumber.h b/src/ui/jsonapi/messages/MsgHandlerEnterNumber.h new file mode 100644 index 0000000..cbfc321 --- /dev/null +++ b/src/ui/jsonapi/messages/MsgHandlerEnterNumber.h @@ -0,0 +1,35 @@ +/*! + * \brief Helper handler for EnterCan, EnterPin and EnterPuk of JSON API. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/WorkflowContext.h" +#include "MsgContext.h" +#include "MsgHandler.h" + +#include + +namespace governikus +{ + +class MsgHandlerEnterNumber + : public MsgHandler +{ + private: + void setError(const QString& pError); + void setReader(const QSharedPointer& pContext); + + protected: + MsgHandlerEnterNumber(MsgType pType, const MsgContext& pContext); + + void parseValue(const QJsonObject& pObj, + const MsgContext& pContext, + const std::function& pFunc, + ushort pCount = 6); +}; + + +} // namespace governikus diff --git a/src/ui/jsonapi/messages/MsgHandlerEnterPin.cpp b/src/ui/jsonapi/messages/MsgHandlerEnterPin.cpp new file mode 100644 index 0000000..7d97565 --- /dev/null +++ b/src/ui/jsonapi/messages/MsgHandlerEnterPin.cpp @@ -0,0 +1,27 @@ +/*! + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "MsgHandlerEnterPin.h" + +#include "context/WorkflowContext.h" + +using namespace governikus; + +MsgHandlerEnterPin::MsgHandlerEnterPin(const MsgContext& pContext) + : MsgHandlerEnterNumber(MsgType::ENTER_PIN, pContext) +{ +} + + +MsgHandlerEnterPin::MsgHandlerEnterPin(const QJsonObject& pObj, MsgContext& pContext) + : MsgHandlerEnterPin(pContext) +{ + parseValue(pObj, pContext, [&](const QString& pNumber) + { + auto ctx = pContext.getWorkflowContext(); + ctx->setPin(pNumber); + ctx->setStateApproved(); + setVoid(); + }); +} diff --git a/src/ui/jsonapi/messages/MsgHandlerEnterPin.h b/src/ui/jsonapi/messages/MsgHandlerEnterPin.h new file mode 100644 index 0000000..8aafb58 --- /dev/null +++ b/src/ui/jsonapi/messages/MsgHandlerEnterPin.h @@ -0,0 +1,24 @@ +/*! + * \brief Message EnterPin of JSON API. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "MsgContext.h" +#include "MsgHandlerEnterNumber.h" + +namespace governikus +{ + +class MsgHandlerEnterPin + : public MsgHandlerEnterNumber +{ + public: + MsgHandlerEnterPin(const MsgContext& pContext); + MsgHandlerEnterPin(const QJsonObject& pObj, MsgContext& pContext); +}; + + +} // namespace governikus diff --git a/src/ui/jsonapi/messages/MsgHandlerEnterPuk.cpp b/src/ui/jsonapi/messages/MsgHandlerEnterPuk.cpp new file mode 100644 index 0000000..dd682ec --- /dev/null +++ b/src/ui/jsonapi/messages/MsgHandlerEnterPuk.cpp @@ -0,0 +1,27 @@ +/*! + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "MsgHandlerEnterPuk.h" + +#include "context/WorkflowContext.h" + +using namespace governikus; + +MsgHandlerEnterPuk::MsgHandlerEnterPuk(const MsgContext& pContext) + : MsgHandlerEnterNumber(MsgType::ENTER_PUK, pContext) +{ +} + + +MsgHandlerEnterPuk::MsgHandlerEnterPuk(const QJsonObject& pObj, MsgContext& pContext) + : MsgHandlerEnterPuk(pContext) +{ + parseValue(pObj, pContext, [&](const QString& pNumber) + { + auto ctx = pContext.getWorkflowContext(); + ctx->setPuk(pNumber); + ctx->setStateApproved(); + setVoid(); + }, 10); +} diff --git a/src/ui/jsonapi/messages/MsgHandlerEnterPuk.h b/src/ui/jsonapi/messages/MsgHandlerEnterPuk.h new file mode 100644 index 0000000..f2a3c0a --- /dev/null +++ b/src/ui/jsonapi/messages/MsgHandlerEnterPuk.h @@ -0,0 +1,24 @@ +/*! + * \brief Message EnterPuk of JSON API. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "MsgContext.h" +#include "MsgHandlerEnterNumber.h" + +namespace governikus +{ + +class MsgHandlerEnterPuk + : public MsgHandlerEnterNumber +{ + public: + MsgHandlerEnterPuk(const MsgContext& pContext); + MsgHandlerEnterPuk(const QJsonObject& pObj, MsgContext& pContext); +}; + + +} // namespace governikus diff --git a/src/jsonapi/messages/MsgHandlerInfo.cpp b/src/ui/jsonapi/messages/MsgHandlerInfo.cpp similarity index 100% rename from src/jsonapi/messages/MsgHandlerInfo.cpp rename to src/ui/jsonapi/messages/MsgHandlerInfo.cpp diff --git a/src/ui/jsonapi/messages/MsgHandlerInfo.h b/src/ui/jsonapi/messages/MsgHandlerInfo.h new file mode 100644 index 0000000..54135a9 --- /dev/null +++ b/src/ui/jsonapi/messages/MsgHandlerInfo.h @@ -0,0 +1,22 @@ +/*! + * \brief Message Info of JSON API. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "MsgHandler.h" + +namespace governikus +{ + +class MsgHandlerInfo + : public MsgHandler +{ + public: + MsgHandlerInfo(); +}; + + +} // namespace governikus diff --git a/src/ui/jsonapi/messages/MsgHandlerInsertCard.cpp b/src/ui/jsonapi/messages/MsgHandlerInsertCard.cpp new file mode 100644 index 0000000..741c310 --- /dev/null +++ b/src/ui/jsonapi/messages/MsgHandlerInsertCard.cpp @@ -0,0 +1,26 @@ +/*! + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "MsgHandlerInsertCard.h" + +#include "ReaderManager.h" + +using namespace governikus; + +MsgHandlerInsertCard::MsgHandlerInsertCard(MsgContext& pContext) + : MsgHandler(MsgType::INSERT_CARD) +{ + Q_ASSERT(pContext.getWorkflowContext()); + pContext.getWorkflowContext()->setStateApproved(); + + const auto& infos = Env::getSingleton()->getReaderInfos(); + for (const auto& entry : infos) + { + if (entry.hasEidCard()) + { + setVoid(); + break; + } + } +} diff --git a/src/ui/jsonapi/messages/MsgHandlerInsertCard.h b/src/ui/jsonapi/messages/MsgHandlerInsertCard.h new file mode 100644 index 0000000..61591f7 --- /dev/null +++ b/src/ui/jsonapi/messages/MsgHandlerInsertCard.h @@ -0,0 +1,23 @@ +/*! + * \brief Message InsertCard of JSON API. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "MsgContext.h" +#include "MsgHandler.h" + +namespace governikus +{ + +class MsgHandlerInsertCard + : public MsgHandler +{ + public: + MsgHandlerInsertCard(MsgContext& pContext); +}; + + +} // namespace governikus diff --git a/src/jsonapi/messages/MsgHandlerInternalError.cpp b/src/ui/jsonapi/messages/MsgHandlerInternalError.cpp similarity index 100% rename from src/jsonapi/messages/MsgHandlerInternalError.cpp rename to src/ui/jsonapi/messages/MsgHandlerInternalError.cpp diff --git a/src/ui/jsonapi/messages/MsgHandlerInternalError.h b/src/ui/jsonapi/messages/MsgHandlerInternalError.h new file mode 100644 index 0000000..3ff49ce --- /dev/null +++ b/src/ui/jsonapi/messages/MsgHandlerInternalError.h @@ -0,0 +1,23 @@ +/*! + * \brief Message INTERNAL_ERROR of JSON API. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "MsgHandler.h" + +namespace governikus +{ + +class MsgHandlerInternalError + : public MsgHandler +{ + public: + MsgHandlerInternalError(const QString& pError = QString()); + MsgHandlerInternalError(const QLatin1String pError); +}; + + +} // namespace governikus diff --git a/src/jsonapi/messages/MsgHandlerInvalid.cpp b/src/ui/jsonapi/messages/MsgHandlerInvalid.cpp similarity index 100% rename from src/jsonapi/messages/MsgHandlerInvalid.cpp rename to src/ui/jsonapi/messages/MsgHandlerInvalid.cpp diff --git a/src/ui/jsonapi/messages/MsgHandlerInvalid.h b/src/ui/jsonapi/messages/MsgHandlerInvalid.h new file mode 100644 index 0000000..8bba4f2 --- /dev/null +++ b/src/ui/jsonapi/messages/MsgHandlerInvalid.h @@ -0,0 +1,26 @@ +/*! + * \brief Message Invalid of JSON API. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "MsgHandler.h" + +#include + +namespace governikus +{ + +class MsgHandlerInvalid + : public MsgHandler +{ + public: + MsgHandlerInvalid(const QString& pError = QString()); + MsgHandlerInvalid(const QLatin1String pError); + MsgHandlerInvalid(const QJsonParseError& pError); +}; + + +} // namespace governikus diff --git a/src/ui/jsonapi/messages/MsgHandlerReader.cpp b/src/ui/jsonapi/messages/MsgHandlerReader.cpp new file mode 100644 index 0000000..e21a0a1 --- /dev/null +++ b/src/ui/jsonapi/messages/MsgHandlerReader.cpp @@ -0,0 +1,80 @@ +/*! + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "MsgHandlerReader.h" + +#include "ReaderManager.h" + +using namespace governikus; + +MsgHandlerReader::MsgHandlerReader(const QJsonObject& pObj) + : MsgHandler(MsgType::READER) +{ + const auto& jsonName = pObj[QLatin1String("name")]; + if (jsonName.isUndefined()) + { + setError(QLatin1String("Name cannot be undefined")); + } + else if (!jsonName.isString()) + { + setError(QLatin1String("Invalid name")); + } + else + { + setReaderInfo(jsonName.toString()); + } +} + + +MsgHandlerReader::MsgHandlerReader(const QString& pName) + : MsgHandler(MsgType::READER) +{ + Q_ASSERT(!pName.isEmpty()); + setReaderInfo(pName); +} + + +void MsgHandlerReader::setError(const QLatin1String pError) +{ + mJsonObject[QLatin1String("error")] = pError; +} + + +void MsgHandlerReader::setReaderInfo(const QString& pName) +{ + setReaderInfo(mJsonObject, Env::getSingleton()->getReaderInfo(pName)); +} + + +QJsonObject MsgHandlerReader::createReaderInfo(const ReaderInfo& pInfo) +{ + Q_ASSERT(!pInfo.getName().isEmpty()); + QJsonObject obj; + setReaderInfo(obj, pInfo); + return obj; +} + + +void MsgHandlerReader::setReaderInfo(QJsonObject& pObj, const ReaderInfo& pInfo) +{ + pObj[QLatin1String("name")] = pInfo.getName(); + pObj[QLatin1String("attached")] = pInfo.isConnected(); + if (pInfo.isConnected()) + { + pObj[QLatin1String("keypad")] = !pInfo.isBasicReader(); + + if (pInfo.hasEidCard()) + { + QJsonObject card; + card[QLatin1String("deactivated")] = pInfo.isPinDeactivated(); + card[QLatin1String("inoperative")] = pInfo.isPukInoperative(); + card[QLatin1String("retryCounter")] = pInfo.getRetryCounter(); + pObj[QLatin1String("card")] = card; + } + else + { + pObj[QLatin1String("card")] = QJsonValue::Null; + } + } +} diff --git a/src/ui/jsonapi/messages/MsgHandlerReader.h b/src/ui/jsonapi/messages/MsgHandlerReader.h new file mode 100644 index 0000000..eab6666 --- /dev/null +++ b/src/ui/jsonapi/messages/MsgHandlerReader.h @@ -0,0 +1,32 @@ +/*! + * \brief Message Reader of JSON API. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "MsgHandler.h" +#include "ReaderInfo.h" + +namespace governikus +{ + +class MsgHandlerReader + : public MsgHandler +{ + private: + static void setReaderInfo(QJsonObject& pObj, const ReaderInfo& pInfo); + + void setError(const QLatin1String pError); + void setReaderInfo(const QString& pName); + + public: + static QJsonObject createReaderInfo(const ReaderInfo& pInfo); + + MsgHandlerReader(const QJsonObject& pObj); + MsgHandlerReader(const QString& pName); +}; + + +} // namespace governikus diff --git a/src/ui/jsonapi/messages/MsgHandlerReaderList.cpp b/src/ui/jsonapi/messages/MsgHandlerReaderList.cpp new file mode 100644 index 0000000..c8e32b6 --- /dev/null +++ b/src/ui/jsonapi/messages/MsgHandlerReaderList.cpp @@ -0,0 +1,24 @@ +/*! + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "MsgHandlerReaderList.h" + +#include "MsgHandlerReader.h" +#include "ReaderManager.h" + +#include + +using namespace governikus; + +MsgHandlerReaderList::MsgHandlerReaderList() + : MsgHandler(MsgType::READER_LIST) +{ + QJsonArray reader; + const auto& infoList = Env::getSingleton()->getReaderInfos(); + for (const auto& info : infoList) + { + reader += MsgHandlerReader::createReaderInfo(info); + } + mJsonObject[QLatin1String("reader")] = reader; +} diff --git a/src/ui/jsonapi/messages/MsgHandlerReaderList.h b/src/ui/jsonapi/messages/MsgHandlerReaderList.h new file mode 100644 index 0000000..5a44739 --- /dev/null +++ b/src/ui/jsonapi/messages/MsgHandlerReaderList.h @@ -0,0 +1,22 @@ +/*! + * \brief Message ReaderList of JSON API. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "MsgHandler.h" + +namespace governikus +{ + +class MsgHandlerReaderList + : public MsgHandler +{ + public: + MsgHandlerReaderList(); +}; + + +} // namespace governikus diff --git a/src/jsonapi/messages/MsgHandlerUnknownCommand.cpp b/src/ui/jsonapi/messages/MsgHandlerUnknownCommand.cpp similarity index 100% rename from src/jsonapi/messages/MsgHandlerUnknownCommand.cpp rename to src/ui/jsonapi/messages/MsgHandlerUnknownCommand.cpp diff --git a/src/ui/jsonapi/messages/MsgHandlerUnknownCommand.h b/src/ui/jsonapi/messages/MsgHandlerUnknownCommand.h new file mode 100644 index 0000000..61ea8e2 --- /dev/null +++ b/src/ui/jsonapi/messages/MsgHandlerUnknownCommand.h @@ -0,0 +1,22 @@ +/*! + * \brief MsgHandlerUnknownCommand of JSON API. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "MsgHandler.h" + +namespace governikus +{ + +class MsgHandlerUnknownCommand + : public MsgHandler +{ + public: + MsgHandlerUnknownCommand(const QString& pName); +}; + + +} // namespace governikus diff --git a/src/jsonapi/messages/MsgTypes.cpp b/src/ui/jsonapi/messages/MsgTypes.cpp similarity index 100% rename from src/jsonapi/messages/MsgTypes.cpp rename to src/ui/jsonapi/messages/MsgTypes.cpp diff --git a/src/ui/jsonapi/messages/MsgTypes.h b/src/ui/jsonapi/messages/MsgTypes.h new file mode 100644 index 0000000..ba1601b --- /dev/null +++ b/src/ui/jsonapi/messages/MsgTypes.h @@ -0,0 +1,49 @@ +/*! + * \brief Enumerations of message types and additional stuff. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "EnumHelper.h" + +namespace governikus +{ +defineEnumType(MsgLevel, v1 = 1) // See MsgHandler::DEFAULT_MSG_LEVEL + +defineEnumType(MsgType, + INVALID, + UNKNOWN_COMMAND, + INTERNAL_ERROR, + INFO, + API_LEVEL, + READER, + READER_LIST, + BAD_STATE, + AUTH, + CERTIFICATE, + ACCESS_RIGHTS, + INSERT_CARD, + ENTER_PIN, + ENTER_CAN, + ENTER_PUK) + +defineEnumType(MsgCmdType, + UNDEFINED, + ACCEPT, + CANCEL, + GET_INFO, + GET_API_LEVEL, + SET_API_LEVEL, + GET_READER, + GET_READER_LIST, + RUN_AUTH, + GET_CERTIFICATE, + GET_ACCESS_RIGHTS, + SET_ACCESS_RIGHTS, + SET_PIN, + SET_CAN, + SET_PUK) + +} // namespace governikus diff --git a/src/jsonapi/metadata.json b/src/ui/jsonapi/metadata.json similarity index 100% rename from src/jsonapi/metadata.json rename to src/ui/jsonapi/metadata.json diff --git a/src/ui/qml/ApplicationModel.cpp b/src/ui/qml/ApplicationModel.cpp new file mode 100644 index 0000000..1806669 --- /dev/null +++ b/src/ui/qml/ApplicationModel.cpp @@ -0,0 +1,285 @@ +/*! + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "ApplicationModel.h" + +#include "context/AuthContext.h" +#include "context/ChangePinContext.h" +#include "context/SelfAuthContext.h" + +#include "AppSettings.h" +#include "BuildHelper.h" +#include "DpiCalculator.h" +#include "Env.h" +#include "ReaderInfo.h" +#include "ReaderManager.h" +#include "RemoteClient.h" +#include "SingletonHelper.h" + +#if !defined(QT_NO_DEBUG) || defined(Q_OS_ANDROID) +#include "SurveyHandler.h" +#endif + +#if (defined(Q_OS_LINUX) && !defined(QT_NO_DEBUG)) || defined(Q_OS_ANDROID) || defined(Q_OS_IOS) +#include +#endif + +#ifdef Q_OS_ANDROID +#include +#endif + + +using namespace governikus; + +defineSingleton(ApplicationModel) + + +void ApplicationModel::onStatusChanged(const ReaderManagerPlugInInfo& pInfo) +{ + if (pInfo.getPlugInType() == ReaderManagerPlugInType::BLUETOOTH) + { + Q_EMIT fireBluetoothEnabledChanged(); + Q_EMIT fireBluetoothRespondingChanged(); + } + else if (pInfo.getPlugInType() == ReaderManagerPlugInType::NFC) + { + Q_EMIT fireNfcEnabledChanged(); + } +} + + +ApplicationModel::ApplicationModel() + : mContext() + , mDpiScale(DpiCalculator::getDpiScale()) + , mScaleFactor(mDpiScale) + , mWifiInfo() + , mWifiEnabled(false) + , mBluetoothResponding(true) +{ + const auto readerManager = Env::getSingleton(); + connect(readerManager, &ReaderManager::fireReaderAdded, this, &ApplicationModel::fireBluetoothReaderChanged); + connect(readerManager, &ReaderManager::fireReaderRemoved, this, &ApplicationModel::fireBluetoothReaderChanged); + connect(readerManager, &ReaderManager::fireReaderPropertiesUpdated, this, &ApplicationModel::fireReaderPropertiesUpdated); + connect(readerManager, &ReaderManager::fireStatusChanged, this, &ApplicationModel::onStatusChanged); + connect(readerManager, &ReaderManager::fireStatusChanged, this, &ApplicationModel::fireBluetoothReaderChanged); + connect(readerManager, &ReaderManager::fireReaderAdded, this, &ApplicationModel::fireSelectedReaderChanged); + connect(readerManager, &ReaderManager::fireReaderRemoved, this, &ApplicationModel::fireSelectedReaderChanged); + connect(&mWifiInfo, &WifiInfo::fireWifiEnabledChanged, this, &ApplicationModel::onWifiEnabledChanged); + + onWifiEnabledChanged(); + + RemoteClient* const remoteClient = Env::getSingleton(); + connect(remoteClient, &RemoteClient::fireCertificateRemoved, this, &ApplicationModel::fireCertificateRemoved); +} + + +void ApplicationModel::resetContext(const QSharedPointer& pContext) +{ + if (mContext) + { + disconnect(mContext.data(), &WorkflowContext::fireReaderPlugInTypesChanged, this, &ApplicationModel::fireSelectedReaderChanged); + } + + if ((mContext = pContext)) + { + connect(mContext.data(), &WorkflowContext::fireReaderPlugInTypesChanged, this, &ApplicationModel::fireSelectedReaderChanged); + } + Q_EMIT fireCurrentWorkflowChanged(); +} + + +QString ApplicationModel::getPackageName() const +{ +#ifdef Q_OS_ANDROID + return BuildHelper::getPackageName(); + +#else + return QString(); + +#endif +} + + +ReaderManagerPlugInInfo ApplicationModel::getFirstPlugInInfo(ReaderManagerPlugInType pType) const +{ + const auto pluginInfos = Env::getSingleton()->getPlugInInfos(); + for (const auto& info : pluginInfos) + { + if (info.getPlugInType() == pType) + { + return info; + } + } + return ReaderManagerPlugInInfo(); +} + + +bool ApplicationModel::isNfcAvailable() const +{ +#if !defined(QT_NO_DEBUG) && !defined(Q_OS_IOS) && !defined(Q_OS_ANDROID) && !defined(Q_OS_WINRT) + return getFirstPlugInInfo(ReaderManagerPlugInType::PCSC).isAvailable(); + +#else + return getFirstPlugInInfo(ReaderManagerPlugInType::NFC).isAvailable(); + +#endif +} + + +bool ApplicationModel::isNfcEnabled() const +{ +#if !defined(QT_NO_DEBUG) && !defined(Q_OS_IOS) && !defined(Q_OS_ANDROID) && !defined(Q_OS_WINRT) + return getFirstPlugInInfo(ReaderManagerPlugInType::PCSC).isEnabled(); + +#else + return getFirstPlugInInfo(ReaderManagerPlugInType::NFC).isEnabled(); + +#endif +} + + +bool ApplicationModel::isExtendedLengthApdusUnsupported() const +{ + if (mContext && !mContext->getReaderName().isEmpty()) + { + ReaderInfo readerInfo = Env::getSingleton()->getReaderInfo(mContext->getReaderName()); + return !readerInfo.sufficientApduLength(); + } + return false; +} + + +bool ApplicationModel::isBluetoothAvailable() const +{ + return getFirstPlugInInfo(ReaderManagerPlugInType::BLUETOOTH).isAvailable(); +} + + +bool ApplicationModel::isBluetoothResponding() const +{ + return getFirstPlugInInfo(ReaderManagerPlugInType::BLUETOOTH).isResponding(); +} + + +bool ApplicationModel::isBluetoothEnabled() const +{ + return getFirstPlugInInfo(ReaderManagerPlugInType::BLUETOOTH).isEnabled(); +} + + +void ApplicationModel::setBluetoothEnabled(bool pEnabled) +{ +#if (defined(Q_OS_LINUX) && !defined(QT_NO_DEBUG)) || defined(Q_OS_ANDROID) || defined(Q_OS_IOS) + if (pEnabled) + { + QBluetoothLocalDevice localDevice; + localDevice.powerOn(); + qDebug() << "Bluetooth enabled"; + } + else + { + qWarning() << "Cannot disable Bluetooth: not supported"; + } +#else + qWarning() << (pEnabled ? "Enabling" : "Disabling") << "Bluetooth not supported on this platform"; +#endif +} + + +bool ApplicationModel::locationPermissionRequired() const +{ +#ifdef Q_OS_ANDROID + const auto& result = QtAndroid::checkPermission(QStringLiteral("android.permission.ACCESS_COARSE_LOCATION")); + + return (result != QtAndroid::PermissionResult::Granted) && Env::getSingleton()->getReaderInfos(ReaderManagerPlugInType::BLUETOOTH).isEmpty(); + +#else + return false; + +#endif +} + + +qreal ApplicationModel::getScaleFactor() const +{ + return mScaleFactor; +} + + +void ApplicationModel::setScaleFactor(qreal pScaleFactor) +{ + pScaleFactor *= mDpiScale; + + if (qAbs(pScaleFactor - mScaleFactor) > 0) + { + mScaleFactor = pScaleFactor; + Q_EMIT fireScaleFactorChanged(); + } +} + + +QString ApplicationModel::getCurrentWorkflow() const +{ + if (mContext.objectCast()) + { + return QStringLiteral("changepin"); + } + if (mContext.objectCast()) + { + return QStringLiteral("selfauthentication"); + } + if (mContext.objectCast()) + { + return QStringLiteral("authentication"); + } + return QString(); +} + + +bool ApplicationModel::foundSelectedReader() const +{ + if (!mContext) + { + return false; + } + + return Env::getSingleton()->getReaderInfos(mContext->getReaderPlugInTypes()).size() > 0; +} + + +bool ApplicationModel::areStoreFeedbackDialogConditionsMet() const +{ + if (!getCurrentWorkflow().isEmpty()) + { + return false; + } + + const auto& settings = Env::getSingleton()->getGeneralSettings(); + return !settings.askForDeviceSurvey() && settings.isRequestStoreFeedback(); +} + + +void ApplicationModel::hideFutureStoreFeedbackDialogs() +{ + Env::getSingleton()->getGeneralSettings().setRequestStoreFeedback(false); +} + + +void ApplicationModel::onWifiEnabledChanged() +{ + mWifiEnabled = mWifiInfo.isWifiEnabled(); + Q_EMIT fireWifiEnabledChanged(); +} + + +void ApplicationModel::enableWifi() +{ + mWifiInfo.enableWifi(); +} + + +ApplicationModel& ApplicationModel::getInstance() +{ + return *Instance; +} diff --git a/src/ui/qml/ApplicationModel.h b/src/ui/qml/ApplicationModel.h new file mode 100644 index 0000000..4933a8d --- /dev/null +++ b/src/ui/qml/ApplicationModel.h @@ -0,0 +1,107 @@ +/*! + * \brief Model implementation for the application. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/WorkflowContext.h" +#include "ReaderInfo.h" +#include "ReaderManagerPlugInInfo.h" +#include "WifiInfo.h" + +#include +#include +#include +#include + +namespace governikus +{ + +class ApplicationModel + : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QString packageName READ getPackageName CONSTANT) + + Q_PROPERTY(bool nfcEnabled READ isNfcEnabled NOTIFY fireNfcEnabledChanged) + Q_PROPERTY(bool nfcAvailable READ isNfcAvailable CONSTANT) + Q_PROPERTY(bool extendedLengthApdusUnsupported READ isExtendedLengthApdusUnsupported NOTIFY fireReaderPropertiesUpdated) + + Q_PROPERTY(bool bluetoothEnabled READ isBluetoothEnabled WRITE setBluetoothEnabled NOTIFY fireBluetoothEnabledChanged) + Q_PROPERTY(bool bluetoothResponding READ isBluetoothResponding NOTIFY fireBluetoothRespondingChanged) + Q_PROPERTY(bool bluetoothAvailable READ isBluetoothAvailable CONSTANT) + Q_PROPERTY(bool locationPermissionRequired READ locationPermissionRequired NOTIFY fireBluetoothReaderChanged) + + Q_PROPERTY(qreal scaleFactor READ getScaleFactor WRITE setScaleFactor NOTIFY fireScaleFactorChanged) + Q_PROPERTY(bool wifiEnabled MEMBER mWifiEnabled NOTIFY fireWifiEnabledChanged) + + Q_PROPERTY(QString currentWorkflow READ getCurrentWorkflow NOTIFY fireCurrentWorkflowChanged) + Q_PROPERTY(bool foundSelectedReader READ foundSelectedReader NOTIFY fireSelectedReaderChanged) + + QSharedPointer mContext; + + void onStatusChanged(const ReaderManagerPlugInInfo& pInfo); + ReaderManagerPlugInInfo getFirstPlugInInfo(ReaderManagerPlugInType pType) const; + + private: + qreal mDpiScale; + qreal mScaleFactor; + WifiInfo mWifiInfo; + bool mWifiEnabled; + bool mBluetoothResponding; + + private Q_SLOTS: + void onWifiEnabledChanged(); + + protected: + ApplicationModel(); + ~ApplicationModel() override = default; + + public: + void resetContext(const QSharedPointer& pContext = QSharedPointer()); + + QString getPackageName() const; + + bool isNfcAvailable() const; + bool isNfcEnabled() const; + bool isExtendedLengthApdusUnsupported() const; + + bool isBluetoothAvailable() const; + bool isBluetoothResponding() const; + bool isBluetoothEnabled() const; + void setBluetoothEnabled(bool pEnabled); + bool locationPermissionRequired() const; + + qreal getScaleFactor() const; + void setScaleFactor(qreal pScaleFactor); + + QString getCurrentWorkflow() const; + bool foundSelectedReader() const; + + Q_INVOKABLE void enableWifi(); + Q_INVOKABLE bool areStoreFeedbackDialogConditionsMet() const; + Q_INVOKABLE void hideFutureStoreFeedbackDialogs(); + + static ApplicationModel& getInstance(); + + Q_SIGNALS: + void fireNfcEnabledChanged(); + void fireReaderPropertiesUpdated(); + + void fireBluetoothEnabledChanged(); + void fireBluetoothRespondingChanged(); + void fireBluetoothReaderChanged(); + + void fireCurrentWorkflowChanged(); + void fireSelectedReaderChanged(); + + void fireScaleFactorChanged(); + void fireWifiEnabledChanged(); + void fireCertificateRemoved(QString pDeviceName); +}; + + +} // namespace governikus diff --git a/src/ui/qml/AuthModel.cpp b/src/ui/qml/AuthModel.cpp new file mode 100644 index 0000000..bbb676c --- /dev/null +++ b/src/ui/qml/AuthModel.cpp @@ -0,0 +1,68 @@ +/*! + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "AuthModel.h" + +#include "Env.h" +#include "ReaderManagerPlugInInfo.h" +#include "SingletonHelper.h" + +using namespace governikus; + +defineSingleton(AuthModel) + + +AuthModel::AuthModel() + : WorkflowModel() + , mContext() + , mTransactionInfo() +{ +} + + +void AuthModel::resetContext(const QSharedPointer& pContext) +{ + mContext = pContext; + WorkflowModel::resetContext(pContext); + + if (mContext) + { + connect(mContext.data(), &AuthContext::fireDidAuthenticateEac1Changed, this, &AuthModel::onDidAuthenticateEac1Changed); + } + + if (!mTransactionInfo.isEmpty()) + { + mTransactionInfo.clear(); + + Q_EMIT fireTransactionInfoChanged(); + } +} + + +const QString& AuthModel::getTransactionInfo() const +{ + return mTransactionInfo; +} + + +AuthModel& AuthModel::getInstance() +{ + return *Instance; +} + + +void AuthModel::onDidAuthenticateEac1Changed() +{ + if (mContext) + { + const QSharedPointer& didAuthenticateEAC1 = mContext->getDidAuthenticateEac1(); + const QString newTransactionInfo = didAuthenticateEAC1.isNull() ? QString() : didAuthenticateEAC1->getTransactionInfo(); + if (newTransactionInfo != mTransactionInfo) + { + mTransactionInfo = newTransactionInfo; + + Q_EMIT fireTransactionInfoChanged(); + } + } +} diff --git a/src/ui/qml/AuthModel.h b/src/ui/qml/AuthModel.h new file mode 100644 index 0000000..619ad6a --- /dev/null +++ b/src/ui/qml/AuthModel.h @@ -0,0 +1,53 @@ +/*! + * \brief Model implementation for the authentication action. + * + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/AuthContext.h" +#include "WorkflowModel.h" + +#include +#include +#include +#include + +class test_AuthModel; + +namespace governikus +{ + +class AuthModel + : public WorkflowModel +{ + Q_OBJECT + + Q_PROPERTY(QString transactionInfo READ getTransactionInfo NOTIFY fireTransactionInfoChanged) + + private: + friend class ::test_AuthModel; + QSharedPointer mContext; + QString mTransactionInfo; + + protected: + AuthModel(); + ~AuthModel() override = default; + + public: + void resetContext(const QSharedPointer& pContext = QSharedPointer()); + + const QString& getTransactionInfo() const; + + static AuthModel& getInstance(); + + public Q_SLOTS: + void onDidAuthenticateEac1Changed(); + + Q_SIGNALS: + void fireTransactionInfoChanged(); +}; + + +} // namespace governikus diff --git a/src/ui/qml/CMakeLists.txt b/src/ui/qml/CMakeLists.txt new file mode 100644 index 0000000..f767236 --- /dev/null +++ b/src/ui/qml/CMakeLists.txt @@ -0,0 +1,30 @@ +##################################################################### +# The qml plugin implements the ui interface for mobile systems. +##################################################################### + +ADD_PLATFORM_LIBRARY(AusweisAppUiQml) + +TARGET_LINK_LIBRARIES(AusweisAppUiQml Qt5::Core Qt5::Svg Qt5::Qml Qt5::Quick Qt5::QuickControls2 AusweisAppGlobal AusweisAppCore AusweisAppUi AusweisAppRemoteDevice AusweisAppUiCommon) + +IF(ANDROID) + TARGET_LINK_LIBRARIES(AusweisAppUiQml AusweisAppWhitelistClient) +ELSE() + TARGET_LINK_LIBRARIES(AusweisAppUiQml debug AusweisAppWhitelistClient) +ENDIF() + +IF(DESKTOP) + TARGET_LINK_LIBRARIES(AusweisAppUiQml AusweisAppExport) +ENDIF() + +IF(TARGET Qt5::Bluetooth) + TARGET_LINK_LIBRARIES(AusweisAppUiQml Qt5::Bluetooth) +ENDIF() + +TARGET_COMPILE_DEFINITIONS(AusweisAppUiQml PRIVATE QT_STATICPLUGIN) + +STRING(REPLACE "\\" "/" RES_DIR ${RESOURCES_DIR}) +IF(CMAKE_GENERATOR STREQUAL Xcode OR CMAKE_VERSION VERSION_LESS "3.8") + TARGET_COMPILE_DEFINITIONS(AusweisAppUiQml PRIVATE $<$:RES_DIR="\\"${RES_DIR}\\"">) +ELSE() + SET_PROPERTY(SOURCE "UIPlugInQml.cpp" APPEND PROPERTY COMPILE_FLAGS $<$:-DRES_DIR="\\"${RES_DIR}\\"">) +ENDIF() diff --git a/src/ui/qml/CertificateDescriptionModel.cpp b/src/ui/qml/CertificateDescriptionModel.cpp new file mode 100644 index 0000000..61353a0 --- /dev/null +++ b/src/ui/qml/CertificateDescriptionModel.cpp @@ -0,0 +1,160 @@ +/*! + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "CertificateDescriptionModel.h" + +#include "AppSettings.h" +#include "SecureStorage.h" + + +using namespace governikus; + + +QSharedPointer CertificateDescriptionModel::getCertificateDescription() const +{ + if (mContext && mContext->getDidAuthenticateEac1()) + { + return mContext->getDidAuthenticateEac1()->getCertificateDescription(); + } + + return QSharedPointer(); +} + + +void CertificateDescriptionModel::onDidAuthenticateEac1Changed() +{ + beginResetModel(); + mData.clear(); + if (const auto& certDescr = getCertificateDescription()) + { + initModelData(certDescr); + } + endResetModel(); + + Q_EMIT fireChanged(); +} + + +void CertificateDescriptionModel::initModelData(const QSharedPointer& pCertDescription) +{ + const QString& serviceProviderAddress = pCertDescription->getServiceProviderAddress(); + const QString& purpose = getPurpose(); + const QString& dataSecurityOfficer = pCertDescription->getDataSecurityOfficer(); + const QString termsOfUsage = pCertDescription->getTermsOfUsage().remove(QLatin1Char('\r')).replace(QLatin1Char('\t'), QLatin1Char(' ')); + const bool showDetailedProviderInfo = !(serviceProviderAddress.isEmpty() || purpose.isEmpty() || dataSecurityOfficer.isEmpty()); + + mData += QPair(tr("Service provider"), getSubjectName() + QLatin1Char('\n') + getSubjectUrl()); + mData += QPair(tr("Certificate issuer"), pCertDescription->getIssuerName() + QLatin1Char('\n') + pCertDescription->getIssuerUrl()); + if (showDetailedProviderInfo) + { + mData += QPair(tr("Name, address and mail address of the service provider"), serviceProviderAddress); + mData += QPair(tr("Purpose"), purpose); + mData += QPair(tr("Indication of the bodies responsible for the service provider, " + "that verify the compliance with data security regulations"), dataSecurityOfficer); + } + else if (!termsOfUsage.isEmpty()) + { + mData += QPair(tr("Service provider information"), termsOfUsage); + } + if (!getValidity().isEmpty()) + { + mData += QPair(tr("Validity"), getValidity()); + } +} + + +CertificateDescriptionModel::CertificateDescriptionModel(QObject* pParent) + : QAbstractListModel(pParent) + , mData() + , mContext() +{ + resetContext(); + connect(Env::getSingleton(), &AppSettings::fireSettingsChanged, this, &CertificateDescriptionModel::onDidAuthenticateEac1Changed); + connect(&Env::getSingleton()->getGeneralSettings(), &GeneralSettings::fireSettingsChanged, this, [this]() + { + beginResetModel(); + onDidAuthenticateEac1Changed(); + endResetModel(); + }); +} + + +void CertificateDescriptionModel::resetContext(const QSharedPointer& pContext) +{ + mContext = pContext; + if (mContext) + { + connect(mContext.data(), &AuthContext::fireDidAuthenticateEac1Changed, this, &CertificateDescriptionModel::onDidAuthenticateEac1Changed); + } + + onDidAuthenticateEac1Changed(); +} + + +QString CertificateDescriptionModel::getSubjectName() const +{ + const auto& certificateDescription = getCertificateDescription(); + return certificateDescription ? getCertificateDescription()->getSubjectName() : QString(); +} + + +QString CertificateDescriptionModel::getSubjectUrl() const +{ + const auto& certificateDescription = getCertificateDescription(); + return certificateDescription ? getCertificateDescription()->getSubjectUrl() : QString(); +} + + +QString CertificateDescriptionModel::getPurpose() const +{ + const auto& certificateDescription = getCertificateDescription(); + return certificateDescription ? getCertificateDescription()->getPurpose() : QString(); +} + + +QString CertificateDescriptionModel::getValidity() const +{ + if (mContext && mContext->getDidAuthenticateEac1() && !mContext->getDidAuthenticateEac1()->getCvCertificates().isEmpty()) + { + const CVCertificateBody body = mContext->getDidAuthenticateEac1()->getCvCertificates().at(0)->getBody(); + const QString effectiveDate = body.getCertificateEffectiveDate().toString(Qt::DefaultLocaleShortDate); + const QString expirationDate = body.getCertificateExpirationDate().toString(Qt::DefaultLocaleShortDate); + + return QStringLiteral("%1 - %2").arg(effectiveDate, expirationDate); + } + return QString(); +} + + +int CertificateDescriptionModel::rowCount(const QModelIndex&) const +{ + return mData.size(); +} + + +QVariant CertificateDescriptionModel::data(const QModelIndex& pIndex, int pRole) const +{ + if (pIndex.isValid() && pIndex.row() < rowCount()) + { + auto entry = mData[pIndex.row()]; + if (pRole == LABEL) + { + return entry.first; + } + if (pRole == TEXT) + { + return entry.second; + } + } + return QVariant(); +} + + +QHash CertificateDescriptionModel::roleNames() const +{ + QHash roles = QAbstractListModel::roleNames(); + roles.insert(LABEL, "label"); + roles.insert(TEXT, "text"); + return roles; +} diff --git a/src/ui/qml/CertificateDescriptionModel.h b/src/ui/qml/CertificateDescriptionModel.h new file mode 100644 index 0000000..8be7560 --- /dev/null +++ b/src/ui/qml/CertificateDescriptionModel.h @@ -0,0 +1,63 @@ +/*! + * \brief Model implementation for the CV certificate description. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "asn1/CertificateDescription.h" +#include "context/AuthContext.h" + +#include +#include +#include +#include +#include + +namespace governikus +{ + +class CertificateDescriptionModel + : public QAbstractListModel +{ + Q_OBJECT + Q_PROPERTY(QString subjectName READ getSubjectName NOTIFY fireChanged) + Q_PROPERTY(QString purpose READ getPurpose NOTIFY fireChanged) + + QVector > mData; + QSharedPointer mContext; + + enum UserRoles + { + LABEL = Qt::UserRole + 1, + TEXT + }; + + + inline QSharedPointer getCertificateDescription() const; + inline QString getValidity() const; + void initModelData(const QSharedPointer& pCertDescription); + + private Q_SLOTS: + void onDidAuthenticateEac1Changed(); + + public: + CertificateDescriptionModel(QObject* pParent = nullptr); + + void resetContext(const QSharedPointer& pContext = QSharedPointer()); + + QString getSubjectName() const; + QString getSubjectUrl() const; + QString getPurpose() const; + + int rowCount(const QModelIndex& = QModelIndex()) const override; + QVariant data(const QModelIndex& pIndex, int pRole = Qt::DisplayRole) const override; + QHash roleNames() const override; + + Q_SIGNALS: + void fireChanged(); +}; + + +} // namespace governikus diff --git a/src/ui/qml/ChangePinModel.cpp b/src/ui/qml/ChangePinModel.cpp new file mode 100644 index 0000000..b668f51 --- /dev/null +++ b/src/ui/qml/ChangePinModel.cpp @@ -0,0 +1,44 @@ +/*! + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "ChangePinModel.h" + +#include "Env.h" +#include "ReaderManager.h" +#include "SingletonHelper.h" + +using namespace governikus; + +defineSingleton(ChangePinModel) + + +void ChangePinModel::resetContext(const QSharedPointer& pContext) +{ + mContext = pContext; + WorkflowModel::resetContext(pContext); + + if (mContext) + { + connect(mContext.data(), &ChangePinContext::fireSuccessMessageChanged, this, &WorkflowModel::fireResultChanged); + + Q_EMIT fireNewContextSet(); + } +} + + +QString ChangePinModel::getResultString() const +{ + if (!mContext) + { + return QString(); + } + + return isError() ? WorkflowModel::getResultString() : mContext->getSuccessMessage(); +} + + +ChangePinModel& ChangePinModel::getInstance() +{ + return *Instance; +} diff --git a/src/ui/qml/ChangePinModel.h b/src/ui/qml/ChangePinModel.h new file mode 100644 index 0000000..fc9ccee --- /dev/null +++ b/src/ui/qml/ChangePinModel.h @@ -0,0 +1,47 @@ +/*! + * \brief Model implementation for the PIN action. + * + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/ChangePinContext.h" +#include "WorkflowModel.h" + +#include +#include +#include +#include + +class test_ChangePinModel; + +namespace governikus +{ + +class ChangePinModel + : public WorkflowModel +{ + Q_OBJECT + + private: + friend class ::test_ChangePinModel; + QSharedPointer mContext; + + protected: + ChangePinModel() = default; + ~ChangePinModel() override = default; + + public: + void resetContext(const QSharedPointer& pContext = QSharedPointer()); + + virtual QString getResultString() const override; + + static ChangePinModel& getInstance(); + + Q_SIGNALS: + void fireNewContextSet(); +}; + + +} // namespace governikus diff --git a/src/ui/qml/ChatModel.cpp b/src/ui/qml/ChatModel.cpp new file mode 100644 index 0000000..8b384ec --- /dev/null +++ b/src/ui/qml/ChatModel.cpp @@ -0,0 +1,224 @@ +/*! + * \brief Model implementation for the chat. + * + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "ChatModel.h" + +#include "asn1/AccessRoleAndRight.h" +#include "asn1/CVCertificate.h" +#include "AppSettings.h" +#include "context/SelfAuthContext.h" + + +using namespace governikus; + + +ChatModel::ChatModel(QObject* pParent) + : QAbstractListModel(pParent) + , mAuthContext() + , mAllRights() + , mOptionalRights() + , mSelectedRights() + , mFilterOptionalModel() + , mFilterRequiredModel() +{ + resetContext(QSharedPointer()); + + initFilterModel(mFilterOptionalModel, QStringLiteral("true")); + initFilterModel(mFilterRequiredModel, QStringLiteral("false")); + + connect(&Env::getSingleton()->getGeneralSettings(), &GeneralSettings::fireSettingsChanged, this, [this]() + { + beginResetModel(); + endResetModel(); + }); +} + + +void ChatModel::initFilterModel(QSortFilterProxyModel& pModel, const QString& pFilter) +{ + pModel.setSourceModel(this); + pModel.setFilterRole(ChatRoles::OPTIONAL_ROLE); + pModel.setFilterRegExp(pFilter); +} + + +void ChatModel::resetContext(const QSharedPointer& pContext) +{ + mAuthContext = pContext; + + if (pContext.objectCast()) + { + /* nothing to do, access rights are static */ + } + else if (!pContext.isNull() /* it's an AuthContext */) + { + beginResetModel(); + + mAllRights.clear(); + mOptionalRights.clear(); + mSelectedRights.clear(); + + endResetModel(); + + connect(mAuthContext.data(), &AuthContext::fireAuthenticationDataChanged, this, &ChatModel::onAuthenticationDataChanged); + } + else + { + /* set static access rights according to selfAuthentication*/ + Q_ASSERT(pContext.isNull()); + + beginResetModel(); + + mAllRights.clear(); + mAllRights += AccessRight::READ_DG05; + mAllRights += AccessRight::READ_DG13; + mAllRights += AccessRight::READ_DG04; + mAllRights += AccessRight::READ_DG07; + mAllRights += AccessRight::READ_DG08; + mAllRights += AccessRight::READ_DG09; + mAllRights += AccessRight::READ_DG17; + mAllRights += AccessRight::READ_DG01; + mAllRights += AccessRight::READ_DG10; + mAllRights += AccessRight::READ_DG06; + mAllRights += AccessRight::READ_DG02; + mAllRights += AccessRight::READ_DG19; + + mOptionalRights.clear(); + mSelectedRights = mAllRights.toSet(); + + endResetModel(); + } +} + + +void ChatModel::onAuthenticationDataChanged() +{ + beginResetModel(); + + mAllRights.clear(); + mOptionalRights.clear(); + mSelectedRights.clear(); + + if (!mAuthContext->getRequiredAccessRights().isEmpty()) + { + setOrderedAllRights(mAuthContext->getRequiredAccessRights()); + mSelectedRights += mAuthContext->getRequiredAccessRights(); + } + + if (!mAuthContext->getOptionalAccessRights().isEmpty()) + { + mOptionalRights += mAuthContext->getOptionalAccessRights(); + setOrderedAllRights(mAuthContext->getOptionalAccessRights()); + mSelectedRights += mAuthContext->getOptionalAccessRights(); + } + + endResetModel(); +} + + +void ChatModel::setOrderedAllRights(const QSet& pAllRights) +{ + for (auto right : AccessRoleAndRightsUtil::allDisplayedOrderedRights()) + { + if (pAllRights.contains(right)) + { + mAllRights += right; + } + } +} + + +int ChatModel::rowCount(const QModelIndex&) const +{ + return mAllRights.size(); +} + + +QVariant ChatModel::data(const QModelIndex& pIndex, int pRole) const +{ + if (pIndex.isValid() && pIndex.row() < rowCount()) + { + auto right = mAllRights.at(pIndex.row()); + if (pRole == Qt::DisplayRole || pRole == NAME_ROLE) + { + QString displayText = AccessRoleAndRightsUtil::toDisplayText(right); + if (right == AccessRight::AGE_VERIFICATION) + { + displayText += QStringLiteral(" (%1)").arg(mAuthContext->getRequiredAge()); + } + return displayText; + } + if (pRole == OPTIONAL_ROLE) + { + return mOptionalRights.contains(right); + } + if (pRole == SELECTED_ROLE) + { + return mSelectedRights.contains(right); + } + } + return QVariant(); +} + + +QHash ChatModel::roleNames() const +{ + QHash roles = QAbstractListModel::roleNames(); + roles.insert(NAME_ROLE, "name"); + roles.insert(OPTIONAL_ROLE, "optional"); + roles.insert(SELECTED_ROLE, "selected"); + return roles; +} + + +bool ChatModel::setData(const QModelIndex& pIndex, const QVariant& pValue, int pRole) +{ + if (pRole == SELECTED_ROLE) + { + auto right = mAllRights.at(pIndex.row()); + + if (pValue.toBool()) // is selected? + { + if (mSelectedRights.contains(right)) + { + return false; + } + else + { + mSelectedRights += right; + } + } + else if (!mSelectedRights.remove(right)) + { + return false; + } + + Q_EMIT dataChanged(pIndex, pIndex, QVector({SELECTED_ROLE})); + return true; + } + + return false; +} + + +void ChatModel::transferAccessRights() +{ + Q_ASSERT(mAuthContext); + + mAuthContext->setEffectiveAccessRights(mSelectedRights); +} + + +QSortFilterProxyModel* ChatModel::getFilterOptionalModel() +{ + return &mFilterOptionalModel; +} + + +QSortFilterProxyModel* ChatModel::getFilterRequiredModel() +{ + return &mFilterRequiredModel; +} diff --git a/src/ui/qml/ChatModel.h b/src/ui/qml/ChatModel.h new file mode 100644 index 0000000..9c7f731 --- /dev/null +++ b/src/ui/qml/ChatModel.h @@ -0,0 +1,68 @@ +/*! + * \brief Model implementation for the chat. + * + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include "context/AuthContext.h" + +class test_ChatModel; + +namespace governikus +{ + +struct cvcertificate_st; + +class ChatModel + : public QAbstractListModel +{ + Q_OBJECT + Q_PROPERTY(QSortFilterProxyModel * optional READ getFilterOptionalModel CONSTANT) + Q_PROPERTY(QSortFilterProxyModel * required READ getFilterRequiredModel CONSTANT) + + QSharedPointer mAuthContext; + QList mAllRights; + QSet mOptionalRights, mSelectedRights; + QSortFilterProxyModel mFilterOptionalModel; + QSortFilterProxyModel mFilterRequiredModel; + + enum ChatRoles + { + NAME_ROLE = Qt::UserRole + 1, + OPTIONAL_ROLE, + SELECTED_ROLE + }; + + private: + friend class ::test_ChatModel; + void initFilterModel(QSortFilterProxyModel& pModel, const QString& pFilter); + void setOrderedAllRights(const QSet& pAllRights); + + private Q_SLOTS: + void onAuthenticationDataChanged(); + + public: + ChatModel(QObject* pParent = nullptr); + + void resetContext(const QSharedPointer& pContext = QSharedPointer()); + + int rowCount(const QModelIndex& = QModelIndex()) const override; + QVariant data(const QModelIndex& pIndex, int pRole = Qt::DisplayRole) const override; + bool setData(const QModelIndex& pIndex, const QVariant& pValue, int pRole) override; + QHash roleNames() const override; + + Q_INVOKABLE void transferAccessRights(); + Q_INVOKABLE QSortFilterProxyModel* getFilterOptionalModel(); + Q_INVOKABLE QSortFilterProxyModel* getFilterRequiredModel(); +}; + + +} // namespace governikus diff --git a/src/ui/qml/ConnectivityManager.cpp b/src/ui/qml/ConnectivityManager.cpp new file mode 100644 index 0000000..b5cde45 --- /dev/null +++ b/src/ui/qml/ConnectivityManager.cpp @@ -0,0 +1,127 @@ +/*! + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "ConnectivityManager.h" + +#include +#include +#include + + +using namespace governikus; + + +Q_DECLARE_LOGGING_CATEGORY(network) + + +ConnectivityManager::ConnectivityManager(QObject* pParent) + : QObject(pParent) + , mTimerId(0) + , mActive(false) +{ +} + + +ConnectivityManager::~ConnectivityManager() +{ + if (mTimerId) + { + killTimer(mTimerId); + } +} + + +void ConnectivityManager::setActive(bool pActive, const QString& pInterfaceName) +{ + if (mActive != pActive) + { + if (pActive) + { + qCDebug(network) << "Found active network interface" << pInterfaceName; + } + else + { + qCDebug(network) << "Found no active network interface"; + } + mActive = pActive; + Q_EMIT fireNetworkInterfaceActiveChanged(mActive); + } +} + + +void ConnectivityManager::updateConnectivity() +{ + const auto& allInterfaces = QNetworkInterface::allInterfaces(); + for (const auto& iface : allInterfaces) + { + if (iface.flags().testFlag(QNetworkInterface::IsUp) + && iface.flags().testFlag(QNetworkInterface::IsRunning) + && !iface.flags().testFlag(QNetworkInterface::IsLoopBack) + && !iface.addressEntries().isEmpty()) + { +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + bool hasNonLocalAddress = false; + const auto& entries = iface.addressEntries(); + for (const auto& entry : entries) + { + const QHostAddress& addr = entry.ip(); + if (addr.protocol() == QAbstractSocket::IPv4Protocol + || !addr.isLinkLocal()) + { + hasNonLocalAddress = true; + break; + } + } + + if (!hasNonLocalAddress) + { + continue; + } +#endif + + setActive(true, iface.name()); + return; + } + } + setActive(false); +} + + +void ConnectivityManager::timerEvent(QTimerEvent* pTimerEvent) +{ + if (pTimerEvent->timerId() == mTimerId) + { + updateConnectivity(); + } +} + + +bool ConnectivityManager::isNetworkInterfaceActive() const +{ + return mActive; +} + + +void ConnectivityManager::startWatching() +{ + if (mTimerId) + { + qCWarning(network) << "Already started, skip"; + return; + } + mTimerId = startTimer(1000); + updateConnectivity(); +} + + +void ConnectivityManager::stopWatching() +{ + if (!mTimerId) + { + qCWarning(network) << "Already stopped, skip"; + return; + } + killTimer(mTimerId); + mTimerId = 0; +} diff --git a/src/ui/qml/ConnectivityManager.h b/src/ui/qml/ConnectivityManager.h new file mode 100644 index 0000000..42c6bde --- /dev/null +++ b/src/ui/qml/ConnectivityManager.h @@ -0,0 +1,46 @@ +/*! + * \brief Utility class providing information about network connectivity status. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + + +#include + +class test_ConnectivityManager; + +namespace governikus +{ + +class ConnectivityManager + : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool networkInterfaceActive READ isNetworkInterfaceActive NOTIFY fireNetworkInterfaceActiveChanged) + + private: + friend class ::test_ConnectivityManager; + int mTimerId; + bool mActive; + void setActive(bool pActive, const QString& pInterfaceName = QString()); + void updateConnectivity(); + + protected: + void timerEvent(QTimerEvent* pEvent) override; + + public: + ConnectivityManager(QObject* pParent = nullptr); + virtual ~ConnectivityManager() override; + + bool isNetworkInterfaceActive() const; + void startWatching(); + void stopWatching(); + + Q_SIGNALS: + void fireNetworkInterfaceActiveChanged(bool pActive); +}; + + +} // namespace governikus diff --git a/src/ui/qml/DpiCalculator.h b/src/ui/qml/DpiCalculator.h new file mode 100644 index 0000000..a483918 --- /dev/null +++ b/src/ui/qml/DpiCalculator.h @@ -0,0 +1,31 @@ +/*! + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include + +namespace governikus +{ + +class DpiCalculator +{ + private: + DpiCalculator() + { + } + + + static qreal getDpi(); + + public: + static qreal getDpiScale() + { + return getDpi() / 160; + } + + +}; + +} // namespace governikus diff --git a/src/ui/qml/DpiCalculator_android.cpp b/src/ui/qml/DpiCalculator_android.cpp new file mode 100644 index 0000000..c61bccf --- /dev/null +++ b/src/ui/qml/DpiCalculator_android.cpp @@ -0,0 +1,31 @@ +/* + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "DpiCalculator.h" + +#include +#include + + +using namespace governikus; + + +Q_DECLARE_LOGGING_CATEGORY(qml) + + +qreal DpiCalculator::getDpi() +{ + auto qtDpi = qgetenv("QT_ANDROID_THEME_DISPLAY_DPI"); + bool ok; + qreal dpi = qtDpi.toDouble(&ok); + if (!ok) + { + qCCritical(qml) << "Cannot get screen dpi"; + return -1.0; + } + + qCInfo(qml) << "Determined dpi" << dpi; + + return dpi; +} diff --git a/src/ui/qml/DpiCalculator_generic.cpp b/src/ui/qml/DpiCalculator_generic.cpp new file mode 100644 index 0000000..b8f5ad5 --- /dev/null +++ b/src/ui/qml/DpiCalculator_generic.cpp @@ -0,0 +1,40 @@ +/* + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "DpiCalculator.h" + +#include +#include +#include + + +using namespace governikus; + + +Q_DECLARE_LOGGING_CATEGORY(qml) + + +qreal DpiCalculator::getDpi() +{ + qreal dpi = 0.0; + auto app = static_cast(QCoreApplication::instance()); + auto screen = app->primaryScreen(); + if (screen) + { + dpi = screen->logicalDotsPerInch(); + } + + qCInfo(qml) << "Determined dpi" << dpi; + +#ifndef QT_NO_DEBUG + const char* overrideDpi = "OVERRIDE_DPI"; + if (!qEnvironmentVariableIsEmpty(overrideDpi)) + { + dpi = qEnvironmentVariableIntValue(overrideDpi); + qCDebug(qml) << "Override DPI:" << dpi; + } +#endif + + return dpi; +} diff --git a/src/qml/DpiCalculator_ios.mm b/src/ui/qml/DpiCalculator_ios.mm similarity index 100% rename from src/qml/DpiCalculator_ios.mm rename to src/ui/qml/DpiCalculator_ios.mm diff --git a/src/ui/qml/HistoryModel.cpp b/src/ui/qml/HistoryModel.cpp new file mode 100644 index 0000000..d421068 --- /dev/null +++ b/src/ui/qml/HistoryModel.cpp @@ -0,0 +1,395 @@ +/*! + * \brief Model implementation for the history entries. + * + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "HistoryModel.h" + +#include "asn1/AccessRoleAndRight.h" +#include "ProviderConfiguration.h" +#include "ProviderModel.h" + +#include +#include + +using namespace governikus; + + +namespace +{ +bool matchProviderWithSubjectUrl(const ProviderConfigurationInfo& pProvider, const QString& pSubjectUrl) +{ + const QString subjectUrlHost = QUrl(pSubjectUrl).host(); + + // Check provider address host. + if (QUrl(pProvider.getAddress()).host() == subjectUrlHost) + { + return true; + } + + // Check subject urls. + for (const auto& subjectUrl : pProvider.getSubjectUrls()) + { + if (QUrl(subjectUrl).host() == subjectUrlHost) + { + return true; + } + } + + return false; +} + + +} // namespace + + +HistoryProxyModel::HistoryProxyModel() +{ +} + + +HistoryProxyModel::~HistoryProxyModel() +{ +} + + +bool HistoryProxyModel::removeRows(int pRow, int pCount, const QModelIndex& pParent) +{ + return QSortFilterProxyModel::removeRows(pRow, pCount, pParent); +} + + +bool ProviderNameFilterModel::filterAcceptsRow(int pSourceRow, const QModelIndex& /* pSourceParent */) const +{ + HistoryModel* const dataSourceModel = qobject_cast(sourceModel()); + if (dataSourceModel == nullptr) + { + return false; + } + + auto entry = mHistorySettings->getHistoryInfos()[pSourceRow]; + + return matchProviderWithSubjectUrl(mProvider, entry.getSubjectUrl()); +} + + +ProviderNameFilterModel::ProviderNameFilterModel(HistorySettings* pHistorySettings) + : mHistorySettings(pHistorySettings) +{ +} + + +ProviderNameFilterModel::~ProviderNameFilterModel() +{ +} + + +void ProviderNameFilterModel::setProviderAddress(const QString& pProviderAddress) +{ + if (mProvider.getAddress() != pProviderAddress) + { + const auto& providers = Env::getSingleton()->getProviderConfigurationInfos(); + for (const auto& provider : providers) + { + if (provider.getAddress() == pProviderAddress) + { + mProvider = provider; + + invalidateFilter(); + + return; + } + } + + // If we get here, no provider for pProviderAddress was found in the settings. + qWarning() << "Cannot select provider with address" << pProviderAddress; + } +} + + +HistoryModel::HistoryModel(HistorySettings* pHistorySettings, QObject* pParent) + : QAbstractListModel(pParent) + , mHistorySettings(pHistorySettings) + , mFilterModel() + , mNameFilterModel(pHistorySettings) +{ + updateConnections(); + mFilterModel.setSourceModel(this); + mFilterModel.setFilterCaseSensitivity(Qt::CaseInsensitive); + mNameFilterModel.setSourceModel(this); + mHistoryModelSearchFilter.setSourceModel(this); + connect(mHistorySettings.data(), &HistorySettings::fireHistoryInfosChanged, this, &HistoryModel::onHistoryEntriesChanged); + connect(mHistorySettings.data(), &HistorySettings::fireEnabledChanged, this, &HistoryModel::fireEnabledChanged); + connect(Env::getSingleton(), &ProviderConfiguration::fireUpdated, this, &HistoryModel::onProvidersChanged); +} + + +HistoryModel::~HistoryModel() +{ +} + + +void HistoryModel::updateConnections() +{ + for (const auto& connection : qAsConst(mConnections)) + { + disconnect(connection); + } + mConnections.clear(); + + const auto& historyEntries = mHistorySettings->getHistoryInfos(); + mConnections.reserve(historyEntries.size() * 2); + for (int i = 0; i < historyEntries.size(); i++) + { + const auto& provider = determineProviderFor(historyEntries.at(i)); + const QModelIndex& modelIndex = createIndex(i, 0); + + mConnections += connect(provider.getIcon().data(), &UpdatableFile::fireUpdated, this, [ = ] { + Q_EMIT dataChanged(modelIndex, modelIndex, {PROVIDER_ICON}); + }); + mConnections += connect(provider.getImage().data(), &UpdatableFile::fireUpdated, this, [ = ] { + Q_EMIT dataChanged(modelIndex, modelIndex, {PROVIDER_IMAGE}); + }); + } +} + + +void HistoryModel::onHistoryEntriesChanged() +{ + beginResetModel(); + updateConnections(); + endResetModel(); +} + + +void HistoryModel::onProvidersChanged() +{ + const static QVector PROVIDER_ROLES({PROVIDER_CATEGORY, + PROVIDER_SHORTNAME, + PROVIDER_LONGNAME, + PROVIDER_SHORTDESCRIPTION, + PROVIDER_LONGDESCRIPTION, + PROVIDER_ADDRESS, + PROVIDER_ADDRESS_DOMAIN, + PROVIDER_HOMEPAGE, + PROVIDER_HOMEPAGE_BASE, + PROVIDER_PHONE, + PROVIDER_PHONE_COST, + PROVIDER_EMAIL, + PROVIDER_POSTALADDRESS, + PROVIDER_ICON, + PROVIDER_IMAGE}); + Q_EMIT dataChanged(index(0), index(rowCount() - 1), PROVIDER_ROLES); +} + + +int HistoryModel::rowCount(const QModelIndex&) const +{ + return mHistorySettings->getHistoryInfos().size(); +} + + +QVariant HistoryModel::data(const QModelIndex& pIndex, int pRole) const +{ + if (pIndex.isValid() && pIndex.row() < rowCount()) + { + auto entry = mHistorySettings->getHistoryInfos()[pIndex.row()]; + if (pRole == Qt::DisplayRole || pRole == SUBJECT) + { + return entry.getSubjectName(); + } + if (pRole == PURPOSE) + { + return entry.getPurpose(); + } + if (pRole == DATETIME) + { + return entry.getDateTime(); + } + if (pRole == TERMSOFUSAGE) + { + return entry.getTermOfUsage(); + } + if (pRole == REQUESTEDDATA) + { + return AccessRoleAndRightsUtil::joinFromTechnicalName(entry.getRequestedData()); + } + if (pRole == PROVIDER_CATEGORY) + { + auto provider = determineProviderFor(entry); + return provider.getCategory(); + } + if (pRole == PROVIDER_SHORTNAME) + { + auto provider = determineProviderFor(entry); + return provider.getShortName().toString(); + } + if (pRole == PROVIDER_LONGNAME) + { + auto provider = determineProviderFor(entry); + return provider.getLongName().toString(); + } + if (pRole == PROVIDER_SHORTDESCRIPTION) + { + auto provider = determineProviderFor(entry); + return provider.getShortDescription().toString(); + } + if (pRole == PROVIDER_LONGDESCRIPTION) + { + auto provider = determineProviderFor(entry); + return provider.getLongDescription().toString(); + } + if (pRole == PROVIDER_ADDRESS) + { + auto provider = determineProviderFor(entry); + return provider.getAddress(); + } + if (pRole == PROVIDER_ADDRESS_DOMAIN) + { + auto provider = determineProviderFor(entry); + return provider.getAddressDomain(); + } + if (pRole == PROVIDER_HOMEPAGE) + { + auto provider = determineProviderFor(entry); + return provider.getHomepage(); + } + if (pRole == PROVIDER_HOMEPAGE_BASE) + { + auto provider = determineProviderFor(entry); + return provider.getHomepageBase(); + } + if (pRole == PROVIDER_PHONE) + { + auto provider = determineProviderFor(entry); + return provider.getPhone(); + } + if (pRole == PROVIDER_PHONE_COST) + { + const auto& provider = determineProviderFor(entry); + const auto& cost = Env::getSingleton()->getCallCost(provider); + return ProviderModel::createCostString(cost); + } + if (pRole == PROVIDER_EMAIL) + { + auto provider = determineProviderFor(entry); + return provider.getEMail(); + } + if (pRole == PROVIDER_POSTALADDRESS) + { + auto provider = determineProviderFor(entry); + return provider.getPostalAddress(); + } + if (pRole == PROVIDER_ICON) + { + auto provider = determineProviderFor(entry); + return provider.getIcon()->lookupUrl(); + } + if (pRole == PROVIDER_IMAGE) + { + auto provider = determineProviderFor(entry); + return provider.getImage()->lookupUrl(); + } + } + return QVariant(); +} + + +ProviderConfigurationInfo HistoryModel::determineProviderFor(const HistoryInfo& pHistoryInfo) const +{ + QVector matchingProviders; + const auto& providers = Env::getSingleton()->getProviderConfigurationInfos(); + for (const auto& provider : providers) + { + if (matchProviderWithSubjectUrl(provider, pHistoryInfo.getSubjectUrl())) + { + matchingProviders += provider; + } + } + if (matchingProviders.size() == 1) + { + return matchingProviders.at(0); + } + return ProviderConfigurationInfo(); +} + + +bool HistoryModel::isEnabled() const +{ + return mHistorySettings->isEnabled(); +} + + +void HistoryModel::setEnabled(bool pEnabled) +{ + if (pEnabled != isEnabled()) + { + mHistorySettings->setEnabled(pEnabled); + mHistorySettings->save(); + } +} + + +QHash HistoryModel::roleNames() const +{ + QHash roles = QAbstractListModel::roleNames(); + roles.insert(SUBJECT, "subject"); + roles.insert(PURPOSE, "purpose"); + roles.insert(DATETIME, "dateTime"); + roles.insert(TERMSOFUSAGE, "termsOfUsage"); + roles.insert(REQUESTEDDATA, "requestedData"); + roles.insert(PROVIDER_CATEGORY, "providerCategory"); + roles.insert(PROVIDER_SHORTNAME, "providerShortName"); + roles.insert(PROVIDER_LONGNAME, "providerLongName"); + roles.insert(PROVIDER_SHORTDESCRIPTION, "providerShortDescription"); + roles.insert(PROVIDER_LONGDESCRIPTION, "providerLongDescription"); + roles.insert(PROVIDER_ADDRESS, "providerAddress"); + roles.insert(PROVIDER_ADDRESS_DOMAIN, "providerAddressDomain"); + roles.insert(PROVIDER_HOMEPAGE, "providerHomepage"); + roles.insert(PROVIDER_HOMEPAGE_BASE, "providerHomepageBase"); + roles.insert(PROVIDER_PHONE, "providerPhone"); + roles.insert(PROVIDER_PHONE_COST, "providerPhoneCost"); + roles.insert(PROVIDER_EMAIL, "providerEmail"); + roles.insert(PROVIDER_POSTALADDRESS, "providerPostalAddress"); + roles.insert(PROVIDER_ICON, "providerIcon"); + roles.insert(PROVIDER_IMAGE, "providerImage"); + return roles; +} + + +bool HistoryModel::removeRows(int pRow, int pCount, const QModelIndex& pParent) +{ + beginRemoveRows(pParent, pRow, pRow + pCount - 1); + + auto entries = mHistorySettings->getHistoryInfos(); + entries.remove(pRow, pCount); + + // disconnect the signal, otherwise this model gets reset + disconnect(mHistorySettings.data(), &HistorySettings::fireHistoryInfosChanged, this, &HistoryModel::onHistoryEntriesChanged); + mHistorySettings->setHistoryInfos(entries); + connect(mHistorySettings.data(), &HistorySettings::fireHistoryInfosChanged, this, &HistoryModel::onHistoryEntriesChanged); + + mHistorySettings->save(); + + endRemoveRows(); + return true; +} + + +HistoryProxyModel* HistoryModel::getFilterModel() +{ + return &mFilterModel; +} + + +ProviderNameFilterModel* HistoryModel::getNameFilterModel() +{ + return &mNameFilterModel; +} + + +HistoryModelSearchFilter* HistoryModel::getHistoryModelSearchFilter() +{ + return &mHistoryModelSearchFilter; +} diff --git a/src/ui/qml/HistoryModel.h b/src/ui/qml/HistoryModel.h new file mode 100644 index 0000000..459a832 --- /dev/null +++ b/src/ui/qml/HistoryModel.h @@ -0,0 +1,125 @@ +/*! + * \brief Model implementation for the history entries. + * + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "HistoryModelSearchFilter.h" +#include "HistorySettings.h" +#include "ProviderConfigurationInfo.h" + +#include +#include +#include + +namespace governikus +{ + +class HistoryProxyModel + : public QSortFilterProxyModel +{ + Q_OBJECT + + public: + Q_INVOKABLE bool removeRows(int pRow, int pCount, const QModelIndex& pParent = QModelIndex()) override; + + HistoryProxyModel(); + + virtual ~HistoryProxyModel() override; +}; + + +class ProviderNameFilterModel + : public QSortFilterProxyModel +{ + Q_OBJECT + + private: + QPointer mHistorySettings; + + ProviderConfigurationInfo mProvider; + + protected: + bool filterAcceptsRow(int pSourceRow, const QModelIndex& pSourceParent) const override; + + public: + ProviderNameFilterModel(HistorySettings* pHistorySettings); + + virtual ~ProviderNameFilterModel() override; + + Q_INVOKABLE void setProviderAddress(const QString& pProviderAddress); + +}; + + +class HistoryModel + : public QAbstractListModel +{ + Q_OBJECT + Q_PROPERTY(HistoryProxyModel * filter READ getFilterModel CONSTANT) + Q_PROPERTY(ProviderNameFilterModel * nameFilter READ getNameFilterModel CONSTANT) + Q_PROPERTY(HistoryModelSearchFilter * searchFilter READ getHistoryModelSearchFilter CONSTANT) + Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY fireEnabledChanged) + + QPointer mHistorySettings; + HistoryProxyModel mFilterModel; + ProviderNameFilterModel mNameFilterModel; + HistoryModelSearchFilter mHistoryModelSearchFilter; + + private: + QVector mConnections; + + ProviderConfigurationInfo determineProviderFor(const HistoryInfo& pHistoryInfo) const; + + bool isEnabled() const; + void setEnabled(bool pEnabled); + void updateConnections(); + + private Q_SLOTS: + void onHistoryEntriesChanged(); + void onProvidersChanged(); + + Q_SIGNALS: + void fireEnabledChanged(bool pValue); + + public: + HistoryModel(HistorySettings* pHistorySettings, QObject* pParent = nullptr); + virtual ~HistoryModel() override; + + enum HistoryRoles + { + SUBJECT = Qt::UserRole + 1, + PURPOSE, + DATETIME, + TERMSOFUSAGE, + REQUESTEDDATA, + PROVIDER_CATEGORY, + PROVIDER_SHORTNAME, + PROVIDER_LONGNAME, + PROVIDER_SHORTDESCRIPTION, + PROVIDER_LONGDESCRIPTION, + PROVIDER_ADDRESS, + PROVIDER_ADDRESS_DOMAIN, + PROVIDER_HOMEPAGE, + PROVIDER_HOMEPAGE_BASE, + PROVIDER_PHONE, + PROVIDER_PHONE_COST, + PROVIDER_EMAIL, + PROVIDER_POSTALADDRESS, + PROVIDER_ICON, + PROVIDER_IMAGE + }; + + int rowCount(const QModelIndex& = QModelIndex()) const override; + QVariant data(const QModelIndex& pIndex, int pRole = Qt::DisplayRole) const override; + QHash roleNames() const override; + Q_INVOKABLE bool removeRows(int pRow, int pCount, const QModelIndex& pParent = QModelIndex()) override; + + Q_INVOKABLE HistoryProxyModel* getFilterModel(); + Q_INVOKABLE ProviderNameFilterModel* getNameFilterModel(); + HistoryModelSearchFilter* getHistoryModelSearchFilter(); +}; + +} // namespace governikus diff --git a/src/qml/HistoryModelSearchFilter.cpp b/src/ui/qml/HistoryModelSearchFilter.cpp similarity index 100% rename from src/qml/HistoryModelSearchFilter.cpp rename to src/ui/qml/HistoryModelSearchFilter.cpp diff --git a/src/ui/qml/HistoryModelSearchFilter.h b/src/ui/qml/HistoryModelSearchFilter.h new file mode 100644 index 0000000..b4bc0a5 --- /dev/null +++ b/src/ui/qml/HistoryModelSearchFilter.h @@ -0,0 +1,31 @@ +/*! + * \brief A filter to search the history model + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include +#include + +namespace governikus +{ + +class HistoryModelSearchFilter + : public QSortFilterProxyModel +{ + Q_OBJECT + + private: + QString mFilterString; + + protected: + bool filterAcceptsRow(int pSourceRow, const QModelIndex&) const override; + + public: + Q_INVOKABLE void setFilterString(const QString& pFilterString); +}; + +} // namespace governikus diff --git a/src/ui/qml/LogModel.cpp b/src/ui/qml/LogModel.cpp new file mode 100644 index 0000000..1ffff73 --- /dev/null +++ b/src/ui/qml/LogModel.cpp @@ -0,0 +1,257 @@ +/* + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "LogModel.h" + +#include "LanguageLoader.h" +#include "LogHandler.h" +#include "SingletonHelper.h" + +#include + + +using namespace governikus; + + +defineSingleton(LogModel) + + +double LogModel::getSizeRatio() const +{ + if (mLogEntries.isEmpty()) + { + return 1.0; + } + + return mCount / static_cast(mLogEntries.size()); +} + + +double LogModel::getPosition() const +{ + if (mLogEntries.isEmpty()) + { + return 0.0; + } + + return mIndex / static_cast(mLogEntries.size()); +} + + +void LogModel::setPosition(double pPosition) +{ + beginResetModel(); + + const int newIndex = qFloor(mLogEntries.size() * pPosition); + const int maxIndex = mLogEntries.size() - mCount; + mIndex = newIndex < maxIndex ? newIndex : maxIndex; + + endResetModel(); + + Q_EMIT fireVisibleAreaChanged(); +} + + +void LogModel::addLogEntry(const QString& mEntry) +{ + const int cutIndex = mEntry.indexOf(QLatin1String(" : ")); + if (cutIndex >= 0) + { + mLogEntries += mEntry.mid(0, cutIndex).trimmed(); + mLogEntries += mEntry.mid(cutIndex + 3).trimmed(); + } + else + { + mLogEntries += mEntry; + mLogEntries += QString(); + } +} + + +void LogModel::setLogEntries(QTextStream& pTextStream) +{ + beginResetModel(); + + mLogEntries.clear(); + while (!pTextStream.atEnd()) + { + addLogEntry(pTextStream.readLine()); + } + + mIndex = 0; + mCount = mLogEntries.size() < 80 ? mLogEntries.size() : 80; + + endResetModel(); + + Q_EMIT fireVisibleAreaChanged(); +} + + +void LogModel::onNewLogMsg(const QString& pMsg) +{ + if (mSelectedLogFile == 0) + { + const int oldSize = mLogEntries.size(); + const bool flickDown = (mIndex + mCount == oldSize); + addLogEntry(pMsg); + Q_EMIT fireVisibleAreaChanged(); + + if (flickDown && mAutoFlick) + { + moveView(mLogEntries.size() - oldSize); + Q_EMIT fireNewLogMsg(); + } + } +} + + +LogModel::LogModel() + : QAbstractListModel() + , mIndex(0) + , mCount(0) + , mLogFiles() + , mSelectedLogFile(0) + , mLogEntries() + , mAutoFlick(false) +{ +} + + +LogModel& LogModel::getInstance() +{ + return *Instance; +} + + +QStringList LogModel::getLogfiles() +{ + mLogFiles.clear(); + mLogFiles += QString(); + const auto& logHandler = Env::getSingleton(); + + QStringList logFileNames; + logFileNames += tr("Current log"); + const auto& logFiles = logHandler->getOtherLogfiles(); + for (const auto& entry : logFiles) + { + mLogFiles += entry.absoluteFilePath(); + logFileNames += LanguageLoader::getInstance().getUsedLocale().toString(logHandler->getFileDate(entry), tr("dd.MM.yyyy hh:mm:ss")); + } + + setLogfile(0); + return logFileNames; +} + + +void LogModel::removeOtherLogfiles() +{ + if (Env::getSingleton()->removeOtherLogfiles()) + { + Q_EMIT fireLogFilesChanged(); + } +} + + +void LogModel::removeCurrentLogfile() +{ + QFile::remove(mLogFiles[mSelectedLogFile]); + Q_EMIT fireLogFilesChanged(); +} + + +void LogModel::setLogfile(int pIndex) +{ + mSelectedLogFile = pIndex; + const auto& logHandler = Env::getSingleton(); + + if (pIndex == 0) + { + const auto& backLog = logHandler->getBacklog(); + QTextStream in(backLog); + setLogEntries(in); + connect(logHandler, &LogHandler::fireLog, this, &LogModel::onNewLogMsg); + } + else + { + disconnect(logHandler, &LogHandler::fireLog, this, &LogModel::onNewLogMsg); + QFile inputFile(mLogFiles[pIndex]); + if (inputFile.open(QIODevice::ReadOnly)) + { + QTextStream in(&inputFile); + setLogEntries(in); + inputFile.close(); + } + } +} + + +void LogModel::moveView(int pDistance) +{ + if (pDistance == 0) + { + return; + } + + int newIndex = mIndex + pDistance; + if (newIndex < 0) + { + newIndex = 0; + } + + const int maxIndex = mLogEntries.size() - mCount; + if (newIndex > maxIndex) + { + newIndex = maxIndex; + } + + if (newIndex != mIndex) + { + pDistance = newIndex - mIndex; + + if (pDistance < 0) + { + if (mCount + pDistance >= 0) + { + beginRemoveRows(QModelIndex(), mCount + pDistance, mCount - 1); + mCount += pDistance; + endRemoveRows(); + } + + beginInsertRows(QModelIndex(), 0, -pDistance - 1); + mCount -= pDistance; + mIndex += pDistance; + endInsertRows(); + } + else + { + if (mCount - pDistance >= 0) + { + beginRemoveRows(QModelIndex(), 0, pDistance - 1); + mCount -= pDistance; + mIndex += pDistance; + endRemoveRows(); + } + + beginInsertRows(QModelIndex(), mCount, mCount + pDistance - 1); + mCount += pDistance; + endInsertRows(); + } + + Q_EMIT fireVisibleAreaChanged(); + } +} + + +int LogModel::rowCount(const QModelIndex& pIndex) const +{ + Q_UNUSED(pIndex); + return mCount; +} + + +QVariant LogModel::data(const QModelIndex& pIndex, int pRole) const +{ + Q_UNUSED(pRole); + return mLogEntries.at(mIndex + pIndex.row()); +} diff --git a/src/ui/qml/LogModel.h b/src/ui/qml/LogModel.h new file mode 100644 index 0000000..95d393f --- /dev/null +++ b/src/ui/qml/LogModel.h @@ -0,0 +1,72 @@ +/* + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include +#include +#include +#include + +class test_LogModel; + +namespace governikus +{ + +class LogModel + : public QAbstractListModel +{ + Q_OBJECT + + Q_PROPERTY(bool autoFlick MEMBER mAutoFlick) + Q_PROPERTY(QStringList logFiles READ getLogfiles NOTIFY fireLogFilesChanged) + Q_PROPERTY(double heightRatio READ getSizeRatio NOTIFY fireVisibleAreaChanged) + Q_PROPERTY(double yPosition READ getPosition WRITE setPosition NOTIFY fireVisibleAreaChanged) + + private: + friend class ::test_LogModel; + int mIndex; + int mCount; + QStringList mLogFiles; + int mSelectedLogFile; + QStringList mLogEntries; + bool mAutoFlick; + + double getSizeRatio() const; + double getPosition() const; + void setPosition(double pPosition); + void addLogEntry(const QString& mEntry); + void setLogEntries(QTextStream& pTextStream); + + private Q_SLOTS: + void onNewLogMsg(const QString& pMsg); + + protected: + LogModel(); + + public: + static LogModel& getInstance(); + + QStringList getLogfiles(); + Q_INVOKABLE void removeOtherLogfiles(); + Q_INVOKABLE void removeCurrentLogfile(); + Q_INVOKABLE void setLogfile(int pIndex); + Q_INVOKABLE void moveView(int pDistance); + + Q_INVOKABLE void mailLog(const QString& pEmail = tr("support.ausweisapp2@governikus.de"), + const QString& pSubject = tr("Android log file"), + const QString& pMsg = tr("")); + Q_INVOKABLE void shareLog(); + + int rowCount(const QModelIndex& pIndex = QModelIndex()) const override; + QVariant data(const QModelIndex& pIndex, int pRole = Qt::DisplayRole) const override; + + Q_SIGNALS: + void fireVisibleAreaChanged(); + void fireLogFilesChanged(); + void fireNewLogMsg(); +}; + +} // namespace governikus diff --git a/src/ui/qml/LogModel_android.cpp b/src/ui/qml/LogModel_android.cpp new file mode 100644 index 0000000..3618189 --- /dev/null +++ b/src/ui/qml/LogModel_android.cpp @@ -0,0 +1,168 @@ +/* + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "LogModel.h" + +#include "LogHandler.h" + +#include +#include +#include +#include + + +Q_DECLARE_LOGGING_CATEGORY(qml) + + +using namespace governikus; + + +/* + * Calling + * QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation).last() + * does the same but we don't want to rely on Qt internals, so we do it on our own. + */ +static QString getPublicLogFileName(QAndroidJniEnvironment& env, const QAndroidJniObject& javaActivity, bool pExternal, const QDateTime& pDateTime = QDateTime::currentDateTime()) +{ + QAndroidJniObject jFile; + if (pExternal) + { + const auto& jEmptyString = QAndroidJniObject::fromString(QString()); + jFile = javaActivity.callObjectMethod("getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;", jEmptyString.object()); + } + else + { + jFile = javaActivity.callObjectMethod("getCacheDir", "()Ljava/io/File;"); + } + + if (env->ExceptionCheck()) + { + qCCritical(qml) << "Exception determining publicLogFileDir"; + env->ExceptionDescribe(); + env->ExceptionClear(); + return QString(); + } + if (!jFile.isValid()) + { + qCCritical(qml) << "Cannot determine publicLogFileDir"; + return QString(); + } + + const auto& jFilesDirPath = jFile.callObjectMethod("getAbsolutePath", "()Ljava/lang/String;"); + if (env->ExceptionCheck()) + { + qCCritical(qml) << "Exception determining publicLogFileDir absolute path"; + env->ExceptionDescribe(); + env->ExceptionClear(); + return QString(); + } + if (!jFilesDirPath.isValid()) + { + qCCritical(qml) << "Cannot determine publicLogFileDir absolute path"; + return QString(); + } + + const auto& nowAsISO = pDateTime.toString(QStringLiteral("yyyy-MM-dd'T'HH:mm:ss-t")); + return QStringLiteral("%1/AusweisApp2-%2.txt").arg(jFilesDirPath.toString(), nowAsISO); +} + + +void LogModel::mailLog(const QString& pEmail, const QString& pSubject, const QString& pMsg) +{ + QAndroidJniEnvironment env; + const QAndroidJniObject javaActivity(QtAndroid::androidActivity()); + if (!javaActivity.isValid()) + { + qCCritical(qml) << "Cannot determine android activity"; + return; + } + + const bool external = QAndroidJniObject::callStaticMethod("com/governikus/ausweisapp2/ShareUtil", "isNotAtLeastMarshmallow"); + + const auto& jEmail = QAndroidJniObject::fromString(pEmail); + const auto& jSubject = QAndroidJniObject::fromString(pSubject); + const auto& jMsg = QAndroidJniObject::fromString(pMsg); + const auto& jChooserTitle = QAndroidJniObject::fromString(tr("Send application log per email...")); + const auto& publicLogFile = getPublicLogFileName(env, javaActivity, external); + const auto& jPublicLogFile = QAndroidJniObject::fromString(publicLogFile); + + qCDebug(qml) << "Copy log file to" << publicLogFile; + if (!Env::getSingleton()->copy(publicLogFile)) + { + qCCritical(qml) << "Cannot copy log file to" << publicLogFile; + return; + } + + QAndroidJniObject::callStaticMethod("com/governikus/ausweisapp2/ShareUtil", + "mailLog", + "(Landroid/app/Activity;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", + javaActivity.object(), + jEmail.object(), + jSubject.object(), + jMsg.object(), + jPublicLogFile.object(), + jChooserTitle.object()); + if (env->ExceptionCheck()) + { + qCCritical(qml) << "Exception calling ShareUtil.mailLog()"; + env->ExceptionDescribe(); + env->ExceptionClear(); + } +} + + +void LogModel::shareLog() +{ + QAndroidJniEnvironment env; + const QAndroidJniObject javaActivity(QtAndroid::androidActivity()); + if (!javaActivity.isValid()) + { + qCCritical(qml) << "Cannot determine android activity"; + return; + } + + const auto& logHandler = Env::getSingleton(); + + QString publicLogFile; + if (mSelectedLogFile == 0) + { + publicLogFile = getPublicLogFileName(env, javaActivity, false); + if (!logHandler->copy(publicLogFile)) + { + qCCritical(qml) << "Cannot copy log file to" << publicLogFile; + return; + } + } + else + { + const auto& source = mLogFiles.at(mSelectedLogFile); + const auto& dateTime = logHandler->getFileDate(QFileInfo(source)); + publicLogFile = getPublicLogFileName(env, javaActivity, false, dateTime); + if (QFile::exists(publicLogFile)) + { + QFile::remove(publicLogFile); + } + if (!QFile::copy(source, publicLogFile)) + { + qCCritical(qml) << "Cannot copy log file to" << publicLogFile; + return; + } + } + + const auto& jChooserTitle = QAndroidJniObject::fromString(tr("Share application log...")); + const auto& jPublicLogFile = QAndroidJniObject::fromString(publicLogFile); + + QAndroidJniObject::callStaticMethod("com/governikus/ausweisapp2/ShareUtil", + "shareLog", + "(Landroid/app/Activity;Ljava/lang/String;Ljava/lang/String;)V", + javaActivity.object(), + jPublicLogFile.object(), + jChooserTitle.object()); + if (env->ExceptionCheck()) + { + qCCritical(qml) << "Exception calling ShareUtil.shareLog()"; + env->ExceptionDescribe(); + env->ExceptionClear(); + } +} diff --git a/src/ui/qml/LogModel_generic.cpp b/src/ui/qml/LogModel_generic.cpp new file mode 100644 index 0000000..8e6bd40 --- /dev/null +++ b/src/ui/qml/LogModel_generic.cpp @@ -0,0 +1,29 @@ +/* + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "LogModel.h" + +#include + + +Q_DECLARE_LOGGING_CATEGORY(qml) + + +using namespace governikus; + + +void LogModel::mailLog(const QString& pEmail, const QString& pSubject, const QString& pMsg) +{ + Q_UNUSED(pEmail); + Q_UNUSED(pSubject); + Q_UNUSED(pMsg); + + qCWarning(qml) << "NOT IMPLEMENTED"; +} + + +void LogModel::shareLog() +{ + qCWarning(qml) << "NOT IMPLEMENTED"; +} diff --git a/src/ui/qml/NumberModel.cpp b/src/ui/qml/NumberModel.cpp new file mode 100644 index 0000000..f92d208 --- /dev/null +++ b/src/ui/qml/NumberModel.cpp @@ -0,0 +1,281 @@ +/*! + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "NumberModel.h" + +#include "ApplicationModel.h" +#include "context/ChangePinContext.h" +#include "context/RemoteServiceContext.h" +#include "ReaderManager.h" +#include "SingletonHelper.h" + + +using namespace governikus; + + +defineSingleton(NumberModel) + + +NumberModel::NumberModel() + : QObject() + , mContext() + , mRequestTransportPin(false) +{ + const auto readerManager = Env::getSingleton(); + connect(readerManager, &ReaderManager::fireReaderPropertiesUpdated, this, &NumberModel::onReaderInfoChanged); + connect(readerManager, &ReaderManager::fireCardRetryCounterChanged, this, &NumberModel::onReaderInfoChanged); + connect(readerManager, &ReaderManager::fireReaderRemoved, this, &NumberModel::onReaderInfoChanged); + connect(readerManager, &ReaderManager::fireCardRemoved, this, &NumberModel::onReaderInfoChanged); + connect(readerManager, &ReaderManager::fireCardInserted, this, &NumberModel::onReaderInfoChanged); +} + + +NumberModel& NumberModel::getInstance() +{ + return *Instance; +} + + +void NumberModel::resetContext(const QSharedPointer& pContext) +{ + mContext = pContext; + if (mContext) + { + connect(mContext.data(), &WorkflowContext::fireCanChanged, this, &NumberModel::fireCanChanged); + connect(mContext.data(), &WorkflowContext::firePinChanged, this, &NumberModel::firePinChanged); + connect(mContext.data(), &WorkflowContext::fireCanAllowedModeChanged, this, &NumberModel::fireCanAllowedModeChanged); + + const auto changePinContext = mContext.objectCast(); + if (changePinContext) + { + connect(changePinContext.data(), &ChangePinContext::fireNewPinChanged, this, &NumberModel::fireNewPinChanged); + connect(changePinContext.data(), &ChangePinContext::firePukChanged, this, &NumberModel::firePukChanged); + } + + connect(mContext.data(), &WorkflowContext::fireCardConnectionChanged, this, &NumberModel::onCardConnectionChanged); + connect(mContext.data(), &WorkflowContext::fireReaderNameChanged, this, &NumberModel::fireReaderInfoChanged); + connect(mContext.data(), &WorkflowContext::fireLastPaceResultChanged, this, &NumberModel::fireInputErrorChanged); + + // The length of the pin doesn't matter for the core. Requesting + // a 5 or 6 digits PIN is only part of the gui. Therefore we handle + // this state in the NumberModel. The only case where the core need + // to know if the transport PIN should be requested is, when we + // are in an authentication workflow and want to start a pin change. + // Therefore we enqueue a ChangePinContext with the transport PIN + // request and consider it in this function. + const QSharedPointer context = mContext.objectCast(); + mRequestTransportPin |= (context && context->requestTransportPin()); + } + else + { + mRequestTransportPin = false; + } + + Q_EMIT fireCanChanged(); + Q_EMIT firePinChanged(); + Q_EMIT fireNewPinChanged(); + Q_EMIT firePukChanged(); + Q_EMIT fireInputErrorChanged(); + Q_EMIT fireReaderInfoChanged(); + Q_EMIT fireCanAllowedModeChanged(); + Q_EMIT fireRequestTransportPinChanged(); +} + + +PacePasswordId NumberModel::getEstablishPaceChannelType() const +{ + return mContext ? mContext->getEstablishPaceChannelType() : PacePasswordId::UNKNOWN; +} + + +QString NumberModel::getCan() const +{ + return mContext ? mContext->getCan() : QString(); +} + + +void NumberModel::setCan(const QString& pCan) +{ + if (mContext) + { + mContext->setCan(pCan); + } +} + + +QString NumberModel::getPin() const +{ + return mContext ? mContext->getPin() : QString(); +} + + +void NumberModel::setPin(const QString& pPin) +{ + if (mContext) + { + mContext->setPin(pPin); + } +} + + +QString NumberModel::getNewPin() const +{ + const auto changePinContext = mContext.objectCast(); + + return changePinContext ? changePinContext->getNewPin() : QString(); +} + + +void NumberModel::setNewPin(const QString& pNewPin) +{ + const auto changePinContext = mContext.objectCast(); + if (changePinContext) + { + changePinContext->setNewPin(pNewPin); + } + + const auto remoteServiceContext = mContext.objectCast(); + if (remoteServiceContext) + { + remoteServiceContext->setNewPin(pNewPin); + } +} + + +QString NumberModel::getPuk() const +{ + return mContext ? mContext->getPuk() : QString(); +} + + +void NumberModel::setPuk(const QString& pPuk) +{ + if (mContext) + { + mContext->setPuk(pPuk); + } +} + + +bool NumberModel::hasError() +{ + return getInputErrorCode() != CardReturnCode::OK || Env::getSingleton()->isExtendedLengthApdusUnsupported() || isPinDeactivated(); +} + + +CardReturnCode NumberModel::getInputErrorCode() const +{ + if (mContext.isNull() + || mContext->getLastPaceResult() == CardReturnCode::OK + || mContext->getLastPaceResult() == CardReturnCode::CANCELLATION_BY_USER + || mContext->getCardConnection().isNull()) + { + return CardReturnCode::OK; + } + + return mContext->getLastPaceResult(); +} + + +QString NumberModel::getInputError() const +{ + const CardReturnCode paceResult = getInputErrorCode(); + switch (paceResult) + { + case CardReturnCode::OK: + return QString(); + + case CardReturnCode::INVALID_PIN: + return tr("The given PIN is not correct. You have 2 tries to enter the correct PIN."); + + case CardReturnCode::INVALID_PIN_2: + return tr("You have entered the wrong PIN twice. " + "Prior to a third attempt, you have to enter your six-digit card access number first. " + "You can find your card access number on the front of your ID card."); + + case CardReturnCode::INVALID_PIN_3: + return tr("You have entered a wrong PIN three times. " + "Your PIN is now blocked. " + "You have to enter the PUK now for unblocking."); + + case CardReturnCode::INVALID_CAN: + return tr("You have entered a wrong CAN, please try again."); + + case CardReturnCode::INVALID_PUK: + return tr("You have entered a wrong PUK. " + "Please try again."); + + default: + return CardReturnCodeUtil::toGlobalStatus(paceResult).toErrorDescription(true); + } +} + + +void NumberModel::onCardConnectionChanged() +{ + Q_ASSERT(mContext); + if (auto cardConnection = mContext->getCardConnection()) + { + connect(cardConnection.data(), &CardConnection::fireReaderInfoChanged, this, &NumberModel::fireReaderInfoChanged); + } + Q_EMIT fireReaderInfoChanged(); +} + + +int NumberModel::getRetryCounter() const +{ + if (mContext.isNull() || mContext->getCardConnection().isNull()) + { + return -1; + } + else + { + return mContext->getCardConnection()->getReaderInfo().getRetryCounter(); + } +} + + +bool NumberModel::isPinDeactivated() const +{ + if (mContext && !mContext->getReaderName().isEmpty()) + { + return Env::getSingleton()->getReaderInfo(mContext->getReaderName()).isPinDeactivated(); + } + return false; +} + + +bool NumberModel::isCanAllowedMode() +{ + if (mContext) + { + return mContext->isCanAllowedMode(); + } + return false; +} + + +void NumberModel::setRequestTransportPin(bool pEnabled) +{ + if (mRequestTransportPin != pEnabled) + { + mRequestTransportPin = pEnabled; + Q_EMIT fireRequestTransportPinChanged(); + } +} + + +bool NumberModel::isRequestTransportPin() const +{ + return mRequestTransportPin; +} + + +void NumberModel::onReaderInfoChanged(const QString& pReaderName) +{ + if (mContext && pReaderName == mContext->getReaderName()) + { + Q_EMIT fireReaderInfoChanged(); + } +} diff --git a/src/ui/qml/NumberModel.h b/src/ui/qml/NumberModel.h new file mode 100644 index 0000000..3e849c2 --- /dev/null +++ b/src/ui/qml/NumberModel.h @@ -0,0 +1,96 @@ +/*! + * \brief Model for accessing PIN, CAN, PUK, according to the + * currently active workflow. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/WorkflowContext.h" +#include "Env.h" + +#include +#include + +class test_NumberModel; + +namespace governikus +{ + +class NumberModel + : public QObject +{ + Q_OBJECT + friend class Env; + friend class ::test_NumberModel; + + Q_PROPERTY(governikus::PacePasswordId establishPaceChannelType READ getEstablishPaceChannelType) + Q_PROPERTY(QString can READ getCan WRITE setCan NOTIFY fireCanChanged) + Q_PROPERTY(QString pin READ getPin WRITE setPin NOTIFY firePinChanged) + Q_PROPERTY(QString newPin READ getNewPin WRITE setNewPin NOTIFY fireNewPinChanged) + Q_PROPERTY(QString puk READ getPuk WRITE setPuk NOTIFY firePukChanged) + Q_PROPERTY(bool hasError READ hasError NOTIFY fireReaderInfoChanged) + Q_PROPERTY(CardReturnCode inputErrorCode READ getInputErrorCode NOTIFY fireInputErrorChanged) + Q_PROPERTY(QString inputError READ getInputError NOTIFY fireInputErrorChanged) + Q_PROPERTY(int retryCounter READ getRetryCounter NOTIFY fireReaderInfoChanged) + Q_PROPERTY(bool pinDeactivated READ isPinDeactivated NOTIFY fireReaderInfoChanged) + Q_PROPERTY(bool isCanAllowedMode READ isCanAllowedMode NOTIFY fireCanAllowedModeChanged) + Q_PROPERTY(bool requestTransportPin READ isRequestTransportPin WRITE setRequestTransportPin NOTIFY fireRequestTransportPinChanged) + + private: + QSharedPointer mContext; + bool mRequestTransportPin; + + private Q_SLOTS: + void onCardConnectionChanged(); + + protected: + NumberModel(); + ~NumberModel() override = default; + static NumberModel& getInstance(); + + public: + void resetContext(const QSharedPointer& pContext = QSharedPointer()); + + PacePasswordId getEstablishPaceChannelType() const; + + QString getCan() const; + void setCan(const QString& pCan); + + QString getPin() const; + void setPin(const QString& pPin); + + QString getNewPin() const; + void setNewPin(const QString& pNewPin); + + QString getPuk() const; + void setPuk(const QString& pPuk); + + bool hasError(); + CardReturnCode getInputErrorCode() const; + QString getInputError() const; + + int getRetryCounter() const; + bool isPinDeactivated() const; + bool isCanAllowedMode(); + + bool isRequestTransportPin() const; + void setRequestTransportPin(bool pEnabled); + + private Q_SLOTS: + void onReaderInfoChanged(const QString& pReaderName); + + Q_SIGNALS: + void fireCanChanged(); + void firePinChanged(); + void fireNewPinChanged(); + void firePukChanged(); + void fireInputErrorChanged(); + void fireReaderInfoChanged(); + void fireCanAllowedModeChanged(); + void fireRequestTransportPinChanged(); +}; + + +} // namespace governikus diff --git a/src/ui/qml/ProviderCategoryFilterModel.cpp b/src/ui/qml/ProviderCategoryFilterModel.cpp new file mode 100644 index 0000000..494dd3d --- /dev/null +++ b/src/ui/qml/ProviderCategoryFilterModel.cpp @@ -0,0 +1,177 @@ +/*! + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "ProviderCategoryFilterModel.h" + + +using namespace governikus; + +namespace +{ +const QStringList& getCategories() +{ + static QStringList cats({QStringLiteral("citizen"), QStringLiteral("insurance"), QStringLiteral("finance"), QStringLiteral("other")}); + return cats; +} + + +} // namespace + + +QString ProviderCategoryFilterModel::getSearchString() const +{ + return mSearchString; +} + + +void ProviderCategoryFilterModel::updateSearchString(const QString& pSearchString) +{ + const QString& newSearchString = pSearchString.trimmed(); + if (mSearchString != newSearchString) + { + mSearchString = newSearchString; + invalidateFilter(); + Q_EMIT fireCriteriaChanged(); + } +} + + +QStringList ProviderCategoryFilterModel::getSelectedCategories() const +{ + return mSelectedCategories.toList(); +} + + +int ProviderCategoryFilterModel::getAdditionalResultCount() const +{ + int results = 0; + for (const QString& p : getCategories()) + { + results += matchesForExcludedCategory(p); + } + return results; +} + + +int ProviderCategoryFilterModel::matchesForExcludedCategory(const QString& pCategory) const +{ + if (mSearchString.isEmpty() || mSelectedCategories.isEmpty() || mSelectedCategories.contains(pCategory)) + { + return 0; + } + + QAbstractItemModel* const model = sourceModel(); + const int count = model->rowCount(); + int matchCount = 0; + for (int sourceRow = 0; sourceRow < count; ++sourceRow) + { + const QModelIndex idx = model->index(sourceRow, 0, QModelIndex()); + const QString dt = model->data(idx, Qt::DisplayRole).toString(); + if (!dt.contains(mSearchString, Qt::CaseInsensitive)) + { + continue; + } + + if (pCategory.toLower() == model->data(idx, ProviderModel::CATEGORY).toString().toLower()) + { + matchCount++; + } + } + + return matchCount; +} + + +bool ProviderCategoryFilterModel::filterAcceptsRow(int pSourceRow, const QModelIndex& pSourceParent) const +{ + QAbstractItemModel* const model = sourceModel(); + Q_ASSERT(model != nullptr); + const QModelIndex idx = model->index(pSourceRow, 0, pSourceParent); + + if (!mSearchString.isEmpty()) + { + const QString dt = model->data(idx, Qt::DisplayRole).toString(); + if (!dt.contains(mSearchString, Qt::CaseInsensitive)) + { + return false; + } + } + + return mSelectedCategories.isEmpty() || mSelectedCategories.contains(QStringLiteral("all")) || + mSelectedCategories.contains(model->data(idx, ProviderModel::CATEGORY).toString().toLower()); +} + + +ProviderCategoryFilterModel::ProviderCategoryFilterModel() : + mProviderModel() +{ + QSortFilterProxyModel::setSourceModel(&mProviderModel); + + QSortFilterProxyModel::sort(0); + sortByCategoryFirst(false); + setSortCaseSensitivity(Qt::CaseInsensitive); +} + + +ProviderCategoryFilterModel::~ProviderCategoryFilterModel() +{ +} + + +void ProviderCategoryFilterModel::sortByCategoryFirst(bool pEnabled) +{ + setSortRole(pEnabled ? ProviderModel::SORT_ROLE : ProviderModel::SHORTNAME); +} + + +void ProviderCategoryFilterModel::setCategorySelection(const QString& pCategory) +{ + mSelectedCategories.clear(); + + if (!pCategory.isEmpty()) + { + mSelectedCategories.insert(pCategory.toLower()); + } + invalidateFilter(); + Q_EMIT fireCriteriaChanged(); +} + + +void ProviderCategoryFilterModel::updateCategorySelection(const QString& pCategory, bool pSelected) +{ + const int categoryCount = mSelectedCategories.count(); + if (pSelected) + { + mSelectedCategories.insert(pCategory.toLower()); + } + else + { + mSelectedCategories.remove(pCategory.toLower()); + } + + if (mSelectedCategories.count() != categoryCount) + { + invalidateFilter(); + Q_EMIT fireCriteriaChanged(); + } +} + + +void ProviderCategoryFilterModel::addAdditionalResultCategories() +{ + bool needUpdate = false; + for (const QString& p : getCategories()) + { + if (matchesForExcludedCategory(p) > 0) + { + needUpdate = true; + mSelectedCategories += p; + } + } + if (needUpdate) + { + invalidateFilter(); + Q_EMIT fireCriteriaChanged(); + } +} diff --git a/src/ui/qml/ProviderCategoryFilterModel.h b/src/ui/qml/ProviderCategoryFilterModel.h new file mode 100644 index 0000000..bd151b2 --- /dev/null +++ b/src/ui/qml/ProviderCategoryFilterModel.h @@ -0,0 +1,60 @@ +/*! + * \brief Model implementation for the providers. + * + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "ProviderModel.h" + +#include +#include +#include +#include + +class test_ProviderCategoryFilterModel; + +namespace governikus +{ + +class ProviderCategoryFilterModel + : public QSortFilterProxyModel +{ + Q_OBJECT + Q_PROPERTY(QString searchString READ getSearchString WRITE updateSearchString NOTIFY fireCriteriaChanged) + Q_PROPERTY(QStringList categories READ getSelectedCategories NOTIFY fireCriteriaChanged) + Q_PROPERTY(int rowCount READ rowCount NOTIFY fireCriteriaChanged) + Q_PROPERTY(int additionalResultCount READ getAdditionalResultCount NOTIFY fireCriteriaChanged) + + private: + friend class ::test_ProviderCategoryFilterModel; + QString mSearchString; + QSet mSelectedCategories; + + ProviderModel mProviderModel; + + QString getSearchString() const; + void updateSearchString(const QString& pSearchString); + QStringList getSelectedCategories() const; + int getAdditionalResultCount() const; + int matchesForExcludedCategory(const QString& pCategory) const; + + protected: + bool filterAcceptsRow(int pSourceRow, const QModelIndex& pSourceParent) const override; + + public: + ProviderCategoryFilterModel(); + virtual ~ProviderCategoryFilterModel() override; + + Q_INVOKABLE void sortByCategoryFirst(bool pEnabled); + Q_INVOKABLE void setCategorySelection(const QString& pCategory); + Q_INVOKABLE void updateCategorySelection(const QString& pCategory, bool pSelected); + Q_INVOKABLE void addAdditionalResultCategories(); + + Q_SIGNALS: + void fireCriteriaChanged(); +}; + + +} // namespace governikus diff --git a/src/ui/qml/ProviderModel.cpp b/src/ui/qml/ProviderModel.cpp new file mode 100644 index 0000000..4291342 --- /dev/null +++ b/src/ui/qml/ProviderModel.cpp @@ -0,0 +1,211 @@ +/*! + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "ProviderModel.h" + +#include "ProviderConfiguration.h" + + +using namespace governikus; + + +QString ProviderModel::createCostString(double pCostsPerMinute, double pCostsPerCall) +{ + if (pCostsPerMinute > 0.0) + { + return tr("%1/min").arg(createAmountString(pCostsPerMinute)); + } + if (pCostsPerCall > 0.0) + { + return tr("%1/call").arg(createAmountString(pCostsPerCall)); + } + return QString(); +} + + +QString ProviderModel::createAmountString(double pCents) +{ + return pCents > 100 ? tr("%1 EUR").arg(pCents / 100.0) : tr("%1 ct").arg(pCents); +} + + +void ProviderModel::updateConnections() +{ + for (const auto& connection : qAsConst(mConnections)) + { + disconnect(connection); + } + mConnections.clear(); + + const auto& providerConfigurationInfos = Env::getSingleton()->getProviderConfigurationInfos(); + mConnections.reserve(providerConfigurationInfos.size() * 2); + for (int i = 0; i < providerConfigurationInfos.size(); i++) + { + const auto& provider = providerConfigurationInfos.at(i); + const QModelIndex& modelIndex = createIndex(i, 0); + + mConnections += connect(provider.getIcon().data(), &UpdatableFile::fireUpdated, this, [ = ] { + Q_EMIT dataChanged(modelIndex, modelIndex, {ProviderRoles::ICON}); + }); + mConnections += connect(provider.getImage().data(), &UpdatableFile::fireUpdated, this, [ = ] { + Q_EMIT dataChanged(modelIndex, modelIndex, {ProviderRoles::IMAGE}); + }); + } +} + + +void ProviderModel::onProvidersChanged() +{ + beginResetModel(); + updateConnections(); + endResetModel(); +} + + +ProviderModel::ProviderModel(QObject* pParent) + : QAbstractListModel(pParent) +{ + updateConnections(); + connect(Env::getSingleton(), &ProviderConfiguration::fireUpdated, this, &ProviderModel::onProvidersChanged); +} + + +ProviderModel::~ProviderModel() +{ +} + + +int ProviderModel::rowCount(const QModelIndex&) const +{ + return Env::getSingleton()->getProviderConfigurationInfos().size(); +} + + +QVariant ProviderModel::data(const QModelIndex& pIndex, int pRole) const +{ + const auto& providerConfiguration = Env::getSingleton(); + + if (pIndex.isValid()) + { + auto provider = providerConfiguration->getProviderConfigurationInfos().at(pIndex.row()); + + if (pRole == Qt::DisplayRole) + { + auto longName = provider.getLongName(); + return longName.isEmpty() ? provider.getShortName().toString() : longName.toString(); + } + + if (pRole == CATEGORY) + { + return provider.getCategory(); + } + if (pRole == SHORTNAME) + { + return provider.getShortName().toString(); + } + if (pRole == LONGNAME) + { + return provider.getLongName().toString(); + } + if (pRole == SHORTDESCRIPTION) + { + return provider.getShortDescription().toString(); + } + if (pRole == LONGDESCRIPTION) + { + return provider.getLongDescription().toString(); + } + if (pRole == ADDRESS) + { + return provider.getAddress(); + } + if (pRole == ADDRESS_DOMAIN) + { + return provider.getAddressDomain(); + } + if (pRole == HOMEPAGE) + { + return provider.getHomepage(); + } + if (pRole == HOMEPAGE_BASE) + { + return provider.getHomepageBase(); + } + if (pRole == PHONE) + { + return provider.getPhone(); + } + if (pRole == PHONE_COST) + { + const auto& cost = providerConfiguration->getCallCost(provider); + return createCostString(cost); + } + if (pRole == EMAIL) + { + return provider.getEMail(); + } + if (pRole == POSTALADDRESS) + { + return provider.getPostalAddress(); + } + if (pRole == ICON) + { + return provider.getIcon()->lookupUrl(); + } + if (pRole == IMAGE) + { + return provider.getImage()->lookupUrl(); + } + if (pRole == SORT_ROLE) + { + auto value = provider.getLongName(); + + return provider.getCategory() + (value.isEmpty() ? provider.getShortName() : value); + } + } + + return QVariant(); +} + + +QHash ProviderModel::roleNames() const +{ + QHash roles = QAbstractListModel::roleNames(); + roles.insert(CATEGORY, "providerCategory"); + roles.insert(SHORTNAME, "providerShortName"); + roles.insert(LONGNAME, "providerLongName"); + roles.insert(SHORTDESCRIPTION, "providerShortDescription"); + roles.insert(LONGDESCRIPTION, "providerLongDescription"); + roles.insert(ADDRESS, "providerAddress"); + roles.insert(ADDRESS_DOMAIN, "providerAddressDomain"); + roles.insert(HOMEPAGE, "providerHomepage"); + roles.insert(HOMEPAGE_BASE, "providerHomepageBase"); + roles.insert(PHONE, "providerPhone"); + roles.insert(PHONE_COST, "providerPhoneCost"); + roles.insert(EMAIL, "providerEmail"); + roles.insert(POSTALADDRESS, "providerPostalAddress"); + roles.insert(ICON, "providerIcon"); + roles.insert(IMAGE, "providerImage"); + + return roles; +} + + +QString ProviderModel::createCostString(const CallCost& pCosts) +{ + if (pCosts.isNull()) + { + return QString(); + } + + QString msg; + if (pCosts.getFreeSeconds() > 0) + { + msg += tr("%1 seconds free, afterwards ").arg(pCosts.getFreeSeconds()); + } + msg += tr("landline costs %1; ").arg(createCostString(pCosts.getLandlineCentsPerMinute(), pCosts.getLandlineCentsPerCall())); + const auto mobileCosts = createCostString(pCosts.getMobileCentsPerMinute(), pCosts.getMobileCentsPerCall()); + msg += mobileCosts.isEmpty() ? tr("mobile costs may vary.") : tr("mobile costs %1").arg(mobileCosts); + return msg; +} diff --git a/src/ui/qml/ProviderModel.h b/src/ui/qml/ProviderModel.h new file mode 100644 index 0000000..f6466b9 --- /dev/null +++ b/src/ui/qml/ProviderModel.h @@ -0,0 +1,71 @@ +/*! + * \brief Model implementation for the providers. + * + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "CallCost.h" + +#include +#include + + +class test_ProviderModel; + + +namespace governikus +{ + +class ProviderModel + : public QAbstractListModel +{ + friend class ::test_ProviderModel; + + Q_OBJECT + + static QString createCostString(double pCostsPerMinute, double pCostsPerCall); + static QString createAmountString(double pCents); + + private: + QVector mConnections; + + void updateConnections(); + + private Q_SLOTS: + void onProvidersChanged(); + + public: + enum ProviderRoles + { + CATEGORY = Qt::UserRole + 1, + SHORTNAME, + LONGNAME, + SHORTDESCRIPTION, + LONGDESCRIPTION, + ADDRESS, + ADDRESS_DOMAIN, + HOMEPAGE, + HOMEPAGE_BASE, + PHONE, + PHONE_COST, + EMAIL, + POSTALADDRESS, + ICON, + IMAGE, + SORT_ROLE + }; + + ProviderModel(QObject* pParent = nullptr); + virtual ~ProviderModel() override; + + int rowCount(const QModelIndex&) const override; + QVariant data(const QModelIndex& pIndex, int pRole = Qt::DisplayRole) const override; + QHash roleNames() const override; + + static QString createCostString(const CallCost& pCosts); +}; + + +} // namespace governikus diff --git a/src/ui/qml/QmlExtension.h b/src/ui/qml/QmlExtension.h new file mode 100644 index 0000000..20d0ba4 --- /dev/null +++ b/src/ui/qml/QmlExtension.h @@ -0,0 +1,28 @@ +/*! + * \brief Utility for sharing text. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include + +namespace governikus +{ + +class QmlExtension + : public QObject +{ + Q_OBJECT + + public: + Q_INVOKABLE void showSettings(const QString& pAction); + Q_INVOKABLE void showFeedback(const QString& pMessage); + Q_INVOKABLE bool exportHistory(const QString& pPdfUrl) const; + Q_INVOKABLE void keepScreenOn(bool pActive); + Q_INVOKABLE void openOnlineHelp(const QString& pHelpSectionName); +}; + +} // namespace governikus diff --git a/src/ui/qml/QmlExtension_android.cpp b/src/ui/qml/QmlExtension_android.cpp new file mode 100644 index 0000000..522c5e1 --- /dev/null +++ b/src/ui/qml/QmlExtension_android.cpp @@ -0,0 +1,107 @@ +/*! + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "QmlExtension.h" + +#include "LogHandler.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(qml) + + +using namespace governikus; + +void QmlExtension::showSettings(const QString& pAction) +{ + QAndroidJniEnvironment env; + + const QAndroidJniObject& jAction = QAndroidJniObject::fromString(pAction); + QAndroidJniObject intent("android/content/Intent", "(Ljava/lang/String;)V", jAction.object()); + const jint flag = QAndroidJniObject::getStaticField("android/content/Intent", "FLAG_ACTIVITY_NEW_TASK"); + intent.callObjectMethod("setFlags", "(I)V", flag); + + if (intent.isValid()) + { + qCCritical(qml) << "Call action:" << pAction; + QtAndroid::startActivity(intent, 0); + } + + if (env->ExceptionCheck()) + { + qCCritical(qml) << "Cannot call an action as activity:" << pAction; + env->ExceptionDescribe(); + env->ExceptionClear(); + } +} + + +void QmlExtension::showFeedback(const QString& pMessage) +{ + // Wait for toast activation synchronously so that the app can not be deactivated + // in the meantime and all used Java objects are still alive when accessed. + QtAndroid::runOnAndroidThreadSync([pMessage](){ + QAndroidJniEnvironment env; + + const QAndroidJniObject& jMessage = QAndroidJniObject::fromString(pMessage); + const QAndroidJniObject& toast = QAndroidJniObject::callStaticObjectMethod( + "android/widget/Toast", + "makeText", + "(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;", + QtAndroid::androidActivity().object(), + jMessage.object(), + jint(1)); + toast.callMethod("show"); + + if (env->ExceptionCheck()) + { + qCCritical(qml) << "Suppressing an unexpected exception."; + env->ExceptionDescribe(); + env->ExceptionClear(); + // The toast was probably not displayed (e.g. DeadObjectException). We halt on error + // since it is used to display information to the user as required by the TR. + Q_ASSERT(false); + } + }); +} + + +bool QmlExtension::exportHistory(const QString&) const +{ + qCWarning(qml) << "NOT IMPLEMENTED"; + return false; +} + + +void QmlExtension::keepScreenOn(bool pActive) +{ + QtAndroid::runOnAndroidThread([pActive](){ + QtAndroid::androidActivity().callMethod("keepScreenOn", "(Z)V", pActive); + QAndroidJniEnvironment env; + if (env->ExceptionCheck()) + { + qCCritical(qml) << "Exception calling java native function."; + env->ExceptionDescribe(); + env->ExceptionClear(); + } + }); +} + + +void QmlExtension::openOnlineHelp(const QString& pHelpSectionName) +{ + Q_UNUSED(pHelpSectionName); + qCWarning(qml) << "NOT IMPLEMENTED"; +} + + +#include "moc_QmlExtension.cpp" diff --git a/src/ui/qml/QmlExtension_generic.cpp b/src/ui/qml/QmlExtension_generic.cpp new file mode 100644 index 0000000..3c395f2 --- /dev/null +++ b/src/ui/qml/QmlExtension_generic.cpp @@ -0,0 +1,58 @@ +/*! + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "QmlExtension.h" + +#ifndef Q_OS_WINRT +#include "PdfExporter.h" +#endif + +#include "HelpAction.h" + +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(qml) + +using namespace governikus; + + +void QmlExtension::showSettings(const QString&) +{ + qCWarning(qml) << "NOT IMPLEMENTED"; +} + + +void QmlExtension::showFeedback(const QString&) +{ + qCWarning(qml) << "NOT IMPLEMENTED"; +} + + +bool QmlExtension::exportHistory(const QString& pPdfUrl) const +{ +#ifdef Q_OS_WINRT + return false; + +#else + PdfExporter exporter(QUrl(pPdfUrl).toLocalFile()); + return exporter.exportHistory(); + +#endif +} + + +void QmlExtension::keepScreenOn(bool) +{ + qCWarning(qml) << "NOT IMPLEMENTED"; +} + + +void QmlExtension::openOnlineHelp(const QString& pHelpSectionName) +{ + HelpAction::openContextHelp(pHelpSectionName); +} + + +#include "moc_QmlExtension.cpp" diff --git a/src/ui/qml/QmlExtension_ios.mm b/src/ui/qml/QmlExtension_ios.mm new file mode 100644 index 0000000..e9fac3c --- /dev/null +++ b/src/ui/qml/QmlExtension_ios.mm @@ -0,0 +1,65 @@ +/*! + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "QmlExtension.h" + +#include +#import + +Q_DECLARE_LOGGING_CATEGORY(qml) + +using namespace governikus; + + +void QmlExtension::showSettings(const QString&) +{ + qCWarning(qml) << "NOT IMPLEMENTED"; +} + + +void QmlExtension::showFeedback(const QString& pMessage) +{ + NSString* msg = pMessage.toNSString(); + + UIAlertController* alert = [UIAlertController + alertControllerWithTitle:msg + message:@"" + preferredStyle:UIAlertControllerStyleAlert]; + + UIViewController* rootController = [[UIApplication sharedApplication].keyWindow rootViewController]; + [rootController presentViewController:alert animated:YES completion:nil]; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, static_cast(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [alert dismissViewControllerAnimated:YES completion:nil]; + }); +} + + +bool QmlExtension::exportHistory(const QString&) const +{ + qCWarning(qml) << "NOT IMPLEMENTED"; + return false; +} + + +void QmlExtension::keepScreenOn(bool pActive) +{ + if (pActive) + { + [[UIApplication sharedApplication]setIdleTimerDisabled:YES]; + } + else + { + [[UIApplication sharedApplication]setIdleTimerDisabled:NO]; + } +} + + +void QmlExtension::openOnlineHelp(const QString& pHelpSectionName) +{ + Q_UNUSED(pHelpSectionName); + qCWarning(qml) << "NOT IMPLEMENTED"; +} + + +#include "moc_QmlExtension.cpp" diff --git a/src/ui/qml/RemoteServiceModel.cpp b/src/ui/qml/RemoteServiceModel.cpp new file mode 100644 index 0000000..330a474 --- /dev/null +++ b/src/ui/qml/RemoteServiceModel.cpp @@ -0,0 +1,345 @@ +/*! + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "RemoteServiceModel.h" + +#include "AppSettings.h" +#include "Env.h" +#include "EstablishPaceChannelParser.h" +#include "RemoteClientImpl.h" +#include "RemoteServiceSettings.h" +#include "SingletonHelper.h" + +using namespace governikus; + +defineSingleton(RemoteServiceModel) + + +RemoteServiceModel::RemoteServiceModel() + : WorkflowModel() + , mContext() + , mWifiInfo() + , mRunnable(false) + , mCanEnableNfc(false) + , mErrorMessage() + , mPsk() + , mAvailableRemoteDevices(this, false, true) + , mKnownDevices(this, true, false) + , mConnectedClientDeviceName() + , mConnectedServerDeviceNames() + , mIsSaCPinChangeWorkflow() +{ + const auto readerManager = Env::getSingleton(); + connect(readerManager, &ReaderManager::firePluginAdded, this, &RemoteServiceModel::onEnvironmentChanged); + connect(readerManager, &ReaderManager::fireStatusChanged, this, &RemoteServiceModel::onEnvironmentChanged); + connect(readerManager, &ReaderManager::fireReaderAdded, this, &RemoteServiceModel::onEnvironmentChanged); + connect(readerManager, &ReaderManager::fireReaderRemoved, this, &RemoteServiceModel::onEnvironmentChanged); + connect(&mWifiInfo, &WifiInfo::fireWifiEnabledChanged, this, &RemoteServiceModel::onEnvironmentChanged); + + RemoteClient* const remoteClient = Env::getSingleton(); + connect(remoteClient, &RemoteClient::fireDetectionChanged, this, &RemoteServiceModel::fireDetectionChanged); + connect(remoteClient, &RemoteClient::fireNewRemoteDispatcher, this, &RemoteServiceModel::onConnectedDevicesChanged); + connect(remoteClient, &RemoteClient::fireDispatcherDestroyed, this, &RemoteServiceModel::onConnectedDevicesChanged); + onEnvironmentChanged(); +} + + +void RemoteServiceModel::onEnvironmentChanged() +{ + bool nfcPluginAvailable = false; + bool nfcPluginEnabled = false; + const auto& allPlugins = Env::getSingleton()->getPlugInInfos(); + for (const auto& pluginInfo : allPlugins) + { + if (pluginInfo.getPlugInType() != ReaderManagerPlugInType::NFC) + { + // At this time no bluetooth basic reader available so we can skip + continue; + } + + nfcPluginAvailable |= pluginInfo.isAvailable(); + nfcPluginEnabled |= pluginInfo.isEnabled(); + } + + bool readerAvailable = (Env::getSingleton()->getReaderInfos().length() > 0); + const bool wifiEnabled = mWifiInfo.isWifiEnabled(); + + const bool runnable = readerAvailable && wifiEnabled; + const bool canEnableNfc = nfcPluginAvailable && !nfcPluginEnabled; + const QString errorMessage = getErrorMessage(nfcPluginAvailable, nfcPluginEnabled, wifiEnabled); + if (mRunnable != runnable || mCanEnableNfc != canEnableNfc || mErrorMessage != errorMessage) + { + mRunnable = runnable; + mCanEnableNfc = canEnableNfc; + mErrorMessage = errorMessage; + + Q_EMIT fireEnvironmentChanged(); + } + + if (!runnable && isRunning()) + { + setRunning(false); + } +} + + +bool RemoteServiceModel::isRunning() const +{ + return mContext ? mContext->isRunning() : false; +} + + +void RemoteServiceModel::setRunning(bool pState) +{ + if (isRunning() == pState) + { + return; + } + + if (isRunning() && mContext) + { + Q_EMIT mContext->fireCancelWorkflow(); + } + else + { + Q_EMIT fireStartWorkflow(); + } + + Q_EMIT fireIsRunningChanged(); +} + + +RemoteDeviceModel* RemoteServiceModel::getAvailableRemoteDevices() +{ + return &mAvailableRemoteDevices; +} + + +RemoteDeviceModel* RemoteServiceModel::getKnownDevices() +{ + return &mKnownDevices; +} + + +void RemoteServiceModel::setDetectRemoteDevices(bool pNewStatus) +{ + if (pNewStatus) + { + mAvailableRemoteDevices.onWidgetShown(); + mKnownDevices.onWidgetShown(); + } + else + { + mAvailableRemoteDevices.onWidgetHidden(); + mKnownDevices.onWidgetHidden(); + } +} + + +bool RemoteServiceModel::detectRemoteDevices() +{ + return Env::getSingleton()->isDetecting(); +} + + +void RemoteServiceModel::connectToServer(const QString& pDeviceId, const QString& pServerPsk) +{ + if (!pServerPsk.isEmpty()) + { + RemoteClient* const remoteClient = Env::getSingleton(); + connect(remoteClient, &RemoteClient::fireEstablishConnectionDone, this, &RemoteServiceModel::onEstablishConnectionDone); + + remoteClient->establishConnection(mAvailableRemoteDevices.getRemoteDeviceListEntry(pDeviceId), pServerPsk); + } +} + + +void RemoteServiceModel::onEstablishConnectionDone(const QSharedPointer& pEntry, const GlobalStatus& pStatus) +{ + Q_UNUSED(pEntry); + RemoteClient* const remoteClient = Env::getSingleton(); + disconnect(remoteClient, &RemoteClient::fireEstablishConnectionDone, this, &RemoteServiceModel::onEstablishConnectionDone); + if (pStatus.isError()) + { + Q_EMIT firePairingFailed(); + } +} + + +void RemoteServiceModel::onClientConnectedChanged(bool pConnected) +{ + const RemoteServiceSettings& settings = Env::getSingleton()->getRemoteServiceSettings(); + const QString peerName = settings.getRemoteInfo(getCurrentFingerprint()).getName(); + mConnectedClientDeviceName = peerName; + Q_EMIT fireConnectedClientDeviceNameChanged(); + Q_EMIT fireConnectedChanged(pConnected); +} + + +void RemoteServiceModel::resetContext(const QSharedPointer& pContext) +{ + mContext = pContext; + WorkflowModel::resetContext(pContext); + + mPsk.clear(); + onEstablishPaceChannelMessageUpdated(QSharedPointer()); + + mContext = pContext; + if (mContext) + { + connect(mContext.data(), &WorkflowContext::fireStateChanged, this, &RemoteServiceModel::fireIsRunningChanged); + connect(mContext->getRemoteServer().data(), &RemoteServer::firePskChanged, this, [this](const QByteArray& pPsk){ + mPsk = pPsk; + }); + connect(mContext->getRemoteServer().data(), &RemoteServer::firePskChanged, this, &RemoteServiceModel::firePskChanged); + connect(mContext->getRemoteServer().data(), &RemoteServer::fireConnectedChanged, this, &RemoteServiceModel::onClientConnectedChanged); + connect(mContext.data(), &RemoteServiceContext::fireEstablishPaceChannelMessageUpdated, this, &RemoteServiceModel::onEstablishPaceChannelMessageUpdated); + } + + Q_EMIT fireConnectedChanged(isConnected()); +} + + +void RemoteServiceModel::setPairing(bool pEnabled) +{ + if (mContext) + { + mContext->getRemoteServer()->setPairing(pEnabled); + } +} + + +QString RemoteServiceModel::getCurrentFingerprint() const +{ + if (mContext && mContext->getRemoteServer()->isConnected()) + { + return RemoteServiceSettings::generateFingerprint(mContext->getRemoteServer()->getCurrentCertificate()); + } + + return QString(); +} + + +bool RemoteServiceModel::isConnected() const +{ + if (mContext) + { + return mContext->getRemoteServer()->isConnected(); + } + + return false; +} + + +bool RemoteServiceModel::isSaCPinChangeWorkflow() const +{ + return mIsSaCPinChangeWorkflow; +} + + +bool RemoteServiceModel::pinPadModeOn() +{ + return Env::getSingleton()->getRemoteServiceSettings().getPinPadMode(); +} + + +QString RemoteServiceModel::getPacePasswordId() const +{ + if (mContext.isNull()) + { + return QString(); + } + + const QSharedPointer establishPaceChannelMessage = mContext->getEstablishPaceChannelMessage(); + if (establishPaceChannelMessage->isIncomplete()) + { + return QString(); + } + + const EstablishPaceChannelParser parser = EstablishPaceChannelParser::fromCcid(establishPaceChannelMessage->getInputData()); + switch (parser.getPasswordId()) + { + case PacePasswordId::PACE_CAN: + return QStringLiteral("CAN"); + + case PacePasswordId::PACE_PIN: + return QStringLiteral("PIN"); + + case PacePasswordId::PACE_PUK: + return QStringLiteral("PUK"); + + default: + return QString(); + } +} + + +QString RemoteServiceModel::getErrorMessage(bool pNfcPluginAvailable, bool pNfcPluginEnabled, bool pWifiEnabled) const +{ + if (!pNfcPluginAvailable) + { + return tr("NFC is not available on your device."); + } + if (!pNfcPluginEnabled) + { + return tr("Please enable NFC to use the remote service."); + } + if (!pWifiEnabled) + { + return tr("Please connect your WiFi to use the remote service."); + } + + return QString(); +} + + +void RemoteServiceModel::forgetDevice(const QString& pId) +{ + mKnownDevices.forgetDevice(pId); +} + + +void RemoteServiceModel::cancelPasswordRequest() +{ + if (mContext) + { + Q_EMIT mContext->fireCancelPasswordRequest(); + } +} + + +RemoteServiceModel& RemoteServiceModel::getInstance() +{ + return *Instance; +} + + +void RemoteServiceModel::onConnectedDevicesChanged() +{ + RemoteClient* const remoteClient = Env::getSingleton(); + const auto deviceInfos = remoteClient->getConnectedDeviceInfos(); + QStringList deviceNames; + for (const auto& info : deviceInfos) + { + deviceNames.append(QLatin1Char('"') + info.getName() + QLatin1Char('"')); + } + mConnectedServerDeviceNames = deviceNames.join(QLatin1String(", ")); + Q_EMIT fireConnectedServerDeviceNamesChanged(); +} + + +void RemoteServiceModel::onEstablishPaceChannelMessageUpdated(const QSharedPointer& pMessage) +{ + if (pMessage.isNull() || pMessage->isIncomplete()) + { + mIsSaCPinChangeWorkflow = false; + } + else + { + const EstablishPaceChannelParser parser = EstablishPaceChannelParser::fromCcid(pMessage->getInputData()); + mIsSaCPinChangeWorkflow = parser.getCertificateDescription().isEmpty(); + } + + Q_EMIT fireEstablishPaceChannelMessageUpdated(); +} diff --git a/src/ui/qml/RemoteServiceModel.h b/src/ui/qml/RemoteServiceModel.h new file mode 100644 index 0000000..bc60b81 --- /dev/null +++ b/src/ui/qml/RemoteServiceModel.h @@ -0,0 +1,107 @@ +/*! + * \brief Model implementation for the remote service component + * + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/RemoteServiceContext.h" +#include "ReaderManager.h" +#include "RemoteDeviceModel.h" +#include "WifiInfo.h" +#include "WorkflowModel.h" + +#include +#include + +class test_RemoteServiceModel; + +namespace governikus +{ + +class RemoteServiceModel + : public WorkflowModel +{ + Q_OBJECT + + Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY fireIsRunningChanged) + Q_PROPERTY(bool runnable MEMBER mRunnable NOTIFY fireEnvironmentChanged) + Q_PROPERTY(bool canEnableNfc MEMBER mCanEnableNfc NOTIFY fireEnvironmentChanged) + Q_PROPERTY(QString errorMessage MEMBER mErrorMessage NOTIFY fireEnvironmentChanged) + Q_PROPERTY(QByteArray psk MEMBER mPsk NOTIFY firePskChanged) + Q_PROPERTY(QString currentFingerprint READ getCurrentFingerprint NOTIFY fireConnectedChanged) + Q_PROPERTY(bool connected READ isConnected NOTIFY fireConnectedChanged) + Q_PROPERTY(QString connectedClientDeviceName MEMBER mConnectedClientDeviceName NOTIFY fireConnectedClientDeviceNameChanged) + Q_PROPERTY(QString connectedServerDeviceNames MEMBER mConnectedServerDeviceNames NOTIFY fireConnectedServerDeviceNamesChanged) + Q_PROPERTY(RemoteDeviceModel * availableRemoteDevices READ getAvailableRemoteDevices CONSTANT) + Q_PROPERTY(RemoteDeviceModel * knownDevices READ getKnownDevices CONSTANT) + Q_PROPERTY(bool detectRemoteDevices READ detectRemoteDevices WRITE setDetectRemoteDevices NOTIFY fireDetectionChanged) + Q_PROPERTY(bool isSaCPinChangeWorkflow READ isSaCPinChangeWorkflow NOTIFY fireEstablishPaceChannelMessageUpdated) + + private: + friend class ::test_RemoteServiceModel; + + QSharedPointer mContext; + WifiInfo mWifiInfo; + bool mRunnable; + bool mCanEnableNfc; + QString mErrorMessage; + QByteArray mPsk; + RemoteDeviceModel mAvailableRemoteDevices; + RemoteDeviceModel mKnownDevices; + QString mConnectedClientDeviceName; + QString mConnectedServerDeviceNames; + bool mIsSaCPinChangeWorkflow; + + void onEnvironmentChanged(); + QString getErrorMessage(bool pNfcPluginAvailable, bool pNfcPluginEnabled, bool pWifiEnabled) const; + + private Q_SLOTS: + void onEstablishConnectionDone(const QSharedPointer& pEntry, const GlobalStatus& pStatus); + void onClientConnectedChanged(bool pConnected); + void onConnectedDevicesChanged(); + void onEstablishPaceChannelMessageUpdated(const QSharedPointer& pMessage); + + protected: + RemoteServiceModel(); + ~RemoteServiceModel() override = default; + + public: + bool isRunning() const; + void setRunning(bool pState); + + RemoteDeviceModel* getAvailableRemoteDevices(); + RemoteDeviceModel* getKnownDevices(); + void setDetectRemoteDevices(bool pNewStatus); + bool detectRemoteDevices(); + Q_INVOKABLE void connectToServer(const QString& pDeviceId, const QString& pServerPsk); + + void resetContext(const QSharedPointer& pContext = QSharedPointer()); + Q_INVOKABLE void setPairing(bool pEnabled = true); + QString getCurrentFingerprint() const; + bool isConnected() const; + bool isSaCPinChangeWorkflow() const; + + Q_INVOKABLE bool pinPadModeOn(); + Q_INVOKABLE QString getPacePasswordId() const; + Q_INVOKABLE void forgetDevice(const QString& pId); + Q_INVOKABLE void cancelPasswordRequest(); + + static RemoteServiceModel& getInstance(); + + Q_SIGNALS: + void fireIsRunningChanged(); + void fireEnvironmentChanged(); + void firePskChanged(const QByteArray& pPsk); + void fireConnectedChanged(bool pConnected); + void fireServerPskChanged(); + void fireDetectionChanged(); + void firePairingFailed(); + void fireConnectedClientDeviceNameChanged(); + void fireConnectedServerDeviceNamesChanged(); + void fireEstablishPaceChannelMessageUpdated(); +}; + + +} // namespace governikus diff --git a/src/ui/qml/SelfAuthModel.cpp b/src/ui/qml/SelfAuthModel.cpp new file mode 100644 index 0000000..d68cb76 --- /dev/null +++ b/src/ui/qml/SelfAuthModel.cpp @@ -0,0 +1,126 @@ +/*! + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "SelfAuthModel.h" + +#include "context/SelfAuthContext.h" +#include "LanguageLoader.h" + +using namespace governikus; + + +void SelfAuthModel::onSelfAuthenticationDataChanged() +{ + beginResetModel(); + mSelfData.clear(); + + if (mContext && mContext->getSelfAuthenticationData().isValid()) + { + const auto& selfdata = mContext->getSelfAuthenticationData().getOrderedSelfData(); + for (const auto& entry : selfdata) + { + if (entry.first.isEmpty()) + { + Q_ASSERT(!mSelfData.isEmpty()); + const auto& previous = mSelfData.takeLast(); + mSelfData << qMakePair(previous.first, previous.second + QStringLiteral("
") + entry.second); + } + else + { + mSelfData << entry; + } + } + } + + endResetModel(); +} + + +SelfAuthModel::SelfAuthModel(QObject* pParent) + : QAbstractListModel(pParent) + , mContext() + , mSelfData() +{ + onSelfAuthenticationDataChanged(); +} + + +void SelfAuthModel::resetContext(const QSharedPointer& pContext) +{ + mContext = pContext; + if (mContext) + { + connect(mContext.data(), &SelfAuthContext::fireSelfAuthenticationDataChanged, this, &SelfAuthModel::onSelfAuthenticationDataChanged); + } + onSelfAuthenticationDataChanged(); +} + + +void SelfAuthModel::startWorkflow() +{ + Q_EMIT fireStartWorkflow(); +} + + +void SelfAuthModel::cancelWorkflow() +{ + if (mContext) + { + Q_EMIT mContext->fireCancelWorkflow(); + } +} + + +bool SelfAuthModel::isBasicReader() +{ + if (mContext) + { + return mContext->getCardConnection()->getReaderInfo().isBasicReader(); + } + + return true; +} + + +int SelfAuthModel::rowCount(const QModelIndex&) const +{ + return mSelfData.size(); +} + + +QVariant SelfAuthModel::data(const QModelIndex& pIndex, int pRole) const +{ + if (pIndex.isValid() && pIndex.row() < rowCount()) + { + auto pair = mSelfData.at(pIndex.row()); + if (pRole == Qt::DisplayRole || pRole == NAME) + { + return pair.first; + } + if (pRole == VALUE) + { + return pair.second; + } + } + return QVariant(); +} + + +QHash SelfAuthModel::roleNames() const +{ + QHash roles = QAbstractListModel::roleNames(); + roles.insert(NAME, "name"); + roles.insert(VALUE, "value"); + return roles; +} + + +bool SelfAuthModel::event(QEvent* pEvent) +{ + if (pEvent->type() == QEvent::LanguageChange) + { + onSelfAuthenticationDataChanged(); + } + return QAbstractListModel::event(pEvent); +} diff --git a/src/ui/qml/SelfAuthModel.h b/src/ui/qml/SelfAuthModel.h new file mode 100644 index 0000000..37eed3c --- /dev/null +++ b/src/ui/qml/SelfAuthModel.h @@ -0,0 +1,61 @@ +/*! + * \brief Model implementation for the self authentication workflow. + * + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "SelfAuthenticationData.h" + +#include +#include +#include +#include + +class test_SelfAuthModel; + +namespace governikus +{ + +class SelfAuthContext; + +class SelfAuthModel + : public QAbstractListModel +{ + Q_OBJECT + + QSharedPointer mContext; + SelfAuthenticationData::OrderedSelfData mSelfData; + + enum DataRoles + { + NAME = Qt::UserRole + 1, + VALUE + }; + + private Q_SLOTS: + friend class ::test_SelfAuthModel; + void onSelfAuthenticationDataChanged(); + + public: + SelfAuthModel(QObject* pParent = nullptr); + void resetContext(const QSharedPointer& pContext = QSharedPointer()); + + Q_INVOKABLE void startWorkflow(); + Q_INVOKABLE void cancelWorkflow(); + Q_INVOKABLE bool isBasicReader(); + + int rowCount(const QModelIndex& = QModelIndex()) const override; + QVariant data(const QModelIndex& pIndex, int pRole = Qt::DisplayRole) const override; + QHash roleNames() const override; + + virtual bool event(QEvent* pEvent) override; + + Q_SIGNALS: + void fireStartWorkflow(); + +}; + + +} // namespace governikus diff --git a/src/ui/qml/SettingsModel.cpp b/src/ui/qml/SettingsModel.cpp new file mode 100644 index 0000000..e53f13a --- /dev/null +++ b/src/ui/qml/SettingsModel.cpp @@ -0,0 +1,175 @@ +/*! + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "SettingsModel.h" + +#include "AppSettings.h" +#include "HistorySettings.h" +#include "LanguageLoader.h" + +#ifdef Q_OS_ANDROID +#include +#endif + +using namespace governikus; + + +SettingsModel::SettingsModel() + : mShowTutorialOnStart(false) +{ + const HistorySettings& settings = Env::getSingleton()->getHistorySettings(); + connect(&settings, &HistorySettings::fireEnabledChanged, this, &SettingsModel::fireHistoryEnabledChanged); + + +#ifdef Q_OS_ANDROID + auto& generalSettings = Env::getSingleton()->getGeneralSettings(); + if (generalSettings.isShowSetupAssistant()) + { + generalSettings.setShowSetupAssistant(false); + generalSettings.save(); + + bool startedByAuth = QAndroidJniObject::callStaticMethod("com/governikus/ausweisapp2/MainActivity", "isStartedByAuth"); + if (!startedByAuth) + { + mShowTutorialOnStart = true; + } + } +#endif +} + + +QString SettingsModel::getEmptyString() +{ + return QString(); +} + + +QString SettingsModel::getLanguage() const +{ + return LanguageLoader::getInstance().getUsedLocale().bcp47Name(); +} + + +void SettingsModel::setLanguage(const QString& pLanguage) +{ + GeneralSettings& settings = Env::getSingleton()->getGeneralSettings(); + settings.setLanguage(QLocale(pLanguage).language()); + settings.save(); + + Q_EMIT fireLanguageChanged(); +} + + +bool SettingsModel::isDeveloperMode() const +{ + return Env::getSingleton()->getGeneralSettings().isDeveloperMode(); +} + + +void SettingsModel::setDeveloperMode(bool pEnable) +{ + if (isDeveloperMode() == pEnable) + { + return; + } + + Env::getSingleton()->getGeneralSettings().setDeveloperMode(pEnable); + Env::getSingleton()->getGeneralSettings().save(); + Q_EMIT fireDeveloperModeChanged(); +} + + +bool SettingsModel::useSelfauthenticationTestUri() const +{ + return Env::getSingleton()->getGeneralSettings().useSelfAuthTestUri(); +} + + +void SettingsModel::setUseSelfauthenticationTestUri(bool pUse) +{ + if (useSelfauthenticationTestUri() == pUse) + { + return; + } + + Env::getSingleton()->getGeneralSettings().setUseSelfauthenticationTestUri(pUse); + Env::getSingleton()->getGeneralSettings().save(); + Q_EMIT fireUseSelfauthenticationTestUriChanged(); +} + + +QString SettingsModel::getServerName() const +{ + return Env::getSingleton()->getRemoteServiceSettings().getServerName(); +} + + +bool SettingsModel::isValidServerName(const QString& name) const +{ + return !name.isEmpty(); +} + + +void SettingsModel::setServerName(const QString& name) +{ + RemoteServiceSettings& settings = Env::getSingleton()->getRemoteServiceSettings(); + settings.setServerName(name); + settings.save(); +} + + +void SettingsModel::removeTrustedCertificate(const QString& pFingerprint) +{ + Env::getSingleton()->getRemoteServiceSettings().removeTrustedCertificate(pFingerprint); +} + + +bool SettingsModel::getPinPadMode() const +{ + return Env::getSingleton()->getRemoteServiceSettings().getPinPadMode(); +} + + +void SettingsModel::setPinPadMode(bool pPinPadMode) +{ + RemoteServiceSettings& settings = Env::getSingleton()->getRemoteServiceSettings(); + settings.setPinPadMode(pPinPadMode); + settings.save(); +} + + +bool SettingsModel::isHistoryEnabled() const +{ + const HistorySettings& settings = Env::getSingleton()->getHistorySettings(); + return settings.isEnabled(); +} + + +void SettingsModel::setHistoryEnabled(bool pEnabled) +{ + HistorySettings& settings = Env::getSingleton()->getHistorySettings(); + settings.setEnabled(pEnabled); + settings.save(); +} + + +int SettingsModel::removeHistory(const QString& pPeriodToRemove) +{ + HistorySettings& settings = Env::getSingleton()->getHistorySettings(); + int removedItemCount = settings.deleteSettings(Enum::fromString(pPeriodToRemove, TimePeriod::UNKNOWN)); + settings.save(); + return removedItemCount; +} + + +bool SettingsModel::askForDeviceSurvey() const +{ + return Env::getSingleton()->getGeneralSettings().askForDeviceSurvey(); +} + + +void SettingsModel::setDeviceSurveyPending(bool pDeviceSurveyPending) +{ + Env::getSingleton()->getGeneralSettings().setDeviceSurveyPending(pDeviceSurveyPending); +} diff --git a/src/ui/qml/SettingsModel.h b/src/ui/qml/SettingsModel.h new file mode 100644 index 0000000..f38333c --- /dev/null +++ b/src/ui/qml/SettingsModel.h @@ -0,0 +1,68 @@ +/*! + * \brief Model implementation for the settings. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include + +namespace governikus +{ + +class SettingsModel + : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString translationTrigger READ getEmptyString NOTIFY fireLanguageChanged) + Q_PROPERTY(QString language READ getLanguage WRITE setLanguage NOTIFY fireLanguageChanged) + Q_PROPERTY(bool developerMode READ isDeveloperMode WRITE setDeveloperMode NOTIFY fireDeveloperModeChanged) + Q_PROPERTY(bool useSelfauthenticationTestUri READ useSelfauthenticationTestUri WRITE setUseSelfauthenticationTestUri NOTIFY fireUseSelfauthenticationTestUriChanged) + Q_PROPERTY(bool pinPadMode READ getPinPadMode WRITE setPinPadMode NOTIFY firePinPadModeChanged) + Q_PROPERTY(QString serverName READ getServerName WRITE setServerName NOTIFY fireDeviceNameChanged) + Q_PROPERTY(bool historyEnabled READ isHistoryEnabled WRITE setHistoryEnabled NOTIFY fireHistoryEnabledChanged) + Q_PROPERTY(bool showTutorialOnStart MEMBER mShowTutorialOnStart CONSTANT) + + private: + bool mShowTutorialOnStart; + + public: + SettingsModel(); + + QString getEmptyString(); + QString getLanguage() const; + void setLanguage(const QString& pLanguage); + + bool isDeveloperMode() const; + void setDeveloperMode(bool pEnabled); + + bool useSelfauthenticationTestUri() const; + void setUseSelfauthenticationTestUri(bool pUse); + + QString getServerName() const; + Q_INVOKABLE bool isValidServerName(const QString& name) const; + void setServerName(const QString& name); + + Q_INVOKABLE void removeTrustedCertificate(const QString& pFingerprint); + Q_INVOKABLE int removeHistory(const QString& pPeriodToRemove); + + bool getPinPadMode() const; + void setPinPadMode(bool pPinPadMode); + + bool isHistoryEnabled() const; + void setHistoryEnabled(bool pEnabled); + + Q_INVOKABLE bool askForDeviceSurvey() const; + Q_INVOKABLE void setDeviceSurveyPending(bool pDeviceSurveyPending); + + Q_SIGNALS: + void fireLanguageChanged(); + void fireDeveloperModeChanged(); + void fireUseSelfauthenticationTestUriChanged(); + void fireDeviceNameChanged(); + void firePinPadModeChanged(); + void fireHistoryEnabledChanged(); +}; + +} // namespace governikus diff --git a/src/ui/qml/ShareUtil.java b/src/ui/qml/ShareUtil.java new file mode 100644 index 0000000..211e5f5 --- /dev/null +++ b/src/ui/qml/ShareUtil.java @@ -0,0 +1,104 @@ +/* + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +package com.governikus.ausweisapp2; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import android.app.Activity; +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.LabeledIntent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.net.Uri; +import android.support.v4.content.FileProvider; +import android.util.Log; + + +public final class ShareUtil +{ + private static final String LOG_TAG = AusweisApp2Service.LOG_TAG; + + private ShareUtil() + { + } + + + public static boolean isNotAtLeastMarshmallow() + { + return android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M; + } + + + public static void mailLog(Activity activity, final String email, final String subject, final String msg, final String logFilePath, final String chooserTitle) + { + try + { + if (isNotAtLeastMarshmallow()) + { + Intent shareData = new Intent(Intent.ACTION_SENDTO); + shareData.setData(Uri.parse("mailto:")); + shareData.putExtra(Intent.EXTRA_EMAIL, new String[] {email}); + shareData.putExtra(Intent.EXTRA_SUBJECT, subject); + shareData.putExtra(Intent.EXTRA_TEXT, msg); + shareData.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(logFilePath))); + activity.startActivity(Intent.createChooser(shareData, chooserTitle)); + + return; + } + + Intent emailIntent = new Intent(Intent.ACTION_SENDTO); + emailIntent.setData(Uri.parse("mailto:")); + + PackageManager packageManager = activity.getPackageManager(); + List emailApps = packageManager.queryIntentActivities(emailIntent, PackageManager.MATCH_DEFAULT_ONLY); + List launcherIntents = new ArrayList(); + for (ResolveInfo resolveInfo : emailApps) + { + String packageName = resolveInfo.activityInfo.packageName; + + Intent shareData = new Intent(Intent.ACTION_SEND); + shareData.setComponent(new ComponentName(packageName, resolveInfo.activityInfo.name)); + shareData.setType("text/plain"); + shareData.putExtra(Intent.EXTRA_EMAIL, new String[] {email}); + shareData.putExtra(Intent.EXTRA_SUBJECT, subject); + shareData.putExtra(Intent.EXTRA_TEXT, msg); + shareData.putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(activity, activity.getPackageName(), new File(logFilePath))); + shareData.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + + launcherIntents.add(new LabeledIntent(shareData, packageName, resolveInfo.loadLabel(packageManager), resolveInfo.icon)); + } + + Intent chooserIntent = Intent.createChooser(new Intent(), chooserTitle); + chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, launcherIntents.toArray(new LabeledIntent[launcherIntents.size()])); + activity.startActivity(chooserIntent); + } + catch (Exception e) + { + Log.e(LOG_TAG, "Error sharing log file", e); + } + } + + + public static void shareLog(Activity activity, final String logFilePath, final String chooserTitle) + { + try + { + Intent shareData = new Intent(Intent.ACTION_SEND); + shareData.setType("text/plain"); + shareData.putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(activity, activity.getPackageName(), new File(logFilePath))); + shareData.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + activity.startActivity(Intent.createChooser(shareData, chooserTitle)); + } + catch (Exception e) + { + Log.e(LOG_TAG, "Error sharing log file", e); + } + } + + +} diff --git a/src/qml/StatusBarUtil.cpp b/src/ui/qml/StatusBarUtil.cpp similarity index 100% rename from src/qml/StatusBarUtil.cpp rename to src/ui/qml/StatusBarUtil.cpp diff --git a/src/ui/qml/StatusBarUtil.h b/src/ui/qml/StatusBarUtil.h new file mode 100644 index 0000000..1110a51 --- /dev/null +++ b/src/ui/qml/StatusBarUtil.h @@ -0,0 +1,31 @@ +/*! + * \brief Utility for changing the color of the status bar. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include +#include + +namespace governikus +{ + +class StatusBarUtil + : public QObject +{ + Q_OBJECT + + private: + bool catchJavaExceptions() const; + + public: + StatusBarUtil() = default; + ~StatusBarUtil() override = default; + + Q_INVOKABLE void setStatusBarColor(const QString& pColor); +}; + +} // namespace governikus diff --git a/src/ui/qml/UIPlugInQml.cpp b/src/ui/qml/UIPlugInQml.cpp new file mode 100644 index 0000000..f338329 --- /dev/null +++ b/src/ui/qml/UIPlugInQml.cpp @@ -0,0 +1,376 @@ +/*! + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "UIPlugInQml.h" + +#include "ApplicationModel.h" +#include "AppSettings.h" +#include "AuthModel.h" +#include "context/AuthContext.h" +#include "context/ChangePinContext.h" +#include "context/SelfAuthContext.h" +#include "CardReturnCode.h" +#include "ChangePinModel.h" +#include "DpiCalculator.h" +#include "Env.h" +#include "FileDestination.h" +#include "LogModel.h" +#include "PlatformTools.h" +#include "ProviderCategoryFilterModel.h" +#include "RemoteServiceModel.h" +#include "Service.h" +#include "StatusBarUtil.h" + +#if defined(Q_OS_ANDROID) + #include + #include + #include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + + +Q_DECLARE_LOGGING_CATEGORY(qml) + + +using namespace governikus; + + +template +QObject* provideQmlType(QQmlEngine* pEngine, QJSEngine* pScriptEngine) +{ + Q_UNUSED(pEngine) + Q_UNUSED(pScriptEngine) + + return new T(); +} + + +template +static QObject* provideSingletonQmlType(QQmlEngine* pEngine, QJSEngine* pScriptEngine) +{ + Q_UNUSED(pScriptEngine); + + const auto model = Env::getSingleton(); + pEngine->setObjectOwnership(model, QQmlEngine::CppOwnership); + return model; +} + + +template +static void registerQmlType(QObject* (*pTypeProvider)(QQmlEngine*, QJSEngine*)) +{ + QByteArray qmlName(T::staticMetaObject.className()); + qmlName.replace(QByteArrayLiteral("governikus::"), QByteArray()); + + const QByteArray url = QByteArrayLiteral("Governikus.Type.") + qmlName; + + qmlRegisterSingletonType(url.constData(), 1, 0, qmlName.constData(), pTypeProvider); +} + + +UIPlugInQml::UIPlugInQml() + : mEngine() + , mHistoryModel(&Env::getSingleton()->getHistorySettings()) + , mVersionInformationModel() + , mQmlExtension() + , mSelfAuthModel() + , mSettingsModel() + , mCertificateDescriptionModel() + , mChatModel() + , mExplicitPlatformStyle(getPlatformSelectors()) + , mConnectivityManager() + , mTrayIcon() +{ +#if defined(Q_OS_ANDROID) + QGuiApplication::setFont(QFont("Roboto")); +#endif + + connect(&mTrayIcon, &TrayIcon::fireShow, this, &UIPlugInQml::show); + connect(&mTrayIcon, &TrayIcon::fireQuit, this, &UIPlugInQml::fireQuitApplicationRequest); + + connect(Env::getSingleton(), &ChangePinModel::fireStartWorkflow, this, &UIPlugIn::fireChangePinRequest); + connect(&mSelfAuthModel, &SelfAuthModel::fireStartWorkflow, this, &UIPlugIn::fireSelfAuthenticationRequested); + connect(Env::getSingleton(), &RemoteServiceModel::fireStartWorkflow, this, &UIPlugIn::fireRemoteServiceRequested); + connect(this, &UIPlugIn::fireShowUserInformation, this, &UIPlugInQml::onShowUserInformation); + init(); +} + + +void UIPlugInQml::registerQmlTypes() +{ + qmlRegisterUncreatableType("Governikus.Type.UiModule", 1, 0, "UiModule", QStringLiteral("Not creatable as it is an enum type")); + qmlRegisterUncreatableType("Governikus.Type.ReaderPlugIn", 1, 0, "ReaderPlugIn", QStringLiteral("Not creatable as it is an enum type")); + qmlRegisterUncreatableType("Governikus.Type.CardReturnCode", 1, 0, "CardReturnCode", QStringLiteral("Not creatable as it is an enum type")); + qmlRegisterUncreatableType("Governikus.Type.PacePasswordId", 1, 0, "PacePasswordId", QStringLiteral("Not creatable as it is an enum type")); + + registerQmlType(&provideQmlType ); + registerQmlType(&provideQmlType ); + + registerQmlType(&provideSingletonQmlType ); + registerQmlType(&provideSingletonQmlType ); + registerQmlType(&provideSingletonQmlType ); + registerQmlType(&provideSingletonQmlType ); + registerQmlType(&provideSingletonQmlType ); + registerQmlType(&provideSingletonQmlType ); +} + + +void UIPlugInQml::init() +{ +#if !defined(QT_NO_DEBUG) && !defined(Q_OS_IOS) && !defined(Q_OS_ANDROID) && !defined(Q_OS_WINRT) + qputenv("QML_DISABLE_DISK_CACHE", "true"); +#endif + + mEngine.reset(new QQmlApplicationEngine()); + + mEngine->rootContext()->setContextProperty(QStringLiteral("plugin"), this); + QQmlFileSelector::get(mEngine.data())->setExtraSelectors(mExplicitPlatformStyle.split(QLatin1Char(','))); + + mEngine->rootContext()->setContextProperty(QStringLiteral("screenDpiScale"), DpiCalculator::getDpiScale()); + mEngine->rootContext()->setContextProperty(QStringLiteral("historyModel"), &mHistoryModel); + mEngine->rootContext()->setContextProperty(QStringLiteral("versionInformationModel"), &mVersionInformationModel); + mEngine->rootContext()->setContextProperty(QStringLiteral("qmlExtension"), &mQmlExtension); + mEngine->rootContext()->setContextProperty(QStringLiteral("selfAuthModel"), &mSelfAuthModel); + mEngine->rootContext()->setContextProperty(QStringLiteral("settingsModel"), &mSettingsModel); + mEngine->rootContext()->setContextProperty(QStringLiteral("certificateDescriptionModel"), &mCertificateDescriptionModel); + mEngine->rootContext()->setContextProperty(QStringLiteral("chatModel"), &mChatModel); + mEngine->rootContext()->setContextProperty(QStringLiteral("connectivityManager"), &mConnectivityManager); + + UIPlugInQml::registerQmlTypes(); + + mEngine->addImportPath(getPath(QStringLiteral("qml/"), false).toString()); + mEngine->addImportPath(getPath(QStringLiteral("qml/")).toString()); + mEngine->load(getPath(QStringLiteral("qml/main.qml"))); + + Env::getSingleton()->updateConfigurations(); +} + + +void UIPlugInQml::hide() +{ + PlatformTools::hideFromTaskbar(); +} + + +QString UIPlugInQml::getPlatformSelectors() const +{ +#ifndef QT_NO_DEBUG + const char* overrideSelector = "OVERRIDE_PLATFORM_SELECTOR"; + if (!qEnvironmentVariableIsEmpty(overrideSelector)) + { + const auto& platform = QString::fromLocal8Bit(qgetenv(overrideSelector)); + qCDebug(qml) << "Override platform selector:" << platform; + if (platform.startsWith(QLatin1Char('+')) || platform.contains(QLatin1String(",+"))) + { + Q_ASSERT(false && "Please supply selectors without a '+'."); + } + return platform; + } +#endif + +#if defined(Q_OS_ANDROID) + const jboolean result = QtAndroid::androidActivity().callMethod("isTablet", "()Z"); + const bool isTablet = result != JNI_FALSE; +#else + // A device with a screen diagnonal above 6 inches is considered a tablet. + static const double MAX_SMARTPHONE_DIAGONAL_IN = 6.0; + static const double MAX_SMARTPHONE_DIAGONAL_MM = 25.4 * MAX_SMARTPHONE_DIAGONAL_IN; + + const QList screens = QGuiApplication::screens(); + + Q_ASSERT(!screens.isEmpty()); + + QScreen* const mainScreen = screens.first(); + Q_ASSERT(mainScreen != nullptr); + + const QSizeF size = mainScreen->physicalSize(); + const double width = size.width(); + const double height = size.height(); + const double diagonal = sqrt(width * width + height * height); + const bool isTablet = diagonal > MAX_SMARTPHONE_DIAGONAL_MM; +#endif + +#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) + const QString platform = QStringLiteral("mobile,"); + const QString tablet = (isTablet ? QStringLiteral("tablet,") : QStringLiteral("phone,")); + const QString brand = QGuiApplication::platformName(); +#else + const QString platform = QStringLiteral("desktop,"); + Q_UNUSED(isTablet); + const QString tablet; +#if defined(Q_OS_MAC) + const QString brand = QStringLiteral("mac"); +#else + const QString brand = QStringLiteral("win"); +#endif +#endif + + return platform + tablet + brand; +} + + +void UIPlugInQml::onWorkflowStarted(QSharedPointer pContext) +{ + onShowUi(UiModule::IDENTIFY); + mQmlExtension.keepScreenOn(true); + + Env::getSingleton()->resetContext(pContext); + Env::getSingleton()->resetContext(pContext); + + if (auto changePinContext = pContext.objectCast()) + { + Env::getSingleton()->resetContext(changePinContext); + } + + if (auto authContext = pContext.objectCast()) + { + mConnectivityManager.startWatching(); + Env::getSingleton()->resetContext(authContext); + mCertificateDescriptionModel.resetContext(authContext); + mChatModel.resetContext(authContext); + } + + if (auto authContext = pContext.objectCast()) + { + mSelfAuthModel.resetContext(authContext); + } + + if (auto remoteServiceContext = pContext.objectCast()) + { + Env::getSingleton()->resetContext(remoteServiceContext); + } +} + + +void UIPlugInQml::onWorkflowFinished(QSharedPointer pContext) +{ + mQmlExtension.keepScreenOn(false); + + Env::getSingleton()->resetContext(); + Env::getSingleton()->resetContext(); + + if (pContext.objectCast()) + { + Env::getSingleton()->resetContext(); + } + + if (pContext.objectCast()) + { + mConnectivityManager.stopWatching(); + Env::getSingleton()->resetContext(); + mCertificateDescriptionModel.resetContext(); + mChatModel.resetContext(); + } + + if (pContext.objectCast()) + { + mSelfAuthModel.resetContext(); + } + + if (pContext.objectCast()) + { + Env::getSingleton()->resetContext(); + } +} + + +void UIPlugInQml::onApplicationStarted() +{ + mTrayIcon.create(); + show(); +} + + +void UIPlugInQml::onShowUi(UiModule pModule) +{ + PlatformTools::restoreToTaskbar(); + Q_EMIT fireShowRequest(pModule); +} + + +void UIPlugInQml::onShowUserInformation(const QString& pMessage) +{ + mQmlExtension.showFeedback(pMessage); +} + + +void UIPlugInQml::show() +{ + onShowUi(UiModule::CURRENT); +} + + +void UIPlugInQml::doShutdown() +{ +} + + +QUrl UIPlugInQml::getPath(const QString& pRelativePath, bool pQrc) +{ +#if !defined(QT_NO_DEBUG) && !defined(Q_OS_IOS) && !defined(Q_OS_ANDROID) && !defined(Q_OS_WINRT) + const QString ressourceFolderPath(QStringLiteral(RES_DIR) + QLatin1Char('/') + pRelativePath); + if (QFile::exists(ressourceFolderPath)) + { + return QUrl::fromLocalFile(ressourceFolderPath); + } + +#endif + + if (pQrc) + { + return QStringLiteral("qrc:///") + pRelativePath; + } + + const QString path = FileDestination::getPath(pRelativePath); + return QUrl::fromLocalFile(path); +} + + +void UIPlugInQml::doRefresh() +{ + qCDebug(qml) << "Reload qml files"; + QMetaObject::invokeMethod(this, &UIPlugInQml::init, Qt::QueuedConnection); +} + + +bool UIPlugInQml::useFlatStyleOnDesktop() const +{ + return QOperatingSystemVersion::current() >= QOperatingSystemVersion(QOperatingSystemVersion::Windows8); +} + + +QString UIPlugInQml::getPlatformStyle() const +{ + return mExplicitPlatformStyle; +} + + +void UIPlugInQml::applyPlatformStyle(const QString& pPlatformStyle) +{ + if (mExplicitPlatformStyle != pPlatformStyle) + { + mExplicitPlatformStyle = pPlatformStyle; + doRefresh(); + } +} + + +bool UIPlugInQml::isDeveloperBuild() const +{ +#ifndef QT_NO_DEBUG + return true; + +#else + return false; + +#endif +} diff --git a/src/ui/qml/UIPlugInQml.h b/src/ui/qml/UIPlugInQml.h new file mode 100644 index 0000000..ce1123f --- /dev/null +++ b/src/ui/qml/UIPlugInQml.h @@ -0,0 +1,82 @@ +/*! + * \brief UIPlugIn implementation of QML. + * + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "CertificateDescriptionModel.h" +#include "ChatModel.h" +#include "ConnectivityManager.h" +#include "HistoryModel.h" +#include "NumberModel.h" +#include "QmlExtension.h" +#include "SelfAuthModel.h" +#include "SettingsModel.h" +#include "TrayIcon.h" +#include "UIPlugIn.h" +#include "VersionInformationModel.h" + +#include +#include + +namespace governikus +{ + +class UIPlugInQml + : public UIPlugIn +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "governikus.UIPlugIn" FILE "metadata.json") + Q_INTERFACES(governikus::UIPlugIn) + Q_PROPERTY(QString platformStyle READ getPlatformStyle CONSTANT) + Q_PROPERTY(bool developerBuild READ isDeveloperBuild CONSTANT) + + private: + QScopedPointer mEngine; + HistoryModel mHistoryModel; + VersionInformationModel mVersionInformationModel; + QmlExtension mQmlExtension; + SelfAuthModel mSelfAuthModel; + SettingsModel mSettingsModel; + CertificateDescriptionModel mCertificateDescriptionModel; + ChatModel mChatModel; + QString mExplicitPlatformStyle; + ConnectivityManager mConnectivityManager; + TrayIcon mTrayIcon; + + QString getPlatformSelectors() const; + static QUrl getPath(const QString& pRelativePath, bool pQrc = true); + void createTrayIcon(); + + public: + UIPlugInQml(); + virtual ~UIPlugInQml() override = default; + + static void registerQmlTypes(); + + Q_INVOKABLE bool useFlatStyleOnDesktop() const; + QString getPlatformStyle() const; + Q_INVOKABLE void applyPlatformStyle(const QString& pPlatformStyle); + Q_INVOKABLE bool isDeveloperBuild() const; + Q_INVOKABLE void init(); + Q_INVOKABLE void hide(); + + Q_SIGNALS: + void fireShowRequest(UiModule pModule); + + private Q_SLOTS: + void show(); + virtual void doShutdown() override; + virtual void onWorkflowStarted(QSharedPointer pContext) override; + virtual void onWorkflowFinished(QSharedPointer pContext) override; + virtual void onApplicationStarted() override; + virtual void onShowUi(UiModule pModule) override; + void onShowUserInformation(const QString& pMessage); + + public Q_SLOTS: + void doRefresh(); +}; + +} // namespace governikus diff --git a/src/ui/qml/VersionInformationModel.cpp b/src/ui/qml/VersionInformationModel.cpp new file mode 100644 index 0000000..ffd5a5e --- /dev/null +++ b/src/ui/qml/VersionInformationModel.cpp @@ -0,0 +1,97 @@ +/*! + * \brief Model implementation for version information. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "VersionInformationModel.h" + +#include "AppSettings.h" +#include "BuildHelper.h" +#include "DeviceInfo.h" + +#include +#include +#include +#include + +#include + + +using namespace governikus; + + +void VersionInformationModel::init() +{ + mData.clear(); + mData += QPair(tr("Application Name"), QCoreApplication::applicationName()); + mData += QPair(tr("Application Version"), QCoreApplication::applicationVersion()); + mData += QPair(tr("Organization"), QCoreApplication::organizationName()); + mData += QPair(tr("Organization domain"), QCoreApplication::organizationDomain()); +#ifdef Q_OS_ANDROID + mData += QPair(tr("VersionCode"), QString::number(BuildHelper::getVersionCode())); +#endif + mData += QPair(tr("System version"), QSysInfo::prettyProductName()); + mData += QPair(tr("Kernel"), QSysInfo::kernelVersion()); + + QString architecture = QSysInfo::currentCpuArchitecture(); + if (architecture != QSysInfo::buildCpuArchitecture()) + { + architecture += QStringLiteral(" (%1)").arg(QSysInfo::buildCpuArchitecture()); + } + mData += QPair(tr("Architecture"), architecture); + +#ifdef Q_OS_ANDROID + mData += QPair(tr("Device"), DeviceInfo::getPrettyInfo()); +#endif + mData += QPair(tr("Qt Version"), QString::fromLatin1(qVersion())); + mData += QPair(tr("OpenSSL Version"), QSslSocket::sslLibraryVersionString()); +} + + +VersionInformationModel::VersionInformationModel(QObject* pParent) + : QAbstractListModel(pParent) + , mData() +{ + init(); + + connect(&Env::getSingleton()->getGeneralSettings(), &GeneralSettings::fireSettingsChanged, this, [this]() + { + beginResetModel(); + init(); + endResetModel(); + }); +} + + +int VersionInformationModel::rowCount(const QModelIndex&) const +{ + return mData.size(); +} + + +QVariant VersionInformationModel::data(const QModelIndex& pIndex, int pRole) const +{ + if (pIndex.isValid() && pIndex.row() < rowCount()) + { + auto entry = mData[pIndex.row()]; + if (pRole == LABEL) + { + return entry.first; + } + if (pRole == TEXT) + { + return entry.second; + } + } + return QVariant(); +} + + +QHash VersionInformationModel::roleNames() const +{ + QHash roles = QAbstractListModel::roleNames(); + roles.insert(LABEL, "label"); + roles.insert(TEXT, "text"); + return roles; +} diff --git a/src/ui/qml/VersionInformationModel.h b/src/ui/qml/VersionInformationModel.h new file mode 100644 index 0000000..81e1771 --- /dev/null +++ b/src/ui/qml/VersionInformationModel.h @@ -0,0 +1,38 @@ +/*! + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include +#include +#include + +namespace governikus +{ + +class VersionInformationModel + : public QAbstractListModel +{ + Q_OBJECT + + private: + enum HistoryRoles + { + LABEL = Qt::UserRole + 1, + TEXT + }; + QVector > mData; + + void init(); + + public: + VersionInformationModel(QObject* pParent = nullptr); + + int rowCount(const QModelIndex& = QModelIndex()) const override; + QVariant data(const QModelIndex& pIndex, int pRole = Qt::DisplayRole) const override; + QHash roleNames() const override; +}; + +} // namespace governikus diff --git a/src/ui/qml/WorkflowModel.cpp b/src/ui/qml/WorkflowModel.cpp new file mode 100644 index 0000000..95ef91b --- /dev/null +++ b/src/ui/qml/WorkflowModel.cpp @@ -0,0 +1,216 @@ +/*! + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "WorkflowModel.h" + +#include "AppSettings.h" +#include "FuncUtils.h" +#include "GeneralSettings.h" +#include "ReaderConfiguration.h" +#include "ReaderManager.h" + + +using namespace governikus; + + +WorkflowModel::WorkflowModel(QObject* pParent) + : QObject(pParent) + , mContext() +{ + onReaderManagerSignal(); + connect(Env::getSingleton(), &ReaderManager::fireCardInserted, this, &WorkflowModel::onReaderManagerSignal); + connect(Env::getSingleton(), &ReaderManager::fireCardRemoved, this, &WorkflowModel::onReaderManagerSignal); + connect(Env::getSingleton(), &ReaderManager::fireReaderAdded, this, &WorkflowModel::onReaderManagerSignal); + connect(Env::getSingleton(), &ReaderManager::fireReaderRemoved, this, &WorkflowModel::onReaderManagerSignal); +} + + +WorkflowModel::~WorkflowModel() +{ +} + + +void WorkflowModel::resetContext(const QSharedPointer& pContext) +{ + mContext = pContext; + if (mContext) + { + connect(mContext.data(), &WorkflowContext::fireStateChanged, this, &WorkflowModel::fireCurrentStateChanged); + connect(mContext.data(), &WorkflowContext::fireResultChanged, this, &WorkflowModel::fireResultChanged); + connect(mContext.data(), &WorkflowContext::fireReaderPlugInTypesChanged, this, &WorkflowModel::fireReaderPlugInTypeChanged); + connect(mContext.data(), &WorkflowContext::fireCardConnectionChanged, this, &WorkflowModel::fireIsBasicReaderChanged); + } + + /* + * Only this state change is emitted when the context is reset, i.e. after the end of the workflow + */ + Q_EMIT fireCurrentStateChanged(getCurrentState()); + Q_EMIT fireResultChanged(); +} + + +QString WorkflowModel::getCurrentState() const +{ + return mContext ? mContext->getCurrentState() : QString(); +} + + +QString WorkflowModel::getResultString() const +{ + return mContext ? mContext->getStatus().toErrorDescription(true) : QString(); +} + + +bool WorkflowModel::isError() const +{ + return mContext && mContext->getStatus().isError(); +} + + +bool WorkflowModel::isMaskedError() const +{ + return mContext && mContext->getStatus().isMessageMasked(); +} + + +ReaderManagerPlugInType WorkflowModel::getReaderPlugInType() const +{ + if (mContext && !mContext->getReaderPlugInTypes().isEmpty()) + { + return mContext->getReaderPlugInTypes().at(0); + } + + return ReaderManagerPlugInType::UNKNOWN; +} + + +void WorkflowModel::setReaderPlugInType(ReaderManagerPlugInType pReaderPlugInType) +{ + if (!mContext) + { + return; + } + mContext->setReaderPlugInTypes({pReaderPlugInType}); + + GeneralSettings& settings = Env::getSingleton()->getGeneralSettings(); + settings.setLastReaderPluginType(getEnumName(pReaderPlugInType)); + settings.save(); +} + + +void WorkflowModel::continueWorkflow() +{ + if (mContext) + { + mContext->setStateApproved(); + } +} + + +void WorkflowModel::startWorkflow() +{ + Q_EMIT fireStartWorkflow(); +} + + +void WorkflowModel::cancelWorkflow() +{ + if (mContext) + { + Q_EMIT mContext->fireCancelWorkflow(); + } +} + + +void WorkflowModel::cancelWorkflowOnPinBlocked() +{ + if (mContext) + { + mContext->setStatus(GlobalStatus::Code::Workflow_Pin_Blocked_And_Puk_Objectionable); + Q_EMIT mContext->fireCancelWorkflow(); + } +} + + +void WorkflowModel::cancelWorkflowToChangePin() +{ + if (mContext) + { + mContext->setStatus(GlobalStatus::Code::Workflow_Cancellation_By_User); + Q_EMIT mContext->fireCancelWorkflow(); + } +} + + +bool WorkflowModel::isBasicReader() +{ + if (mContext && mContext->getCardConnection()) + { + return mContext->getCardConnection()->getReaderInfo().isBasicReader(); + } + + return true; +} + + +void WorkflowModel::setInitialPluginType() +{ + const GeneralSettings& settings = Env::getSingleton()->getGeneralSettings(); + + const QString& lastReaderPluginTypeString = settings.getLastReaderPluginType(); + const auto& lastReaderPluginType = Enum::fromString(lastReaderPluginTypeString, ReaderManagerPlugInType::UNKNOWN); + + if (lastReaderPluginType == ReaderManagerPlugInType::UNKNOWN) + { +#if defined(Q_OS_ANDROID) + setReaderPlugInType(ReaderManagerPlugInType::NFC); +#elif defined(Q_OS_IOS) + setReaderPlugInType(ReaderManagerPlugInType::BLUETOOTH); +#else + setReaderPlugInType(ReaderManagerPlugInType::PCSC); +#endif + return; + } + setReaderPlugInType(lastReaderPluginType); +} + + +bool WorkflowModel::selectedReaderHasCard() const +{ + if (mContext && !mContext->getReaderName().isEmpty()) + { + return Env::getSingleton()->getReaderInfo(mContext->getReaderName()).hasCard(); + } + return false; +} + + +void WorkflowModel::onReaderManagerSignal() +{ + QString newReaderImage; + const auto& readerInfos = Env::getSingleton()->getReaderInfos(ReaderFilter::UniqueReaderTypes); + const auto& readersWithNPA = filter([](const ReaderInfo& i){return i.hasEidCard();}, readerInfos); + if (readersWithNPA.size() == 1) + { + newReaderImage = readersWithNPA.at(0).getReaderConfigurationInfo().getIconWithNPA()->lookupPath(); + } + else if (readerInfos.size() == 1) + { + newReaderImage = readerInfos.at(0).getReaderConfigurationInfo().getIcon()->lookupPath(); + } + else if (readerInfos.size() > 1) + { + newReaderImage = ReaderConfiguration::getMultipleReaderIconPath().replace(QLatin1Char(':'), QLatin1String("qrc://")); + } + else + { + newReaderImage = ReaderConfiguration::getNoReaderFoundIconPath().replace(QLatin1Char(':'), QLatin1String("qrc://")); + } + + if (newReaderImage != mReaderImage) + { + mReaderImage = newReaderImage; + Q_EMIT fireReaderImageChanged(); + } +} diff --git a/src/ui/qml/WorkflowModel.h b/src/ui/qml/WorkflowModel.h new file mode 100644 index 0000000..3398243 --- /dev/null +++ b/src/ui/qml/WorkflowModel.h @@ -0,0 +1,76 @@ +/*! + * \brief Model implementation for the authentication action. + * + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/WorkflowContext.h" +#include "ReaderManagerPlugInInfo.h" + +#include +#include +#include + +class test_WorkflowModel; + +namespace governikus +{ + +class WorkflowModel + : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString currentState READ getCurrentState NOTIFY fireCurrentStateChanged) + Q_PROPERTY(QString resultString READ getResultString NOTIFY fireResultChanged) + Q_PROPERTY(bool error READ isError NOTIFY fireResultChanged) + Q_PROPERTY(bool errorIsMasked READ isMaskedError NOTIFY fireResultChanged) + Q_PROPERTY(ReaderManagerPlugInType readerPlugInType READ getReaderPlugInType WRITE setReaderPlugInType NOTIFY fireReaderPlugInTypeChanged) + Q_PROPERTY(bool isBasicReader READ isBasicReader NOTIFY fireIsBasicReaderChanged) + Q_PROPERTY(QString readerImage MEMBER mReaderImage NOTIFY fireReaderImageChanged) + + private: + friend class ::test_WorkflowModel; + + QSharedPointer mContext; + QString mReaderImage; + + public: + WorkflowModel(QObject* pParent = nullptr); + virtual ~WorkflowModel(); + + void resetContext(const QSharedPointer& pContext = QSharedPointer()); + + QString getCurrentState() const; + virtual QString getResultString() const; + bool isError() const; + bool isMaskedError() const; + + ReaderManagerPlugInType getReaderPlugInType() const; + void setReaderPlugInType(ReaderManagerPlugInType pReaderPlugInType); + + bool isBasicReader(); + + Q_INVOKABLE void startWorkflow(); + Q_INVOKABLE void cancelWorkflow(); + Q_INVOKABLE void cancelWorkflowOnPinBlocked(); + Q_INVOKABLE void cancelWorkflowToChangePin(); + Q_INVOKABLE void continueWorkflow(); + Q_INVOKABLE void setInitialPluginType(); + Q_INVOKABLE bool selectedReaderHasCard() const; + + public Q_SLOTS: + void onReaderManagerSignal(); + + Q_SIGNALS: + void fireStartWorkflow(); + void fireCurrentStateChanged(const QString& pState); + void fireResultChanged(); + void fireReaderPlugInTypeChanged(); + void fireIsBasicReaderChanged(); + void fireReaderImageChanged(); +}; + + +} // namespace governikus diff --git a/src/qml/metadata.json b/src/ui/qml/metadata.json similarity index 100% rename from src/qml/metadata.json rename to src/ui/qml/metadata.json diff --git a/src/ui/websocket/CMakeLists.txt b/src/ui/websocket/CMakeLists.txt new file mode 100644 index 0000000..2d39144 --- /dev/null +++ b/src/ui/websocket/CMakeLists.txt @@ -0,0 +1,13 @@ +##################################################################### +# The websockets plugin implements the ui interface for a websocket. +# +# This websocket can be used as a SDK for desktop systems as it +# uses the json api. +##################################################################### + +IF(TARGET Qt5::WebSockets) + ADD_PLATFORM_LIBRARY(AusweisAppUiWebsocket) + + TARGET_LINK_LIBRARIES(AusweisAppUiWebsocket Qt5::Core Qt5::WebSockets AusweisAppUi AusweisAppUiJsonApi AusweisAppNetwork AusweisAppGlobal) + TARGET_COMPILE_DEFINITIONS(AusweisAppUiWebsocket PRIVATE QT_STATICPLUGIN) +ENDIF() diff --git a/src/ui/websocket/UIPlugInWebSocket.cpp b/src/ui/websocket/UIPlugInWebSocket.cpp new file mode 100644 index 0000000..268d5e9 --- /dev/null +++ b/src/ui/websocket/UIPlugInWebSocket.cpp @@ -0,0 +1,196 @@ +/*! + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "UIPlugInWebSocket.h" + +#include "AppSettings.h" +#include "Env.h" +#include "ReaderManager.h" +#include "UILoader.h" + +#include +#include +#include +#include +#include +#include + + +Q_DECLARE_LOGGING_CATEGORY(websocket) + +using namespace governikus; + + +UIPlugInWebSocket::UIPlugInWebSocket() + : UIPlugIn() + , mServer(QCoreApplication::applicationName() + QLatin1Char('/') + QCoreApplication::applicationVersion(), QWebSocketServer::NonSecureMode) + , mConnection(nullptr) + , mRequest() + , mJsonApi(nullptr) + , mContext() + , mUiDomination(false) +{ + if (!UILoader::getInstance().load(UIPlugInName::UIPlugInJsonApi)) + { + qCWarning(websocket) << "Cannot start WebSocket because JSON-API is missing"; + return; + } + + mJsonApi = qobject_cast(UILoader::getInstance().getLoaded(UIPlugInName::UIPlugInJsonApi)); + Q_ASSERT(mJsonApi); + + mHttpServer = Env::getShared(); + if (mHttpServer->isListening()) + { + qCDebug(websocket) << "Enable WebSocket..."; + connect(mHttpServer.data(), &HttpServer::fireNewWebSocketRequest, this, &UIPlugInWebSocket::onNewWebSocketRequest); + connect(&mServer, &QWebSocketServer::newConnection, this, &UIPlugInWebSocket::onNewConnection); + } +} + + +UIPlugInWebSocket::~UIPlugInWebSocket() +{ +} + + +void UIPlugInWebSocket::onWorkflowStarted(QSharedPointer pContext) +{ + if (mUiDomination) + { + pContext->setReaderPlugInTypes({ReaderManagerPlugInType::PCSC, ReaderManagerPlugInType::REMOTE}); + mContext = pContext; + } +} + + +void UIPlugInWebSocket::onWorkflowFinished(QSharedPointer pContext) +{ + Q_UNUSED(pContext); + + mContext.clear(); +} + + +void UIPlugInWebSocket::onUiDomination(const UIPlugIn* pUi, const QString& pInformation, bool pAccepted) +{ + Q_UNUSED(pInformation) + + if (pUi != this) + { + return; + } + + if (pAccepted) + { + Q_ASSERT(!mConnection); + mUiDomination = true; + Env::getSingleton()->setUsedAsSDK(true); + Env::getSingleton()->startScanAll(); + mServer.handleConnection(mRequest->take()); + } + else + { + mRequest->send(HTTP_STATUS_CONFLICT); + mRequest.reset(); + } +} + + +void UIPlugInWebSocket::onUiDominationReleased() +{ + mUiDomination = false; + mJsonApi->setEnabled(false); + Env::getSingleton()->stopScanAll(); + Env::getSingleton()->setUsedAsSDK(false); +} + + +void UIPlugInWebSocket::onNewWebSocketRequest(const QSharedPointer& pRequest) +{ + const auto& url = pRequest->getUrl(); + + const QRegularExpression path(QStringLiteral("^/eID-Kernel(/)?$")); + if (!path.match(url.path()).hasMatch()) + { + qCDebug(websocket) << "Request path not supported! Use /eID-Kernel"; + pRequest->send(HTTP_STATUS_NOT_FOUND); + return; + } + + if (mRequest) + { + qCDebug(websocket) << "Client is already connected..."; + pRequest->send(HTTP_STATUS_TOO_MANY_REQUESTS); + return; + } + + mRequest = pRequest; + Q_EMIT fireUiDominationRequest(this, QString::fromLatin1(pRequest->getHeader().value(QByteArrayLiteral("user-agent")))); +} + + +void UIPlugInWebSocket::onNewConnection() +{ + if (mServer.hasPendingConnections()) + { + mConnection.reset(mServer.nextPendingConnection()); + connect(mJsonApi, &UIPlugInJsonApi::fireMessage, this, &UIPlugInWebSocket::onJsonApiMessage); + connect(mConnection.data(), &QWebSocket::textMessageReceived, this, &UIPlugInWebSocket::onTextMessageReceived); + connect(mConnection.data(), &QWebSocket::disconnected, this, &UIPlugInWebSocket::onClientDisconnected); + mJsonApi->setEnabled(); + } + else + { + mConnection.reset(); + mRequest.reset(); + Q_EMIT fireUiDominationRelease(); + } +} + + +void UIPlugInWebSocket::onClientDisconnected() +{ + qCDebug(websocket) << "Client disconnected..."; + + if (mContext && mUiDomination) + { + const QSignalBlocker blocker(mJsonApi); + Q_EMIT mContext->fireCancelWorkflow(); + } + + mConnection.reset(); + mRequest.reset(); + disconnect(mJsonApi, &UIPlugInJsonApi::fireMessage, this, &UIPlugInWebSocket::onJsonApiMessage); + Q_EMIT fireUiDominationRelease(); +} + + +void UIPlugInWebSocket::onTextMessageReceived(const QString& pMessage) +{ + if (mConnection) + { + mJsonApi->doMessageProcessing(pMessage.toUtf8()); + } +} + + +void UIPlugInWebSocket::onJsonApiMessage(const QByteArray& pMessage) +{ + if (mConnection) + { + mConnection->sendTextMessage(QString::fromUtf8(pMessage)); + } +} + + +void UIPlugInWebSocket::doShutdown() +{ + if (mConnection) + { + mConnection->close(QWebSocketProtocol::CloseCodeGoingAway); + } + + mHttpServer.reset(); +} diff --git a/src/ui/websocket/UIPlugInWebSocket.h b/src/ui/websocket/UIPlugInWebSocket.h new file mode 100644 index 0000000..9c23bf4 --- /dev/null +++ b/src/ui/websocket/UIPlugInWebSocket.h @@ -0,0 +1,56 @@ +/*! + * \brief UIPlugIn implementation of the Websocket. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "HttpRequest.h" +#include "HttpServer.h" +#include "UIPlugIn.h" +#include "UIPlugInJsonApi.h" + +#include +#include +#include +#include +#include + +namespace governikus +{ + +class UIPlugInWebSocket + : public UIPlugIn +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "governikus.UIPlugIn" FILE "metadata.json") + Q_INTERFACES(governikus::UIPlugIn) + + private: + QSharedPointer mHttpServer; + QWebSocketServer mServer; + QScopedPointer mConnection; + QSharedPointer mRequest; + UIPlugInJsonApi* mJsonApi; + QSharedPointer mContext; + bool mUiDomination; + + private Q_SLOTS: + virtual void doShutdown() override; + virtual void onWorkflowStarted(QSharedPointer pContext) override; + virtual void onWorkflowFinished(QSharedPointer pContext) override; + virtual void onUiDomination(const UIPlugIn* pUi, const QString& pInformation, bool pAccepted) override; + virtual void onUiDominationReleased() override; + void onNewWebSocketRequest(const QSharedPointer& pRequest); + void onNewConnection(); + void onClientDisconnected(); + void onTextMessageReceived(const QString& pMessage); + void onJsonApiMessage(const QByteArray& pMessage); + + public: + UIPlugInWebSocket(); + virtual ~UIPlugInWebSocket() override; +}; + +} // namespace governikus diff --git a/src/websocket/metadata.json b/src/ui/websocket/metadata.json similarity index 100% rename from src/websocket/metadata.json rename to src/ui/websocket/metadata.json diff --git a/src/ui/widget/AboutDialog.cpp b/src/ui/widget/AboutDialog.cpp new file mode 100644 index 0000000..3d2a805 --- /dev/null +++ b/src/ui/widget/AboutDialog.cpp @@ -0,0 +1,94 @@ +/*! + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "AboutDialog.h" +#include "ui_AboutDialog.h" + +#include "AppSettings.h" +#include "BuildHelper.h" +#include "SecureStorage.h" +#include "VersionNumber.h" + +using namespace governikus; + + +AboutDialog::AboutDialog(QWidget* pParent) + : QDialog(pParent) + , mUi(new Ui::AboutDialog) +{ + mUi->setupUi(this); + + setWindowFlags(Qt::Dialog | Qt::WindowTitleHint | Qt::WindowCloseButtonHint); + layout()->setSizeConstraint(QLayout::SetFixedSize); // For platform != Windows: Disable maximize button + setWindowModality(Qt::WindowModal); // For platform == macOS: Make dialog slide in from the top + + const SecureStorage& storage = SecureStorage::getInstance(); + const auto& url = VersionNumber::getApplicationVersion().isDeveloperVersion() ? storage.getAppcastBetaUpdateUrl() : storage.getAppcastUpdateUrl(); + const auto& releaseNotes = url.adjusted(QUrl::RemoveFilename).toString() + QStringLiteral("ReleaseNotes.html"); + + setWindowTitle(tr("About %1 - %2").arg(QCoreApplication::applicationName(), QCoreApplication::organizationName())); + + mUi->lblFurtherInformation->setText(QStringLiteral("%1: https://www.ausweisapp.bund.de/") + .arg(tr("Further information"))); + + mUi->lblReleaseNotes->setText(tr("The current release notes can be found %1 here.%2") + .arg(QStringLiteral("").arg(releaseNotes), QStringLiteral(""))); + + mUi->lblVersion->setText(QStringLiteral("%1: %2").arg(tr("Version"), QApplication::applicationVersion())); + + mUi->lblDeveloperModeWarning->setText(QStringLiteral("

%1

") + .arg(tr("The developer mode is aimed at integrators / developers for new service applications." + " For this reason, the developer mode works only in the test PKI." + " By activating the developer mode, some safety tests are deactivated." + " This means that the authentication process continues although the AusweisApp2 would usually abort the process with an error message when used in normal operation mode." + " Information on the disregarded error in the developer mode is displayed in the attached window below the AusweisApp2."))); + + const QIcon icon = windowIcon(); + const QSize size = icon.actualSize(QSize(64, 64)); + mUi->imgAusweisApp2->setPixmap(icon.pixmap(size)); + + connect(mUi->btnOkay, &QPushButton::clicked, this, &QDialog::accept); + connect(this, &QDialog::accepted, this, &AboutDialog::onAccept); + + mUi->chkbDeveloperMode->setCheckState(Env::getSingleton()->getGeneralSettings().isDeveloperMode() ? Qt::Checked : Qt::Unchecked); + + connect(mUi->chkbDeveloperMode, &QCheckBox::stateChanged, this, &AboutDialog::onCheckboxStateChanged); + onCheckboxStateChanged(); +} + + +AboutDialog::~AboutDialog() +{ +} + + +void AboutDialog::onCheckboxStateChanged() +{ + const bool developerModeActivated = mUi->chkbDeveloperMode->checkState() == Qt::Checked; + mUi->lblDeveloperModeWarning->setVisible(developerModeActivated); + resize(minimumSize()); + adjustSize(); +} + + +void AboutDialog::onAccept() +{ + const bool developerModeActivated = mUi->chkbDeveloperMode->checkState() == Qt::Checked; + GeneralSettings& generalSettings = Env::getSingleton()->getGeneralSettings(); + if (generalSettings.isDeveloperMode() != developerModeActivated) + { + generalSettings.setDeveloperMode(developerModeActivated); + generalSettings.save(); + } +} + + +void AboutDialog::changeEvent(QEvent* pEvent) +{ + if (pEvent->type() == QEvent::LanguageChange) + { + mUi->retranslateUi(this); + } + QWidget::changeEvent(pEvent); +} diff --git a/src/ui/widget/AboutDialog.h b/src/ui/widget/AboutDialog.h new file mode 100644 index 0000000..beeebb9 --- /dev/null +++ b/src/ui/widget/AboutDialog.h @@ -0,0 +1,40 @@ +/*! + * \brief Dialog to display information about the application + * + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include + +namespace Ui +{ +class AboutDialog; +} // namespace Ui + +namespace governikus +{ + +class AboutDialog + : public QDialog +{ + Q_OBJECT + + private: + QScopedPointer mUi; + + private Q_SLOTS: + void onCheckboxStateChanged(); + void onAccept(); + + protected: + virtual void changeEvent(QEvent* pEvent) override; + + public: + AboutDialog(QWidget* pParent = nullptr); + virtual ~AboutDialog() override; +}; + +} // namespace governikus diff --git a/src/widget/AboutDialog.ui b/src/ui/widget/AboutDialog.ui similarity index 100% rename from src/widget/AboutDialog.ui rename to src/ui/widget/AboutDialog.ui diff --git a/src/ui/widget/AppQtGui.cpp b/src/ui/widget/AppQtGui.cpp new file mode 100644 index 0000000..b436aa1 --- /dev/null +++ b/src/ui/widget/AppQtGui.cpp @@ -0,0 +1,585 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "AppQtGui.h" + +#include "AppSettings.h" +#include "CredentialDialog.h" +#include "HelpAction.h" +#include "NetworkManager.h" +#include "PlatformTools.h" +#include "ReaderManager.h" +#include "RemoteClient.h" +#include "Service.h" +#include "workflow/WorkflowGui.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef QT_NO_NETWORKPROXY + #include + #include +#endif + + +using namespace governikus; + + +Q_DECLARE_LOGGING_CATEGORY(gui) + + +AppQtGui::AppQtGui() + : QObject() + , mMainWidget(new AppQtMainWidget()) + , mIcon(QStringLiteral(":/images/npa.svg")) + , mTrayIcon() + , mActiveWorkflowUi() + , mSetupAssistantGui(nullptr) + , mDiagnosisGui(nullptr) + , mUpdateInfo(new QMessageBox(mMainWidget)) + , mCertificateInfo(new QMessageBox(mMainWidget)) + , mLockedInfo(new QMessageBox(mMainWidget)) + , mUpdateWindow(new UpdateWindow(mMainWidget)) + , mAggressiveToForeground(false) +{ + loadStyleSheet(); + + mMainWidget->setWindowIcon(mIcon); + connect(mMainWidget, &AppQtMainWidget::fireCloseActiveDialogs, this, &AppQtGui::onCloseActiveDialogs); + + connect(&mTrayIcon, &TrayIcon::fireShow, this, [this] { + AppQtGui::show(); + }); + connect(&mTrayIcon, &TrayIcon::fireQuit, this, &AppQtGui::quitApplicationRequested); + + mUpdateInfo->setWindowTitle(QApplication::applicationName() + QStringLiteral(" - ") + tr("Updates")); + mUpdateInfo->setWindowIcon(mIcon); + mUpdateInfo->setWindowModality(Qt::WindowModal); + mUpdateInfo->setStandardButtons(QMessageBox::Ok); + mUpdateInfo->button(QMessageBox::Ok)->setFocus(); + connect(mUpdateWindow, &UpdateWindow::fireShowUpdateDialog, this, + [this](QMessageBox::Icon pIcon, const QString& pMsg) + { + mUpdateInfo->setIcon(pIcon); + mUpdateInfo->setText(pMsg); + mUpdateInfo->exec(); + }); + + mCertificateInfo->setWindowModality(Qt::ApplicationModal); + mCertificateInfo->setWindowFlags(mCertificateInfo->windowFlags() & ~Qt::WindowContextHelpButtonHint); + mCertificateInfo->setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("Information")); + mCertificateInfo->setIconPixmap(mMainWidget->windowIcon().pixmap(QSize(48, 48))); + mCertificateInfo->setStandardButtons(QMessageBox::Ok); + mCertificateInfo->button(QMessageBox::Ok)->setFocus(); + + mLockedInfo->setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("SDK")); + mLockedInfo->setWindowFlags(mLockedInfo->windowFlags() & ~Qt::WindowCloseButtonHint& ~Qt::WindowContextHelpButtonHint& ~Qt::WindowMinMaxButtonsHint); + mLockedInfo->setWindowIcon(mIcon); + mLockedInfo->setWindowModality(Qt::WindowModal); + mLockedInfo->setIcon(QMessageBox::Information); + mLockedInfo->setText(tr("Another application uses AusweisApp2.")); + mLockedInfo->setStandardButtons(QMessageBox::NoButton); + + Service* service = Env::getSingleton(); + connect(service, &Service::fireAppUpdateFinished, this, &AppQtGui::onAppUpdateReady); + connect(service, &Service::fireUpdateScheduled, this, &AppQtGui::onUpdateScheduled); +} + + +AppQtGui::~AppQtGui() +{ + delete mUpdateInfo; + delete mCertificateInfo; + delete mLockedInfo; + delete mMainWidget; +} + + +void AppQtGui::init() +{ + connect(mMainWidget, &AppQtMainWidget::fireChangePinRequested, this, &AppQtGui::fireChangePinRequested); + connect(mMainWidget, &AppQtMainWidget::fireSetupAssistantWizardRequest, this, &AppQtGui::onSetupAssistantWizardRequest); + connect(mMainWidget, &AppQtMainWidget::fireDiagnosisRequested, this, &AppQtGui::onDiagnosisRequested); + connect(mMainWidget, &AppQtMainWidget::fireCloseWindowRequested, this, &AppQtGui::onCloseWindowRequested); + connect(mMainWidget, &AppQtMainWidget::fireSelfAuthenticationRequested, this, &AppQtGui::selfAuthenticationRequested); + connect(mMainWidget, &AppQtMainWidget::fireQuitApplicationRequested, this, &AppQtGui::quitApplicationRequested); + connect(mMainWidget, &AppQtMainWidget::fireChangeHighContrast, this, &AppQtGui::onChangeHighContrast); +} + + +void AppQtGui::onApplicationStarted() +{ + mTrayIcon.create(); + + if (!QSystemTrayIcon::isSystemTrayAvailable() + || Env::getSingleton()->getGeneralSettings().isShowSetupAssistant() + || Env::getSingleton()->getGeneralSettings().isDeveloperMode()) + { + QMetaObject::invokeMethod(this, [this] { + show(); + }, Qt::QueuedConnection); + } + + if (Env::getSingleton()->getGeneralSettings().isShowSetupAssistant()) + { + mMainWidget->setSelectedTab(nullptr); + QMetaObject::invokeMethod(this, &AppQtGui::onSetupAssistantWizardRequest, Qt::QueuedConnection); + + // just show the setup assistant once + Env::getSingleton()->getGeneralSettings().setShowSetupAssistant(false); + Env::getSingleton()->getGeneralSettings().save(); + } + + if (Env::getSingleton()->getGeneralSettings().isDeveloperMode()) + { + QMetaObject::invokeMethod(this, &AppQtGui::onDeveloperModeQuestion, Qt::QueuedConnection); + } + + const auto& remoteClient = Env::getSingleton(); + connect(remoteClient, &RemoteClient::fireCertificateRemoved, this, &AppQtGui::onCertificateRemoved); +} + + +QSharedPointer AppQtGui::createWorkflowAuthenticateUi(const QSharedPointer& pContext) +{ + return QSharedPointer::create(pContext, mMainWidget); +} + + +QSharedPointer AppQtGui::createWorkflowChangePinUi(const QSharedPointer& pContext) +{ + return QSharedPointer::create(pContext, mMainWidget); +} + + +QSharedPointer AppQtGui::createWorkflowSelfInfoUi(const QSharedPointer& pContext) +{ + return QSharedPointer::create(pContext, mMainWidget); +} + + +void AppQtGui::activateWorkflowUi(QSharedPointer pWorkflowUi, bool pAllowHideAfterWorkflow) +{ + if (pWorkflowUi) + { + mActiveWorkflowUi = pWorkflowUi; + mActiveWorkflowUi->activate(); + connect(this, &AppQtGui::fireCloseActiveDialogs, mActiveWorkflowUi.data(), &WorkflowGui::onCloseActiveDialogs); + } + + mMainWidget->activateMenuBarItems(false); + closeDialogs(); + +#ifdef Q_OS_WIN + mAggressiveToForeground = mActiveWorkflowUi.objectCast(); +#endif + bool hideAfterWorkflow = pAllowHideAfterWorkflow + && mActiveWorkflowUi.objectCast() + && Env::getSingleton()->getGeneralSettings().isAutoCloseWindowAfterAuthentication(); + mMainWidget->setHideWindowAfterWorkflow(hideAfterWorkflow); + show(); +} + + +void AppQtGui::deactivateCurrentWorkflowUi() +{ + if (mMainWidget->isHideWindowAfterWorkflow()) + { + PlatformTools::hideFromTaskbar(); + mMainWidget->hideWithoutConfirmation(); + } + mMainWidget->activateMenuBarItems(true); + + if (mActiveWorkflowUi) + { + disconnect(this, &AppQtGui::fireCloseActiveDialogs, mActiveWorkflowUi.data(), &WorkflowGui::onCloseActiveDialogs); + mActiveWorkflowUi->deactivate(); + mActiveWorkflowUi.clear(); + } +} + + +void AppQtGui::onShowUserInformation(const QString& pInformationMessage) +{ + QMessageBox msgBox(mMainWidget); + msgBox.setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("Information")); + msgBox.setIcon(QMessageBox::Information); + msgBox.setText(pInformationMessage); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.button(QMessageBox::Ok)->setFocus(); + msgBox.exec(); +} + + +void AppQtGui::onSetupAssistantWizardRequest() +{ + Env::getSingleton()->runUpdateIfNeeded(); + + if (!mSetupAssistantGui) + { + mSetupAssistantGui = new SetupAssistantGui(mMainWidget); + connect(mSetupAssistantGui, &SetupAssistantGui::fireChangePinButtonClicked, mMainWidget, &AppQtMainWidget::onChangePinButtonClicked); + } + + bool stopRemoteScan = false; + if (!mMainWidget->remoteScanRunning()) + { + Env::getSingleton()->startScanAll(false); + stopRemoteScan = true; + } + mSetupAssistantGui->activate(); + if (stopRemoteScan) + { + Env::getSingleton()->stopScanAll(); + } +} + + +void AppQtGui::onDeveloperModeQuestion() +{ + QMessageBox msgBox(mMainWidget); + msgBox.setWindowModality(Qt::WindowModal); + msgBox.setWindowFlags(msgBox.windowFlags() & ~Qt::WindowContextHelpButtonHint); + msgBox.setIconPixmap(mMainWidget->windowIcon().pixmap(QSize(48, 48))); + msgBox.setText(tr("The developer mode is enabled.")); + msgBox.setInformativeText(tr("Do you want to disable the developer mode?")); + msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + msgBox.setDefaultButton(QMessageBox::Yes); + msgBox.button(QMessageBox::Yes)->setFocus(); + + if (msgBox.exec() == QMessageBox::Yes) + { + Env::getSingleton()->getGeneralSettings().setDeveloperMode(false); + Env::getSingleton()->getGeneralSettings().save(); + } +} + + +void AppQtGui::onDiagnosisRequested() +{ + if (!mDiagnosisGui) + { + mDiagnosisGui = new DiagnosisGui(mMainWidget); + } + mDiagnosisGui->activate(); +} + + +bool AppQtGui::askChangeTransportPinNow() +{ + GeneralSettings& settings = Env::getSingleton()->getGeneralSettings(); + if (!settings.isTransportPinReminder()) + { + return false; + } + settings.setTransportPinReminder(false); + settings.save(); + + show(UiModule::PINMANAGEMENT); + closeDialogs(); + + QMessageBox messageBox(mMainWidget); + messageBox.setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("Information")); + messageBox.setWindowModality(Qt::WindowModal); + messageBox.setWindowFlags(messageBox.windowFlags() & ~Qt::WindowContextHelpButtonHint); + messageBox.setText(tr("Did you change the transport PIN already?

Prior to the first use of the online identification function you have to replace the transport PIN by an individual 6-digit PIN. Online identification with transport PIN is not possible.")); + messageBox.setStandardButtons(QMessageBox::Yes); + messageBox.button(QMessageBox::Yes)->setFocus(); + auto changePinButton = messageBox.addButton(tr("No, change transport PIN now"), QMessageBox::NoRole); + messageBox.exec(); + + if (messageBox.clickedButton() != changePinButton) + { + show(UiModule::IDENTIFY); + return false; + } + + return true; +} + + +bool AppQtGui::eventFilter(QObject* /*pObject*/, QEvent* pEvent) +{ + if (pEvent->type() == QEvent::KeyPress) + { + QKeyEvent* keyEvent = static_cast(pEvent); + if (keyEvent->key() == Qt::Key_F1) + { + HelpAction::openContextHelp(); + return true; + } + + if (keyEvent->key() == Qt::BackButton || keyEvent->key() == Qt::Key_Back) + { + Q_EMIT quitApplicationRequested(); + return true; + } + } + return false; +} + + +void AppQtGui::loadStyleSheet() +{ + const auto& styleSheetName = QStringLiteral(":/stylesheets/desktop.qss"); + qCDebug(gui) << "loading style sheet" << styleSheetName; + + QFile file(styleSheetName); + if (!file.open(QIODevice::ReadOnly)) + { + qCWarning(gui) << "Failed to read style sheet"; + return; + } + + qApp->setStyleSheet(QString::fromLatin1(file.readAll())); +} + + +void AppQtGui::onChangeHighContrast(bool* pHighContrastOn) +{ + if (*pHighContrastOn) + { + qApp->setStyleSheet(QString()); + } + else + { + loadStyleSheet(); + } +} + + +void AppQtGui::closeDialogs() +{ + if (mSetupAssistantGui) + { + mSetupAssistantGui->deactivate(); + } + if (mDiagnosisGui) + { + mDiagnosisGui->deactivate(); + } +} + + +void AppQtGui::onCloseWindowRequested(bool* pDoClose) +{ + if (mActiveWorkflowUi == nullptr && mMainWidget->isRemindUserToClose()) + { + QMessageBox messageBox(mMainWidget); + messageBox.installEventFilter(this); + messageBox.setWindowTitle(QApplication::applicationName() + QStringLiteral(" - ") + tr("Information")); + messageBox.setWindowModality(Qt::ApplicationModal); + messageBox.setIcon(QMessageBox::Information); + messageBox.setWindowFlags(messageBox.windowFlags() & ~Qt::WindowContextHelpButtonHint); + messageBox.setText(tr("The user interface of the %1 is closed.").arg(QApplication::applicationName())); + messageBox.setInformativeText(tr("The program remains available via the icon in the system tray. Click on the %1 icon to reopen the user interface.").arg(QApplication::applicationName())); + messageBox.setCheckBox(new QCheckBox(tr("Do not show this dialog again."))); + messageBox.setStandardButtons(QMessageBox::Ok); + messageBox.button(QMessageBox::Ok)->setFocus(); + connect(this, &AppQtGui::fireCloseActiveDialogs, &messageBox, &QMessageBox::reject); + messageBox.exec(); + + Q_EMIT fireCloseReminderFinished(messageBox.checkBox()->isChecked()); + } + mMainWidget->onCloseWindowRequested(); + + if (mActiveWorkflowUi == nullptr) + { + *pDoClose = true; + } + else + { + connect(this, &AppQtGui::fireCloseActiveDialogs, mActiveWorkflowUi.data(), &WorkflowGui::fireCloseActiveDialogs); + if ((*pDoClose = mActiveWorkflowUi->verifyAbortWorkflow())) + { + Q_EMIT mActiveWorkflowUi->fireUserCancelled(); + } + } + + if (*pDoClose) + { + PlatformTools::hideFromTaskbar(); + } +} + + +void AppQtGui::onCloseActiveDialogs() +{ + if (mSetupAssistantGui != nullptr) + { + mSetupAssistantGui->deactivate(); + } + + Q_EMIT fireCloseActiveDialogs(); +} + + +#ifndef QT_NO_NETWORKPROXY +void AppQtGui::onProxyAuthenticationRequired(const QNetworkProxy& pProxy, QAuthenticator* pAuthenticator) +{ + CredentialDialog dialog(mMainWidget); + dialog.setUser(pProxy.user()); + + if (dialog.exec() == QDialog::Accepted) + { + pAuthenticator->setUser(dialog.getUser()); + pAuthenticator->setPassword(dialog.getPassword()); + } +} + + +#endif + + +void AppQtGui::show(UiModule pModule) +{ + if (!mActiveWorkflowUi.isNull()) + { + pModule = UiModule::CURRENT; + } + + switch (pModule) + { + case UiModule::PINMANAGEMENT: + mMainWidget->switchToGuiModule(GuiModule::PIN_SETTINGS); + break; + + case UiModule::SETTINGS: + mMainWidget->switchToGuiModule(GuiModule::GENERAL_SETTINGS); + break; + + case UiModule::IDENTIFY: + mMainWidget->switchToGuiModule(GuiModule::IDENTIFY); + break; + + case UiModule::DEFAULT: + mMainWidget->switchToGuiModule(GuiModule::START_PAGE); + break; + + case UiModule::CURRENT: + // don't switch the module, just show the current one + break; + } + + if (mMainWidget->isMinimized()) + { + mMainWidget->showNormal(); + } + + // Ensure the window's minimumSizeHint is respected (work-around for a Windows Qt bug). + if (!mMainWidget->isMaximized()) + { + QSize size = mMainWidget->size(); + size = size.expandedTo(mMainWidget->minimumSizeHint()); + mMainWidget->resize(size); + } + +#ifdef Q_OS_WIN + if (mAggressiveToForeground) + { + // Changing the window flags seems to be the only way to + // bring the window to the foreground if it is not minimized. + const Qt::WindowFlags flags = mMainWidget->windowFlags(); + mMainWidget->setWindowFlags(flags | Qt::WindowStaysOnTopHint); + mMainWidget->show(); + mMainWidget->setWindowFlags(flags); + + mAggressiveToForeground = false; + } +#endif + + mMainWidget->show(); + mMainWidget->activateWindow(); + + // Work-around for the window not being brought to the foreground. Invoke + // activateWindow() again after the events currently pending in the event + // queue have been processed. This appears to do the job. Note that the + // first activateWindow() above is apparently still necessary. + QCoreApplication::processEvents(); + mMainWidget->activateWindow(); + + PlatformTools::restoreToTaskbar(); + + Env::getSingleton()->runUpdateIfNeeded(); +} + + +void AppQtGui::hideWithoutConfirmation() +{ + mMainWidget->hideWithoutConfirmation(); +} + + +void AppQtGui::setEnabled(bool pEnable, const QString& pLockedInfoText) +{ + mMainWidget->setEnabled(pEnable); + if (pEnable) + { + mLockedInfo->hide(); + } + else + { + const auto& txt = pLockedInfoText.isEmpty() ? QString() : tr("Application: %1").arg(pLockedInfoText); + mLockedInfo->setInformativeText(txt); + mLockedInfo->open(); + mLockedInfo->setEnabled(true); + } +} + + +bool AppQtGui::isEnabled() const +{ + return mMainWidget->isEnabled(); +} + + +void AppQtGui::onAppUpdateReady(bool pSuccess, const GlobalStatus& pError) +{ + if (pError.isError()) + { + mUpdateInfo->setIcon(QMessageBox::Critical); + mUpdateInfo->setText(pError.toErrorDescription()); + mUpdateInfo->exec(); + } + else if (pSuccess) + { + mUpdateWindow->fillData(); + mUpdateWindow->show(); + } + else + { + mUpdateInfo->setIcon(QMessageBox::Information); + mUpdateInfo->setText(tr("Your software is up to date.")); + mUpdateInfo->exec(); + } +} + + +void AppQtGui::onUpdateScheduled() +{ + if (!mMainWidget->isHidden()) + { + Env::getSingleton()->runUpdateIfNeeded(); + } +} + + +void AppQtGui::onCertificateRemoved(QString pDeviceName) +{ + mCertificateInfo->setText(tr("The device \"%1\" was unpaired because it does not react to connection attempts. Retry the pairing process if you want to use this device to authenticate yourself.").arg(pDeviceName)); + mCertificateInfo->show(); +} + + +void AppQtGui::shutdown() +{ + Q_EMIT fireCloseActiveDialogs(); + mTrayIcon.hide(); +} diff --git a/src/ui/widget/AppQtGui.h b/src/ui/widget/AppQtGui.h new file mode 100644 index 0000000..eb1698d --- /dev/null +++ b/src/ui/widget/AppQtGui.h @@ -0,0 +1,96 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "ActivationHandler.h" +#include "DiagnosisGui.h" +#include "GlobalStatus.h" +#include "SetupAssistantGui.h" +#include "TrayIcon.h" +#include "UpdateWindow.h" +#include "workflow/WorkflowAuthenticateQtGui.h" +#include "workflow/WorkflowChangePinQtGui.h" +#include "workflow/WorkflowSelfInfoQtGui.h" + +#include + + +namespace governikus +{ + +class AppQtGui + : public QObject +{ + Q_OBJECT + + public: + AppQtGui(); + virtual ~AppQtGui() override; + + virtual void init(); + + virtual QSharedPointer createWorkflowAuthenticateUi(const QSharedPointer& pContext); + virtual QSharedPointer createWorkflowChangePinUi(const QSharedPointer& pContext); + virtual QSharedPointer createWorkflowSelfInfoUi(const QSharedPointer& pContext); + + virtual void activateWorkflowUi(QSharedPointer pWorkflowUi, bool pAllowHideAfterWorkflow = true); + virtual void deactivateCurrentWorkflowUi(); + + virtual bool askChangeTransportPinNow(); + + void shutdown(); + void hideWithoutConfirmation(); + void setEnabled(bool pEnable, const QString& pLockedInfoText = QString()); + bool isEnabled() const; + + protected: + virtual bool eventFilter(QObject* pObject, QEvent* pEvent) override; + + private: + void loadStyleSheet(); + void closeDialogs(); + + public Q_SLOTS: + virtual void show(UiModule pModule = UiModule::CURRENT); + virtual void onApplicationStarted(); + virtual void onShowUserInformation(const QString& pAppName); +#ifndef QT_NO_NETWORKPROXY + void onProxyAuthenticationRequired(const QNetworkProxy& pProxy, QAuthenticator* pAuthenticator); +#endif + + private Q_SLOTS: + void onCloseWindowRequested(bool* pDoClose); + void onCloseActiveDialogs(); + void onChangeHighContrast(bool* pHighContrastOn); + void onSetupAssistantWizardRequest(); + void onDeveloperModeQuestion(); + void onDiagnosisRequested(); + void onAppUpdateReady(bool pSuccess, const GlobalStatus& pError); + void onUpdateScheduled(); + void onCertificateRemoved(QString pDeviceName); + + private: + AppQtMainWidget* mMainWidget; + QIcon mIcon; + TrayIcon mTrayIcon; + QSharedPointer mActiveWorkflowUi; + SetupAssistantGui* mSetupAssistantGui; + DiagnosisGui* mDiagnosisGui; + QMessageBox* mUpdateInfo; + QMessageBox* mCertificateInfo; + QMessageBox* mLockedInfo; + UpdateWindow* mUpdateWindow; + bool mAggressiveToForeground; + + Q_SIGNALS: + void fireCloseReminderFinished(bool pDontRemindAgain); + + void fireChangePinRequested(); + void selfAuthenticationRequested(); + void quitApplicationRequested(); + void fireCloseActiveDialogs(); +}; + +} // namespace governikus diff --git a/src/ui/widget/AppQtMainWidget.cpp b/src/ui/widget/AppQtMainWidget.cpp new file mode 100644 index 0000000..223f7e4 --- /dev/null +++ b/src/ui/widget/AppQtMainWidget.cpp @@ -0,0 +1,609 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "AppQtMainWidget.h" +#include "ui_AppQtMainWidget.h" + +#include "AboutDialog.h" +#include "AppSettings.h" +#include "BuildHelper.h" +#include "generic/ExclusiveButtonGroup.h" +#include "HelpAction.h" +#include "LanguageLoader.h" +#include "LogHandler.h" +#include "ReaderDetector.h" +#include "step/AuthenticateStepsWidget.h" +#include "SetupAssistantWizard.h" +#include "VersionNumber.h" +#include "workflow/WorkflowQtWidget.h" + +#include +#include +#include +#include +#include +#include + +#ifdef Q_OS_WIN +#include +#endif + +using namespace governikus; + +Q_DECLARE_LOGGING_CATEGORY(gui) + +AppQtMainWidget::AppQtMainWidget() + : QMainWindow() + , mUi(new Ui::AppQtMainWidget()) + , mTabButton2Page() + , mTabAction2Button() + , mAuthenticationWorkflowWidget(nullptr) + , mSelectedPushButton(nullptr) + , mSelectedPushButtonBeforeWorkflow(nullptr) + , mSelectedPagesBeforeWorkflow() + , mHideWindowAfterWorkflow(false) + , mLogFilesDialog() + , mClosingDialogsPending(false) + , mCloseWithoutConfirmation(false) +{ + mUi->setupUi(this); + mUi->englishButton->setBackgroundRole(QPalette::NoRole); + mUi->germanButton->setBackgroundRole(QPalette::NoRole); + + refreshLanguageButton(); + + mUi->appLogoWidget->setAttribute(Qt::WA_TransparentForMouseEvents); + + QStackedLayout* centralStackLayout = qobject_cast(mUi->centralWidget->layout()); + centralStackLayout->setStackingMode(QStackedLayout::StackAll); + centralStackLayout->setCurrentIndex(1); + + QSpacerItem* spacer = new QSpacerItem(1, mUi->logoLabel->sizeHint().height(), QSizePolicy::Fixed, QSizePolicy::Ignored); + mUi->applicationGridLayout->addItem(spacer, mUi->applicationGridLayout->rowCount(), 0); + + mUi->providerPage->layout()->setMargin(20); + mUi->historyPage->layout()->setMargin(20); + + mAuthenticationWorkflowWidget = new WorkflowQtWidget(); + mUi->workflowPage->layout()->addWidget(mAuthenticationWorkflowWidget); + mAuthenticationWorkflowWidget->getStepWidgetArea()->layout()->addWidget(new AuthenticateStepsWidget); + + //menu file + connect(mUi->actionBeenden, &QAction::triggered, this, &AppQtMainWidget::fireQuitApplicationRequested); + connect(mUi->actionSettings, &QAction::triggered, this, &AppQtMainWidget::onSettingsButtonClicked); + + //menu PIN management + connect(mUi->actionChangePin, &QAction::triggered, this, &AppQtMainWidget::onChangePinButtonClicked); + + //help + connect(mUi->actionSetupAssistant, &QAction::triggered, this, &AppQtMainWidget::fireSetupAssistantWizardRequest); + connect(mUi->actionDiagnosis, &QAction::triggered, this, &AppQtMainWidget::fireDiagnosisRequested); + + connect(mUi->actionShowProtocol, &QAction::triggered, this, &AppQtMainWidget::onOpenLoggingFileButtonClicked); + connect(mUi->actionSaveProtocol, &QAction::triggered, this, &AppQtMainWidget::onSaveLoggingFileButtonClicked); + connect(mUi->actionInfo, &QAction::triggered, this, &AppQtMainWidget::onAboutActionClicked); + connect(mUi->actionSendError, &QAction::triggered, this, &AppQtMainWidget::onSendErrorActionClicked); + connect(mUi->actionEvaluate, &QAction::triggered, this, &AppQtMainWidget::onEvaluateActionClicked); + connect(mUi->actionQuestion, &QAction::triggered, this, &AppQtMainWidget::onQuestionActionClicked); + connect(mUi->actionHelp, &QAction::triggered, this, &AppQtMainWidget::onContentActionClicked); + + connect(mUi->identifyPage, &SelfInformationWidget::selfAuthenticationRequested, this, &AppQtMainWidget::fireSelfAuthenticationRequested); + connect(mUi->settingsPage, &SettingsWidget::changePinRequested, this, &AppQtMainWidget::fireChangePinRequested); + connect(mUi->settingsPage, &SettingsWidget::diagnosisRequested, this, &AppQtMainWidget::fireDiagnosisRequested); + connect(mUi->settingsPage, &SettingsWidget::settingsDone, this, &AppQtMainWidget::onSettingsDone); + + connect(mUi->germanButton, &QPushButton::clicked, this, [&]() + { + setLanguage(QLocale::German); + }); + connect(mUi->englishButton, &QPushButton::clicked, this, [&]() + { + setLanguage(QLocale::English); + }); + + //NavigationButtons + mUi->startPushButton->setVisible(false); + mUi->mainTabList->addButton(mUi->startPushButton); + mTabButton2Page.insert(mUi->startPushButton, mUi->applicationPage); + + struct ButtonInfo + { + QAbstractButton* mButton; + QWidget* mPage; + QAction* mAction; + const char* mOnIcon; + const char* mOffIcon; + }; + ButtonInfo buttonInfos[] = { + {mUi->ausweisenToolButton, mUi->identifyPage, mUi->actionAusweisen, ":/images/bt_1.svg", ":/images/bt_1b.svg"}, + {mUi->providerToolButton, mUi->providerPage, mUi->actionProvider, ":/images/bt_2.svg", ":/images/bt_2b.svg"}, + {mUi->historyToolButton, mUi->historyPage, mUi->actionHistory, ":/images/bt_3.svg", ":/images/bt_3b.svg"}, + {mUi->settingsToolButton, mUi->settingsPage, mUi->actionSettings, ":/images/bt_4.svg", ":/images/bt_4b.svg"}, + }; + + for (size_t i = 0; i < sizeof(buttonInfos) / sizeof(buttonInfos[0]); ++i) + { + const ButtonInfo& buttonInfo = buttonInfos[i]; + QIcon icon; + icon.addPixmap(QPixmap(QString::fromLatin1(buttonInfo.mOnIcon)), QIcon::Normal, QIcon::Off); + icon.addPixmap(QPixmap(QString::fromLatin1(buttonInfo.mOffIcon)), QIcon::Normal, QIcon::On); + + buttonInfo.mButton->setIcon(icon); + + connect(buttonInfo.mAction, &QAction::triggered, this, &AppQtMainWidget::onTabActionTriggered); + + mUi->mainTabList->addButton(buttonInfo.mButton); + mTabButton2Page.insert(buttonInfo.mButton, buttonInfo.mPage); + mTabAction2Button.insert(buttonInfo.mAction, buttonInfo.mButton); + } + + connect(mUi->mainTabList, &TabButtonGroup::buttonToggled, this, &AppQtMainWidget::onTabButtonToggled); + mUi->stackedWidget->setCurrentIndex(0); + + if (VersionNumber::getApplicationVersion().isDeveloperVersion()) + { + setWindowTitle(windowTitle() + QStringLiteral(" - Beta - ") + QCoreApplication::applicationVersion()); + mUi->betaLabel->setVisible(true); + } + else + { + mUi->betaLabel->setVisible(false); + } + +#ifdef Q_OS_WIN + // we need to call create() explicitly because Windows needs a handle to fire WM_ENDSESSION! + // Since we start as a systemtray only we don't have a correct handle until we call show() + create(); +#endif + + connect(this, &AppQtMainWidget::fireAskUserToConfirmClosing, this, &AppQtMainWidget::onAskUserToConfirmClosing, Qt::QueuedConnection); +} + + +AppQtMainWidget::~AppQtMainWidget() +{ +} + + +void AppQtMainWidget::showEvent(QShowEvent* pEvent) +{ + Q_UNUSED(pEvent); + mCloseWithoutConfirmation = false; +} + + +void AppQtMainWidget::changeEvent(QEvent* pEvent) +{ + if (pEvent->type() == QEvent::LanguageChange) + { + mUi->retranslateUi(this); + refreshLanguageButton(); + } + + if (pEvent->type() == QEvent::PaletteChange) // QEvent::StyleChange is called, too + { +#if defined(Q_OS_WIN) + HIGHCONTRAST hc; + hc.cbSize = sizeof(hc); + + bool highContrastOn = false; + if (SystemParametersInfo(SPI_GETHIGHCONTRAST, sizeof(hc), &hc, FALSE) && (hc.dwFlags & HCF_HIGHCONTRASTON)) + { + qDebug() << "High contrast (SPI_GETHIGHCONTRAST) switched on."; + highContrastOn = true; + Q_EMIT fireChangeHighContrast(&highContrastOn); + } + else + { + qDebug() << "High contrast (SPI_GETHIGHCONTRAST) switched off."; + Q_EMIT fireChangeHighContrast(&highContrastOn); + } +#endif + } + QWidget::changeEvent(pEvent); +} + + +bool AppQtMainWidget::remoteScanRunning() const +{ + return mUi->settingsPage->remoteScanRunning(); +} + + +void AppQtMainWidget::workflowActivated(WorkflowWidgetParent pParent, const QString& /*pName*/) +{ + QAbstractButton* tabToolButton = nullptr; + QWidget* containingWidget = nullptr; + + // activate the correct tab and set the workflow page + switch (pParent) + { + case WorkflowWidgetParent::SelfAuthentication: + case WorkflowWidgetParent::Authentication: + tabToolButton = mUi->ausweisenToolButton; + containingWidget = mUi->workflowPage; + break; + + case WorkflowWidgetParent::SettingsChangePin: + tabToolButton = mUi->settingsToolButton; + containingWidget = nullptr; + break; + } + + mSelectedPushButtonBeforeWorkflow = mSelectedPushButton; + + setSelectedTab(tabToolButton); + + if (tabToolButton != nullptr) + { + const auto tabButtons = mTabButton2Page.keys(); + for (auto button : tabButtons) + { + if (button != tabToolButton) + { + button->setEnabled(false); + } + } + } + + // show the respective page in the widget stacks + while (containingWidget != nullptr) + { + QWidget* containerParent = containingWidget->parentWidget(); + if (QStackedWidget* stackedWidget = qobject_cast(containerParent)) + { + mSelectedPagesBeforeWorkflow += stackedWidget->currentWidget(); + stackedWidget->setCurrentWidget(containingWidget); + } + + containingWidget = containerParent; + } + + mUi->settingsPage->workflowStarted(); + mUi->mainTabList->setWorkflowActive(true); +} + + +void AppQtMainWidget::workflowDeactivated() +{ + const auto tabButtons = mTabButton2Page.keys(); + for (auto button : tabButtons) + { + button->setEnabled(true); + } + + for (auto widget : qAsConst(mSelectedPagesBeforeWorkflow)) + { + if (QStackedWidget* stackedWidget = qobject_cast(widget->parentWidget())) + { + stackedWidget->setCurrentWidget(widget); + } + } + mSelectedPagesBeforeWorkflow.clear(); + + mUi->settingsPage->workflowFinished(); + mUi->mainTabList->setWorkflowActive(false); + + // switch back to the tab selected before the workflow + if (mSelectedPushButtonBeforeWorkflow == nullptr) + { + return; + } + setSelectedTab(mSelectedPushButtonBeforeWorkflow); + mSelectedPushButtonBeforeWorkflow = nullptr; +} + + +void AppQtMainWidget::switchToGuiModule(GuiModule pModule) +{ + switch (pModule) + { + case GuiModule::START_PAGE: + setSelectedTab(mUi->startPushButton); + break; + + case GuiModule::IDENTIFY: + setSelectedTab(mUi->ausweisenToolButton); + break; + + case GuiModule::GENERAL_SETTINGS: + case GuiModule::PIN_SETTINGS: + setSelectedTab(mUi->settingsToolButton); + mUi->settingsPage->switchToGuiModule(pModule); + break; + } +} + + +void AppQtMainWidget::switchToPinSettingsAfterWorkflow() +{ + mHideWindowAfterWorkflow = false; + mSelectedPushButtonBeforeWorkflow = mUi->settingsToolButton; + + mSelectedPagesBeforeWorkflow.clear(); + + QWidget* containingWidget = mUi->stackedWidget->parentWidget(); + + while (containingWidget != nullptr) + { + QWidget* containerParent = containingWidget->parentWidget(); + if (qobject_cast(containerParent) != nullptr) + { + mSelectedPagesBeforeWorkflow += containingWidget; + } + + containingWidget = containerParent; + } + + mUi->settingsPage->switchToGuiModule(GuiModule::PIN_SETTINGS); +} + + +void AppQtMainWidget::hideWithoutConfirmation() +{ + if (mLogFilesDialog) + { + mLogFilesDialog->closeActiveDialogs(); + mLogFilesDialog->reject(); + } + + Q_EMIT fireCloseActiveDialogs(); + mCloseWithoutConfirmation = true; + close(); +} + + +void AppQtMainWidget::onAskUserToConfirmClosing() +{ + bool doClose = true; + Q_EMIT fireCloseWindowRequested(&doClose); + if (doClose) + { + mCloseWithoutConfirmation = true; + close(); + } + else + { + mClosingDialogsPending = false; + } +} + + +void AppQtMainWidget::closeEvent(QCloseEvent* pEvent) +{ + if (mCloseWithoutConfirmation) + { + pEvent->accept(); + mClosingDialogsPending = false; + } + else + { + if (!mClosingDialogsPending) + { + mClosingDialogsPending = true; + Q_EMIT fireAskUserToConfirmClosing(); + } + pEvent->ignore(); + } +} + + +void AppQtMainWidget::keyPressEvent(QKeyEvent* keyEvent) +{ + switch (keyEvent->key()) + { + case Qt::Key_F1: + onContentActionClicked(); + break; + + default: + break; + } +} + + +void AppQtMainWidget::setSelectedTab(QAbstractButton* pSelectedPushButton) +{ + if (pSelectedPushButton == nullptr) + { + pSelectedPushButton = mUi->settingsToolButton; + mUi->settingsPage->switchToGuiModule(GuiModule::GENERAL_SETTINGS); + } + if (mSelectedPushButton == mUi->settingsToolButton && mUi->settingsPage->isSettingsChanged()) + { + const auto tabButtons = mTabButton2Page.keys(); + for (auto button : tabButtons) + { + button->setChecked(button == mUi->settingsToolButton); + } + mUi->settingsPage->showSettingsChangedMessage(); + } + + mUi->appLogoWidget->setVisible(pSelectedPushButton == mUi->startPushButton); + + if (mSelectedPushButton) + { + mSelectedPushButton->clearFocus(); + } + mSelectedPushButton = pSelectedPushButton; + mSelectedPushButton->setChecked(true); + + mUi->stackedWidget->setCurrentWidget(mTabButton2Page.value(pSelectedPushButton)); +} + + +void AppQtMainWidget::activateWindow() +{ + QMainWindow::activateWindow(); + +#if defined(Q_OS_MACOS) + // Workaround. When switching from "BackgroundApplication" to "ForegroundApplication" + // on MacOS, it is a known problem that the menu bar of the previous active application + // stays visible, although the window has changed to the current application. As soon + // as the user clicks the menu, it magically transforms to the correct one. We therefore + // manually trigger an update. Neither update() nor repaint() of QMenuBar solve this problem. + QMenu menu; + mUi->menuBar->addAction(menu.menuAction()); + mUi->menuBar->removeAction(menu.menuAction()); +#endif +} + + +void AppQtMainWidget::onSettingsDone() +{ + setSelectedTab(mUi->startPushButton); +} + + +void AppQtMainWidget::onOpenLoggingFileButtonClicked() +{ + if (!mLogFilesDialog) + { + mLogFilesDialog = new LogFilesDialog(this); + } + + mLogFilesDialog->show(); +} + + +void AppQtMainWidget::onSaveLoggingFileButtonClicked() +{ + if (!mLogFilesDialog) + { + mLogFilesDialog = new LogFilesDialog(this); + } + + mLogFilesDialog->saveLogFile(this); +} + + +void AppQtMainWidget::onTabButtonToggled(QAbstractButton* pButton, bool pChecked) +{ + if (pChecked) + { + if (mTabButton2Page.contains(pButton) && mSelectedPushButton != pButton) + { + setSelectedTab(pButton); + } + } +} + + +void AppQtMainWidget::onTabActionTriggered() +{ + if (QAction* action = qobject_cast(sender())) + { + if (QAbstractButton* button = mTabAction2Button.value(action)) + { + setSelectedTab(button); + } + } +} + + +void AppQtMainWidget::onSettingsButtonClicked() +{ + setSelectedTab(mUi->settingsToolButton); + mUi->settingsPage->switchToGuiModule(GuiModule::GENERAL_SETTINGS); +} + + +void AppQtMainWidget::onChangePinButtonClicked() +{ + setSelectedTab(mUi->settingsToolButton); + mUi->settingsPage->switchToGuiModule(GuiModule::PIN_SETTINGS); +} + + +void AppQtMainWidget::onQuestionActionClicked() +{ + QString link = tr("https://www.ausweisapp.bund.de/en/service/haeufig-gestellte-fragen/"); + QDesktopServices::openUrl(QUrl(link)); +} + + +void AppQtMainWidget::onSendErrorActionClicked() +{ + QString link = tr("https://www.ausweisapp.bund.de/en/feedback/melden-sie-einen-fehler/"); + QDesktopServices::openUrl(QUrl(link)); +} + + +void AppQtMainWidget::onEvaluateActionClicked() +{ + QString link = tr("https://www.ausweisapp.bund.de/en/feedback/bewerten-sie-uns/"); + QDesktopServices::openUrl(QUrl(link)); +} + + +void AppQtMainWidget::onContentActionClicked() +{ + QString name = mUi->stackedWidget->widget(mUi->stackedWidget->currentIndex())->objectName(); + if (name.startsWith(QLatin1String("settingsPage"))) + { + SettingsWidget* settingsWidget = static_cast(mUi->stackedWidget->widget(mUi->stackedWidget->currentIndex())); + HelpAction::openContextHelp(settingsWidget->getActiveTabObjectName()); + } + else + { + HelpAction::openContextHelp(name); + } +} + + +void AppQtMainWidget::onAboutActionClicked() +{ + AboutDialog* dialog = new AboutDialog(this); + dialog->show(); +} + + +void AppQtMainWidget::setLanguage(QLocale::Language pLocale) +{ + GeneralSettings& generalSettings = Env::getSingleton()->getGeneralSettings(); + generalSettings.setLanguage(pLocale); + generalSettings.save(); +} + + +void AppQtMainWidget::refreshLanguageButton() +{ + const QString& selected = QStringLiteral("border-style: solid; border-width: 1px; border-color: grey; padding: 4px;"); + const QString& unselected = QStringLiteral("padding: 4px;"); + + const auto& locale = LanguageLoader::getInstance().getUsedLocale(); + if (locale == QLocale::English) + { + mUi->englishButton->setStyleSheet(selected); + mUi->germanButton->setStyleSheet(unselected); + } + else if (locale == QLocale::German) + { + mUi->englishButton->setStyleSheet(unselected); + mUi->germanButton->setStyleSheet(selected); + } +} + + +void AppQtMainWidget::activateMenuBarItems(bool pEnable) +{ + mUi->actionAusweisen->setEnabled(pEnable); + mUi->actionProvider->setEnabled(pEnable); + mUi->actionHistory->setEnabled(pEnable); + mUi->actionSettings->setEnabled(pEnable); + mUi->actionChangePin->setEnabled(pEnable); + mUi->actionSetupAssistant->setEnabled(pEnable); +} + + +void AppQtMainWidget::onCloseWindowRequested() +{ + mUi->settingsPage->onTabChanged(-1); +} + + +bool AppQtMainWidget::isRemindUserToClose() +{ + return Env::getSingleton()->getGeneralSettings().isRemindUserToClose(); +} diff --git a/src/ui/widget/AppQtMainWidget.h b/src/ui/widget/AppQtMainWidget.h new file mode 100644 index 0000000..5910d1d --- /dev/null +++ b/src/ui/widget/AppQtMainWidget.h @@ -0,0 +1,128 @@ +/*! + * \brief Main class for the top level main widget + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "generic/GuiModule.h" +#include "LogFilesDialog.h" +#include "workflow/WorkflowWidgetParent.h" + +#include +#include +#include +#include +#include +#include + +namespace Ui +{ +class AppQtMainWidget; +} // namespace Ui + +namespace governikus +{ + +class WorkflowQtWidget; + +class AppQtMainWidget + : public QMainWindow +{ + Q_OBJECT + + public: + AppQtMainWidget(); + virtual ~AppQtMainWidget() override; + + bool remoteScanRunning() const; + void workflowActivated(WorkflowWidgetParent pParent, const QString& pName); + void workflowDeactivated(); + + void switchToGuiModule(GuiModule pModule); + + void switchToPinSettingsAfterWorkflow(); + + bool isHideWindowAfterWorkflow() const + { + return mHideWindowAfterWorkflow; + } + + + void setHideWindowAfterWorkflow(bool pHide) + { + mHideWindowAfterWorkflow = pHide; + } + + + void activateMenuBarItems(bool pEnable); + + WorkflowQtWidget* getAuthenticationWorkflowWidget() const + { + return mAuthenticationWorkflowWidget; + } + + + bool isRemindUserToClose(); + + void setSelectedTab(QAbstractButton* pSelectedPushButton); + + void activateWindow(); + + void hideWithoutConfirmation(); + + protected: + virtual void showEvent(QShowEvent* pEvent) override; + virtual void closeEvent(QCloseEvent* pEvent) override; + virtual void keyPressEvent(QKeyEvent* keyEvent) override; + virtual void changeEvent(QEvent* event) override; + + private Q_SLOTS: + void onSettingsDone(); + + void onOpenLoggingFileButtonClicked(); + void onSaveLoggingFileButtonClicked(); + void onTabButtonToggled(QAbstractButton* pButton, bool pChecked); + void onTabActionTriggered(); + void onAboutActionClicked(); + void onEvaluateActionClicked(); + void onQuestionActionClicked(); + void onContentActionClicked(); + void onAskUserToConfirmClosing(); + + public Q_SLOTS: + void onSettingsButtonClicked(); + void onChangePinButtonClicked(); + void onCloseWindowRequested(); + void onSendErrorActionClicked(); + + Q_SIGNALS: + void fireSetupAssistantWizardRequest(); + void fireChangePinRequested(); + void fireDiagnosisRequested(); + void fireCloseWindowRequested(bool* pDoClose); + void fireSelfAuthenticationRequested(); + void fireQuitApplicationRequested(); + void fireChangeHighContrast(bool* pHighContrastOn); + void fireAskUserToConfirmClosing(); + void fireCloseActiveDialogs(); + + private: + QScopedPointer mUi; + QMap mTabButton2Page; + QMap mTabAction2Button; + WorkflowQtWidget* mAuthenticationWorkflowWidget; + QAbstractButton* mSelectedPushButton; + QAbstractButton* mSelectedPushButtonBeforeWorkflow; + QVector mSelectedPagesBeforeWorkflow; + bool mHideWindowAfterWorkflow; + QPointer mLogFilesDialog; + QString mStyleSheet; + void refreshLanguageButton(); + void setLanguage(QLocale::Language pLocale); + bool mClosingDialogsPending; + bool mCloseWithoutConfirmation; +}; + +} // namespace governikus diff --git a/src/ui/widget/AppQtMainWidget.ui b/src/ui/widget/AppQtMainWidget.ui new file mode 100644 index 0000000..43537c3 --- /dev/null +++ b/src/ui/widget/AppQtMainWidget.ui @@ -0,0 +1,843 @@ + + + AppQtMainWidget + + + + 0 + 0 + 950 + 600 + + + + Qt::NoContextMenu + + + AusweisApp2 + + + + :/images/npa.svg:/images/npa.svg + + + + 64 + 64 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 10 + + + + + 16777215 + 10 + + + + + + + + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Actions + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 5 + + + 10 + + + 0 + + + + + Qt::Horizontal + + + QSizePolicy::Minimum + + + + 13 + 20 + + + + + + + + + 0 + 0 + + + + + 53 + 22 + + + + Switch language to German + + + padding: 4px; + + + DE + + + + :/images/location_flag_de.svg:/images/location_flag_de.svg + + + true + + + buttonGroup + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + true + + + + 0 + 0 + + + + + 53 + 23 + + + + Switch language to English + + + padding: 4px; + + + EN + + + + :/images/location_flag_en.svg:/images/location_flag_en.svg + + + false + + + true + + + buttonGroup + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 40 + 20 + + + + + + + + + + + 0 + 0 + + + + Welcome + + + true + + + true + + + + + + + + 0 + 0 + + + + margin-top: 10px; + + + Identify + + + + 32 + 32 + + + + true + + + false + + + Qt::ToolButtonTextBesideIcon + + + + + + + + 0 + 0 + + + + Provider + + + + 32 + 32 + + + + true + + + 300 + + + 100 + + + Qt::ToolButtonTextBesideIcon + + + + + + + + 0 + 0 + + + + History + + + + 32 + 32 + + + + true + + + Qt::ToolButtonTextBesideIcon + + + + + + + + 0 + 0 + + + + Settings + + + + 32 + 32 + + + + true + + + Qt::ToolButtonTextBesideIcon + + + + + + + + 160 + 120 + + + + :/images/beta.svg + + + true + + + + + + + Qt::Vertical + + + QSizePolicy::MinimumExpanding + + + + 20 + 40 + + + + + + + + + + + + 1 + 0 + + + + 0 + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::StrongFocus + + + nPA and eAT Logo + + + :/images/start_nPA_eAT.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::StrongFocus + + + AusweisApp2 Logo + + + :/images/Logo_AusweisApp2.png + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + true + + + + + + + + 0 + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + :/images/Logo_Governikus.png + + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 170 + + + + + + + + + + 0 + 0 + 840 + 20 + + + + true + + + + Fi&le + + + + + + + + + + + &Help + + + + + + + + + + + + + + + + + + &PIN Management + + + + + + + + + + Minimise + + + + + &Exit + + + QAction::QuitRole + + + + + &Identify + + + + + &Settings + + + + + &Online help + + + + + &Evaluate + + + + + &Report error + + + + + &About AusweisApp2 + + + QAction::AboutRole + + + + + &Provider + + + + + S&how log + + + + + Save &log + + + + + &Questions + + + + + &History + + + + + &Change PIN + + + + + &Setup assistant + + + QAction::NoRole + + + + + &Diagnosis + + + + + + governikus::TabButtonGroup + QWidget +
generic/TabButtonGroup.h
+ 1 +
+ + governikus::SettingsWidget + QWidget +
SettingsWidget.h
+ 1 +
+ + governikus::ProviderWidget + QWidget +
ProviderWidget.h
+ 1 +
+ + governikus::SelfInformationWidget + QWidget +
SelfInformationWidget.h
+ 1 +
+ + governikus::HistoryWidget + QWidget +
HistoryWidget.h
+ 1 +
+ + governikus::DeveloperModeHistoryWidget + QWidget +
DeveloperModeHistoryWidget.h
+ 1 +
+ + governikus::TabButton + QToolButton +
generic/TabButtonGroup.h
+
+
+ + startPushButton + appLogoLabel + appLabel + ausweisenToolButton + providerToolButton + historyToolButton + settingsToolButton + + + + + + + + + +
diff --git a/src/ui/widget/AppStartPage.cpp b/src/ui/widget/AppStartPage.cpp new file mode 100644 index 0000000..e59099b --- /dev/null +++ b/src/ui/widget/AppStartPage.cpp @@ -0,0 +1,51 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "AppStartPage.h" +#include "ui_AppStartPage.h" + +#include "generic/TabButtonGroup.h" + +#include +#include + +using namespace governikus; + +AppStartPage::AppStartPage(QWidget* pParent) + : QWidget(pParent) + , mApplicationLogoLabel(new QLabel(this)) + , mUi(new Ui::AppStartPage()) +{ + mUi->setupUi(this); + + QPixmap applicationPixmap(QStringLiteral(":/images/Logo_AusweisApp2.png")); + QPixmap nPAeATPixmap(QStringLiteral(":/images/start_nPA_eAT.png")); + + mUi->applicationLogoLabel->setPixmap(applicationPixmap); + mUi->nPAeATLabel->setPixmap(nPAeATPixmap); +} + + +AppStartPage::~AppStartPage() +{ +} + + +void AppStartPage::paintEvent(QPaintEvent* /*pPaintEvent*/) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} + + +void AppStartPage::changeEvent(QEvent* pEvent) +{ + if (pEvent->type() == QEvent::LanguageChange) + { + mUi->retranslateUi(this); + } + QWidget::changeEvent(pEvent); +} diff --git a/src/ui/widget/AppStartPage.h b/src/ui/widget/AppStartPage.h new file mode 100644 index 0000000..9a73de3 --- /dev/null +++ b/src/ui/widget/AppStartPage.h @@ -0,0 +1,44 @@ +/*! + * \brief Main page widget. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include + +namespace Ui +{ +class AppStartPage; +} // namespace Ui + +namespace governikus +{ + +class AppStartPage + : public QWidget +{ + Q_OBJECT + + public: + AppStartPage(QWidget* pParent = nullptr); + virtual ~AppStartPage() override; + + Q_SIGNALS: + void selfInfoPageRequested(); + void bookmarksPageRequested(); + void settingsPageRequested(); + + private: + QLabel* mApplicationLogoLabel; + QScopedPointer mUi; + + virtual void paintEvent(QPaintEvent*) override; + + protected: + virtual void changeEvent(QEvent* pEvent) override; +}; + +} // namespace governikus diff --git a/src/widget/AppStartPage.ui b/src/ui/widget/AppStartPage.ui similarity index 100% rename from src/widget/AppStartPage.ui rename to src/ui/widget/AppStartPage.ui diff --git a/src/ui/widget/CMakeLists.txt b/src/ui/widget/CMakeLists.txt new file mode 100644 index 0000000..f392685 --- /dev/null +++ b/src/ui/widget/CMakeLists.txt @@ -0,0 +1,15 @@ +##################################################################### +# The widget plugin implements the ui interface for desktop systems. +# +# It is a wrapper to QWidgets that allows to use the platform +# specific styles for the underlying system. +##################################################################### + +ADD_PLATFORM_LIBRARY(AusweisAppUiWidget) + +TARGET_LINK_LIBRARIES(AusweisAppUiWidget Qt5::Core Qt5::Widgets Qt5::Svg AusweisAppGlobal AusweisAppCore AusweisAppUi AusweisAppExport AusweisAppRemoteDevice AusweisAppConfiguration AusweisAppUiCommon) +TARGET_COMPILE_DEFINITIONS(AusweisAppUiWidget PRIVATE QT_STATICPLUGIN) + +IF(WIN32) + TARGET_LINK_LIBRARIES(AusweisAppUiWidget Qt5::WinExtras) +ENDIF() diff --git a/src/widget/CredentialDialog.cpp b/src/ui/widget/CredentialDialog.cpp similarity index 100% rename from src/widget/CredentialDialog.cpp rename to src/ui/widget/CredentialDialog.cpp diff --git a/src/ui/widget/CredentialDialog.h b/src/ui/widget/CredentialDialog.h new file mode 100644 index 0000000..e516814 --- /dev/null +++ b/src/ui/widget/CredentialDialog.h @@ -0,0 +1,43 @@ +/*! + * \brief Show a dialog to fill in proxy credentials. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +namespace Ui +{ +class CredentialDialog; +} // namespace Ui + +#include + +namespace governikus +{ + +class CredentialDialog + : public QDialog +{ + Q_OBJECT + + private: + Ui::CredentialDialog* mUi; + + protected: + virtual void changeEvent(QEvent* pEvent) override; + + public: + explicit CredentialDialog(QWidget* pParent = nullptr); + virtual ~CredentialDialog() override; + + void setUser(const QString& pUser); + + QString getUser() const; + + QString getPassword() const; + + +}; + +} // namespace governikus diff --git a/src/widget/CredentialDialog.ui b/src/ui/widget/CredentialDialog.ui similarity index 100% rename from src/widget/CredentialDialog.ui rename to src/ui/widget/CredentialDialog.ui diff --git a/src/widget/DeleteHistoryDialog.cpp b/src/ui/widget/DeleteHistoryDialog.cpp similarity index 100% rename from src/widget/DeleteHistoryDialog.cpp rename to src/ui/widget/DeleteHistoryDialog.cpp diff --git a/src/ui/widget/DeleteHistoryDialog.h b/src/ui/widget/DeleteHistoryDialog.h new file mode 100644 index 0000000..adab48e --- /dev/null +++ b/src/ui/widget/DeleteHistoryDialog.h @@ -0,0 +1,32 @@ +/* + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "HistorySettings.h" + +#include +#include +#include +#include +#include + +namespace governikus +{ + +class DeleteHistoryDialog + : public QDialog +{ + Q_OBJECT + QPointer mRadioButtonGroup; + QPointer mComboBox; + + QRadioButton* createRadioButtonAndAppendToGroup(const QString& pText, TimePeriod pTimePeriod); + + public: + explicit DeleteHistoryDialog(QWidget* pParent = nullptr); + TimePeriod getTimePeriod(); +}; + +} // namespace governikus diff --git a/src/ui/widget/DetailDialog.cpp b/src/ui/widget/DetailDialog.cpp new file mode 100644 index 0000000..254add2 --- /dev/null +++ b/src/ui/widget/DetailDialog.cpp @@ -0,0 +1,68 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "DetailDialog.h" + +#include "ui_DetailDialog.h" + +#include "DetailWidget.h" +#include "HelpAction.h" + +#include +#include + +using namespace governikus; + +DetailDialog::DetailDialog(QWidget* pParent) + : QDialog(pParent) + , mUi(new Ui::DetailDialog) +{ + mUi->setupUi(this); + + setWindowFlags(Qt::Dialog | Qt::WindowTitleHint | Qt::WindowCloseButtonHint); + setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("Service provider data")); + + installEventFilter(this); + + connect(mUi->buttonBox, &QDialogButtonBox::rejected, this, &DetailDialog::close); +} + + +DetailDialog::~DetailDialog() +{ + delete mUi; +} + + +void DetailDialog::setDetails(const QString& pDetails) +{ + mUi->detailWidget->setDetails(pDetails); + mUi->buttonBox->button(QDialogButtonBox::Close)->setFocus(); + adjustSize(); +} + + +bool DetailDialog::eventFilter(QObject* pObject, QEvent* pEvent) +{ + if (pEvent->type() == QEvent::KeyPress) + { + QKeyEvent* keyEvent = static_cast(pEvent); + if (keyEvent->key() == Qt::Key_F1) + { + HelpAction::openContextHelp(); + return true; + } + } + return QDialog::eventFilter(pObject, pEvent); +} + + +void DetailDialog::changeEvent(QEvent* pEvent) +{ + if (pEvent->type() == QEvent::LanguageChange) + { + mUi->retranslateUi(this); + } + QWidget::changeEvent(pEvent); +} diff --git a/src/ui/widget/DetailDialog.h b/src/ui/widget/DetailDialog.h new file mode 100644 index 0000000..7174095 --- /dev/null +++ b/src/ui/widget/DetailDialog.h @@ -0,0 +1,39 @@ +/*! + * \brief Detail dialog for certificate description + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include + +namespace Ui +{ +class DetailDialog; +} // namespace Ui + +namespace governikus +{ + +class DetailDialog + : public QDialog +{ + Q_OBJECT + + private: + Ui::DetailDialog* mUi; + + public: + explicit DetailDialog(QWidget* pParent = nullptr); + virtual ~DetailDialog() override; + + void setDetails(const QString& pDetails); + + protected: + virtual bool eventFilter(QObject* pObject, QEvent* pEvent) override; + virtual void changeEvent(QEvent* pEvent) override; + +}; + +} // namespace governikus diff --git a/src/widget/DetailDialog.ui b/src/ui/widget/DetailDialog.ui similarity index 100% rename from src/widget/DetailDialog.ui rename to src/ui/widget/DetailDialog.ui diff --git a/src/ui/widget/DetailWidget.cpp b/src/ui/widget/DetailWidget.cpp new file mode 100644 index 0000000..4849a54 --- /dev/null +++ b/src/ui/widget/DetailWidget.cpp @@ -0,0 +1,82 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "DetailWidget.h" +#include "ui_DetailWidget.h" + +#include +#include + +using namespace governikus; + +DetailWidget::DetailWidget(QWidget* pParent) + : QWidget(pParent) + , mUi(new Ui::DetailWidget()) +{ + mUi->setupUi(this); + + // The scroll area may be resized while the content keeps its + // size, therefore the scroll area must have the same background + // color as the content. + mUi->scrollArea->setStyleSheet(QStringLiteral("QScrollArea { background-color: white; }" + "" + "QScrollBar {" + " background-color: #f8f6f4;" + "}")); + + const QString& border = QStringLiteral("border-width: 0;"); + mUi->scrollAreaWidgetContents->setStyleSheet(border); + mUi->detailText->setStyleSheet(border); +} + + +DetailWidget::~DetailWidget() +{ +} + + +void DetailWidget::setDetails(const QString& pDetails) +{ + mUi->detailText->setAccessibleName(tr("Service provider details dialog") + pDetails); + mUi->detailText->setText(pDetails); +} + + +void DetailWidget::paintEvent(QPaintEvent*) +{ + static const int SCROLLAREA_PREFERRED_WIDTH = 504; + // See comment to accepted answer at + // http://stackoverflow.com/questions/16515646/how-to-get-scroll-bar-real-width-in-qt +#ifdef Q_OS_WIN + static const int SCROLLBAR_EXTRA_SPACE = 40; +#else + static const int SCROLLBAR_EXTRA_SPACE = 3; +#endif + + const int scrollbarWidth = qApp->style()->pixelMetric(QStyle::PM_ScrollBarExtent) + SCROLLBAR_EXTRA_SPACE; + const int contentMaxWidth = SCROLLAREA_PREFERRED_WIDTH - scrollbarWidth; + + if (mUi->detailText->width() > contentMaxWidth) + { + mUi->detailText->setFixedWidth(contentMaxWidth); + } + + mUi->scrollAreaWidgetContents->setFixedHeight(mUi->detailText->height()); + mUi->scrollAreaWidgetContents->setFixedWidth(mUi->detailText->width()); + + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} + + +void DetailWidget::changeEvent(QEvent* pEvent) +{ + if (pEvent->type() == QEvent::LanguageChange) + { + mUi->retranslateUi(this); + } + QWidget::changeEvent(pEvent); +} diff --git a/src/ui/widget/DetailWidget.h b/src/ui/widget/DetailWidget.h new file mode 100644 index 0000000..3575ae9 --- /dev/null +++ b/src/ui/widget/DetailWidget.h @@ -0,0 +1,41 @@ +/*! + * \brief Widget for cvc description. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include + +namespace Ui +{ +class DetailWidget; +} // namespace Ui + +namespace governikus +{ + +class DetailWidget + : public QWidget +{ + Q_OBJECT + + private: + QScopedPointer mUi; + + virtual void paintEvent(QPaintEvent*) override; + + protected: + virtual void changeEvent(QEvent* pEvent) override; + + public: + DetailWidget(QWidget* pParent = nullptr); + virtual ~DetailWidget() override; + + void setDetails(const QString& pDetails); + +}; + +} // namespace governikus diff --git a/src/widget/DetailWidget.ui b/src/ui/widget/DetailWidget.ui similarity index 100% rename from src/widget/DetailWidget.ui rename to src/ui/widget/DetailWidget.ui diff --git a/src/ui/widget/DeveloperModeHistoryWidget.cpp b/src/ui/widget/DeveloperModeHistoryWidget.cpp new file mode 100644 index 0000000..1b4ada2 --- /dev/null +++ b/src/ui/widget/DeveloperModeHistoryWidget.cpp @@ -0,0 +1,83 @@ +/* + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "DeveloperModeHistoryWidget.h" +#include "ui_DeveloperModeHistoryWidget.h" + +#include "AppSettings.h" +#include "LogHandler.h" + +#include + +using namespace governikus; + +Q_DECLARE_LOGGING_CATEGORY(developermode) + + +DeveloperModeHistoryWidget::DeveloperModeHistoryWidget(QWidget* pParent) + : QWidget(pParent) + , mUi(new Ui::DeveloperModeHistoryWidget()) +{ + mUi->setupUi(this); + + connect(Env::getSingleton(), &AppSettings::fireSettingsChanged, this, &DeveloperModeHistoryWidget::onSettingsChanged); + connect(Env::getSingleton(), &LogHandler::fireRawLog, this, &DeveloperModeHistoryWidget::onRawLog); + connect(mUi->btnDisableDeveloperMode, &QPushButton::clicked, this, &DeveloperModeHistoryWidget::onDisableDeveloperMode); + + // initialize visibility state + onSettingsChanged(); +} + + +DeveloperModeHistoryWidget::~DeveloperModeHistoryWidget() +{ +} + + +void DeveloperModeHistoryWidget::appendLoggingDump(const QString& pLog) +{ + QString formatted = pLog; + + // Remove outer quotation + const QLatin1Char quote('"'); + if (formatted.startsWith(quote) && formatted.endsWith(quote)) + { + formatted = formatted.mid(1, formatted.length() - 2); + } + + mUi->plainTextEdit->appendHtml(QStringLiteral("
%1
").arg(formatted)); +} + + +void DeveloperModeHistoryWidget::onRawLog(const QString& pMsg, const QString& pCategoryName) +{ + static const QString categoryDevMode = QString::fromLatin1(developermode().categoryName()); + if (pCategoryName == categoryDevMode) + { + appendLoggingDump(pMsg); + } +} + + +void DeveloperModeHistoryWidget::onSettingsChanged() +{ + QWidget::setVisible(Env::getSingleton()->getGeneralSettings().isDeveloperMode()); +} + + +void DeveloperModeHistoryWidget::onDisableDeveloperMode() +{ + Env::getSingleton()->getGeneralSettings().setDeveloperMode(false); + Env::getSingleton()->getGeneralSettings().save(); +} + + +void DeveloperModeHistoryWidget::changeEvent(QEvent* pEvent) +{ + if (pEvent->type() == QEvent::LanguageChange) + { + mUi->retranslateUi(this); + } + QWidget::changeEvent(pEvent); +} diff --git a/src/ui/widget/DeveloperModeHistoryWidget.h b/src/ui/widget/DeveloperModeHistoryWidget.h new file mode 100644 index 0000000..cd623aa --- /dev/null +++ b/src/ui/widget/DeveloperModeHistoryWidget.h @@ -0,0 +1,46 @@ +/*! + * \brief A Widget to display developer mode errors which occurred + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include + + +namespace Ui +{ +class DeveloperModeHistoryWidget; +} // namespace Ui + + +namespace governikus +{ + +class DeveloperModeHistoryWidget + : public QWidget +{ + Q_OBJECT + + private: + QScopedPointer mUi; + + void appendLoggingDump(const QString& pLog); + + private Q_SLOTS: + void onDisableDeveloperMode(); + + protected: + virtual void changeEvent(QEvent* pEvent) override; + + public: + explicit DeveloperModeHistoryWidget(QWidget* pParent = nullptr); + virtual ~DeveloperModeHistoryWidget() override; + + public Q_SLOTS: + void onRawLog(const QString& pMsg, const QString& pCategoryName); + void onSettingsChanged(); +}; + +} // namespace governikus diff --git a/src/widget/DeveloperModeHistoryWidget.ui b/src/ui/widget/DeveloperModeHistoryWidget.ui similarity index 100% rename from src/widget/DeveloperModeHistoryWidget.ui rename to src/ui/widget/DeveloperModeHistoryWidget.ui diff --git a/src/ui/widget/DeveloperSettingsWidget.cpp b/src/ui/widget/DeveloperSettingsWidget.cpp new file mode 100644 index 0000000..4ffa0fe --- /dev/null +++ b/src/ui/widget/DeveloperSettingsWidget.cpp @@ -0,0 +1,62 @@ +/*! + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "DeveloperSettingsWidget.h" +#include "ui_DeveloperSettingsWidget.h" + +#include "AppSettings.h" + +using namespace governikus; + +DeveloperSettingsWidget::DeveloperSettingsWidget(QWidget* pParent) + : QWidget(pParent) + , mUi(new Ui::DeveloperSettingsWidget()) +{ + mUi->setupUi(this); + + reset(); + + connect(mUi->selfAuthTestCheckBox, &QCheckBox::stateChanged, this, &DeveloperSettingsWidget::onCheckBoxStateChanged); +} + + +DeveloperSettingsWidget::~DeveloperSettingsWidget() +{ +} + + +void governikus::DeveloperSettingsWidget::onCheckBoxStateChanged() +{ + Q_EMIT fireSettingsChanged(); +} + + +void DeveloperSettingsWidget::showEvent(QShowEvent* pEvent) +{ + QWidget::showEvent(pEvent); +} + + +void DeveloperSettingsWidget::apply() +{ + auto& generalSettings = Env::getSingleton()->getGeneralSettings(); + generalSettings.setUseSelfauthenticationTestUri(mUi->selfAuthTestCheckBox->isChecked()); + generalSettings.save(); +} + + +void DeveloperSettingsWidget::reset() +{ + mUi->selfAuthTestCheckBox->setChecked(Env::getSingleton()->getGeneralSettings().useSelfAuthTestUri()); +} + + +void DeveloperSettingsWidget::changeEvent(QEvent* pEvent) +{ + if (pEvent->type() == QEvent::LanguageChange) + { + mUi->retranslateUi(this); + } + QWidget::changeEvent(pEvent); +} diff --git a/src/ui/widget/DeveloperSettingsWidget.h b/src/ui/widget/DeveloperSettingsWidget.h new file mode 100644 index 0000000..7b77319 --- /dev/null +++ b/src/ui/widget/DeveloperSettingsWidget.h @@ -0,0 +1,47 @@ +/*! + * \brief Widget for the developer settings. + * + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include + + +namespace Ui +{ +class DeveloperSettingsWidget; +} // namespace Ui + +namespace governikus +{ + +class DeveloperSettingsWidget + : public QWidget +{ + Q_OBJECT + + private: + QScopedPointer mUi; + + private Q_SLOTS: + void onCheckBoxStateChanged(); + virtual void showEvent(QShowEvent*) override; + + protected: + virtual void changeEvent(QEvent* pEvent) override; + + public: + DeveloperSettingsWidget(QWidget* pParent = nullptr); + virtual ~DeveloperSettingsWidget() override; + + void apply(); + void reset(); + + Q_SIGNALS: + void fireSettingsChanged(); +}; + +} // namespace governikus diff --git a/src/widget/DeveloperSettingsWidget.ui b/src/ui/widget/DeveloperSettingsWidget.ui similarity index 100% rename from src/widget/DeveloperSettingsWidget.ui rename to src/ui/widget/DeveloperSettingsWidget.ui diff --git a/src/ui/widget/DiagnosisDialog.cpp b/src/ui/widget/DiagnosisDialog.cpp new file mode 100644 index 0000000..5938a09 --- /dev/null +++ b/src/ui/widget/DiagnosisDialog.cpp @@ -0,0 +1,105 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "DiagnosisDialog.h" +#include "ui_DiagnosisDialog.h" + +#include "context/DiagnosisContext.h" +#include "HelpAction.h" + +#include +#include +#include +#include + +using namespace governikus; + + +DiagnosisDialog::DiagnosisDialog(const QSharedPointer& pContext, QWidget* pParent) + : QDialog(pParent) + , mUi(new Ui::DiagnosisDialog) + , mDiagnosisModel(new DiagnosisModel(pContext)) + , mTreeView(new QTreeView(this)) +{ + mUi->setupUi(this); + + setAttribute(Qt::WA_DeleteOnClose, true); + setWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint); + setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("Diagnosis")); + + installEventFilter(this); + + mTreeView->setModel(mDiagnosisModel.data()); + mUi->diagnosisLayout->addWidget(mTreeView); + + connect(mUi->saveButton, &QAbstractButton::clicked, this, &DiagnosisDialog::onSaveButtonClicked); + connect(mUi->closeButton, &QAbstractButton::clicked, this, &DiagnosisDialog::close); +} + + +DiagnosisDialog::~DiagnosisDialog() +{ +} + + +void DiagnosisDialog::onSaveButtonClicked() +{ + const auto& creationTime = mDiagnosisModel->getCreationTime(); + + QString filename = QStringLiteral("AusweisApp2.Diagnosis.%1.txt").arg(creationTime.toString(QStringLiteral("yyyy-MM-dd_HH-mm"))); + filename = QFileDialog::getSaveFileName(this, + QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("Save"), + QDir::homePath() + QLatin1Char('/') + filename, + tr("Text files") + QStringLiteral(" (*.txt)")); + + if (filename.isEmpty()) + { + return; + } + if (!filename.endsWith(QLatin1String(".txt"), Qt::CaseSensitivity::CaseInsensitive)) + { + filename += QStringLiteral(".txt"); + } + + QString text = mDiagnosisModel->getAsPlaintext(); + + QFile file(filename); + if (!file.open(QIODevice::WriteOnly | QFile::Truncate) || file.write(text.toUtf8()) < 0) + { + QMessageBox box(this); + box.setWindowTitle(QApplication::applicationName() + QStringLiteral(" - ") + tr("File error")); + box.setWindowModality(Qt::ApplicationModal); + box.setIcon(QMessageBox::Warning); + box.setWindowFlags(box.windowFlags() & ~Qt::WindowContextHelpButtonHint); + box.setText(tr("An error occurred while saving the file.")); + box.setStandardButtons(QMessageBox::Ok); + box.button(QMessageBox::Ok)->setFocus(); + box.exec(); + } +} + + +bool DiagnosisDialog::eventFilter(QObject* pObject, QEvent* pEvent) +{ + if (pEvent->type() == QEvent::KeyPress) + { + QKeyEvent* keyEvent = static_cast(pEvent); + if (keyEvent->key() == Qt::Key_F1) + { + HelpAction::openContextHelp(); + return true; + } + } + return QDialog::eventFilter(pObject, pEvent); +} + + +void DiagnosisDialog::changeEvent(QEvent* pEvent) +{ + if (pEvent->type() == QEvent::LanguageChange) + { + mUi->retranslateUi(this); + } + QWidget::changeEvent(pEvent); +} diff --git a/src/ui/widget/DiagnosisDialog.h b/src/ui/widget/DiagnosisDialog.h new file mode 100644 index 0000000..ea4ce93 --- /dev/null +++ b/src/ui/widget/DiagnosisDialog.h @@ -0,0 +1,48 @@ +/*! + * \brief Dialog for display the diagnosis information. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "DiagnosisModel.h" + +#include +#include +#include + +namespace Ui +{ +class DiagnosisDialog; +} // namespace Ui + +namespace governikus +{ + +class DiagnosisContext; +class DiagnosisWidget; + +class DiagnosisDialog + : public QDialog +{ + Q_OBJECT + + private: + QScopedPointer mUi; + QScopedPointer mDiagnosisModel; + QTreeView* mTreeView; + + private Q_SLOTS: + void onSaveButtonClicked(); + + protected: + virtual bool eventFilter(QObject* pObject, QEvent* pEvent) override; + virtual void changeEvent(QEvent* pEvent) override; + + public: + explicit DiagnosisDialog(const QSharedPointer& pContext, QWidget* pParent = nullptr); + virtual ~DiagnosisDialog() override; +}; + +} // namespace governikus diff --git a/src/widget/DiagnosisDialog.ui b/src/ui/widget/DiagnosisDialog.ui similarity index 100% rename from src/widget/DiagnosisDialog.ui rename to src/ui/widget/DiagnosisDialog.ui diff --git a/src/ui/widget/DiagnosisGui.cpp b/src/ui/widget/DiagnosisGui.cpp new file mode 100644 index 0000000..9e8bf2c --- /dev/null +++ b/src/ui/widget/DiagnosisGui.cpp @@ -0,0 +1,63 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "DiagnosisGui.h" + + +#include "controller/DiagnosisController.h" + +using namespace governikus; + +DiagnosisGui::DiagnosisGui(QWidget* pParentWidget) + : QObject(pParentWidget) + , mDialog() +{ +} + + +DiagnosisGui::~DiagnosisGui() +{ +} + + +void DiagnosisGui::activate() +{ + if (mDialog) + { + if (mDialog->isMinimized()) + { + mDialog->showNormal(); + } + if (!mDialog->isVisible()) + { + mDialog->show(); + } + mDialog->activateWindow(); + mDialog->raise(); + return; + } + + QWidget* dialogParent = qobject_cast(parent()); + if (!dialogParent) + { + return; + } + + auto context = QSharedPointer::create(); + mDialog = new DiagnosisDialog(context, dialogParent); + connect(mDialog.data(), &QDialog::finished, this, &DiagnosisGui::fireFinished); + mDialog->show(); + + auto controller = new DiagnosisController(context, mDialog); + controller->run(); +} + + +void DiagnosisGui::deactivate() +{ + if (mDialog) + { + mDialog->close(); + } +} diff --git a/src/ui/widget/DiagnosisGui.h b/src/ui/widget/DiagnosisGui.h new file mode 100644 index 0000000..f9da3e6 --- /dev/null +++ b/src/ui/widget/DiagnosisGui.h @@ -0,0 +1,36 @@ +/*! + * \brief Qt widget based DiagnosisUi implementation. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "DiagnosisDialog.h" + +#include +#include + +namespace governikus +{ + +class DiagnosisGui + : public QObject +{ + Q_OBJECT + + public: + DiagnosisGui(QWidget* pParentWidget); + virtual ~DiagnosisGui(); + + void activate(); + void deactivate(); + + Q_SIGNALS: + void fireFinished(); + + private: + QPointer mDialog; +}; + +} // namespace governikus diff --git a/src/widget/DiagnosisWidget.ui b/src/ui/widget/DiagnosisWidget.ui similarity index 100% rename from src/widget/DiagnosisWidget.ui rename to src/ui/widget/DiagnosisWidget.ui diff --git a/src/ui/widget/GeneralSettingsWidget.cpp b/src/ui/widget/GeneralSettingsWidget.cpp new file mode 100644 index 0000000..93a26f6 --- /dev/null +++ b/src/ui/widget/GeneralSettingsWidget.cpp @@ -0,0 +1,112 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "GeneralSettingsWidget.h" +#include "ui_GeneralSettingsWidget.h" + +#include "AppSettings.h" +#include "Service.h" +#include "UpdateWindow.h" + +#include + + +using namespace governikus; + + +Q_DECLARE_LOGGING_CATEGORY(settings) + + +GeneralSettingsWidget::GeneralSettingsWidget(QWidget* pParent) + : QWidget(pParent) + , mUi(new Ui::GeneralSettingsWidget()) +{ + mUi->setupUi(this); + + auto& generalSettings = Env::getSingleton()->getGeneralSettings(); + if (generalSettings.autoStartIsSetByAdmin()) + { + qCInfo(settings) << "Auto start is enabled system wide"; + mUi->autostartCheckBox->setEnabled(false); + } + if (generalSettings.autoUpdateCheckIsSetByAdmin()) + { + qCInfo(settings) << "Auto update check is configured system wide"; + mUi->regularlyUpdateCheckBox->setEnabled(false); + } + + reset(); + + connect(mUi->autostartCheckBox, &QCheckBox::stateChanged, this, &GeneralSettingsWidget::onCheckBoxStateChanged); + connect(mUi->regularlyUpdateCheckBox, &QCheckBox::stateChanged, this, &GeneralSettingsWidget::onCheckBoxStateChanged); + connect(mUi->closeWindowCheckBox, &QCheckBox::stateChanged, this, &GeneralSettingsWidget::onCheckBoxStateChanged); + connect(mUi->saveHistoryCheckBox, &QCheckBox::stateChanged, this, &GeneralSettingsWidget::onCheckBoxStateChanged); + connect(mUi->updateCheckButton, &QCheckBox::clicked, this, &GeneralSettingsWidget::onUpdateCheckButtonClicked); + connect(mUi->keylessPasswordCheckBox, &QCheckBox::stateChanged, this, &GeneralSettingsWidget::onCheckBoxStateChanged); +} + + +GeneralSettingsWidget::~GeneralSettingsWidget() +{ +} + + +void GeneralSettingsWidget::showEvent(QShowEvent* pEvent) +{ + disconnect(mUi->saveHistoryCheckBox, &QCheckBox::stateChanged, this, &GeneralSettingsWidget::onCheckBoxStateChanged); + mUi->saveHistoryCheckBox->setChecked(Env::getSingleton()->getHistorySettings().isEnabled()); + connect(mUi->saveHistoryCheckBox, &QCheckBox::stateChanged, this, &GeneralSettingsWidget::onCheckBoxStateChanged); + + QWidget::showEvent(pEvent); +} + + +void GeneralSettingsWidget::apply() +{ + auto& historySettings = Env::getSingleton()->getHistorySettings(); + historySettings.setEnabled(mUi->saveHistoryCheckBox->isChecked()); + historySettings.save(); + + auto& generalSettings = Env::getSingleton()->getGeneralSettings(); + generalSettings.setAutoCloseWindowAfterAuthentication(mUi->closeWindowCheckBox->isChecked()); + generalSettings.setAutoStart(mUi->autostartCheckBox->isChecked()); + generalSettings.setAutoUpdateCheck(mUi->regularlyUpdateCheckBox->isChecked()); + generalSettings.setUseScreenKeyboard(mUi->keylessPasswordCheckBox->isChecked()); + generalSettings.save(); +} + + +void GeneralSettingsWidget::reset() +{ + const auto& historySettings = Env::getSingleton()->getHistorySettings(); + mUi->saveHistoryCheckBox->setChecked(historySettings.isEnabled()); + + const auto& generalSettings = Env::getSingleton()->getGeneralSettings(); + mUi->closeWindowCheckBox->setChecked(generalSettings.isAutoCloseWindowAfterAuthentication()); + mUi->autostartCheckBox->setChecked(generalSettings.isAutoStart()); + mUi->regularlyUpdateCheckBox->setChecked(generalSettings.isAutoUpdateCheck()); + mUi->keylessPasswordCheckBox->setChecked(generalSettings.isUseScreenKeyboard()); +} + + +void GeneralSettingsWidget::onCheckBoxStateChanged(int) +{ + Q_EMIT settingsChanged(); +} + + +void GeneralSettingsWidget::onUpdateCheckButtonClicked() +{ + Env::getSingleton()->updateApp(true); +} + + +void GeneralSettingsWidget::changeEvent(QEvent* pEvent) +{ + if (pEvent->type() == QEvent::LanguageChange) + { + mUi->retranslateUi(this); + } + QWidget::changeEvent(pEvent); +} diff --git a/src/ui/widget/GeneralSettingsWidget.h b/src/ui/widget/GeneralSettingsWidget.h new file mode 100644 index 0000000..46d98d1 --- /dev/null +++ b/src/ui/widget/GeneralSettingsWidget.h @@ -0,0 +1,49 @@ +/*! + * \brief Widget for the general settings. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "GlobalStatus.h" + +#include +#include + +namespace Ui +{ +class GeneralSettingsWidget; +} // namespace Ui + +namespace governikus +{ + +class GeneralSettingsWidget + : public QWidget +{ + Q_OBJECT + + private: + QScopedPointer mUi; + + private Q_SLOTS: + void onCheckBoxStateChanged(int pState); + void onUpdateCheckButtonClicked(); + virtual void showEvent(QShowEvent*) override; + + protected: + virtual void changeEvent(QEvent* pEvent) override; + + public: + GeneralSettingsWidget(QWidget* pParent = nullptr); + virtual ~GeneralSettingsWidget() override; + + void apply(); + void reset(); + + Q_SIGNALS: + void settingsChanged(); +}; + +} // namespace governikus diff --git a/src/widget/GeneralSettingsWidget.ui b/src/ui/widget/GeneralSettingsWidget.ui similarity index 100% rename from src/widget/GeneralSettingsWidget.ui rename to src/ui/widget/GeneralSettingsWidget.ui diff --git a/src/ui/widget/HistoryDetailWidget.cpp b/src/ui/widget/HistoryDetailWidget.cpp new file mode 100644 index 0000000..f8abc1c --- /dev/null +++ b/src/ui/widget/HistoryDetailWidget.cpp @@ -0,0 +1,49 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "HistoryDetailWidget.h" +#include "ui_HistoryDetailWidget.h" + +#include +#include + +using namespace governikus; + + +HistoryDetailWidget::HistoryDetailWidget(QWidget* pParent) + : QWidget(pParent) + , mUi(new Ui::HistoryDetailWidget()) +{ + mUi->setupUi(this); +} + + +HistoryDetailWidget::~HistoryDetailWidget() +{ +} + + +void HistoryDetailWidget::setDetails(const QString& pDetails) +{ + mUi->detailText->setText(pDetails); +} + + +void HistoryDetailWidget::paintEvent(QPaintEvent*) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} + + +void HistoryDetailWidget::changeEvent(QEvent* pEvent) +{ + if (pEvent->type() == QEvent::LanguageChange) + { + mUi->retranslateUi(this); + } + QWidget::changeEvent(pEvent); +} diff --git a/src/ui/widget/HistoryDetailWidget.h b/src/ui/widget/HistoryDetailWidget.h new file mode 100644 index 0000000..82e5c92 --- /dev/null +++ b/src/ui/widget/HistoryDetailWidget.h @@ -0,0 +1,40 @@ +/*! + * \brief Widget for history item. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include + +namespace Ui +{ +class HistoryDetailWidget; +} // namespace Ui + +namespace governikus +{ + +class HistoryDetailWidget + : public QWidget +{ + Q_OBJECT + + private: + QScopedPointer mUi; + + virtual void paintEvent(QPaintEvent*) override; + + protected: + virtual void changeEvent(QEvent* pEvent) override; + + public: + HistoryDetailWidget(QWidget* pParent = nullptr); + virtual ~HistoryDetailWidget() override; + + void setDetails(const QString& pDetails); +}; + +} // namespace governikus diff --git a/src/widget/HistoryDetailWidget.ui b/src/ui/widget/HistoryDetailWidget.ui similarity index 100% rename from src/widget/HistoryDetailWidget.ui rename to src/ui/widget/HistoryDetailWidget.ui diff --git a/src/ui/widget/HistoryWidget.cpp b/src/ui/widget/HistoryWidget.cpp new file mode 100644 index 0000000..4f00c73 --- /dev/null +++ b/src/ui/widget/HistoryWidget.cpp @@ -0,0 +1,299 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "HistoryWidget.h" +#include "ui_HistoryWidget.h" + +#include "asn1/AccessRoleAndRight.h" +#include "AppSettings.h" +#include "DeleteHistoryDialog.h" +#include "DetailDialog.h" +#include "generic/ListCheckItemWidget.h" +#include "generic/ListItem.h" +#include "generic/ListItemIconLeft.h" +#include "generic/ListItemIconRight.h" +#include "generic/ListItemSubTitle.h" +#include "generic/ListItemTitle.h" +#include "LanguageLoader.h" +#include "PdfExporter.h" +#include "ScopeGuard.h" + +#include +#include +#include +#include + +using namespace governikus; + + +HistoryWidget::HistoryWidget(QWidget* pParent) + : QWidget(pParent) + , mUi(new Ui::HistoryWidget()) + , mHistoryDetailWidget(nullptr) +{ + mUi->setupUi(this); + + connect(mUi->historyDeleteButton, &QPushButton::clicked, this, &HistoryWidget::deleteHistory); + connect(mUi->historySearch, &QLineEdit::textChanged, this, &HistoryWidget::searchHistory); + connect(mUi->historyExportButton, &QPushButton::clicked, this, &HistoryWidget::exportHistory); + connect(mUi->historyTableWidget, &QTableWidget::doubleClicked, this, &HistoryWidget::onItemClicked); + connect(mUi->saveHistoryCheckBox, &QCheckBox::stateChanged, this, &HistoryWidget::onCheckBoxStateChanged); + mUi->saveHistoryCheckBox->setChecked(Env::getSingleton()->getHistorySettings().isEnabled()); + + connect(&Env::getSingleton()->getHistorySettings(), &HistorySettings::fireHistoryInfosChanged, this, &HistoryWidget::updateTable); + connect(&Env::getSingleton()->getHistorySettings(), &HistorySettings::fireEnabledChanged, this, [this](bool pValue){ + mUi->saveHistoryCheckBox->setChecked(pValue); + }); + + init(); +} + + +HistoryWidget::~HistoryWidget() +{ +} + + +void HistoryWidget::init() +{ + QStringList header; + header += tr("Date"); + header += tr("Details"); + + mUi->historyTableWidget->setColumnCount(header.count()); + mUi->historyTableWidget->setHorizontalHeaderLabels(header); + mUi->historyTableWidget->verticalHeader()->setVisible(false); //Hide row number + mUi->historyTableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); //Not allowed to change content + mUi->historyTableWidget->setSortingEnabled(false); + mUi->historyTableWidget->installEventFilter(this); + + mUi->noResultWidget->setVisible(false); + updateTable(); +} + + +QWidget* HistoryWidget::getDetailActivatingWidget() const +{ + return mUi->historyTableWidget; +} + + +void HistoryWidget::paintEvent(QPaintEvent*) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} + + +void HistoryWidget::onCheckBoxStateChanged(int /*pState*/) +{ + auto& historySettings = Env::getSingleton()->getHistorySettings(); + historySettings.setEnabled(mUi->saveHistoryCheckBox->isChecked()); + historySettings.save(); +} + + +void HistoryWidget::updateTable() +{ + const auto& items = Env::getSingleton()->getHistorySettings().getHistoryInfos(); + + const ScopeGuard guard([this] { + mUi->historyTableWidget->setUpdatesEnabled(true); + }); + + mUi->historyTableWidget->setUpdatesEnabled(false); + mUi->historyTableWidget->clearContents(); + mUi->historyTableWidget->setRowCount(0); + + for (const HistoryInfo& info : items) + { + int rowIndex = mUi->historyTableWidget->rowCount(); + mUi->historyTableWidget->insertRow(rowIndex); + + //date column with needed properties + const auto& dateTime = LanguageLoader::getInstance().getUsedLocale().toString(info.getDateTime(), tr("dd.MM.yyyy hh:mm AP")); + QLabel* dateLabel = new QLabel(dateTime); + dateLabel->setContentsMargins(11, 11, 11, 11); + dateLabel->setAlignment(Qt::AlignTop); + dateLabel->setProperty("termsOfUsage", info.getTermOfUsage()); + dateLabel->setProperty("date", dateTime); + + dateLabel->setFocusPolicy(Qt::TabFocus); + dateLabel->setAccessibleName(tr("Date:") + dateTime); + + mUi->historyTableWidget->setCellWidget(rowIndex, 0, dateLabel); + + //details column with needed properties + QWidget* centralWidget = new QWidget(); + + QFormLayout* centralLayout = new QFormLayout(centralWidget); + centralLayout->setSpacing(6); + centralLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); + + const auto& bold = QStringLiteral("%1:"); + QLabel* providerLabel = new QLabel(bold.arg(tr("Provider"))); + providerLabel->setFocusPolicy(Qt::TabFocus); + centralLayout->setWidget(0, QFormLayout::LabelRole, providerLabel); + + QLabel* providerField = new QLabel(info.getSubjectName()); + providerField->setFocusPolicy(Qt::TabFocus); + centralLayout->setWidget(0, QFormLayout::FieldRole, providerField); + + centralWidget->setProperty("provider", info.getSubjectName()); + + QLabel* purposeLabel = new QLabel(bold.arg(tr("Purpose"))); + purposeLabel->setFocusPolicy(Qt::TabFocus); + centralLayout->setWidget(1, QFormLayout::LabelRole, purposeLabel); + + QLabel* purposeField = new QLabel(info.getPurpose()); + purposeField->setFocusPolicy(Qt::TabFocus); + centralLayout->setWidget(1, QFormLayout::FieldRole, purposeField); + + centralWidget->setProperty("usage", info.getPurpose()); + + QLabel* dataLabel = new QLabel(bold.arg(tr("Data"))); + dataLabel->setFocusPolicy(Qt::TabFocus); + dataLabel->setAlignment(Qt::AlignTop); + centralLayout->setWidget(2, QFormLayout::LabelRole, dataLabel); + + const auto& requestedData = AccessRoleAndRightsUtil::joinFromTechnicalName(info.getRequestedData()); + QLabel* requestedDataLabel = new QLabel(requestedData); + requestedDataLabel->setWordWrap(true); + requestedDataLabel->setFocusPolicy(Qt::TabFocus); + centralLayout->setWidget(2, QFormLayout::FieldRole, requestedDataLabel); + centralWidget->setProperty("requestedData", requestedData); + + mUi->historyTableWidget->setCellWidget(rowIndex, 1, centralWidget); + } + + mUi->historyTableWidget->resizeRowsToContents(); + mUi->historyTableWidget->sortByColumn(dateColumn); // Sort by date +} + + +bool HistoryWidget::eventFilter(QObject* pObject, QEvent* pEvent) +{ + if (pEvent->type() == QEvent::KeyPress) + { + QKeyEvent* pressed = static_cast(pEvent); + if ((pressed->key() == Qt::Key_Enter) || (pressed->key() == Qt::Key_Return) || (pressed->key() == Qt::Key_Space)) + { + const auto selectedIndexes = mUi->historyTableWidget->selectionModel()->selectedIndexes(); + for (auto index : selectedIndexes) + { + QWidget* tmpWidget = mUi->historyTableWidget->cellWidget(index.row(), dateColumn); + DetailDialog d(this); + d.setDetails(tmpWidget->property("termsOfUsage").toString()); + d.exec(); + } + return true; + } + } + + return QWidget::eventFilter(pObject, pEvent); +} + + +void HistoryWidget::deleteHistory() +{ + DeleteHistoryDialog* deleteHistoryDialog = new DeleteHistoryDialog(this); + if (deleteHistoryDialog->exec() == QDialog::Rejected) + { + return; + } + + auto& settings = Env::getSingleton()->getHistorySettings(); + settings.deleteSettings(deleteHistoryDialog->getTimePeriod()); + settings.save(); +} + + +void HistoryWidget::exportHistory() +{ + QString filename = tr("AusweisApp2.History.%1.pdf").arg(QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd"))); + filename = QFileDialog::getSaveFileName(this, + QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("Save"), + QDir::homePath() + QLatin1Char('/') + filename, + tr("PDF Documents") + QStringLiteral(" (*.pdf)")); + + PdfExporter exporter(filename); + exporter.exportHistory(); +} + + +void HistoryWidget::searchHistory() +{ + mUi->historyTableWidget->setVisible(true); + mUi->noResultWidget->setVisible(false); + + if (mUi->historySearch->text().isEmpty()) + { + for (int i = 0; i < mUi->historyTableWidget->rowCount(); ++i) + { + mUi->historyTableWidget->setRowHidden(i, false); + } + return; + } + + bool anyMatch = false; + + for (int i = 0; i < mUi->historyTableWidget->rowCount(); ++i) + { + bool match = false; + for (int j = 0; j < mUi->historyTableWidget->columnCount(); ++j) + { + QString tmpData; + QWidget* tmpWidget = mUi->historyTableWidget->cellWidget(i, j); + if (j == dateColumn) + { + tmpData = tmpWidget->property("date").toString(); + } + else + { + tmpData = tmpWidget->property("provider").toString() + QLatin1Char(' ') + tmpWidget->property("usage").toString() + QLatin1Char(' ') + tmpWidget->property("requestedData").toString(); + } + + if (tmpData.contains(mUi->historySearch->text(), Qt::CaseInsensitive)) + { + match = true; + anyMatch = true; + break; + } + } + mUi->historyTableWidget->setRowHidden(i, !match); + } + + mUi->historyTableWidget->setVisible(anyMatch); + mUi->noResultWidget->setVisible(!anyMatch); +} + + +void HistoryWidget::onItemClicked(const QModelIndex& pIndex) +{ + QModelIndex idx = pIndex; + + qDebug() << "selected model index:" << idx; + + if (pIndex.isValid()) + { + QWidget* tmpWidget = mUi->historyTableWidget->cellWidget(idx.row(), dateColumn); + DetailDialog d(this); + d.setDetails(tmpWidget->property("termsOfUsage").toString()); + d.exec(); + } +} + + +void HistoryWidget::changeEvent(QEvent* pEvent) +{ + if (pEvent->type() == QEvent::LanguageChange) + { + init(); + searchHistory(); + mUi->retranslateUi(this); + } + QWidget::changeEvent(pEvent); +} diff --git a/src/ui/widget/HistoryWidget.h b/src/ui/widget/HistoryWidget.h new file mode 100644 index 0000000..bf1a783 --- /dev/null +++ b/src/ui/widget/HistoryWidget.h @@ -0,0 +1,61 @@ +/*! + * \brief Show history entries. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Ui +{ +class HistoryWidget; +} // namespace Ui + +namespace governikus +{ + +class HistoryDetailWidget; + +class HistoryWidget + : public QWidget +{ + Q_OBJECT + + private: + static const int dateColumn = 0; + QScopedPointer mUi; + HistoryDetailWidget* mHistoryDetailWidget; + virtual bool eventFilter(QObject* pWatched, QEvent* pEvent) override; + virtual void paintEvent(QPaintEvent*) override; + void init(); + + private Q_SLOTS: + void updateTable(); + + protected: + virtual void changeEvent(QEvent* pEvent) override; + + public: + HistoryWidget(QWidget* pParent = nullptr); + virtual ~HistoryWidget() override; + QWidget* getDetailActivatingWidget() const; + + public Q_SLOTS: + void onCheckBoxStateChanged(int pState); + void deleteHistory(); + void exportHistory(); + void searchHistory(); + void onItemClicked(const QModelIndex& pIndex); +}; + +} // namespace governikus diff --git a/src/ui/widget/HistoryWidget.ui b/src/ui/widget/HistoryWidget.ui new file mode 100644 index 0000000..71977cd --- /dev/null +++ b/src/ui/widget/HistoryWidget.ui @@ -0,0 +1,197 @@ + + + HistoryWidget + + + + 0 + 0 + 599 + 666 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Qt::TabFocus + + + This page displays the history of your successful authentications. Double-click on a service provider for more information. You can delete parts or the entire history. You can also save the history as a PDF file. + + + true + + + + + + + + + Qt::TabFocus + + + Search: + + + + + + + Please enter your search + + + Please enter your search + + + + + + + + + QAbstractScrollArea::AdjustToContents + + + false + + + true + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectItems + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + + + true + + + 150 + + + false + + + true + + + + + + + + 5 + + + + + No matching history entries were found. Please modify your search criteria. + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + Qt::TabFocus + + + save history: + + + History: + + + + + + + save history + + + + + + save + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Delete history... + + + + + + + save history as PDF + + + Save as PDF... + + + + + + + + + + + + diff --git a/src/ui/widget/LogFileSaveDialog.cpp b/src/ui/widget/LogFileSaveDialog.cpp new file mode 100644 index 0000000..ac27bd4 --- /dev/null +++ b/src/ui/widget/LogFileSaveDialog.cpp @@ -0,0 +1,84 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "Env.h" +#include "LogFileSaveDialog.h" +#include "LogHandler.h" + +#include +#include +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(gui) + +using namespace governikus; + + +QString LogFileSaveDialog::getSaveFileName(QWidget* pParent, const QString& pSource) +{ + Q_ASSERT(mFileDialog.isNull()); + + const QString caption(QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("Save")); + const QDateTime creationDateTime = pSource.isEmpty() ? Env::getSingleton()->getCurrentLogfileDate() : LogHandler::getFileDate(pSource); + const QString filename = QStringLiteral("AusweisApp2.%1.log").arg(creationDateTime.toString(QStringLiteral("yyyy-MM-dd_HH-mm"))); + const QString directory = QDir::homePath() + QLatin1Char('/') + filename; + const QString filter = QStringLiteral("*.log"); + + mFileDialog = QPointer(new QFileDialog(pParent, caption, directory, filter)); + mFileDialog->setAcceptMode(QFileDialog::AcceptSave); + + const QStringList fileNames = mFileDialog->exec() == QDialog::Accepted ? mFileDialog->selectedFiles() : QStringList(); + mFileDialog->deleteLater(); + + return fileNames.isEmpty() ? QString() : fileNames.first(); +} + + +void LogFileSaveDialog::saveLogFile(QWidget* pParent, const QString& pSource) +{ + Q_ASSERT(pParent != nullptr); + + QString filename = getSaveFileName(pParent, pSource); + if (!filename.isEmpty()) // if user does not select "cancel" + { + if (!filename.endsWith(QLatin1String(".log"), Qt::CaseSensitivity::CaseInsensitive) + && !filename.endsWith(QLatin1String(".txt"), Qt::CaseSensitivity::CaseInsensitive)) + { + filename += QStringLiteral(".log"); + } + + qCDebug(gui) << "File location:" << filename; + + if (QFile::exists(filename)) + { + const bool deleted = QFile::remove(filename); + qCDebug(gui) << "Delete file location:" << deleted; + } + + const bool copied = pSource.isEmpty() ? Env::getSingleton()->copy(filename) : QFile::copy(pSource, filename); + qCDebug(gui) << "Copy log to file location:" << copied; + if (!copied) + { + QMessageBox box(pParent); + box.setWindowTitle(QApplication::applicationName() + QStringLiteral(" - ") + tr("File error")); + box.setWindowModality(Qt::ApplicationModal); + box.setIcon(QMessageBox::Warning); + box.setWindowFlags(box.windowFlags() & ~Qt::WindowContextHelpButtonHint); + box.setText(tr("An error occurred while saving the file.")); + box.setStandardButtons(QMessageBox::Ok); + box.button(QMessageBox::Ok)->setFocus(); + box.exec(); + } + } +} + + +void LogFileSaveDialog::closeActiveDialogs() +{ + if (mFileDialog) + { + mFileDialog->reject(); + } +} diff --git a/src/ui/widget/LogFileSaveDialog.h b/src/ui/widget/LogFileSaveDialog.h new file mode 100644 index 0000000..8f8d299 --- /dev/null +++ b/src/ui/widget/LogFileSaveDialog.h @@ -0,0 +1,30 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include +#include + +namespace governikus +{ + +class LogFileSaveDialog + : public QObject +{ + Q_OBJECT + + private: + QPointer mFileDialog; + + QString getSaveFileName(QWidget* pParent, const QString& pSource); + + public: + void saveLogFile(QWidget* pParent, const QString& pSource = QString()); + + void closeActiveDialogs(); +}; + +} // namespace governikus diff --git a/src/ui/widget/LogFilesDialog.cpp b/src/ui/widget/LogFilesDialog.cpp new file mode 100644 index 0000000..d725ecf --- /dev/null +++ b/src/ui/widget/LogFilesDialog.cpp @@ -0,0 +1,207 @@ +/*! + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "LogFilesDialog.h" +#include "ui_LogFilesDialog.h" + +#include "HelpAction.h" +#include "LanguageLoader.h" +#include "LogHandler.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(gui) + +using namespace governikus; + + +LogFilesDialog::LogFilesDialog(QWidget* pParent) + : QDialog(pParent) + , mUi(new Ui::LogFilesDialog) + , mFileDialog() +{ + mUi->setupUi(this); + + setAttribute(Qt::WA_DeleteOnClose, true); + setWindowFlags(Qt::Dialog | Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowMinMaxButtonsHint); + setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("Log")); + + connect(mUi->saveButton, &QAbstractButton::clicked, this, &LogFilesDialog::onSaveButtonClicked); + connect(mUi->deleteButton, &QAbstractButton::clicked, this, &LogFilesDialog::onDeleteButtonClicked); + connect(mUi->closeButton, &QAbstractButton::clicked, this, &LogFilesDialog::close); + + init(); +} + + +LogFilesDialog::~LogFilesDialog() +{ +} + + +void LogFilesDialog::init() +{ + const auto& logHandler = Env::getSingleton(); + mUi->logFilesComboBox->clear(); + + auto model = new QStandardItemModel(this); + mUi->logFilesComboBox->setModel(model); + + const auto& otherLogs = logHandler->getOtherLogfiles(); + QList items; + items.reserve(otherLogs.size() + 1); + items << new QStandardItem(tr("Current log")); + for (const auto& entry : otherLogs) + { + auto date = LogHandler::getFileDate(entry); + auto item = new QStandardItem(LanguageLoader::getInstance().getUsedLocale().toString(date, tr("dd.MM.yyyy hh:mm:ss AP"))); + item->setData(date, Qt::UserRole); + item->setData(entry.absoluteFilePath()); + items << item; + } + + mUi->logFilesComboBox->setEnabled(!otherLogs.isEmpty()); + mUi->deleteButton->setEnabled(!otherLogs.isEmpty()); + mUi->saveButton->setEnabled(logHandler->useLogfile()); + + model->invisibleRootItem()->appendRows(items); + model->setSortRole(Qt::UserRole); + model->sort(0, Qt::DescendingOrder); + + connect(mUi->logFilesComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &LogFilesDialog::onCurrentIndexChanged); + + mUi->plainTextEdit->clear(); + appendLoggingDump(QString::fromUtf8(logHandler->getBacklog()).toHtmlEscaped()); + mUi->plainTextEdit->moveCursor(QTextCursor::Start); + connect(logHandler, &LogHandler::fireLog, this, &LogFilesDialog::doLogMsg); +} + + +void LogFilesDialog::appendLoggingDump(const QString& pLog) +{ + mUi->plainTextEdit->appendHtml(QStringLiteral("
%1
").arg(pLog)); +} + + +void LogFilesDialog::doLogMsg(const QString& pMsg) +{ + appendLoggingDump(pMsg.toHtmlEscaped()); +} + + +void LogFilesDialog::onCurrentIndexChanged(int pIndex) +{ + const auto& logHandler = Env::getSingleton(); + + mUi->plainTextEdit->clear(); + mUi->saveButton->setEnabled(logHandler->useLogfile() || pIndex > 0); + + if (pIndex == 0) + { + connect(logHandler, &LogHandler::fireLog, this, &LogFilesDialog::doLogMsg); + + appendLoggingDump(QString::fromUtf8(logHandler->getBacklog()).toHtmlEscaped()); + mUi->plainTextEdit->moveCursor(QTextCursor::Start); + } + else + { + disconnect(logHandler, &LogHandler::fireLog, this, &LogFilesDialog::doLogMsg); + + QFile file(mUi->logFilesComboBox->itemData(pIndex, Qt::UserRole + 1).toString()); + if (file.size() < 3145728) + { + if (file.open(QIODevice::ReadOnly)) + { + appendLoggingDump(QString::fromUtf8(file.readAll())); + } + else + { + appendLoggingDump(tr("File could not be opened: ") + file.fileName()); + } + } + else + { + appendLoggingDump(tr("File is larger than 3 MB and can not be displayed: ") + file.fileName()); + } + } +} + + +void LogFilesDialog::onDeleteButtonClicked() +{ + QMessageBox box(this); + box.setWindowTitle(tr("Delete log files")); + box.setWindowModality(Qt::ApplicationModal); + box.setIcon(QMessageBox::Question); + box.setWindowFlags(box.windowFlags() & ~Qt::WindowContextHelpButtonHint); + box.setText(tr("Do you really want to delete all old log files?")); + box.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + box.button(QMessageBox::Yes)->setFocus(); + + if (box.exec() == QMessageBox::Yes) + { + Env::getSingleton()->removeOtherLogfiles(); + init(); + } +} + + +void LogFilesDialog::onSaveButtonClicked() +{ + const int index = mUi->logFilesComboBox->currentIndex(); + + QString source; + if (index != 0) + { + source = mUi->logFilesComboBox->itemData(index, Qt::UserRole + 1).toString(); + } + + saveLogFile(this, source); +} + + +bool LogFilesDialog::eventFilter(QObject* pObject, QEvent* pEvent) +{ + if (pEvent->type() == QEvent::KeyPress) + { + QKeyEvent* keyEvent = static_cast(pEvent); + if (keyEvent->key() == Qt::Key_F1) + { + HelpAction::openContextHelp(); + return true; + } + } + return QDialog::eventFilter(pObject, pEvent); +} + + +void LogFilesDialog::changeEvent(QEvent* pEvent) +{ + if (pEvent->type() == QEvent::LanguageChange) + { + mUi->retranslateUi(this); + } + QWidget::changeEvent(pEvent); +} + + +void LogFilesDialog::saveLogFile(QWidget* pParent, const QString& pSource) +{ + Q_ASSERT(pParent != nullptr); + mFileDialog.saveLogFile(pParent, pSource); +} + + +void LogFilesDialog::closeActiveDialogs() +{ + mFileDialog.closeActiveDialogs(); +} diff --git a/src/ui/widget/LogFilesDialog.h b/src/ui/widget/LogFilesDialog.h new file mode 100644 index 0000000..8fb5192 --- /dev/null +++ b/src/ui/widget/LogFilesDialog.h @@ -0,0 +1,54 @@ +/*! + * \brief Dialog for display the old log files. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "LogFileSaveDialog.h" + +#include +#include +#include +#include + +namespace Ui +{ +class LogFilesDialog; +} // namespace Ui + +namespace governikus +{ + +class LogFilesDialog + : public QDialog +{ + Q_OBJECT + + public: + void saveLogFile(QWidget* pParent, const QString& pSource = QString()); + + LogFilesDialog(QWidget* pParent = nullptr); + virtual ~LogFilesDialog() override; + void closeActiveDialogs(); + + protected: + virtual bool eventFilter(QObject* pObject, QEvent* pEvent) override; + virtual void changeEvent(QEvent* pEvent) override; + + private: + QScopedPointer mUi; + LogFileSaveDialog mFileDialog; + + void init(); + void appendLoggingDump(const QString& pLog); + + private Q_SLOTS: + void doLogMsg(const QString& pMsg); + void onSaveButtonClicked(); + void onCurrentIndexChanged(int pIndex); + void onDeleteButtonClicked(); +}; + +} // namespace governikus diff --git a/src/widget/LogFilesDialog.ui b/src/ui/widget/LogFilesDialog.ui similarity index 100% rename from src/widget/LogFilesDialog.ui rename to src/ui/widget/LogFilesDialog.ui diff --git a/src/widget/PinSettingsInfoWidget.cpp b/src/ui/widget/PinSettingsInfoWidget.cpp similarity index 100% rename from src/widget/PinSettingsInfoWidget.cpp rename to src/ui/widget/PinSettingsInfoWidget.cpp diff --git a/src/ui/widget/PinSettingsInfoWidget.h b/src/ui/widget/PinSettingsInfoWidget.h new file mode 100644 index 0000000..356a588 --- /dev/null +++ b/src/ui/widget/PinSettingsInfoWidget.h @@ -0,0 +1,41 @@ +/*! + * \brief Widget for PIN settings information. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include + +namespace Ui +{ +class PinSettingsInfoWidget; +} // namespace Ui + +namespace governikus +{ + +class PinSettingsInfoWidget + : public QWidget +{ + Q_OBJECT + + public: + PinSettingsInfoWidget(QWidget* pParent = nullptr); + virtual ~PinSettingsInfoWidget() override; + + void setInfoTitle(const QString& pTitle); + void setInfoDescription(const QString& pDescription); + + protected: + virtual void changeEvent(QEvent* pEvent) override; + + private: + QScopedPointer mUi; + + virtual void paintEvent(QPaintEvent*) override; +}; + +} // namespace governikus diff --git a/src/widget/PinSettingsInfoWidget.ui b/src/ui/widget/PinSettingsInfoWidget.ui similarity index 100% rename from src/widget/PinSettingsInfoWidget.ui rename to src/ui/widget/PinSettingsInfoWidget.ui diff --git a/src/ui/widget/PinSettingsWidget.cpp b/src/ui/widget/PinSettingsWidget.cpp new file mode 100644 index 0000000..f542982 --- /dev/null +++ b/src/ui/widget/PinSettingsWidget.cpp @@ -0,0 +1,801 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "PinSettingsWidget.h" +#include "ui_PinSettingsWidget.h" + +#include "FuncUtils.h" +#include "generic/PasswordEdit.h" +#include "ReaderConfiguration.h" +#include "ReaderManager.h" +#include "SmartCardDefinitions.h" + +#include +#include +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(gui) + +using namespace governikus; + +PinSettingsWidget::PinSettingsWidget(QWidget* pParent) + : QWidget(pParent) + , mUi(new Ui::PinSettingsWidget()) + , mMode(Mode::Normal) + , mContext() + , mRetryCounter(3) + , mPinDeactivated(false) + , mPinButtonEnabled(false) + , mCancelButtonEnabled(false) + , mPinSettingsInfoTitle() + , mPinSettingsInfoDescription() + , mRandomPinDialog() +{ + mUi->setupUi(this); + + QRegularExpression onlyNumbersExpression(QStringLiteral("[0-9]*")); + + mUi->canEdit->setMaxLength(6); + mUi->canEdit->configureValidation(onlyNumbersExpression, tr("Only digits (0-9) are allowed.")); + connect(mUi->canEdit, &PasswordEdit::textEdited, this, &PinSettingsWidget::onCanTextEdited); + + mUi->oldPinEdit->setMaxLength(6); + mUi->oldPinEdit->configureValidation(onlyNumbersExpression, tr("Only digits (0-9) are allowed.")); + connect(mUi->oldPinEdit, &PasswordEdit::textEdited, this, &PinSettingsWidget::onOldPinTextEdited); + connect(mUi->oldPinEdit, &PasswordEdit::fireBackspacePressedAndEmpty, this, [this] { + if (mUi->canEdit->isVisible()) + { + mUi->canEdit->removeLastCharacter(); + mUi->canEdit->setFocus(); + } + }); + + mUi->newPinEdit->setMaxLength(6); + mUi->newPinEdit->configureValidation(onlyNumbersExpression, tr("Only digits (0-9) are allowed.")); + connect(mUi->newPinEdit, &PasswordEdit::textEdited, this, &PinSettingsWidget::onNewPinTextEdited); + connect(mUi->newPinEdit, &PasswordEdit::fireBackspacePressedAndEmpty, this, [this] { + mUi->oldPinEdit->removeLastCharacter(); + mUi->oldPinEdit->setFocus(); + }); + + mUi->repeatNewPinEdit->setMaxLength(6); + mUi->repeatNewPinEdit->configureValidation(onlyNumbersExpression, tr("Only digits (0-9) are allowed.")); + connect(mUi->repeatNewPinEdit, &PasswordEdit::textEdited, this, &PinSettingsWidget::onRepeatNewPinTextEdited); + connect(mUi->repeatNewPinEdit, &PasswordEdit::fireBackspacePressedAndEmpty, this, [this] { + mUi->newPinEdit->removeLastCharacter(); + mUi->newPinEdit->setFocus(); + }); + + mUi->pukEdit->setMaxLength(10); + mUi->pukEdit->configureValidation(onlyNumbersExpression, tr("Only digits (0-9) are allowed.")); + connect(mUi->pukEdit, &PasswordEdit::textEdited, this, &PinSettingsWidget::onPukTextEdited); + + mUi->canRandomPinButton->setIcon(QPixmap(QStringLiteral(":/images/randompin/screen_keyboard.png"))); + mUi->canRandomPinButton->setIconSize(QSize(44, 26)); + connect(mUi->canRandomPinButton, &QAbstractButton::clicked, this, &PinSettingsWidget::onRandomPinButtonClicked); + + mUi->oldRandomPinButton->setIcon(QPixmap(QStringLiteral(":/images/randompin/screen_keyboard.png"))); + mUi->oldRandomPinButton->setIconSize(QSize(44, 26)); + connect(mUi->oldRandomPinButton, &QAbstractButton::clicked, this, &PinSettingsWidget::onRandomPinButtonClicked); + + mUi->newRandomPinButton->setIcon(QPixmap(QStringLiteral(":/images/randompin/screen_keyboard.png"))); + mUi->newRandomPinButton->setIconSize(QSize(44, 26)); + connect(mUi->newRandomPinButton, &QAbstractButton::clicked, this, &PinSettingsWidget::onRandomPinButtonClicked); + + mUi->repeatNewRandomPinButton->setIcon(QPixmap(QStringLiteral(":/images/randompin/screen_keyboard.png"))); + mUi->repeatNewRandomPinButton->setIconSize(QSize(44, 26)); + connect(mUi->repeatNewRandomPinButton, &QAbstractButton::clicked, this, &PinSettingsWidget::onRandomPinButtonClicked); + + mUi->pukRandomPinButton->setIcon(QPixmap(QStringLiteral(":/images/randompin/screen_keyboard.png"))); + mUi->pukRandomPinButton->setIconSize(QSize(44, 26)); + connect(mUi->pukRandomPinButton, &QAbstractButton::clicked, this, &PinSettingsWidget::onRandomPukButtonClicked); +} + + +PinSettingsWidget::~PinSettingsWidget() +{ +} + + +void PinSettingsWidget::setContext(const QSharedPointer& pContext) +{ + mContext = pContext; + if (mContext) + { + if (mUi->stackedWidget->currentWidget() == mUi->changePinComfortPage) + { + mUi->changePinComfortDetailsStackedWidget->setCurrentWidget(mUi->changePinComfortDetailsInProgressPage); + mPinSettingsInfoDescription = mUi->changePinComfortInProgressLabel->text(); + } + else if (mUi->stackedWidget->currentWidget() == mUi->changePinBasicPage) + { + setUserInputEnabled(false); + } + } + else + { + updateReaders(); + } +} + + +void PinSettingsWidget::cancelWorkflow() +{ + if (mContext) + { + Q_EMIT mContext->fireCancelWorkflow(); + } +} + + +void PinSettingsWidget::continueWorkflow() +{ + if (mContext) + { + updatePinButton(false); + mContext->setStateApproved(); + } +} + + +QString PinSettingsWidget::getCan() const +{ + return mUi->canEdit->text(); +} + + +QString PinSettingsWidget::getPin() const +{ + return mUi->oldPinEdit->text(); +} + + +QString PinSettingsWidget::getPuk() const +{ + return mUi->pukEdit->text(); +} + + +QString PinSettingsWidget::getNewPin() const +{ + return mUi->newPinEdit->text(); +} + + +void PinSettingsWidget::updatePasswordFields() +{ + const auto& readerManager = Env::getSingleton(); + QVector readersWithNPA = getReaderWithNPA(readerManager->getReaderInfos()); + mRetryCounter = 3; + updateReadersForOneNPA(readersWithNPA.at(0)); +} + + +void PinSettingsWidget::updatePinButton(bool pEnabled) +{ + mPinButtonEnabled = pEnabled; + Q_EMIT fireButtonEnabledUpdated(); +} + + +void PinSettingsWidget::clearPasswords() +{ + mUi->canEdit->clear(); + mUi->oldPinEdit->clear(); + mUi->newPinEdit->clear(); + mUi->repeatNewPinEdit->clear(); + mUi->pukEdit->clear(); + setUserInputEnabled(true); +} + + +void PinSettingsWidget::setPasswordFocus() +{ + if (isCanFieldVisible()) + { + focusCAN(); + } + else if (isPukFieldVisible()) + { + focusPUK(); + } + else + { + focusPIN(); + } +} + + +void PinSettingsWidget::setMode(PinSettingsWidget::Mode pMode) +{ + mMode = pMode; +} + + +QString PinSettingsWidget::getButtonText() const +{ + return mRetryCounter == 0 && !mPinDeactivated ? tr("Enter PUK") : tr("Change PIN"); +} + + +QVector PinSettingsWidget::getReaderWithNPA(const QVector& pReaderInfos) +{ + return filter([](const ReaderInfo& i){return i.hasEidCard();}, pReaderInfos); +} + + +void PinSettingsWidget::updateReadersWithoutNPA(const QVector& pReaderInfos) +{ + mMode = Mode::Normal; + mUi->headerStackedWidget->setCurrentWidget(mUi->errorNoNpaHeaderPage); + + bool basicReaderPage = false; + if (pReaderInfos.size() == 1) + { + const ReaderInfo& readerInfo = pReaderInfos.at(0); + if (readerInfo.isBasicReader()) + { + setupPinBasicPage(readerInfo); + basicReaderPage = true; + } + else + { + QPixmap pixmap(readerInfo.getReaderConfigurationInfo().getIcon()->lookupPath()); + mUi->noNpaLabel->setPixmap(pixmap.scaledToWidth(SCALEWIDTH, Qt::SmoothTransformation)); + } + } + else + { + QPixmap pixmap(ReaderConfiguration::getMultipleReaderIconPath()); + mUi->noNpaLabel->setPixmap(pixmap.scaledToWidth(250, Qt::SmoothTransformation)); + } + + mUi->stackedWidget->setCurrentWidget(basicReaderPage ? mUi->changePinBasicPage : mUi->errorNoNpaPage); + mUi->repeatNewPinEdit->setDigitFieldInvalid(false, tr("PIN correct.")); +} + + +void PinSettingsWidget::setUseScreenKeyboard(bool pUseScreenKeyboard) +{ + mUi->canRandomPinButton->setVisible(pUseScreenKeyboard); + mUi->oldRandomPinButton->setVisible(pUseScreenKeyboard); + mUi->newRandomPinButton->setVisible(pUseScreenKeyboard); + mUi->repeatNewRandomPinButton->setVisible(pUseScreenKeyboard); + mUi->pukRandomPinButton->setVisible(pUseScreenKeyboard); +} + + +bool PinSettingsWidget::getPinButtonEnabled() const +{ + return mPinButtonEnabled; +} + + +bool PinSettingsWidget::getCancelButtonEnabled() const +{ + return mCancelButtonEnabled; +} + + +void PinSettingsWidget::fillInfoDescription(const QString& pTitle, const QString& pMessage) +{ + mPinSettingsInfoTitle = pTitle; + mPinSettingsInfoDescription = pMessage; +} + + +bool PinSettingsWidget::updateReadersForOneNPA(const ReaderInfo& pReaderInfo) +{ + mRetryCounter = pReaderInfo.getRetryCounter(); + mPinDeactivated = pReaderInfo.isPinDeactivated(); + + if (mMode == Mode::AfterPinChange) + { + setupPinSuccessfullyChangedPage(pReaderInfo); + mUi->headerStackedWidget->setCurrentWidget(mUi->pinSuccessHeaderPage); + mUi->stackedWidget->setCurrentWidget(mUi->pinSuccessPage); + clearPasswords(); + return true; + } + + if (pReaderInfo.isBasicReader()) + { + setupPinBasicPage(pReaderInfo); + mUi->stackedWidget->setCurrentWidget(mUi->changePinBasicPage); + } + else + { + updateCancelButton(false); + setupPinComfortPage(pReaderInfo); + mUi->stackedWidget->setCurrentWidget(mUi->changePinComfortPage); + } + + if (!pReaderInfo.sufficientApduLength()) + { + if (!pReaderInfo.isBasicReader()) + { + mUi->stackedWidget->setCurrentWidget(mUi->errorNoNpaPage); + } + + mUi->headerStackedWidget->setCurrentWidget(mUi->errorInsufficientApduLength); + return false; + } + + if (mPinDeactivated) + { + if (!pReaderInfo.isBasicReader()) + { + QPixmap pixmap(pReaderInfo.getReaderConfigurationInfo().getIconWithNPA()->lookupPath()); + mUi->deactivatedReaderImageLabel->setPixmap(pixmap.scaledToWidth(SCALEWIDTH, Qt::SmoothTransformation)); + mUi->stackedWidget->setCurrentWidget(mUi->errorPinDeactivatedPage); + } + + mUi->headerStackedWidget->setCurrentWidget(mUi->errorPinDeactivatedHeaderPage); + return false; + } + + return !pReaderInfo.isBasicReader(); +} + + +void PinSettingsWidget::updateReaders() +{ + if (mContext) + { + return; + } + + const ReaderManager& readerManager = *Env::getSingleton(); + if (readerManager.getReaderInfos().isEmpty()) + { + QPixmap pixmap(ReaderConfiguration::getNoReaderFoundIconPath()); + mUi->noReaderLabel->setPixmap(pixmap.scaledToWidth(SCALEWIDTH, Qt::SmoothTransformation)); + mUi->headerStackedWidget->setCurrentWidget(mUi->errorNoReaderHeaderPage); + mUi->stackedWidget->setCurrentWidget(mUi->errorNoReaderPage); + return; + } + + QVector readersWithNPA = getReaderWithNPA(readerManager.getReaderInfos()); + mRetryCounter = 3; + bool enableButton = false; + + if (readersWithNPA.size() == 0) + { + updateReadersWithoutNPA(readerManager.getReaderInfos(ReaderFilter::UniqueReaderTypes)); + } + else if (readersWithNPA.size() == 1) + { + enableButton = updateReadersForOneNPA(readersWithNPA.at(0)); + } + else + { + mMode = Mode::Normal; + QPixmap pixmap(ReaderConfiguration::getMultipleReaderIconPath()); + mUi->multipleReaderLabel->setPixmap(pixmap.scaledToWidth(SCALEWIDTH, Qt::SmoothTransformation)); + mUi->headerStackedWidget->setCurrentWidget(mUi->errorMultipleNpasHeaderPage); + mUi->stackedWidget->setCurrentWidget(mUi->errorMultipleNpasPage); + } + + updatePinButton(enableButton); +} + + +void PinSettingsWidget::onBackspacePressedOnApply() +{ + mUi->repeatNewPinEdit->removeLastCharacter(); + mUi->repeatNewPinEdit->setFocus(); + onRepeatNewPinTextEdited(); +} + + +void PinSettingsWidget::paintEvent(QPaintEvent*) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} + + +void PinSettingsWidget::showEvent(QShowEvent* pEvent) +{ + const auto readerManager = Env::getSingleton(); + connect(readerManager, &ReaderManager::fireReaderEvent, this, &PinSettingsWidget::updateReaders); + connect(readerManager, &ReaderManager::fireCardRetryCounterChanged, this, &PinSettingsWidget::updateReaders); + + updateReaders(); + QWidget::showEvent(pEvent); +} + + +void PinSettingsWidget::hideEvent(QHideEvent* pEvent) +{ + const auto readerManager = Env::getSingleton(); + disconnect(readerManager, &ReaderManager::fireReaderEvent, this, &PinSettingsWidget::updateReaders); + disconnect(readerManager, &ReaderManager::fireCardRetryCounterChanged, this, &PinSettingsWidget::updateReaders); + + QWidget::hideEvent(pEvent); + + // Reset the mode, so that the next time the users sees this widget, it doesn't show the state of the previous + // action. + mMode = Mode::Normal; + + if (mRandomPinDialog && mRandomPinDialog->isVisible()) + { + // close the PinPad in case the tab is hidden, + // e.g. an authentication was started, so the PIN change is aborted. + mRandomPinDialog->reject(); + } +} + + +void PinSettingsWidget::onCanTextEdited(const QString& pText) +{ + if (!pText.isNull() && !pText.isEmpty()) + { + mUi->canEdit->setText(pText); + } + + if (isCanFieldVisible()) + { + if (mUi->canEdit->text().length() == 6) + { + mUi->oldPinEdit->setEnabled(true); + mUi->oldRandomPinButton->setEnabled(true); + + QTimer::singleShot(300, this, &PinSettingsWidget::focusPIN); + } + else + { + mUi->oldPinEdit->setEnabled(false); + mUi->oldRandomPinButton->setEnabled(false); + } + } + else + { + mUi->oldPinEdit->setEnabled(true); + mUi->oldRandomPinButton->setEnabled(true); + QTimer::singleShot(300, this, &PinSettingsWidget::focusPIN); + } + + mUi->oldPinEdit->clear(); + onOldPinTextEdited(); +} + + +void PinSettingsWidget::onOldPinTextEdited(const QString& pText) +{ + if (!pText.isNull() && !pText.isEmpty()) + { + mUi->oldPinEdit->setText(pText); + } + + bool enable = mUi->oldPinEdit->text().length() >= 5; + mUi->newPinEdit->setEnabled(enable); + mUi->newRandomPinButton->setEnabled(enable); + + if (mUi->oldPinEdit->text().length() == 6) + { + mUi->newPinEdit->setFocus(); + mUi->newPinEdit->setCursorPosition(0); + } + + mUi->newPinEdit->clear(); + onNewPinTextEdited(); +} + + +void PinSettingsWidget::onNewPinTextEdited(const QString& pText) +{ + if (!pText.isNull() && !pText.isEmpty()) + { + mUi->newPinEdit->setText(pText); + } + + bool enable = mUi->newPinEdit->text().length() == 6; + mUi->repeatNewPinEdit->setEnabled(enable); + mUi->repeatNewRandomPinButton->setEnabled(enable); + if (mUi->newPinEdit->text().length() == 6) + { + mUi->repeatNewPinEdit->setFocus(); + mUi->repeatNewPinEdit->setCursorPosition(0); + } + + mUi->repeatNewPinEdit->clear(); + onRepeatNewPinTextEdited(); +} + + +void PinSettingsWidget::onRepeatNewPinTextEdited(const QString& pText) +{ + if (!pText.isNull() && !pText.isEmpty()) + { + mUi->repeatNewPinEdit->setText(pText); + } + + if (!mUi->repeatNewPinEdit->isEnabled() || mUi->newPinEdit->text().startsWith(mUi->repeatNewPinEdit->text()) || mUi->repeatNewPinEdit->text().length() != 6) + { + mUi->repeatNewPinEdit->setDigitFieldInvalid(false, QString()); + + bool inputOk = mUi->repeatNewPinEdit->text().length() == 6 && mUi->repeatNewPinEdit->text() == mUi->newPinEdit->text(); + updatePinButton(inputOk); + } + else + { + QString invalidMessage = tr("The PIN in the field \"%1\" does not match the PIN in the field \"%2\".").arg(mUi->repeatNewPinEditLabel->text().replace(QStringLiteral(":"), QLatin1String("")), mUi->newPinEditLabel->text().replace(QStringLiteral(":"), QLatin1String(""))); + mUi->repeatNewPinEdit->setDigitFieldInvalid(true, invalidMessage); + } +} + + +void PinSettingsWidget::onPukTextEdited(const QString& pText) +{ + if (!pText.isNull() && !pText.isEmpty()) + { + mUi->pukEdit->setText(pText); + } + updatePinButton(mUi->pukEdit->text().length() == 10); +} + + +void PinSettingsWidget::updateCancelButton(bool pEnabled) +{ + mCancelButtonEnabled = pEnabled; + Q_EMIT fireButtonEnabledUpdated(); +} + + +void PinSettingsWidget::setUserInputEnabled(bool pEnabled) +{ + updateCancelButton(pEnabled); + mUi->canEdit->setEnabled(pEnabled); + mUi->oldPinEdit->setEnabled(pEnabled); + mUi->newPinEdit->setEnabled(pEnabled); + mUi->repeatNewPinEdit->setEnabled(pEnabled); + mUi->canRandomPinButton->setEnabled(pEnabled); + mUi->oldRandomPinButton->setEnabled(pEnabled); + mUi->newRandomPinButton->setEnabled(pEnabled); + mUi->repeatNewRandomPinButton->setEnabled(pEnabled); +} + + +void PinSettingsWidget::setupPinBasicPage(const ReaderInfo& pReaderInfo) +{ + clearPasswords(); + + bool hasCard = pReaderInfo.hasEidCard(); + + QPixmap pixmap; + if (hasCard) + { + pixmap = pReaderInfo.getReaderConfigurationInfo().getIconWithNPA()->lookupPath(); + mUi->basicReaderImageLabel->setAccessibleName(tr("card inserted")); + } + else + { + pixmap = pReaderInfo.getReaderConfigurationInfo().getIcon()->lookupPath(); + mUi->basicReaderImageLabel->setAccessibleName(tr("no card inserted")); + } + + mUi->basicReaderImageLabel->setPixmap(pixmap.scaledToWidth(SCALEWIDTH, Qt::SmoothTransformation)); + + bool canEditVisible = false; + bool pukEditVisible = false; + + if (hasCard) + { + setupChangePinHeader(pReaderInfo.getRetryCounter(), true); + + switch (pReaderInfo.getRetryCounter()) + { + case 0: + pukEditVisible = true; + break; + + case 1: + canEditVisible = true; + break; + + default: + break; + } + } + + if (mPinDeactivated) + { + pukEditVisible = false; + canEditVisible = false; + hasCard = false; + } + + mUi->canEditStackedWidget->setCurrentWidget(canEditVisible ? mUi->canEditPage : mUi->noCanEditPage); + mUi->canEditLabelStackedWidget->setCurrentWidget(canEditVisible ? mUi->canEditLabelPage : mUi->noCanEditLabelPage); + + mUi->basicReaderPukStackedWidget->setCurrentWidget(pukEditVisible ? mUi->basicReaderPukPage : mUi->basicReaderPinPage); + + mUi->canEditLabel->setEnabled(hasCard); + mUi->oldPinEditLabel->setEnabled(hasCard); + mUi->newPinEditLabel->setEnabled(hasCard); + mUi->repeatNewPinEditLabel->setEnabled(hasCard); + mUi->canEdit->setEnabled(hasCard); + mUi->oldPinEdit->setEnabled(hasCard); + mUi->newPinEdit->setEnabled(hasCard); + mUi->repeatNewPinEdit->setEnabled(hasCard); + mUi->canRandomPinButton->setEnabled(hasCard); + mUi->oldRandomPinButton->setEnabled(hasCard); + mUi->newRandomPinButton->setEnabled(hasCard); + mUi->repeatNewRandomPinButton->setEnabled(hasCard); + + if (hasCard) + { + if (isCanFieldVisible()) + { + QTimer::singleShot(300, this, &PinSettingsWidget::focusCAN); + } + else if (pukEditVisible) + { + QTimer::singleShot(300, this, &PinSettingsWidget::focusPUK); + } + + if (!pukEditVisible) + { + onCanTextEdited(); + } + } +} + + +void PinSettingsWidget::focusPUK() +{ + mUi->pukEdit->setFocus(); + mUi->pukEdit->setCursorPosition(0); +} + + +void PinSettingsWidget::focusPIN() +{ + mUi->oldPinEdit->setFocus(); + mUi->oldPinEdit->setCursorPosition(0); +} + + +void PinSettingsWidget::focusCAN() +{ + mUi->canEdit->setFocus(); + mUi->canEdit->setCursorPosition(0); +} + + +void PinSettingsWidget::setupPinComfortPage(const ReaderInfo& pReaderInfo) +{ + QPixmap pixmap(pReaderInfo.getReaderConfigurationInfo().getIconWithNPA()->lookupPath()); + mUi->comfortReaderImageLabel->setPixmap(pixmap.scaledToWidth(SCALEWIDTH, Qt::SmoothTransformation)); + setupChangePinHeader(pReaderInfo.getRetryCounter(), false); + + switch (pReaderInfo.getRetryCounter()) + { + case 0: + mUi->changePinComfortDetailsStackedWidget->setCurrentWidget(mUi->changePinComfortPukDetailsPage); + break; + + case 1: + mUi->changePinComfortDetailsStackedWidget->setCurrentWidget(mUi->changePinComfortCanDetailsPage); + break; + + default: + mUi->changePinComfortDetailsStackedWidget->setCurrentWidget(mUi->changePinComfortDetailsPage); + break; + } +} + + +void PinSettingsWidget::setupPinSuccessfullyChangedPage(const ReaderInfo& pReaderInfo) +{ + QPixmap pixmap(pReaderInfo.getReaderConfigurationInfo().getIconWithNPA()->lookupPath()); + mUi->pinSuccessReaderImageLabel->setPixmap(pixmap.scaledToWidth(SCALEWIDTH, Qt::SmoothTransformation)); +} + + +void PinSettingsWidget::setupChangePinHeader(int pRetryCounter, bool pIsBasicReader) +{ + switch (pRetryCounter) + { + case 0: + mUi->headerStackedWidget->setCurrentWidget(mUi->changePinPukHeaderPage); + break; + + case 1: + mUi->headerStackedWidget->setCurrentWidget(mUi->changePinWithCanHeaderPage); + break; + + default: + if (mMode == Mode::AfterPinUnblock) + { + mUi->headerStackedWidget->setCurrentWidget(mUi->pinUnblockedHeaderPage); + } + else + { + if (pIsBasicReader) + { + mUi->headerStackedWidget->setCurrentWidget(mUi->changePinBasicHeaderPage); + } + else + { + mUi->headerStackedWidget->setCurrentWidget(mUi->changePinComfortHeaderPage); + } + } + break; + } +} + + +bool PinSettingsWidget::isCanFieldVisible() const +{ + return mUi->canEditStackedWidget->currentWidget() == mUi->canEditPage; +} + + +bool PinSettingsWidget::isPukFieldVisible() const +{ + return mUi->basicReaderPukStackedWidget->currentWidget() == mUi->basicReaderPukPage; +} + + +void PinSettingsWidget::onRandomPinButtonClicked() +{ + const auto& readersWithNpa = getReaderWithNPA(Env::getSingleton()->getReaderInfos()); + const auto& selectedReaderName = readersWithNpa.size() == 1 ? readersWithNpa.at(0).getName() : QString(); + mRandomPinDialog = new RandomPinDialog(6, selectedReaderName, this); + if (mRandomPinDialog->exec() == QDialog::Accepted && !mRandomPinDialog->getPin().isEmpty()) + { + QToolButton* pinButton = qobject_cast(sender()); + if (pinButton == nullptr) + { + qCCritical(gui) << "sender == nullptr"; + } + else if (pinButton->objectName() == QLatin1String("canRandomPinButton")) + { + onCanTextEdited(mRandomPinDialog->getPin()); + } + else if (pinButton->objectName() == QLatin1String("oldRandomPinButton")) + { + onOldPinTextEdited(mRandomPinDialog->getPin()); + } + else if (pinButton->objectName() == QLatin1String("newRandomPinButton")) + { + onNewPinTextEdited(mRandomPinDialog->getPin()); + } + else if (pinButton->objectName() == QLatin1String("repeatNewRandomPinButton")) + { + onRepeatNewPinTextEdited(mRandomPinDialog->getPin()); + } + else if (pinButton->objectName() == QLatin1String("pukRandomPinButton")) + { + onPukTextEdited(mRandomPinDialog->getPin()); + } + } +} + + +void PinSettingsWidget::onRandomPukButtonClicked() +{ + const auto& readersWithNpa = getReaderWithNPA(Env::getSingleton()->getReaderInfos()); + const auto& selectedReaderName = readersWithNpa.size() == 1 ? readersWithNpa.at(0).getName() : QString(); + mRandomPinDialog = new RandomPinDialog(10, selectedReaderName, this); + if (mRandomPinDialog->exec() == QDialog::Accepted && !mRandomPinDialog->getPin().isEmpty()) + { + onPukTextEdited(mRandomPinDialog->getPin()); + } +} + + +void PinSettingsWidget::changeEvent(QEvent* pEvent) +{ + if (pEvent->type() == QEvent::LanguageChange) + { + mUi->retranslateUi(this); + } + QWidget::changeEvent(pEvent); +} diff --git a/src/ui/widget/PinSettingsWidget.h b/src/ui/widget/PinSettingsWidget.h new file mode 100644 index 0000000..7dc851d --- /dev/null +++ b/src/ui/widget/PinSettingsWidget.h @@ -0,0 +1,126 @@ +/*! + * \brief Widget for the PIN settings. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/ChangePinContext.h" +#include "RandomPinDialog.h" +#include "ReaderInfo.h" + +#include +#include +#include + +namespace Ui +{ +class PinSettingsWidget; +} // namespace Ui + +namespace governikus +{ + +class PinSettingsWidget + : public QWidget +{ + Q_OBJECT + + public: + static const int SCALEWIDTH = 200; + + enum class Mode + { + Normal, + AfterPinChange, + AfterPinUnblock, + }; + + public: + PinSettingsWidget(QWidget* pParent = nullptr); + virtual ~PinSettingsWidget() override; + + void setContext(const QSharedPointer& pContext); + void cancelWorkflow(); + void continueWorkflow(); + + QString getCan() const; + QString getPin() const; + QString getPuk() const; + QString getNewPin() const; + void updatePasswordFields(); + void updatePinButton(bool pEnabled); + void clearPasswords(); + void setPasswordFocus(); + + + Mode getMode() const + { + return mMode; + } + + + void setMode(Mode pMode); + QString getButtonText() const; + void setUseScreenKeyboard(bool pUseScreenKeyboard); + + bool getPinButtonEnabled() const; + bool getCancelButtonEnabled() const; + + Q_SIGNALS: + void fireButtonEnabledUpdated(); + + public Q_SLOTS: + void updateReaders(); + void onBackspacePressedOnApply(); + + protected: + virtual void paintEvent(QPaintEvent*) override; + virtual void showEvent(QShowEvent* pEvent) override; + virtual void hideEvent(QHideEvent* pEvent) override; + virtual void changeEvent(QEvent* pEvent) override; + + private Q_SLOTS: + void onCanTextEdited(const QString& pText = QString()); + void onOldPinTextEdited(const QString& pText = QString()); + void onNewPinTextEdited(const QString& pText = QString()); + void onRepeatNewPinTextEdited(const QString& pText = QString()); + void onPukTextEdited(const QString& pText = QString()); + void focusPUK(); + void focusPIN(); + void focusCAN(); + void onRandomPinButtonClicked(); + void onRandomPukButtonClicked(); + + private: + void updateCancelButton(bool pEnabled); + void setUserInputEnabled(bool pEnabled); + void setupPinBasicPage(const ReaderInfo& pReaderInfo); + void setupPinComfortPage(const ReaderInfo& pReaderInfo); + void setupPinSuccessfullyChangedPage(const ReaderInfo& pReaderInfo); + void setupChangePinHeader(int pRetryCounter, bool pIsBasicReader); + + bool isCanFieldVisible() const; + bool isPukFieldVisible() const; + + QVector getReaderWithNPA(const QVector& pReaderInfos); + void updateReadersWithoutNPA(const QVector& pReaderInfos); + bool updateReadersForOneNPA(const ReaderInfo& pReaderInfo); + + void fillInfoDescription(const QString& pTitle, const QString& pMessage); + + QScopedPointer mUi; + Mode mMode; + QSharedPointer mContext; + int mRetryCounter; + bool mPinDeactivated; + bool mPinButtonEnabled; + bool mCancelButtonEnabled; + + QString mPinSettingsInfoTitle; + QString mPinSettingsInfoDescription; + QPointer mRandomPinDialog; +}; + +} // namespace governikus diff --git a/src/ui/widget/PinSettingsWidget.ui b/src/ui/widget/PinSettingsWidget.ui new file mode 100644 index 0000000..56ae84b --- /dev/null +++ b/src/ui/widget/PinSettingsWidget.ui @@ -0,0 +1,1464 @@ + + + PinSettingsWidget + + + + 0 + 0 + 536 + 551 + + + + + + + Qt::NoFocus + + + 3 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::TabFocus + + + Select a secure PIN that consists of six digits. Do not select a number that can be guessed easily, such as "123456", your date of birth or any other number that is printed on your ID card. + +When you change your PIN for the first time, please enter your five-digit transport PIN in the field "Current PIN / Transport PIN". You received your transport PIN with the letter sent to you by your competent authority. + +Please note that the PIN may only consist of digits (0-9). + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::TabFocus + + + Select a secure PIN that consists of six digits. Do not select a number that can be guessed easily, such as "123456", your date of birth or any other number that is printed on your ID card. + +When you change your PIN for the first time, please enter your five-digit transport PIN in the field "Current PIN / Transport PIN". You received your transport PIN with the letter sent to you by your competent authority. + +Please note that the PIN may only consist of digits (0-9). + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::TabFocus + + + You have entered the wrong PIN three times. The online identification function is now blocked. Please use your personal unblocking key (PUK) to unblock your ID card. You received the PUK with the letter sent to you by your competent authority. + +Please note that you can only use the PUK to unblock the eID function. If you have forgotten your PIN, you can have a new PIN set at your competent authority. + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + 0 + 0 + + + + Qt::TabFocus + + + CAN on nPA icon + + + :/images/canHint.png + + + false + + + + + + + Qt::TabFocus + + + You have entered the wrong PIN two times. For a third attempt you first have to enter your six-digit card access number. You can find your card access number on the front side of your ID card next to the date of expiry. On the electronic residence permit the card access number is printed above your signature. + + + true + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 20 + + + 20 + + + + + + + + :/images/Icon_Checked.svg + + + + + + + Qt::TabFocus + + + <h4>PUK entry successful</h4><p>Your ID card is unblocked. You now have three more tries to change your PIN.</p> + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + true + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 20 + + + 20 + + + + + + + + :/images/Icon_Checked.svg + + + + + + + Qt::TabFocus + + + <h4>PIN successfully changed</h4> + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + true + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::TabFocus + + + <html> +<h4>No card reader detected. Please make sure that a card reader is connected.</h4> +<p>If you need help or have problems with your card reader click on the "Diagnosis" button for further information. +</p> +<p>Please note: It is currently not possible to change your PIN whilst using your smartphone as a card reader. +However, you can change your PIN on your smartphone directly as long as the remote service is disabled. +</p> +</html> + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::TabFocus + + + <html> +<h4>Please place your ID card on the card reader.</h4> +<p>If you have already placed an ID card on the card reader but it is not displayed here, please click on "Diagnosis".</p> +</html> + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::TabFocus + + + <html> +<h4>Extended Length is not supported.</h4> +<p>Your remote reader does not meet the technical requirements (Extended Length not supported).</p> +</html> + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::TabFocus + + + <html> +<h4>Several ID cards detected</h4> +<p>Please place just one ID card on the card reader.</p> +</html> + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::TabFocus + + + <html> +<h4>eID feature deactivated</h4> +<p>The eID function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the eID function.</p> +</html> + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + + + + + Qt::Horizontal + + + + + + + Qt::NoFocus + + + 2 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 20 + + + 20 + + + 20 + + + + + 1 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::TabFocus + + + Current PIN / Transport PIN: + + + + + + + + + + Qt::StrongFocus + + + open on screen keyboard + + + true + + + + + + + Qt::TabFocus + + + New PIN: + + + + + + + + + + Qt::StrongFocus + + + open on screen keyboard + + + true + + + + + + + Qt::TabFocus + + + Confirm: + + + + + + + + + + Qt::StrongFocus + + + open on screen keyboard + + + true + + + + + + + Qt::NoFocus + + + 1 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::TabFocus + + + Card access number (CAN): + + + + + + + + + 0 + 0 + + + + + + + + + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + Qt::StrongFocus + + + open on screen keyboard + + + true + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Vertical + + + + 20 + 85 + + + + + + + + + + Qt::TabFocus + + + PUK: + + + + + + + + + + Qt::StrongFocus + + + open on screen keyboard + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Vertical + + + + 20 + 85 + + + + + + + + + + + + 0 + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + + 200 + 200 + + + + + 200 + 200 + + + + Qt::TabFocus + + + card reader icon + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + 15 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 20 + + + 20 + + + 0 + + + 20 + + + 0 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 200 + + + + + 200 + 200 + + + + Qt::TabFocus + + + card reader icon + + + Qt::AlignCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::TabFocus + + + Click on "Change PIN" to enter a new PIN. + + + Qt::AlignCenter + + + true + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Click on "Enter PUK" to unblock your ID card. + + + Qt::AlignCenter + + + true + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Click on "Change PIN" to enter your card access number and then set a new PIN. You can find your card access number on the front side of your ID card next to the date of expiry. On the electronic residence permit the card access number is printed above your signature. + + + Qt::AlignCenter + + + true + + + + + + + + + 0 + + + 0 + + + 0 + + + + + Please pay attention to the display of your card reader. + + + Qt::AlignCenter + + + true + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 169 + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 20 + + + 20 + + + 0 + + + 20 + + + 0 + + + + + Qt::TabFocus + + + Click on "Change PIN" if you want to change your PIN again. + +If not, you can now remove your ID card form the card reader. + + + true + + + + + + + + 0 + 0 + + + + Qt::TabFocus + + + successful PIN change icon + + + Qt::AlignCenter + + + + + + + + + + Qt::Vertical + + + + 20 + 237 + + + + + + + + + + 15 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::TabFocus + + + no reader icon + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + 15 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::TabFocus + + + no id card icon + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + 15 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Qt::TabFocus + + + + + + Please make sure that only one card reader with an ID card on it is connected to your computer. + + + true + + + + + + + Qt::TabFocus + + + multiple card reader icon + + + Qt::AlignCenter + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::TabFocus + + + deactivated card reader icon + + + deactivatedReaderImageLabel + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Qt::Vertical + + + + 20 + 263 + + + + + + + + + + + + + governikus::PasswordEdit + QWidget +
generic/PasswordEdit.h
+ 1 +
+
+ + + + +
diff --git a/src/ui/widget/ProviderWidget.cpp b/src/ui/widget/ProviderWidget.cpp new file mode 100644 index 0000000..16e0cd1 --- /dev/null +++ b/src/ui/widget/ProviderWidget.cpp @@ -0,0 +1,152 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "ProviderWidget.h" +#include "ui_ProviderWidget.h" + +#include "generic/ListCheckItemWidget.h" +#include "generic/ListItem.h" +#include "generic/ListItemIconLeft.h" +#include "generic/ListItemIconRight.h" +#include "generic/ListItemSubTitle.h" +#include "generic/ListItemTitle.h" +#include "ProviderConfiguration.h" + +#include +#include +#include +#include +#include +#include + +using namespace governikus; + +ProviderWidget::ProviderWidget(QWidget* pParent) + : QWidget(pParent) + , mUi(new Ui::ProviderWidget()) +{ + mUi->setupUi(this); + + connect(mUi->providerSearch, &QLineEdit::textChanged, this, &ProviderWidget::searchProvider); + connect(Env::getSingleton(), &ProviderConfiguration::fireUpdated, this, &ProviderWidget::onProviderChanged); + + fill(); + mUi->noResultWidget->setVisible(false); +} + + +ProviderWidget::~ProviderWidget() +{ +} + + +void ProviderWidget::onProviderChanged() +{ + mUi->providerTableWidget->clear(); + fill(); + searchProvider(); +} + + +void ProviderWidget::fill() +{ + qDebug() << "add provider for desktop widgets."; + QStringList header; + header += tr("Name"); + header += tr("Address"); + + const auto& providers = Env::getSingleton()->getProviderConfigurationInfos(); + + mUi->providerTableWidget->setColumnCount(header.count()); + mUi->providerTableWidget->setHorizontalHeaderLabels(header); + mUi->providerTableWidget->setRowCount(providers.size()); + + int row = 0; + for (const auto& provider : providers) + { + QLabel* providerName = new QLabel(provider.getLongName().isEmpty() ? provider.getShortName() : provider.getLongName()); + providerName->setFocusPolicy(Qt::TabFocus); + providerName->setToolTip(providerName->text()); + providerName->setTextFormat(Qt::RichText); + providerName->setMargin(3); + mUi->providerTableWidget->setCellWidget(row, 0, providerName); + + const QString& url = provider.getAddress(); + QString displayUrl = url; + const int maxUrlLength = 70; + if (url.length() > maxUrlLength) + { + displayUrl = url.left(maxUrlLength) + QStringLiteral("..."); + } + + QLabel* providerLink = new QLabel(QStringLiteral(R"(%2)").arg(url, displayUrl)); + providerLink->setToolTip(url); + providerLink->setFocusPolicy(Qt::TabFocus); + providerLink->setTextFormat(Qt::RichText); + providerLink->setTextInteractionFlags(Qt::TextBrowserInteraction); + providerLink->setOpenExternalLinks(true); + providerLink->setMargin(3); + mUi->providerTableWidget->setCellWidget(row, 1, providerLink); + + ++row; + } + + for (int i = 0; i < header.size(); ++i) + { + mUi->providerTableWidget->resizeColumnToContents(i); + } + + mUi->providerTableWidget->verticalHeader()->setVisible(false); //Hide row number + mUi->providerTableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); //Not allowed to change content + mUi->providerTableWidget->setAlternatingRowColors(true); //Grey and white alternating row +} + + +void ProviderWidget::searchProvider() +{ + QString searchText = mUi->providerSearch->text().trimmed(); + mUi->providerTableWidget->setVisible(true); + mUi->noResultWidget->setVisible(false); + + bool anyMatch = false; + + for (int i = 0; i < mUi->providerTableWidget->rowCount(); ++i) + { + bool match = false; + for (int j = 0; j < mUi->providerTableWidget->columnCount(); ++j) + { + if (qobject_cast(mUi->providerTableWidget->cellWidget(i, j))->text().contains(searchText, Qt::CaseInsensitive)) + { + match = true; + anyMatch = true; + break; + } + } + mUi->providerTableWidget->setRowHidden(i, !match); + } + + mUi->providerTableWidget->setVisible(anyMatch); + mUi->noResultWidget->setVisible(!anyMatch); +} + + +void ProviderWidget::paintEvent(QPaintEvent*) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} + + +void ProviderWidget::changeEvent(QEvent* pEvent) +{ + if (pEvent->type() == QEvent::LanguageChange) + { + fill(); + searchProvider(); + mUi->retranslateUi(this); + } + QWidget::changeEvent(pEvent); +} diff --git a/src/ui/widget/ProviderWidget.h b/src/ui/widget/ProviderWidget.h new file mode 100644 index 0000000..187ab2d --- /dev/null +++ b/src/ui/widget/ProviderWidget.h @@ -0,0 +1,45 @@ +/*! + * \brief The provider page in gui. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include +#include +#include + +namespace Ui +{ +class ProviderWidget; +} // namespace Ui + +namespace governikus +{ + +class ProviderWidget + : public QWidget +{ + Q_OBJECT + + public: + ProviderWidget(QWidget* pParent = nullptr); + virtual ~ProviderWidget() override; + + public Q_SLOTS: + void searchProvider(); + void onProviderChanged(); + + protected: + virtual void changeEvent(QEvent* pEvent) override; + + private: + QScopedPointer mUi; + + virtual void paintEvent(QPaintEvent*) override; + void fill(); +}; + +} // namespace governikus diff --git a/src/widget/ProviderWidget.ui b/src/ui/widget/ProviderWidget.ui similarity index 100% rename from src/widget/ProviderWidget.ui rename to src/ui/widget/ProviderWidget.ui diff --git a/src/ui/widget/RandomPinDialog.cpp b/src/ui/widget/RandomPinDialog.cpp new file mode 100644 index 0000000..f127050 --- /dev/null +++ b/src/ui/widget/RandomPinDialog.cpp @@ -0,0 +1,150 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "RandomPinDialog.h" +#include "ui_RandomPinDialog.h" + +#include "HelpAction.h" +#include "Randomizer.h" +#include "ReaderManager.h" + +#include +#include + +using namespace governikus; + +static const char* PIN = "pin"; + +RandomPinDialog::RandomPinDialog(int pLength, const QString& pSelectedReader, QWidget* pParent) + : QDialog(pParent) + , mUi(new Ui::RandomPinDialog) + , mSelectedReader(pSelectedReader) +{ + mUi->setupUi(this); + mUi->pin->setMaxLength(pLength); + + setWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::WindowCloseButtonHint); + setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("On screen password")); + + setModal(true); + + const auto readerManager = Env::getSingleton(); + connect(readerManager, &ReaderManager::fireCardRemoved, this, &RandomPinDialog::onCardRemoved); + connect(readerManager, &ReaderManager::fireCardInserted, this, &RandomPinDialog::onCardInserted); + + installEventFilter(this); + + mUi->pinButtons->setId(mUi->button_pos_0, 0); + mUi->pinButtons->setId(mUi->button_pos_1, 1); + mUi->pinButtons->setId(mUi->button_pos_2, 2); + mUi->pinButtons->setId(mUi->button_pos_3, 3); + mUi->pinButtons->setId(mUi->button_pos_4, 4); + mUi->pinButtons->setId(mUi->button_pos_5, 5); + mUi->pinButtons->setId(mUi->button_pos_6, 6); + mUi->pinButtons->setId(mUi->button_pos_7, 7); + mUi->pinButtons->setId(mUi->button_pos_8, 8); + mUi->pinButtons->setId(mUi->button_pos_9, 9); + + initComponents(); + createButton(); +} + + +RandomPinDialog::~RandomPinDialog() +{ +} + + +void RandomPinDialog::initComponents() +{ + mUi->clrButton->setIcon(QPixmap(QStringLiteral(":/images/randompin/btn_clear.png"))); + mUi->clrButton->setIconSize(QSize(44, 26)); + connect(mUi->clrButton, &QAbstractButton::clicked, mUi->pin, &QLineEdit::clear); + + mUi->cnlButton->setIcon(QPixmap(QStringLiteral(":/images/randompin/btn_cancel.png"))); + mUi->cnlButton->setIconSize(QSize(58, 50)); + connect(mUi->cnlButton, &QAbstractButton::clicked, this, &RandomPinDialog::reject); + + mUi->okButton->setIcon(QPixmap(QStringLiteral(":/images/randompin/btn_ok.png"))); + mUi->okButton->setIconSize(QSize(58, 50)); + connect(mUi->okButton, &QAbstractButton::clicked, this, &RandomPinDialog::accept); +} + + +void RandomPinDialog::createButton() +{ + QVector buttonList = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 + }; + std::shuffle(buttonList.begin(), buttonList.end(), Randomizer::getInstance().getGenerator()); + + Q_ASSERT(buttonList.size() == mUi->pinButtons->buttons().size()); + for (int i = 0; i < buttonList.size(); ++i) + { + QAbstractButton* button = mUi->pinButtons->button(i); + button->setIcon(QPixmap(QStringLiteral(":/images/randompin/btn_normal_%1.png").arg(buttonList.value(i)))); + button->setIconSize(QSize(58, 50)); + button->setProperty(PIN, QVariant::fromValue(buttonList.value(i))); + connect(button, &QAbstractButton::clicked, this, &RandomPinDialog::onPosButtonClicked); + } +} + + +QString RandomPinDialog::getPin() +{ + return mUi->pin->text(); +} + + +void RandomPinDialog::onPosButtonClicked() +{ + QToolButton* posButton = qobject_cast(sender()); + if (posButton) + { + mUi->pin->setText(mUi->pin->text() + posButton->property(PIN).toString()); + } +} + + +void RandomPinDialog::onCardRemoved(const QString& pReaderName) +{ + if (isVisible() && pReaderName == mSelectedReader) + { + reject(); + } +} + + +void RandomPinDialog::onCardInserted() +{ + if (isVisible()) + { + reject(); + } +} + + +bool RandomPinDialog::eventFilter(QObject* pObject, QEvent* pEvent) +{ + if (pEvent->type() == QEvent::KeyPress) + { + QKeyEvent* keyEvent = static_cast(pEvent); + if (keyEvent->key() == Qt::Key_F1) + { + HelpAction::openContextHelp(); + return true; + } + } + return QDialog::eventFilter(pObject, pEvent); +} + + +void RandomPinDialog::changeEvent(QEvent* pEvent) +{ + if (pEvent->type() == QEvent::LanguageChange) + { + mUi->retranslateUi(this); + } + QWidget::changeEvent(pEvent); +} diff --git a/src/ui/widget/RandomPinDialog.h b/src/ui/widget/RandomPinDialog.h new file mode 100644 index 0000000..a3993c8 --- /dev/null +++ b/src/ui/widget/RandomPinDialog.h @@ -0,0 +1,48 @@ +/*! + * \brief Dialog for display the random PIN. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include + +namespace Ui +{ +class RandomPinDialog; +} // namespace Ui + +namespace governikus +{ + +class RandomPinDialog + : public QDialog +{ + Q_OBJECT + + public: + RandomPinDialog(int pLength, const QString& pSelectedReader, QWidget* pParent = nullptr); + virtual ~RandomPinDialog() override; + + QString getPin(); + + protected: + virtual bool eventFilter(QObject* pObject, QEvent* pEvent) override; + virtual void changeEvent(QEvent* pEvent) override; + + private: + QScopedPointer mUi; + const QString mSelectedReader; + + void initComponents(); + void createButton(); + + private Q_SLOTS: + void onPosButtonClicked(); + void onCardRemoved(const QString& pReaderName); + void onCardInserted(); +}; + +} // namespace governikus diff --git a/src/widget/RandomPinDialog.ui b/src/ui/widget/RandomPinDialog.ui similarity index 100% rename from src/widget/RandomPinDialog.ui rename to src/ui/widget/RandomPinDialog.ui diff --git a/src/ui/widget/ReaderDeviceDialog.cpp b/src/ui/widget/ReaderDeviceDialog.cpp new file mode 100644 index 0000000..595e5e2 --- /dev/null +++ b/src/ui/widget/ReaderDeviceDialog.cpp @@ -0,0 +1,61 @@ +/*! + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "ReaderDeviceDialog.h" +#include "ui_ReaderDeviceDialog.h" + +#include "HelpAction.h" + +#include + + +using namespace governikus; + +ReaderDeviceDialog::ReaderDeviceDialog(QWidget* pParent) + : QDialog(pParent) + , mUi(new Ui::ReaderDeviceDialog) + , mReaderDeviceWidget(new ReaderDeviceWidget(pParent)) +{ + mUi->setupUi(this); + + setAttribute(Qt::WA_DeleteOnClose, true); + setWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint); + setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("Reader Driver Integration")); + + installEventFilter(this); + + mUi->readerDriverLayout->addWidget(mReaderDeviceWidget); + + connect(mUi->closeButton, &QAbstractButton::clicked, this, &ReaderDeviceDialog::close); +} + + +ReaderDeviceDialog::~ReaderDeviceDialog() +{ +} + + +bool ReaderDeviceDialog::eventFilter(QObject* pObject, QEvent* pEvent) +{ + if (pEvent->type() == QEvent::KeyPress) + { + QKeyEvent* const keyEvent = static_cast(pEvent); + if (keyEvent->key() == Qt::Key_F1) + { + HelpAction::openContextHelp(); + return true; + } + } + return QDialog::eventFilter(pObject, pEvent); +} + + +void ReaderDeviceDialog::changeEvent(QEvent* pEvent) +{ + if (pEvent->type() == QEvent::LanguageChange) + { + mUi->retranslateUi(this); + } + QWidget::changeEvent(pEvent); +} diff --git a/src/ui/widget/ReaderDeviceDialog.h b/src/ui/widget/ReaderDeviceDialog.h new file mode 100644 index 0000000..cc18bfb --- /dev/null +++ b/src/ui/widget/ReaderDeviceDialog.h @@ -0,0 +1,42 @@ +/*! + * \brief Dialog for detecting attached card readers or available + * remote card readers. + * + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "ReaderDeviceWidget.h" + +#include +#include + +namespace Ui +{ +class ReaderDeviceDialog; +} // namespace Ui + + +namespace governikus +{ + +class ReaderDeviceDialog + : public QDialog +{ + Q_OBJECT + + public: + ReaderDeviceDialog(QWidget* pParent = nullptr); + virtual ~ReaderDeviceDialog() override; + + protected: + virtual bool eventFilter(QObject* pObject, QEvent* pEvent) override; + virtual void changeEvent(QEvent* pEvent) override; + + private: + QScopedPointer mUi; + ReaderDeviceWidget* mReaderDeviceWidget; +}; + +} // namespace governikus diff --git a/src/widget/ReaderDeviceDialog.ui b/src/ui/widget/ReaderDeviceDialog.ui similarity index 100% rename from src/widget/ReaderDeviceDialog.ui rename to src/ui/widget/ReaderDeviceDialog.ui diff --git a/src/ui/widget/ReaderDeviceGui.cpp b/src/ui/widget/ReaderDeviceGui.cpp new file mode 100644 index 0000000..171e23b --- /dev/null +++ b/src/ui/widget/ReaderDeviceGui.cpp @@ -0,0 +1,81 @@ +/*! + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "ReaderDeviceGui.h" + +#include + +using namespace governikus; + +Q_DECLARE_LOGGING_CATEGORY(gui) + +ReaderDeviceGui::ReaderDeviceGui(QWidget* pParentWidget) + : QObject(pParentWidget) + , mDialog(nullptr) +{ +} + + +ReaderDeviceGui::~ReaderDeviceGui() +{ +} + + +void ReaderDeviceGui::activate() +{ + if (!mDialog) + { + QWidget* dialogParent = qobject_cast(parent()); + if (!dialogParent) + { + return; + } + + mDialog = new ReaderDeviceDialog(dialogParent); + connect(mDialog, &ReaderDeviceDialog::finished, this, &ReaderDeviceGui::onFinished); + connect(mDialog, &QDialog::finished, this, &ReaderDeviceGui::fireFinished); + } + reactivate(); +} + + +void ReaderDeviceGui::deactivate() +{ + if (mDialog) + { + mDialog->close(); + } +} + + +void ReaderDeviceGui::reactToReaderCount(int pReaderCount) +{ + if (mDialog && pReaderCount > 0) + { + mDialog->close(); + } +} + + +void ReaderDeviceGui::reactivate() +{ + if (mDialog->isMinimized()) + { + mDialog->showNormal(); + } + if (!mDialog->isVisible()) + { + mDialog->show(); + } + mDialog->activateWindow(); + mDialog->raise(); +} + + +void ReaderDeviceGui::onFinished(int result) +{ + Q_UNUSED(result); + + mDialog = nullptr; +} diff --git a/src/ui/widget/ReaderDeviceGui.h b/src/ui/widget/ReaderDeviceGui.h new file mode 100644 index 0000000..f5cc48f --- /dev/null +++ b/src/ui/widget/ReaderDeviceGui.h @@ -0,0 +1,40 @@ +/*! + * \brief Qt widget based ReaderDriverUi implementation. + * + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "ReaderDeviceDialog.h" + +#include + +namespace governikus +{ + +class ReaderDeviceGui + : public QObject +{ + Q_OBJECT + + public: + ReaderDeviceGui(QWidget* pParentWidget); + virtual ~ReaderDeviceGui(); + + void activate(); + void deactivate(); + void reactToReaderCount(int pReaderCount); + + Q_SIGNALS: + void fireFinished(); + + private: + ReaderDeviceDialog* mDialog; + void reactivate(); + + private Q_SLOTS: + void onFinished(int result); +}; + +} // namespace governikus diff --git a/src/ui/widget/ReaderDeviceWidget.cpp b/src/ui/widget/ReaderDeviceWidget.cpp new file mode 100644 index 0000000..d3ef52e --- /dev/null +++ b/src/ui/widget/ReaderDeviceWidget.cpp @@ -0,0 +1,386 @@ +/*! + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "ReaderDeviceWidget.h" +#include "ui_ReaderDeviceWidget.h" + +#include "HelpAction.h" +#include "LanguageLoader.h" +#include "ReaderConfiguration.h" +#include "RemoteClient.h" +#include "RemotePinInputDialog.h" + +#include +#include +#include +#include + + +using namespace governikus; + + +Q_DECLARE_LOGGING_CATEGORY(gui) + + +ReaderDeviceWidget::ReaderDeviceWidget(QWidget* pParent) + : QWidget(pParent) + , mUi(new Ui::ReaderDeviceWidget) + , mLocalReaderDataModel(this) + , mRemoteReaderDataModel(this) +{ + mUi->setupUi(this); + + setDisplayText(); + + mUi->tableViewLocal->setModel(&mLocalReaderDataModel); + mUi->tableViewLocal->horizontalHeader()->setStretchLastSection(true); + mUi->tableViewLocal->verticalHeader()->setVisible(false); + + mUi->tableViewRemote->setModel(&mRemoteReaderDataModel); + mUi->tableViewRemote->horizontalHeader()->setStretchLastSection(true); + mUi->tableViewRemote->verticalHeader()->setVisible(false); + + mUi->infoText->setOpenExternalLinks(true); + + connect(Env::getSingleton(), &ReaderConfiguration::fireUpdated, this, &ReaderDeviceWidget::onAdjustReaderNameColumnWidth); + onAdjustReaderNameColumnWidth(); + + connect(&mLocalReaderDataModel, &ReaderDriverModel::fireModelChanged, this, &ReaderDeviceWidget::onUpdateLocalTableSelection); + connect(&mRemoteReaderDataModel, &RemoteDeviceModel::fireModelChanged, this, &ReaderDeviceWidget::onUpdateRemoteTableSelection); + onUpdateLocalTableSelection(); + onUpdateRemoteTableSelection(); + + connect(mUi->tableViewRemote->selectionModel(), &QItemSelectionModel::selectionChanged, this, &ReaderDeviceWidget::onRemoteSelectionChanged); + connect(mUi->tableViewLocal->selectionModel(), &QItemSelectionModel::selectionChanged, this, &ReaderDeviceWidget::onUpdateInfo); + onRemoteSelectionChanged(); + onUpdateInfo(); + + connect(mUi->connectRemote, &QPushButton::clicked, this, &ReaderDeviceWidget::onConnectClicked); + connect(mUi->forgetRemote, &QPushButton::clicked, this, &ReaderDeviceWidget::onForgetClicked); + connect(mUi->tableViewRemote, &QTableView::doubleClicked, this, &ReaderDeviceWidget::onRemoteDoubleClicked); + + RemoteClient* const remoteClient = Env::getSingleton(); + connect(remoteClient, &RemoteClient::fireDispatcherDestroyed, &mRemoteReaderDataModel, &RemoteDeviceModel::onDeviceDisconnected); +} + + +ReaderDeviceWidget::~ReaderDeviceWidget() +{ +} + + +void ReaderDeviceWidget::prependAccessibleName(const QString& pAccessibleNameAddition) +{ + mUi->cardReaderDescription->setAccessibleName(pAccessibleNameAddition + mUi->cardReaderDescription->accessibleName()); +} + + +void ReaderDeviceWidget::onRemoteSelectionChanged() +{ + const QItemSelectionModel* const selectionModel = mUi->tableViewRemote->selectionModel(); + const QModelIndexList& selectionList = selectionModel->selectedRows(); + + if (selectionList.isEmpty()) + { + mUi->connectRemote->setEnabled(false); + mUi->forgetRemote->setEnabled(false); + } + else + { + const QModelIndex& index = selectionList.at(0); + if (mRemoteReaderDataModel.isPaired(index)) + { + mUi->connectRemote->setEnabled(false); + mUi->forgetRemote->setEnabled(true); + } + else + { + mUi->connectRemote->setEnabled(mRemoteReaderDataModel.isSupported(index)); + mUi->forgetRemote->setEnabled(false); + } + } +} + + +void ReaderDeviceWidget::onUpdateInfo() +{ + updateInfoIcon(); + updateInfoText(); + updateInfoUpdate(); +} + + +void ReaderDeviceWidget::setDisplayText() +{ + const QString& url = HelpAction::getOnlineUrl(QStringLiteral("readerDeviceTab")); + //: Is embedded in a sentence. + const QString hyperlink = QStringLiteral("%2").arg(url, tr("online help")); + + const QString remoteEmptyListDescriptionString = tr("No smartphone with enabled remote service found. See %1 for details of use.").arg(hyperlink); + mUi->remoteEmptyListDescription->setText(remoteEmptyListDescriptionString); + mUi->remoteEmptyListDescription->setAccessibleName(remoteEmptyListDescriptionString); + + const QString localEmptyListDescriptionString = tr("No connected card reader found. See %1 for installation of card readers.").arg(hyperlink); + mUi->localEmptyListDescription->setText(localEmptyListDescriptionString); + mUi->localEmptyListDescription->setAccessibleDescription(localEmptyListDescriptionString); +} + + +void ReaderDeviceWidget::updateInfoIcon() +{ + const QItemSelectionModel* const selectionModel = mUi->tableViewLocal->selectionModel(); + const QModelIndexList& selectionList = selectionModel->selectedRows(); + + QPixmap pixmap; + if (selectionList.isEmpty()) + { + pixmap = QPixmap(ReaderConfiguration::getNoReaderFoundIconPath()); + } + else + { + const QModelIndex& index = selectionList.at(0); + const QString& path = mLocalReaderDataModel.getReaderConfigurationInfo(index).getIcon()->lookupPath(); + pixmap = QPixmap(path); + if (mLocalReaderDataModel.isInstalledSupportedReader(index)) + { + QPixmap checkMark(QStringLiteral(":/images/status_ok.svg")); + checkMark = checkMark.scaledToHeight(pixmap.height() / 3, Qt::SmoothTransformation); + + const int insertAtX = pixmap.width() - checkMark.width(); + const int insertAtY = pixmap.height() / 5 - checkMark.height() / 2; + QPainter(&pixmap).drawPixmap(insertAtX, insertAtY, checkMark); + } + } + const int layoutHeight = mUi->detailInfoLayout->geometry().height(); + mUi->readerLabel->setPixmap(pixmap.scaledToHeight(layoutHeight / 2, Qt::SmoothTransformation)); +} + + +void ReaderDeviceWidget::updateInfoText() +{ + const QItemSelectionModel* const selectionModel = mUi->tableViewLocal->selectionModel(); + const QModelIndexList& selectionList = selectionModel->selectedRows(); + + QString infoText; + if (selectionList.isEmpty()) + { + if (mLocalReaderDataModel.rowCount() == 0) + { + infoText = tr("Please connect suitable card reader."); + } + else + { + infoText = tr("Select a device to display more information about it"); + } + } + else + { + const QModelIndex& index = selectionList.at(0); + infoText = mLocalReaderDataModel.getHTMLDescription(index); + } + + if (infoText.isEmpty()) + { + mUi->stackedWidget->setCurrentWidget(mUi->emptyWidget); + } + else + { + mUi->infoText->setHtml(QStringLiteral("

") + infoText + QStringLiteral("

")); + mUi->stackedWidget->setCurrentWidget(mUi->infoText); + } +} + + +void ReaderDeviceWidget::updateInfoUpdate() +{ + const auto& now = LanguageLoader::getInstance().getUsedLocale().toString(QTime::currentTime(), tr("hh:mm:ss AP")); + const QString& text = tr("The list of card readers was last updated at %1."); + + mUi->updateTimeLabel->setText(text.arg(now)); +} + + +void ReaderDeviceWidget::onUpdateLocalTableSelection() +{ + if (mLocalReaderDataModel.rowCount() > 0) + { + mUi->localEmptyListDescriptionFrame->setVisible(false); + mUi->tableViewLocal->setVisible(true); + QItemSelectionModel* const selectionModel = mUi->tableViewLocal->selectionModel(); + if (selectionModel->selectedRows().isEmpty()) + { + selectionModel->select(mLocalReaderDataModel.index(0, ReaderDriverModel::ColumnId::ReaderName), QItemSelectionModel::Select); + selectionModel->select(mLocalReaderDataModel.index(0, ReaderDriverModel::ColumnId::ReaderStatus), QItemSelectionModel::Select); + } + } + else + { + mUi->tableViewLocal->setVisible(false); + mUi->localEmptyListDescriptionFrame->setVisible(true); + } + onUpdateInfo(); +} + + +void ReaderDeviceWidget::onUpdateRemoteTableSelection() +{ + if (mRemoteReaderDataModel.rowCount() > 0) + { + mUi->remoteEmptyListDescriptionFrame->setVisible(false); + mUi->tableViewRemote->setVisible(true); + QItemSelectionModel* const selectionModel = mUi->tableViewRemote->selectionModel(); + if (selectionModel->selectedRows().isEmpty()) + { + selectionModel->select(mRemoteReaderDataModel.index(0, RemoteDeviceModel::ColumnId::ReaderName), QItemSelectionModel::Select); + selectionModel->select(mRemoteReaderDataModel.index(0, RemoteDeviceModel::ColumnId::ReaderStatus), QItemSelectionModel::Select); + } + } + else + { + mUi->tableViewRemote->setVisible(false); + mUi->remoteEmptyListDescriptionFrame->setVisible(true); + onRemoteSelectionChanged(); + } +} + + +void ReaderDeviceWidget::onAdjustReaderNameColumnWidth() +{ + const auto& infos = Env::getSingleton()->getReaderConfigurationInfos(); + + int maxWidth = 0; + const QFontMetrics metrics(QGuiApplication::font()); + for (const auto& info : infos) + { + const int deviceNameWidth = metrics.width(info.getName()); + if (deviceNameWidth > maxWidth) + { + maxWidth = deviceNameWidth; + } + } + + if (maxWidth <= 0) + { + return; + } + + const int CELL_PADDING = 20; + mUi->tableViewLocal->setColumnWidth(0, maxWidth + CELL_PADDING); + mUi->tableViewRemote->setColumnWidth(0, maxWidth + CELL_PADDING); +} + + +void ReaderDeviceWidget::onConnectClicked() +{ + const auto& selectionModel = mUi->tableViewRemote->selectionModel(); + const QModelIndexList& selectionList = selectionModel->selectedRows(); + + if (!selectionList.isEmpty()) + { + const QModelIndex& index = selectionList.at(0); + + const QSharedPointer remoteDeviceListEntry = mRemoteReaderDataModel.getRemoteDeviceListEntry(index); + if (remoteDeviceListEntry.isNull()) + { + return; + } + + setEnabled(false); + + QMessageBox pairingInfoBox(this); + pairingInfoBox.setText(tr("Please start pairing mode first.")); + pairingInfoBox.setWindowModality(Qt::WindowModal); + pairingInfoBox.setWindowFlags(pairingInfoBox.windowFlags() & ~Qt::WindowContextHelpButtonHint); + pairingInfoBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); + pairingInfoBox.setDefaultButton(QMessageBox::Ok); + pairingInfoBox.button(QMessageBox::Ok)->setFocus(); + pairingInfoBox.setIconPixmap(QIcon(QStringLiteral(":/images/npa.svg")).pixmap(32, 32)); + + if (pairingInfoBox.exec() == QMessageBox::Cancel) + { + setEnabled(true); + return; + } + + const QString pin = RemotePinInputDialog::getPin(this); + if (!pin.isEmpty()) + { + RemoteClient* const remoteClient = Env::getSingleton(); + connect(remoteClient, &RemoteClient::fireEstablishConnectionDone, this, &ReaderDeviceWidget::onEstablishConnectionDone); + remoteClient->establishConnection(remoteDeviceListEntry, pin); + } + + setEnabled(true); + } +} + + +void ReaderDeviceWidget::onEstablishConnectionDone(const QSharedPointer& pEntry, const GlobalStatus& pStatus) +{ + Q_UNUSED(pEntry); + RemoteClient* const remoteClient = Env::getSingleton(); + disconnect(remoteClient, &RemoteClient::fireEstablishConnectionDone, this, &ReaderDeviceWidget::onEstablishConnectionDone); + if (pStatus.isError()) + { + QMessageBox box(QApplication::activeWindow()); + box.setIcon(QMessageBox::Critical); + box.setWindowTitle(QApplication::applicationName() + QStringLiteral(" - ") + tr("Pairing")); + box.setWindowIcon(QIcon(QStringLiteral(":/images/npa.svg"))); + box.setWindowModality(Qt::WindowModal); + box.setText(pStatus.toErrorDescription()); + box.setStandardButtons(QMessageBox::Ok); + box.button(QMessageBox::Ok)->setFocus(); + box.exec(); + } +} + + +void ReaderDeviceWidget::onForgetClicked() +{ + const auto& selectionModel = mUi->tableViewRemote->selectionModel(); + const QModelIndexList& selectionList = selectionModel->selectedRows(); + + if (!selectionList.isEmpty()) + { + const QModelIndex& index = selectionList.at(0); + + mRemoteReaderDataModel.forgetDevice(index); + } +} + + +void ReaderDeviceWidget::showEvent(QShowEvent* pEevent) +{ + onUpdateInfo(); + Q_EMIT fireWidgetShown(); + QWidget::showEvent(pEevent); +} + + +void ReaderDeviceWidget::hideEvent(QHideEvent* pEvent) +{ + Q_EMIT fireWidgetHidden(); + QWidget::hideEvent(pEvent); +} + + +void ReaderDeviceWidget::changeEvent(QEvent* pEvent) +{ + if (pEvent->type() == QEvent::LanguageChange) + { + mUi->retranslateUi(this); + setDisplayText(); + onUpdateInfo(); + } + QWidget::changeEvent(pEvent); +} + + +void ReaderDeviceWidget::onRemoteDoubleClicked(const QModelIndex& pIndex) +{ + if (!mRemoteReaderDataModel.isPaired(pIndex) && mRemoteReaderDataModel.isSupported((pIndex))) + { + onConnectClicked(); + } +} diff --git a/src/ui/widget/ReaderDeviceWidget.h b/src/ui/widget/ReaderDeviceWidget.h new file mode 100644 index 0000000..91d0e84 --- /dev/null +++ b/src/ui/widget/ReaderDeviceWidget.h @@ -0,0 +1,72 @@ +/*! + * \brief Widget for detecting attached card readers and + * suggesting an appropriate driver to be installed. + * + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "AppSettings.h" +#include "ReaderDriverModel.h" +#include "RemoteDeviceModel.h" + +#include +#include +#include +#include + +namespace Ui +{ +class ReaderDeviceWidget; +} // namespace Ui + +namespace governikus +{ + +class ReaderDeviceWidget + : public QWidget +{ + Q_OBJECT + + private: + QScopedPointer mUi; + ReaderDriverModel mLocalReaderDataModel; + RemoteDeviceModel mRemoteReaderDataModel; + + void setDisplayText(); + + void updateInfoIcon(); + void updateInfoText(); + void updateInfoUpdate(); + + static QString askForPin(QWidget* pParent); + + private Q_SLOTS: + void showEvent(QShowEvent* pEevent) override; + void hideEvent(QHideEvent* pEvent) override; + void onUpdateInfo(); + void onAdjustReaderNameColumnWidth(); + void onUpdateLocalTableSelection(); + void onUpdateRemoteTableSelection(); + void onConnectClicked(); + void onEstablishConnectionDone(const QSharedPointer& pEntry, const GlobalStatus& pStatus); + void onRemoteSelectionChanged(); + void onForgetClicked(); + void onRemoteDoubleClicked(const QModelIndex& pIndex); + + protected: + virtual void changeEvent(QEvent* pEvent) override; + + public: + explicit ReaderDeviceWidget(QWidget* pParent = nullptr); + virtual ~ReaderDeviceWidget() override; + + void prependAccessibleName(const QString& pAccessibleNameAddition); + + Q_SIGNALS: + void fireWidgetShown(); + void fireWidgetHidden(); +}; + +} // namespace governikus diff --git a/src/ui/widget/ReaderDeviceWidget.ui b/src/ui/widget/ReaderDeviceWidget.ui new file mode 100644 index 0000000..3af32a4 --- /dev/null +++ b/src/ui/widget/ReaderDeviceWidget.ui @@ -0,0 +1,394 @@ + + + ReaderDeviceWidget + + + + 0 + 0 + 649 + 440 + + + + + + + 6 + + + + + Qt::TabFocus + + + In order to use the online identification function of your ID card you need a separate card reader or a suitable smartphone. The following overview shows the status of a connected card reader or connected smartphone. + + + In order to use the online identification function of your ID card you need a separate card reader or a suitable smartphone. The following overview shows the status of a connected card reader or connected smartphone. + + + true + + + + + + + 0 + + + + + Qt::Vertical + + + QSizePolicy::Minimum + + + + 20 + 5 + + + + + + + + + 0 + 0 + + + + + 11 + 75 + true + + + + Smartphone as card reader + + + + + + + + + + 0 + 0 + + + + + 470 + 100 + + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + false + + + + + + + QFrame::StyledPanel + + + QFrame::Plain + + + + 0 + 0 + + + + false + + + + 470 + 100 + + + + + + + $text + + + true + + + true + + + + + + + + + + 10 + + + + + true + + + + 0 + 0 + + + + + 200 + 16777215 + + + + Pair + + + + + + + false + + + + 0 + 0 + + + + + 200 + 16777215 + + + + Forget + + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Minimum + + + + 20 + 20 + + + + + + + + + 11 + 75 + true + + + + Card readers + + + + + + + 6 + + + + + + 0 + 0 + + + + + 470 + 0 + + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + false + + + + + + + QFrame::StyledPanel + + + QFrame::Plain + + + + 0 + 0 + + + + false + + + + 470 + 100 + + + + + + + $text + + + true + + + true + + + + + + + + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + 0 + 0 + + + + + 200 + 16777215 + + + + + + 0 + 0 + + + + + 200 + 16777215 + + + + + + + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 10 + 10 + + + + + + + + Qt::TabFocus + + + After connecting a new card reader it may take a few seconds to recognize the driver. It may be necessary to restart your system after installing the driver. + + + After connecting a new card reader it may take a few seconds to recognize the driver. It may be necessary to restart your system after installing the driver. + + + true + + + + + + + TextLabel + + + + + + + + + + diff --git a/src/ui/widget/ReaderDriverModel.cpp b/src/ui/widget/ReaderDriverModel.cpp new file mode 100644 index 0000000..c2efbb9 --- /dev/null +++ b/src/ui/widget/ReaderDriverModel.cpp @@ -0,0 +1,171 @@ +/*! + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "ReaderDriverModel.h" + +#include "AppSettings.h" +#include "ReaderConfiguration.h" +#include "ReaderDetector.h" +#include "ReaderManager.h" + + +using namespace governikus; + + +ReaderDriverModel::ReaderDriverModel(QObject* pParent) + : QAbstractTableModel(pParent) + , mKnownDrivers() + , mConnectedReaders() +{ + const ReaderManager* const readerManager = Env::getSingleton(); + + connect(readerManager, &ReaderManager::fireReaderAdded, this, &ReaderDriverModel::onUpdateContent); + connect(readerManager, &ReaderManager::fireReaderRemoved, this, &ReaderDriverModel::onUpdateContent); + connect(Env::getSingleton(), &ReaderConfiguration::fireUpdated, this, &ReaderDriverModel::onUpdateContent); + connect(Env::getSingleton(), &ReaderDetector::fireReaderChangeDetected, this, &ReaderDriverModel::onUpdateContent); + connect(Env::getSingleton(), &AppSettings::fireSettingsChanged, this, &ReaderDriverModel::onUpdateContent); + onUpdateContent(); +} + + +void ReaderDriverModel::collectReaderData() +{ + mConnectedReaders.clear(); + + const QVector installedReaders = Env::getSingleton()->getReaderInfos(ReaderFilter({ + ReaderManagerPlugInType::PCSC + , ReaderManagerPlugInType::BLUETOOTH + , ReaderManagerPlugInType::NFC + })); + + for (const auto& installedReader : installedReaders) + { + const auto& readerSettingsInfo = installedReader.getReaderConfigurationInfo(); + if (!readerSettingsInfo.getUrl().isEmpty()) + { + mKnownDrivers += readerSettingsInfo; + mConnectedReaders += readerSettingsInfo; + } + } + + QVector readersWithoutDriver; + const auto& attachedSupportedDevices = Env::getSingleton()->getAttachedSupportedDevices(); + for (const auto& info : attachedSupportedDevices) + { + if (!mConnectedReaders.contains(info)) + { + readersWithoutDriver.append(info); + } + } + mConnectedReaders += readersWithoutDriver; +} + + +QString ReaderDriverModel::getStatus(const ReaderConfigurationInfo& pReaderConfigurationInfo) const +{ + if (mConnectedReaders.isEmpty()) + { + return tr("Not connected"); + } + + if (mKnownDrivers.contains(pReaderConfigurationInfo)) + { + return tr("Driver installed"); + } + + return tr("No driver installed"); +} + + +void ReaderDriverModel::onUpdateContent() +{ + beginResetModel(); + collectReaderData(); + endResetModel(); + + Q_EMIT fireModelChanged(); +} + + +QVariant ReaderDriverModel::headerData(int pSection, Qt::Orientation pOrientation, int pRole) const +{ + if (pRole == Qt::DisplayRole && pOrientation == Qt::Horizontal) + { + switch (pSection) + { + case ColumnId::ReaderName: + return tr("Device"); + + case ColumnId::ReaderStatus: + return tr("Status"); + + default: + return QVariant(); + } + } + return QVariant(); +} + + +int ReaderDriverModel::rowCount(const QModelIndex&) const +{ + return mConnectedReaders.size(); +} + + +int ReaderDriverModel::columnCount(const QModelIndex&) const +{ + return NUMBER_OF_COLUMNS; +} + + +QVariant ReaderDriverModel::data(const QModelIndex& pIndex, int pRole) const +{ + if (pRole == Qt::DisplayRole) + { + const auto& reader = mConnectedReaders.at(pIndex.row()); + switch (pIndex.column()) + { + case ColumnId::ReaderName: + return reader.getName(); + + case ColumnId::ReaderStatus: + return getStatus(reader); + } + } + + return QVariant(); +} + + +const ReaderConfigurationInfo& ReaderDriverModel::getReaderConfigurationInfo(const QModelIndex& pIndex) const +{ + return mConnectedReaders.at(pIndex.row()); +} + + +QString ReaderDriverModel::getHTMLDescription(const QModelIndex& pIndex) const +{ + if (mConnectedReaders.isEmpty()) + { + return QString(); + } + + if (mKnownDrivers.contains(mConnectedReaders.at(pIndex.row()))) + { + return tr("Card reader ready for use."); + } + + return tr("Please download and install the driver you can find at: %1"). + arg(QStringLiteral("%1").arg(mConnectedReaders.at(pIndex.row()).getUrl())); +} + + +bool ReaderDriverModel::isInstalledSupportedReader(const QModelIndex& pIndex) const +{ + const auto& readerSettingsInfo = mConnectedReaders.at(pIndex.row()); + const bool knownDriver = mKnownDrivers.contains(readerSettingsInfo); + const bool knownReader = readerSettingsInfo.isKnownReader(); + return knownDriver && knownReader; +} diff --git a/src/ui/widget/ReaderDriverModel.h b/src/ui/widget/ReaderDriverModel.h new file mode 100644 index 0000000..caf15da --- /dev/null +++ b/src/ui/widget/ReaderDriverModel.h @@ -0,0 +1,59 @@ +/*! + * \brief Model implementation for the reader driver table + * + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "ReaderDetector.h" + +#include +#include +#include + + +namespace governikus +{ + +class ReaderDriverModel + : public QAbstractTableModel +{ + Q_OBJECT + + private: + const int NUMBER_OF_COLUMNS = 2; + + QSet mKnownDrivers; + QVector mConnectedReaders; + + QString getStatus(const ReaderConfigurationInfo& pReaderConfigurationInfo) const; + void collectReaderData(); + + public: + enum ColumnId : int + { + ReaderName = 0, + ReaderStatus = 1 + }; + ReaderDriverModel(QObject* pParent = nullptr); + + virtual QVariant headerData(int pSection, Qt::Orientation pOrientation, int pRole) const override; + virtual int rowCount(const QModelIndex& pParent = QModelIndex()) const override; + virtual int columnCount(const QModelIndex& pParent = QModelIndex()) const override; + virtual QVariant data(const QModelIndex& pIndex, int pRole = Qt::DisplayRole) const override; + + const ReaderConfigurationInfo& getReaderConfigurationInfo(const QModelIndex& pIndex) const; + QString getHTMLDescription(const QModelIndex& pIndex) const; + bool isInstalledSupportedReader(const QModelIndex& pIndex) const; + + public Q_SLOTS: + void onUpdateContent(); + + Q_SIGNALS: + void fireModelChanged(); + +}; + + +} // namespace governikus diff --git a/src/ui/widget/RemotePinInputDialog.cpp b/src/ui/widget/RemotePinInputDialog.cpp new file mode 100644 index 0000000..f84caa5 --- /dev/null +++ b/src/ui/widget/RemotePinInputDialog.cpp @@ -0,0 +1,81 @@ +/*! + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "RemotePinInputDialog.h" +#include "ui_RemotePinInputDialog.h" + +#include "generic/PasswordEdit.h" + +#include +#include +#include + +using namespace governikus; + + +Q_DECLARE_LOGGING_CATEGORY(gui) + + +RemotePinInputDialog::RemotePinInputDialog(QWidget* pParent) + : QDialog(pParent, Qt::WindowTitleHint) + , mUi(new Ui::RemotePinInputDialog()) +{ + mUi->setupUi(this); + connect(mUi->buttonBox, &QDialogButtonBox::accepted, this, &RemotePinInputDialog::onOkClicked); + connect(mUi->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + + mUi->pinEntryLine->setMaxLength(4, false); + mUi->pinEntryLine->setAlignment(Qt::AlignCenter); + QRegularExpression onlyNumbersExpression(QStringLiteral("[0-9]*")); + mUi->pinEntryLine->configureValidation(onlyNumbersExpression, tr("Only digits (0-9) are allowed.")); +} + + +RemotePinInputDialog::~RemotePinInputDialog() +{ +} + + +const QString RemotePinInputDialog::getPin(QWidget* pParent) +{ + RemotePinInputDialog dialog(pParent); + int result = dialog.exec(); + if (!result) + { + return QString(); + } + + if (isValidPin(dialog.getPinEntry())) + { + return dialog.getPinEntry(); + } + + qCCritical(gui) << "Pairing code entered was not valid (4 Digits)"; + return QString(); +} + + +bool RemotePinInputDialog::isValidPin(const QString& pPin) +{ + return QRegularExpression(QStringLiteral("[0-9]{4}")).match(pPin).hasMatch(); +} + + +QString RemotePinInputDialog::getPinEntry() const +{ + return mUi->pinEntryLine->text(); +} + + +void RemotePinInputDialog::onOkClicked() +{ + if (isValidPin(getPinEntry())) + { + accept(); + } + else + { + QToolTip::showText(mUi->pinEntryLine->mapToGlobal(QPoint(0, 0)), tr("A pairing code has to be 4 digits long."), mUi->pinEntryLine, QRect(), 3000); + } +} diff --git a/src/ui/widget/RemotePinInputDialog.h b/src/ui/widget/RemotePinInputDialog.h new file mode 100644 index 0000000..b3e85af --- /dev/null +++ b/src/ui/widget/RemotePinInputDialog.h @@ -0,0 +1,39 @@ +/*! + * \brief Dialog for PIN input for device pairing. + * + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include + +namespace Ui +{ +class RemotePinInputDialog; +} // namespace Ui + +namespace governikus +{ + +class RemotePinInputDialog + : public QDialog +{ + Q_OBJECT + + private: + QScopedPointer mUi; + + private Q_SLOTS: + void onOkClicked(); + + public: + RemotePinInputDialog(QWidget* pParent = 0); + virtual ~RemotePinInputDialog() override; + + static const QString getPin(QWidget* pParent); + static bool isValidPin(const QString& pPin); + QString getPinEntry() const; +}; + +} // namespace governikus diff --git a/src/widget/RemotePinInputDialog.ui b/src/ui/widget/RemotePinInputDialog.ui similarity index 100% rename from src/widget/RemotePinInputDialog.ui rename to src/ui/widget/RemotePinInputDialog.ui diff --git a/src/ui/widget/SelfInformationWidget.cpp b/src/ui/widget/SelfInformationWidget.cpp new file mode 100644 index 0000000..f49e352 --- /dev/null +++ b/src/ui/widget/SelfInformationWidget.cpp @@ -0,0 +1,75 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "SelfInformationWidget.h" +#include "ui_SelfInformationWidget.h" + +#include "AppSettings.h" + +#include +#include +#include +#include + +using namespace governikus; + +SelfInformationWidget::SelfInformationWidget(QWidget* pParent) + : QWidget(pParent) + , mUi(new Ui::SelfInformationWidget()) +{ + mUi->setupUi(this); + + connect(mUi->selfAuthenticationButton, &QAbstractButton::clicked, this, &SelfInformationWidget::selfAuthenticationRequested); + connect(&Env::getSingleton()->getGeneralSettings(), &AbstractSettings::fireSettingsChanged, this, &SelfInformationWidget::onSettingsChanged); + + mPixDescLogoLabel.reset(new QPixmap(QStringLiteral(":/images/siteWithLogo.png"))); + mUi->descriptionLogoLabel->setPixmap(mPixDescLogoLabel->scaled(159, 120, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + + onSettingsChanged(); +} + + +SelfInformationWidget::~SelfInformationWidget() +{ +} + + +void SelfInformationWidget::paintEvent(QPaintEvent*) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} + + +void SelfInformationWidget::onSettingsChanged() +{ + const auto desc = tr("Use the button 'See my personal data now...' to display the data stored on your ID card. An Internet connection is required to display the data."); + const auto hyperlink = QStringLiteral(R"(%2)").arg(tr("https://www.ausweisapp.bund.de/datenschutz/"), tr("data privacy statement")); + const auto info = tr("Your personal data is neither saved nor processed in any way. Please see our %1 for details on how your personal data is processed.").arg(hyperlink); + mUi->descriptionLabel->setText(desc + QStringLiteral("

") + info); + + if (Env::getSingleton()->getGeneralSettings().useSelfAuthTestUri()) + { + mUi->selfAuthenticationButton->setStyleSheet(QStringLiteral("QPushButton { background: red; }")); + mUi->selfAuthenticationButton->setToolTip(tr("Test environment")); + } + else + { + mUi->selfAuthenticationButton->setStyleSheet(QString()); + mUi->selfAuthenticationButton->setToolTip(QString()); + } +} + + +void SelfInformationWidget::changeEvent(QEvent* pEvent) +{ + if (pEvent->type() == QEvent::LanguageChange) + { + mUi->retranslateUi(this); + onSettingsChanged(); + } + QWidget::changeEvent(pEvent); +} diff --git a/src/ui/widget/SelfInformationWidget.h b/src/ui/widget/SelfInformationWidget.h new file mode 100644 index 0000000..22895a4 --- /dev/null +++ b/src/ui/widget/SelfInformationWidget.h @@ -0,0 +1,45 @@ +/*! + * \brief Widget for starting the self information workflow. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include + +namespace Ui +{ +class SelfInformationWidget; +} // namespace Ui + +namespace governikus +{ + +class SelfInformationWidget + : public QWidget +{ + Q_OBJECT + + public: + SelfInformationWidget(QWidget* pParent = nullptr); + virtual ~SelfInformationWidget() override; + + protected: + virtual void changeEvent(QEvent* pEvent) override; + + Q_SIGNALS: + void selfAuthenticationRequested(); + + private: + QScopedPointer mUi; + QScopedPointer mPixDescLogoLabel; + + virtual void paintEvent(QPaintEvent*) override; + + private Q_SLOTS: + void onSettingsChanged(); +}; + +} // namespace governikus diff --git a/src/ui/widget/SelfInformationWidget.ui b/src/ui/widget/SelfInformationWidget.ui new file mode 100644 index 0000000..4c733aa --- /dev/null +++ b/src/ui/widget/SelfInformationWidget.ui @@ -0,0 +1,193 @@ + + + SelfInformationWidget + + + + 0 + 0 + 607 + 375 + + + + + 20 + + + 20 + + + 20 + + + 20 + + + + + + 20 + + + 10 + + + 20 + + + 10 + + + + + + + Qt::TabFocus + + + eID Logo + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + Qt::TabFocus + + + You can use your ID card anywhere you see this logo. + + + true + + + + + + + + + + + + + 20 + + + 20 + + + 20 + + + 20 + + + + + + + Qt::TabFocus + + + See my personal data + + + true + + + + + + + + + Qt::TabFocus + + + true + + + true + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 20 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + See my personal data now... + + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/src/ui/widget/SettingsWidget.cpp b/src/ui/widget/SettingsWidget.cpp new file mode 100644 index 0000000..e2ecbb6 --- /dev/null +++ b/src/ui/widget/SettingsWidget.cpp @@ -0,0 +1,375 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "SettingsWidget.h" +#include "ui_SettingsWidget.h" + +#include "AppSettings.h" +#include "ReaderManager.h" + +#include +#include +#include +#include +#include + +using namespace governikus; + +SettingsWidget::SettingsWidget(QWidget* pParent) + : QWidget(pParent) + , mUi(new Ui::SettingsWidget()) + , mScanRunning(false) + , mWorkflowRunning(false) + , mSettingsChanged(false) +{ + mUi->setupUi(this); + + connect(mUi->diagnosisButton, &QAbstractButton::clicked, this, &SettingsWidget::diagnosisRequested); + + connect(mUi->generalTab, &GeneralSettingsWidget::settingsChanged, this, &SettingsWidget::onSettingsChanged); + connect(mUi->pinTab, &PinSettingsWidget::fireButtonEnabledUpdated, this, &SettingsWidget::onUpdateButtonState); + connect(mUi->pinTab, &PinSettingsWidget::fireButtonEnabledUpdated, this, &SettingsWidget::onUpdateApplyButtonText); + + connect(mUi->cancelButton, &QPushButton::clicked, this, &SettingsWidget::onCancelButtonClicked); + connect(mUi->applyButton, &QPushButton::clicked, this, &SettingsWidget::onApplyButtonClicked); + + connect(mUi->settingsTabWidget, &QTabWidget::currentChanged, this, &SettingsWidget::onTabChanged); + + mUi->pinTab->setUseScreenKeyboard(Env::getSingleton()->getGeneralSettings().isUseScreenKeyboard()); + connect(this, &SettingsWidget::fireBackspacePressedOnApply, mUi->pinTab, &PinSettingsWidget::onBackspacePressedOnApply); + +#ifndef QT_NO_DEBUG + mDeveloperTab.reset(new DeveloperSettingsWidget()); + mDeveloperTab->setObjectName(QStringLiteral("developerTab")); + mUi->settingsTabWidget->addTab(mDeveloperTab.data(), QString()); + setDeveloperTabName(); + + connect(mDeveloperTab.data(), &DeveloperSettingsWidget::fireSettingsChanged, this, &SettingsWidget::onSettingsChanged); +#endif + + setSettingsChanged(false); + onUpdateApplyButtonText(); +} + + +SettingsWidget::~SettingsWidget() +{ +} + + +void SettingsWidget::keyPressEvent(QKeyEvent* pEvent) +{ + if (pEvent->key() == Qt::Key_Backspace && mUi->applyButton->hasFocus()) + { + Q_EMIT fireBackspacePressedOnApply(); + } + QWidget::keyPressEvent(pEvent); +} + + +void SettingsWidget::workflowStarted() +{ + mWorkflowRunning = true; + + // disable the non-selected tabs + int tabCount = mUi->settingsTabWidget->count(); + for (int i = 0; i < tabCount; ++i) + { + mUi->settingsTabWidget->setTabEnabled(i, i == mUi->settingsTabWidget->currentIndex()); + } + + onUpdateButtonState(); +} + + +void SettingsWidget::workflowFinished() +{ + mWorkflowRunning = false; + + // enable all tabs + int tabCount = mUi->settingsTabWidget->count(); + for (int i = 0; i < tabCount; ++i) + { + mUi->settingsTabWidget->setTabEnabled(i, true); + } + + onUpdateButtonState(); + + if (mUi->pinTab->isVisible()) + { + QMetaObject::invokeMethod(mUi->pinTab, &PinSettingsWidget::updateReaders, Qt::QueuedConnection); + } +} + + +void SettingsWidget::switchToGuiModule(GuiModule pModule) +{ + switch (pModule) + { + case GuiModule::START_PAGE: + case GuiModule::IDENTIFY: + // not handled here + break; + + case GuiModule::GENERAL_SETTINGS: + mUi->settingsTabWidget->setCurrentWidget(mUi->generalTab); + break; + + case GuiModule::PIN_SETTINGS: + mUi->settingsTabWidget->setCurrentWidget(mUi->pinTab); + break; + } +} + + +void SettingsWidget::paintEvent(QPaintEvent*) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} + + +void SettingsWidget::hideEvent(QHideEvent* pEvent) +{ + onTabChanged(-1); + QWidget::hideEvent(pEvent); +} + + +void SettingsWidget::showEvent(QShowEvent* pEvent) +{ + QWidget::showEvent(pEvent); + + if (mUi->settingsTabWidget->currentWidget() == mUi->pinTab || mUi->settingsTabWidget->currentWidget() == mUi->readerDeviceTab) + { + mScanRunning = true; + Env::getSingleton()->startScanAll(mUi->settingsTabWidget->currentWidget() == mUi->pinTab); + } +} + + +void SettingsWidget::onTabChanged(int pIndex) +{ + QWidget* const currentWidget = mUi->settingsTabWidget->widget(pIndex); + if (currentWidget != mUi->generalTab) + { + if (mSettingsChanged) + { + showSettingsChangedMessage(); + } + mUi->pinTab->setUseScreenKeyboard(Env::getSingleton()->getGeneralSettings().isUseScreenKeyboard()); + } + else + { + setSettingsChanged(mSettingsChanged); + } + + const auto readerManager = Env::getSingleton(); + if (currentWidget == mUi->pinTab || currentWidget == mUi->readerDeviceTab) + { + mScanRunning = true; + readerManager->startScanAll(currentWidget == mUi->pinTab); + } + else if (mScanRunning) + { + mScanRunning = false; + readerManager->stopScanAll(); + } + + onUpdateApplyButtonText(); + onUpdateButtonState(); +} + + +void SettingsWidget::showSettingsChangedMessage() +{ + QMessageBox msgBox(this); + msgBox.setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("Apply settings?")); + msgBox.setWindowModality(Qt::WindowModal); + msgBox.setText(tr("Do you want to apply the changes?")); + msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + msgBox.button(QMessageBox::Yes)->setFocus(); + + if (msgBox.exec() == QMessageBox::Yes) + { + applyAppSettings(); + } + else + { + resetSettings(); + } +} + + +void SettingsWidget::onApplyButtonClicked() +{ + const QWidget* const currentWidget = mUi->settingsTabWidget->currentWidget(); + if (currentWidget == mUi->pinTab) + { + if (mWorkflowRunning == true) + { + mUi->pinTab->continueWorkflow(); + } + else if (mUi->pinTab->getMode() == PinSettingsWidget::Mode::AfterPinChange) + { + mUi->pinTab->setMode(PinSettingsWidget::Mode::Normal); + mUi->pinTab->updateReaders(); + } + else if (mUi->pinTab->isVisible()) + { + Q_EMIT changePinRequested(); + } + } + else if (currentWidget == mUi->generalTab +#ifndef QT_NO_DEBUG + || currentWidget == mDeveloperTab.data() +#endif + ) + { + applyAppSettings(); + } + else if (currentWidget == mUi->readerDeviceTab) + { + mUi->settingsTabWidget->setCurrentWidget(mUi->generalTab); + } +} + + +bool SettingsWidget::isSettingsChanged() +{ + return mSettingsChanged; +} + + +void SettingsWidget::applyAppSettings() +{ + // apply button clicked + mUi->generalTab->apply(); +#ifndef QT_NO_DEBUG + mDeveloperTab->apply(); +#endif + + setSettingsChanged(false); +} + + +void SettingsWidget::onCancelButtonClicked() +{ + if (mWorkflowRunning == true) + { + mUi->pinTab->cancelWorkflow(); + } + + resetSettings(); + Q_EMIT settingsDone(); +} + + +void SettingsWidget::onSettingsChanged() +{ + setSettingsChanged(true); +} + + +void SettingsWidget::resetSettings() +{ + mUi->generalTab->reset(); +#ifndef QT_NO_DEBUG + mDeveloperTab->reset(); +#endif + setSettingsChanged(false); +} + + +QString SettingsWidget::getActiveTabObjectName() +{ + return mUi->settingsTabWidget->currentWidget()->objectName(); +} + + +void SettingsWidget::setSettingsChanged(bool pChanged) +{ + mSettingsChanged = pChanged; + onUpdateButtonState(); +} + + +void SettingsWidget::changeEvent(QEvent* pEvent) +{ + if (pEvent->type() == QEvent::LanguageChange) + { + mUi->retranslateUi(this); + setDeveloperTabName(); + onUpdateApplyButtonText(); + } + QWidget::changeEvent(pEvent); +} + + +void SettingsWidget::setDeveloperTabName() +{ +#ifndef QT_NO_DEBUG + mUi->settingsTabWidget->setTabText(mUi->settingsTabWidget->indexOf(mDeveloperTab.data()), tr("Developer Settings")); +#endif +} + + +void SettingsWidget::onUpdateApplyButtonText() +{ + const QWidget* const currentWidget = mUi->settingsTabWidget->currentWidget(); + if (currentWidget == mUi->generalTab +#ifndef QT_NO_DEBUG + || currentWidget == mDeveloperTab.data() +#endif + ) + { + mUi->applyButton->setText(tr("Apply")); + } + else if (currentWidget == mUi->pinTab) + { + mUi->applyButton->setText(mUi->pinTab->getButtonText()); + } + else if (currentWidget == mUi->readerDeviceTab) + { + mUi->applyButton->setText(tr("OK")); + } +} + + +void SettingsWidget::onUpdateButtonState() +{ + QPushButton* const applyButton = mUi->applyButton; + QPushButton* const cancelButton = mUi->cancelButton; + + cancelButton->setEnabled(true); + + const QWidget* const currentWidget = mUi->settingsTabWidget->currentWidget(); + if (currentWidget == mUi->generalTab +#ifndef QT_NO_DEBUG + || currentWidget == mDeveloperTab.data() +#endif + ) + { + applyButton->setEnabled(mSettingsChanged); + } + else if (currentWidget == mUi->pinTab) + { + const bool pinButtonEnabled = mUi->pinTab->getPinButtonEnabled(); + const bool cancelButtonEnabled = mUi->pinTab->getCancelButtonEnabled(); + mUi->applyButton->setEnabled(pinButtonEnabled); + if (pinButtonEnabled) + { + applyButton->setAutoDefault(true); + applyButton->setDefault(true); + applyButton->setFocus(); + } + cancelButton->setEnabled(!mWorkflowRunning || cancelButtonEnabled); + } + else if (currentWidget == mUi->readerDeviceTab) + { + applyButton->setEnabled(true); + } +} diff --git a/src/ui/widget/SettingsWidget.h b/src/ui/widget/SettingsWidget.h new file mode 100644 index 0000000..906841a --- /dev/null +++ b/src/ui/widget/SettingsWidget.h @@ -0,0 +1,89 @@ +/*! + * \brief Widget for the settings. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "DeveloperSettingsWidget.h" +#include "generic/GuiModule.h" + +#include + + +namespace Ui +{ +class SettingsWidget; +} // namespace Ui + +namespace governikus +{ + +class SettingsWidget + : public QWidget +{ + Q_OBJECT + + private: + QScopedPointer mUi; + bool mScanRunning; + bool mWorkflowRunning; + bool mSettingsChanged; + +#ifndef QT_NO_DEBUG + QScopedPointer mDeveloperTab; +#endif + + void resetSettings(); + void setSettingsChanged(bool pChanged); + void applyAppSettings(); + void setDeveloperTabName(); + + public Q_SLOTS: + void onTabChanged(int pIndex); + + private Q_SLOTS: + void onApplyButtonClicked(); + void onCancelButtonClicked(); + + void onUpdateButtonState(); + void onUpdateApplyButtonText(); + void onSettingsChanged(); + + protected: + virtual void paintEvent(QPaintEvent*) override; + virtual void hideEvent(QHideEvent* pEvent) override; + virtual void showEvent(QShowEvent* pEvent) override; + virtual void changeEvent(QEvent* pEvent) override; + + public: + SettingsWidget(QWidget* pParent = nullptr); + virtual ~SettingsWidget() override; + + void keyPressEvent(QKeyEvent* pEvent) override; + + bool remoteScanRunning() const + { + return mScanRunning; + } + + + void workflowStarted(); + void workflowFinished(); + + void switchToGuiModule(GuiModule pModule); + + QString getActiveTabObjectName(); + + bool isSettingsChanged(); + void showSettingsChangedMessage(); + + Q_SIGNALS: + void changePinRequested(); + void diagnosisRequested(); + void settingsDone(); + void fireBackspacePressedOnApply(); +}; + +} // namespace governikus diff --git a/src/widget/SettingsWidget.ui b/src/ui/widget/SettingsWidget.ui similarity index 100% rename from src/widget/SettingsWidget.ui rename to src/ui/widget/SettingsWidget.ui diff --git a/src/ui/widget/SetupAssistantGui.cpp b/src/ui/widget/SetupAssistantGui.cpp new file mode 100644 index 0000000..5a20bc2 --- /dev/null +++ b/src/ui/widget/SetupAssistantGui.cpp @@ -0,0 +1,52 @@ +/*! + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "SetupAssistantGui.h" + +#include + + +using namespace governikus; + + +Q_DECLARE_LOGGING_CATEGORY(gui) + + +SetupAssistantGui::SetupAssistantGui(QWidget* pParentWidget) + : QObject(pParentWidget) + , mWizard(nullptr) +{ +} + + +SetupAssistantGui::~SetupAssistantGui() +{ +} + + +void SetupAssistantGui::activate() +{ + if (!mWizard) + { + QWidget* dialogParent = qobject_cast(parent()); + if (!dialogParent) + { + return; + } + + mWizard = new SetupAssistantWizard(dialogParent); + connect(mWizard.data(), &SetupAssistantWizard::fireChangePinButtonClicked, this, &SetupAssistantGui::fireChangePinButtonClicked); + } + + mWizard->exec(); +} + + +void SetupAssistantGui::deactivate() +{ + if (mWizard) + { + mWizard->close(); + } +} diff --git a/src/ui/widget/SetupAssistantGui.h b/src/ui/widget/SetupAssistantGui.h new file mode 100644 index 0000000..26226c4 --- /dev/null +++ b/src/ui/widget/SetupAssistantGui.h @@ -0,0 +1,37 @@ +/*! + * \brief Qt widget based SetupAssistantUi implementation. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "SetupAssistantWizard.h" + +#include +#include + +namespace governikus +{ + +class SetupAssistantGui + : public QObject +{ + Q_OBJECT + + private: + QPointer mWizard; + + public: + SetupAssistantGui(QWidget* pParentWidget); + virtual ~SetupAssistantGui(); + + void activate(); + void deactivate(); + + Q_SIGNALS: + void fireChangePinButtonClicked(); + +}; + +} // namespace governikus diff --git a/src/ui/widget/SetupAssistantWizard.cpp b/src/ui/widget/SetupAssistantWizard.cpp new file mode 100644 index 0000000..a8280d5 --- /dev/null +++ b/src/ui/widget/SetupAssistantWizard.cpp @@ -0,0 +1,243 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "SetupAssistantWizard.h" + +#include "AppSettings.h" +#include "HelpAction.h" +#include "ReaderDeviceWidget.h" +#include "ReaderInfo.h" +#include "ReaderManager.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace governikus; + +CardReaderPage::CardReaderPage(const QString& pTitle, const QString& pAccessibleName) + : mWidget(new ReaderDeviceWidget(this)) +{ + setTitle(pTitle); + mWidget->prependAccessibleName(pAccessibleName); + + QVBoxLayout* cardReaderPageVLayout = new QVBoxLayout(this); + cardReaderPageVLayout->addWidget(mWidget); +} + + +SetupAssistantWizard::SetupAssistantWizard(QWidget* pParent) + : QWizard(pParent) + , mPageCount(3) + , mSaveHistoryCheckBox(new QCheckBox(this)) + , mChangeTransportPinButton() +{ +#ifdef Q_OS_MACOS + static const int MIN_HEIGHT = 700; +#else + static const int MIN_HEIGHT = 500; +#endif + + setObjectName(QStringLiteral("setupAssistant")); + installEventFilter(this); + setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("setup assistant")); + setMinimumSize(700, MIN_HEIGHT); + setWizardStyle(QWizard::ClassicStyle); + setWindowModality(Qt::WindowModal); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + setOption(QWizard::NoCancelButton, false); + setAttribute(Qt::WA_DeleteOnClose); + + addPage(createWizardInitialPinPage()); + addPage(createWizardCardReaderPage()); + addPage(createConclusionPage()); +} + + +SetupAssistantWizard::~SetupAssistantWizard() +{ +} + + +QString SetupAssistantWizard::createAccessibleName(const QString& pName, const QString& pText) const +{ + const QString& stepIndex = QString::number(pageIds().size() + 1); + const QString& pageCount = QString::number(mPageCount); + + auto result = pName + QStringLiteral(". ") + tr("Step %1 of %2").arg(stepIndex, pageCount) + QStringLiteral(". ") + pText; + return result.remove(QLatin1Char('"')).trimmed(); +} + + +QString SetupAssistantWizard::createTitle(const QString& pName) const +{ + const QString& stepIndex = QString::number(pageIds().size() + 1); + const QString& pageCount = QString::number(mPageCount); + + return QStringLiteral("
") + + tr("Step %1 of %2").arg(stepIndex, pageCount) + + QStringLiteral("
%1
").arg(pName); +} + + +QString SetupAssistantWizard::createDescription(const QString& pTitle, const QString& pSummary) const +{ + return QStringLiteral("
%1

%2").arg(pTitle, pSummary); +} + + +QWizardPage* SetupAssistantWizard::createWizardInitialPinPage() +{ + QWizardPage* initialPinPage = new QWizardPage; + const auto& introduction = tr("Introduction"); + initialPinPage->setTitle(createTitle(introduction)); + + const auto& welcome = tr("Welcome to the AusweisApp2 setup assistant." + " This assistant will guide you through the setup process in %1 steps." + " You can cancel the setup assistant at any time. To restart it, go to the tab \"Help\" and select \"Setup assistant\".").arg(mPageCount); + QLabel* label = new QLabel(welcome); + label->setWordWrap(true); + label->setFocusPolicy(Qt::TabFocus); + label->setAccessibleName(createAccessibleName(introduction, welcome)); + + QVBoxLayout* initialPinPageLayout = new QVBoxLayout; + initialPinPageLayout->addWidget(label); + + const auto& historyTitle = tr("History"); + const auto& historySummary = tr("Do you want to save the history of your authentications?"); + QLabel* historyDescLabel = new QLabel(createDescription(historyTitle, historySummary)); + historyDescLabel->setWordWrap(true); + historyDescLabel->setFocusPolicy(Qt::TabFocus); + historyDescLabel->setAccessibleName(createAccessibleName(historyTitle, historySummary)); + + initialPinPageLayout->addWidget(historyDescLabel); + + QWidget* saveHistoryWidget = new QWidget(this); + + QSizePolicy saveHistorySizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + saveHistorySizePolicy.setHorizontalStretch(0); + saveHistorySizePolicy.setVerticalStretch(0); + saveHistorySizePolicy.setHeightForWidth(saveHistoryWidget->sizePolicy().hasHeightForWidth()); + saveHistoryWidget->setSizePolicy(saveHistorySizePolicy); + + QFormLayout* saveHistoryFormLayout = new QFormLayout(saveHistoryWidget); + saveHistoryFormLayout->setHorizontalSpacing(30); + saveHistoryFormLayout->setContentsMargins(11, 11, 11, 11); + saveHistoryFormLayout->setContentsMargins(0, 5, 0, 20); + + QLabel* saveHistoryLabel = new QLabel(); + saveHistoryLabel->setFocusPolicy(Qt::TabFocus); + saveHistoryLabel->setText(historyTitle + QLatin1Char(':')); + + mSaveHistoryCheckBox->setText(tr("save")); + mSaveHistoryCheckBox->setAccessibleName(tr("save history")); + mSaveHistoryCheckBox->setChecked(Env::getSingleton()->getHistorySettings().isEnabled()); + + saveHistoryFormLayout->setWidget(0, QFormLayout::LabelRole, saveHistoryLabel); + saveHistoryFormLayout->setWidget(0, QFormLayout::FieldRole, mSaveHistoryCheckBox); + + initialPinPageLayout->addWidget(saveHistoryWidget); + + initialPinPage->setLayout(initialPinPageLayout); + + return initialPinPage; +} + + +QWizardPage* SetupAssistantWizard::createWizardCardReaderPage() +{ + const auto& title = tr("Card Readers"); + const auto& titleField = createTitle(title); + const auto& accessibleField = createAccessibleName(title); + return new CardReaderPage(titleField, accessibleField); +} + + +QWizardPage* SetupAssistantWizard::createConclusionPage() +{ + QWizardPage* conclusionPage = new QWizardPage; + const auto& almostDone = tr("Almost done!"); + conclusionPage->setTitle(createTitle(almostDone)); + + QVBoxLayout* conclusionPageVLayout = new QVBoxLayout(conclusionPage); + + const auto& title = tr("Personal 6 - digit PIN"); + const auto& desc = tr("Prior to the first use of the online identification function, you have to replace the transport PIN by an individual 6-digit PIN. " + "The transport PIN was sent to you by postal mail."); + QLabel* transportPinLabel = new QLabel(createDescription(title, desc)); + transportPinLabel->setWordWrap(true); + transportPinLabel->setFocusPolicy(Qt::TabFocus); + transportPinLabel->setAccessibleName(createAccessibleName(title, desc)); + + conclusionPageVLayout->addWidget(transportPinLabel); + + mChangeTransportPinButton = new QPushButton(conclusionPage); + mChangeTransportPinButton->setText(tr("Set individual PIN")); + connect(mChangeTransportPinButton.data(), &QAbstractButton::clicked, this, &SetupAssistantWizard::onChangeTransportPinButtonPressed); + + QSizePolicy transportPinSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + transportPinSizePolicy.setHorizontalStretch(0); + transportPinSizePolicy.setVerticalStretch(0); + transportPinSizePolicy.setHeightForWidth(mChangeTransportPinButton->sizePolicy().hasHeightForWidth()); + mChangeTransportPinButton->setSizePolicy(transportPinSizePolicy); + + conclusionPageVLayout->addWidget(mChangeTransportPinButton); + + const QString onlineHelpUrl = HelpAction::getOnlineUrl(QStringLiteral("setupAssistantSetupCompleted")); + const auto conclusionDesc = + tr("AusweisApp2 is now ready for use." + " For more information on the software or the online identification function, visit the %1online help%2.") + .arg(QStringLiteral("").arg(onlineHelpUrl), QStringLiteral("")); + QLabel* conclusionDescLabel = new QLabel(QStringLiteral("
") + conclusionDesc); + conclusionDescLabel->setWordWrap(true); + conclusionDescLabel->setFocusPolicy(Qt::TabFocus); + conclusionDescLabel->setAccessibleName(createAccessibleName(almostDone, conclusionDesc)); + conclusionDescLabel->setOpenExternalLinks(true); + + conclusionPageVLayout->addWidget(conclusionDescLabel); + + QSpacerItem* verticalSpacer = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); + conclusionPageVLayout->addItem(verticalSpacer); + + return conclusionPage; +} + + +void SetupAssistantWizard::onChangeTransportPinButtonPressed() +{ + auto& historySettings = Env::getSingleton()->getHistorySettings(); + historySettings.setEnabled(mSaveHistoryCheckBox->isChecked()); + historySettings.save(); + Q_EMIT fireChangePinButtonClicked(); + close(); +} + + +void SetupAssistantWizard::accept() +{ + auto& historySettings = Env::getSingleton()->getHistorySettings(); + historySettings.setEnabled(mSaveHistoryCheckBox->isChecked()); + historySettings.save(); + hide(); +} + + +bool SetupAssistantWizard::eventFilter(QObject* pObject, QEvent* pEvent) +{ + if (pEvent->type() == QEvent::KeyPress) + { + QKeyEvent* keyEvent = static_cast(pEvent); + if (keyEvent->key() == Qt::Key_F1) + { + HelpAction::openContextHelp(objectName()); + return true; + } + } + return QWizard::eventFilter(pObject, pEvent); +} diff --git a/src/ui/widget/SetupAssistantWizard.h b/src/ui/widget/SetupAssistantWizard.h new file mode 100644 index 0000000..dfc32d8 --- /dev/null +++ b/src/ui/widget/SetupAssistantWizard.h @@ -0,0 +1,74 @@ +/*! + * \brief Setup assistant wizard before application startup. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace governikus +{ + +class ReaderDeviceWidget; + + +class CardReaderPage + : public QWizardPage +{ + Q_OBJECT + + private: + ReaderDeviceWidget* const mWidget; + + public: + CardReaderPage(const QString& pTitle, const QString& pAccessibleName); + virtual ~CardReaderPage() = default; +}; + + +class SetupAssistantWizard + : public QWizard +{ + Q_OBJECT + + int mPageCount; + QPointer mSaveHistoryCheckBox; + QPointer mChangeTransportPinButton; + + public: + SetupAssistantWizard(QWidget* pParent = nullptr); + virtual ~SetupAssistantWizard() override; + + virtual void accept() override; + bool isRemindWizardAgain(); + + Q_SIGNALS: + void fireChangePinButtonClicked(); + + private: + QString createAccessibleName(const QString& pName, const QString& pText = QString()) const; + QString createTitle(const QString& pName) const; + QString createDescription(const QString& pTitle, const QString& pSummary) const; + QWizardPage* createWizardInitialPinPage(); + QWizardPage* createWizardCardReaderPage(); + QWizardPage* createConclusionPage(); + + private Q_SLOTS: + void onChangeTransportPinButtonPressed(); + + protected: + virtual bool eventFilter(QObject* pObject, QEvent* pEvent) override; + +}; + +} // namespace governikus diff --git a/src/ui/widget/UIPlugInWidgets.cpp b/src/ui/widget/UIPlugInWidgets.cpp new file mode 100644 index 0000000..38ae988 --- /dev/null +++ b/src/ui/widget/UIPlugInWidgets.cpp @@ -0,0 +1,137 @@ +/*! + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "UIPlugInWidgets.h" + +#include "workflow/WorkflowAuthenticateQtGui.h" +#include "workflow/WorkflowChangePinQtGui.h" +#include "workflow/WorkflowSelfInfoQtGui.h" + +using namespace governikus; + +UIPlugInWidgets::UIPlugInWidgets() + : UIPlugIn() + , mGui() +{ + connect(&mGui, &AppQtGui::quitApplicationRequested, this, &UIPlugIn::fireQuitApplicationRequest); + connect(&mGui, &AppQtGui::fireChangePinRequested, this, &UIPlugIn::fireChangePinRequest); + connect(&mGui, &AppQtGui::selfAuthenticationRequested, this, &UIPlugIn::fireSelfAuthenticationRequested); + connect(&mGui, &AppQtGui::fireCloseReminderFinished, this, &UIPlugInWidgets::fireCloseReminderFinished); + connect(this, &UIPlugIn::fireShowUserInformation, &mGui, &AppQtGui::onShowUserInformation); + mGui.init(); +} + + +UIPlugInWidgets::~UIPlugInWidgets() +{ +} + + +void UIPlugInWidgets::doShutdown() +{ + mGui.shutdown(); +} + + +void UIPlugInWidgets::onWorkflowStarted(QSharedPointer pContext) +{ + if (!mGui.isEnabled()) + { + return; + } + + pContext->setReaderPlugInTypes({ReaderManagerPlugInType::PCSC, ReaderManagerPlugInType::REMOTE}); + + QSharedPointer currentWorkflowGui; + if (auto changePinContext = pContext.objectCast()) + { + currentWorkflowGui = mGui.createWorkflowChangePinUi(changePinContext); + mGui.activateWorkflowUi(currentWorkflowGui); + return; + } + + bool allowHideAfterWorklow = true; + if (auto selfAuthContext = pContext.objectCast()) + { + if (mGui.askChangeTransportPinNow()) + { + allowHideAfterWorklow = false; + Q_EMIT pContext->fireCancelWorkflow(); + } + currentWorkflowGui = mGui.createWorkflowSelfInfoUi(selfAuthContext); + } + else if (auto authContext = pContext.objectCast()) + { + if (mGui.askChangeTransportPinNow()) + { + allowHideAfterWorklow = false; + Q_EMIT pContext->fireCancelWorkflow(); + } + currentWorkflowGui = mGui.createWorkflowAuthenticateUi(authContext); + } + + Q_ASSERT(currentWorkflowGui != nullptr); + mGui.activateWorkflowUi(currentWorkflowGui, allowHideAfterWorklow); + pContext->setStateApproved(); +} + + +void UIPlugInWidgets::onWorkflowFinished(QSharedPointer pContext) +{ + if (!mGui.isEnabled()) + { + return; + } + + Q_UNUSED(pContext) + mGui.deactivateCurrentWorkflowUi(); +} + + +void UIPlugInWidgets::onApplicationStarted() +{ + mGui.onApplicationStarted(); +} + + +void UIPlugInWidgets::onShowUi(UiModule pModule) +{ + mGui.show(pModule); +} + + +void UIPlugInWidgets::onHideUi() +{ + mGui.hideWithoutConfirmation(); +} + + +#ifndef QT_NO_NETWORKPROXY +void UIPlugInWidgets::onProxyAuthenticationRequired(const QNetworkProxy& pProxy, QAuthenticator* pAuthenticator) +{ + mGui.onProxyAuthenticationRequired(pProxy, pAuthenticator); +} + + +#endif + + +void UIPlugInWidgets::onUiDomination(const UIPlugIn* pUi, const QString& pInformation, bool pAccepted) +{ + if (pUi == this) + { + return; + } + + if (pAccepted) + { + mGui.setEnabled(false, pInformation); + } +} + + +void UIPlugInWidgets::onUiDominationReleased() +{ + mGui.setEnabled(true); +} diff --git a/src/ui/widget/UIPlugInWidgets.h b/src/ui/widget/UIPlugInWidgets.h new file mode 100644 index 0000000..9c2d850 --- /dev/null +++ b/src/ui/widget/UIPlugInWidgets.h @@ -0,0 +1,43 @@ +/*! + * \brief QWidgets implementation of UIPlugIn. + * + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "AppQtGui.h" +#include "UIPlugIn.h" + +namespace governikus +{ + +class UIPlugInWidgets + : public UIPlugIn +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "governikus.UIPlugIn" FILE "metadata.json") + Q_INTERFACES(governikus::UIPlugIn) + + private: + AppQtGui mGui; + + public: + UIPlugInWidgets(); + virtual ~UIPlugInWidgets() override; + + public Q_SLOTS: + virtual void doShutdown() override; + virtual void onWorkflowStarted(QSharedPointer pContext) override; + virtual void onWorkflowFinished(QSharedPointer pContext) override; + virtual void onApplicationStarted() override; + virtual void onShowUi(UiModule pModule) override; + virtual void onHideUi() override; +#ifndef QT_NO_NETWORKPROXY + virtual void onProxyAuthenticationRequired(const QNetworkProxy& pProxy, QAuthenticator* pAuthenticator) override; +#endif + virtual void onUiDomination(const UIPlugIn* pUi, const QString& pInformation, bool pAccepted) override; + virtual void onUiDominationReleased() override; +}; + +} // namespace governikus diff --git a/src/ui/widget/UpdateWindow.cpp b/src/ui/widget/UpdateWindow.cpp new file mode 100644 index 0000000..7d3b616 --- /dev/null +++ b/src/ui/widget/UpdateWindow.cpp @@ -0,0 +1,76 @@ +/*! + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "UpdateWindow.h" + +#include "ui_UpdateWindow.h" + +#include "Service.h" + +#include +#include + +using namespace governikus; + +UpdateWindow::UpdateWindow(QWidget* pParent) + : QDialog(pParent) + , mUi() + , mUpdateData() +{ + fillData(); +} + + +UpdateWindow::~UpdateWindow() +{ +} + + +void UpdateWindow::onUpdateClicked() +{ + const auto& url = mUpdateData.getUrl(); + qDebug() << "Download application update:" << url; + if (!QDesktopServices::openUrl(url)) + { + Q_EMIT fireShowUpdateDialog(QMessageBox::Warning, tr("Unable to open this link in a browser. Please copy and paste the link into the address bar of your browser.")); + } + close(); +} + + +void UpdateWindow::onSkipVersionClicked() +{ + Env::getSingleton()->skipVersion(mUpdateData.getVersion()); + close(); +} + + +void UpdateWindow::fillData() +{ + mUpdateData = Env::getSingleton()->getUpdateData(); + if (!mUi) + { + mUi.reset(new Ui::UpdateWindow()); + mUi->setupUi(this); + + connect(mUi->skipButton, &QAbstractButton::clicked, this, &UpdateWindow::onSkipVersionClicked); + connect(mUi->reminderButton, &QAbstractButton::clicked, this, &QWidget::close); + connect(mUi->downloadButton, &QAbstractButton::clicked, this, &UpdateWindow::onUpdateClicked); + } + + mUi->downloadLabel->setText(tr("AusweisApp2 %1 is now available - you have %2. Would you like to download it now?").arg(mUpdateData.getVersion(), QApplication::applicationVersion())); + mUi->linkLabel->setText(QStringLiteral("%1").arg(mUpdateData.getUrl().toString())); + mUi->releaseNotes->setHtml(mUpdateData.getNotes().isEmpty() ? tr("

Download of release notes failed

") : mUpdateData.getNotes()); + mUi->releaseNotes->setAccessibleName(mUi->releaseNotes->toPlainText()); +} + + +void UpdateWindow::changeEvent(QEvent* pEvent) +{ + if (pEvent->type() == QEvent::LanguageChange && mUi) + { + mUi->retranslateUi(this); + } + QWidget::changeEvent(pEvent); +} diff --git a/src/ui/widget/UpdateWindow.h b/src/ui/widget/UpdateWindow.h new file mode 100644 index 0000000..251a368 --- /dev/null +++ b/src/ui/widget/UpdateWindow.h @@ -0,0 +1,48 @@ +/*! + * \brief Window for application updates + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "AppUpdateData.h" + +#include +#include + +namespace Ui +{ +class UpdateWindow; +} // namespace Ui + +namespace governikus +{ + +class UpdateWindow + : public QDialog +{ + Q_OBJECT + + private: + QScopedPointer mUi; + bool mSilent; + AppUpdateData mUpdateData; + + protected: + virtual void changeEvent(QEvent* pEvent) override; + + public: + UpdateWindow(QWidget* pParent = nullptr); + virtual ~UpdateWindow() override; + void fillData(); + + private Q_SLOTS: + void onUpdateClicked(); + void onSkipVersionClicked(); + + Q_SIGNALS: + void fireShowUpdateDialog(QMessageBox::Icon pIcon, const QString& pMsg); +}; + +} // namespace governikus diff --git a/src/ui/widget/UpdateWindow.ui b/src/ui/widget/UpdateWindow.ui new file mode 100644 index 0000000..8d1e3b4 --- /dev/null +++ b/src/ui/widget/UpdateWindow.ui @@ -0,0 +1,179 @@ + + + UpdateWindow + + + Qt::ApplicationModal + + + + 0 + 0 + 600 + 450 + + + + Software Update + + + + :/images/npa.svg:/images/npa.svg + + + + + + + + + 75 + true + + + + Qt::TabFocus + + + A new version of AusweisApp2 is available! + + + + + + + Qt::TabFocus + + + + + + + Qt::TabFocus + + + The update file is located at: + + + + + + + Qt::TabFocus + + + LinkLabel + + + true + + + Qt::TextBrowserInteraction + + + + + + + + 75 + true + + + + Qt::TabFocus + + + Release Notes: + + + + + + + 160 + 80 + + + + Qt::WheelFocus + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse + + + + + + + + + + Qt::TabFocus + + + Download this update and close current "AusweisApp2". Install the update and start "AusweisApp2" again. + + + + + + + Qt::TabFocus + + + When you click "Download update", this link will be opened in your browser. + + + + + + + + + Skip update + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Remind me later + + + + + + + Download update + + + true + + + true + + + + + + + + + + + + diff --git a/src/widget/generic/BusyOverlay.cpp b/src/ui/widget/generic/BusyOverlay.cpp similarity index 100% rename from src/widget/generic/BusyOverlay.cpp rename to src/ui/widget/generic/BusyOverlay.cpp diff --git a/src/ui/widget/generic/BusyOverlay.h b/src/ui/widget/generic/BusyOverlay.h new file mode 100644 index 0000000..b1cdf22 --- /dev/null +++ b/src/ui/widget/generic/BusyOverlay.h @@ -0,0 +1,44 @@ +/*! + * \brief Widget for the settings. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include + +namespace Ui +{ +class BusyOverlay; +} // namespace Ui + + +namespace governikus +{ + +class BusyOverlay + : public QWidget +{ + Q_OBJECT + + public: + BusyOverlay(bool pStart = true, QWidget* pParent = nullptr); + virtual ~BusyOverlay() override; + + void startAnimation(); + void stopAnimation(); + + virtual QSize sizeHint() const override; + + protected: + void paintEvent(QPaintEvent*) override; + void changeEvent(QEvent* pEvent) override; + + private: + QScopedPointer mUi; + QScopedPointer mMovie; +}; + +} // namespace governikus diff --git a/src/widget/generic/BusyOverlay.ui b/src/ui/widget/generic/BusyOverlay.ui similarity index 100% rename from src/widget/generic/BusyOverlay.ui rename to src/ui/widget/generic/BusyOverlay.ui diff --git a/src/ui/widget/generic/BusyOverlayContainer.cpp b/src/ui/widget/generic/BusyOverlayContainer.cpp new file mode 100644 index 0000000..273574a --- /dev/null +++ b/src/ui/widget/generic/BusyOverlayContainer.cpp @@ -0,0 +1,45 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "BusyOverlayContainer.h" + +#include "BusyOverlay.h" + +#include +#include + +using namespace governikus; + +BusyOverlayContainer::BusyOverlayContainer(QWidget* pWidgetToOverlay, bool pStart, QWidget* pParent) + : QStackedWidget(pParent) + , mOverlay(new BusyOverlay(pStart)) +{ + QWidget* busyOverlayContainer = new QWidget; + QBoxLayout* overlayContainerLayout = new QVBoxLayout(busyOverlayContainer); + overlayContainerLayout->addWidget(mOverlay, 0, Qt::AlignHCenter | Qt::AlignVCenter); + + QStackedLayout* stackLayout = qobject_cast(layout()); + stackLayout->setStackingMode(QStackedLayout::StackAll); + + stackLayout->addWidget(pWidgetToOverlay); + stackLayout->addWidget(busyOverlayContainer); + stackLayout->setCurrentIndex(1); +} + + +BusyOverlayContainer::~BusyOverlayContainer() +{ +} + + +void BusyOverlayContainer::startAnimation() +{ + mOverlay->startAnimation(); +} + + +void BusyOverlayContainer::stopAnimation() +{ + mOverlay->stopAnimation(); +} diff --git a/src/ui/widget/generic/BusyOverlayContainer.h b/src/ui/widget/generic/BusyOverlayContainer.h new file mode 100644 index 0000000..c3b588b --- /dev/null +++ b/src/ui/widget/generic/BusyOverlayContainer.h @@ -0,0 +1,32 @@ +/*! + * \brief An overlay to show a busy indicator. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include + +namespace governikus +{ + +class BusyOverlay; + +class BusyOverlayContainer + : public QStackedWidget +{ + Q_OBJECT + + public: + BusyOverlayContainer(QWidget* pWidgetToOverlay, bool pStart = true, QWidget* pParent = nullptr); + virtual ~BusyOverlayContainer(); + + void startAnimation(); + void stopAnimation(); + + private: + BusyOverlay* mOverlay; +}; + +} // namespace governikus diff --git a/src/ui/widget/generic/ButtonState.h b/src/ui/widget/generic/ButtonState.h new file mode 100644 index 0000000..90bc6f1 --- /dev/null +++ b/src/ui/widget/generic/ButtonState.h @@ -0,0 +1,27 @@ +/*! + * \brief Defines the ButtonState enum. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +namespace governikus +{ + +enum class ButtonState +{ + /*! Button is visible, enabled, and focussed. */ + FOCUSSED, + + /*! Button is visible and enabled. */ + ENABLED, + + /*! Button is visible and disabled. */ + DISABLED, + + /*! Button is not visible. */ + HIDDEN +}; + +} // namespace governikus diff --git a/src/ui/widget/generic/ExclusiveButtonGroup.cpp b/src/ui/widget/generic/ExclusiveButtonGroup.cpp new file mode 100644 index 0000000..17503a4 --- /dev/null +++ b/src/ui/widget/generic/ExclusiveButtonGroup.cpp @@ -0,0 +1,119 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "ExclusiveButtonGroup.h" + +#include + +using namespace governikus; + +ExclusiveButtonGroup::ExclusiveButtonGroup(QObject* pParent) + : QObject(pParent) + , mButtons() +{ +} + + +ExclusiveButtonGroup::~ExclusiveButtonGroup() +{ +} + + +void ExclusiveButtonGroup::addButton(QAbstractButton* pButton) +{ + mButtons += pButton; + + pButton->installEventFilter(this); + + connect(pButton, &QAbstractButton::clicked, this, &ExclusiveButtonGroup::onButtonClicked); + connect(pButton, &QAbstractButton::pressed, this, &ExclusiveButtonGroup::onButtonPressed); + connect(pButton, &QAbstractButton::released, this, &ExclusiveButtonGroup::onButtonReleased); + connect(pButton, &QAbstractButton::toggled, this, &ExclusiveButtonGroup::onButtonToggled); +} + + +void ExclusiveButtonGroup::removeButton(QAbstractButton* pButton) +{ + if (mButtons.removeAll(pButton) == 0) + { + return; + } + + pButton->removeEventFilter(this); + + disconnect(pButton, &QAbstractButton::clicked, this, &ExclusiveButtonGroup::onButtonClicked); + disconnect(pButton, &QAbstractButton::pressed, this, &ExclusiveButtonGroup::onButtonPressed); + disconnect(pButton, &QAbstractButton::released, this, &ExclusiveButtonGroup::onButtonReleased); + disconnect(pButton, &QAbstractButton::toggled, this, &ExclusiveButtonGroup::onButtonToggled); +} + + +bool ExclusiveButtonGroup::eventFilter(QObject* pWatched, QEvent* pEvent) +{ + if (QAbstractButton* button = qobject_cast(pWatched)) + { + if (pEvent->type() == QEvent::MouseButtonPress && button->isChecked()) + { + return true; + } + + if (pEvent->type() == QEvent::KeyPress) + { + QKeyEvent* keyEvent = static_cast(pEvent); + if (keyEvent->key() == Qt::Key_Select || keyEvent->key() == Qt::Key_Space) + { + return true; + } + } + } + + return false; +} + + +void ExclusiveButtonGroup::onButtonClicked(bool /*pChecked*/) +{ + if (QAbstractButton* button = qobject_cast(sender())) + { + Q_EMIT buttonClicked(button); + } +} + + +void ExclusiveButtonGroup::onButtonPressed() +{ + if (QAbstractButton* button = qobject_cast(sender())) + { + Q_EMIT buttonPressed(button); + } +} + + +void ExclusiveButtonGroup::onButtonReleased() +{ + if (QAbstractButton* button = qobject_cast(sender())) + { + Q_EMIT buttonReleased(button); + } +} + + +void ExclusiveButtonGroup::onButtonToggled(bool pChecked) +{ + if (QAbstractButton* button = qobject_cast(sender())) + { + if (pChecked) + { + for (auto otherButton : qAsConst(mButtons)) + { + if (otherButton != button && otherButton->isChecked()) + { + otherButton->setChecked(false); + } + } + } + + Q_EMIT buttonToggled(button, pChecked); + } +} diff --git a/src/ui/widget/generic/ExclusiveButtonGroup.h b/src/ui/widget/generic/ExclusiveButtonGroup.h new file mode 100644 index 0000000..302cab4 --- /dev/null +++ b/src/ui/widget/generic/ExclusiveButtonGroup.h @@ -0,0 +1,55 @@ +/*! + * \brief Rudimentary replacement for QButtonGroup to work around tab navigation issues. + * + * Bug in Qt 5.2.1: Buttons in a QButtonGroup cannot be navigated via the Tab key. This + * class provides a work-around for simple cases. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include +#include + +namespace governikus +{ + +class ExclusiveButtonGroup + : public QObject +{ + Q_OBJECT + + public: + ExclusiveButtonGroup(QObject* pParent = nullptr); + virtual ~ExclusiveButtonGroup() override; + + const QVector& getButtons() const + { + return mButtons; + } + + + void addButton(QAbstractButton* pButton); + void removeButton(QAbstractButton* pButton); + + virtual bool eventFilter(QObject* pWatched, QEvent* pEvent) override; + + Q_SIGNALS: + void buttonClicked(QAbstractButton* pButton); + void buttonPressed(QAbstractButton* pButton); + void buttonReleased(QAbstractButton* pButton); + void buttonToggled(QAbstractButton* pButton, bool pChecked); + + private Q_SLOTS: + void onButtonClicked(bool pChecked); + void onButtonPressed(); + void onButtonReleased(); + void onButtonToggled(bool pChecked); + + private: + QVector mButtons; +}; + +} // namespace governikus diff --git a/src/ui/widget/generic/GuiModule.h b/src/ui/widget/generic/GuiModule.h new file mode 100644 index 0000000..ecf540e --- /dev/null +++ b/src/ui/widget/generic/GuiModule.h @@ -0,0 +1,20 @@ +/*! + * \brief Defines the GuiModule enum. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +namespace governikus +{ + +enum class GuiModule +{ + START_PAGE, + IDENTIFY, + GENERAL_SETTINGS, + PIN_SETTINGS +}; + +} // namespace governikus diff --git a/src/ui/widget/generic/GuiUtils.cpp b/src/ui/widget/generic/GuiUtils.cpp new file mode 100644 index 0000000..c9bc40c --- /dev/null +++ b/src/ui/widget/generic/GuiUtils.cpp @@ -0,0 +1,109 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "GuiUtils.h" + +#include +#include +#include +#include +#include + +using namespace governikus; + + +bool GuiUtils::showPinCanPukErrorDialog(CardReturnCode pReturnCode, bool pCanAllowedMode, QWidget* pParent) +{ + bool errorDisplayed = false; + QString title; + QString text; + switch (pReturnCode) + { + case CardReturnCode::INVALID_CAN: + title = tr("Wrong card access number (CAN)"); + if (pCanAllowedMode) + { + text = tr("The given card access number (CAN) is not correct."); + } + else + { + text = tr("The given card access number (CAN) is not correct. You have one more try to enter the correct PIN." + " Please mind that you have to acknowledge this last try with your card access" + " number (CAN)."); + } + break; + + case CardReturnCode::INVALID_PUK: + title = tr("Wrong PUK"); + text = tr("Please enter your PUK again."); + break; + + case CardReturnCode::PUK_INOPERATIVE: + title = tr("PUK is inoperative"); + text = tr("You have correctly entered the PUK ten times and have thus reached the maximum count." + " The PUK is now inoperative and can no longer be used for unblocking the PIN. Please address your" + " competent authority that has issued your ID card for unblocking your PIN."); + break; + + case CardReturnCode::INVALID_PIN: + title = tr("Wrong PIN"); + text = tr("The given PIN is not correct. You have 2 remaining tries to enter the correct PIN."); + break; + + case CardReturnCode::INVALID_PIN_2: + title = tr("Wrong PIN"); + text = tr("The given PIN is not correct. You have one more try to enter the correct PIN." + " Please mind that you have to acknowledge this last try with your card access" + " number (CAN)."); + break; + + case CardReturnCode::INVALID_PIN_3: + title = tr("Wrong PIN"); + text = tr("After three wrong entries your PIN is blocked. Using the online identification" + " function is no longer possible.

You can unblock your PIN in the" + " following dialog. The program supports you with the steps now required."); + break; + + default: + return errorDisplayed; + } + + errorDisplayed = true; + QMessageBox messageBox(pParent); + messageBox.setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + title); + messageBox.setWindowModality(Qt::WindowModal); + messageBox.setWindowFlags(messageBox.windowFlags() & ~Qt::WindowContextHelpButtonHint); + messageBox.setText(QStringLiteral("

%1

%2

").arg(title, text)); + messageBox.setIconPixmap(QIcon(QStringLiteral(":/images/npa.svg")).pixmap(32, 32)); + messageBox.setStandardButtons(QMessageBox::Ok); + messageBox.button(QMessageBox::Ok)->setFocus(); + + messageBox.exec(); + return errorDisplayed; +} + + +bool GuiUtils::showWrongPinBlockedDialog(QWidget* pParent) +{ + QMessageBox messageBox(pParent); + + QString title = tr("PIN blocked"); + QString text = tr("After three wrong entries your PIN is blocked. Using the online identification" + " function is no longer possible.
You can unblock the PIN as" + " follows:
  1. Select the \"Settings\" function.
  2. Select the \"PIN" + " Management\" tab.
  3. Follow the instructions on the" + " screen.
Note: You will find the PUK in the letter you received during" + " the application for the ID card in the \"Unblocking key PUK\" section. Further" + " information is available on the site http://www.personalausweisportal.de.
" + "Do you want to unblock the PIN now?"); + messageBox.setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + title); + messageBox.setWindowModality(Qt::WindowModal); + messageBox.setText(QStringLiteral("

%1

%2

").arg(title, text)); + messageBox.setIcon(QMessageBox::Warning); + messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel); + messageBox.button(QMessageBox::Yes)->setFocus(); + + return messageBox.exec() == QMessageBox::Yes; +} diff --git a/src/ui/widget/generic/GuiUtils.h b/src/ui/widget/generic/GuiUtils.h new file mode 100644 index 0000000..12c5f9a --- /dev/null +++ b/src/ui/widget/generic/GuiUtils.h @@ -0,0 +1,26 @@ +/*! + * \brief Gui utility functions. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "CardReturnCode.h" + +#include + +namespace governikus +{ + +class GuiUtils + : private QObject +{ + Q_OBJECT + + public: + static bool showPinCanPukErrorDialog(CardReturnCode pReturnCode, bool pCanAllowedMode, QWidget* pParent); + static bool showWrongPinBlockedDialog(QWidget* pParent); +}; + +} // namespace governikus diff --git a/src/ui/widget/generic/ListCheckItemWidget.cpp b/src/ui/widget/generic/ListCheckItemWidget.cpp new file mode 100644 index 0000000..2dab1e5 --- /dev/null +++ b/src/ui/widget/generic/ListCheckItemWidget.cpp @@ -0,0 +1,133 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "ListCheckItemWidget.h" +#include "ui_ListCheckItemWidget.h" + +#include +#include +#include +#include +#include +#include + +using namespace governikus; + + +ListCheckItemWidget::ListCheckItemWidget(QWidget* pParent) + : QWidget(pParent) + , mUi(new Ui::ListCheckItemWidget()) +{ + mUi->setupUi(this); + mUi->listIcon->hide(); + + connect(mUi->listCheckBox, &QCheckBox::stateChanged, this, &ListCheckItemWidget::onCheckBoxChanged); + + installEventFilter(this); +} + + +ListCheckItemWidget::ListCheckItemWidget(QWidget* pParent, const QPixmap& pPixmap) + : QWidget(pParent) + , mUi(new Ui::ListCheckItemWidget()) +{ + mUi->setupUi(this); + mUi->listIcon->setScaledContents(true); + mUi->listIcon->setPixmap(pPixmap); + + connect(mUi->listCheckBox, &QCheckBox::stateChanged, this, &ListCheckItemWidget::onCheckBoxChanged); + + installEventFilter(this); +} + + +ListCheckItemWidget::~ListCheckItemWidget() +{ +} + + +void ListCheckItemWidget::itemWidgetReleased() +{ + mUi->listItemLayout->setObjectName(QStringLiteral("listItemLayout")); + //qApp->setStyleSheet(qApp->styleSheet()); +} + + +void ListCheckItemWidget::onCheckBoxChanged(int /*pChanged*/) +{ + Q_EMIT listItemWidgetChecked(this); +} + + +void ListCheckItemWidget::itemWidgetPressed() +{ + mUi->listItemLayout->setObjectName(QStringLiteral("listItemLayout_pressed")); + //qApp->setStyleSheet(qApp->styleSheet()); + + Q_EMIT listItemWidgetChecked(this); +} + + +bool ListCheckItemWidget::eventFilter(QObject* /*pWatched*/, QEvent* pEvent) +{ + switch (pEvent->type()) + { + case QEvent::MouseButtonPress: + itemWidgetPressed(); + return false; + + case QEvent::MouseButtonRelease: + itemWidgetReleased(); + return false; + + default: + return false; + } +} + + +void ListCheckItemWidget::setHeading(const QString& pHeading) +{ + mUi->heading->setText(pHeading); +} + + +void ListCheckItemWidget::setSubHeading(const QString& pSubHeading) +{ + if (!pSubHeading.isNull() || !pSubHeading.isEmpty()) + { + mUi->subHeading->setText(pSubHeading); + } + else + { + mUi->headingFrameLayout->layout()->removeWidget(mUi->subHeading); + delete mUi->subHeading; + } +} + + +void ListCheckItemWidget::paintEvent(QPaintEvent*) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} + + +QCheckBox* ListCheckItemWidget::getListItemCheckBox() +{ + return mUi->listCheckBox; +} + + +void ListCheckItemWidget::changeEvent(QEvent* pEvent) +{ + if (pEvent->type() == QEvent::LanguageChange) + { + mUi->retranslateUi(this); + } + + QWidget::changeEvent(pEvent); +} diff --git a/src/ui/widget/generic/ListCheckItemWidget.h b/src/ui/widget/generic/ListCheckItemWidget.h new file mode 100644 index 0000000..5e05727 --- /dev/null +++ b/src/ui/widget/generic/ListCheckItemWidget.h @@ -0,0 +1,55 @@ +/*! + * \brief List item widget for list actions. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include +#include + +namespace Ui +{ +class ListCheckItemWidget; +} // namespace Ui + +namespace governikus +{ + +class ListCheckItemWidget + : public QWidget +{ + Q_OBJECT + + public: + ListCheckItemWidget(QWidget* pParent, const QPixmap& pPixmap); + ListCheckItemWidget(QWidget* pParent); + virtual ~ListCheckItemWidget() override; + + void setHeading(const QString& pHeading); + void setSubHeading(const QString& pSubHeading); + + QCheckBox* getListItemCheckBox(); + + protected: + void changeEvent(QEvent* pEvent) override; + + Q_SIGNALS: + void listItemWidgetChecked(ListCheckItemWidget* pListCheckItemWidget); + + private Q_SLOTS: + void onCheckBoxChanged(int pChanged); + + private: + QScopedPointer mUi; + + virtual bool eventFilter(QObject* pWatched, QEvent* pEvent) override; + virtual void paintEvent(QPaintEvent*) override; + + void itemWidgetReleased(); + void itemWidgetPressed(); +}; + +} // namespace governikus diff --git a/src/widget/generic/ListCheckItemWidget.ui b/src/ui/widget/generic/ListCheckItemWidget.ui similarity index 100% rename from src/widget/generic/ListCheckItemWidget.ui rename to src/ui/widget/generic/ListCheckItemWidget.ui diff --git a/src/widget/generic/ListItem.cpp b/src/ui/widget/generic/ListItem.cpp similarity index 100% rename from src/widget/generic/ListItem.cpp rename to src/ui/widget/generic/ListItem.cpp diff --git a/src/ui/widget/generic/ListItem.h b/src/ui/widget/generic/ListItem.h new file mode 100644 index 0000000..13c4633 --- /dev/null +++ b/src/ui/widget/generic/ListItem.h @@ -0,0 +1,25 @@ +/* + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include + + +namespace governikus +{ + +class ListItem + : public QWidget +{ + Q_OBJECT + + public: + ListItem(QWidget* pParent = nullptr, Qt::WindowFlags pWindowFlags = 0); + virtual ~ListItem() override; + + void paintEvent(QPaintEvent*) override; +}; + +} // namespace governikus diff --git a/src/widget/generic/ListItemIconLeft.cpp b/src/ui/widget/generic/ListItemIconLeft.cpp similarity index 100% rename from src/widget/generic/ListItemIconLeft.cpp rename to src/ui/widget/generic/ListItemIconLeft.cpp diff --git a/src/ui/widget/generic/ListItemIconLeft.h b/src/ui/widget/generic/ListItemIconLeft.h new file mode 100644 index 0000000..1d074e4 --- /dev/null +++ b/src/ui/widget/generic/ListItemIconLeft.h @@ -0,0 +1,23 @@ +/* + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include + +namespace governikus +{ + +class ListItemIconLeft + : public QLabel +{ + Q_OBJECT + + public: + ListItemIconLeft(QWidget* pParent = nullptr); + ListItemIconLeft(const QString& pText, QWidget* pParent = nullptr); + virtual ~ListItemIconLeft(); +}; + +} // namespace governikus diff --git a/src/widget/generic/ListItemIconRight.cpp b/src/ui/widget/generic/ListItemIconRight.cpp similarity index 100% rename from src/widget/generic/ListItemIconRight.cpp rename to src/ui/widget/generic/ListItemIconRight.cpp diff --git a/src/ui/widget/generic/ListItemIconRight.h b/src/ui/widget/generic/ListItemIconRight.h new file mode 100644 index 0000000..bd169fa --- /dev/null +++ b/src/ui/widget/generic/ListItemIconRight.h @@ -0,0 +1,23 @@ +/* + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include + +namespace governikus +{ + +class ListItemIconRight + : public QLabel +{ + Q_OBJECT + + public: + ListItemIconRight(QWidget* pParent = nullptr); + ListItemIconRight(const QString& pText, QWidget* pParent = nullptr); + virtual ~ListItemIconRight(); +}; + +} // namespace governikus diff --git a/src/widget/generic/ListItemSubTitle.cpp b/src/ui/widget/generic/ListItemSubTitle.cpp similarity index 100% rename from src/widget/generic/ListItemSubTitle.cpp rename to src/ui/widget/generic/ListItemSubTitle.cpp diff --git a/src/ui/widget/generic/ListItemSubTitle.h b/src/ui/widget/generic/ListItemSubTitle.h new file mode 100644 index 0000000..6170115 --- /dev/null +++ b/src/ui/widget/generic/ListItemSubTitle.h @@ -0,0 +1,23 @@ +/* + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include + +namespace governikus +{ + +class ListItemSubTitle + : public QLabel +{ + Q_OBJECT + + public: + ListItemSubTitle(QWidget* pParent = nullptr); + ListItemSubTitle(const QString& pText, QWidget* pParent = nullptr); + virtual ~ListItemSubTitle(); +}; + +} // namespace governikus diff --git a/src/widget/generic/ListItemTitle.cpp b/src/ui/widget/generic/ListItemTitle.cpp similarity index 100% rename from src/widget/generic/ListItemTitle.cpp rename to src/ui/widget/generic/ListItemTitle.cpp diff --git a/src/ui/widget/generic/ListItemTitle.h b/src/ui/widget/generic/ListItemTitle.h new file mode 100644 index 0000000..c5d98e2 --- /dev/null +++ b/src/ui/widget/generic/ListItemTitle.h @@ -0,0 +1,23 @@ +/* + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include + +namespace governikus +{ + +class ListItemTitle + : public QLabel +{ + Q_OBJECT + + public: + ListItemTitle(QWidget* pParent = nullptr); + ListItemTitle(const QString& pText, QWidget* pParent = nullptr); + virtual ~ListItemTitle(); +}; + +} // namespace governikus diff --git a/src/ui/widget/generic/PasswordEdit.cpp b/src/ui/widget/generic/PasswordEdit.cpp new file mode 100644 index 0000000..a82007e --- /dev/null +++ b/src/ui/widget/generic/PasswordEdit.cpp @@ -0,0 +1,187 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + + +#include "PasswordEdit.h" + +#include "ScopeGuard.h" + +#include +#include + + +Q_DECLARE_LOGGING_CATEGORY(gui) + +using namespace governikus; + + +namespace governikus +{ +class RegExValidator + : public QRegularExpressionValidator +{ + Q_OBJECT + + private: + const QString mInvalidValueToolTip; + + public: + RegExValidator(const QRegularExpression& pExpression, const QString& pInvalidValueToolTip, QWidget* pParent) + : QRegularExpressionValidator(pExpression, pParent) + , mInvalidValueToolTip(pInvalidValueToolTip) + { + } + + + virtual QValidator::State validate(QString& pInput, int& pPos) const override + { + QValidator::State state = QRegularExpressionValidator::validate(pInput, pPos); + + if (state == State::Invalid && !mInvalidValueToolTip.isNull()) + { + QWidget* parentWidget = static_cast(parent()); + QToolTip::showText(parentWidget->mapToGlobal(QPoint(0, 0)), mInvalidValueToolTip, parentWidget, QRect(), 3000); + } + + return state; + } + + +}; + +} // namespace governikus + + +PasswordEdit::PasswordEdit(QWidget* pParent) + : QWidget(pParent) + , mUi(new Ui::PasswordEdit()) +{ + mUi->setupUi(this); + mUi->lineEdit->installEventFilter(this); + + connect(mUi->lineEdit, &QLineEdit::textEdited, this, &PasswordEdit::textEdited); + connect(mUi->lineEdit, &QLineEdit::selectionChanged, this, &PasswordEdit::selectionChanged); +} + + +int PasswordEdit::determindeWidth(int pNumChars) +{ + QLineEdit* const lineEdit = mUi->lineEdit; + + const QString currentText = lineEdit->text(); + const ScopeGuard resetText([lineEdit, currentText] { + lineEdit->setText(currentText); + }); + + // get the display text for a password of length pWidth + lineEdit->setText(QString(pNumChars, QLatin1Char('6'))); + const int displayTextWidth = lineEdit->fontMetrics().width(lineEdit->displayText()); + + // in QLineEdit::sizeHint() the width is calculated as + // 17th times the size of 'x' plus some magic margins. + // So we calculate this margin by subtraction to set the content size correctly. + const int widthHint = lineEdit->sizeHint().width(); + const int margin = widthHint - 17 * lineEdit->fontMetrics().width(QLatin1Char('x')); + return margin + displayTextWidth; +} + + +bool PasswordEdit::eventFilter(QObject* pObj, QEvent* pEvent) +{ + if (pEvent->type() == QEvent::KeyPress) + { + const QKeyEvent* const keyEvent = static_cast(pEvent); + if (keyEvent->key() == Qt::Key_Backspace && text().isEmpty()) + { + Q_EMIT fireBackspacePressedAndEmpty(); + } + } + return QObject::eventFilter(pObj, pEvent); +} + + +void PasswordEdit::setMaxLength(int pLength, bool pShrink) +{ + if (pShrink) + { + mUi->lineEdit->setFixedWidth(determindeWidth(pLength)); + } + mUi->lineEdit->setMaxLength(pLength); +} + + +void PasswordEdit::configureValidation(const QRegularExpression& pExpression, const QString& pInvalidValueToolTip) +{ + mUi->lineEdit->setValidator(new RegExValidator(pExpression, pInvalidValueToolTip, this)); +} + + +void PasswordEdit::removeLastCharacter() +{ + const QString current = mUi->lineEdit->text(); + if (current.isEmpty()) + { + return; + } + mUi->lineEdit->setText(current.left(current.length() - 1)); +} + + +QString PasswordEdit::text() const +{ + return mUi->lineEdit->text(); +} + + +void PasswordEdit::setDigitFieldInvalid(bool pMakeInvalid, const QString& pInvalidMessage) +{ + if (pMakeInvalid) + { + mUi->lineEdit->setStyleSheet(QStringLiteral("background-color: red;")); + QToolTip::showText(mapToGlobal(QPoint(0, 32)), pInvalidMessage, mUi->lineEdit, QRect(), 3000); + } + else + { + mUi->lineEdit->setStyleSheet(QString()); + } +} + + +void PasswordEdit::clear() +{ + mUi->lineEdit->clear(); +} + + +void PasswordEdit::setText(const QString& pText) +{ + mUi->lineEdit->setText(pText); +} + + +void PasswordEdit::setCursorPosition(int pPosition) +{ + mUi->lineEdit->setCursorPosition(pPosition); +} + + +void PasswordEdit::setAccessibleName(const QString& pName) +{ + mUi->lineEdit->setAccessibleName(pName); +} + + +void PasswordEdit::setFocus() +{ + mUi->lineEdit->setFocus(); +} + + +void PasswordEdit::setAlignment(Qt::Alignment pAlignment) +{ + mUi->lineEdit->setAlignment(pAlignment); +} + + +#include "PasswordEdit.moc" diff --git a/src/ui/widget/generic/PasswordEdit.h b/src/ui/widget/generic/PasswordEdit.h new file mode 100644 index 0000000..e16de24 --- /dev/null +++ b/src/ui/widget/generic/PasswordEdit.h @@ -0,0 +1,55 @@ +/*! + * \brief Widget for entering a password that uses the password echo mode. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "ui_PasswordEdit.h" + +#include +#include +#include +#include +#include +#include + +namespace governikus +{ + +class PasswordEdit + : public QWidget +{ + Q_OBJECT + + private: + QScopedPointer mUi; + + int determindeWidth(int pNumChars); + + protected: + virtual bool eventFilter(QObject* pObj, QEvent* pEvent) override; + + public: + PasswordEdit(QWidget* pParent = nullptr); + + void setMaxLength(int pLength, bool pShrink = true); + void configureValidation(const QRegularExpression& pExpression, const QString& pInvalidValueToolTip); + void removeLastCharacter(); + QString text() const; + void setDigitFieldInvalid(bool pMakeInvalid, const QString& pInvalidMessage); + void clear(); + void setText(const QString& pText); + void setCursorPosition(int pPosition); + void setAccessibleName(const QString& pName); + void setFocus(); + void setAlignment(Qt::Alignment pAlignment); + + Q_SIGNALS: + void textEdited(const QString& pText); + void selectionChanged(); + void fireBackspacePressedAndEmpty(); +}; + +} // namespace governikus diff --git a/src/widget/generic/PasswordEdit.ui b/src/ui/widget/generic/PasswordEdit.ui similarity index 100% rename from src/widget/generic/PasswordEdit.ui rename to src/ui/widget/generic/PasswordEdit.ui diff --git a/src/widget/generic/TabButtonGroup.cpp b/src/ui/widget/generic/TabButtonGroup.cpp similarity index 100% rename from src/widget/generic/TabButtonGroup.cpp rename to src/ui/widget/generic/TabButtonGroup.cpp diff --git a/src/ui/widget/generic/TabButtonGroup.h b/src/ui/widget/generic/TabButtonGroup.h new file mode 100644 index 0000000..a0713cb --- /dev/null +++ b/src/ui/widget/generic/TabButtonGroup.h @@ -0,0 +1,110 @@ +/*! + * \brief Contains the accessibility friendly TabButtonGroup and TabButton classes. + * + * The other classes defined in this header are implementation private. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include + +namespace governikus +{ + +class ExclusiveButtonGroup; + + +/*! + * \brief A push button that is presented to accessibility clients as a page tab. + */ +class TabButton + : public QToolButton +{ + Q_OBJECT + + public: + TabButton(QWidget* pParent = nullptr); + virtual ~TabButton() override; + + protected: + virtual void focusInEvent(QFocusEvent* pEvent) override; + virtual void nextCheckState() override; +}; + +/*! + * \brief A widget that is presented to accessibility clients as a page tab list, + * but uses TabButtons as tabs. + * + * The class is a regular widget. Buttons (that must be checkable) added via + * addButton() are added to an exclusive button group. Only one button can be + * checked at a time. The focus handling and cursor key navigation is overridden + * so that it works like in the tab row of a regular QTabWidget. + */ +class TabButtonGroup + : public QWidget +{ + Q_OBJECT + + public: + TabButtonGroup(QWidget* pParent = nullptr); + virtual ~TabButtonGroup() override; + + void addButton(QAbstractButton* pButton); + + void setWorkflowActive(bool pWorkflowActiv); + + virtual bool eventFilter(QObject* pWatched, QEvent* pEvent) override; + + private Q_SLOTS: + void onButtonToggled(QAbstractButton* pButton, bool pChecked); + + private: + QAbstractButton* getNextPrevFocussableButton(QAbstractButton* pCurrentButton, bool pNext, bool pCycle) const; + void updateFocusPolicies(); + + private: + ExclusiveButtonGroup* mButtonGroup; + bool mWorkflowActive; + void paintEvent(QPaintEvent*) override; + + Q_SIGNALS: + void buttonToggled(QAbstractButton* pButton, bool pChecked); +}; + +/*! + * \brief Implementation private class providing the accessibility functionality + * for TabButton. + */ +class AccessibleTabButton + : public QAccessibleWidget +{ + public: + AccessibleTabButton(QWidget* pWidget); + + TabButton* getTabButton() const; + + virtual QString text(QAccessible::Text pText) const override; + + virtual QStringList actionNames() const override; + virtual void doAction(const QString& pActionName) override; + +}; + +/*! + * \brief Implementation private class providing the accessibility functionality + * for TabButtonGroup. + */ +class AccessibleTabButtonGroup + : public QAccessibleWidget +{ + public: + AccessibleTabButtonGroup(QWidget* pWidget); + virtual ~AccessibleTabButtonGroup(); + + TabButtonGroup* getTabButtonGroup() const; +}; + +} // namespace governikus diff --git a/src/widget/metadata.json b/src/ui/widget/metadata.json similarity index 100% rename from src/widget/metadata.json rename to src/ui/widget/metadata.json diff --git a/src/ui/widget/step/AuthenticateStepsWidget.cpp b/src/ui/widget/step/AuthenticateStepsWidget.cpp new file mode 100644 index 0000000..72a822e --- /dev/null +++ b/src/ui/widget/step/AuthenticateStepsWidget.cpp @@ -0,0 +1,60 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "AuthenticateStepsWidget.h" +#include "ui_AuthenticateStepsWidget.h" + +#include "AppStartPage.h" +#include "generic/BusyOverlayContainer.h" + +#include +#include + +using namespace governikus; + +AuthenticateStepsWidget::AuthenticateStepsWidget(QWidget* pParent) + : QStackedWidget(pParent) + , mUi(new Ui::AuthenticateStepsWidget()) + , mProcessingPage(new BusyOverlayContainer(new AppStartPage(), false)) +{ + mUi->setupUi(this); + + addWidget(mProcessingPage); +} + + +AuthenticateStepsWidget::~AuthenticateStepsWidget() +{ +} + + +StepAuthenticationEac1Widget* AuthenticateStepsWidget::getEac1Page() const +{ + return mUi->authenticationEac1Page; +} + + +SelfInfoWidget* AuthenticateStepsWidget::getSelfInfoPage() const +{ + return mUi->selfInfoPage; +} + + +void AuthenticateStepsWidget::paintEvent(QPaintEvent*) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} + + +void AuthenticateStepsWidget::changeEvent(QEvent* pEvent) +{ + if (pEvent->type() == QEvent::LanguageChange) + { + mUi->retranslateUi(this); + } + QWidget::changeEvent(pEvent); +} diff --git a/src/ui/widget/step/AuthenticateStepsWidget.h b/src/ui/widget/step/AuthenticateStepsWidget.h new file mode 100644 index 0000000..a65fc3c --- /dev/null +++ b/src/ui/widget/step/AuthenticateStepsWidget.h @@ -0,0 +1,53 @@ +/*! + * \brief A stacked widget containing the widgets for the authentication steps. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include + +namespace Ui +{ +class AuthenticateStepsWidget; +} // namespace Ui + + +namespace governikus +{ + +class BusyOverlayContainer; +class SelfInfoWidget; +class StepAuthenticationEac1Widget; + +class AuthenticateStepsWidget + : public QStackedWidget +{ + Q_OBJECT + + public: + AuthenticateStepsWidget(QWidget* pParent = nullptr); + virtual ~AuthenticateStepsWidget() override; + + BusyOverlayContainer* getProcessingPage() const + { + return mProcessingPage; + } + + + StepAuthenticationEac1Widget* getEac1Page() const; + + SelfInfoWidget* getSelfInfoPage() const; + + protected: + void paintEvent(QPaintEvent*) override; + void changeEvent(QEvent* pEvent) override; + + private: + QScopedPointer mUi; + BusyOverlayContainer* mProcessingPage; +}; + +} // namespace governikus diff --git a/src/widget/step/AuthenticateStepsWidget.ui b/src/ui/widget/step/AuthenticateStepsWidget.ui similarity index 100% rename from src/widget/step/AuthenticateStepsWidget.ui rename to src/ui/widget/step/AuthenticateStepsWidget.ui diff --git a/src/ui/widget/step/SelfInfoWidget.cpp b/src/ui/widget/step/SelfInfoWidget.cpp new file mode 100644 index 0000000..05bf624 --- /dev/null +++ b/src/ui/widget/step/SelfInfoWidget.cpp @@ -0,0 +1,135 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "SelfInfoWidget.h" + +#include "ui_SelfInfoWidget.h" + +#include "generic/ListItem.h" +#include "generic/ListItemSubTitle.h" +#include "generic/ListItemTitle.h" +#include "PdfExporter.h" + +#include +#include +#include +#include +#include + +using namespace governikus; + + +Q_DECLARE_LOGGING_CATEGORY(gui) + + +SelfInfoWidget::SelfInfoWidget(QWidget* pParent) + : QWidget(pParent) + , mUi(new Ui::SelfInfoWidget()) + , mSelfAuthenticationData() +{ + mUi->setupUi(this); + layout()->setMargin(20); +} + + +SelfInfoWidget::~SelfInfoWidget() +{ +} + + +void SelfInfoWidget::setInfo(const SelfAuthenticationData& pData) +{ + mSelfAuthenticationData = pData; + fillLayout(); +} + + +void SelfInfoWidget::fillLayout() +{ + //delete old data from ui + QLayout* uiLayout = mUi->dataLayout; + cleanLayout(uiLayout); + cleanLayout(mUi->saveLayout); + + const auto& orderedSelfData = mSelfAuthenticationData.getOrderedSelfData(); + for (const auto& entry : orderedSelfData) + { + QLabel* const tmpLabel = new QLabel(entry.first); + if (!entry.first.isEmpty()) + { + tmpLabel->setFocusPolicy(Qt::TabFocus); + } + tmpLabel->setAlignment(Qt::AlignLeading | Qt::AlignLeft | Qt::AlignTop); + + QLabel* const tmpField = new QLabel(entry.second); + tmpField->setFocusPolicy(Qt::TabFocus); + tmpField->setWordWrap(true); + + mUi->dataLayout->insertRow(-1, tmpLabel, tmpField); + } + + QPushButton* exportButton = new QPushButton(tr("Save as PDF...")); + exportButton->setAccessibleName(tr("save id card data as pdf")); + + connect(exportButton, &QPushButton::clicked, this, &SelfInfoWidget::onPrintButtonClicked); + + mUi->saveLayout->addWidget(exportButton); + mUi->saveLayout->addItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum)); +} + + +void SelfInfoWidget::cleanLayout(QLayout* pLayout) +{ + while (QLayoutItem* child = pLayout->itemAt(0)) + { + if (QWidget* childWidget = child->widget()) + { + pLayout->removeWidget(childWidget); + delete childWidget; + } + else + { + pLayout->removeItem(child); + delete child; + } + } +} + + +void SelfInfoWidget::paintEvent(QPaintEvent*) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} + + +void SelfInfoWidget::onPrintButtonClicked() +{ + const auto& selfData = mSelfAuthenticationData.getOrderedSelfData(); + if (!selfData.isEmpty()) + { + const auto& dataTime = mSelfAuthenticationData.getDateTime(); + QString filename = tr("AusweisApp2.Information.%1.pdf").arg(dataTime.toString(QStringLiteral("yyyy-MM-dd"))); + filename = QFileDialog::getSaveFileName(this, + QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("Save"), + QDir::homePath() + QLatin1Char('/') + filename, + tr("PDF Documents") + QStringLiteral(" (*.pdf)")); + + PdfExporter exporter(filename); + exporter.exportSelfInfo(dataTime, selfData); + } +} + + +void SelfInfoWidget::changeEvent(QEvent* pEvent) +{ + if (pEvent->type() == QEvent::LanguageChange) + { + mUi->retranslateUi(this); + fillLayout(); + } + QWidget::changeEvent(pEvent); +} diff --git a/src/ui/widget/step/SelfInfoWidget.h b/src/ui/widget/step/SelfInfoWidget.h new file mode 100644 index 0000000..a013128 --- /dev/null +++ b/src/ui/widget/step/SelfInfoWidget.h @@ -0,0 +1,49 @@ +/*! + * \brief A widget displaying the card data retrieved in the self info workflow. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "SelfAuthenticationData.h" + +#include +#include +#include + +namespace Ui +{ +class SelfInfoWidget; +} // namespace Ui + +namespace governikus +{ +class SelfInfoWidget + : public QWidget +{ + Q_OBJECT + + public: + SelfInfoWidget(QWidget* pParent = nullptr); + virtual ~SelfInfoWidget() override; + + void setInfo(const SelfAuthenticationData& pData); + + protected: + void paintEvent(QPaintEvent*) override; + void changeEvent(QEvent* pEvent) override; + + private: + QScopedPointer mUi; + SelfAuthenticationData mSelfAuthenticationData; + + void add(const QString& pKey, const QString& pValue); + void fillLayout(); + void cleanLayout(QLayout* pLayout); + + private Q_SLOTS: + void onPrintButtonClicked(); +}; + +} // namespace governikus diff --git a/src/widget/step/SelfInfoWidget.ui b/src/ui/widget/step/SelfInfoWidget.ui similarity index 100% rename from src/widget/step/SelfInfoWidget.ui rename to src/ui/widget/step/SelfInfoWidget.ui diff --git a/src/ui/widget/step/StepAdviseUserToRemoveCardGui.cpp b/src/ui/widget/step/StepAdviseUserToRemoveCardGui.cpp new file mode 100644 index 0000000..443ba06 --- /dev/null +++ b/src/ui/widget/step/StepAdviseUserToRemoveCardGui.cpp @@ -0,0 +1,113 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "StepAdviseUserToRemoveCardGui.h" + +#include "ReaderManager.h" + +#include +#include +#include + +using namespace governikus; + + +void StepAdviseUserToRemoveCardGui::onReaderManagerSignal() +{ + const auto readerManager = Env::getSingleton(); + if (!readerManager->getReaderInfo(mContext->getReaderName()).hasCard()) + { + qDebug() << "No more ID cards found -> auto closing card reminder dialog"; + + disconnect(readerManager, &ReaderManager::fireReaderAdded, this, &StepAdviseUserToRemoveCardGui::onReaderManagerSignal); + disconnect(readerManager, &ReaderManager::fireReaderRemoved, this, &StepAdviseUserToRemoveCardGui::onReaderManagerSignal); + disconnect(readerManager, &ReaderManager::fireCardInserted, this, &StepAdviseUserToRemoveCardGui::onReaderManagerSignal); + disconnect(readerManager, &ReaderManager::fireCardRemoved, this, &StepAdviseUserToRemoveCardGui::onReaderManagerSignal); + disconnect(&mMessageTimeoutTimer, &QTimer::timeout, this, &StepAdviseUserToRemoveCardGui::onReaderManagerSignal); + + mMessageBox->reject(); + } +} + + +StepAdviseUserToRemoveCardGui::StepAdviseUserToRemoveCardGui(QSharedPointer pContext, QWidget* pMainWidget) + : StepGui(pContext) + , mContext(pContext) + , mMainWidget(pMainWidget) + , mMessageBox(nullptr) + , mMessageTimeoutTimer() +{ +} + + +StepAdviseUserToRemoveCardGui::~StepAdviseUserToRemoveCardGui() +{ + if (mMessageBox && mMessageBox->isActiveWindow()) + { + mMessageBox->reject(); + } +} + + +void StepAdviseUserToRemoveCardGui::activate() +{ + setCancelButtonState(ButtonState::HIDDEN); + + const QString selectedReaderName = mContext->getReaderName(); + if (selectedReaderName.isEmpty()) + { + return; + } + + const auto readerManager = Env::getSingleton(); + ReaderInfo selectedReader = readerManager->getReaderInfo(mContext->getReaderName()); + if (selectedReader.isConnected()) + { + if (!selectedReader.hasCard()) + { + return; + } + + connect(readerManager, &ReaderManager::fireReaderAdded, this, &StepAdviseUserToRemoveCardGui::onReaderManagerSignal); + connect(readerManager, &ReaderManager::fireReaderRemoved, this, &StepAdviseUserToRemoveCardGui::onReaderManagerSignal); + connect(readerManager, &ReaderManager::fireCardInserted, this, &StepAdviseUserToRemoveCardGui::onReaderManagerSignal); + connect(readerManager, &ReaderManager::fireCardRemoved, this, &StepAdviseUserToRemoveCardGui::onReaderManagerSignal); + } + else + { + mMessageTimeoutTimer.setInterval(3000); + mMessageTimeoutTimer.setSingleShot(true); + connect(&mMessageTimeoutTimer, &QTimer::timeout, this, &StepAdviseUserToRemoveCardGui::onReaderManagerSignal); + mMessageTimeoutTimer.start(); + } + + // If workflow has been killed, we do not open the dialog. + if (mContext->isWorkflowKilled()) + { + return; + } + + if (mMessageBox.isNull()) + { + mMessageBox = QPointer(new QMessageBox(mMainWidget)); + mMessageBox->setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("Information")); + mMessageBox->setWindowModality(Qt::ApplicationModal); + mMessageBox->setWindowFlags(mMessageBox->windowFlags() & ~Qt::WindowContextHelpButtonHint); + mMessageBox->setText(tr("You may now remove your ID card from the card reader.")); + mMessageBox->setIconPixmap(QIcon(QStringLiteral(":/images/npa.svg")).pixmap(32, 32)); + mMessageBox->setStandardButtons(QMessageBox::Ok); + mMessageBox->button(QMessageBox::Ok)->setFocus(); + } + + mMessageBox->exec(); +} + + +void StepAdviseUserToRemoveCardGui::closeActiveDialogs() +{ + if (mMessageBox) + { + mMessageBox->reject(); + } +} diff --git a/src/ui/widget/step/StepAdviseUserToRemoveCardGui.h b/src/ui/widget/step/StepAdviseUserToRemoveCardGui.h new file mode 100644 index 0000000..f8674cb --- /dev/null +++ b/src/ui/widget/step/StepAdviseUserToRemoveCardGui.h @@ -0,0 +1,41 @@ +/*! + * \brief Qt UI for the advise user to remove card step. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/AuthContext.h" +#include "StepGui.h" + +#include +#include +#include + +namespace governikus +{ + +class StepAdviseUserToRemoveCardGui + : public StepGui +{ + Q_OBJECT + + public Q_SLOTS: + void onReaderManagerSignal(); + + public: + StepAdviseUserToRemoveCardGui(QSharedPointer pContext, QWidget* const pMainWidget); + virtual ~StepAdviseUserToRemoveCardGui() override; + + virtual void activate() override; + void closeActiveDialogs(); + + private: + QSharedPointer mContext; + QWidget* const mMainWidget; + QPointer mMessageBox; + QTimer mMessageTimeoutTimer; +}; + +} // namespace governikus diff --git a/src/widget/step/StepAuthenticationDoneGui.cpp b/src/ui/widget/step/StepAuthenticationDoneGui.cpp similarity index 100% rename from src/widget/step/StepAuthenticationDoneGui.cpp rename to src/ui/widget/step/StepAuthenticationDoneGui.cpp diff --git a/src/ui/widget/step/StepAuthenticationDoneGui.h b/src/ui/widget/step/StepAuthenticationDoneGui.h new file mode 100644 index 0000000..49ae603 --- /dev/null +++ b/src/ui/widget/step/StepAuthenticationDoneGui.h @@ -0,0 +1,28 @@ +/*! + * \brief Qt UI for the authentication done step. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/AuthContext.h" +#include "StepGui.h" + + +namespace governikus +{ + +class StepAuthenticationDoneGui + : public StepGui +{ + Q_OBJECT + + public: + StepAuthenticationDoneGui(QSharedPointer pContext); + virtual ~StepAuthenticationDoneGui() override; + + virtual void forwardStep() override; +}; + +} // namespace governikus diff --git a/src/ui/widget/step/StepAuthenticationEac1Gui.cpp b/src/ui/widget/step/StepAuthenticationEac1Gui.cpp new file mode 100644 index 0000000..c91e778 --- /dev/null +++ b/src/ui/widget/step/StepAuthenticationEac1Gui.cpp @@ -0,0 +1,147 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "StepAuthenticationEac1Gui.h" + +#include "generic/GuiUtils.h" +#include "step/AuthenticateStepsWidget.h" + +#include + +using namespace governikus; + +StepAuthenticationEac1Gui::StepAuthenticationEac1Gui(QSharedPointer pContext, + AuthenticateStepsWidget* pStepsWidget) + : StepGui(pContext) + , mContext(pContext) + , mStepsWidget(pStepsWidget) + , mWidget(nullptr) + , mState(StepAuthenticationEac1Widget::State::EDIT_CHAT) + , mPayAttentionToReaderMsgBox(new QMessageBox(pStepsWidget->window())) + , mActive(false) +{ + mPayAttentionToReaderMsgBox->setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("Information")); + mPayAttentionToReaderMsgBox->setText(tr("Please observe the display of your card reader.")); + mPayAttentionToReaderMsgBox->setIcon(QMessageBox::Information); + mPayAttentionToReaderMsgBox->setStandardButtons(QMessageBox::NoButton); +} + + +StepAuthenticationEac1Gui::~StepAuthenticationEac1Gui() +{ +} + + +void StepAuthenticationEac1Gui::activate() +{ + mActive = true; + + mWidget = mStepsWidget->getEac1Page(); + + connect(mWidget, &StepAuthenticationEac1Widget::setForwardButtonState, getStepGuiDelegate(), &StepGuiDelegate::setForwardButtonState); + connect(mWidget, &StepAuthenticationEac1Widget::setCancelButtonState, getStepGuiDelegate(), &StepGuiDelegate::setCancelButtonState); + + connect(mWidget, &StepAuthenticationEac1Widget::fireCanUpdated, this, &StepAuthenticationEac1Gui::onCanUpdated); + connect(mWidget, &StepAuthenticationEac1Widget::firePinUpdated, this, &StepAuthenticationEac1Gui::onPinUpdated); + + connect(this, &StepAuthenticationEac1Gui::fireUiFinished, this, &StepAuthenticationEac1Gui::onUiFinished); + + mWidget->setContext(mContext); + mWidget->setState(StepAuthenticationEac1Widget::State::INITIAL); + mStepsWidget->setCurrentWidget(mWidget); +} + + +void StepAuthenticationEac1Gui::deactivate() +{ + mWidget->setContext(QSharedPointer()); + + disconnect(mWidget, &StepAuthenticationEac1Widget::setForwardButtonState, getStepGuiDelegate(), &StepGuiDelegate::setForwardButtonState); + disconnect(mWidget, &StepAuthenticationEac1Widget::setCancelButtonState, getStepGuiDelegate(), &StepGuiDelegate::setCancelButtonState); + + disconnect(mWidget, &StepAuthenticationEac1Widget::fireCanUpdated, this, &StepAuthenticationEac1Gui::onCanUpdated); + disconnect(mWidget, &StepAuthenticationEac1Widget::firePinUpdated, this, &StepAuthenticationEac1Gui::onPinUpdated); + + disconnect(this, &StepAuthenticationEac1Gui::fireUiFinished, this, &StepAuthenticationEac1Gui::onUiFinished); + + mActive = false; +} + + +bool StepAuthenticationEac1Gui::isActive() const +{ + return mActive; +} + + +void StepAuthenticationEac1Gui::setState(StepAuthenticationEac1Widget::State pState) +{ + mState = pState; + + mWidget->setState(pState); + + if (pState == StepAuthenticationEac1Widget::State::FINISHED) + { + forwardStep(); + } +} + + +void StepAuthenticationEac1Gui::incorrectPinError() +{ + GuiUtils::showPinCanPukErrorDialog(mContext->getLastPaceResult(), mContext->isCanAllowedMode(), mStepsWidget->window()); +} + + +void StepAuthenticationEac1Gui::forwardStep() +{ + if (mState == StepAuthenticationEac1Widget::State::FINISHED) + { + Q_EMIT fireUiFinished(); + return; + } + + mWidget->forwardStep(); + + Q_EMIT fireUiFinished(); +} + + +void StepAuthenticationEac1Gui::hidePayAttentionToReader() +{ + mPayAttentionToReaderMsgBox->reject(); +} + + +void StepAuthenticationEac1Gui::onShowPayAttentionToReader() +{ + mPayAttentionToReaderMsgBox->open(); +} + + +void StepAuthenticationEac1Gui::onPinUpdated(const QString& pPin) +{ + if (mContext->isCanAllowedMode()) + { + mCan = pPin; + } + else + { + mPin = pPin; + } +} + + +void StepAuthenticationEac1Gui::onCanUpdated(const QString& pCan) +{ + mCan = pCan; +} + + +void StepAuthenticationEac1Gui::onUiFinished() +{ + mContext->setCan(mCan); + mContext->setPin(mPin); + mContext->setStateApproved(); +} diff --git a/src/ui/widget/step/StepAuthenticationEac1Gui.h b/src/ui/widget/step/StepAuthenticationEac1Gui.h new file mode 100644 index 0000000..1dbc64a --- /dev/null +++ b/src/ui/widget/step/StepAuthenticationEac1Gui.h @@ -0,0 +1,56 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/AuthContext.h" +#include "step/StepAuthenticationEac1Widget.h" +#include "StepGui.h" + +#include +#include + +namespace governikus +{ + +class AuthenticateStepsWidget; + +class StepAuthenticationEac1Gui + : public StepGui +{ + Q_OBJECT + + private: + QSharedPointer mContext; + AuthenticateStepsWidget* mStepsWidget; + StepAuthenticationEac1Widget* mWidget; + StepAuthenticationEac1Widget::State mState; + QPointer mPayAttentionToReaderMsgBox; + QString mPin; + QString mCan; + bool mActive; + + public: + StepAuthenticationEac1Gui(QSharedPointer pContext, AuthenticateStepsWidget* pStepsWidget); + virtual ~StepAuthenticationEac1Gui() override; + + virtual void activate() override; + virtual void deactivate() override; + bool isActive() const; + + virtual void setState(StepAuthenticationEac1Widget::State pState); + virtual void incorrectPinError(); + + virtual void forwardStep() override; + + virtual void hidePayAttentionToReader(); + + public Q_SLOTS: + virtual void onShowPayAttentionToReader(); + void onPinUpdated(const QString& pPin); + void onCanUpdated(const QString& pCan); + void onUiFinished(); +}; + +} // namespace governikus diff --git a/src/ui/widget/step/StepAuthenticationEac1Widget.cpp b/src/ui/widget/step/StepAuthenticationEac1Widget.cpp new file mode 100644 index 0000000..08f0515 --- /dev/null +++ b/src/ui/widget/step/StepAuthenticationEac1Widget.cpp @@ -0,0 +1,654 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "StepAuthenticationEac1Widget.h" +#include "ui_StepAuthenticationEac1Widget.h" + +#include "AppSettings.h" +#include "CardConnection.h" +#include "DetailDialog.h" +#include "generic/PasswordEdit.h" +#include "RandomPinDialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef Q_OS_WIN +#include +#endif + +using namespace governikus; + +Q_DECLARE_LOGGING_CATEGORY(gui) + +StepAuthenticationEac1Widget::StepAuthenticationEac1Widget(QWidget* pParent) + : QWidget(pParent) + , mUi(new Ui::StepAuthenticationEac1Widget()) + , mContext() + , mCANField(nullptr) + , mPINField(nullptr) + , mState(State::INITIAL) + , mProgressBar(nullptr) + , mProgressBarLabel(nullptr) + , mCloseWindowWhenFinished() +#ifdef Q_OS_WIN + , mTaskbarButton(new QWinTaskbarButton(this)) +#endif +{ + mUi->setupUi(this); + setToolTip(); + mUi->listWidgetWest->setAttribute(Qt::WA_MacShowFocusRect, false); + mUi->listWidgetEast->setAttribute(Qt::WA_MacShowFocusRect, false); + + connect(mUi->detailsPushButton, &QPushButton::clicked, this, &StepAuthenticationEac1Widget::onDetailsButtonClicked); +} + + +StepAuthenticationEac1Widget::~StepAuthenticationEac1Widget() +{ +} + + +void StepAuthenticationEac1Widget::setContext(const QSharedPointer& pContext) +{ + mContext = pContext; + +#ifdef Q_OS_WIN + if (mContext) + { + connect(mContext.data(), &AuthContext::fireResultChanged, this, &StepAuthenticationEac1Widget::onResultChanged); + } +#endif +} + + +void StepAuthenticationEac1Widget::setState(State pState) +{ + if (pState != mState || pState == StepAuthenticationEac1Widget::State::ENTER_PIN) + { + mState = pState; + updateWidget(); + } +} + + +void StepAuthenticationEac1Widget::forwardStep() +{ + Q_EMIT setForwardButtonState(ButtonState::DISABLED, tr("Identify now")); + + if (mState == State::EDIT_CHAT) + { + mUi->detailsPushButton->setEnabled(false); + mUi->listWidgetWest->setEnabled(false); + mUi->listWidgetEast->setEnabled(false); + } + else if (mState == State::ENTER_PIN) + { + if (mContext->getCardConnection() != nullptr && mContext->getCardConnection()->getReaderInfo().isBasicReader()) + { + mUi->pinGroupBox->setVisible(false); + + int childCount = mUi->pinWidgetLayout->count(); + for (int i = 0; i < childCount; ++i) + { + QLayoutItem* child = mUi->pinWidgetLayout->itemAt(i); + if (child->widget() != nullptr) + { + child->widget()->setEnabled(false); + } + } + } + else + { + Q_EMIT setCancelButtonState(ButtonState::DISABLED); + } + } +} + + +void StepAuthenticationEac1Widget::updateButtonsAndPinWidget() +{ + Q_EMIT setCancelButtonState(ButtonState::ENABLED); + + const QSharedPointer cardConnection = mContext->getCardConnection(); + if (cardConnection) + { + if (mContext->isCanAllowedMode()) + { + mUi->pinGroupBox->setTitle(tr("Please enter the six-digit card access number (CAN) for identification.")); + } + else if (cardConnection->getReaderInfo().getRetryCounter() == 1) + { + mUi->pinGroupBox->setTitle(tr("Please enter your six-digit card access number (CAN) and your PIN for identification.")); + } + else + { + mUi->pinGroupBox->setTitle(tr("Please enter your six digit PIN for identification")); + } + + if (cardConnection->getReaderInfo().isBasicReader()) + { + clearPinWidgetLayout(); + createBasicReaderWidget(); + focusWidget(); + } + else + { + clearPinWidgetLayout(); + QLabel* label = new QLabel(tr("Please pay attention to the display of your card reader.")); + label->setFocusPolicy(Qt::TabFocus); + label->setObjectName(QStringLiteral("eac1PinInformationLabel")); + mUi->pinWidgetLayout->invalidate(); + mUi->pinWidgetLayout->addWidget(label); + Q_EMIT setCancelButtonState(ButtonState::DISABLED); + } + mUi->pinGroupBox->setVisible(true); + } + else + { + mUi->pinGroupBox->setVisible(false); + } + + Q_EMIT setForwardButtonState(ButtonState::DISABLED, tr("Identify now")); +} + + +void StepAuthenticationEac1Widget::clearPinWidgetLayout() +{ + while (QLayoutItem* child = mUi->pinWidgetLayout->itemAt(0)) + { + if (QWidget* childWidget = child->widget()) + { + mUi->pinWidgetLayout->removeWidget(childWidget); + delete childWidget; + } + else + { + mUi->pinWidgetLayout->removeItem(child); + delete child; + } + } + + mProgressBar = nullptr; + mProgressBarLabel = nullptr; +} + + +void StepAuthenticationEac1Widget::onDetailsButtonClicked() +{ + DetailDialog d(this); + + auto eac1 = mContext->getDidAuthenticateEac1(); + CVCertificateBody body = eac1->getCvCertificates().at(0)->getBody(); + QString effectiveDate = body.getCertificateEffectiveDate().toString(Qt::DefaultLocaleShortDate); + QString expirationDate = body.getCertificateExpirationDate().toString(Qt::DefaultLocaleShortDate); + auto certificateDescription = eac1->getCertificateDescription(); + + QString details; + details += tr("Service provider:") + QLatin1Char('\n'); + details += certificateDescription->getSubjectName(); + details += QLatin1Char('\n'); + details += certificateDescription->getSubjectUrl(); + + details += QLatin1String("\n\n"); + details += tr("Certificate issuer:") + QLatin1Char('\n'); + details += certificateDescription->getIssuerName(); + details += QLatin1Char('\n'); + details += certificateDescription->getIssuerUrl(); + + details += QLatin1String("\n\n"); + details += certificateDescription->getTermsOfUsage(); + details += QLatin1String("\n\n"); + details += tr("Validity:\n%1 - %2").arg(effectiveDate, expirationDate); + + // collapse multiple blank lines + details.replace(QRegularExpression(QStringLiteral("\n\n\n*")), QStringLiteral("\n\n")); + + d.setDetails(details); + d.exec(); +} + + +void StepAuthenticationEac1Widget::setToolTip() +{ + const auto& align = QStringLiteral("

%1

"); + + const auto& certDesc = tr("Information on the service provider who wants to read out data from your ID card is given here. For further information press the button \"more...\"."); + mUi->certificateDescriptionGroupBox->setToolTip(align.arg(certDesc)); + + const auto& fieldDesc = tr("Here you can select or deselect data fields to be read out. Mandatory data fields required by the service provider cannot be deselected."); + mUi->groupBox->setToolTip(align.arg(fieldDesc)); +} + + +void StepAuthenticationEac1Widget::updateWidget() +{ + if (!mContext) + { + return; + } + + switch (mState) + { + case State::INITIAL: + break; + + case State::EDIT_CHAT: + setupChatView(); + return; + + case State::ENTER_PIN: + updateButtonsAndPinWidget(); + return; + + case State::AUTHENTICATING_ESERVICE: + Q_EMIT setCancelButtonState(ButtonState::ENABLED); + updateProgressPanel(1, tr("Service provider is verified")); + break; + + case State::AUTHENTICATING_CARD: + updateProgressPanel(2, tr("Card is being verified")); + break; + + case State::READING_CARD_DATA: + updateProgressPanel(3, tr("Reading data")); + break; + + case State::REDIRECTING_BROWSER: + updateProgressPanel(4, tr("Service provider is being verified")); + break; + + case State::FINISHED: + updateProgressPanel(); + Q_EMIT setCancelButtonState(ButtonState::HIDDEN); + if (mContext->isWorkflowCancelled()) + { + Q_EMIT setForwardButtonState(ButtonState::HIDDEN); + } + else + { + Q_EMIT setForwardButtonState(ButtonState::DISABLED, tr("Close")); + } + break; + } +} + + +void StepAuthenticationEac1Widget::setupChatView() +{ + auto eac1 = mContext->getDidAuthenticateEac1(); + mUi->subjectName->setText(eac1->getCertificateDescription()->getSubjectName()); + QString purpose = eac1->getCertificateDescription()->getPurpose(); + if (purpose.isEmpty()) + { + purpose = tr("See details under \"more...\""); + } + mUi->usage->setText(purpose); + + if (eac1->getTransactionInfo().isNull() || eac1->getTransactionInfo().isEmpty()) + { + mUi->transactionInfoGroupBox->setVisible(false); + } + else + { + mUi->transactionInfo->setText(eac1->getTransactionInfo()); + mUi->transactionInfoGroupBox->setVisible(true); + } + + mUi->listWidgetWest->clear(); + mUi->listWidgetEast->clear(); + + prepareChatsForGui(); + + mUi->detailsPushButton->setEnabled(true); + mUi->listWidgetWest->setEnabled(true); + mUi->listWidgetEast->setEnabled(true); + mUi->pinGroupBox->setVisible(false); + + Q_EMIT setCancelButtonState(ButtonState::ENABLED); + Q_EMIT setForwardButtonState(ButtonState::FOCUSSED, tr("Identify now")); +} + + +void StepAuthenticationEac1Widget::prepareChatsForGui() +{ + const double optionalRightsCount = mContext->getOptionalAccessRights().size(); + const double requiredRightsCount = mContext->getRequiredAccessRights().size(); + int listSize = qCeil((optionalRightsCount + requiredRightsCount) / 2.0); + + for (AccessRight orderedRight : AccessRoleAndRightsUtil::allDisplayedOrderedRights()) + { + if (mContext->getOptionalAccessRights().contains(orderedRight)) + { + addChatRightToGui(orderedRight, true, listSize); + } + else if (mContext->getRequiredAccessRights().contains(orderedRight)) + { + addChatRightToGui(orderedRight, false, listSize); + } + } +} + + +void StepAuthenticationEac1Widget::addChatRightToGui(AccessRight pRight, bool pOptional, int pListSize) +{ + QString displayText = AccessRoleAndRightsUtil::toDisplayText(pRight); + if (pRight == AccessRight::AGE_VERIFICATION) + { + displayText += QStringLiteral(" (%1)").arg(mContext->getDidAuthenticateEac1()->getAuthenticatedAuxiliaryData()->getRequiredAge()); + } + QCheckBox* cb = new QCheckBox(displayText); + cb->setEnabled(pOptional); + cb->setChecked(mContext->getEffectiveAccessRights().contains(pRight)); + + mMap.insert(cb, pRight); + + connect(cb, &QCheckBox::stateChanged, this, &StepAuthenticationEac1Widget::checkBoxChanged); + + QListWidgetItem* item = new QListWidgetItem(); + item->setSizeHint(QSize(0, 20)); + item->setData(Qt::AccessibleTextRole, displayText); + if (mUi->listWidgetWest->count() < pListSize) + { + mUi->listWidgetWest->addItem(item); + mUi->listWidgetWest->setItemWidget(item, cb); + } + else + { + mUi->listWidgetEast->addItem(item); + mUi->listWidgetEast->setItemWidget(item, cb); + } +} + + +void StepAuthenticationEac1Widget::createBasicReaderWidget() +{ + QWidget* basicReaderWidget = new QWidget(); + + QHBoxLayout* basicReaderWidgetLayout = new QHBoxLayout(basicReaderWidget); + + const auto& allowedDigitsMsg = tr("Only digits (0-9) are permitted."); + QRegularExpression onlyNumbersExpression(QStringLiteral("[0-9]*")); + if (mContext->getCardConnection()->getReaderInfo().getRetryCounter() == 1 && !mContext->isCanAllowedMode()) + { + mCANField = new PasswordEdit(); + mCANField->setAccessibleName(tr("please enter your can")); + mCANField->setAccessibleDescription(allowedDigitsMsg); + mCANField->setMaxLength(6); + mCANField->configureValidation(onlyNumbersExpression, allowedDigitsMsg); + connect(mCANField, &PasswordEdit::textEdited, this, &StepAuthenticationEac1Widget::canTextEdited); + + QLabel* canLabel = new QLabel(tr("Card access number (CAN):")); + canLabel->setFocusPolicy(Qt::TabFocus); + basicReaderWidgetLayout->addWidget(canLabel); + basicReaderWidgetLayout->addWidget(mCANField); + + if (Env::getSingleton()->getGeneralSettings().isUseScreenKeyboard()) + { + QToolButton* button = new QToolButton(); + button->setObjectName(QStringLiteral("canRandomButton")); + button->setAccessibleName(tr("open on screen keyboard")); + button->setAutoRaise(true); + button->setIcon(QPixmap(QStringLiteral(":/images/randompin/screen_keyboard.png"))); + button->setIconSize(QSize(44, 26)); + + connect(button, &QAbstractButton::clicked, this, &StepAuthenticationEac1Widget::onRandomButtonClicked); + + basicReaderWidgetLayout->addWidget(button); + } + } + + mPINField = new PasswordEdit(); + mPINField->setAccessibleName(tr("please enter your pin")); + mPINField->setAccessibleDescription(allowedDigitsMsg); + mPINField->setMaxLength(6); + mPINField->configureValidation(onlyNumbersExpression, allowedDigitsMsg); + connect(mPINField, &PasswordEdit::textEdited, this, &StepAuthenticationEac1Widget::pinTextEdited); + + if (mContext->getCardConnection()->getReaderInfo().getRetryCounter() == 1 && !mContext->isCanAllowedMode()) + { + mPINField->setEnabled(false); + } + + const QString labelLabel = mContext->isCanAllowedMode() == false ? tr("PIN:") : tr("CAN:"); + QLabel* pinLabel = new QLabel(labelLabel); + pinLabel->setFocusPolicy(Qt::TabFocus); + basicReaderWidgetLayout->addWidget(pinLabel); + basicReaderWidgetLayout->addWidget(mPINField); + + if (Env::getSingleton()->getGeneralSettings().isUseScreenKeyboard()) + { + QToolButton* button = new QToolButton(); + button->setObjectName(QStringLiteral("pinRandomButton")); + button->setAccessibleName(tr("open on screen keyboard")); + button->setAutoRaise(true); + button->setIcon(QPixmap(QStringLiteral(":/images/randompin/screen_keyboard.png"))); + button->setIconSize(QSize(44, 26)); + + connect(button, &QAbstractButton::clicked, this, &StepAuthenticationEac1Widget::onRandomButtonClicked); + + basicReaderWidgetLayout->addWidget(button); + } + + mUi->pinWidgetLayout->invalidate(); + mUi->pinWidgetLayout->addWidget(basicReaderWidget); +} + + +void StepAuthenticationEac1Widget::updateProgressPanel(int pProgressValue, const QString& pProgressText) +{ + if (pProgressValue > 0) + { + if (mProgressBar == nullptr) + { + clearPinWidgetLayout(); + QWidget* progressWidget = new QWidget(); + QVBoxLayout* progressWidgetLayout = new QVBoxLayout(progressWidget); + progressWidgetLayout->setMargin(0); + mUi->pinWidgetLayout->addWidget(progressWidget); + + mProgressBar = new QProgressBar(); + mProgressBar->setTextVisible(false); + mProgressBar->setRange(0, 4); + progressWidgetLayout->addWidget(mProgressBar); + + mProgressBarLabel = new QLabel(); + progressWidgetLayout->addWidget(mProgressBarLabel); + + mUi->pinGroupBox->setTitle(tr("Identify")); + mUi->pinGroupBox->setVisible(true); + } + + mProgressBar->setValue(pProgressValue); + mProgressBarLabel->setText(pProgressText); + } + else + { + const bool cancelled = mContext->getStatus().isCancellationByUser(); + clearPinWidgetLayout(); + QWidget* doneWidget = new QWidget(); + QHBoxLayout* doneWidgetLayout = new QHBoxLayout(doneWidget); + doneWidgetLayout->setMargin(0); + mUi->pinWidgetLayout->addWidget(doneWidget); + + doneWidgetLayout->addStretch(); + QLabel* doneIcon = new QLabel; + QLabel* doneText = new QLabel(cancelled ? tr("The process was cancelled by the user") : tr("Identification successful")); + doneIcon->setPixmap(QPixmap(cancelled ? QStringLiteral(":/images/icon_cancelled.png") : QStringLiteral(":/images/icon_ok.png"))); + doneWidgetLayout->addWidget(doneIcon); + doneWidgetLayout->addWidget(doneText); + doneWidgetLayout->addStretch(); + + mUi->pinGroupBox->setTitle(tr("Result")); + } + +#ifdef Q_OS_WIN + if (mTaskbarButton) + { + auto progress = mTaskbarButton->progress(); + progress->setValue(pProgressValue == 0 ? progress->maximum() : pProgressValue); + } +#endif +} + + +void StepAuthenticationEac1Widget::checkBoxChanged(int pCheckState) +{ + QCheckBox* cb = qobject_cast(sender()); + if (cb != nullptr) + { + if (pCheckState == Qt::Unchecked) + { + QMap::ConstIterator i = qAsConst(mMap).find(cb); + bool success = mContext->removeEffectiveAccessRight(i.value()); + qCDebug(gui) << "Removed from effective chat:" << i.value() << "| success:" << success; + } + else + { + QMap::ConstIterator i = qAsConst(mMap).find(cb); + bool success = mContext->addEffectiveAccessRight(i.value()); + qCDebug(gui) << "Added to effective chat:" << i.value() << "| success:" << success; + } + } +} + + +void StepAuthenticationEac1Widget::onRandomButtonClicked() +{ + RandomPinDialog randomPinDialog(6, mContext->getReaderName(), this); + if (randomPinDialog.exec() == QDialog::Accepted && !randomPinDialog.getPin().isEmpty()) + { + QToolButton* pinButton = qobject_cast(sender()); + if (pinButton == nullptr) + { + qCCritical(gui) << "sender == nullptr"; + } + else if (pinButton->objectName() == QLatin1String("canRandomButton")) + { + canTextEdited(randomPinDialog.getPin()); + } + else if (pinButton->objectName() == QLatin1String("pinRandomButton")) + { + pinTextEdited(randomPinDialog.getPin()); + } + } +} + + +void StepAuthenticationEac1Widget::onResultChanged() +{ +#ifdef Q_OS_WIN + if (mTaskbarButton && (mContext.isNull() || mContext->getStatus().isError())) + { + mTaskbarButton->progress()->stop(); + } +#endif +} + + +void StepAuthenticationEac1Widget::hideEvent(QHideEvent* pEvent) +{ +#ifdef Q_OS_WIN + if (mTaskbarButton) + { + mTaskbarButton->progress()->setVisible(false); + } +#endif + QWidget::hideEvent(pEvent); +} + + +void StepAuthenticationEac1Widget::showEvent(QShowEvent* pEvent) +{ +#ifdef Q_OS_WIN + auto window = QApplication::activeWindow(); + if (window) + { + mTaskbarButton->setWindow(window->windowHandle()); + auto progress = mTaskbarButton->progress(); + progress->setVisible(true); + progress->setRange(0, 5); // 5 == count of states in setState + progress->reset(); + progress->resume(); // reset stop() + } +#endif + QWidget::showEvent(pEvent); +} + + +void StepAuthenticationEac1Widget::canTextEdited(const QString& pText) +{ + if (!pText.isNull() && !pText.isEmpty()) + { + mCANField->setText(pText); + } + + if (mCANField->text().size() == 6) + { + mPINField->setEnabled(true); + mPINField->setFocus(); + + Q_EMIT fireCanUpdated(mCANField->text()); + } + else + { + mPINField->setEnabled(false); + } + + mPINField->clear(); + pinTextEdited(QString()); +} + + +void StepAuthenticationEac1Widget::pinTextEdited(const QString& pText) +{ + if (!pText.isNull() && !pText.isEmpty()) + { + mPINField->setText(pText); + } + + if (mPINField->text().size() == 6) + { + Q_EMIT setForwardButtonState(ButtonState::FOCUSSED, tr("Identify now")); + Q_EMIT firePinUpdated(mPINField->text()); + } + else + { + Q_EMIT setForwardButtonState(ButtonState::DISABLED, tr("Identify now")); + } +} + + +void StepAuthenticationEac1Widget::focusWidget() +{ + if (mContext->getCardConnection()->getReaderInfo().getRetryCounter() == 1 && !mContext->isCanAllowedMode()) + { + mCANField->setFocus(); + mCANField->setCursorPosition(0); + } + else + { + mPINField->setFocus(); + mPINField->setCursorPosition(0); + } +} + + +void StepAuthenticationEac1Widget::changeEvent(QEvent* pEvent) +{ + if (pEvent->type() == QEvent::LanguageChange) + { + updateWidget(); + mUi->retranslateUi(this); + setToolTip(); + } + QWidget::changeEvent(pEvent); +} diff --git a/src/ui/widget/step/StepAuthenticationEac1Widget.h b/src/ui/widget/step/StepAuthenticationEac1Widget.h new file mode 100644 index 0000000..42ab215 --- /dev/null +++ b/src/ui/widget/step/StepAuthenticationEac1Widget.h @@ -0,0 +1,114 @@ +/*! + * \brief Widget for the desktop StepAuthenticationEac1Gui. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/AuthContext.h" +#include "generic/ButtonState.h" + +#include +#include +#include + +#ifdef Q_OS_WIN +#include +#endif + + +namespace Ui +{ +class StepAuthenticationEac1Widget; +} // namespace Ui + + +namespace governikus +{ + +class PasswordEdit; + +class StepAuthenticationEac1Widget + : public QWidget +{ + Q_OBJECT + + public: + enum class State + { + INITIAL, + EDIT_CHAT, + ENTER_PIN, + AUTHENTICATING_ESERVICE, + AUTHENTICATING_CARD, + READING_CARD_DATA, + REDIRECTING_BROWSER, + FINISHED, + }; + Q_ENUM(State); + + StepAuthenticationEac1Widget(QWidget* pParent = nullptr); + virtual ~StepAuthenticationEac1Widget() override; + + void setContext(const QSharedPointer& pContext); + + void setState(State pState); + void forwardStep(); + + void updateButtonsAndPinWidget(); + + Q_SIGNALS: + void setForwardButtonState(ButtonState pState, const QString& pText = QString()); + void setCancelButtonState(ButtonState pState); + + void firePinUpdated(const QString& pPin); + void fireCanUpdated(const QString& pCan); + + private Q_SLOTS: + void focusWidget(); + void onDetailsButtonClicked(); + void checkBoxChanged(int pCheckState); + void canTextEdited(const QString& pText); + void pinTextEdited(const QString& pText); + void onRandomButtonClicked(); + void onResultChanged(); + + protected: + virtual void hideEvent(QHideEvent* pEvent) override; + virtual void showEvent(QShowEvent* pEvent) override; + virtual void changeEvent(QEvent* pEvent) override; + + private: + void setToolTip(); + void updateWidget(); + void setupChatView(); + void prepareChatsForGui(); + void updateProgressPanel(int pProgressValue = 0, const QString& pProgressText = QString()); + void addChatRightToGui(AccessRight pRight, bool pOptional, int pListSize); + void clearPinWidgetLayout(); + void createBasicReaderWidget(); + + private: + QScopedPointer mUi; + QSharedPointer mContext; + QMap mMap; + + PasswordEdit* mCANField; + PasswordEdit* mPINField; + + State mState; + QProgressBar* mProgressBar; + QLabel* mProgressBarLabel; + + bool mCloseWindowWhenFinished; + + #ifdef Q_OS_WIN + QWinTaskbarButton* mTaskbarButton; + #endif +}; + + +defineEnumOperators(StepAuthenticationEac1Widget::State) + +} // namespace governikus diff --git a/src/ui/widget/step/StepAuthenticationEac1Widget.ui b/src/ui/widget/step/StepAuthenticationEac1Widget.ui new file mode 100644 index 0000000..5fd1b97 --- /dev/null +++ b/src/ui/widget/step/StepAuthenticationEac1Widget.ui @@ -0,0 +1,395 @@ + + + StepAuthenticationEac1Widget + + + + 0 + 0 + 515 + 448 + + + + + 0 + 0 + + + + AusweisApp2 + + + + + + Qt::TabFocus + + + Service provider + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + 6 + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::TabFocus + + + Purpose for reading out requested data: + + + + + + + Qt::TabFocus + + + true + + + + + + + Qt::TabFocus + + + Name: + + + + + + + Qt::TabFocus + + + true + + + + + + + + + + + 0 + 0 + + + + + 75 + true + + + + details + + + more... + + + false + + + + + + + + + + + + + Qt::TabFocus + + + The following data is required by the service provider. You can deselect the non-mandatory data fields if you do not want this data to be transmitted. + + + true + + + + + + + Qt::TabFocus + + + Data + + + + 9 + + + 9 + + + 9 + + + 9 + + + + + 0 + + + + + true + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + Qt::ScrollBarAsNeeded + + + true + + + + + + + true + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + Qt::ScrollBarAsNeeded + + + 0 + + + true + + + + + + + + + + + + Qt::TabFocus + + + Important transactional information + + + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + true + + + + + 0 + 0 + 475 + 69 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::TabFocus + + + background-color: white; + + + + + + Qt::PlainText + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + true + + + + + + + + + + + + + + + + + 0 + + + 9 + + + 9 + + + 9 + + + 9 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + certificateDescriptionGroupBox + subjectNameLabel + subjectName + usageLabel + usage + detailsPushButton + label + groupBox + listWidgetWest + listWidgetEast + transactionInfoGroupBox + + + + diff --git a/src/ui/widget/step/StepChooseCardGui.cpp b/src/ui/widget/step/StepChooseCardGui.cpp new file mode 100644 index 0000000..5dc62b8 --- /dev/null +++ b/src/ui/widget/step/StepChooseCardGui.cpp @@ -0,0 +1,269 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "StepChooseCardGui.h" + +#include "HelpAction.h" +#include "ReaderConfiguration.h" +#include "RemoteClient.h" +#include "step/AuthenticateStepsWidget.h" + +#include +#include +#include +#include +#include + +using namespace governikus; + + +Q_DECLARE_LOGGING_CATEGORY(gui) + + +StepChooseCardGui::StepChooseCardGui(const QSharedPointer& pContext, QWidget* pParent) + : StepGui(pContext) + , mContext(pContext) + , mAuthWidget(nullptr) + , mInformationMessageBox(new QMessageBox(pParent)) + , mReaderDeviceGui(new ReaderDeviceGui(pParent)) + , mCancelButton(nullptr) + , mDeviceButton(nullptr) + , mSubDialogOpen(false) +{ + const auto& widget = qobject_cast(pParent); + if (widget) + { + mAuthWidget = widget->getEac1Page(); + } + + mInformationMessageBox->setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("Information")); + mInformationMessageBox->setWindowModality(Qt::WindowModal); + mInformationMessageBox->setWindowFlags(mInformationMessageBox->windowFlags() & ~Qt::WindowContextHelpButtonHint); + mCancelButton = mInformationMessageBox->addButton(tr("Cancel"), QMessageBox::NoRole); + mDeviceButton = mInformationMessageBox->addButton(tr("Settings"), QMessageBox::YesRole); + mDeviceButton->setFocus(); + + connect(mReaderDeviceGui.data(), &ReaderDeviceGui::fireFinished, this, &StepChooseCardGui::onSubDialogFinished); +} + + +StepChooseCardGui::~StepChooseCardGui() +{ +} + + +void StepChooseCardGui::activate() +{ + if (mAuthWidget) + { + mAuthWidget->setContext(mContext.objectCast()); + } + + setCancelButtonState(ButtonState::ENABLED); + + connect(Env::getSingleton(), &ReaderManager::fireReaderEvent, this, &StepChooseCardGui::onReaderManagerSignal); + onReaderManagerSignal(); +} + + +void StepChooseCardGui::deactivate() +{ + if (mAuthWidget) + { + mAuthWidget->setContext(QSharedPointer()); + } + + disconnect(Env::getSingleton(), &ReaderManager::fireReaderEvent, this, &StepChooseCardGui::onReaderManagerSignal); +} + + +QString StepChooseCardGui::getCurrentReaderImage(const QVector& pReaderInfos) +{ + if (pReaderInfos.size() == 1) + { + return pReaderInfos.at(0).getReaderConfigurationInfo().getIcon()->lookupPath(); + } + else if (pReaderInfos.size() > 1) + { + return ReaderConfiguration::getMultipleReaderIconPath(); + } + + return ReaderConfiguration::getNoReaderFoundIconPath(); +} + + +QString StepChooseCardGui::formatErrorMessages(const QString& pMessage1, const QString& pMessage2) +{ + const bool oneMessageIsEmpty = pMessage1.isEmpty() || pMessage2.isEmpty(); + + return oneMessageIsEmpty ? pMessage1 + pMessage2 : QStringLiteral("

%1

%2

").arg(pMessage1, pMessage2); +} + + +void StepChooseCardGui::updateErrorMessage(const QString& pTitle, const QString& pMessage1, const QString& pMessage2, bool closeErrorMessage) +{ + if (closeErrorMessage || mContext->getStatus().isError()) + { + mReaderDeviceGui->deactivate(); + mInformationMessageBox->done(QMessageBox::InvalidRole); + return; + } + + QString iconPath = getCurrentReaderImage(Env::getSingleton()->getReaderInfos(ReaderFilter::UniqueReaderTypes)); + if (iconPath.isEmpty()) + { + mInformationMessageBox->setIcon(QMessageBox::Information); + } + else + { + mInformationMessageBox->setIconPixmap(QPixmap(iconPath).scaledToWidth(200, Qt::SmoothTransformation)); + } + mInformationMessageBox->setText(QStringLiteral("%1").arg(pTitle)); + mInformationMessageBox->setInformativeText(formatErrorMessages(pMessage1, pMessage2)); + + if (mInformationMessageBox->isVisible() || mSubDialogOpen) + { + return; + } + + if (mInformationMessageBox->exec() != QMessageBox::InvalidRole) + { + if (mInformationMessageBox->clickedButton() == mCancelButton) + { + Q_EMIT fireCancelled(); + return; + } + + mSubDialogOpen = true; + if (mInformationMessageBox->clickedButton() == mDeviceButton) + { + mReaderDeviceGui->activate(); + } + } + // else: dialog was closed by an onErrorMessage(..., true) call (i.e. card found) +} + + +void StepChooseCardGui::onSubDialogFinished() +{ + mSubDialogOpen = false; + Env::getSingleton()->startDetection(); + + QMetaObject::invokeMethod(this, &StepChooseCardGui::onReaderManagerSignal, Qt::QueuedConnection); +} + + +const QString StepChooseCardGui::connectedRemoteReaderNames() const +{ + RemoteClient* remoteClient = Env::getSingleton(); + const auto deviceInfos = remoteClient->getConnectedDeviceInfos(); + QStringList deviceNames; + for (const auto& info : deviceInfos) + { + deviceNames.append(QLatin1Char('"') + info.getName() + QLatin1Char('"')); + } + return deviceNames.join(QLatin1String(", ")); +} + + +void StepChooseCardGui::onReaderManagerSignal() +{ + const auto readers = Env::getSingleton()->getReaderInfos(); + + mReaderDeviceGui->reactToReaderCount(readers.size()); + + bool readerWithInsufficientApduLength = false; + QVector readersWithNpa; + QVector remoteReaders; + for (const auto& readerInfo : readers) + { + if (!readerInfo.sufficientApduLength()) + { + readerWithInsufficientApduLength = true; + } + if (readerInfo.hasEidCard()) + { + readersWithNpa << readerInfo; + } + if (readerInfo.getPlugInType() == ReaderManagerPlugInType::REMOTE) + { + remoteReaders << readerInfo; + } + } + + if (readers.size() == 0) + { + const QString onlineHelpUrl = HelpAction::getOnlineUrl(QStringLiteral("stepChooseCardGui")); + const QString onlineHelpText = tr("If you need help or have problems with your card reader, you can consult the " + "%1online help%2 for futher information.") + .arg(QStringLiteral("").arg(onlineHelpUrl), QStringLiteral("")); + + updateErrorMessage(tr("No card reader found. Please connect card reader first."), + tr("Please choose \"Settings\" to install a card reader or configure your smartphone as a card reader."), + onlineHelpText, + false); + return; + } + + if (readersWithNpa.size() == 0) + { + if (readerWithInsufficientApduLength) + { + if (readers.size() == 1) + { + updateErrorMessage(tr("Extended Length is not supported."), + tr("Your remote reader does not meet the technical requirements (Extended Length not supported)."), + QString(), + false); + return; + } + else + { + updateErrorMessage(tr("Extended Length is not supported."), + tr("At least one of your card readers does not meet the technical requirements (Extended Length not supported). Please place the ID card on a different card reader."), + QString(), + false); + } + } + else + { + QString remoteReaderInfo; + if (remoteReaders.size() > 0) + { + remoteReaderInfo = tr("Connected to following remote readers: %1.").arg(connectedRemoteReaderNames()); + } + const QString onlineHelpUrl = HelpAction::getOnlineUrl(QStringLiteral("stepChooseCardGui")); + const QString onlineHelpText = tr("If you have already placed an ID card on your card reader, " + "you can consult the %1online help%2 for futher information.") + .arg(QStringLiteral("").arg(onlineHelpUrl), QStringLiteral("")); + + updateErrorMessage(tr("Please place an ID card on the card reader."), onlineHelpText, remoteReaderInfo, false); + } + } + else if (readersWithNpa.size() > 1) + { + const QString onlineHelpUrl = HelpAction::getOnlineUrl(QStringLiteral("stepChooseCardGui")); + const QString onlineHelpText = tr("Please make sure that only one card reader with an ID card on it is connected to " + "your computer. If you have already placed an ID card on your card reader, " + "you can consult the %1online help%2 for futher information.") + .arg(QStringLiteral("").arg(onlineHelpUrl), QStringLiteral("")); + + updateErrorMessage(tr("Please place only one ID card on the card reader."), onlineHelpText, QString(), false); + } + else + { + if (readersWithNpa[0].isPinDeactivated() && !mContext->isCanAllowedMode()) + { + updateErrorMessage(tr("Online identification function is disabled."), + tr("This action cannot be performed. The online identification function of your ID card is deactivated." + " Please contact the authority responsible for issuing your identification document to activate the online identification function."), + QString(), + false); + } + else + { + updateErrorMessage(QString(), QString(), QString(), true); + } + } +} diff --git a/src/ui/widget/step/StepChooseCardGui.h b/src/ui/widget/step/StepChooseCardGui.h new file mode 100644 index 0000000..e33b120 --- /dev/null +++ b/src/ui/widget/step/StepChooseCardGui.h @@ -0,0 +1,56 @@ +/*! + * \brief GUI to select reader/card. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/AuthContext.h" +#include "DiagnosisGui.h" +#include "ReaderDeviceGui.h" +#include "ReaderManager.h" +#include "step/StepAuthenticationEac1Widget.h" +#include "StepGui.h" + +#include + + +namespace governikus +{ + +class AuthenticateStepsWidget; + +class StepChooseCardGui + : public StepGui +{ + Q_OBJECT + + private: + const QSharedPointer mContext; + StepAuthenticationEac1Widget* mAuthWidget; + QPointer mInformationMessageBox; + QPointer mReaderDeviceGui; + QPushButton* mCancelButton, * mDeviceButton; + bool mSubDialogOpen; + + QString getCurrentReaderImage(const QVector& pReaderInfos); + static QString formatErrorMessages(const QString& pMessage1, const QString& pMessage2); + void updateErrorMessage(const QString& pTitle, const QString& pMessage1, const QString& pMessage2 = QString(), bool closeErrorMessage = false); + const QString connectedRemoteReaderNames() const; + + private Q_SLOTS: + void onSubDialogFinished(); + + public Q_SLOTS: + void onReaderManagerSignal(); + + public: + StepChooseCardGui(const QSharedPointer& pContext, QWidget* pParent); + virtual ~StepChooseCardGui() override; + + virtual void activate() override; + virtual void deactivate() override; +}; + +} // namespace governikus diff --git a/src/ui/widget/step/StepErrorGui.cpp b/src/ui/widget/step/StepErrorGui.cpp new file mode 100644 index 0000000..2d1a882 --- /dev/null +++ b/src/ui/widget/step/StepErrorGui.cpp @@ -0,0 +1,114 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "StepErrorGui.h" + +#include "AppQtMainWidget.h" +#include "generic/GuiUtils.h" +#include "LogFileSaveDialog.h" + +#include +#include +#include + + +using namespace governikus; + + +Q_DECLARE_LOGGING_CATEGORY(gui) + + +StepErrorGui::StepErrorGui(QSharedPointer pContext, AppQtMainWidget* const pMainWidget) + : StepGui(pContext) + , mContext(pContext) + , mMainWidget(pMainWidget) + , mMessageBox() +{ +} + + +StepErrorGui::~StepErrorGui() +{ +} + + +void StepErrorGui::reportError() +{ + // Do not close the window automatically in case of errors when the workflow is done. + mMainWidget->setHideWindowAfterWorkflow(false); + + if (mContext->getStatus().is(GlobalStatus::Code::Paos_Error_SAL_Invalid_Key) && !mContext->getCardConnection()->getReaderInfo().getCardInfo().isPinDeactivated()) + { + if (GuiUtils::showWrongPinBlockedDialog(mMainWidget)) + { + mMainWidget->switchToPinSettingsAfterWorkflow(); + Q_EMIT switchedToPinSettings(); + } + else + { + Q_EMIT fireUiFinished(); + } + return; + } + + QString message = mContext->getStatus().toErrorDescription(true); + if (message.isEmpty()) + { + qCCritical(gui) << "No error message determined:" << mContext->getStatus(); + message = tr("Sorry, that should not have happened! Please contact the support team."); + Q_ASSERT(!message.isEmpty()); + } + + // If workflow has been killed, we do not show anything. + if (mContext->isWorkflowKilled()) + { + Q_EMIT fireUiFinished(); + + return; + } + + if (mMessageBox.isNull()) + { + mMessageBox = QPointer(new QMessageBox(mMainWidget)); + + QPushButton* saveLogButton = nullptr; + if (mContext->getStatus().is(GlobalStatus::Code::Card_Unexpected_Transmit_Status) || mContext->getStatus().isMessageMasked()) + { + saveLogButton = mMessageBox->addButton(tr("Save Log"), QMessageBox::HelpRole); + } + mMessageBox->setWindowTitle(QApplication::applicationName() + QStringLiteral(" - ") + tr("Error")); + mMessageBox->setWindowModality(Qt::ApplicationModal); + mMessageBox->setIcon(QMessageBox::Warning); + mMessageBox->setWindowFlags(mMessageBox->windowFlags() & ~Qt::WindowContextHelpButtonHint); + mMessageBox->setText(message); + mMessageBox->setStandardButtons(QMessageBox::Ok); + mMessageBox->button(QMessageBox::Ok)->setFocus(); + do + { + mMessageBox->exec(); + if (mMessageBox->clickedButton() == saveLogButton) + { + LogFileSaveDialog().saveLogFile(mMessageBox); + } + } + while (mMessageBox->clickedButton() == saveLogButton); + } + + Q_EMIT fireUiFinished(); +} + + +void StepErrorGui::forwardStep() +{ + Q_EMIT fireUiFinished(); +} + + +void StepErrorGui::closeActiveDialogs() +{ + if (mMessageBox) + { + mMessageBox->reject(); + } +} diff --git a/src/ui/widget/step/StepErrorGui.h b/src/ui/widget/step/StepErrorGui.h new file mode 100644 index 0000000..fad9579 --- /dev/null +++ b/src/ui/widget/step/StepErrorGui.h @@ -0,0 +1,43 @@ +/*! + * \brief GUI for step "Error". + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/WorkflowContext.h" +#include "StepGui.h" + +#include + +namespace governikus +{ + +class AppQtMainWidget; + +class StepErrorGui + : public StepGui +{ + Q_OBJECT + + public: + StepErrorGui(QSharedPointer pContext, AppQtMainWidget* const pMainWidget); + virtual ~StepErrorGui() override; + + virtual void reportError(); + void closeActiveDialogs(); + + public Q_SLOTS: + virtual void forwardStep() override; + + private: + QSharedPointer mContext; + AppQtMainWidget* const mMainWidget; + QPointer mMessageBox; + + Q_SIGNALS: + void switchedToPinSettings(); +}; + +} // namespace governikus diff --git a/src/widget/step/StepGui.cpp b/src/ui/widget/step/StepGui.cpp similarity index 100% rename from src/widget/step/StepGui.cpp rename to src/ui/widget/step/StepGui.cpp diff --git a/src/ui/widget/step/StepGui.h b/src/ui/widget/step/StepGui.h new file mode 100644 index 0000000..291ca8f --- /dev/null +++ b/src/ui/widget/step/StepGui.h @@ -0,0 +1,82 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "generic/ButtonState.h" + +#include +#include +#include + +namespace governikus +{ + +class WorkflowContext; + + +class StepGuiDelegate + : public QObject +{ + Q_OBJECT + + public: + StepGuiDelegate(); + + Q_SIGNALS: + void setForwardButtonState(ButtonState pState, const QString& pText); + void setCancelButtonState(ButtonState pState); +}; + + +class StepGui + : public QObject +{ + Q_OBJECT + + public: + StepGui(const QSharedPointer& pContext); + virtual ~StepGui(); + + StepGuiDelegate* getStepGuiDelegate() const + { + return mDelegate.data(); + } + + + virtual void activate() + { + } + + + virtual void deactivate() + { + } + + + virtual void forwardStep(); + + protected: + void setForwardButtonState(ButtonState pState, const QString& pText = QString()) + { + Q_EMIT mDelegate->setForwardButtonState(pState, pText); + } + + + void setCancelButtonState(ButtonState pState) + { + Q_EMIT mDelegate->setCancelButtonState(pState); + } + + + protected: + QScopedPointer mDelegate; + + Q_SIGNALS: + void fireUiFinished(); + void fireCancelled(); + +}; + +} // namespace governikus diff --git a/src/widget/step/StepProcessingGui.cpp b/src/ui/widget/step/StepProcessingGui.cpp similarity index 100% rename from src/widget/step/StepProcessingGui.cpp rename to src/ui/widget/step/StepProcessingGui.cpp diff --git a/src/ui/widget/step/StepProcessingGui.h b/src/ui/widget/step/StepProcessingGui.h new file mode 100644 index 0000000..937e0f1 --- /dev/null +++ b/src/ui/widget/step/StepProcessingGui.h @@ -0,0 +1,31 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "StepGui.h" + + +namespace governikus +{ + +class AuthenticateStepsWidget; + +class StepProcessingGui + : public StepGui +{ + Q_OBJECT + + public: + StepProcessingGui(const QSharedPointer& pContext, AuthenticateStepsWidget* pStepsWidget); + virtual ~StepProcessingGui() override; + + virtual void activate() override; + virtual void deactivate() override; + + private: + AuthenticateStepsWidget* mStepsWidget; +}; + +} // namespace governikus diff --git a/src/widget/step/StepShowSelfAuthenticationDataGui.cpp b/src/ui/widget/step/StepShowSelfAuthenticationDataGui.cpp similarity index 100% rename from src/widget/step/StepShowSelfAuthenticationDataGui.cpp rename to src/ui/widget/step/StepShowSelfAuthenticationDataGui.cpp diff --git a/src/ui/widget/step/StepShowSelfAuthenticationDataGui.h b/src/ui/widget/step/StepShowSelfAuthenticationDataGui.h new file mode 100644 index 0000000..6b9df7f --- /dev/null +++ b/src/ui/widget/step/StepShowSelfAuthenticationDataGui.h @@ -0,0 +1,37 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/SelfAuthContext.h" +#include "StepGui.h" + +#include + +namespace governikus +{ + +class AuthenticateStepsWidget; + +class StepShowSelfAuthenticationDataGui + : public StepGui +{ + Q_OBJECT + + public: + StepShowSelfAuthenticationDataGui(QSharedPointer pContext, AuthenticateStepsWidget* pStepsWidget); + virtual ~StepShowSelfAuthenticationDataGui() override; + + virtual void activate() override; + virtual void deactivate() override; + + private Q_SLOTS: + virtual void forwardStep() override; + + private: + QSharedPointer mContext; + AuthenticateStepsWidget* mStepsWidget; +}; + +} // namespace governikus diff --git a/src/ui/widget/workflow/GenericWorkflowGui.h b/src/ui/widget/workflow/GenericWorkflowGui.h new file mode 100644 index 0000000..3316069 --- /dev/null +++ b/src/ui/widget/workflow/GenericWorkflowGui.h @@ -0,0 +1,91 @@ +/*! + * \brief Generic base class for Qt based WorkflowUi implementations. + * + * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "AppQtMainWidget.h" +#include "context/WorkflowContext.h" +#include "step/StepGui.h" +#include "WorkflowGui.h" +#include "WorkflowQtWidget.h" + +#include +#include + + +namespace governikus +{ + +template +class GenericWorkflowGui + : public WorkflowGui +{ + protected: + AppQtMainWidget* mParentWidget; + WorkflowQtWidget* mWidget; + QSharedPointer mStepGui; + QSharedPointer mContext; + + public: + GenericWorkflowGui(const QSharedPointer& pContext, AppQtMainWidget* pParentWidget, WorkflowQtWidget* pWidget) + : WorkflowGui() + , mParentWidget(pParentWidget) + , mWidget(pWidget) + , mStepGui(nullptr) + , mContext(pContext.objectCast()) + { + Q_ASSERT(mContext != nullptr); + connect(this, &WorkflowGui::fireUserCancelled, mContext.data(), &WorkflowContext::fireCancelWorkflow); + } + + + virtual void deactivate() override + { + deactivateCurrentStepUi(); + } + + + virtual void activateStepUi(const QSharedPointer& pStepUi) + { + Q_ASSERT(pStepUi); + if (mStepGui == pStepUi) + { + return; + } + + deactivateCurrentStepUi(); + + mStepGui = pStepUi; + if (mWidget != nullptr) + { + QObject::connect(mStepGui->getStepGuiDelegate(), &StepGuiDelegate::setForwardButtonState, mWidget, &WorkflowQtWidget::setForwardButtonState); + QObject::connect(mStepGui->getStepGuiDelegate(), &StepGuiDelegate::setCancelButtonState, mWidget, &WorkflowQtWidget::setCancelButtonState); + } + pStepUi->activate(); + } + + + private: + void deactivateCurrentStepUi() + { + if (mStepGui == nullptr) + { + return; + } + + mStepGui->deactivate(); + if (mWidget != nullptr) + { + QObject::disconnect(mStepGui->getStepGuiDelegate(), &StepGuiDelegate::setForwardButtonState, mWidget, &WorkflowQtWidget::setForwardButtonState); + QObject::disconnect(mStepGui->getStepGuiDelegate(), &StepGuiDelegate::setCancelButtonState, mWidget, &WorkflowQtWidget::setCancelButtonState); + } + mStepGui.clear(); + } + + +}; + +} // namespace governikus diff --git a/src/ui/widget/workflow/WorkflowAuthenticateQtGui.cpp b/src/ui/widget/workflow/WorkflowAuthenticateQtGui.cpp new file mode 100644 index 0000000..cd97785 --- /dev/null +++ b/src/ui/widget/workflow/WorkflowAuthenticateQtGui.cpp @@ -0,0 +1,201 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "WorkflowAuthenticateQtGui.h" + +#include "AppSettings.h" +#include "generic/GuiUtils.h" +#include "states/FinalState.h" +#include "states/StateCheckRefreshAddress.h" +#include "states/StateDidAuthenticateEac1.h" +#include "states/StateDidAuthenticateEac2.h" +#include "states/StateEditAccessRights.h" +#include "states/StateEnterPacePassword.h" +#include "states/StateMaintainCardConnection.h" +#include "states/StateProcessing.h" +#include "states/StateSelectReader.h" +#include "states/StateTransmit.h" +#include "states/StateWriteHistory.h" +#include "step/AuthenticateStepsWidget.h" +#include "step/StepAdviseUserToRemoveCardGui.h" +#include "step/StepAuthenticationDoneGui.h" +#include "step/StepAuthenticationEac1Gui.h" +#include "step/StepChooseCardGui.h" +#include "step/StepErrorGui.h" +#include "step/StepProcessingGui.h" +#include "workflow/WorkflowQtWidget.h" + + +using namespace governikus; + + +WorkflowAuthenticateQtGui::WorkflowAuthenticateQtGui(const QSharedPointer& pContext, AppQtMainWidget* const pParentWidget) + : GenericWorkflowGui(pContext, pParentWidget, pParentWidget->getAuthenticationWorkflowWidget()) + , mCanEntered(false) + , mAuthenticateStepsWidget(mParentWidget->findChild()) + , mAdviseUserToRemoveCardGui(new StepAdviseUserToRemoveCardGui(mContext, mParentWidget)) + , mDidAuthenticateGui(new StepAuthenticationEac1Gui(mContext, mAuthenticateStepsWidget)) + , mChooseCardGui(new StepChooseCardGui(mContext, mAuthenticateStepsWidget)) + , mErrorGui(new StepErrorGui(mContext, mParentWidget)) + , mProcessingGui(new StepProcessingGui(mContext, mAuthenticateStepsWidget)) + , mAuthenticationDoneGui(new StepAuthenticationDoneGui(mContext)) +{ + Q_ASSERT(mAuthenticateStepsWidget != nullptr); + connect(mWidget, &WorkflowQtWidget::fireUserCancelled, this, &WorkflowGui::fireUserCancelled); + connect(mWidget, &WorkflowQtWidget::forwardStep, this, &WorkflowAuthenticateQtGui::onForwardStep); +} + + +WorkflowAuthenticateQtGui::~WorkflowAuthenticateQtGui() +{ +} + + +void WorkflowAuthenticateQtGui::activate() +{ + activateStepUi(mProcessingGui); + mParentWidget->workflowActivated(WorkflowWidgetParent::Authentication, tr("Identify")); + connect(mContext.data(), &WorkflowContext::fireStateChanged, this, &WorkflowAuthenticateQtGui::onStateChanged); +} + + +void WorkflowAuthenticateQtGui::deactivate() +{ + mParentWidget->workflowDeactivated(); +} + + +bool WorkflowAuthenticateQtGui::verifyAbortWorkflow() +{ + QMessageBox msgBox(mParentWidget); + msgBox.setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("Cancel")); + msgBox.setWindowModality(Qt::WindowModal); + msgBox.setText(tr("Do you really want to cancel?")); + msgBox.setInformativeText(tr("You can as well identity later by calling the service provider's Internet page" + " again.")); + msgBox.setIconPixmap(QIcon(QStringLiteral(":/images/npa.svg")).pixmap(32, 32)); + msgBox.setWindowFlags(msgBox.windowFlags() & ~Qt::WindowContextHelpButtonHint); + msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + msgBox.button(QMessageBox::Yes)->setFocus(); + + connect(this, &WorkflowAuthenticateQtGui::fireCloseActiveDialogs, &msgBox, &QMessageBox::reject); + msgBox.exec(); + return msgBox.result() == QMessageBox::Yes; +} + + +void WorkflowAuthenticateQtGui::onForwardStep() +{ + if (mStepGui != nullptr) + { + mStepGui->forwardStep(); + } +} + + +void WorkflowAuthenticateQtGui::onStateChanged(const QString& pNewState) +{ + if (mContext->getStatus().isError() && !mContext->isErrorReportedToUser()) + { + if (!mContext->getStatus().isCancellationByUser()) + { + activateStepUi(mErrorGui); + mErrorGui->reportError(); + } + mContext->setErrorReportedToUser(); + } + + bool approveNewState = true; + if (AbstractState::isState(pNewState)) + { + activateStepUi(mProcessingGui); + + GeneralSettings& settings = Env::getSingleton()->getGeneralSettings(); + approveNewState = !settings.isTransportPinReminder(); + } + else if (AbstractState::isState(pNewState)) + { + approveNewState = false; + activateStepUi(mDidAuthenticateGui); + mDidAuthenticateGui->setState(StepAuthenticationEac1Widget::State::EDIT_CHAT); + } + else if (AbstractState::isState(pNewState)) + { + mDidAuthenticateGui->setState(StepAuthenticationEac1Widget::State::INITIAL); + if (mContext->getLastPaceResult() != CardReturnCode::OK && !mContext->isPaceResultReportedToUser()) + { + mContext->setPaceResultReportedToUser(); + mDidAuthenticateGui->incorrectPinError(); + } + } + else if (AbstractState::isState(pNewState)) + { + mContext->setStateApproved(true); + activateStepUi(mChooseCardGui); + return; + } + else if (AbstractState::isState(pNewState)) + { + const PacePasswordId passwordId = mContext->getEstablishPaceChannelType(); + if (passwordId == PacePasswordId::PACE_PIN || passwordId == PacePasswordId::PACE_CAN) + { + if (passwordId == PacePasswordId::PACE_CAN) + { + approveNewState = !mContext->getCardConnection()->getReaderInfo().isBasicReader(); + mCanEntered = true; + } + else if (passwordId == PacePasswordId::PACE_PIN) + { + // PIN entry after CAN entry is done without user interaction + approveNewState = mCanEntered || !mContext->getCardConnection()->getReaderInfo().isBasicReader(); + } + activateStepUi(mDidAuthenticateGui); + mDidAuthenticateGui->setState(StepAuthenticationEac1Widget::State::ENTER_PIN); + } + else if (passwordId == PacePasswordId::PACE_PUK) + { + approveNewState = false; + Q_EMIT mContext->fireCancelWorkflow(); + if (GuiUtils::showWrongPinBlockedDialog(mWidget)) + { + mContext->setReaderName(QString()); + mParentWidget->switchToPinSettingsAfterWorkflow(); + } + } + } + else if (AbstractState::isState(pNewState)) + { + activateStepUi(mDidAuthenticateGui); + mDidAuthenticateGui->setState(StepAuthenticationEac1Widget::State::AUTHENTICATING_ESERVICE); + } + else if (AbstractState::isState(pNewState)) + { + mDidAuthenticateGui->setState(StepAuthenticationEac1Widget::State::AUTHENTICATING_CARD); + } + else if (AbstractState::isState(pNewState)) + { + mDidAuthenticateGui->setState(StepAuthenticationEac1Widget::State::READING_CARD_DATA); + } + else if (AbstractState::isState(pNewState) && mDidAuthenticateGui->isActive()) + { + mDidAuthenticateGui->setState(StepAuthenticationEac1Widget::State::REDIRECTING_BROWSER); + } + else if (AbstractState::isState(pNewState) && mDidAuthenticateGui->isActive()) + { + mDidAuthenticateGui->setState(StepAuthenticationEac1Widget::State::FINISHED); + } + else if (AbstractState::isState(pNewState)) + { + activateStepUi(mAdviseUserToRemoveCardGui); + activateStepUi(mAuthenticationDoneGui); + } + mContext->setStateApproved(approveNewState); +} + + +void WorkflowAuthenticateQtGui::onCloseActiveDialogs() +{ + mAdviseUserToRemoveCardGui->closeActiveDialogs(); + mErrorGui->closeActiveDialogs(); +} diff --git a/src/ui/widget/workflow/WorkflowAuthenticateQtGui.h b/src/ui/widget/workflow/WorkflowAuthenticateQtGui.h new file mode 100644 index 0000000..af0daa9 --- /dev/null +++ b/src/ui/widget/workflow/WorkflowAuthenticateQtGui.h @@ -0,0 +1,55 @@ +/*! + * \brief Qt widget based WorkflowAuthenticateUi implementation. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/AuthContext.h" +#include "GenericWorkflowGui.h" + + +namespace governikus +{ + +class AuthenticateStepsWidget; +class StepAdviseUserToRemoveCardGui; +class StepAuthenticationDoneGui; +class StepAuthenticationEac1Gui; +class StepChooseCardGui; +class StepErrorGui; +class StepProcessingGui; + + +class WorkflowAuthenticateQtGui + : public GenericWorkflowGui +{ + Q_OBJECT + + private: + bool mCanEntered; + AuthenticateStepsWidget* mAuthenticateStepsWidget; + QSharedPointer mAdviseUserToRemoveCardGui; + QSharedPointer mDidAuthenticateGui; + QSharedPointer mChooseCardGui; + QSharedPointer mErrorGui; + QSharedPointer mProcessingGui; + QSharedPointer mAuthenticationDoneGui; + + private Q_SLOTS: + void onForwardStep(); + void onStateChanged(const QString& pNewState); + + public: + WorkflowAuthenticateQtGui(const QSharedPointer& pContext, AppQtMainWidget* const pParentWidget); + virtual ~WorkflowAuthenticateQtGui() override; + + virtual void activate() override; + virtual void deactivate() override; + virtual bool verifyAbortWorkflow() override; + + virtual void onCloseActiveDialogs() override; +}; + +} // namespace governikus diff --git a/src/ui/widget/workflow/WorkflowChangePinQtGui.cpp b/src/ui/widget/workflow/WorkflowChangePinQtGui.cpp new file mode 100644 index 0000000..ecf8806 --- /dev/null +++ b/src/ui/widget/workflow/WorkflowChangePinQtGui.cpp @@ -0,0 +1,142 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "WorkflowChangePinQtGui.h" + +#include "AppQtMainWidget.h" +#include "generic/GuiUtils.h" +#include "PinSettingsWidget.h" +#include "states/StateChangePin.h" +#include "states/StateCleanUpReaderManager.h" +#include "states/StateConnectCard.h" +#include "states/StateEnterNewPacePin.h" +#include "states/StateEnterPacePassword.h" +#include "states/StateEstablishPaceChannel.h" +#include "states/StateMaintainCardConnection.h" +#include "states/StateSelectReader.h" +#include "step/StepChooseCardGui.h" +#include "step/StepErrorGui.h" + +using namespace governikus; + +WorkflowChangePinQtGui::WorkflowChangePinQtGui(QSharedPointer pContext, AppQtMainWidget* const pParentWidget) + : GenericWorkflowGui(pContext, pParentWidget, nullptr) + , mPinSettingsWidget(mParentWidget->findChild()) + , mChooseCardGui(new StepChooseCardGui(mContext, mPinSettingsWidget)) + , mErrorGui(new StepErrorGui(mContext, mParentWidget)) +{ + Q_ASSERT(mPinSettingsWidget); + connect(mContext.data(), &WorkflowContext::fireStateChanged, this, &WorkflowChangePinQtGui::onStateChanged); +} + + +WorkflowChangePinQtGui::~WorkflowChangePinQtGui() +{ +} + + +void WorkflowChangePinQtGui::activate() +{ + mParentWidget->workflowActivated(WorkflowWidgetParent::SettingsChangePin, QString()); + mPinSettingsWidget->setContext(mContext); +} + + +void WorkflowChangePinQtGui::deactivate() +{ + mPinSettingsWidget->setContext(QSharedPointer()); + mParentWidget->workflowDeactivated(); +} + + +bool WorkflowChangePinQtGui::verifyAbortWorkflow() +{ + // not really necessary to notify the user + return true; +} + + +void WorkflowChangePinQtGui::onStateChanged(const QString& pNextState) +{ + if (mContext->getStatus().isError() && !mContext->isErrorReportedToUser()) + { + if (!mContext->getStatus().isCancellationByUser()) + { + activateStepUi(mErrorGui); + mErrorGui->reportError(); + } + mContext->setErrorReportedToUser(); + } + + if (AbstractState::isState(pNextState)) + { + activateStepUi(mChooseCardGui); + } + else if (AbstractState::isState(pNextState)) + { + GenericWorkflowGui::deactivate(); + } + else if (AbstractState::isState(pNextState)) + { + const bool isBasicReader = mContext->getCardConnection()->getReaderInfo().isBasicReader(); + if (mContext->getEstablishPaceChannelType() == PacePasswordId::PACE_PUK) + { + mPinSettingsWidget->updatePinButton(isBasicReader && mPinSettingsWidget->getPuk().isEmpty()); + } + else + { + mPinSettingsWidget->updatePinButton(isBasicReader && mPinSettingsWidget->getPin().isEmpty()); + } + + if (CardReturnCodeUtil::equalsWrongPacePassword(mContext->getLastPaceResult())) + { + if (isBasicReader) + { + mPinSettingsWidget->updatePasswordFields(); + mPinSettingsWidget->setPasswordFocus(); + } + GuiUtils::showPinCanPukErrorDialog(mContext->getLastPaceResult(), mContext->isCanAllowedMode(), mPinSettingsWidget); + if (isBasicReader) + { + return; + } + } + + if (mPinSettingsWidget->getPinButtonEnabled()) + { + return; + } + } + else if (AbstractState::isState(pNextState)) + { + if (mContext->getCardConnection()->getReaderInfo().isBasicReader()) + { + Q_ASSERT(!mPinSettingsWidget->getPin().isNull()); + Q_ASSERT(!mPinSettingsWidget->getCan().isNull()); + Q_ASSERT(!mPinSettingsWidget->getPuk().isNull()); + mContext->setPin(mPinSettingsWidget->getPin()); + mContext->setCan(mPinSettingsWidget->getCan()); + mContext->setPuk(mPinSettingsWidget->getPuk()); + } + } + else if (AbstractState::isState(pNextState)) + { + if (mContext->getCardConnection()->getReaderInfo().isBasicReader()) + { + Q_ASSERT(!mPinSettingsWidget->getNewPin().isNull()); + mContext->setNewPin(mPinSettingsWidget->getNewPin()); + } + } + else if (AbstractState::isState(pNextState) && mContext->getStatus().isNoError()) + { + bool pinBlocked = false; + if (mContext->getCardConnection()) + { + pinBlocked = (mContext->getCardConnection()->getReaderInfo().getRetryCounter() == 0); + } + mPinSettingsWidget->setMode(pinBlocked ? PinSettingsWidget::Mode::AfterPinUnblock : PinSettingsWidget::Mode::AfterPinChange); + } + + mContext->setStateApproved(); +} diff --git a/src/ui/widget/workflow/WorkflowChangePinQtGui.h b/src/ui/widget/workflow/WorkflowChangePinQtGui.h new file mode 100644 index 0000000..79bf0b3 --- /dev/null +++ b/src/ui/widget/workflow/WorkflowChangePinQtGui.h @@ -0,0 +1,42 @@ +/*! + * \brief Qt widget based WorkflowChangePinUi implementation. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/ChangePinContext.h" +#include "GenericWorkflowGui.h" + +namespace governikus +{ + +class StepChooseCardGui; +class PinSettingsWidget; +class StepErrorGui; + + +class WorkflowChangePinQtGui + : public GenericWorkflowGui +{ + Q_OBJECT + + public: + WorkflowChangePinQtGui(QSharedPointer pContext, AppQtMainWidget* const pParentWidget); + virtual ~WorkflowChangePinQtGui() override; + + virtual void activate() override; + virtual void deactivate() override; + virtual bool verifyAbortWorkflow() override; + + private Q_SLOTS: + void onStateChanged(const QString& pNextState); + + private: + PinSettingsWidget* mPinSettingsWidget; + QSharedPointer mChooseCardGui; + QSharedPointer mErrorGui; +}; + +} // namespace governikus diff --git a/src/ui/widget/workflow/WorkflowGui.cpp b/src/ui/widget/workflow/WorkflowGui.cpp new file mode 100644 index 0000000..78bd1d3 --- /dev/null +++ b/src/ui/widget/workflow/WorkflowGui.cpp @@ -0,0 +1,29 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "WorkflowGui.h" + +#include "step/StepGui.h" + +using namespace governikus; + +WorkflowGuiDelegate::WorkflowGuiDelegate() +{ +} + + +WorkflowGui::WorkflowGui() + : mDelegate(new WorkflowGuiDelegate) +{ +} + + +WorkflowGui::~WorkflowGui() +{ +} + + +void WorkflowGui::onCloseActiveDialogs() +{ +} diff --git a/src/ui/widget/workflow/WorkflowGui.h b/src/ui/widget/workflow/WorkflowGui.h new file mode 100644 index 0000000..8767aa2 --- /dev/null +++ b/src/ui/widget/workflow/WorkflowGui.h @@ -0,0 +1,71 @@ +/*! + * \brief Base class for Qt based WorkflowUi implementations. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "AppQtMainWidget.h" +#include "context/WorkflowContext.h" +#include "step/StepGui.h" +#include "WorkflowQtWidget.h" +#include "WorkflowWidgetParent.h" + +#include +#include + +namespace governikus +{ + +class WorkflowGuiDelegate + : public QObject +{ + Q_OBJECT + + public: + WorkflowGuiDelegate(); +}; + + +class WorkflowGui + : public QObject +{ + Q_OBJECT + + protected: + QScopedPointer mDelegate; + + public: + WorkflowGui(); + virtual ~WorkflowGui(); + + WorkflowGuiDelegate* getWorkflowGuiDelegate() const + { + return mDelegate.data(); + } + + + virtual bool verifyAbortWorkflow() = 0; + + virtual void activate() + { + } + + + virtual void deactivate() + { + } + + + public Q_SLOTS: + virtual void onCloseActiveDialogs(); + + Q_SIGNALS: + void fireUserCancelled(); + void fireChangePinRequest(); + void fireCloseActiveDialogs(); + +}; + +} // namespace governikus diff --git a/src/ui/widget/workflow/WorkflowQtWidget.cpp b/src/ui/widget/workflow/WorkflowQtWidget.cpp new file mode 100644 index 0000000..1a0aa7e --- /dev/null +++ b/src/ui/widget/workflow/WorkflowQtWidget.cpp @@ -0,0 +1,117 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "WorkflowQtWidget.h" + +#include +#include + +using namespace governikus; + +WorkflowQtWidget::WorkflowQtWidget(QWidget* pParent) + : QWidget(pParent) + , mMainLayout(new QVBoxLayout(this)) + , mStepWidgetArea(new QWidget(this)) + , mCancelButton(nullptr) + , mForwardButton(nullptr) +{ + (new QVBoxLayout(mStepWidgetArea))->setMargin(0); + mMainLayout->addWidget(mStepWidgetArea); + mMainLayout->setMargin(0); + + QHBoxLayout* buttonLayout = new QHBoxLayout(); + buttonLayout->setMargin(0); + mMainLayout->addLayout(buttonLayout); + + QSpacerItem* horizontalSpacer = new QSpacerItem(40, 1, QSizePolicy::Expanding, QSizePolicy::Ignored); + + buttonLayout->addItem(horizontalSpacer); + + mCancelButton = new QPushButton(this); + connect(mCancelButton, &QPushButton::clicked, this, &WorkflowQtWidget::onCancelButtonClicked); + mCancelButton->setVisible(false); + buttonLayout->addWidget(mCancelButton); + setCancelButtonState(ButtonState::HIDDEN); + + mForwardButton = new QPushButton(this); + connect(mForwardButton, &QPushButton::clicked, this, &WorkflowQtWidget::forwardStep); + mForwardButton->setVisible(false); + buttonLayout->addWidget(mForwardButton); + setForwardButtonState(ButtonState::HIDDEN); +} + + +WorkflowQtWidget::~WorkflowQtWidget() +{ +} + + +void WorkflowQtWidget::addStepWidget(QWidget* pStepWidget) +{ + mStepWidgetArea->layout()->addWidget(pStepWidget); +} + + +void WorkflowQtWidget::removeStepWidget(QWidget* pStepWidget) +{ + mStepWidgetArea->layout()->removeWidget(pStepWidget); + pStepWidget->setParent(nullptr); // Remove widget from display + // Note: The Step* widgets are deleted in the Step* dtors. +} + + +void WorkflowQtWidget::setForwardButtonState(ButtonState pState, const QString& pText) +{ + setButtonState(mForwardButton, pState, pText.isEmpty() ? tr("Next") : pText); +} + + +void WorkflowQtWidget::setCancelButtonState(ButtonState pState) +{ + setButtonState(mCancelButton, pState, tr("Cancel")); +} + + +void WorkflowQtWidget::onCancelButtonClicked() +{ + setCancelButtonState(ButtonState::DISABLED); + Q_EMIT fireUserCancelled(); +} + + +void WorkflowQtWidget::setButtonState(QAbstractButton* pButton, ButtonState pState, const QString& pText) +{ + pButton->setText(pText); + + switch (pState) + { + case ButtonState::ENABLED: + pButton->setVisible(true); + pButton->setEnabled(true); + break; + + case ButtonState::DISABLED: + pButton->setVisible(true); + pButton->setEnabled(false); + break; + + case ButtonState::HIDDEN: + pButton->setVisible(false); + break; + + case ButtonState::FOCUSSED: + pButton->setVisible(true); + pButton->setEnabled(true); + focusForwardButton(); + break; + } +} + + +void WorkflowQtWidget::focusForwardButton() +{ + mForwardButton->setAutoDefault(true); + mForwardButton->setDefault(true); + mForwardButton->setFocus(); +} diff --git a/src/ui/widget/workflow/WorkflowQtWidget.h b/src/ui/widget/workflow/WorkflowQtWidget.h new file mode 100644 index 0000000..a2bafd6 --- /dev/null +++ b/src/ui/widget/workflow/WorkflowQtWidget.h @@ -0,0 +1,64 @@ +/*! + * \brief Base class for workflow Qt GUI widgets. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "step/StepGui.h" + +#include +#include +#include +#include + +namespace governikus +{ + +class WorkflowQtWidget + : public QWidget +{ + Q_OBJECT + + public: + WorkflowQtWidget(QWidget* pParent = nullptr); + virtual ~WorkflowQtWidget(); + + QWidget* getStepWidgetArea() const + { + return mStepWidgetArea; + } + + + void addStepWidget(QWidget* widget); + void removeStepWidget(QWidget* widget); + + Q_SIGNALS: + /*! + * This signal is sent when the user presses the "Cancel" button. + */ + void fireUserCancelled(); + + /*! + * This signal is sent when the user presses the "Continue" button. + */ + void forwardStep(); + + public Q_SLOTS: + void onCancelButtonClicked(); + void setForwardButtonState(ButtonState pState, const QString& pText = QString()); + void setCancelButtonState(ButtonState pState); + + private: + void setButtonState(QAbstractButton* pButton, ButtonState pState, const QString& pText); + void focusForwardButton(); + + private: + QVBoxLayout* mMainLayout; + QWidget* mStepWidgetArea; + QPushButton* mCancelButton; + QPushButton* mForwardButton; +}; + +} // namespace governikus diff --git a/src/ui/widget/workflow/WorkflowSelfInfoQtGui.cpp b/src/ui/widget/workflow/WorkflowSelfInfoQtGui.cpp new file mode 100644 index 0000000..e9ef925 --- /dev/null +++ b/src/ui/widget/workflow/WorkflowSelfInfoQtGui.cpp @@ -0,0 +1,186 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "WorkflowSelfInfoQtGui.h" + +#include "AppSettings.h" +#include "generic/GuiUtils.h" +#include "states/FinalState.h" +#include "states/StateDidAuthenticateEac1.h" +#include "states/StateDidAuthenticateEac2.h" +#include "states/StateEditAccessRights.h" +#include "states/StateEnterPacePassword.h" +#include "states/StateLoadTcTokenUrl.h" +#include "states/StateMaintainCardConnection.h" +#include "states/StateSelectReader.h" +#include "states/StateTransmit.h" +#include "states/StateWriteHistory.h" +#include "step/AuthenticateStepsWidget.h" +#include "step/StepAdviseUserToRemoveCardGui.h" +#include "step/StepAuthenticationEac1Gui.h" +#include "step/StepChooseCardGui.h" +#include "step/StepErrorGui.h" +#include "step/StepProcessingGui.h" +#include "step/StepShowSelfAuthenticationDataGui.h" +#include "workflow/WorkflowQtWidget.h" + + +using namespace governikus; + + +WorkflowSelfInfoQtGui::WorkflowSelfInfoQtGui(const QSharedPointer& pContext, AppQtMainWidget* const pParentWidget) + : GenericWorkflowGui(pContext, pParentWidget, pParentWidget->getAuthenticationWorkflowWidget()) + , mCanEntered(false) + , mAuthenticateStepsWidget(pParentWidget->findChild()) + , mAdviseUserToRemoveCardGui(new StepAdviseUserToRemoveCardGui(mContext, mParentWidget)) + , mDidAuthenticateGui(new StepAuthenticationEac1Gui(mContext, mAuthenticateStepsWidget)) + , mChooseCardGui(new StepChooseCardGui(mContext, mAuthenticateStepsWidget)) + , mErrorGui(new StepErrorGui(mContext, mParentWidget)) + , mProcessingGui(new StepProcessingGui(mContext, mAuthenticateStepsWidget)) + , mShowSelfAuthenticationDataGui(new StepShowSelfAuthenticationDataGui(mContext, mAuthenticateStepsWidget)) +{ + Q_ASSERT(mAuthenticateStepsWidget != nullptr); + connect(mWidget, &WorkflowQtWidget::fireUserCancelled, this, &WorkflowGui::fireUserCancelled); + connect(mWidget, &WorkflowQtWidget::forwardStep, this, &WorkflowSelfInfoQtGui::onForwardStep); +} + + +WorkflowSelfInfoQtGui::~WorkflowSelfInfoQtGui() +{ +} + + +void WorkflowSelfInfoQtGui::activate() +{ + activateStepUi(mProcessingGui); + mParentWidget->workflowActivated(WorkflowWidgetParent::SelfAuthentication, tr("Identify")); + connect(mContext.data(), &SelfAuthContext::fireStateChanged, this, &WorkflowSelfInfoQtGui::onStateChanged); +} + + +void WorkflowSelfInfoQtGui::deactivate() +{ + mParentWidget->workflowDeactivated(); +} + + +bool WorkflowSelfInfoQtGui::verifyAbortWorkflow() +{ + // not really necessary to notify the user + return true; +} + + +void WorkflowSelfInfoQtGui::onForwardStep() +{ + if (mStepGui) + { + mStepGui->forwardStep(); + } +} + + +void WorkflowSelfInfoQtGui::onStateChanged(const QString& pNewState) +{ + if (mContext->getStatus().isError() && !mContext->isErrorReportedToUser()) + { + if (!mContext->getStatus().isCancellationByUser()) + { + activateStepUi(mErrorGui); + mErrorGui->reportError(); + } + mContext->setErrorReportedToUser(); + } + + bool approveNewState = true; + if (AbstractState::isState(pNewState)) + { + GeneralSettings& settings = Env::getSingleton()->getGeneralSettings(); + approveNewState = !settings.isTransportPinReminder(); + } + else if (AbstractState::isState(pNewState)) + { + approveNewState = false; + activateStepUi(mDidAuthenticateGui); + mDidAuthenticateGui->setState(StepAuthenticationEac1Widget::State::EDIT_CHAT); + } + else if (AbstractState::isState(pNewState)) + { + mDidAuthenticateGui->setState(StepAuthenticationEac1Widget::State::INITIAL); + if (mContext->getLastPaceResult() != CardReturnCode::OK && !mContext->isPaceResultReportedToUser()) + { + mContext->setPaceResultReportedToUser(); + mDidAuthenticateGui->incorrectPinError(); + } + } + else if (AbstractState::isState(pNewState)) + { + mContext->setStateApproved(true); + activateStepUi(mChooseCardGui); + return; + } + else if (AbstractState::isState(pNewState)) + { + const PacePasswordId passwordId = mContext->getEstablishPaceChannelType(); + if (passwordId == PacePasswordId::PACE_PIN || passwordId == PacePasswordId::PACE_CAN) + { + if (passwordId == PacePasswordId::PACE_CAN) + { + approveNewState = !mContext->getCardConnection()->getReaderInfo().isBasicReader(); + mCanEntered = true; + } + else if (passwordId == PacePasswordId::PACE_PIN) + { + // PIN entry after CAN entry is done without user interaction + approveNewState = mCanEntered || !mContext->getCardConnection()->getReaderInfo().isBasicReader(); + } + activateStepUi(mDidAuthenticateGui); + mDidAuthenticateGui->setState(StepAuthenticationEac1Widget::State::ENTER_PIN); + } + else if (passwordId == PacePasswordId::PACE_PUK) + { + approveNewState = false; + Q_EMIT mContext->fireCancelWorkflow(); + if (GuiUtils::showWrongPinBlockedDialog(mWidget)) + { + mContext->setReaderName(QString()); + mParentWidget->switchToPinSettingsAfterWorkflow(); + } + } + } + else if (AbstractState::isState(pNewState)) + { + activateStepUi(mDidAuthenticateGui); + mDidAuthenticateGui->setState(StepAuthenticationEac1Widget::State::AUTHENTICATING_ESERVICE); + } + else if (AbstractState::isState(pNewState)) + { + mDidAuthenticateGui->setState(StepAuthenticationEac1Widget::State::AUTHENTICATING_CARD); + } + else if (AbstractState::isState(pNewState)) + { + mDidAuthenticateGui->setState(StepAuthenticationEac1Widget::State::READING_CARD_DATA); + } + else if (AbstractState::isState(pNewState) && mDidAuthenticateGui->isActive()) + { + mDidAuthenticateGui->setState(StepAuthenticationEac1Widget::State::FINISHED); + } + else if (AbstractState::isState(pNewState)) + { + activateStepUi(mAdviseUserToRemoveCardGui); + if (mContext->getStatus().isNoError()) + { + approveNewState = false; + activateStepUi(mShowSelfAuthenticationDataGui); + } + } + mContext->setStateApproved(approveNewState); +} + + +void WorkflowSelfInfoQtGui::onCloseActiveDialogs() +{ + mAdviseUserToRemoveCardGui->closeActiveDialogs(); + mErrorGui->closeActiveDialogs(); +} diff --git a/src/ui/widget/workflow/WorkflowSelfInfoQtGui.h b/src/ui/widget/workflow/WorkflowSelfInfoQtGui.h new file mode 100644 index 0000000..31a390a --- /dev/null +++ b/src/ui/widget/workflow/WorkflowSelfInfoQtGui.h @@ -0,0 +1,53 @@ +/*! + * \brief Qt widget based WorkflowSelfInfoUi implementation. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/SelfAuthContext.h" +#include "GenericWorkflowGui.h" + +namespace governikus +{ + +class AuthenticateStepsWidget; +class StepAdviseUserToRemoveCardGui; +class StepShowSelfAuthenticationDataGui; +class StepAuthenticationEac1Gui; +class StepChooseCardGui; +class StepErrorGui; +class StepProcessingGui; + +class WorkflowSelfInfoQtGui + : public GenericWorkflowGui +{ + Q_OBJECT + + private: + bool mCanEntered; + AuthenticateStepsWidget* mAuthenticateStepsWidget; + QSharedPointer mAdviseUserToRemoveCardGui; + QSharedPointer mDidAuthenticateGui; + QSharedPointer mChooseCardGui; + QSharedPointer mErrorGui; + QSharedPointer mProcessingGui; + QSharedPointer mShowSelfAuthenticationDataGui; + + private Q_SLOTS: + void onForwardStep(); + void onStateChanged(const QString& pNewState); + + public: + WorkflowSelfInfoQtGui(const QSharedPointer& pContext, AppQtMainWidget* const pParentWidget); + virtual ~WorkflowSelfInfoQtGui() override; + + virtual void activate() override; + virtual void deactivate() override; + virtual bool verifyAbortWorkflow() override; + + virtual void onCloseActiveDialogs() override; +}; + +} // namespace governikus diff --git a/src/ui/widget/workflow/WorkflowWidgetParent.h b/src/ui/widget/workflow/WorkflowWidgetParent.h new file mode 100644 index 0000000..9bbeeaa --- /dev/null +++ b/src/ui/widget/workflow/WorkflowWidgetParent.h @@ -0,0 +1,21 @@ +/*! + * \brief Enum identifying the containers in the application GUI which can be + * parent to a workflow widget. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +namespace governikus +{ + +enum class WorkflowWidgetParent +{ + Authentication, + SettingsChangePin, + SelfAuthentication +}; + + +} // namespace governikus diff --git a/src/websocket/CMakeLists.txt b/src/websocket/CMakeLists.txt deleted file mode 100644 index 638ac85..0000000 --- a/src/websocket/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -IF(TARGET Qt5::WebSockets) - ADD_PLATFORM_LIBRARY(AusweisAppWebSocket) - - TARGET_LINK_LIBRARIES(AusweisAppWebSocket Qt5::Core Qt5::WebSockets AusweisAppJsonApi AusweisAppGlobal) - TARGET_COMPILE_DEFINITIONS(AusweisAppWebSocket PRIVATE QT_STATICPLUGIN) -ENDIF() diff --git a/src/websocket/UIPlugInWebSocket.cpp b/src/websocket/UIPlugInWebSocket.cpp deleted file mode 100644 index aa838eb..0000000 --- a/src/websocket/UIPlugInWebSocket.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/*! - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "UIPlugInWebSocket.h" - -#include "view/UILoader.h" - -#include -#include -#include -#include - - -Q_DECLARE_LOGGING_CATEGORY(websocket) - -using namespace governikus; - - -quint16 UIPlugInWebSocket::cWebSocketPort = UIPlugInWebSocket::WEBSOCKET_DEFAULT_PORT; - - -UIPlugInWebSocket::UIPlugInWebSocket() - : UIPlugIn() - , mServer(QCoreApplication::applicationName() + QLatin1Char('/') + QCoreApplication::applicationVersion(), QWebSocketServer::NonSecureMode) - , mConnection(nullptr) - , mJsonApi(nullptr) - , mContext() -{ - if (!UILoader::getInstance().load(UIPlugInName::UIPlugInJsonApi)) - { - qWarning(websocket) << "Cannot start WebSocket because JSON-API is missing"; - return; - } - - mJsonApi = qobject_cast(UILoader::getInstance().getLoaded(UIPlugInName::UIPlugInJsonApi)); - Q_ASSERT(mJsonApi); - connect(&mServer, &QWebSocketServer::newConnection, this, &UIPlugInWebSocket::onNewConnection); - qDebug(websocket) << "Starting WebSocket..." << mServer.listen(QHostAddress::LocalHost, cWebSocketPort); - if (!mServer.isListening()) - { - qCritical(websocket) << mServer.errorString(); - return; - } - - const quint16 port = mServer.serverPort(); - qDebug(websocket) << "Listening on port" << port; - -#ifndef QT_NO_DEBUG - if (cWebSocketPort == 0) - { - const QString path = WEBSOCKET_PORT_FILENAME(QCoreApplication::applicationPid()); - QFile file(path); - if (file.open(QIODevice::WriteOnly)) - { - QTextStream(&file) << port; - qDebug(websocket) << "Stored port number to info file" << path; - } - else - { - qCritical(websocket) << "Failed to store port number to info file" << path; - } - } -#endif - -} - - -UIPlugInWebSocket::~UIPlugInWebSocket() -{ -} - - -void UIPlugInWebSocket::setPort(quint16 pPort) -{ - cWebSocketPort = pPort; -} - - -quint16 UIPlugInWebSocket::getPort() -{ - return cWebSocketPort; -} - - -void UIPlugInWebSocket::onWorkflowStarted(QSharedPointer pContext) -{ - mContext = pContext; -} - - -void UIPlugInWebSocket::onWorkflowFinished(QSharedPointer pContext) -{ - Q_UNUSED(pContext); - - mContext.clear(); -} - - -void UIPlugInWebSocket::onNewConnection() -{ - if (mServer.hasPendingConnections()) - { - auto connection = mServer.nextPendingConnection(); - if (mConnection) - { - qDebug(websocket) << "Client is already connected..."; - connection->close(QWebSocketProtocol::CloseCodePolicyViolated); - return; - } - mConnection.reset(connection); - - connect(mConnection.data(), &QWebSocket::disconnected, this, &UIPlugInWebSocket::onClientDisconnected); - connect(mConnection.data(), &QWebSocket::textMessageReceived, this, &UIPlugInWebSocket::onTextMessageReceived); - connect(mJsonApi, &UIPlugInJsonApi::fireMessage, this, &UIPlugInWebSocket::onJsonApiMessage); - } -} - - -void UIPlugInWebSocket::onClientDisconnected() -{ - qDebug(websocket) << "Client disconnected..."; - - if (mContext) - { - const QSignalBlocker blocker(mJsonApi); - Q_EMIT mContext->fireCancelWorkflow(); - } - - mConnection.reset(); - disconnect(mJsonApi, &UIPlugInJsonApi::fireMessage, this, &UIPlugInWebSocket::onJsonApiMessage); -} - - -void UIPlugInWebSocket::onTextMessageReceived(const QString& pMessage) -{ - if (mConnection) - { - mJsonApi->doMessageProcessing(pMessage.toUtf8()); - } -} - - -void UIPlugInWebSocket::onJsonApiMessage(const QByteArray& pMessage) -{ - if (mConnection) - { - mConnection->sendTextMessage(QString::fromUtf8(pMessage)); - } -} - - -void UIPlugInWebSocket::doShutdown() -{ - if (mConnection) - { - mConnection->close(QWebSocketProtocol::CloseCodeGoingAway); - } -} diff --git a/src/websocket/UIPlugInWebSocket.h b/src/websocket/UIPlugInWebSocket.h deleted file mode 100644 index 02e845b..0000000 --- a/src/websocket/UIPlugInWebSocket.h +++ /dev/null @@ -1,57 +0,0 @@ -/*! - * \brief UIPlugIn implementation of the Websocket. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "UIPlugInJsonApi.h" -#include "view/UIPlugIn.h" - -#include -#include -#include -#include - -namespace governikus -{ - -#define WEBSOCKET_PORT_FILENAME(PID) (QDir::tempPath() + QDir::separator() + QStringLiteral("web_socket_port-") + QString::number(PID)) - -class UIPlugInWebSocket - : public UIPlugIn -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID "governikus.UIPlugIn" FILE "metadata.json") - Q_INTERFACES(governikus::UIPlugIn) - - private: - QWebSocketServer mServer; - QScopedPointer mConnection; - UIPlugInJsonApi* mJsonApi; - QSharedPointer mContext; - - static quint16 cWebSocketPort; - - private Q_SLOTS: - virtual void doShutdown() override; - virtual void onWorkflowStarted(QSharedPointer pContext) override; - virtual void onWorkflowFinished(QSharedPointer pContext) override; - void onNewConnection(); - void onClientDisconnected(); - void onTextMessageReceived(const QString& pMessage); - - void onJsonApiMessage(const QByteArray& pMessage); - - public: - UIPlugInWebSocket(); - virtual ~UIPlugInWebSocket() override; - - static void setPort(quint16 pPort); - static quint16 getPort(); - - static const quint16 WEBSOCKET_DEFAULT_PORT = 14727; -}; - -} /* namespace governikus */ diff --git a/src/whitelist_client/CMakeLists.txt b/src/whitelist_client/CMakeLists.txt new file mode 100644 index 0000000..a3c91fd --- /dev/null +++ b/src/whitelist_client/CMakeLists.txt @@ -0,0 +1,9 @@ +######################################################################## +# The module whitelist client is responsible for collecting information +# about the NFC interface on an Android device and sending them to the +# whitelist server. +####################################################################### + +ADD_PLATFORM_LIBRARY(AusweisAppWhitelistClient) + +TARGET_LINK_LIBRARIES(AusweisAppWhitelistClient Qt5::Core AusweisAppSettings AusweisAppNetwork) diff --git a/src/whitelist_client/Survey.cpp b/src/whitelist_client/Survey.cpp new file mode 100644 index 0000000..a49408d --- /dev/null +++ b/src/whitelist_client/Survey.cpp @@ -0,0 +1,102 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "Survey.h" + +#ifndef QT_NO_DEBUG +#include +#endif + +#include +#include + + +using namespace governikus; + + +#define VALUE_NAME(_name, _key)\ + inline QLatin1String _name(){\ + return QLatin1String(_key);\ + } + + +namespace +{ +VALUE_NAME(ROM, "Rom") +VALUE_NAME(BUILD_NUMBER, "BuildNumber") +VALUE_NAME(ANDROID_VERSION, "AndroidVersion") +VALUE_NAME(KERNEL_VERSION, "KernelVersion") +VALUE_NAME(MAXIMUM_NFC_PACKET_LENGTH, "MaximumNfcPacketLength") +VALUE_NAME(VENDOR, "Vendor") +VALUE_NAME(MODEL_NUMBER, "ModelNumber") +VALUE_NAME(MODEL_NAME, "ModelName") +VALUE_NAME(AUSWEISAPP_VERSION_NUMBER, "AusweisAppVersionNumber") +} // namespace + + +Survey::Survey(const QString& pBuildNumber, + const QString& pAndroidVersion, + const QString& pKernelVersion, + int pMaximumNfcPacketLength, + const QString& pVendor, + const QString& pModelNumber, + const QString& pModelName, + const QString& pAusweisAppVersionNumber) + : mNull(false) + , mBuildNumber(pBuildNumber) + , mAndroidVersion(pAndroidVersion) + , mKernelVersion(pKernelVersion) + , mMaximumNfcPacketLength(pMaximumNfcPacketLength) + , mVendor(pVendor) + , mModelNumber(pModelNumber) + , mModelName(pModelName) + , mAusweisAppVersionNumber(pAusweisAppVersionNumber) +{ +} + + +Survey::Survey() + : mNull(true) + , mBuildNumber() + , mAndroidVersion() + , mKernelVersion() + , mMaximumNfcPacketLength(0) + , mVendor() + , mModelNumber() + , mModelName() + , mAusweisAppVersionNumber() +{ +} + + +QByteArray Survey::toJsonByteArray() const +{ + QJsonObject rom; + rom[BUILD_NUMBER()] = mBuildNumber; + rom[ANDROID_VERSION()] = mAndroidVersion; + rom[KERNEL_VERSION()] = mKernelVersion; + rom[MAXIMUM_NFC_PACKET_LENGTH()] = QJsonValue(mMaximumNfcPacketLength); + + QJsonObject mainObject; + mainObject[ROM()] = rom; + mainObject[VENDOR()] = mVendor; + mainObject[MODEL_NUMBER()] = mModelNumber; + mainObject[MODEL_NAME()] = mModelName; + mainObject[AUSWEISAPP_VERSION_NUMBER()] = mAusweisAppVersionNumber; + +#ifndef QT_NO_DEBUG + if (QCoreApplication::applicationName().startsWith(QLatin1String("Test"))) + { + return QJsonDocument(mainObject).toJson(QJsonDocument::Indented); + } +#endif + + return QJsonDocument(mainObject).toJson(QJsonDocument::Compact); +} + + +bool Survey::isNull() const +{ + return mNull; +} diff --git a/src/whitelist_client/Survey.h b/src/whitelist_client/Survey.h new file mode 100644 index 0000000..71def0e --- /dev/null +++ b/src/whitelist_client/Survey.h @@ -0,0 +1,43 @@ +/*! + * \brief Class holding information about an Android device to be sent to + * the whitelist server. + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include + +namespace governikus +{ +class Survey +{ + private: + const bool mNull; + const QString mBuildNumber; + const QString mAndroidVersion; + const QString mKernelVersion; + const int mMaximumNfcPacketLength; + const QString mVendor; + const QString mModelNumber; + const QString mModelName; + const QString mAusweisAppVersionNumber; + + public: + Survey(const QString& pBuildNumber, + const QString& pAndroidVersion, + const QString& pKernelVersion, + int pMaximumNfcPacketLength, + const QString& pVendor, + const QString& pModelNumber, + const QString& pModelName, + const QString& pAusweisAppVersionNumber); + + Survey(); + + QByteArray toJsonByteArray() const; + bool isNull() const; +}; + +} // namespace governikus diff --git a/src/whitelist_client/SurveyHandler.cpp b/src/whitelist_client/SurveyHandler.cpp new file mode 100644 index 0000000..a414ee1 --- /dev/null +++ b/src/whitelist_client/SurveyHandler.cpp @@ -0,0 +1,67 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "SurveyHandler.h" + +#include "DeviceInfo.h" +#include "Env.h" +#include "NetworkManager.h" +#include "SecureStorage.h" + +#include +#include +#include +#include +#include + +using namespace governikus; + + +void SurveyHandler::transmitSurvey(const Survey& pSurvey) const +{ + const QUrl whitelistServerBaseUrl = SecureStorage::getInstance().getWhitelistServerBaseUrl(); + const QUrl postSurveyUrl(whitelistServerBaseUrl.toString() + QStringLiteral("/new")); + QNetworkRequest request(postSurveyUrl); + request.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("application/json; charset=UTF-8")); + + const QByteArray jsonData = pSurvey.toJsonByteArray(); + QNetworkReply* reply = Env::getSingleton()->post(request, jsonData); + QObject::connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); + + qDebug().noquote() << "Sent survey to whitelist server:" << jsonData; +} + + +SurveyHandler::SurveyHandler() +{ +} + + +void SurveyHandler::sendSurvey(int pMaximumNfcPackageLength) const +{ + const Survey survey = createSurvey(pMaximumNfcPackageLength); + + if (survey.isNull()) + { + qCritical() << "Cannot send survey containing no information at all"; + + return; + } + + transmitSurvey(survey); +} + + +Survey SurveyHandler::createSurvey(int pMaximumNfcPackageLength) const +{ + return Survey( + DeviceInfo::getOSBuildNumber(), + DeviceInfo::getOSVersion(), + DeviceInfo::getKernelVersion(), + pMaximumNfcPackageLength, + DeviceInfo::getVendor(), + DeviceInfo::getModelNumber(), + DeviceInfo::getModelName(), + QCoreApplication::applicationVersion()); +} diff --git a/src/whitelist_client/SurveyHandler.h b/src/whitelist_client/SurveyHandler.h new file mode 100644 index 0000000..94ccc87 --- /dev/null +++ b/src/whitelist_client/SurveyHandler.h @@ -0,0 +1,26 @@ +/*! + * \brief Class holding information about an Android device to be sent to + * the whitelist server. + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "Survey.h" + +namespace governikus +{ +class SurveyHandler +{ + private: + Survey createSurvey(int pMaximumNfcPackageLength) const; + void transmitSurvey(const Survey& pSurvey) const; + + public: + SurveyHandler(); + + void sendSurvey(int pMaximumNfcPackageLength) const; +}; + +} // namespace governikus diff --git a/src/widget/AboutDialog.cpp b/src/widget/AboutDialog.cpp deleted file mode 100644 index c13087c..0000000 --- a/src/widget/AboutDialog.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "AboutDialog.h" -#include "ui_AboutDialog.h" - -#include "AppSettings.h" -#include "BuildHelper.h" -#include "Env.h" -#include "SecureStorage.h" -#include "VersionNumber.h" - -using namespace governikus; - - -AboutDialog::AboutDialog(QWidget* pParent) - : QDialog(pParent) - , mUi(new Ui::AboutDialog) -{ - mUi->setupUi(this); - - setWindowFlags(Qt::Dialog | Qt::WindowTitleHint | Qt::WindowCloseButtonHint); - layout()->setSizeConstraint(QLayout::SetFixedSize); // For platform != Windows: Disable maximize button - setWindowModality(Qt::WindowModal); // For platform == macOS: Make dialog slide in from the top - - const SecureStorage& storage = SecureStorage::getInstance(); - const auto& url = VersionNumber::getApplicationVersion().isDeveloperVersion() ? storage.getAppcastBetaUpdateUrl() : storage.getAppcastUpdateUrl(); - const auto& releaseNotes = url.adjusted(QUrl::RemoveFilename).toString() + QStringLiteral("ReleaseNotes.html"); - - setWindowTitle(tr("About %1 - %2").arg(QCoreApplication::applicationName(), QCoreApplication::organizationName())); - - mUi->lblFurtherInformation->setText(QStringLiteral("%1: https://www.ausweisapp.bund.de/") - .arg(tr("Further information"))); - - mUi->lblReleaseNotes->setText(tr("The current release notes can be found %1 here.%2") - .arg(QStringLiteral("").arg(releaseNotes), QStringLiteral(""))); - - mUi->lblVersion->setText(QStringLiteral("%1: %2 (%3)").arg(tr("Version"), QApplication::applicationVersion(), QString::fromLatin1(BuildHelper::getDateTime()))); - - mUi->lblDeveloperModeWarning->setText(QStringLiteral("

%1

") - .arg(tr("The developer mode is aimed at integrators / developers for new service applications." - " For this reason, the developer mode works only in the test PKI." - " By activating the developer mode, some safety tests are deactivated." - " This means that the authentication process continues although the AusweisApp2 would usually abort the process with an error message when used in normal operation mode." - " Information on the disregarded error in the developer mode is displayed in the attached window below the AusweisApp2."))); - - const QIcon icon = windowIcon(); - const QSize size = icon.actualSize(QSize(64, 64)); - mUi->imgAusweisApp2->setPixmap(icon.pixmap(size)); - - connect(mUi->btnOkay, &QPushButton::clicked, this, &QDialog::accept); - connect(this, &QDialog::accepted, this, &AboutDialog::onAccept); - - mUi->chkbDeveloperMode->setCheckState(AppSettings::getInstance().getGeneralSettings().isDeveloperMode() ? Qt::Checked : Qt::Unchecked); - - connect(mUi->chkbDeveloperMode, &QCheckBox::stateChanged, this, &AboutDialog::onCheckboxStateChanged); - onCheckboxStateChanged(); -} - - -AboutDialog::~AboutDialog() -{ -} - - -void AboutDialog::onCheckboxStateChanged() -{ - const bool developerModeActivated = mUi->chkbDeveloperMode->checkState() == Qt::Checked; - mUi->lblDeveloperModeWarning->setVisible(developerModeActivated); - resize(minimumSize()); - adjustSize(); -} - - -void AboutDialog::onAccept() -{ - const bool developerModeActivated = mUi->chkbDeveloperMode->checkState() == Qt::Checked; - GeneralSettings& generalSettings = AppSettings::getInstance().getGeneralSettings(); - if (generalSettings.isDeveloperMode() != developerModeActivated) - { - generalSettings.setDeveloperMode(developerModeActivated); - generalSettings.save(); - } -} - - -void AboutDialog::changeEvent(QEvent* pEvent) -{ - if (pEvent->type() == QEvent::LanguageChange) - { - mUi->retranslateUi(this); - } - QWidget::changeEvent(pEvent); -} diff --git a/src/widget/AboutDialog.h b/src/widget/AboutDialog.h deleted file mode 100644 index ff41afe..0000000 --- a/src/widget/AboutDialog.h +++ /dev/null @@ -1,40 +0,0 @@ -/*! - * \brief Dialog to display information about the application - * - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include - -namespace Ui -{ -class AboutDialog; -} - -namespace governikus -{ - -class AboutDialog - : public QDialog -{ - Q_OBJECT - - private: - QScopedPointer mUi; - - private Q_SLOTS: - void onCheckboxStateChanged(); - void onAccept(); - - protected: - virtual void changeEvent(QEvent* pEvent) override; - - public: - AboutDialog(QWidget* pParent = nullptr); - virtual ~AboutDialog() override; -}; - -} /* namespace governikus */ diff --git a/src/widget/AppQtGui.cpp b/src/widget/AppQtGui.cpp deleted file mode 100644 index cc12a74..0000000 --- a/src/widget/AppQtGui.cpp +++ /dev/null @@ -1,645 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "AppQtGui.h" - -#include "AppSettings.h" -#include "CredentialDialog.h" -#include "DiagnosisGui.h" -#include "Env.h" -#include "generic/HelpAction.h" -#include "GuiProfile.h" -#include "NetworkManager.h" -#include "ReaderManager.h" -#include "RemoteClient.h" -#include "Service.h" -#include "SetupAssistantGui.h" -#include "UpdateWindow.h" -#include "workflow/WorkflowAuthenticateQtGui.h" -#include "workflow/WorkflowChangePinQtGui.h" -#include "workflow/WorkflowGui.h" -#include "workflow/WorkflowSelfInfoQtGui.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef QT_NO_NETWORKPROXY - #include - #include -#endif - - -#if defined(Q_OS_MACOS) -#import -#endif - - -using namespace governikus; - - -Q_DECLARE_LOGGING_CATEGORY(gui) - - -AppQtGui::AppQtGui() - : QObject() - , mMainWidget(nullptr) - , mIcon(QStringLiteral(":/images/npa.svg")) - , mTrayIcon(nullptr) - , mActiveWorkflowUi() - , mSetupAssistantGui(nullptr) - , mDiagnosisGui(nullptr) - , mUpdateInfo(nullptr) - , mCertificateInfo(nullptr) - , mAggressiveToForeground(false) -{ - loadStyleSheet(); - - mMainWidget = new AppQtMainWidget(); - mMainWidget->setWindowIcon(mIcon); - - mUpdateInfo = new QMessageBox(mMainWidget); - mUpdateInfo->setWindowTitle(QApplication::applicationName() + QStringLiteral(" - ") + tr("Updates")); - mUpdateInfo->setWindowIcon(QIcon(QStringLiteral(":/images/npa.svg"))); - mUpdateInfo->setWindowModality(Qt::WindowModal); - mUpdateInfo->setStandardButtons(QMessageBox::Ok); - mUpdateInfo->button(QMessageBox::Ok)->setFocus(); - - mCertificateInfo = new QMessageBox(mMainWidget); - mCertificateInfo->setWindowModality(Qt::ApplicationModal); - mCertificateInfo->setWindowFlags(mCertificateInfo->windowFlags() & ~Qt::WindowContextHelpButtonHint); - mCertificateInfo->setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("Information")); - mCertificateInfo->setIconPixmap(mMainWidget->windowIcon().pixmap(QSize(48, 48))); - mCertificateInfo->setStandardButtons(QMessageBox::Ok); - mCertificateInfo->button(QMessageBox::Ok)->setFocus(); - - Service* service = Env::getSingleton(); - connect(service, &Service::fireAppUpdateFinished, this, &AppQtGui::onAppUpdateReady); - connect(service, &Service::fireUpdateScheduled, this, &AppQtGui::onUpdateScheduled); -} - - -AppQtGui::~AppQtGui() -{ - if (mTrayIcon != nullptr) - { - QMenu* menu = mTrayIcon->contextMenu(); - if (menu != nullptr) - { - qDeleteAll(menu->actions()); - delete menu; - } - } - - delete mUpdateInfo; - delete mCertificateInfo; - delete mMainWidget; -} - - -void AppQtGui::init() -{ - connect(mMainWidget, &AppQtMainWidget::fireChangePinRequested, this, &AppQtGui::fireChangePinRequested); - connect(mMainWidget, &AppQtMainWidget::fireSetupAssistantWizardRequest, this, &AppQtGui::onSetupAssistantWizardRequest); - connect(mMainWidget, &AppQtMainWidget::fireDiagnosisRequested, this, &AppQtGui::onDiagnosisRequested); - connect(mMainWidget, &AppQtMainWidget::fireCloseWindowRequested, this, &AppQtGui::onCloseWindowRequested); - connect(mMainWidget, &AppQtMainWidget::fireSelfAuthenticationRequested, this, &AppQtGui::selfAuthenticationRequested); - connect(mMainWidget, &AppQtMainWidget::fireQuitApplicationRequested, this, &AppQtGui::quitApplicationRequested); - connect(mMainWidget, &AppQtMainWidget::fireChangeHighContrast, this, &AppQtGui::onChangeHighContrast); -} - - -void AppQtGui::onApplicationStarted() -{ - if (QSystemTrayIcon::isSystemTrayAvailable()) - { - createTrayIcon(); - } - - if (mTrayIcon != nullptr) - { - mTrayIcon->show(); - mTrayIcon->showMessage(QString(), tr("AusweisApp2 was started."), mIcon, 3000); - } - - if (!QSystemTrayIcon::isSystemTrayAvailable() - || GuiProfile::getProfile().getShowWindow() - || AppSettings::getInstance().getGeneralSettings().isShowSetupAssistant() - || AppSettings::getInstance().getGeneralSettings().isDeveloperMode()) - { - QMetaObject::invokeMethod(this, "show", Qt::QueuedConnection); - } - - if (AppSettings::getInstance().getGeneralSettings().isShowSetupAssistant()) - { - mMainWidget->setSelectedTab(nullptr); - QMetaObject::invokeMethod(this, "onSetupAssistantWizardRequest", Qt::QueuedConnection); - - // just show the setup assistant once - AppSettings::getInstance().getGeneralSettings().setShowSetupAssistant(false); - AppSettings::getInstance().getGeneralSettings().save(); - } - - if (AppSettings::getInstance().getGeneralSettings().isDeveloperMode()) - { - QMetaObject::invokeMethod(this, "onDeveloperModeQuestion", Qt::QueuedConnection); - } - - const QSharedPointer& remoteClient = Env::getSingleton()->getRemoteClient(); - connect(remoteClient.data(), &RemoteClient::fireCertificateRemoved, this, &AppQtGui::onCertificateRemoved); -} - - -QSharedPointer AppQtGui::createWorkflowAuthenticateUi(const QSharedPointer& pContext) -{ - const QSharedPointer gui(new WorkflowAuthenticateQtGui(pContext, mMainWidget)); - connect(gui.data(), &WorkflowGui::fireSwitchToReaderSettingsRequested, this, &AppQtGui::onSwitchToReaderSettingsRequested); - - return gui; -} - - -QSharedPointer AppQtGui::createWorkflowChangePinUi(const QSharedPointer& pContext) -{ - return QSharedPointer(new WorkflowChangePinQtGui(pContext, mMainWidget)); -} - - -QSharedPointer AppQtGui::createWorkflowSelfInfoUi(const QSharedPointer& pContext) -{ - const QSharedPointer gui(new WorkflowSelfInfoQtGui(pContext, mMainWidget)); - connect(gui.data(), &WorkflowGui::fireSwitchToReaderSettingsRequested, this, &AppQtGui::onSwitchToReaderSettingsRequested); - - return gui; -} - - -void AppQtGui::onSwitchToReaderSettingsRequested() -{ - mMainWidget->setHideWindowAfterWorkflow(false); - - Q_EMIT fireSwitchToReaderSettingsRequested(); -} - - -void AppQtGui::activateWorkflowUi(QSharedPointer pWorkflowUi, bool pAllowHideAfterWorkflow) -{ - if (pWorkflowUi) - { - mActiveWorkflowUi = pWorkflowUi; - mActiveWorkflowUi->activate(); - } - - mMainWidget->activateMenuBarItems(false); - closeDialogs(); - -#ifdef Q_OS_WIN - mAggressiveToForeground = mActiveWorkflowUi.objectCast(); -#endif - bool hideAfterWorkflow = pAllowHideAfterWorkflow - && mActiveWorkflowUi.objectCast() - && AppSettings::getInstance().getGeneralSettings().isAutoCloseWindowAfterAuthentication(); - mMainWidget->setHideWindowAfterWorkflow(hideAfterWorkflow); - show(); -} - - -void AppQtGui::deactivateCurrentWorkflowUi() -{ - if (mMainWidget->isHideWindowAfterWorkflow()) - { - hideFromTaskbar(); - mMainWidget->hide(); - } - mMainWidget->activateMenuBarItems(true); - - if (mActiveWorkflowUi) - { - mActiveWorkflowUi->deactivate(); - mActiveWorkflowUi.clear(); - } -} - - -void AppQtGui::onShowUserInformation(const QString& pInformationMessage) -{ - QMessageBox msgBox(mMainWidget); - msgBox.setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("Information")); - msgBox.setIcon(QMessageBox::Information); - msgBox.setText(pInformationMessage); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.button(QMessageBox::Ok)->setFocus(); - msgBox.exec(); -} - - -void AppQtGui::onSetupAssistantWizardRequest() -{ - Env::getSingleton()->runUpdateIfNeeded(); - - if (!mSetupAssistantGui) - { - mSetupAssistantGui = new SetupAssistantGui(mMainWidget); - connect(mSetupAssistantGui, &SetupAssistantGui::fireChangePinButtonClicked, mMainWidget, &AppQtMainWidget::onChangePinButtonClicked); - } - - bool stopRemoteScan = false; - if (!mMainWidget->remoteScanRunning()) - { - Env::getSingleton()->startScanAll(false); - stopRemoteScan = true; - } - mSetupAssistantGui->activate(); - if (stopRemoteScan) - { - Env::getSingleton()->stopScanAll(); - } -} - - -void AppQtGui::onDeveloperModeQuestion() -{ - QMessageBox msgBox(mMainWidget); - msgBox.setWindowModality(Qt::WindowModal); - msgBox.setWindowFlags(msgBox.windowFlags() & ~Qt::WindowContextHelpButtonHint); - msgBox.setIconPixmap(mMainWidget->windowIcon().pixmap(QSize(48, 48))); - msgBox.setText(tr("The developer mode is enabled.")); - msgBox.setInformativeText(tr("Do you want to disable the developer mode?")); - msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); - msgBox.setDefaultButton(QMessageBox::Yes); - msgBox.button(QMessageBox::Yes)->setFocus(); - - if (msgBox.exec() == QMessageBox::Yes) - { - AppSettings::getInstance().getGeneralSettings().setDeveloperMode(false); - AppSettings::getInstance().getGeneralSettings().save(); - } -} - - -void AppQtGui::onDiagnosisRequested() -{ - if (!mDiagnosisGui) - { - mDiagnosisGui = new DiagnosisGui(mMainWidget); - } - mDiagnosisGui->activate(); -} - - -bool AppQtGui::askChangeTransportPinNow() -{ - GeneralSettings& settings = AppSettings::getInstance().getGeneralSettings(); - if (!settings.isTransportPinReminder()) - { - return false; - } - settings.setTransportPinReminder(false); - settings.save(); - - show(UiModule::PINMANAGEMENT); - closeDialogs(); - - QMessageBox messageBox(mMainWidget); - messageBox.setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("Information")); - messageBox.setWindowModality(Qt::WindowModal); - messageBox.setWindowFlags(messageBox.windowFlags() & ~Qt::WindowContextHelpButtonHint); - messageBox.setText(tr("Did you change the initial transport PIN already?

Prior to the first use of the online identification function you have to replace the transport PIN by an individual 6-digit PIN. Online identification with transport PIN is not possible.")); - messageBox.setStandardButtons(QMessageBox::Yes); - messageBox.button(QMessageBox::Yes)->setFocus(); - auto changePinButton = messageBox.addButton(tr("No, change transport PIN now"), QMessageBox::NoRole); - messageBox.exec(); - - if (messageBox.clickedButton() != changePinButton) - { - show(UiModule::IDENTIFY); - return false; - } - - return true; -} - - -void AppQtGui::switchToReaderSettings() -{ - mMainWidget->switchToGuiModule(GuiModule::DEVICE_SETTINGS); -} - - -bool AppQtGui::eventFilter(QObject* /*pObject*/, QEvent* pEvent) -{ - if (pEvent->type() == QEvent::KeyPress) - { - QKeyEvent* keyEvent = static_cast(pEvent); - if (keyEvent->key() == Qt::Key_F1) - { - HelpAction::openContextHelp(); - return true; - } - - if (keyEvent->key() == Qt::BackButton || keyEvent->key() == Qt::Key_Back) - { - Q_EMIT quitApplicationRequested(); - return true; - } - } - return false; -} - - -void AppQtGui::loadStyleSheet() -{ - const auto& styleSheetName = QStringLiteral(":/stylesheets/desktop.qss"); - qCDebug(gui) << "loading style sheet" << styleSheetName; - - QFile file(styleSheetName); - if (!file.open(QIODevice::ReadOnly)) - { - qCWarning(gui) << "Failed to read style sheet"; - return; - } - - qApp->setStyleSheet(QString::fromLatin1(file.readAll())); -} - - -void AppQtGui::onChangeHighContrast(bool* pHighContrastOn) -{ - if (*pHighContrastOn) - { - qApp->setStyleSheet(QString()); - } - else - { - loadStyleSheet(); - } -} - - -void AppQtGui::createTrayIcon() -{ - QMenu* trayIconMenu = new QMenu(nullptr); - -#if defined(Q_OS_MACOS) - QAction* showApplicationAction = new QAction(tr("Open"), trayIconMenu); - connect(showApplicationAction, &QAction::triggered, this, [this] { - AppQtGui::show(); - }); -#endif - - QAction* quitAction = new QAction(tr("Exit AusweisApp2"), trayIconMenu); - connect(quitAction, &QAction::triggered, this, &AppQtGui::quitApplicationRequested); - -#if defined(Q_OS_MACOS) - trayIconMenu->addAction(showApplicationAction); - trayIconMenu->addSeparator(); -#endif - trayIconMenu->addAction(quitAction); - - mTrayIcon = new QSystemTrayIcon(mIcon, mMainWidget); - connect(mTrayIcon, &QSystemTrayIcon::activated, this, &AppQtGui::onActivated); - connect(mTrayIcon, &QSystemTrayIcon::messageClicked, this, [this] { - show(UiModule::CURRENT); - }); - - mTrayIcon->setContextMenu(trayIconMenu); - mTrayIcon->setToolTip(QCoreApplication::applicationName()); -} - - -void AppQtGui::closeDialogs() -{ - if (mSetupAssistantGui) - { - mSetupAssistantGui->deactivate(); - } - if (mDiagnosisGui) - { - mDiagnosisGui->deactivate(); - } -} - - -void AppQtGui::hideFromTaskbar() -{ -#if defined(Q_OS_MACOS) - ProcessSerialNumber psn = { - 0, kCurrentProcess - }; - TransformProcessType(&psn, kProcessTransformToBackgroundApplication); -#endif -} - - -void AppQtGui::restoreToTaskbar() -{ -#if defined(Q_OS_MACOS) - ProcessSerialNumber psn = { - 0, kCurrentProcess - }; - TransformProcessType(&psn, kProcessTransformToForegroundApplication); - [NSApp activateIgnoringOtherApps: YES]; -#endif -} - - -void AppQtGui::onActivated(QSystemTrayIcon::ActivationReason pReason) -{ -#ifdef Q_OS_MACOS - Q_UNUSED(pReason) -#else - if (pReason == QSystemTrayIcon::Trigger) - { - show(); - } -#endif -} - - -void AppQtGui::onCloseWindowRequested(bool* pDoClose) -{ - if (mMainWidget->isRemindUserToClose()) - { - QMessageBox messageBox(mMainWidget); - messageBox.installEventFilter(this); - messageBox.setWindowTitle(QApplication::applicationName() + QStringLiteral(" - ") + tr("Information")); - messageBox.setWindowModality(Qt::WindowModal); - messageBox.setIcon(QMessageBox::Information); - messageBox.setWindowFlags(messageBox.windowFlags() & ~Qt::WindowContextHelpButtonHint); - messageBox.setText(tr("The user interface of the %1 is closed.").arg(QApplication::applicationName())); - messageBox.setInformativeText(tr("The program remains available via the icon in the system tray. Click on the %1 icon to reopen the user interface.").arg(QApplication::applicationName())); - messageBox.setCheckBox(new QCheckBox(tr("Do not show this dialog again."))); - messageBox.setStandardButtons(QMessageBox::Ok); - messageBox.button(QMessageBox::Ok)->setFocus(); - messageBox.exec(); - - Q_EMIT fireCloseReminderFinished(messageBox.checkBox()->isChecked()); - } - - mMainWidget->onCloseWindowRequested(); - - if (mActiveWorkflowUi == nullptr) - { - *pDoClose = true; - } - else if ((*pDoClose = mActiveWorkflowUi->verifyAbortWorkflow())) - { - Q_EMIT mActiveWorkflowUi->fireUserCancelled(); - } - - if (*pDoClose) - { - hideFromTaskbar(); - } -} - - -#ifndef QT_NO_NETWORKPROXY -void AppQtGui::onProxyAuthenticationRequired(const QNetworkProxy& pProxy, QAuthenticator* pAuthenticator) -{ - CredentialDialog dialog(mMainWidget); - dialog.setUser(pProxy.user()); - - if (dialog.exec() == QDialog::Accepted) - { - pAuthenticator->setUser(dialog.getUser()); - pAuthenticator->setPassword(dialog.getPassword()); - } -} - - -#endif - - -void AppQtGui::show(UiModule pModule) -{ - if (!mActiveWorkflowUi.isNull()) - { - pModule = UiModule::CURRENT; - } - - switch (pModule) - { - case UiModule::PINMANAGEMENT: - mMainWidget->switchToGuiModule(GuiModule::PIN_SETTINGS); - break; - - case UiModule::SETTINGS: - mMainWidget->switchToGuiModule(GuiModule::GENERAL_SETTINGS); - break; - - case UiModule::IDENTIFY: - mMainWidget->switchToGuiModule(GuiModule::IDENTIFY); - break; - - - case UiModule::DEFAULT: - mMainWidget->switchToGuiModule(GuiModule::START_PAGE); - break; - - case UiModule::CURRENT: - // don't switch the module, just show the current one - break; - } - - restoreToTaskbar(); - - if (mMainWidget->isMinimized()) - { - mMainWidget->showNormal(); - } - - // Ensure the window's minimumSizeHint is respected (work-around for a Windows Qt bug). - if (!mMainWidget->isMaximized()) - { - QSize size = mMainWidget->size(); - size = size.expandedTo(mMainWidget->minimumSizeHint()); - mMainWidget->resize(size); - } - -#ifdef Q_OS_WIN - if (mAggressiveToForeground) - { - // Changing the window flags seems to be the only way to - // bring the window to the foreground if it is not minimized. - const Qt::WindowFlags flags = mMainWidget->windowFlags(); - mMainWidget->setWindowFlags(flags | Qt::WindowStaysOnTopHint); - mMainWidget->show(); - mMainWidget->setWindowFlags(flags); - - mAggressiveToForeground = false; - } -#endif - - mMainWidget->show(); - mMainWidget->activateWindow(); - - // Work-around for the window not being brought to the foreground. Invoke - // activateWindow() again after the events currently pending in the event - // queue have been processed. This appears to do the job. Note that the - // first activateWindow() above is apparently still necessary. - QCoreApplication::processEvents(); - mMainWidget->activateWindow(); - - Env::getSingleton()->runUpdateIfNeeded(); -} - - -void AppQtGui::onAppUpdateReady(bool pSuccess, const GlobalStatus& pError) -{ - if (pError.isError()) - { - mUpdateInfo->setIcon(QMessageBox::Critical); - mUpdateInfo->setText(pError.toErrorDescription()); - mUpdateInfo->exec(); - } - else if (pSuccess) - { - const auto updateWindow = new UpdateWindow(mMainWidget); - connect(updateWindow, &UpdateWindow::fireShowUpdateDialog, this, - [this](QMessageBox::Icon pIcon, const QString& pMsg) - { - mUpdateInfo->setIcon(pIcon); - mUpdateInfo->setText(pMsg); - mUpdateInfo->exec(); - } - ); - } - else - { - mUpdateInfo->setIcon(QMessageBox::Information); - mUpdateInfo->setText(tr("Your software is up to date.")); - mUpdateInfo->exec(); - } -} - - -void AppQtGui::onUpdateScheduled() -{ - if (!mMainWidget->isHidden()) - { - Env::getSingleton()->runUpdateIfNeeded(); - } -} - - -void AppQtGui::onCertificateRemoved(QString pDeviceName) -{ - mCertificateInfo->setText(tr("The device \"%1\" was unpaired because it does not react to connection attempts. Retry the pairing process if you want to use this device to authenticate yourself.").arg(pDeviceName)); - mCertificateInfo->show(); -} - - -void AppQtGui::shutdown() -{ - if (mTrayIcon != nullptr) - { - mTrayIcon->hide(); - } -} diff --git a/src/widget/AppQtGui.h b/src/widget/AppQtGui.h deleted file mode 100644 index 3c92c04..0000000 --- a/src/widget/AppQtGui.h +++ /dev/null @@ -1,106 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "ActivationHandler.h" -#include "GlobalStatus.h" - -#include -#include - - -#ifndef QT_NO_NETWORKPROXY -class QNetworkProxy; -class QAuthenticator; -#endif - -namespace governikus -{ - -class AppQtMainWidget; -class AuthContext; -class ChangePinContext; -class DiagnosisGui; -class SelfAuthContext; -class SetupAssistantGui; -class WorkflowGui; -class WorkflowAuthenticateQtGui; -class WorkflowChangePinQtGui; -class WorkflowSelfInfoQtGui; - -class AppQtGui - : public QObject -{ - Q_OBJECT - - public: - AppQtGui(); - virtual ~AppQtGui() override; - - virtual void init(); - - virtual QSharedPointer createWorkflowAuthenticateUi(const QSharedPointer& pContext); - virtual QSharedPointer createWorkflowChangePinUi(const QSharedPointer& pContext); - virtual QSharedPointer createWorkflowSelfInfoUi(const QSharedPointer& pContext); - - virtual void activateWorkflowUi(QSharedPointer pWorkflowUi, bool pAllowHideAfterWorkflow = true); - virtual void deactivateCurrentWorkflowUi(); - - virtual bool askChangeTransportPinNow(); - void switchToReaderSettings(); - - void shutdown(); - - protected: - virtual bool eventFilter(QObject* pObject, QEvent* pEvent) override; - - private: - void loadStyleSheet(); - void createTrayIcon(); - void closeDialogs(); - void hideFromTaskbar(); - void restoreToTaskbar(); - - public Q_SLOTS: - virtual void show(UiModule pModule = UiModule::CURRENT); - virtual void onApplicationStarted(); - virtual void onShowUserInformation(const QString& pAppName); -#ifndef QT_NO_NETWORKPROXY - void onProxyAuthenticationRequired(const QNetworkProxy& pProxy, QAuthenticator* pAuthenticator); -#endif - - private Q_SLOTS: - void onActivated(QSystemTrayIcon::ActivationReason reason); - void onCloseWindowRequested(bool* pDoClose); - void onChangeHighContrast(bool* pHighContrastOn); - void onSetupAssistantWizardRequest(); - void onDeveloperModeQuestion(); - void onDiagnosisRequested(); - void onAppUpdateReady(bool pSuccess, const GlobalStatus& pError); - void onUpdateScheduled(); - void onCertificateRemoved(QString pDeviceName); - void onSwitchToReaderSettingsRequested(); - - private: - AppQtMainWidget* mMainWidget; - QIcon mIcon; - QSystemTrayIcon* mTrayIcon; - QSharedPointer mActiveWorkflowUi; - SetupAssistantGui* mSetupAssistantGui; - DiagnosisGui* mDiagnosisGui; - QMessageBox* mUpdateInfo; - QMessageBox* mCertificateInfo; - bool mAggressiveToForeground; - - Q_SIGNALS: - void fireCloseReminderFinished(bool pDontRemindAgain); - - void fireChangePinRequested(); - void selfAuthenticationRequested(); - void fireSwitchToReaderSettingsRequested(); - void quitApplicationRequested(); -}; - -} /* namespace governikus */ diff --git a/src/widget/AppQtMainWidget.cpp b/src/widget/AppQtMainWidget.cpp deleted file mode 100644 index d7d0060..0000000 --- a/src/widget/AppQtMainWidget.cpp +++ /dev/null @@ -1,622 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "AppQtMainWidget.h" - -#include "AboutDialog.h" -#include "AppSettings.h" -#include "BuildHelper.h" -#include "generic/ExclusiveButtonGroup.h" -#include "generic/HelpAction.h" -#include "GuiProfile.h" -#include "LanguageLoader.h" -#include "LogHandler.h" -#include "ReaderDetector.h" -#include "step/AuthenticateStepsWidget.h" -#include "SetupAssistantWizard.h" -#include "ui_AppQtMainWidget.h" -#include "VersionNumber.h" -#include "workflow/WorkflowQtWidget.h" - -#include -#include -#include -#include -#include -#include - -#ifdef Q_OS_WIN -#include -#endif - -using namespace governikus; - -Q_DECLARE_LOGGING_CATEGORY(gui) - -AppQtMainWidget::AppQtMainWidget() - : QMainWindow() - , mUi(new Ui::AppQtMainWidget()) - , mTabButton2Page() - , mTabAction2Button() - , mAuthenticationWorkflowWidget(nullptr) - , mSelectedPushButton(nullptr) - , mSelectedPushButtonBeforeWorkflow(nullptr) - , mSelectedPagesBeforeWorkflow() - , mHideWindowAfterWorkflow(false) - , mLogFilesDialog() -{ - mUi->setupUi(this); - mUi->englishButton->setBackgroundRole(QPalette::NoRole); - mUi->germanButton->setBackgroundRole(QPalette::NoRole); - - refreshLanguageButton(); - - mUi->appLogoWidget->setAttribute(Qt::WA_TransparentForMouseEvents); - - QStackedLayout* centralStackLayout = qobject_cast(mUi->centralWidget->layout()); - centralStackLayout->setStackingMode(QStackedLayout::StackAll); - centralStackLayout->setCurrentIndex(1); - - QSpacerItem* spacer = new QSpacerItem(1, mUi->logoLabel->sizeHint().height(), QSizePolicy::Fixed, QSizePolicy::Ignored); - mUi->applicationGridLayout->addItem(spacer, mUi->applicationGridLayout->rowCount(), 0); - - mUi->providerPage->layout()->setMargin(20); - mUi->historyPage->layout()->setMargin(20); - - mAuthenticationWorkflowWidget = new WorkflowQtWidget(); - mUi->workflowPage->layout()->addWidget(mAuthenticationWorkflowWidget); - mAuthenticationWorkflowWidget->getStepWidgetArea()->layout()->addWidget(new AuthenticateStepsWidget); - - //menu file - connect(mUi->actionBeenden, &QAction::triggered, this, &AppQtMainWidget::fireQuitApplicationRequested); - connect(mUi->actionSettings, &QAction::triggered, this, &AppQtMainWidget::onSettingsButtonClicked); - - //menu PIN management - connect(mUi->actionChangePin, &QAction::triggered, this, &AppQtMainWidget::onChangePinButtonClicked); - - //help - connect(mUi->actionSetupAssistant, &QAction::triggered, this, &AppQtMainWidget::fireSetupAssistantWizardRequest); - connect(mUi->actionDiagnosis, &QAction::triggered, this, &AppQtMainWidget::fireDiagnosisRequested); - - connect(mUi->actionShowProtocol, &QAction::triggered, this, &AppQtMainWidget::onOpenLoggingFileButtonClicked); - connect(mUi->actionSaveProtocol, &QAction::triggered, this, &AppQtMainWidget::onSaveLoggingFileButtonClicked); - connect(mUi->actionInfo, &QAction::triggered, this, &AppQtMainWidget::onAboutActionClicked); - connect(mUi->actionSendError, &QAction::triggered, this, &AppQtMainWidget::onSendErrorActionClicked); - connect(mUi->actionEvaluate, &QAction::triggered, this, &AppQtMainWidget::onEvaluateActionClicked); - connect(mUi->actionQuestion, &QAction::triggered, this, &AppQtMainWidget::onQuestionActionClicked); - connect(mUi->actionHelp, &QAction::triggered, this, &AppQtMainWidget::onContentActionClicked); - - connect(mUi->identifyPage, &SelfInformationWidget::selfAuthenticationRequested, this, &AppQtMainWidget::fireSelfAuthenticationRequested); - connect(mUi->settingsPage, &SettingsWidget::changePinRequested, this, &AppQtMainWidget::fireChangePinRequested); - connect(mUi->settingsPage, &SettingsWidget::diagnosisRequested, this, &AppQtMainWidget::fireDiagnosisRequested); - connect(mUi->settingsPage, &SettingsWidget::settingsDone, this, &AppQtMainWidget::onSettingsDone); - - connect(mUi->germanButton, &QPushButton::clicked, [&]() - { - setLanguage(QLocale::German); - }); - connect(mUi->englishButton, &QPushButton::clicked, [&]() - { - setLanguage(QLocale::English); - }); - - //NavigationButtons - mUi->startPushButton->setVisible(false); - mUi->mainTabList->addButton(mUi->startPushButton); - mTabButton2Page.insert(mUi->startPushButton, mUi->applicationPage); - - struct ButtonInfo - { - QAbstractButton* mButton; - QWidget* mPage; - QAction* mAction; - const char* mOnIcon; - const char* mOffIcon; - }; - ButtonInfo buttonInfos[] = { - {mUi->ausweisenToolButton, mUi->identifyPage, mUi->actionAusweisen, ":/images/bt_1.svg", ":/images/bt_1b.svg"}, - {mUi->providerToolButton, mUi->providerPage, mUi->actionProvider, ":/images/bt_2.svg", ":/images/bt_2b.svg"}, - {mUi->historyToolButton, mUi->historyPage, mUi->actionHistory, ":/images/bt_3.svg", ":/images/bt_3b.svg"}, - {mUi->settingsToolButton, mUi->settingsPage, mUi->actionSettings, ":/images/bt_4.svg", ":/images/bt_4b.svg"}, - }; - - for (size_t i = 0; i < sizeof(buttonInfos) / sizeof(buttonInfos[0]); ++i) - { - const ButtonInfo& buttonInfo = buttonInfos[i]; - QIcon icon; - icon.addPixmap(QPixmap(QString::fromLatin1(buttonInfo.mOnIcon)), QIcon::Normal, QIcon::Off); - icon.addPixmap(QPixmap(QString::fromLatin1(buttonInfo.mOffIcon)), QIcon::Normal, QIcon::On); - - buttonInfo.mButton->setIcon(icon); - - connect(buttonInfo.mAction, &QAction::triggered, this, &AppQtMainWidget::onTabActionTriggered); - - mUi->mainTabList->addButton(buttonInfo.mButton); - mTabButton2Page.insert(buttonInfo.mButton, buttonInfo.mPage); - mTabAction2Button.insert(buttonInfo.mAction, buttonInfo.mButton); - } - - connect(mUi->mainTabList, &TabButtonGroup::buttonToggled, this, &AppQtMainWidget::onTabButtonToggled); - mUi->stackedWidget->setCurrentIndex(0); - - if (VersionNumber::getApplicationVersion().isDeveloperVersion()) - { - setWindowTitle(windowTitle() + QStringLiteral(" - Beta - ") + QCoreApplication::applicationVersion()); - mUi->betaLabel->setMinimumSize(QSize(150, 150)); - mUi->betaLabel->setPixmap(QPixmap(QStringLiteral(":/images/beta.svg")).scaled(mUi->betaLabel->width(), mUi->betaLabel->width(), Qt::KeepAspectRatio)); - mUi->betaLabel->setVisible(true); - } - - - // work-around for bug QT/QTBUG-40869 - QSet visitedObjects; - updateGeometryRecursively(this, visitedObjects); - -#ifdef Q_OS_WIN - // we need to call create() explicitly because Windows needs a handle to fire WM_ENDSESSION! - // Since we start as a systemtray only we don't have a correct handle until we call show() - create(); -#endif -} - - -AppQtMainWidget::~AppQtMainWidget() -{ -} - - -void AppQtMainWidget::changeEvent(QEvent* pEvent) -{ - if (pEvent->type() == QEvent::LanguageChange) - { - mUi->retranslateUi(this); - refreshLanguageButton(); - } - - if (pEvent->type() == QEvent::PaletteChange) // QEvent::StyleChange is called, too - { - -#if defined(Q_OS_WIN) - HIGHCONTRAST hc; - hc.cbSize = sizeof(hc); - - bool highContrastOn = false; - if (SystemParametersInfo(SPI_GETHIGHCONTRAST, sizeof(hc), &hc, FALSE) && (hc.dwFlags & HCF_HIGHCONTRASTON)) - { - qDebug() << "High contrast (SPI_GETHIGHCONTRAST) switched on."; - highContrastOn = true; - Q_EMIT fireChangeHighContrast(&highContrastOn); - } - else - { - qDebug() << "High contrast (SPI_GETHIGHCONTRAST) switched off."; - Q_EMIT fireChangeHighContrast(&highContrastOn); - } -#endif - } - QWidget::changeEvent(pEvent); -} - - -bool AppQtMainWidget::remoteScanRunning() const -{ - return mUi->settingsPage->remoteScanRunning(); -} - - -void AppQtMainWidget::workflowActivated(WorkflowWidgetParent pParent, const QString& /*pName*/) -{ - QAbstractButton* tabToolButton = nullptr; - QWidget* containingWidget = nullptr; - - // activate the correct tab and set the workflow page - switch (pParent) - { - case WorkflowWidgetParent::SelfAuthentication: - case WorkflowWidgetParent::Authentication: - tabToolButton = mUi->ausweisenToolButton; - containingWidget = mUi->workflowPage; - break; - - case WorkflowWidgetParent::SettingsChangePin: - tabToolButton = mUi->settingsToolButton; - containingWidget = nullptr; - break; - } - - mSelectedPushButtonBeforeWorkflow = mSelectedPushButton; - - setSelectedTab(tabToolButton); - - if (tabToolButton != nullptr) - { - const auto tabButtons = mTabButton2Page.keys(); - for (auto button : tabButtons) - { - if (button != tabToolButton) - { - button->setEnabled(false); - } - } - } - - // show the respective page in the widget stacks - while (containingWidget != nullptr) - { - QWidget* containerParent = containingWidget->parentWidget(); - if (QStackedWidget* stackedWidget = qobject_cast(containerParent)) - { - mSelectedPagesBeforeWorkflow += stackedWidget->currentWidget(); - stackedWidget->setCurrentWidget(containingWidget); - } - - containingWidget = containerParent; - } - - mUi->settingsPage->workflowStarted(); - mUi->mainTabList->setWorkflowActive(true); - -} - - -void AppQtMainWidget::workflowDeactivated() -{ - const auto tabButtons = mTabButton2Page.keys(); - for (auto button : tabButtons) - { - button->setEnabled(true); - } - - for (auto widget : qAsConst(mSelectedPagesBeforeWorkflow)) - { - if (QStackedWidget* stackedWidget = qobject_cast(widget->parentWidget())) - { - stackedWidget->setCurrentWidget(widget); - } - - } - mSelectedPagesBeforeWorkflow.clear(); - - mUi->settingsPage->workflowFinished(); - mUi->mainTabList->setWorkflowActive(false); - - // switch back to the tab selected before the workflow - if (mSelectedPushButtonBeforeWorkflow == nullptr) - { - return; - } - setSelectedTab(mSelectedPushButtonBeforeWorkflow); - mSelectedPushButtonBeforeWorkflow = nullptr; -} - - -void AppQtMainWidget::switchToGuiModule(GuiModule pModule) -{ - switch (pModule) - { - case GuiModule::START_PAGE: - setSelectedTab(mUi->startPushButton); - break; - - case GuiModule::IDENTIFY: - setSelectedTab(mUi->ausweisenToolButton); - break; - - case GuiModule::GENERAL_SETTINGS: - case GuiModule::PIN_SETTINGS: - case GuiModule::DEVICE_SETTINGS: - setSelectedTab(mUi->settingsToolButton); - mUi->settingsPage->switchToGuiModule(pModule); - break; - } -} - - -void AppQtMainWidget::switchToPinSettingsAfterWorkflow() -{ - mHideWindowAfterWorkflow = false; - mSelectedPushButtonBeforeWorkflow = mUi->settingsToolButton; - - mSelectedPagesBeforeWorkflow.clear(); - - QWidget* containingWidget = mUi->stackedWidget->parentWidget(); - - while (containingWidget != nullptr) - { - QWidget* containerParent = containingWidget->parentWidget(); - if (qobject_cast(containerParent) != nullptr) - { - mSelectedPagesBeforeWorkflow += containingWidget; - } - - containingWidget = containerParent; - } - - mUi->settingsPage->switchToGuiModule(GuiModule::PIN_SETTINGS); -} - - -void AppQtMainWidget::closeEvent(QCloseEvent* pEvent) -{ - bool doClose = true; - Q_EMIT fireCloseWindowRequested(&doClose); - if (doClose) - { - pEvent->accept(); - } - else - { - pEvent->ignore(); - } -} - - -void AppQtMainWidget::keyPressEvent(QKeyEvent* keyEvent) -{ - switch (keyEvent->key()) - { - case Qt::Key_F1: - onContentActionClicked(); - break; - - default: - break; - } -} - - -void AppQtMainWidget::setSelectedTab(QAbstractButton* pSelectedPushButton) -{ - if (pSelectedPushButton == nullptr) - { - pSelectedPushButton = mUi->settingsToolButton; - mUi->settingsPage->switchToGuiModule(GuiModule::GENERAL_SETTINGS); - - } - if (mSelectedPushButton == mUi->settingsToolButton && mUi->settingsPage->isSettingsChanged()) - { - const auto tabButtons = mTabButton2Page.keys(); - for (auto button : tabButtons) - { - button->setChecked(button == mUi->settingsToolButton); - } - mUi->settingsPage->showSettingsChangedMessage(); - } - - mUi->appLogoWidget->setVisible(pSelectedPushButton == mUi->startPushButton); - - if (mSelectedPushButton) - { - mSelectedPushButton->clearFocus(); - } - mSelectedPushButton = pSelectedPushButton; - mSelectedPushButton->setChecked(true); - - mUi->stackedWidget->setCurrentWidget(mTabButton2Page.value(pSelectedPushButton)); -} - - -void AppQtMainWidget::activateWindow() -{ - QMainWindow::activateWindow(); - -#if defined(Q_OS_MACOS) - // Workaround. When switching from "BackgroundApplication" to "ForegroundApplication" - // on MacOS, it is a known problem that the menu bar of the previous active application - // stays visible, although the window has changed to the current application. As soon - // as the user clicks the menu, it magically transforms to the correct one. We therefore - // manually trigger an update. Neither update() nor repaint() of QMenuBar solve this problem. - QMenu menu; - mUi->menuBar->addAction(menu.menuAction()); - mUi->menuBar->removeAction(menu.menuAction()); -#endif -} - - -void AppQtMainWidget::updateGeometryRecursively(QWidget* pWidget, QSet& pVisitedObjects) -{ - if (pVisitedObjects.contains(pWidget)) - { - return; - } - - pVisitedObjects.insert(pWidget); - - pWidget->updateGeometry(); - - if (pWidget->layout() != nullptr) - { - updateGeometryRecursively(pWidget->layout(), pVisitedObjects); - } - - for (QObject* child : pWidget->children()) - { - if (QWidget* widget = qobject_cast(child)) - { - updateGeometryRecursively(widget, pVisitedObjects); - } - } -} - - -void AppQtMainWidget::updateGeometryRecursively(QLayout* pLayout, QSet& pVisitedObjects) -{ - if (pVisitedObjects.contains(pLayout)) - { - return; - } - - pVisitedObjects.insert(pLayout); - - pLayout->invalidate(); - - int itemCount = pLayout->count(); - for (int i = 0; i < itemCount; ++i) - { - QLayoutItem* item = pLayout->itemAt(i); - - if (QLayout* layout = item->layout()) - { - updateGeometryRecursively(layout, pVisitedObjects); - } - else if (QWidget* widget = item->widget()) - { - updateGeometryRecursively(widget, pVisitedObjects); - } - } -} - - -void AppQtMainWidget::onSettingsDone() -{ - setSelectedTab(mUi->startPushButton); -} - - -void AppQtMainWidget::onOpenLoggingFileButtonClicked() -{ - if (!mLogFilesDialog) - { - mLogFilesDialog = new LogFilesDialog(this); - } - - mLogFilesDialog->show(); -} - - -void AppQtMainWidget::onSaveLoggingFileButtonClicked() -{ - LogFilesDialog::saveLogFile(this); -} - - -void AppQtMainWidget::onTabButtonToggled(QAbstractButton* pButton, bool pChecked) -{ - if (pChecked) - { - if (mTabButton2Page.contains(pButton) && mSelectedPushButton != pButton) - { - setSelectedTab(pButton); - } - } -} - - -void AppQtMainWidget::onTabActionTriggered() -{ - if (QAction* action = qobject_cast(sender())) - { - if (QAbstractButton* button = mTabAction2Button.value(action)) - { - setSelectedTab(button); - } - } -} - - -void AppQtMainWidget::onSettingsButtonClicked() -{ - setSelectedTab(mUi->settingsToolButton); - mUi->settingsPage->switchToGuiModule(GuiModule::GENERAL_SETTINGS); -} - - -void AppQtMainWidget::onChangePinButtonClicked() -{ - setSelectedTab(mUi->settingsToolButton); - mUi->settingsPage->switchToGuiModule(GuiModule::PIN_SETTINGS); -} - - -void AppQtMainWidget::onQuestionActionClicked() -{ - QString link = tr("https://www.ausweisapp.bund.de/en/service/haeufig-gestellte-fragen/"); - QDesktopServices::openUrl(QUrl(link)); -} - - -void AppQtMainWidget::onSendErrorActionClicked() -{ - QString link = tr("https://www.ausweisapp.bund.de/en/feedback/melden-sie-einen-fehler/"); - QDesktopServices::openUrl(QUrl(link)); -} - - -void AppQtMainWidget::onEvaluateActionClicked() -{ - QString link = tr("https://www.ausweisapp.bund.de/en/feedback/bewerten-sie-uns/"); - QDesktopServices::openUrl(QUrl(link)); -} - - -void AppQtMainWidget::onContentActionClicked() -{ - QString name = mUi->stackedWidget->widget(mUi->stackedWidget->currentIndex())->objectName(); - if (name.startsWith(QLatin1String("settingsPage"))) - { - SettingsWidget* settingsWidget = static_cast(mUi->stackedWidget->widget(mUi->stackedWidget->currentIndex())); - HelpAction::openContextHelp(settingsWidget->getActiveTabObjectName()); - } - else - { - HelpAction::openContextHelp(name); - } -} - - -void AppQtMainWidget::onAboutActionClicked() -{ - AboutDialog* dialog = new AboutDialog(this); - dialog->show(); -} - - -void AppQtMainWidget::setLanguage(QLocale::Language pLocale) -{ - GeneralSettings& generalSettings = AppSettings::getInstance().getGeneralSettings(); - generalSettings.setLanguage(pLocale); - generalSettings.save(); -} - - -void AppQtMainWidget::refreshLanguageButton() -{ - const QString& selected = QStringLiteral("border-style: solid; border-width: 1px; border-color: grey; padding: 4px;"); - const QString& unselected = QStringLiteral("padding: 4px;"); - - const auto& locale = LanguageLoader::getInstance().getUsedLocale(); - if (locale == QLocale::English) - { - mUi->englishButton->setStyleSheet(selected); - mUi->germanButton->setStyleSheet(unselected); - } - else if (locale == QLocale::German) - { - mUi->englishButton->setStyleSheet(unselected); - mUi->germanButton->setStyleSheet(selected); - } -} - - -void AppQtMainWidget::activateMenuBarItems(bool pEnable) -{ - mUi->actionAusweisen->setEnabled(pEnable); - mUi->actionProvider->setEnabled(pEnable); - mUi->actionHistory->setEnabled(pEnable); - mUi->actionSettings->setEnabled(pEnable); - mUi->actionChangePin->setEnabled(pEnable); - mUi->actionSetupAssistant->setEnabled(pEnable); -} - - -void AppQtMainWidget::onCloseWindowRequested() -{ - mUi->settingsPage->onTabChanged(-1); -} - - -bool AppQtMainWidget::isRemindUserToClose() -{ - return AppSettings::getInstance().getGeneralSettings().isRemindUserToClose(); -} diff --git a/src/widget/AppQtMainWidget.h b/src/widget/AppQtMainWidget.h deleted file mode 100644 index 3c68aff..0000000 --- a/src/widget/AppQtMainWidget.h +++ /dev/null @@ -1,124 +0,0 @@ -/*! - * \brief Main class for the top level main widget - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "generic/GuiModule.h" -#include "LogFilesDialog.h" -#include "workflow/WorkflowWidgetParent.h" - -namespace Ui -{ -class AppQtMainWidget; -} - -namespace governikus -{ - -class WorkflowQtWidget; - -class AppQtMainWidget - : public QMainWindow -{ - Q_OBJECT - - public: - AppQtMainWidget(); - virtual ~AppQtMainWidget() override; - - bool remoteScanRunning() const; - void workflowActivated(WorkflowWidgetParent pParent, const QString& pName); - void workflowDeactivated(); - - void switchToGuiModule(GuiModule pModule); - - void switchToPinSettingsAfterWorkflow(); - - bool isHideWindowAfterWorkflow() const - { - return mHideWindowAfterWorkflow; - } - - - void setHideWindowAfterWorkflow(bool pHide) - { - mHideWindowAfterWorkflow = pHide; - } - - - void activateMenuBarItems(bool pEnable); - - WorkflowQtWidget* getAuthenticationWorkflowWidget() const - { - return mAuthenticationWorkflowWidget; - } - - - bool isRemindUserToClose(); - - void setSelectedTab(QAbstractButton* pSelectedPushButton); - - void activateWindow(); - - protected: - virtual void closeEvent(QCloseEvent* pEvent) override; - virtual void keyPressEvent(QKeyEvent* keyEvent) override; - virtual void changeEvent(QEvent* event) override; - - private: - static void updateGeometryRecursively(QWidget* pWidget, QSet& pVisitedObjects); - static void updateGeometryRecursively(QLayout* pLayout, QSet& pVisitedObjects); - - private Q_SLOTS: - void onSettingsDone(); - - void onOpenLoggingFileButtonClicked(); - void onSaveLoggingFileButtonClicked(); - void onTabButtonToggled(QAbstractButton* pButton, bool pChecked); - void onTabActionTriggered(); - void onAboutActionClicked(); - void onSendErrorActionClicked(); - void onEvaluateActionClicked(); - void onQuestionActionClicked(); - void onContentActionClicked(); - - public Q_SLOTS: - void onSettingsButtonClicked(); - void onChangePinButtonClicked(); - void onCloseWindowRequested(); - - Q_SIGNALS: - void fireSetupAssistantWizardRequest(); - void fireChangePinRequested(); - void fireDiagnosisRequested(); - void fireCloseWindowRequested(bool* pDoClose); - void fireSelfAuthenticationRequested(); - void fireQuitApplicationRequested(); - void fireChangeHighContrast(bool* pHighContrastOn); - - private: - QScopedPointer mUi; - QMap mTabButton2Page; - QMap mTabAction2Button; - WorkflowQtWidget* mAuthenticationWorkflowWidget; - QAbstractButton* mSelectedPushButton; - QAbstractButton* mSelectedPushButtonBeforeWorkflow; - QVector mSelectedPagesBeforeWorkflow; - bool mHideWindowAfterWorkflow; - QPointer mLogFilesDialog; - QString mStyleSheet; - void refreshLanguageButton(); - void setLanguage(QLocale::Language pLocale); -}; - -} /* namespace governikus */ diff --git a/src/widget/AppQtMainWidget.ui b/src/widget/AppQtMainWidget.ui deleted file mode 100644 index 9f00ead..0000000 --- a/src/widget/AppQtMainWidget.ui +++ /dev/null @@ -1,869 +0,0 @@ - - - AppQtMainWidget - - - - 0 - 0 - 950 - 600 - - - - Qt::NoContextMenu - - - AusweisApp2 - - - - :/images/npa.svg:/images/npa.svg - - - - 64 - 64 - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 0 - 10 - - - - - 16777215 - 10 - - - - - - - - 0 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 0 - - - 0 - - - - - - 0 - 0 - - - - Actions - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 5 - - - 10 - - - 0 - - - - - Qt::Horizontal - - - QSizePolicy::Minimum - - - - 13 - 20 - - - - - - - - - 0 - 0 - - - - - 53 - 22 - - - - Switch language to German - - - padding: 4px; - - - DE - - - - :/images/location_flag_de.svg:/images/location_flag_de.svg - - - true - - - buttonGroup - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 5 - 20 - - - - - - - - true - - - - 0 - 0 - - - - - 53 - 23 - - - - Switch language to English - - - padding: 4px; - - - EN - - - - :/images/location_flag_en.svg:/images/location_flag_en.svg - - - false - - - true - - - buttonGroup - - - - - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 40 - 20 - - - - - - - - - - - 0 - 0 - - - - Welcome - - - true - - - true - - - - - - - - 0 - 0 - - - - margin-top: 10px; - - - Identify - - - - 32 - 32 - - - - true - - - false - - - Qt::ToolButtonTextBesideIcon - - - - - - - - 0 - 0 - - - - Provider - - - - 32 - 32 - - - - true - - - 300 - - - 100 - - - Qt::ToolButtonTextBesideIcon - - - - - - - - 0 - 0 - - - - History - - - - 32 - 32 - - - - true - - - Qt::ToolButtonTextBesideIcon - - - - - - - - 0 - 0 - - - - Settings - - - - 32 - 32 - - - - true - - - Qt::ToolButtonTextBesideIcon - - - - - - - 0 - - - 15 - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Qt::Vertical - - - QSizePolicy::MinimumExpanding - - - - 20 - 40 - - - - - - - - - - - - 1 - 0 - - - - 0 - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::StrongFocus - - - nPA and eAT Logo - - - :/images/start_nPA_eAT.png - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Qt::StrongFocus - - - AusweisApp2 Logo - - - :/images/AppLogo_AutentApp2_2014.png - - - Qt::AlignCenter - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - - true - - - - - - - - 0 - - - 6 - - - 6 - - - 6 - - - 6 - - - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - :/images/Logo_AutentApp2_2014.png - - - - - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 16777215 - 170 - - - - - - - - - - 0 - 0 - 840 - 20 - - - - true - - - - Fi&le - - - - - - - - - - - &Help - - - - - - - - - - - - - - - - - - &PIN Management - - - - - - - - - - Minimise - - - - - &Exit - - - QAction::QuitRole - - - - - &Identify - - - - - &Settings - - - - - &Online help - - - - - &Evaluate - - - - - &Report error - - - - - &About AusweisApp2 - - - QAction::AboutRole - - - - - &Provider - - - - - S&how log - - - - - Save &log - - - - - &Questions - - - - - &History - - - - - &Change PIN - - - - - &Setup assistant - - - QAction::NoRole - - - - - &Diagnosis - - - - - - governikus::TabButtonGroup - QWidget -
generic/TabButtonGroup.h
- 1 -
- - governikus::SettingsWidget - QWidget -
SettingsWidget.h
- 1 -
- - governikus::ProviderWidget - QWidget -
ProviderWidget.h
- 1 -
- - governikus::SelfInformationWidget - QWidget -
SelfInformationWidget.h
- 1 -
- - governikus::HistoryWidget - QWidget -
HistoryWidget.h
- 1 -
- - governikus::DeveloperModeHistoryWidget - QWidget -
DeveloperModeHistoryWidget.h
- 1 -
- - governikus::TabButton - QToolButton -
generic/TabButtonGroup.h
-
-
- - startPushButton - appLogoLabel - appLabel - ausweisenToolButton - providerToolButton - historyToolButton - settingsToolButton - - - - - - - - -
diff --git a/src/widget/AppStartPage.cpp b/src/widget/AppStartPage.cpp deleted file mode 100644 index 9530572..0000000 --- a/src/widget/AppStartPage.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "AppStartPage.h" - -#include "generic/TabButtonGroup.h" -#include "GuiProfile.h" -#include "ui_AppStartPage.h" - -#include -#include - -using namespace governikus; - -AppStartPage::AppStartPage(QWidget* pParent) - : QWidget(pParent) - , mApplicationLogoLabel(new QLabel(this)) - , mUi(new Ui::AppStartPage()) -{ - mUi->setupUi(this); - - QPixmap applicationPixmap(QStringLiteral(":/images/AppLogo_AutentApp2_2014.png")); - QPixmap nPAeATPixmap(QStringLiteral(":/images/start_nPA_eAT.png")); - - mUi->applicationLogoLabel->setPixmap(applicationPixmap); - mUi->nPAeATLabel->setPixmap(nPAeATPixmap); -} - - -AppStartPage::~AppStartPage() -{ -} - - -void AppStartPage::paintEvent(QPaintEvent* /*pPaintEvent*/) -{ - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} - - -void AppStartPage::changeEvent(QEvent* pEvent) -{ - if (pEvent->type() == QEvent::LanguageChange) - { - mUi->retranslateUi(this); - } - QWidget::changeEvent(pEvent); -} diff --git a/src/widget/AppStartPage.h b/src/widget/AppStartPage.h deleted file mode 100644 index 177a64e..0000000 --- a/src/widget/AppStartPage.h +++ /dev/null @@ -1,44 +0,0 @@ -/*! - * \brief Main page widget. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include - -namespace Ui -{ -class AppStartPage; -} - -namespace governikus -{ - -class AppStartPage - : public QWidget -{ - Q_OBJECT - - public: - AppStartPage(QWidget* pParent = nullptr); - virtual ~AppStartPage() override; - - Q_SIGNALS: - void selfInfoPageRequested(); - void bookmarksPageRequested(); - void settingsPageRequested(); - - private: - QLabel* mApplicationLogoLabel; - QScopedPointer mUi; - - virtual void paintEvent(QPaintEvent*) override; - - protected: - virtual void changeEvent(QEvent* pEvent) override; -}; - -} /* namespace governikus */ diff --git a/src/widget/CMakeLists.txt b/src/widget/CMakeLists.txt deleted file mode 100644 index 2ce00ab..0000000 --- a/src/widget/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -ADD_PLATFORM_LIBRARY(AusweisAppWidget) - -TARGET_LINK_LIBRARIES(AusweisAppWidget Qt5::Core Qt5::Widgets Qt5::Svg AusweisAppCore AusweisAppGlobal AusweisAppExport AusweisAppRemoteDevice AusweisAppConfiguration) -TARGET_COMPILE_DEFINITIONS(AusweisAppWidget PRIVATE QT_STATICPLUGIN) - -IF(WIN32) - TARGET_LINK_LIBRARIES(AusweisAppWidget Qt5::WinExtras) -ENDIF() diff --git a/src/widget/CredentialDialog.h b/src/widget/CredentialDialog.h deleted file mode 100644 index 8f51da6..0000000 --- a/src/widget/CredentialDialog.h +++ /dev/null @@ -1,43 +0,0 @@ -/*! - * \brief Show a dialog to fill in proxy credentials. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -namespace Ui -{ -class CredentialDialog; -} - -#include - -namespace governikus -{ - -class CredentialDialog - : public QDialog -{ - Q_OBJECT - - private: - Ui::CredentialDialog* mUi; - - protected: - virtual void changeEvent(QEvent* pEvent) override; - - public: - explicit CredentialDialog(QWidget* pParent = nullptr); - virtual ~CredentialDialog() override; - - void setUser(const QString& pUser); - - QString getUser() const; - - QString getPassword() const; - - -}; - -} /* namespace governikus */ diff --git a/src/widget/DeleteHistoryDialog.h b/src/widget/DeleteHistoryDialog.h deleted file mode 100644 index c37204f..0000000 --- a/src/widget/DeleteHistoryDialog.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "HistorySettings.h" - -#include -#include -#include -#include -#include - -namespace governikus -{ - -class DeleteHistoryDialog - : public QDialog -{ - Q_OBJECT - QPointer mRadioButtonGroup; - QPointer mComboBox; - - QRadioButton* createRadioButtonAndAppendToGroup(const QString& pText, TimePeriod pTimePeriod); - - public: - explicit DeleteHistoryDialog(QWidget* pParent = nullptr); - TimePeriod getTimePeriod(); -}; - -} /* namespace governikus */ diff --git a/src/widget/DetailDialog.cpp b/src/widget/DetailDialog.cpp deleted file mode 100644 index 7907595..0000000 --- a/src/widget/DetailDialog.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "DetailDialog.h" - -#include "ui_DetailDialog.h" - -#include "DetailWidget.h" -#include "generic/HelpAction.h" - -#include -#include - -using namespace governikus; - -DetailDialog::DetailDialog(QWidget* pParent) - : QDialog(pParent) - , mUi(new Ui::DetailDialog) -{ - mUi->setupUi(this); - - setWindowFlags(Qt::Dialog | Qt::WindowTitleHint | Qt::WindowCloseButtonHint); - setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("Service provider data")); - - installEventFilter(this); - - connect(mUi->buttonBox, &QDialogButtonBox::rejected, this, &DetailDialog::close); -} - - -DetailDialog::~DetailDialog() -{ - delete mUi; -} - - -void DetailDialog::setDetails(const QString& pDetails) -{ - mUi->detailWidget->setDetails(pDetails); - mUi->buttonBox->button(QDialogButtonBox::Close)->setFocus(); - adjustSize(); -} - - -bool DetailDialog::eventFilter(QObject* pObject, QEvent* pEvent) -{ - if (pEvent->type() == QEvent::KeyPress) - { - QKeyEvent* keyEvent = static_cast(pEvent); - if (keyEvent->key() == Qt::Key_F1) - { - HelpAction::openContextHelp(); - return true; - } - } - return QDialog::eventFilter(pObject, pEvent); -} - - -void DetailDialog::changeEvent(QEvent* pEvent) -{ - if (pEvent->type() == QEvent::LanguageChange) - { - mUi->retranslateUi(this); - } - QWidget::changeEvent(pEvent); -} diff --git a/src/widget/DetailDialog.h b/src/widget/DetailDialog.h deleted file mode 100644 index 2f3022b..0000000 --- a/src/widget/DetailDialog.h +++ /dev/null @@ -1,39 +0,0 @@ -/*! - * \brief Detail dialog for certificate description - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include - -namespace Ui -{ -class DetailDialog; -} - -namespace governikus -{ - -class DetailDialog - : public QDialog -{ - Q_OBJECT - - private: - Ui::DetailDialog* mUi; - - public: - explicit DetailDialog(QWidget* pParent = nullptr); - virtual ~DetailDialog() override; - - void setDetails(const QString& pDetails); - - protected: - virtual bool eventFilter(QObject* pObject, QEvent* pEvent) override; - virtual void changeEvent(QEvent* pEvent) override; - -}; - -} /* namespace governikus */ diff --git a/src/widget/DetailWidget.cpp b/src/widget/DetailWidget.cpp deleted file mode 100644 index eb48c0d..0000000 --- a/src/widget/DetailWidget.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "DetailWidget.h" - -#include "ui_DetailWidget.h" - -#include -#include - -using namespace governikus; - -DetailWidget::DetailWidget(QWidget* pParent) - : QWidget(pParent) - , mUi(new Ui::DetailWidget()) -{ - mUi->setupUi(this); - - // The scroll area may be resized while the content keeps its - // size, therefore the scroll area must have the same background - // color as the content. - mUi->scrollArea->setStyleSheet(QStringLiteral("QScrollArea { background-color: white; }" - "" - "QScrollBar {" - " background-color: #f8f6f4;" - "}")); - - const QString& border = QStringLiteral("border-width: 0;"); - mUi->scrollAreaWidgetContents->setStyleSheet(border); - mUi->detailText->setStyleSheet(border); -} - - -DetailWidget::~DetailWidget() -{ -} - - -void DetailWidget::setDetails(const QString& pDetails) -{ - mUi->detailText->setAccessibleName(tr("Service provider details dialog") + pDetails); - mUi->detailText->setText(pDetails); -} - - -void DetailWidget::paintEvent(QPaintEvent*) -{ - static const int SCROLLAREA_PREFERRED_WIDTH = 504; - // See comment to accepted answer at - // http://stackoverflow.com/questions/16515646/how-to-get-scroll-bar-real-width-in-qt -#ifdef Q_OS_WIN - static const int SCROLLBAR_EXTRA_SPACE = 40; -#else - static const int SCROLLBAR_EXTRA_SPACE = 3; -#endif - - const int scrollbarWidth = qApp->style()->pixelMetric(QStyle::PM_ScrollBarExtent) + SCROLLBAR_EXTRA_SPACE; - const int contentMaxWidth = SCROLLAREA_PREFERRED_WIDTH - scrollbarWidth; - - if (mUi->detailText->width() > contentMaxWidth) - { - mUi->detailText->setFixedWidth(contentMaxWidth); - } - - mUi->scrollAreaWidgetContents->setFixedHeight(mUi->detailText->height()); - mUi->scrollAreaWidgetContents->setFixedWidth(mUi->detailText->width()); - - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} - - -void DetailWidget::changeEvent(QEvent* pEvent) -{ - if (pEvent->type() == QEvent::LanguageChange) - { - mUi->retranslateUi(this); - } - QWidget::changeEvent(pEvent); -} diff --git a/src/widget/DetailWidget.h b/src/widget/DetailWidget.h deleted file mode 100644 index 038c982..0000000 --- a/src/widget/DetailWidget.h +++ /dev/null @@ -1,41 +0,0 @@ -/*! - * \brief Widget for cvc description. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include - -namespace Ui -{ -class DetailWidget; -} - -namespace governikus -{ - -class DetailWidget - : public QWidget -{ - Q_OBJECT - - private: - QScopedPointer mUi; - - virtual void paintEvent(QPaintEvent*) override; - - protected: - virtual void changeEvent(QEvent* pEvent) override; - - public: - DetailWidget(QWidget* pParent = nullptr); - virtual ~DetailWidget() override; - - void setDetails(const QString& pDetails); - -}; - -} /* namespace governikus */ diff --git a/src/widget/DeveloperModeHistoryWidget.cpp b/src/widget/DeveloperModeHistoryWidget.cpp deleted file mode 100644 index d161bde..0000000 --- a/src/widget/DeveloperModeHistoryWidget.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "AppSettings.h" -#include "DeveloperModeHistoryWidget.h" -#include "LogHandler.h" -#include "ui_DeveloperModeHistoryWidget.h" - -#include - -using namespace governikus; - -Q_DECLARE_LOGGING_CATEGORY(developermode) - - -DeveloperModeHistoryWidget::DeveloperModeHistoryWidget(QWidget* pParent) - : QWidget(pParent) - , mUi(new Ui::DeveloperModeHistoryWidget()) -{ - mUi->setupUi(this); - - connect(&AppSettings::getInstance(), &AppSettings::fireSettingsChanged, this, &DeveloperModeHistoryWidget::onSettingsChanged); - connect(&LogHandler::getInstance(), &LogHandler::fireRawLog, this, &DeveloperModeHistoryWidget::onRawLog); - connect(mUi->btnDisableDeveloperMode, &QPushButton::clicked, this, &DeveloperModeHistoryWidget::onDisableDeveloperMode); - - // initialize visibility state - onSettingsChanged(); -} - - -DeveloperModeHistoryWidget::~DeveloperModeHistoryWidget() -{ -} - - -void DeveloperModeHistoryWidget::appendLoggingDump(const QString& pLog) -{ - QString formatted = pLog; - - // Remove outer quotation - const QLatin1Char quote('"'); - if (formatted.startsWith(quote) && formatted.endsWith(quote)) - { - formatted = formatted.mid(1, formatted.length() - 2); - } - - mUi->plainTextEdit->appendHtml(QStringLiteral("
%1
").arg(formatted)); -} - - -void DeveloperModeHistoryWidget::onRawLog(const QString& pMsg, const QString& pCategoryName) -{ - static const QString categoryDevMode = QString::fromLatin1(developermode().categoryName()); - if (pCategoryName == categoryDevMode) - { - appendLoggingDump(pMsg); - } -} - - -void DeveloperModeHistoryWidget::onSettingsChanged() -{ - QWidget::setVisible(AppSettings::getInstance().getGeneralSettings().isDeveloperMode()); -} - - -void DeveloperModeHistoryWidget::onDisableDeveloperMode() -{ - AppSettings::getInstance().getGeneralSettings().setDeveloperMode(false); - AppSettings::getInstance().getGeneralSettings().save(); -} - - -void DeveloperModeHistoryWidget::changeEvent(QEvent* pEvent) -{ - if (pEvent->type() == QEvent::LanguageChange) - { - mUi->retranslateUi(this); - } - QWidget::changeEvent(pEvent); -} diff --git a/src/widget/DeveloperModeHistoryWidget.h b/src/widget/DeveloperModeHistoryWidget.h deleted file mode 100644 index 6ad62ef..0000000 --- a/src/widget/DeveloperModeHistoryWidget.h +++ /dev/null @@ -1,46 +0,0 @@ -/*! - * \brief A Widget to display developer mode errors which occurred - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include - - -namespace Ui -{ -class DeveloperModeHistoryWidget; -} - - -namespace governikus -{ - -class DeveloperModeHistoryWidget - : public QWidget -{ - Q_OBJECT - - private: - QScopedPointer mUi; - - void appendLoggingDump(const QString& pLog); - - private Q_SLOTS: - void onDisableDeveloperMode(); - - protected: - virtual void changeEvent(QEvent* pEvent) override; - - public: - explicit DeveloperModeHistoryWidget(QWidget* pParent = nullptr); - virtual ~DeveloperModeHistoryWidget() override; - - public Q_SLOTS: - void onRawLog(const QString& pMsg, const QString& pCategoryName); - void onSettingsChanged(); -}; - -} /* namespace governikus */ diff --git a/src/widget/DeveloperSettingsWidget.cpp b/src/widget/DeveloperSettingsWidget.cpp deleted file mode 100644 index eb39952..0000000 --- a/src/widget/DeveloperSettingsWidget.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "DeveloperSettingsWidget.h" -#include "ui_DeveloperSettingsWidget.h" - -#include "AppSettings.h" - -using namespace governikus; - -DeveloperSettingsWidget::DeveloperSettingsWidget(QWidget* pParent) - : QWidget(pParent) - , mUi(new Ui::DeveloperSettingsWidget()) -{ - mUi->setupUi(this); - - reset(); - - connect(mUi->selfAuthTestCheckBox, &QCheckBox::stateChanged, this, &DeveloperSettingsWidget::onCheckBoxStateChanged); -} - - -DeveloperSettingsWidget::~DeveloperSettingsWidget() -{ - -} - - -void governikus::DeveloperSettingsWidget::onCheckBoxStateChanged() -{ - Q_EMIT fireSettingsChanged(); -} - - -void DeveloperSettingsWidget::showEvent(QShowEvent* pEvent) -{ - QWidget::showEvent(pEvent); -} - - -void DeveloperSettingsWidget::apply() -{ - auto& generalSettings = AppSettings::getInstance().getGeneralSettings(); - generalSettings.setUseSelfauthenticationTestUri(mUi->selfAuthTestCheckBox->isChecked()); - generalSettings.save(); -} - - -void DeveloperSettingsWidget::reset() -{ - mUi->selfAuthTestCheckBox->setChecked(AppSettings::getInstance().getGeneralSettings().useSelfAuthTestUri()); -} - - -void DeveloperSettingsWidget::changeEvent(QEvent* pEvent) -{ - if (pEvent->type() == QEvent::LanguageChange) - { - mUi->retranslateUi(this); - } - QWidget::changeEvent(pEvent); -} diff --git a/src/widget/DeveloperSettingsWidget.h b/src/widget/DeveloperSettingsWidget.h deleted file mode 100644 index 6ce0c5a..0000000 --- a/src/widget/DeveloperSettingsWidget.h +++ /dev/null @@ -1,47 +0,0 @@ -/*! - * \brief Widget for the developer settings. - * - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include - - -namespace Ui -{ -class DeveloperSettingsWidget; -} - -namespace governikus -{ - -class DeveloperSettingsWidget - : public QWidget -{ - Q_OBJECT - - private: - QScopedPointer mUi; - - private Q_SLOTS: - void onCheckBoxStateChanged(); - virtual void showEvent(QShowEvent*) override; - - protected: - virtual void changeEvent(QEvent* pEvent) override; - - public: - DeveloperSettingsWidget(QWidget* pParent = nullptr); - virtual ~DeveloperSettingsWidget() override; - - void apply(); - void reset(); - - Q_SIGNALS: - void fireSettingsChanged(); -}; - -} /* namespace governikus */ diff --git a/src/widget/DiagnosisDialog.cpp b/src/widget/DiagnosisDialog.cpp deleted file mode 100644 index c2b3651..0000000 --- a/src/widget/DiagnosisDialog.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "DiagnosisDialog.h" - -#include "context/DiagnosisContext.h" -#include "DiagnosisWidget.h" -#include "generic/HelpAction.h" -#include "ui_DiagnosisDialog.h" - -#include -#include -#include -#include - -using namespace governikus; - - -DiagnosisDialog::DiagnosisDialog(DiagnosisContext* pContext, QWidget* pParent) - : QDialog(pParent) - , mUi(new Ui::DiagnosisDialog) - , mDiagnosisWidget(new DiagnosisWidget(pContext, pParent)) -{ - mUi->setupUi(this); - - setAttribute(Qt::WA_DeleteOnClose, true); - setWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint); - setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("Diagnosis")); - - installEventFilter(this); - - mUi->diagnosisLayout->addWidget(mDiagnosisWidget); - - connect(mUi->saveButton, &QAbstractButton::clicked, this, &DiagnosisDialog::onSaveButtonClicked); - connect(mUi->closeButton, &QAbstractButton::clicked, this, &DiagnosisDialog::close); -} - - -DiagnosisDialog::~DiagnosisDialog() -{ -} - - -void DiagnosisDialog::onSaveButtonClicked() -{ - const auto& creationTime = mDiagnosisWidget->getCreationTime(); - - QString filename = QStringLiteral("AusweisApp2.Diagnosis.%1.txt").arg(creationTime.toString(QStringLiteral("yyyy-MM-dd_HH-mm"))); - filename = QFileDialog::getSaveFileName(this, - QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("Save"), - QDir::homePath() + QLatin1Char('/') + filename, -#ifndef Q_OS_MACOS - tr("Text files") + QStringLiteral(" (*.txt)")); -#else - tr("Text files") + QStringLiteral(" (*.txt)"), nullptr, QFileDialog::DontUseNativeDialog); -#endif - - if (filename.isEmpty()) - { - return; - } - if (!filename.endsWith(QLatin1String(".txt"), Qt::CaseSensitivity::CaseInsensitive)) - { - filename += QStringLiteral(".txt"); - } - - QString text = mDiagnosisWidget->getInfoTextEdit(); -#ifdef Q_OS_WIN - text.replace(QLatin1Char('\n'), QStringLiteral("\r\n")); -#endif - - QFile file(filename); - if (!file.open(QIODevice::WriteOnly | QFile::Truncate) || file.write(text.toUtf8()) < 0) - { - QMessageBox box(this); - box.setWindowTitle(QApplication::applicationName() + QStringLiteral(" - ") + tr("File error")); - box.setWindowModality(Qt::ApplicationModal); - box.setIcon(QMessageBox::Warning); - box.setWindowFlags(box.windowFlags() & ~Qt::WindowContextHelpButtonHint); - box.setText(tr("An error occurred while saving the file.")); - box.setStandardButtons(QMessageBox::Ok); - box.button(QMessageBox::Ok)->setFocus(); - box.exec(); - } -} - - -bool DiagnosisDialog::eventFilter(QObject* pObject, QEvent* pEvent) -{ - if (pEvent->type() == QEvent::KeyPress) - { - QKeyEvent* keyEvent = static_cast(pEvent); - if (keyEvent->key() == Qt::Key_F1) - { - HelpAction::openContextHelp(); - return true; - } - } - return QDialog::eventFilter(pObject, pEvent); -} - - -void DiagnosisDialog::changeEvent(QEvent* pEvent) -{ - if (pEvent->type() == QEvent::LanguageChange) - { - mUi->retranslateUi(this); - } - QWidget::changeEvent(pEvent); -} diff --git a/src/widget/DiagnosisDialog.h b/src/widget/DiagnosisDialog.h deleted file mode 100644 index d73ed2c..0000000 --- a/src/widget/DiagnosisDialog.h +++ /dev/null @@ -1,44 +0,0 @@ -/*! - * \brief Dialog for display the diagnosis information. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include - -namespace Ui -{ -class DiagnosisDialog; -} - -namespace governikus -{ - -class DiagnosisContext; -class DiagnosisWidget; - -class DiagnosisDialog - : public QDialog -{ - Q_OBJECT - - private: - QScopedPointer mUi; - DiagnosisWidget* mDiagnosisWidget; - - private Q_SLOTS: - void onSaveButtonClicked(); - - protected: - virtual bool eventFilter(QObject* pObject, QEvent* pEvent) override; - virtual void changeEvent(QEvent* pEvent) override; - - public: - DiagnosisDialog(DiagnosisContext* pContext, QWidget* pParent = nullptr); - virtual ~DiagnosisDialog() override; -}; - -} /* namespace governikus */ diff --git a/src/widget/DiagnosisGui.cpp b/src/widget/DiagnosisGui.cpp deleted file mode 100644 index f41bb04..0000000 --- a/src/widget/DiagnosisGui.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "DiagnosisGui.h" - - -#include "controller/DiagnosisController.h" -#include "DiagnosisWidget.h" - -using namespace governikus; - -DiagnosisGui::DiagnosisGui(QWidget* pParentWidget) - : QObject(pParentWidget) - , mDialog() -{ -} - - -DiagnosisGui::~DiagnosisGui() -{ -} - - -void DiagnosisGui::activate() -{ - if (mDialog) - { - if (mDialog->isMinimized()) - { - mDialog->showNormal(); - } - if (!mDialog->isVisible()) - { - mDialog->show(); - } - mDialog->activateWindow(); - mDialog->raise(); - return; - } - - QWidget* dialogParent = qobject_cast(parent()); - if (!dialogParent) - { - return; - } - - auto context = new DiagnosisContext(); - mDialog = new DiagnosisDialog(context, dialogParent); - connect(mDialog, &QDialog::finished, this, &DiagnosisGui::fireFinished); - mDialog->show(); - - auto controller = new DiagnosisController(context, mDialog); - controller->run(); -} - - -void DiagnosisGui::deactivate() -{ - if (mDialog) - { - mDialog->close(); - } -} diff --git a/src/widget/DiagnosisGui.h b/src/widget/DiagnosisGui.h deleted file mode 100644 index 1e66975..0000000 --- a/src/widget/DiagnosisGui.h +++ /dev/null @@ -1,36 +0,0 @@ -/*! - * \brief Qt widget based DiagnosisUi implementation. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "DiagnosisDialog.h" - -#include -#include - -namespace governikus -{ - -class DiagnosisGui - : public QObject -{ - Q_OBJECT - - public: - DiagnosisGui(QWidget* pParentWidget); - virtual ~DiagnosisGui(); - - void activate(); - void deactivate(); - - Q_SIGNALS: - void fireFinished(); - - private: - QPointer mDialog; -}; - -} /* namespace governikus */ diff --git a/src/widget/DiagnosisWidget.cpp b/src/widget/DiagnosisWidget.cpp deleted file mode 100644 index 10660fb..0000000 --- a/src/widget/DiagnosisWidget.cpp +++ /dev/null @@ -1,416 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "DiagnosisWidget.h" - -#include "LanguageLoader.h" -#include "ui_DiagnosisWidget.h" - -#include -#include -#include -#include -#include -#include - -// Includes for version API -#include - -#include "BuildHelper.h" -#include "context/DiagnosisContext.h" -#include "generic/HelpAction.h" - -using namespace governikus; - -class DiagnosisWidget::Field -{ - public: - Field(QTextCursor& pCursor, const QString& pHeading, const QTextCharFormat& pPlainFormat, const QTextCharFormat& pHeadingFormat) - : mStart() - , mEnd() - { - pCursor.insertText(pHeading + QLatin1Char('\n'), pHeadingFormat); - mStart = pCursor; - mStart.setKeepPositionOnInsert(true); - pCursor.insertText(tr("Diagnosis is running..."), pPlainFormat); - mEnd = pCursor; - mEnd.setKeepPositionOnInsert(true); - pCursor.insertText(QStringLiteral("\n"), pPlainFormat); - } - - - void prepareSet() - { - mEnd.setKeepPositionOnInsert(false); - mEnd.clearSelection(); - mEnd.setPosition(mStart.position(), QTextCursor::KeepAnchor); - } - - - void finishSet() - { - mEnd.setKeepPositionOnInsert(true); - } - - - const QTextCursor& getCursor() const - { - return mEnd; - } - - - void setText(const QString& pText, const QTextCharFormat& pPlainFormat) - { - prepareSet(); - mEnd.insertText(pText, pPlainFormat); - finishSet(); - } - - - void setFragment(const QTextDocumentFragment& pFragment) - { - prepareSet(); - mEnd.insertFragment(pFragment); - finishSet(); - } - - - private: - QTextCursor mStart; - QTextCursor mEnd; -}; - -DiagnosisWidget::DiagnosisWidget(DiagnosisContext* pContext, QWidget* pParent) - : QWidget(pParent) - , mUi(new Ui::DiagnosisWidget) - , mContext(pContext) - , mOsField() - , mAppVersionField() - , mPcscField() - , mReadersField() - , mTimestampField() - , mPlainTextFormat() - , mHeadingTextFormat() - , mBasicBlockFormat() - , mOsVersionTreeItem(nullptr) - , mAppVersionTreeItem(nullptr) - , mReadersTreeItem(nullptr) - , mPcscTreeItem(nullptr) - , mReaderWaitItem(nullptr) - , mPcscWaitItem(nullptr) - , mTimestampItem(nullptr) -{ - mUi->setupUi(this); - - setAttribute(Qt::WA_DeleteOnClose, true); - setWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint); - setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("Diagnosis")); - - QTextCursor insertCursor(mUi->infoTextEdit->document()); - mPlainTextFormat = insertCursor.charFormat(); - mHeadingTextFormat = mPlainTextFormat; - mHeadingTextFormat.setFontWeight(QFont::Bold); - mBasicBlockFormat = insertCursor.blockFormat(); - - mOsField.reset(insertField(insertCursor, tr("Operating system"), true)); - mAppVersionField.reset(insertField(insertCursor, QCoreApplication::applicationName())); - mReadersField.reset(insertField(insertCursor, tr("Card reader"))); - mPcscField.reset(insertField(insertCursor, tr("PC/SC"))); - mTimestampField.reset(insertField(insertCursor, tr("Time of diagnosis"))); - - mOsVersionTreeItem = new QTreeWidgetItem(mUi->infoTreeWidget); - mOsVersionTreeItem->setText(0, tr("Operating system")); - mOsVersionTreeItem->setExpanded(true); - - (new QTreeWidgetItem(mOsVersionTreeItem))->setText(0, QSysInfo::prettyProductName()); - (new QTreeWidgetItem(mOsVersionTreeItem))->setText(0, QSysInfo::kernelVersion()); - (new QTreeWidgetItem(mOsVersionTreeItem))->setText(0, QSysInfo::currentCpuArchitecture()); - - QStringList fields; - fields << QSysInfo::prettyProductName(); - fields << QSysInfo::kernelVersion(); - fields << QSysInfo::currentCpuArchitecture(); - setFieldText(mOsField, fields.join(QLatin1Char('\n'))); - - mPcscWaitItem = new QTreeWidgetItem(mPcscTreeItem); - mPcscWaitItem->setText(0, tr("Diagnosis is running...")); - - mAppVersionTreeItem = new QTreeWidgetItem(mUi->infoTreeWidget); - mAppVersionTreeItem->setText(0, QCoreApplication::applicationName()); - mAppVersionTreeItem->setExpanded(true); - - mReadersTreeItem = new QTreeWidgetItem(mUi->infoTreeWidget); - mReadersTreeItem->setText(0, tr("Card reader")); - mReadersTreeItem->setExpanded(true); - mReaderWaitItem = new QTreeWidgetItem(mReadersTreeItem); - mReaderWaitItem->setText(0, tr("Diagnosis is running...")); - - mPcscTreeItem = new QTreeWidgetItem(mUi->infoTreeWidget); - mPcscTreeItem->setText(0, tr("PC/SC")); - mPcscTreeItem->setExpanded(true); - mPcscWaitItem = new QTreeWidgetItem(mPcscTreeItem); - mPcscWaitItem->setText(0, tr("Diagnosis is running...")); - - mTimestampItem = new QTreeWidgetItem(mUi->infoTreeWidget); - mTimestampItem->setExpanded(true); - mTimestampItem->setText(0, tr("Time of diagnosis")); - - const QStringList appVersion({ - QStringLiteral("%1 (%2)").arg(QCoreApplication::applicationVersion(), QString::fromLatin1(BuildHelper::getDateTime())), - QCoreApplication::organizationName(), - QStringLiteral("Qt ") + QString::fromLatin1(qVersion()), - QString::fromLatin1(SSLeay_version(0)) - }); - for (const auto& str : appVersion) - { - (new QTreeWidgetItem(mAppVersionTreeItem))->setText(0, str); - } - setFieldText(mAppVersionField, appVersion.join(QLatin1Char('\n'))); - - connect(mContext, &DiagnosisContext::pcscInfoChanged, this, &DiagnosisWidget::onPcscInfoChanged); - connect(mContext, &DiagnosisContext::readerInfosChanged, this, &DiagnosisWidget::onReaderInfosChanged); - connect(mContext, &DiagnosisContext::timestampChanged, this, &DiagnosisWidget::onTimestampChanged); - - mUi->infoTextEdit->setVisible(false); -} - - -DiagnosisWidget::~DiagnosisWidget() -{ -} - - -bool DiagnosisWidget::eventFilter(QObject* pObject, QEvent* pEvent) -{ - if (pEvent->type() == QEvent::KeyPress) - { - QKeyEvent* keyEvent = static_cast(pEvent); - if (keyEvent->key() == Qt::Key_F1) - { - HelpAction::openContextHelp(); - return true; - } - } - return QWidget::eventFilter(pObject, pEvent); -} - - -void DiagnosisWidget::onPcscInfoChanged() -{ - mPcscTreeItem->removeChild(mPcscWaitItem); - delete mPcscWaitItem; - mPcscWaitItem = nullptr; - - mPcscField->prepareSet(); - - QTextCursor insertCursor = mPcscField->getCursor(); - insertCursor.removeSelectedText(); - - //version - insertCursor.insertText(tr("Version: %1").arg(mContext->getPcscVersion()), mPlainTextFormat); - endBlockAndResetFormat(insertCursor); - (new QTreeWidgetItem(mPcscTreeItem))->setText(0, tr("Version: %1").arg(mContext->getPcscVersion())); - - //components - QTreeWidgetItem* componentTreeItem = new QTreeWidgetItem(mPcscTreeItem); - componentTreeItem->setText(0, tr("Components")); - insertComponentList(insertCursor, tr("Components:"), mContext->getPcscComponents(), componentTreeItem); - - //driver - QTreeWidgetItem* driverTreeItem = new QTreeWidgetItem(mPcscTreeItem); - driverTreeItem->setText(0, tr("Driver")); - insertComponentList(insertCursor, tr("Driver:"), mContext->getPcscDrivers(), driverTreeItem); - - mPcscField->finishSet(); - -} - - -void DiagnosisWidget::onReaderInfosChanged() -{ - mReadersTreeItem->removeChild(mReaderWaitItem); - delete mReaderWaitItem; - mReaderWaitItem = nullptr; - - const QVector& infos = mContext->getReaderInfos(); - if (infos.isEmpty()) - { - setFieldText(mReadersField, tr("not recognised")); - (new QTreeWidgetItem(mReadersTreeItem))->setText(0, tr("not recognised")); - return; - } - - mReadersField->prepareSet(); - - QTextCursor insertCursor = mReadersField->getCursor(); - insertCursor.removeSelectedText(); - QTextListFormat listFormat; - listFormat.setStyle(QTextListFormat::ListDisc); - QTextList* list = insertCursor.insertList(listFormat); - - bool isFirst = true; - for (const ReaderInfo& info : infos) - { - if (isFirst) - { - isFirst = false; - } - else - { - insertCursor.insertText(QStringLiteral("\n"), mPlainTextFormat); - } - - // Reset the indent. Otherwise each list element would be indented further. - QTextBlockFormat blockFormat = insertCursor.blockFormat(); - blockFormat.setIndent(0); - insertCursor.setBlockFormat(blockFormat); - - // reader name - insertCursor.insertText(info.getName(), mPlainTextFormat); - list->add(insertCursor.block()); - QTreeWidgetItem* readerTreeItem = new QTreeWidgetItem(mReadersTreeItem); - readerTreeItem->setText(0, info.getName()); - - insertCursor.insertText(QStringLiteral("\n"), mPlainTextFormat); - list->remove(insertCursor.block()); - - // reader type - QString readerType = info.isBasicReader() ? tr("Basic card reader") : tr("Standard / deluxe card reader"); - insertCursor.insertText(tr("Type: %1").arg(readerType), mPlainTextFormat); - (new QTreeWidgetItem(readerTreeItem))->setText(0, tr("Type: %1").arg(readerType)); - - // card type - QString cardType = info.getCardTypeString(); - - insertCursor.insertText(QStringLiteral("\n"), mPlainTextFormat); - insertCursor.insertText(tr("Card: %1").arg(cardType), mPlainTextFormat); - (new QTreeWidgetItem(readerTreeItem))->setText(0, tr("Card: %1").arg(cardType)); - - - // retry counter - if (info.hasEidCard()) - { - insertCursor.insertText(QStringLiteral("\n"), mPlainTextFormat); - insertCursor.insertText(tr("Retry counter: %1").arg(3 - info.getRetryCounter()), mPlainTextFormat); - (new QTreeWidgetItem(readerTreeItem))->setText(0, tr("Retry counter: %1").arg(3 - info.getRetryCounter())); - } - } - - mReadersField->finishSet(); -} - - -void DiagnosisWidget::onTimestampChanged() -{ - QString timestamp = LanguageLoader::getInstance().getUsedLocale().toString(getCreationTime(), tr("d. MMMM yyyy, hh:mm:ss AP")); - setFieldText(mTimestampField, timestamp); - (new QTreeWidgetItem(mTimestampItem))->setText(0, timestamp); - -} - - -QString DiagnosisWidget::getInfoTextEdit() const -{ - return mUi->infoTextEdit->toPlainText(); -} - - -QDateTime DiagnosisWidget::getCreationTime() const -{ - return mContext->getTimestamp(); -} - - -DiagnosisWidget::Field* DiagnosisWidget::insertField(QTextCursor& pCursor, const QString& pHeading, bool pIsFirstField) -{ - if (!pIsFirstField) - { - pCursor.insertText(QStringLiteral("\n"), mPlainTextFormat); - } - - return new Field(pCursor, pHeading, mPlainTextFormat, mHeadingTextFormat); -} - - -void DiagnosisWidget::setFieldText(const QScopedPointer& pField, const QString& pText) -{ - pField->setText(pText, mPlainTextFormat); -} - - -void DiagnosisWidget::endBlockAndResetFormat(QTextCursor& pCursor) -{ - pCursor.insertText(QStringLiteral("\n")); - pCursor.setBlockFormat(mBasicBlockFormat); -} - - -void DiagnosisWidget::insertComponentList(QTextCursor& pCursor, const QString& pTitle, - const QVector& pComponents, QTreeWidgetItem* pParentTreeWidgetItem) -{ - if (pComponents.isEmpty()) - { - return; - } - - pCursor.insertText(pTitle, mPlainTextFormat); - - QTextListFormat listFormat; - listFormat.setStyle(QTextListFormat::ListDisc); - QTextList* list = pCursor.insertList(listFormat); - - bool isFirst = true; - for (const DiagnosisContext::ComponentInfo& info : pComponents) - { - if (isFirst) - { - isFirst = false; - } - else - { - pCursor.insertText(QStringLiteral("\n"), mPlainTextFormat); - } - - // Reset the indent. Otherwise each list element would be indented further. - QTextBlockFormat blockFormat = pCursor.blockFormat(); - blockFormat.setIndent(0); - pCursor.setBlockFormat(blockFormat); - - // description - pCursor.insertText(info.getDescription(), mPlainTextFormat); - QTreeWidgetItem* descriptionTreeItem = new QTreeWidgetItem(pParentTreeWidgetItem); - descriptionTreeItem->setText(0, info.getDescription()); - - list->add(pCursor.block()); - - pCursor.insertText(QStringLiteral("\n"), mPlainTextFormat); - list->remove(pCursor.block()); - - // company - pCursor.insertText(tr("Vendor: %1").arg(info.getManufacturer()), mPlainTextFormat); - (new QTreeWidgetItem(descriptionTreeItem))->setText(0, tr("Vendor: %1").arg(info.getManufacturer())); - - // version - pCursor.insertText(QStringLiteral("\n"), mPlainTextFormat); - pCursor.insertText(tr("Version: %1").arg(info.getVersion()), mPlainTextFormat); - (new QTreeWidgetItem(descriptionTreeItem))->setText(0, tr("Version: %1").arg(info.getVersion())); - - // path - pCursor.insertText(QStringLiteral("\n"), mPlainTextFormat); - pCursor.insertText(tr("File path: %1").arg(info.getPath()), mPlainTextFormat); - (new QTreeWidgetItem(descriptionTreeItem))->setText(0, tr("File path: %1").arg(info.getPath())); - } - - endBlockAndResetFormat(pCursor); -} - - -void DiagnosisWidget::changeEvent(QEvent* pEvent) -{ - if (pEvent->type() == QEvent::LanguageChange) - { - mUi->retranslateUi(this); - } - QWidget::changeEvent(pEvent); -} diff --git a/src/widget/DiagnosisWidget.h b/src/widget/DiagnosisWidget.h deleted file mode 100644 index f7bc4ef..0000000 --- a/src/widget/DiagnosisWidget.h +++ /dev/null @@ -1,82 +0,0 @@ -/*! - * \brief Widget for display the diagnosis information. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include -#include -#include -#include - -#include "context/DiagnosisContext.h" - -namespace Ui -{ -class DiagnosisWidget; -} - -class QTextCursor; - -namespace governikus -{ - -class DiagnosisWidget - : public QWidget -{ - Q_OBJECT - - private: - class Field; - - private Q_SLOTS: - void onPcscInfoChanged(); - void onReaderInfosChanged(); - void onTimestampChanged(); - - private: - Field* insertField(QTextCursor& pCursor, const QString& pHeading, bool pIsFirstField = false); - void setFieldText(const QScopedPointer& pField, const QString& pText); - - void endBlockAndResetFormat(QTextCursor& pCursor); - void insertComponentList(QTextCursor& pCursor, const QString& pTitle, - const QVector& pComponents, QTreeWidgetItem* pParentTreeWidgetItem); - - void deleteItem(QTreeWidgetItem* pItem); - - private: - QScopedPointer mUi; - DiagnosisContext* mContext; - QScopedPointer mOsField; - QScopedPointer mAppVersionField; - QScopedPointer mPcscField; - QScopedPointer mReadersField; - QScopedPointer mTimestampField; - QTextCharFormat mPlainTextFormat; - QTextCharFormat mHeadingTextFormat; - QTextBlockFormat mBasicBlockFormat; - - QTreeWidgetItem* mOsVersionTreeItem; - QTreeWidgetItem* mAppVersionTreeItem; - QTreeWidgetItem* mReadersTreeItem; - QTreeWidgetItem* mPcscTreeItem; - QTreeWidgetItem* mReaderWaitItem; - QTreeWidgetItem* mPcscWaitItem; - QTreeWidgetItem* mTimestampItem; - - protected: - virtual bool eventFilter(QObject* pObject, QEvent* pEvent) override; - virtual void changeEvent(QEvent* pEvent) override; - - public: - DiagnosisWidget(DiagnosisContext* pContext, QWidget* pParent = nullptr); - virtual ~DiagnosisWidget() override; - - QString getInfoTextEdit() const; - QDateTime getCreationTime() const; -}; - -} /* namespace governikus */ diff --git a/src/widget/GeneralSettingsWidget.cpp b/src/widget/GeneralSettingsWidget.cpp deleted file mode 100644 index c51e2df..0000000 --- a/src/widget/GeneralSettingsWidget.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "GeneralSettingsWidget.h" - -#include "ui_GeneralSettingsWidget.h" - -#include "AppSettings.h" -#include "Env.h" -#include "Service.h" -#include "UpdateWindow.h" - -using namespace governikus; - -GeneralSettingsWidget::GeneralSettingsWidget(QWidget* pParent) - : QWidget(pParent) - , mUi(new Ui::GeneralSettingsWidget()) -{ - mUi->setupUi(this); - - reset(); - - connect(mUi->autostartCheckBox, &QCheckBox::stateChanged, this, &GeneralSettingsWidget::onCheckBoxStateChanged); - connect(mUi->regularlyUpdateCheckBox, &QCheckBox::stateChanged, this, &GeneralSettingsWidget::onCheckBoxStateChanged); - connect(mUi->closeWindowCheckBox, &QCheckBox::stateChanged, this, &GeneralSettingsWidget::onCheckBoxStateChanged); - connect(mUi->saveHistoryCheckBox, &QCheckBox::stateChanged, this, &GeneralSettingsWidget::onCheckBoxStateChanged); - connect(mUi->updateCheckButton, &QCheckBox::clicked, this, &GeneralSettingsWidget::onUpdateCheckButtonClicked); - connect(mUi->keylessPasswordCheckBox, &QCheckBox::stateChanged, this, &GeneralSettingsWidget::onCheckBoxStateChanged); -} - - -GeneralSettingsWidget::~GeneralSettingsWidget() -{ -} - - -void GeneralSettingsWidget::showEvent(QShowEvent* pEvent) -{ - disconnect(mUi->saveHistoryCheckBox, &QCheckBox::stateChanged, this, &GeneralSettingsWidget::onCheckBoxStateChanged); - mUi->saveHistoryCheckBox->setChecked(AppSettings::getInstance().getHistorySettings().isEnabled()); - connect(mUi->saveHistoryCheckBox, &QCheckBox::stateChanged, this, &GeneralSettingsWidget::onCheckBoxStateChanged); - - QWidget::showEvent(pEvent); -} - - -void GeneralSettingsWidget::apply() -{ - auto& historySettings = AppSettings::getInstance().getHistorySettings(); - historySettings.setEnabled(mUi->saveHistoryCheckBox->isChecked()); - historySettings.save(); - - auto& generalSettings = AppSettings::getInstance().getGeneralSettings(); - generalSettings.setAutoCloseWindowAfterAuthentication(mUi->closeWindowCheckBox->isChecked()); - generalSettings.setAutoStart(mUi->autostartCheckBox->isChecked()); - generalSettings.setAutoUpdateCheck(mUi->regularlyUpdateCheckBox->isChecked()); - generalSettings.setUseScreenKeyboard(mUi->keylessPasswordCheckBox->isChecked()); - generalSettings.save(); -} - - -void GeneralSettingsWidget::reset() -{ - const auto& historySettings = AppSettings::getInstance().getHistorySettings(); - mUi->saveHistoryCheckBox->setChecked(historySettings.isEnabled()); - - const auto& generalSettings = AppSettings::getInstance().getGeneralSettings(); - mUi->closeWindowCheckBox->setChecked(generalSettings.isAutoCloseWindowAfterAuthentication()); - mUi->autostartCheckBox->setChecked(generalSettings.isAutoStart()); - mUi->regularlyUpdateCheckBox->setChecked(generalSettings.isAutoUpdateCheck()); - mUi->keylessPasswordCheckBox->setChecked(generalSettings.isUseScreenKeyboard()); -} - - -void GeneralSettingsWidget::onCheckBoxStateChanged(int) -{ - Q_EMIT settingsChanged(); -} - - -void GeneralSettingsWidget::onUpdateCheckButtonClicked() -{ - Env::getSingleton()->updateApp(true); -} - - -void GeneralSettingsWidget::changeEvent(QEvent* pEvent) -{ - if (pEvent->type() == QEvent::LanguageChange) - { - mUi->retranslateUi(this); - } - QWidget::changeEvent(pEvent); -} diff --git a/src/widget/GeneralSettingsWidget.h b/src/widget/GeneralSettingsWidget.h deleted file mode 100644 index eca43c3..0000000 --- a/src/widget/GeneralSettingsWidget.h +++ /dev/null @@ -1,49 +0,0 @@ -/*! - * \brief Widget for the general settings. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "GlobalStatus.h" - -#include -#include - -namespace Ui -{ -class GeneralSettingsWidget; -} - -namespace governikus -{ - -class GeneralSettingsWidget - : public QWidget -{ - Q_OBJECT - - private: - QScopedPointer mUi; - - private Q_SLOTS: - void onCheckBoxStateChanged(int pState); - void onUpdateCheckButtonClicked(); - virtual void showEvent(QShowEvent*) override; - - protected: - virtual void changeEvent(QEvent* pEvent) override; - - public: - GeneralSettingsWidget(QWidget* pParent = nullptr); - virtual ~GeneralSettingsWidget() override; - - void apply(); - void reset(); - - Q_SIGNALS: - void settingsChanged(); -}; - -} /* namespace governikus */ diff --git a/src/widget/GuiProfile.cpp b/src/widget/GuiProfile.cpp deleted file mode 100644 index a3df7e8..0000000 --- a/src/widget/GuiProfile.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "GuiProfile.h" - -using namespace governikus; - -GuiProfile GuiProfile::mProfile; - - -GuiProfile::GuiProfile() - : mShowWindow(false) -{ -} - - -bool GuiProfile::getShowWindow() const -{ - return mShowWindow; -} - - -void GuiProfile::setShowWindow(bool pShow) -{ - mShowWindow = pShow; -} diff --git a/src/widget/GuiProfile.h b/src/widget/GuiProfile.h deleted file mode 100644 index ed6a225..0000000 --- a/src/widget/GuiProfile.h +++ /dev/null @@ -1,33 +0,0 @@ -/*! - * \brief Singleton GuiProfile specifies platform specific customizations the - * GUI. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -namespace governikus -{ - -class GuiProfile -{ - private: - static GuiProfile mProfile; - GuiProfile(); - ~GuiProfile() = default; - - bool mShowWindow; - - public: - static GuiProfile& getProfile() - { - return mProfile; - } - - - void setShowWindow(bool pShow); - bool getShowWindow() const; -}; - -} /* namespace governikus */ diff --git a/src/widget/HistoryDetailWidget.cpp b/src/widget/HistoryDetailWidget.cpp deleted file mode 100644 index 8807245..0000000 --- a/src/widget/HistoryDetailWidget.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "HistoryDetailWidget.h" - -#include "ui_HistoryDetailWidget.h" - -#include -#include - -using namespace governikus; - - -HistoryDetailWidget::HistoryDetailWidget(QWidget* pParent) - : QWidget(pParent) - , mUi(new Ui::HistoryDetailWidget()) -{ - mUi->setupUi(this); -} - - -HistoryDetailWidget::~HistoryDetailWidget() -{ -} - - -void HistoryDetailWidget::setDetails(const QString& pDetails) -{ - mUi->detailText->setText(pDetails); -} - - -void HistoryDetailWidget::paintEvent(QPaintEvent*) -{ - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} - - -void HistoryDetailWidget::changeEvent(QEvent* pEvent) -{ - if (pEvent->type() == QEvent::LanguageChange) - { - mUi->retranslateUi(this); - } - QWidget::changeEvent(pEvent); -} diff --git a/src/widget/HistoryDetailWidget.h b/src/widget/HistoryDetailWidget.h deleted file mode 100644 index 3349add..0000000 --- a/src/widget/HistoryDetailWidget.h +++ /dev/null @@ -1,40 +0,0 @@ -/*! - * \brief Widget for history item. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include - -namespace Ui -{ -class HistoryDetailWidget; -} - -namespace governikus -{ - -class HistoryDetailWidget - : public QWidget -{ - Q_OBJECT - - private: - QScopedPointer mUi; - - virtual void paintEvent(QPaintEvent*) override; - - protected: - virtual void changeEvent(QEvent* pEvent) override; - - public: - HistoryDetailWidget(QWidget* pParent = nullptr); - virtual ~HistoryDetailWidget() override; - - void setDetails(const QString& pDetails); -}; - -} /* namespace governikus */ diff --git a/src/widget/HistoryWidget.cpp b/src/widget/HistoryWidget.cpp deleted file mode 100644 index 267252d..0000000 --- a/src/widget/HistoryWidget.cpp +++ /dev/null @@ -1,304 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "HistoryWidget.h" - -#include "ui_HistoryWidget.h" - -#include "AppSettings.h" -#include "DeleteHistoryDialog.h" -#include "DetailDialog.h" -#include "generic/ListCheckItemWidget.h" -#include "generic/ListItem.h" -#include "generic/ListItemIconLeft.h" -#include "generic/ListItemIconRight.h" -#include "generic/ListItemSubTitle.h" -#include "generic/ListItemTitle.h" -#include "LanguageLoader.h" -#include "PdfExporter.h" -#include "ScopeGuard.h" - -#include -#include -#include -#include - -using namespace governikus; - - -HistoryWidget::HistoryWidget(QWidget* pParent) - : QWidget(pParent) - , mUi(new Ui::HistoryWidget()) - , mHistoryDetailWidget(nullptr) -{ - mUi->setupUi(this); - - connect(mUi->historyDeleteButton, &QPushButton::clicked, this, &HistoryWidget::deleteHistory); - connect(mUi->historySearch, &QLineEdit::textChanged, this, &HistoryWidget::searchHistory); - connect(mUi->historyExportButton, &QPushButton::clicked, this, &HistoryWidget::exportHistory); - connect(mUi->historyTableWidget, &QTableWidget::doubleClicked, this, &HistoryWidget::onItemClicked); - connect(mUi->saveHistoryCheckBox, &QCheckBox::stateChanged, this, &HistoryWidget::onCheckBoxStateChanged); - mUi->saveHistoryCheckBox->setChecked(AppSettings::getInstance().getHistorySettings().isEnabled()); - - connect(&AppSettings::getInstance().getHistorySettings(), &HistorySettings::fireHistoryInfosChanged, this, &HistoryWidget::updateTable); - connect(&AppSettings::getInstance().getHistorySettings(), &HistorySettings::fireEnabledChanged, this, [this](bool pValue){ - mUi->saveHistoryCheckBox->setChecked(pValue); - }); - - init(); -} - - -HistoryWidget::~HistoryWidget() -{ -} - - -void HistoryWidget::init() -{ - QStringList header; - header += tr("Date"); - header += tr("Details"); - - mUi->historyTableWidget->setColumnCount(header.count()); - mUi->historyTableWidget->setHorizontalHeaderLabels(header); - mUi->historyTableWidget->verticalHeader()->setVisible(false); //Hide row number - mUi->historyTableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); //Not allowed to change content - mUi->historyTableWidget->setSortingEnabled(false); - mUi->historyTableWidget->installEventFilter(this); - - mUi->noResultWidget->setVisible(false); - updateTable(); -} - - -QWidget* HistoryWidget::getDetailActivatingWidget() const -{ - return mUi->historyTableWidget; -} - - -void HistoryWidget::paintEvent(QPaintEvent*) -{ - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} - - -void HistoryWidget::onCheckBoxStateChanged(int /*pState*/) -{ - auto& historySettings = AppSettings::getInstance().getHistorySettings(); - historySettings.setEnabled(mUi->saveHistoryCheckBox->isChecked()); - historySettings.save(); -} - - -void HistoryWidget::updateTable() -{ - const auto& items = AppSettings::getInstance().getHistorySettings().getHistoryInfos(); - - const ScopeGuard guard([this] { - mUi->historyTableWidget->setUpdatesEnabled(true); - }); - - mUi->historyTableWidget->setUpdatesEnabled(false); - mUi->historyTableWidget->clearContents(); - mUi->historyTableWidget->setRowCount(0); - - for (const HistoryInfo& info : items) - { - int rowIndex = mUi->historyTableWidget->rowCount(); - mUi->historyTableWidget->insertRow(rowIndex); - - //date column with needed properties - const auto& dateTime = LanguageLoader::getInstance().getUsedLocale().toString(info.getDateTime(), tr("dd.MM.yyyy hh:mm AP")); - QLabel* dateLabel = new QLabel(dateTime); - dateLabel->setContentsMargins(11, 11, 11, 11); - dateLabel->setAlignment(Qt::AlignTop); - dateLabel->setProperty("termsOfUsage", info.getTermOfUsage()); - dateLabel->setProperty("date", dateTime); - - dateLabel->setFocusPolicy(Qt::TabFocus); - dateLabel->setAccessibleName(tr("Date:") + dateTime); - - mUi->historyTableWidget->setCellWidget(rowIndex, 0, dateLabel); - - //details column with needed properties - QWidget* centralWidget = new QWidget(); - - QFormLayout* centralLayout = new QFormLayout(centralWidget); - centralLayout->setSpacing(6); - centralLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); - - const auto& bold = QStringLiteral("%1:"); - QLabel* providerLabel = new QLabel(bold.arg(tr("Provider"))); - providerLabel->setFocusPolicy(Qt::TabFocus); - centralLayout->setWidget(0, QFormLayout::LabelRole, providerLabel); - - QLabel* providerField = new QLabel(info.getSubjectName()); - providerField->setFocusPolicy(Qt::TabFocus); - centralLayout->setWidget(0, QFormLayout::FieldRole, providerField); - - centralWidget->setProperty("provider", info.getSubjectName()); - - QLabel* purposeLabel = new QLabel(bold.arg(tr("Purpose"))); - purposeLabel->setFocusPolicy(Qt::TabFocus); - centralLayout->setWidget(1, QFormLayout::LabelRole, purposeLabel); - - QLabel* purposeField = new QLabel(info.getPurpose()); - purposeField->setFocusPolicy(Qt::TabFocus); - centralLayout->setWidget(1, QFormLayout::FieldRole, purposeField); - - centralWidget->setProperty("usage", info.getPurpose()); - - QLabel* dataLabel = new QLabel(bold.arg(tr("Data"))); - dataLabel->setFocusPolicy(Qt::TabFocus); - dataLabel->setAlignment(Qt::AlignTop); - centralLayout->setWidget(2, QFormLayout::LabelRole, dataLabel); - - QLabel* requestedDataLabel = new QLabel(info.getRequestedData().trimmed()); - requestedDataLabel->setWordWrap(true); - requestedDataLabel->setFocusPolicy(Qt::TabFocus); - centralLayout->setWidget(2, QFormLayout::FieldRole, requestedDataLabel); - - centralWidget->setProperty("requestedData", info.getRequestedData()); - - mUi->historyTableWidget->setCellWidget(rowIndex, 1, centralWidget); - } - - mUi->historyTableWidget->resizeRowsToContents(); - mUi->historyTableWidget->sortByColumn(dateColumn); // Sort by date -} - - -bool HistoryWidget::eventFilter(QObject* pObject, QEvent* pEvent) -{ - if (pEvent->type() == QEvent::KeyPress) - { - QKeyEvent* pressed = static_cast(pEvent); - if ((pressed->key() == Qt::Key_Enter) || (pressed->key() == Qt::Key_Return) || (pressed->key() == Qt::Key_Space)) - { - const auto selectedIndexes = mUi->historyTableWidget->selectionModel()->selectedIndexes(); - for (auto index : selectedIndexes) - { - QWidget* tmpWidget = mUi->historyTableWidget->cellWidget(index.row(), dateColumn); - DetailDialog d(this); - d.setDetails(tmpWidget->property("termsOfUsage").toString()); - d.exec(); - } - return true; - } - } - - return QWidget::eventFilter(pObject, pEvent); -} - - -void HistoryWidget::deleteHistory() -{ - DeleteHistoryDialog* deleteHistoryDialog = new DeleteHistoryDialog(this); - if (deleteHistoryDialog->exec() == QDialog::Rejected) - { - return; - } - - auto& settings = AppSettings::getInstance().getHistorySettings(); - settings.deleteSettings(deleteHistoryDialog->getTimePeriod()); - settings.save(); -} - - -void HistoryWidget::exportHistory() -{ - QString filename = tr("AusweisApp2.History.%1.pdf").arg(QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd"))); - filename = QFileDialog::getSaveFileName(this, - QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("Save"), - QDir::homePath() + QLatin1Char('/') + filename, -#ifndef Q_OS_MACOS - tr("PDF Documents") + QStringLiteral(" (*.pdf)")); -#else - tr("PDF Documents") + QStringLiteral(" (*.pdf)"), nullptr, QFileDialog::DontUseNativeDialog); -#endif - - PdfExporter exporter(filename); - exporter.exportHistory(); -} - - -void HistoryWidget::searchHistory() -{ - mUi->historyTableWidget->setVisible(true); - mUi->noResultWidget->setVisible(false); - - if (mUi->historySearch->text().isEmpty()) - { - for (int i = 0; i < mUi->historyTableWidget->rowCount(); ++i) - { - mUi->historyTableWidget->setRowHidden(i, false); - } - return; - } - - bool anyMatch = false; - - for (int i = 0; i < mUi->historyTableWidget->rowCount(); ++i) - { - bool match = false; - for (int j = 0; j < mUi->historyTableWidget->columnCount(); ++j) - { - QString tmpData; - QWidget* tmpWidget = mUi->historyTableWidget->cellWidget(i, j); - if (j == dateColumn) - { - tmpData = tmpWidget->property("date").toString(); - } - else - { - tmpData = tmpWidget->property("provider").toString() + QLatin1Char(' ') + tmpWidget->property("usage").toString() + QLatin1Char(' ') + tmpWidget->property("requestedData").toString(); - } - - if (tmpData.contains(mUi->historySearch->text(), Qt::CaseInsensitive)) - { - match = true; - anyMatch = true; - break; - } - } - mUi->historyTableWidget->setRowHidden(i, !match); - - } - - mUi->historyTableWidget->setVisible(anyMatch); - mUi->noResultWidget->setVisible(!anyMatch); -} - - -void HistoryWidget::onItemClicked(const QModelIndex& pIndex) -{ - QModelIndex idx = pIndex; - - qDebug() << "selected model index: " << idx; - - if (pIndex.isValid()) - { - QWidget* tmpWidget = mUi->historyTableWidget->cellWidget(idx.row(), dateColumn); - DetailDialog d(this); - d.setDetails(tmpWidget->property("termsOfUsage").toString()); - d.exec(); - } -} - - -void HistoryWidget::changeEvent(QEvent* pEvent) -{ - if (pEvent->type() == QEvent::LanguageChange) - { - init(); - searchHistory(); - mUi->retranslateUi(this); - } - QWidget::changeEvent(pEvent); -} diff --git a/src/widget/HistoryWidget.h b/src/widget/HistoryWidget.h deleted file mode 100644 index 335cd9a..0000000 --- a/src/widget/HistoryWidget.h +++ /dev/null @@ -1,61 +0,0 @@ -/*! - * \brief Show history entries. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Ui -{ -class HistoryWidget; -} - -namespace governikus -{ - -class HistoryDetailWidget; - -class HistoryWidget - : public QWidget -{ - Q_OBJECT - - private: - static const int dateColumn = 0; - QScopedPointer mUi; - HistoryDetailWidget* mHistoryDetailWidget; - virtual bool eventFilter(QObject* pWatched, QEvent* pEvent) override; - virtual void paintEvent(QPaintEvent*) override; - void init(); - - private Q_SLOTS: - void updateTable(); - - protected: - virtual void changeEvent(QEvent* pEvent) override; - - public: - HistoryWidget(QWidget* pParent = nullptr); - virtual ~HistoryWidget() override; - QWidget* getDetailActivatingWidget() const; - - public Q_SLOTS: - void onCheckBoxStateChanged(int pState); - void deleteHistory(); - void exportHistory(); - void searchHistory(); - void onItemClicked(const QModelIndex& pIndex); -}; - -} /* namespace governikus */ diff --git a/src/widget/HistoryWidget.ui b/src/widget/HistoryWidget.ui deleted file mode 100644 index 7f682c4..0000000 --- a/src/widget/HistoryWidget.ui +++ /dev/null @@ -1,200 +0,0 @@ - - - HistoryWidget - - - - 0 - 0 - 599 - 666 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - Qt::TabFocus - - - This page displays the history of your successful authentications. Double-click on a service provider for more information. You can delete parts or the entire history. You can also save the history as a PDF file. - - - true - - - - - - - - - Qt::TabFocus - - - Search: - - - - - - - Please enter your search - - - Please enter your search - - - - - - - - - - - - QAbstractScrollArea::AdjustToContents - - - false - - - true - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectItems - - - QAbstractItemView::ScrollPerPixel - - - QAbstractItemView::ScrollPerPixel - - - true - - - 150 - - - false - - - true - - - - - - - - 5 - - - - - No matching history entries were found. Please modify your search criteria. - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - Qt::TabFocus - - - save history: - - - History: - - - - - - - save history - - - - - - save - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Delete history... - - - - - - - save history as PDF - - - Save as PDF... - - - - - - - - - - - - diff --git a/src/widget/LogFilesDialog.cpp b/src/widget/LogFilesDialog.cpp deleted file mode 100644 index 300ad80..0000000 --- a/src/widget/LogFilesDialog.cpp +++ /dev/null @@ -1,235 +0,0 @@ -/*! - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "LanguageLoader.h" -#include "LogFilesDialog.h" -#include "ui_LogFilesDialog.h" - -#include "generic/HelpAction.h" -#include "LogHandler.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -Q_DECLARE_LOGGING_CATEGORY(gui) - -using namespace governikus; - - -LogFilesDialog::LogFilesDialog(QWidget* pParent) - : QDialog(pParent) - , mUi(new Ui::LogFilesDialog) -{ - mUi->setupUi(this); - - setAttribute(Qt::WA_DeleteOnClose, true); - setWindowFlags(Qt::Dialog | Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowMinMaxButtonsHint); - setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("Log")); - - connect(mUi->saveButton, &QAbstractButton::clicked, this, &LogFilesDialog::onSaveButtonClicked); - connect(mUi->deleteButton, &QAbstractButton::clicked, this, &LogFilesDialog::onDeleteButtonClicked); - connect(mUi->closeButton, &QAbstractButton::clicked, this, &LogFilesDialog::close); - - init(); -} - - -LogFilesDialog::~LogFilesDialog() -{ -} - - -void LogFilesDialog::init() -{ - mUi->logFilesComboBox->clear(); - - auto model = new QStandardItemModel(this); - mUi->logFilesComboBox->setModel(model); - - const auto& otherLogs = LogHandler::getInstance().getOtherLogfiles(); - QList items; - items.reserve(otherLogs.size() + 1); - items << new QStandardItem(tr("Current log")); - for (const auto& entry : otherLogs) - { - auto date = LogHandler::getFileDate(entry); - auto item = new QStandardItem(LanguageLoader::getInstance().getUsedLocale().toString(date, tr("dd.MM.yyyy hh:mm:ss AP"))); - item->setData(date, Qt::UserRole); - item->setData(entry.absoluteFilePath()); - items << item; - } - - mUi->logFilesComboBox->setEnabled(!otherLogs.isEmpty()); - mUi->deleteButton->setEnabled(!otherLogs.isEmpty()); - - model->invisibleRootItem()->appendRows(items); - model->setSortRole(Qt::UserRole); - model->sort(0, Qt::DescendingOrder); - - connect(mUi->logFilesComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &LogFilesDialog::onCurrentIndexChanged); - - mUi->plainTextEdit->clear(); - appendLoggingDump(QString::fromUtf8(LogHandler::getInstance().getBacklog()).toHtmlEscaped()); - mUi->plainTextEdit->moveCursor(QTextCursor::Start); - connect(&LogHandler::getInstance(), &LogHandler::fireLog, this, &LogFilesDialog::doLogMsg); -} - - -void LogFilesDialog::appendLoggingDump(const QString& pLog) -{ - mUi->plainTextEdit->appendHtml(QStringLiteral("
%1
").arg(pLog)); -} - - -void LogFilesDialog::doLogMsg(const QString& pMsg) -{ - appendLoggingDump(pMsg.toHtmlEscaped()); -} - - -void LogFilesDialog::onCurrentIndexChanged(int pIndex) -{ - mUi->plainTextEdit->clear(); - if (pIndex == 0) - { - connect(&LogHandler::getInstance(), &LogHandler::fireLog, this, &LogFilesDialog::doLogMsg); - - appendLoggingDump(QString::fromUtf8(LogHandler::getInstance().getBacklog()).toHtmlEscaped()); - mUi->plainTextEdit->moveCursor(QTextCursor::Start); - } - else - { - disconnect(&LogHandler::getInstance(), &LogHandler::fireLog, this, &LogFilesDialog::doLogMsg); - - QFile file(mUi->logFilesComboBox->itemData(pIndex, Qt::UserRole + 1).toString()); - if (file.size() < 3145728) - { - if (file.open(QIODevice::ReadOnly)) - { - appendLoggingDump(QString::fromUtf8(file.readAll())); - } - else - { - appendLoggingDump(tr("File could not be opened: ") + file.fileName()); - } - } - else - { - appendLoggingDump(tr("File is larger than 3 MB and can not be displayed: ") + file.fileName()); - } - } -} - - -void LogFilesDialog::onDeleteButtonClicked() -{ - QMessageBox box(this); - box.setWindowTitle(tr("Delete log files")); - box.setWindowModality(Qt::ApplicationModal); - box.setIcon(QMessageBox::Question); - box.setWindowFlags(box.windowFlags() & ~Qt::WindowContextHelpButtonHint); - box.setText(tr("Do you really want to delete all old log files?")); - box.setStandardButtons(QMessageBox::Yes | QMessageBox::No); - box.button(QMessageBox::Yes)->setFocus(); - - if (box.exec() == QMessageBox::Yes) - { - LogHandler::getInstance().removeOtherLogfiles(); - init(); - } -} - - -void LogFilesDialog::onSaveButtonClicked() -{ - const int index = mUi->logFilesComboBox->currentIndex(); - - QString source; - if (index != 0) - { - source = mUi->logFilesComboBox->itemData(index, Qt::UserRole + 1).toString(); - } - - saveLogFile(this, source); -} - - -bool LogFilesDialog::eventFilter(QObject* pObject, QEvent* pEvent) -{ - if (pEvent->type() == QEvent::KeyPress) - { - QKeyEvent* keyEvent = static_cast(pEvent); - if (keyEvent->key() == Qt::Key_F1) - { - HelpAction::openContextHelp(); - return true; - } - } - return QDialog::eventFilter(pObject, pEvent); -} - - -void LogFilesDialog::changeEvent(QEvent* pEvent) -{ - if (pEvent->type() == QEvent::LanguageChange) - { - mUi->retranslateUi(this); - } - QWidget::changeEvent(pEvent); -} - - -void LogFilesDialog::saveLogFile(QWidget* pParent, const QString& pSource) -{ - const QDateTime creationDateTime = pSource.isEmpty() ? LogHandler::getInstance().getCurrentLogfileDate() : LogHandler::getFileDate(pSource); - - QString filename = QStringLiteral("AusweisApp2.%1.log").arg(creationDateTime.toString(QStringLiteral("yyyy-MM-dd_HH-mm"))); - filename = QFileDialog::getSaveFileName(pParent, - QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("Save"), - QDir::homePath() + QLatin1Char('/') + filename, -#ifndef Q_OS_MACOS - QStringLiteral("*.log")); -#else - QStringLiteral("*.log"), nullptr, QFileDialog::DontUseNativeDialog); -#endif - if (!filename.isEmpty()) // if user does not select "cancel" - { - if (!filename.endsWith(QLatin1String(".log"), Qt::CaseSensitivity::CaseInsensitive) - && !filename.endsWith(QLatin1String(".txt"), Qt::CaseSensitivity::CaseInsensitive)) - { - filename += QStringLiteral(".log"); - } - - qCDebug(gui) << "File location:" << filename; - - if (QFile::exists(filename)) - { - bool deleted = QFile::remove(filename); - qCDebug(gui) << "Delete file location:" << deleted; - } - - bool copied = pSource.isEmpty() ? LogHandler::getInstance().copy(filename) : QFile::copy(pSource, filename); - qCDebug(gui) << "Copy log to file location:" << copied; - if (!copied) - { - QMessageBox box(pParent); - box.setWindowTitle(QApplication::applicationName() + QStringLiteral(" - ") + tr("File error")); - box.setWindowModality(Qt::ApplicationModal); - box.setIcon(QMessageBox::Warning); - box.setWindowFlags(box.windowFlags() & ~Qt::WindowContextHelpButtonHint); - box.setText(tr("An error occurred while saving the file.")); - box.setStandardButtons(QMessageBox::Ok); - box.button(QMessageBox::Ok)->setFocus(); - box.exec(); - } - } -} diff --git a/src/widget/LogFilesDialog.h b/src/widget/LogFilesDialog.h deleted file mode 100644 index fcdb05a..0000000 --- a/src/widget/LogFilesDialog.h +++ /dev/null @@ -1,48 +0,0 @@ -/*! - * \brief Dialog for display the old log files. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include - -namespace Ui -{ -class LogFilesDialog; -} - -namespace governikus -{ - -class LogFilesDialog - : public QDialog -{ - Q_OBJECT - - public: - static void saveLogFile(QWidget* pParent, const QString& pSource = QString()); - - LogFilesDialog(QWidget* pParent = nullptr); - virtual ~LogFilesDialog() override; - - protected: - virtual bool eventFilter(QObject* pObject, QEvent* pEvent) override; - virtual void changeEvent(QEvent* pEvent) override; - - private: - QScopedPointer mUi; - - void init(); - void appendLoggingDump(const QString& pLog); - - private Q_SLOTS: - void doLogMsg(const QString& pMsg); - void onSaveButtonClicked(); - void onCurrentIndexChanged(int pIndex); - void onDeleteButtonClicked(); -}; - -} /* namespace governikus */ diff --git a/src/widget/PinSettingsInfoWidget.h b/src/widget/PinSettingsInfoWidget.h deleted file mode 100644 index 48c1630..0000000 --- a/src/widget/PinSettingsInfoWidget.h +++ /dev/null @@ -1,41 +0,0 @@ -/*! - * \brief Widget for PIN settings information. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include - -namespace Ui -{ -class PinSettingsInfoWidget; -} - -namespace governikus -{ - -class PinSettingsInfoWidget - : public QWidget -{ - Q_OBJECT - - public: - PinSettingsInfoWidget(QWidget* pParent = nullptr); - virtual ~PinSettingsInfoWidget() override; - - void setInfoTitle(const QString& pTitle); - void setInfoDescription(const QString& pDescription); - - protected: - virtual void changeEvent(QEvent* pEvent) override; - - private: - QScopedPointer mUi; - - virtual void paintEvent(QPaintEvent*) override; -}; - -} /* namespace governikus */ diff --git a/src/widget/PinSettingsWidget.cpp b/src/widget/PinSettingsWidget.cpp deleted file mode 100644 index 9e89297..0000000 --- a/src/widget/PinSettingsWidget.cpp +++ /dev/null @@ -1,739 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "PinSettingsWidget.h" - -#include "Env.h" -#include "generic/PasswordEdit.h" -#include "RandomPinDialog.h" -#include "ReaderConfiguration.h" -#include "ReaderInfo.h" -#include "ReaderManager.h" -#include "SmartCardDefinitions.h" -#include "ui_PinSettingsWidget.h" - -#include -#include -#include -#include - -Q_DECLARE_LOGGING_CATEGORY(gui) - -using namespace governikus; - -PinSettingsWidget::PinSettingsWidget(QWidget* pParent) - : QWidget(pParent) - , mUi(new Ui::PinSettingsWidget()) - , mMode(Mode::Normal) - , mRetryCounter(3) - , mPinDeactivated(false) - , mPinButtonEnabled(false) - , mPinSettingsInfoTitle() - , mPinSettingsInfoDescription() - , mRandomPinDialog() -{ - mUi->setupUi(this); - - QRegularExpression onlyNumbersExpression(QStringLiteral("[0-9]*")); - - mUi->canEdit->setMaxLength(6); - mUi->canEdit->configureValidation(onlyNumbersExpression, tr("Only digits (0-9) are allowed.")); - connect(mUi->canEdit, &PasswordEdit::textEdited, this, &PinSettingsWidget::onCanTextEdited); - - mUi->oldPinEdit->setMaxLength(6); - mUi->oldPinEdit->configureValidation(onlyNumbersExpression, tr("Only digits (0-9) are allowed.")); - connect(mUi->oldPinEdit, &PasswordEdit::textEdited, this, &PinSettingsWidget::onOldPinTextEdited); - connect(mUi->oldPinEdit, &PasswordEdit::fireBackspacePressedAndEmpty, this, [this] { - if (mUi->canEdit->isVisible()) - { - mUi->canEdit->removeLastCharacter(); - mUi->canEdit->setFocus(); - } - }); - - mUi->newPinEdit->setMaxLength(6); - mUi->newPinEdit->configureValidation(onlyNumbersExpression, tr("Only digits (0-9) are allowed.")); - connect(mUi->newPinEdit, &PasswordEdit::textEdited, this, &PinSettingsWidget::onNewPinTextEdited); - connect(mUi->newPinEdit, &PasswordEdit::fireBackspacePressedAndEmpty, this, [this] { - mUi->oldPinEdit->removeLastCharacter(); - mUi->oldPinEdit->setFocus(); - }); - - mUi->repeatNewPinEdit->setMaxLength(6); - mUi->repeatNewPinEdit->configureValidation(onlyNumbersExpression, tr("Only digits (0-9) are allowed.")); - connect(mUi->repeatNewPinEdit, &PasswordEdit::textEdited, this, &PinSettingsWidget::onRepeatNewPinTextEdited); - connect(mUi->repeatNewPinEdit, &PasswordEdit::fireBackspacePressedAndEmpty, this, [this] { - mUi->newPinEdit->removeLastCharacter(); - mUi->newPinEdit->setFocus(); - }); - - mUi->pukEdit->setMaxLength(10); - mUi->pukEdit->configureValidation(onlyNumbersExpression, tr("Only digits (0-9) are allowed.")); - connect(mUi->pukEdit, &PasswordEdit::textEdited, this, &PinSettingsWidget::onPukTextEdited); - - mUi->canRandomPinButton->setIcon(QPixmap(QStringLiteral(":/images/randompin/screen_keyboard.png"))); - mUi->canRandomPinButton->setIconSize(QSize(44, 26)); - connect(mUi->canRandomPinButton, &QAbstractButton::clicked, this, &PinSettingsWidget::onRandomPinButtonClicked); - - mUi->oldRandomPinButton->setIcon(QPixmap(QStringLiteral(":/images/randompin/screen_keyboard.png"))); - mUi->oldRandomPinButton->setIconSize(QSize(44, 26)); - connect(mUi->oldRandomPinButton, &QAbstractButton::clicked, this, &PinSettingsWidget::onRandomPinButtonClicked); - - mUi->newRandomPinButton->setIcon(QPixmap(QStringLiteral(":/images/randompin/screen_keyboard.png"))); - mUi->newRandomPinButton->setIconSize(QSize(44, 26)); - connect(mUi->newRandomPinButton, &QAbstractButton::clicked, this, &PinSettingsWidget::onRandomPinButtonClicked); - - mUi->repeatNewRandomPinButton->setIcon(QPixmap(QStringLiteral(":/images/randompin/screen_keyboard.png"))); - mUi->repeatNewRandomPinButton->setIconSize(QSize(44, 26)); - connect(mUi->repeatNewRandomPinButton, &QAbstractButton::clicked, this, &PinSettingsWidget::onRandomPinButtonClicked); - - mUi->pukRandomPinButton->setIcon(QPixmap(QStringLiteral(":/images/randompin/screen_keyboard.png"))); - mUi->pukRandomPinButton->setIconSize(QSize(44, 26)); - connect(mUi->pukRandomPinButton, &QAbstractButton::clicked, this, &PinSettingsWidget::onRandomPukButtonClicked); -} - - -PinSettingsWidget::~PinSettingsWidget() -{ -} - - -void PinSettingsWidget::setInProgress(bool pInProgress) -{ - if (pInProgress) - { - if (mUi->stackedWidget->currentWidget() == mUi->changePinComfortPage) - { - mUi->changePinComfortDetailsStackedWidget->setCurrentWidget(mUi->changePinComfortDetailsInProgressPage); - mPinSettingsInfoDescription = mUi->changePinComfortInProgressLabel->text(); - } - else if (mUi->stackedWidget->currentWidget() == mUi->changePinBasicPage) - { - mUi->canEdit->setEnabled(false); - mUi->oldPinEdit->setEnabled(false); - mUi->newPinEdit->setEnabled(false); - mUi->repeatNewPinEdit->setEnabled(false); - mUi->canRandomPinButton->setEnabled(false); - mUi->oldRandomPinButton->setEnabled(false); - mUi->newRandomPinButton->setEnabled(false); - mUi->repeatNewRandomPinButton->setEnabled(false); - } - } -} - - -QString PinSettingsWidget::getCan() const -{ - return mUi->canEdit->text(); -} - - -QString PinSettingsWidget::getPin() const -{ - return mUi->oldPinEdit->text(); -} - - -QString PinSettingsWidget::getPuk() const -{ - return mUi->pukEdit->text(); -} - - -QString PinSettingsWidget::getNewPin() const -{ - return mUi->newPinEdit->text(); -} - - -void PinSettingsWidget::setMode(PinSettingsWidget::Mode pMode) -{ - mMode = pMode; -} - - -QString PinSettingsWidget::getButtonText() const -{ - return mRetryCounter == 0 && !mPinDeactivated ? tr("Enter PUK") : tr("Change PIN"); -} - - -QVector PinSettingsWidget::getReaderWithNPA(const QVector& pReaderInfos) -{ - QVector readersWithNPA; - for (const ReaderInfo& readerInfo : pReaderInfos) - { - if (readerInfo.hasEidCard()) - { - readersWithNPA += readerInfo; - } - } - - return readersWithNPA; -} - - -void PinSettingsWidget::updateReadersWithoutNPA(const QVector& pReaderInfos) -{ - mMode = Mode::Normal; - mUi->headerStackedWidget->setCurrentWidget(mUi->errorNoNpaHeaderPage); - - bool basicReaderPage = false; - if (pReaderInfos.size() == 1) - { - const ReaderInfo& readerInfo = pReaderInfos.at(0); - if (readerInfo.isBasicReader()) - { - setupPinBasicPage(readerInfo); - basicReaderPage = true; - } - else - { - QPixmap pixmap(readerInfo.getReaderConfigurationInfo().getIcon()->lookupPath()); - mUi->noNpaLabel->setPixmap(pixmap.scaledToWidth(SCALEWIDTH, Qt::SmoothTransformation)); - } - } - else - { - QPixmap pixmap(ReaderConfiguration::getMultipleReaderIconPath()); - mUi->noNpaLabel->setPixmap(pixmap.scaledToWidth(250, Qt::SmoothTransformation)); - } - - mUi->stackedWidget->setCurrentWidget(basicReaderPage ? mUi->changePinBasicPage : mUi->errorNoNpaPage); - mUi->repeatNewPinEdit->setDigitFieldInvalid(false, tr("PIN correct.")); -} - - -void PinSettingsWidget::setUseScreenKeyboard(bool pUseScreenKeyboard) -{ - mUi->canRandomPinButton->setVisible(pUseScreenKeyboard); - mUi->oldRandomPinButton->setVisible(pUseScreenKeyboard); - mUi->newRandomPinButton->setVisible(pUseScreenKeyboard); - mUi->repeatNewRandomPinButton->setVisible(pUseScreenKeyboard); - mUi->pukRandomPinButton->setVisible(pUseScreenKeyboard); -} - - -bool PinSettingsWidget::getPinButtonEnabled() const -{ - return mPinButtonEnabled; -} - - -void PinSettingsWidget::fillInfoDescription(const QString& pTitle, const QString& pMessage) -{ - mPinSettingsInfoTitle = pTitle; - mPinSettingsInfoDescription = pMessage; -} - - -bool PinSettingsWidget::updateReadersForOneNPA(const ReaderInfo& pReaderInfo) -{ - mRetryCounter = pReaderInfo.getRetryCounter(); - mPinDeactivated = pReaderInfo.isPinDeactivated(); - - if (mMode == Mode::AfterPinChange) - { - setupPinSuccessfullyChangedPage(pReaderInfo); - mUi->headerStackedWidget->setCurrentWidget(mUi->pinSuccessHeaderPage); - mUi->stackedWidget->setCurrentWidget(mUi->pinSuccessPage); - mUi->canEdit->clear(); - mUi->oldPinEdit->clear(); - mUi->newPinEdit->clear(); - mUi->repeatNewPinEdit->clear(); - mUi->pukEdit->clear(); - return true; - } - - if (pReaderInfo.isBasicReader()) - { - setupPinBasicPage(pReaderInfo); - mUi->stackedWidget->setCurrentWidget(mUi->changePinBasicPage); - } - else - { - setupPinComfortPage(pReaderInfo); - mUi->stackedWidget->setCurrentWidget(mUi->changePinComfortPage); - } - - if (!pReaderInfo.sufficientApduLength()) - { - if (!pReaderInfo.isBasicReader()) - { - mUi->stackedWidget->setCurrentWidget(mUi->errorNoNpaPage); - } - - mUi->headerStackedWidget->setCurrentWidget(mUi->errorInsufficientApduLength); - return false; - } - - if (mPinDeactivated) - { - if (!pReaderInfo.isBasicReader()) - { - QPixmap pixmap(pReaderInfo.getReaderConfigurationInfo().getIconWithNPA()->lookupPath()); - mUi->deactivatedReaderImageLabel->setPixmap(pixmap.scaledToWidth(SCALEWIDTH, Qt::SmoothTransformation)); - mUi->stackedWidget->setCurrentWidget(mUi->errorPinDeactivatedPage); - } - - mUi->headerStackedWidget->setCurrentWidget(mUi->errorPinDeactivatedHeaderPage); - return false; - } - - return !pReaderInfo.isBasicReader(); -} - - -void PinSettingsWidget::updateReaders() -{ - const ReaderManager& readerManager = *Env::getSingleton(); - if (readerManager.getReaderInfos().isEmpty()) - { - QPixmap pixmap(ReaderConfiguration::getNoReaderFoundIconPath()); - mUi->noReaderLabel->setPixmap(pixmap.scaledToWidth(SCALEWIDTH, Qt::SmoothTransformation)); - mUi->headerStackedWidget->setCurrentWidget(mUi->errorNoReaderHeaderPage); - mUi->stackedWidget->setCurrentWidget(mUi->errorNoReaderPage); - return; - } - - QVector readersWithNPA = getReaderWithNPA(readerManager.getReaderInfos()); - mRetryCounter = 3; - bool enableButton = false; - - if (readersWithNPA.size() == 0) - { - updateReadersWithoutNPA(readerManager.getReaderInfos(ReaderFilter::UniqueReaderTypes)); - } - else if (readersWithNPA.size() == 1) - { - enableButton = updateReadersForOneNPA(readersWithNPA.at(0)); - } - else - { - mMode = Mode::Normal; - QPixmap pixmap(ReaderConfiguration::getMultipleReaderIconPath()); - mUi->multipleReaderLabel->setPixmap(pixmap.scaledToWidth(SCALEWIDTH, Qt::SmoothTransformation)); - mUi->headerStackedWidget->setCurrentWidget(mUi->errorMultipleNpasHeaderPage); - mUi->stackedWidget->setCurrentWidget(mUi->errorMultipleNpasPage); - } - - mPinButtonEnabled = enableButton; - Q_EMIT firePinButtonEnabledUpdated(mPinButtonEnabled); -} - - -void PinSettingsWidget::onBackspacePressedOnApply() -{ - mUi->repeatNewPinEdit->removeLastCharacter(); - mUi->repeatNewPinEdit->setFocus(); - onRepeatNewPinTextEdited(); -} - - -void PinSettingsWidget::paintEvent(QPaintEvent*) -{ - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} - - -void PinSettingsWidget::showEvent(QShowEvent* pEvent) -{ - connect(&ReaderManager::getInstance(), &ReaderManager::fireReaderEvent, this, &PinSettingsWidget::updateReaders); - connect(&ReaderManager::getInstance(), &ReaderManager::fireCardRetryCounterChanged, this, &PinSettingsWidget::updateReaders); - - updateReaders(); - QWidget::showEvent(pEvent); -} - - -void PinSettingsWidget::hideEvent(QHideEvent* pEvent) -{ - disconnect(&ReaderManager::getInstance(), &ReaderManager::fireReaderEvent, this, &PinSettingsWidget::updateReaders); - disconnect(&ReaderManager::getInstance(), &ReaderManager::fireCardRetryCounterChanged, this, &PinSettingsWidget::updateReaders); - - QWidget::hideEvent(pEvent); - - // Reset the mode, so that the next time the users sees this widget, it doesn't show the state of the previous - // action. - mMode = Mode::Normal; - - if (mRandomPinDialog && mRandomPinDialog->isVisible()) - { - // close the PinPad in case the tab is hidden, - // e.g. an authentication was started, so the PIN change is aborted. - mRandomPinDialog->reject(); - } -} - - -void PinSettingsWidget::onScanButtonClicked() -{ - ReaderManager::getInstance().startScanAll(); - QTimer::singleShot(3000, this, &PinSettingsWidget::onScanTimeout); -} - - -void PinSettingsWidget::onScanTimeout() -{ -} - - -void PinSettingsWidget::onUiFinished(const QString& pReaderName) -{ - qDebug() << "Set reader name" << pReaderName; - updateReaders(); -} - - -void PinSettingsWidget::onCanTextEdited(const QString& pText) -{ - if (!pText.isNull() && !pText.isEmpty()) - { - mUi->canEdit->setText(pText); - } - - if (isCanFieldVisible()) - { - if (mUi->canEdit->text().length() == 6) - { - mUi->oldPinEdit->setEnabled(true); - mUi->oldRandomPinButton->setEnabled(true); - - QTimer::singleShot(300, this, &PinSettingsWidget::focusPIN); - } - else - { - mUi->oldPinEdit->setEnabled(false); - mUi->oldRandomPinButton->setEnabled(false); - } - } - else - { - mUi->oldPinEdit->setEnabled(true); - mUi->oldRandomPinButton->setEnabled(true); - QTimer::singleShot(300, this, &PinSettingsWidget::focusPIN); - } - - mUi->oldPinEdit->clear(); - onOldPinTextEdited(); -} - - -void PinSettingsWidget::onOldPinTextEdited(const QString& pText) -{ - if (!pText.isNull() && !pText.isEmpty()) - { - mUi->oldPinEdit->setText(pText); - } - - bool enable = mUi->oldPinEdit->text().length() >= 5; - mUi->newPinEdit->setEnabled(enable); - mUi->newRandomPinButton->setEnabled(enable); - - if (mUi->oldPinEdit->text().length() == 6) - { - mUi->newPinEdit->setFocus(); - mUi->newPinEdit->setCursorPosition(0); - } - - mUi->newPinEdit->clear(); - onNewPinTextEdited(); -} - - -void PinSettingsWidget::onNewPinTextEdited(const QString& pText) -{ - if (!pText.isNull() && !pText.isEmpty()) - { - mUi->newPinEdit->setText(pText); - } - - bool enable = mUi->newPinEdit->text().length() == 6; - mUi->repeatNewPinEdit->setEnabled(enable); - mUi->repeatNewRandomPinButton->setEnabled(enable); - if (mUi->newPinEdit->text().length() == 6) - { - mUi->repeatNewPinEdit->setFocus(); - mUi->repeatNewPinEdit->setCursorPosition(0); - } - - mUi->repeatNewPinEdit->clear(); - onRepeatNewPinTextEdited(); -} - - -void PinSettingsWidget::onRepeatNewPinTextEdited(const QString& pText) -{ - if (!pText.isNull() && !pText.isEmpty()) - { - mUi->repeatNewPinEdit->setText(pText); - } - - if (!mUi->repeatNewPinEdit->isEnabled() || mUi->newPinEdit->text().startsWith(mUi->repeatNewPinEdit->text()) || mUi->repeatNewPinEdit->text().length() != 6) - { - mUi->repeatNewPinEdit->setDigitFieldInvalid(false, QString()); - - bool inputOk = mUi->repeatNewPinEdit->text().length() == 6 && mUi->repeatNewPinEdit->text() == mUi->newPinEdit->text(); - mPinButtonEnabled = inputOk; - Q_EMIT firePinButtonEnabledUpdated(mPinButtonEnabled); - } - else - { - QString invalidMessage = tr("The PIN in the field \"%1\" does not match the PIN in the field \"%2\".").arg(mUi->repeatNewPinEditLabel->text().replace(QStringLiteral(":"), QLatin1String("")), mUi->newPinEditLabel->text().replace(QStringLiteral(":"), QLatin1String(""))); - mUi->repeatNewPinEdit->setDigitFieldInvalid(true, invalidMessage); - } - -} - - -void PinSettingsWidget::onPukTextEdited(const QString& pText) -{ - if (!pText.isNull() && !pText.isEmpty()) - { - mUi->pukEdit->setText(pText); - } - mPinButtonEnabled = mUi->pukEdit->text().length() == 10; - Q_EMIT firePinButtonEnabledUpdated(mPinButtonEnabled); -} - - -void PinSettingsWidget::setupPinBasicPage(const ReaderInfo& pReaderInfo) -{ - mUi->canEdit->clear(); - mUi->oldPinEdit->clear(); - mUi->newPinEdit->clear(); - mUi->repeatNewPinEdit->clear(); - mUi->pukEdit->clear(); - - bool hasCard = pReaderInfo.hasEidCard(); - - QPixmap pixmap; - if (hasCard) - { - pixmap = pReaderInfo.getReaderConfigurationInfo().getIconWithNPA()->lookupPath(); - mUi->basicReaderImageLabel->setAccessibleName(tr("card inserted")); - } - else - { - pixmap = pReaderInfo.getReaderConfigurationInfo().getIcon()->lookupPath(); - mUi->basicReaderImageLabel->setAccessibleName(tr("no card inserted")); - } - - mUi->basicReaderImageLabel->setPixmap(pixmap.scaledToWidth(SCALEWIDTH, Qt::SmoothTransformation)); - - bool canEditVisible = false; - bool pukEditVisible = false; - - if (hasCard) - { - setupChangePinHeader(pReaderInfo.getRetryCounter(), true); - - switch (pReaderInfo.getRetryCounter()) - { - case 0: - pukEditVisible = true; - break; - - case 1: - canEditVisible = true; - break; - - default: - break; - } - } - - if (mPinDeactivated) - { - pukEditVisible = false; - canEditVisible = false; - hasCard = false; - } - - mUi->canEditStackedWidget->setCurrentWidget(canEditVisible ? mUi->canEditPage : mUi->noCanEditPage); - mUi->canEditLabelStackedWidget->setCurrentWidget(canEditVisible ? mUi->canEditLabelPage : mUi->noCanEditLabelPage); - - mUi->basicReaderPukStackedWidget->setCurrentWidget(pukEditVisible ? mUi->basicReaderPukPage : mUi->basicReaderPinPage); - - mUi->canEditLabel->setEnabled(hasCard); - mUi->oldPinEditLabel->setEnabled(hasCard); - mUi->newPinEditLabel->setEnabled(hasCard); - mUi->repeatNewPinEditLabel->setEnabled(hasCard); - mUi->canEdit->setEnabled(hasCard); - mUi->oldPinEdit->setEnabled(hasCard); - mUi->newPinEdit->setEnabled(hasCard); - mUi->repeatNewPinEdit->setEnabled(hasCard); - mUi->canRandomPinButton->setEnabled(hasCard); - mUi->oldRandomPinButton->setEnabled(hasCard); - mUi->newRandomPinButton->setEnabled(hasCard); - mUi->repeatNewRandomPinButton->setEnabled(hasCard); - - if (hasCard) - { - if (isCanFieldVisible()) - { - QTimer::singleShot(300, this, &PinSettingsWidget::focusCAN); - } - else if (pukEditVisible) - { - QTimer::singleShot(300, this, &PinSettingsWidget::focusPUK); - } - - if (!pukEditVisible) - { - onCanTextEdited(); - } - } -} - - -void PinSettingsWidget::focusPUK() -{ - mUi->pukEdit->setFocus(); - mUi->pukEdit->setCursorPosition(0); -} - - -void PinSettingsWidget::focusPIN() -{ - mUi->oldPinEdit->setFocus(); - mUi->oldPinEdit->setCursorPosition(0); -} - - -void PinSettingsWidget::focusCAN() -{ - mUi->canEdit->setFocus(); - mUi->canEdit->setCursorPosition(0); -} - - -void PinSettingsWidget::setupPinComfortPage(const ReaderInfo& pReaderInfo) -{ - QPixmap pixmap(pReaderInfo.getReaderConfigurationInfo().getIconWithNPA()->lookupPath()); - mUi->comfortReaderImageLabel->setPixmap(pixmap.scaledToWidth(SCALEWIDTH, Qt::SmoothTransformation)); - setupChangePinHeader(pReaderInfo.getRetryCounter(), false); - - switch (pReaderInfo.getRetryCounter()) - { - case 0: - mUi->changePinComfortDetailsStackedWidget->setCurrentWidget(mUi->changePinComfortPukDetailsPage); - break; - - case 1: - mUi->changePinComfortDetailsStackedWidget->setCurrentWidget(mUi->changePinComfortCanDetailsPage); - break; - - default: - mUi->changePinComfortDetailsStackedWidget->setCurrentWidget(mUi->changePinComfortDetailsPage); - break; - } -} - - -void PinSettingsWidget::setupPinSuccessfullyChangedPage(const ReaderInfo& pReaderInfo) -{ - QPixmap pixmap(pReaderInfo.getReaderConfigurationInfo().getIconWithNPA()->lookupPath()); - mUi->pinSuccessReaderImageLabel->setPixmap(pixmap.scaledToWidth(SCALEWIDTH, Qt::SmoothTransformation)); -} - - -void PinSettingsWidget::setupChangePinHeader(int pRetryCounter, bool pIsBasicReader) -{ - switch (pRetryCounter) - { - case 0: - mUi->headerStackedWidget->setCurrentWidget(mUi->changePinPukHeaderPage); - break; - - case 1: - mUi->headerStackedWidget->setCurrentWidget(mUi->changePinWithCanHeaderPage); - break; - - default: - if (mMode == Mode::AfterPinUnblock) - { - mUi->headerStackedWidget->setCurrentWidget(mUi->pinUnblockedHeaderPage); - } - else - { - if (pIsBasicReader) - { - mUi->headerStackedWidget->setCurrentWidget(mUi->changePinBasicHeaderPage); - } - else - { - mUi->headerStackedWidget->setCurrentWidget(mUi->changePinComfortHeaderPage); - } - } - break; - } -} - - -bool PinSettingsWidget::isCanFieldVisible() const -{ - return mUi->canEditStackedWidget->currentWidget() == mUi->canEditPage; -} - - -void PinSettingsWidget::onRandomPinButtonClicked() -{ - const auto& readersWithNpa = getReaderWithNPA(ReaderManager::getInstance().getReaderInfos()); - const auto& selectedReaderName = readersWithNpa.size() == 1 ? readersWithNpa.at(0).getName() : QString(); - mRandomPinDialog = new RandomPinDialog(6, selectedReaderName, this); - if (mRandomPinDialog->exec() == QDialog::Accepted && !mRandomPinDialog->getPin().isEmpty()) - { - QToolButton* pinButton = qobject_cast(sender()); - if (pinButton == nullptr) - { - qCCritical(gui) << "sender == nullptr"; - } - else if (pinButton->objectName() == QLatin1String("canRandomPinButton")) - { - onCanTextEdited(mRandomPinDialog->getPin()); - } - else if (pinButton->objectName() == QLatin1String("oldRandomPinButton")) - { - onOldPinTextEdited(mRandomPinDialog->getPin()); - } - else if (pinButton->objectName() == QLatin1String("newRandomPinButton")) - { - onNewPinTextEdited(mRandomPinDialog->getPin()); - } - else if (pinButton->objectName() == QLatin1String("repeatNewRandomPinButton")) - { - onRepeatNewPinTextEdited(mRandomPinDialog->getPin()); - } - else if (pinButton->objectName() == QLatin1String("pukRandomPinButton")) - { - onPukTextEdited(mRandomPinDialog->getPin()); - } - } -} - - -void PinSettingsWidget::onRandomPukButtonClicked() -{ - const auto& readersWithNpa = getReaderWithNPA(ReaderManager::getInstance().getReaderInfos()); - const auto& selectedReaderName = readersWithNpa.size() == 1 ? readersWithNpa.at(0).getName() : QString(); - mRandomPinDialog = new RandomPinDialog(10, selectedReaderName, this); - if (mRandomPinDialog->exec() == QDialog::Accepted && !mRandomPinDialog->getPin().isEmpty()) - { - onPukTextEdited(mRandomPinDialog->getPin()); - } -} - - -void PinSettingsWidget::changeEvent(QEvent* pEvent) -{ - if (pEvent->type() == QEvent::LanguageChange) - { - mUi->retranslateUi(this); - } - QWidget::changeEvent(pEvent); -} diff --git a/src/widget/PinSettingsWidget.h b/src/widget/PinSettingsWidget.h deleted file mode 100644 index 477f7e8..0000000 --- a/src/widget/PinSettingsWidget.h +++ /dev/null @@ -1,118 +0,0 @@ -/*! - * \brief Widget for the PIN settings. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include -#include - - -namespace Ui -{ -class PinSettingsWidget; -} - -namespace governikus -{ - -class RandomPinDialog; -class ReaderInfo; - -class PinSettingsWidget - : public QWidget -{ - Q_OBJECT - - public: - static const int SCALEWIDTH = 200; - - enum class Mode - { - Normal, - AfterPinChange, - AfterPinUnblock, - }; - - public: - PinSettingsWidget(QWidget* pParent = nullptr); - virtual ~PinSettingsWidget() override; - - void setInProgress(bool pInProgress); - - QString getCan() const; - QString getPin() const; - QString getPuk() const; - QString getNewPin() const; - - - Mode getMode() const - { - return mMode; - } - - - void setMode(Mode pMode); - QString getButtonText() const; - void setUseScreenKeyboard(bool pUseScreenKeyboard); - - bool getPinButtonEnabled() const; - - Q_SIGNALS: - void firePinButtonEnabledUpdated(bool pEnabled); - - public Q_SLOTS: - void updateReaders(); - void onBackspacePressedOnApply(); - - protected: - virtual void paintEvent(QPaintEvent*) override; - virtual void showEvent(QShowEvent* pEvent) override; - virtual void hideEvent(QHideEvent* pEvent) override; - virtual void changeEvent(QEvent* pEvent) override; - - private Q_SLOTS: - void onCanTextEdited(const QString& pText = QString()); - void onOldPinTextEdited(const QString& pText = QString()); - void onNewPinTextEdited(const QString& pText = QString()); - void onRepeatNewPinTextEdited(const QString& pText = QString()); - void onPukTextEdited(const QString& pText = QString()); - void focusPUK(); - void focusPIN(); - void focusCAN(); - void onRandomPinButtonClicked(); - void onRandomPukButtonClicked(); - void onScanTimeout(); - - void onScanButtonClicked(); - void onUiFinished(const QString& pReaderName); - - private: - void setupPinBasicPage(const ReaderInfo& pReaderInfo); - void setupPinComfortPage(const ReaderInfo& pReaderInfo); - void setupPinSuccessfullyChangedPage(const ReaderInfo& pReaderInfo); - void setupChangePinHeader(int pRetryCounter, bool pIsBasicReader); - - bool isCanFieldVisible() const; - - QVector getReaderWithNPA(const QVector& pReaderInfos); - void updateReadersWithoutNPA(const QVector& pReaderInfos); - bool updateReadersForOneNPA(const ReaderInfo& pReaderInfo); - - void fillInfoDescription(const QString& pTitle, const QString& pMessage); - - QScopedPointer mUi; - Mode mMode; - int mRetryCounter; - bool mPinDeactivated; - bool mPinButtonEnabled; - - QString mPinSettingsInfoTitle; - QString mPinSettingsInfoDescription; - QPointer mRandomPinDialog; -}; - -} /* namespace governikus */ diff --git a/src/widget/PinSettingsWidget.ui b/src/widget/PinSettingsWidget.ui deleted file mode 100644 index ce8c490..0000000 --- a/src/widget/PinSettingsWidget.ui +++ /dev/null @@ -1,1464 +0,0 @@ - - - PinSettingsWidget - - - - 0 - 0 - 536 - 551 - - - - - - - Qt::NoFocus - - - 3 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::TabFocus - - - Select a secure PIN that consists of six digits. Do not select a number that can be guessed easily, such as "123456", your date of birth or any other number that is printed on your ID card. - -When you change your PIN for the first time, please enter your five-digit transport PIN in the field "Current PIN / Transport PIN". You received your transport PIN with the letter sent to you by your competent authority. - -Please note that the PIN may only consist of digits (0-9). - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - true - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::TabFocus - - - Select a secure PIN that consists of six digits. Do not select a number that can be guessed easily, such as "123456", your date of birth or any other number that is printed on your ID card. - -When you change your PIN for the first time, please enter your five-digit transport PIN in the field "Current PIN / Transport PIN". You received your transport PIN with the letter sent to you by your competent authority. - -Please note that the PIN may only consist of digits (0-9). - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - true - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::TabFocus - - - You have entered the wrong PIN three times. The online identification function is now blocked. Please use your personal unblocking key (PUK) to unblock your ID card. You received the PUK with the letter sent to you by your competent authority. - -Please note that you can only use the PUK to unblock the eID function. If you have forgotten your PIN, you can have a new PIN set at your competent authority. - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - true - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - 0 - 0 - - - - Qt::TabFocus - - - CAN on nPA icon - - - :/images/canHint.png - - - false - - - - - - - Qt::TabFocus - - - You have entered the wrong PIN two times. For a third attempt you first have to enter your six-digit card access number. You can find your card access number on the front side of your ID card next to the date of expiry. On the electronic residence permit the card access number is printed above your signature. - - - true - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 20 - - - 20 - - - - - - - - :/images/Icon_Checked.svg - - - - - - - Qt::TabFocus - - - <h4>PUK entry successful</h4><p>Your ID card is unblocked. You now have three more tries to change your PIN.</p> - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - true - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 20 - - - 20 - - - - - - - - :/images/Icon_Checked.svg - - - - - - - Qt::TabFocus - - - <h4>PIN successfully changed</h4> - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - true - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::TabFocus - - - <html> -<h4>No card reader detected. Please make sure that a card reader is connected.</h4> -<p>If you need help or have problems with your card reader click on the "Diagnosis" button for further information. -</p> -<p>Please note: It is currently not possible to change your PIN whilst using your smartphone as a card reader. -However, you can change your PIN on your smartphone directly as long as the remote service is disabled. -</p> -</html> - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - true - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::TabFocus - - - <html> -<h4>Please place your ID card on the card reader.</h4> -<p>If you have already placed an ID card on the card reader but it is not displayed here, please click on "Diagnosis".</p> -</html> - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - true - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::TabFocus - - - <html> -<h4>Extended Length is not supported.</h4> -<p>Your remote reader does not meet the technical requirements (Extended Length not supported).</p> -</html> - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - true - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::TabFocus - - - <html> -<h4>Several ID cards detected</h4> -<p>Please place just one ID card on the card reader.</p> -</html> - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - true - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::TabFocus - - - <html> -<h4>eID feature deactivated</h4> -<p>The eID function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the eID function.</p> -</html> - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - true - - - - - - - - - - - Qt::Horizontal - - - - - - - Qt::NoFocus - - - 2 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 20 - - - 20 - - - 20 - - - - - 1 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::TabFocus - - - Current PIN / Transport PIN: - - - - - - - - - - Qt::StrongFocus - - - open on screen keyboard - - - true - - - - - - - Qt::TabFocus - - - New PIN: - - - - - - - - - - Qt::StrongFocus - - - open on screen keyboard - - - true - - - - - - - Qt::TabFocus - - - Confirm: - - - - - - - - - - Qt::StrongFocus - - - open on screen keyboard - - - true - - - - - - - Qt::NoFocus - - - 1 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::TabFocus - - - Card access number (CAN): - - - - - - - - - 0 - 0 - - - - - - - - - 0 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - Qt::StrongFocus - - - open on screen keyboard - - - true - - - - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Vertical - - - - 20 - 85 - - - - - - - - - - Qt::TabFocus - - - PUK: - - - - - - - - - - Qt::StrongFocus - - - open on screen keyboard - - - true - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Qt::Vertical - - - - 20 - 85 - - - - - - - - - - - - 0 - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - 0 - 0 - - - - - 200 - 200 - - - - - 200 - 200 - - - - Qt::TabFocus - - - card reader icon - - - Qt::AlignCenter - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - 15 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 20 - - - 20 - - - 0 - - - 20 - - - 0 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - - 200 - 200 - - - - - 200 - 200 - - - - Qt::TabFocus - - - card reader icon - - - Qt::AlignCenter - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::TabFocus - - - Click on "Change PIN" to enter a new PIN. - - - Qt::AlignCenter - - - true - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Click on "Enter PUK" to unblock your ID card. - - - Qt::AlignCenter - - - true - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Click on "Change PIN" to enter your card access number and then set a new PIN. You can find your card access number on the front side of your ID card next to the date of expiry. On the electronic residence permit the card access number is printed above your signature. - - - Qt::AlignCenter - - - true - - - - - - - - - 0 - - - 0 - - - 0 - - - - - Please pay attention to the display of your card reader. - - - Qt::AlignCenter - - - true - - - - - - - - - - - - - - Qt::Vertical - - - - 20 - 169 - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 20 - - - 20 - - - 0 - - - 20 - - - 0 - - - - - Qt::TabFocus - - - Click on "Change PIN" if you want to change your PIN again. - -If not, you can now remove your ID card form the card reader. - - - true - - - - - - - - 0 - 0 - - - - Qt::TabFocus - - - successful PIN change icon - - - Qt::AlignCenter - - - - - - - - - - Qt::Vertical - - - - 20 - 237 - - - - - - - - - - 15 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::TabFocus - - - no reader icon - - - Qt::AlignCenter - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - 15 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::TabFocus - - - no id card icon - - - Qt::AlignCenter - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - 15 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - Qt::TabFocus - - - - - - Please make sure that only one card reader with an ID card on it is connected to your computer. - - - true - - - - - - - Qt::TabFocus - - - multiple card reader icon - - - Qt::AlignCenter - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::TabFocus - - - deactivated card reader icon - - - deactivatedReaderImageLabel - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - Qt::Vertical - - - - 20 - 263 - - - - - - - - - - - - - governikus::PasswordEdit - QWidget -
generic/PasswordEdit.h
- 1 -
-
- - - - -
diff --git a/src/widget/ProviderWidget.cpp b/src/widget/ProviderWidget.cpp deleted file mode 100644 index ffa72bc..0000000 --- a/src/widget/ProviderWidget.cpp +++ /dev/null @@ -1,155 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "ProviderWidget.h" - -#include "Env.h" -#include "generic/ListCheckItemWidget.h" -#include "generic/ListItem.h" -#include "generic/ListItemIconLeft.h" -#include "generic/ListItemIconRight.h" -#include "generic/ListItemSubTitle.h" -#include "generic/ListItemTitle.h" -#include "ProviderConfiguration.h" -#include "ui_ProviderWidget.h" - -#include -#include -#include -#include -#include -#include - -using namespace governikus; - -ProviderWidget::ProviderWidget(QWidget* pParent) - : QWidget(pParent) - , mUi(new Ui::ProviderWidget()) -{ - mUi->setupUi(this); - - connect(mUi->providerSearch, &QLineEdit::textChanged, this, &ProviderWidget::searchProvider); - connect(Env::getSingleton(), &ProviderConfiguration::fireUpdated, this, &ProviderWidget::onProviderChanged); - - fill(); - mUi->noResultWidget->setVisible(false); -} - - -ProviderWidget::~ProviderWidget() -{ -} - - -void ProviderWidget::onProviderChanged() -{ - mUi->providerTableWidget->clear(); - fill(); - searchProvider(); -} - - -void ProviderWidget::fill() -{ - qDebug() << "add provider for desktop widgets."; - QStringList header; - header += tr("Name"); - header += tr("Address"); - - const auto& providers = Env::getSingleton()->getProviderConfigurationInfos(); - - mUi->providerTableWidget->setColumnCount(header.count()); - mUi->providerTableWidget->setHorizontalHeaderLabels(header); - mUi->providerTableWidget->setRowCount(providers.size()); - - int row = 0; - for (const auto& provider : providers) - { - - QLabel* providerName = new QLabel(provider.getLongName().isEmpty() ? provider.getShortName() : provider.getLongName()); - providerName->setFocusPolicy(Qt::TabFocus); - providerName->setToolTip(providerName->text()); - providerName->setTextFormat(Qt::RichText); - providerName->setMargin(3); - mUi->providerTableWidget->setCellWidget(row, 0, providerName); - - const QString& url = provider.getAddress(); - QString displayUrl = url; - const int maxUrlLength = 70; - if (url.length() > maxUrlLength) - { - displayUrl = url.left(maxUrlLength) + QStringLiteral("..."); - } - - QLabel* providerLink = new QLabel(QStringLiteral(R"(%2)").arg(url, displayUrl)); - providerLink->setToolTip(url); - providerLink->setFocusPolicy(Qt::TabFocus); - providerLink->setTextFormat(Qt::RichText); - providerLink->setTextInteractionFlags(Qt::TextBrowserInteraction); - providerLink->setOpenExternalLinks(true); - providerLink->setMargin(3); - mUi->providerTableWidget->setCellWidget(row, 1, providerLink); - - ++row; - } - - for (int i = 0; i < header.size(); ++i) - { - mUi->providerTableWidget->resizeColumnToContents(i); - } - - mUi->providerTableWidget->verticalHeader()->setVisible(false); //Hide row number - mUi->providerTableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); //Not allowed to change content - mUi->providerTableWidget->setAlternatingRowColors(true); //Grey and white alternating row -} - - -void ProviderWidget::searchProvider() -{ - QString searchText = mUi->providerSearch->text().trimmed(); - mUi->providerTableWidget->setVisible(true); - mUi->noResultWidget->setVisible(false); - - bool anyMatch = false; - - for (int i = 0; i < mUi->providerTableWidget->rowCount(); ++i) - { - bool match = false; - for (int j = 0; j < mUi->providerTableWidget->columnCount(); ++j) - { - - if (qobject_cast(mUi->providerTableWidget->cellWidget(i, j))->text().contains(searchText, Qt::CaseInsensitive)) - { - match = true; - anyMatch = true; - break; - } - } - mUi->providerTableWidget->setRowHidden(i, !match); - } - - mUi->providerTableWidget->setVisible(anyMatch); - mUi->noResultWidget->setVisible(!anyMatch); -} - - -void ProviderWidget::paintEvent(QPaintEvent*) -{ - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} - - -void ProviderWidget::changeEvent(QEvent* pEvent) -{ - if (pEvent->type() == QEvent::LanguageChange) - { - fill(); - searchProvider(); - mUi->retranslateUi(this); - } - QWidget::changeEvent(pEvent); -} diff --git a/src/widget/ProviderWidget.h b/src/widget/ProviderWidget.h deleted file mode 100644 index 6a3baf1..0000000 --- a/src/widget/ProviderWidget.h +++ /dev/null @@ -1,45 +0,0 @@ -/*! - * \brief The provider page in gui. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include -#include -#include - -namespace Ui -{ -class ProviderWidget; -} - -namespace governikus -{ - -class ProviderWidget - : public QWidget -{ - Q_OBJECT - - public: - ProviderWidget(QWidget* pParent = nullptr); - virtual ~ProviderWidget() override; - - public Q_SLOTS: - void searchProvider(); - void onProviderChanged(); - - protected: - virtual void changeEvent(QEvent* pEvent) override; - - private: - QScopedPointer mUi; - - virtual void paintEvent(QPaintEvent*) override; - void fill(); -}; - -} /* namespace governikus */ diff --git a/src/widget/RandomPinDialog.cpp b/src/widget/RandomPinDialog.cpp deleted file mode 100644 index eccd191..0000000 --- a/src/widget/RandomPinDialog.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "RandomPinDialog.h" -#include "ui_RandomPinDialog.h" - -#include "generic/HelpAction.h" -#include "Randomizer.h" -#include "ReaderManager.h" - -#include -#include - -using namespace governikus; - -static const char* PIN = "pin"; - -RandomPinDialog::RandomPinDialog(int pLength, const QString& pSelectedReader, QWidget* pParent) - : QDialog(pParent) - , mUi(new Ui::RandomPinDialog) - , mSelectedReader(pSelectedReader) -{ - mUi->setupUi(this); - mUi->pin->setMaxLength(pLength); - - setWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::WindowCloseButtonHint); - setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("On screen password")); - - setModal(true); - - connect(&ReaderManager::getInstance(), &ReaderManager::fireCardRemoved, this, &RandomPinDialog::onCardRemoved); - connect(&ReaderManager::getInstance(), &ReaderManager::fireCardInserted, this, &RandomPinDialog::onCardInserted); - - installEventFilter(this); - - mUi->pinButtons->setId(mUi->button_pos_0, 0); - mUi->pinButtons->setId(mUi->button_pos_1, 1); - mUi->pinButtons->setId(mUi->button_pos_2, 2); - mUi->pinButtons->setId(mUi->button_pos_3, 3); - mUi->pinButtons->setId(mUi->button_pos_4, 4); - mUi->pinButtons->setId(mUi->button_pos_5, 5); - mUi->pinButtons->setId(mUi->button_pos_6, 6); - mUi->pinButtons->setId(mUi->button_pos_7, 7); - mUi->pinButtons->setId(mUi->button_pos_8, 8); - mUi->pinButtons->setId(mUi->button_pos_9, 9); - - initComponents(); - createButton(); -} - - -RandomPinDialog::~RandomPinDialog() -{ -} - - -void RandomPinDialog::initComponents() -{ - mUi->clrButton->setIcon(QPixmap(QStringLiteral(":/images/randompin/btn_clear.png"))); - mUi->clrButton->setIconSize(QSize(44, 26)); - connect(mUi->clrButton, &QAbstractButton::clicked, mUi->pin, &QLineEdit::clear); - - mUi->cnlButton->setIcon(QPixmap(QStringLiteral(":/images/randompin/btn_cancel.png"))); - mUi->cnlButton->setIconSize(QSize(58, 50)); - connect(mUi->cnlButton, &QAbstractButton::clicked, this, &RandomPinDialog::reject); - - mUi->okButton->setIcon(QPixmap(QStringLiteral(":/images/randompin/btn_ok.png"))); - mUi->okButton->setIconSize(QSize(58, 50)); - connect(mUi->okButton, &QAbstractButton::clicked, this, &RandomPinDialog::accept); -} - - -void RandomPinDialog::createButton() -{ - QVector buttonList = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 - }; - std::shuffle(buttonList.begin(), buttonList.end(), Randomizer::getInstance().getGenerator()); - - Q_ASSERT(buttonList.size() == mUi->pinButtons->buttons().size()); - for (int i = 0; i < buttonList.size(); ++i) - { - QAbstractButton* button = mUi->pinButtons->button(i); - button->setIcon(QPixmap(QStringLiteral(":/images/randompin/btn_normal_%1.png").arg(buttonList.value(i)))); - button->setIconSize(QSize(58, 50)); - button->setProperty(PIN, QVariant::fromValue(buttonList.value(i))); - connect(button, &QAbstractButton::clicked, this, &RandomPinDialog::onPosButtonClicked); - } -} - - -QString RandomPinDialog::getPin() -{ - return mUi->pin->text(); -} - - -void RandomPinDialog::onPosButtonClicked() -{ - QToolButton* posButton = qobject_cast(sender()); - if (posButton) - { - mUi->pin->setText(mUi->pin->text() + posButton->property(PIN).toString()); - } -} - - -void RandomPinDialog::onCardRemoved(const QString& pReaderName) -{ - if (isVisible() && pReaderName == mSelectedReader) - { - reject(); - } -} - - -void RandomPinDialog::onCardInserted() -{ - if (isVisible()) - { - reject(); - } -} - - -bool RandomPinDialog::eventFilter(QObject* pObject, QEvent* pEvent) -{ - if (pEvent->type() == QEvent::KeyPress) - { - QKeyEvent* keyEvent = static_cast(pEvent); - if (keyEvent->key() == Qt::Key_F1) - { - HelpAction::openContextHelp(); - return true; - } - } - return QDialog::eventFilter(pObject, pEvent); -} - - -void RandomPinDialog::changeEvent(QEvent* pEvent) -{ - if (pEvent->type() == QEvent::LanguageChange) - { - mUi->retranslateUi(this); - } - QWidget::changeEvent(pEvent); -} diff --git a/src/widget/RandomPinDialog.h b/src/widget/RandomPinDialog.h deleted file mode 100644 index d954198..0000000 --- a/src/widget/RandomPinDialog.h +++ /dev/null @@ -1,48 +0,0 @@ -/*! - * \brief Dialog for display the random PIN. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include - -namespace Ui -{ -class RandomPinDialog; -} - -namespace governikus -{ - -class RandomPinDialog - : public QDialog -{ - Q_OBJECT - - public: - RandomPinDialog(int pLength, const QString& pSelectedReader, QWidget* pParent = nullptr); - virtual ~RandomPinDialog() override; - - QString getPin(); - - protected: - virtual bool eventFilter(QObject* pObject, QEvent* pEvent) override; - virtual void changeEvent(QEvent* pEvent) override; - - private: - QScopedPointer mUi; - const QString mSelectedReader; - - void initComponents(); - void createButton(); - - private Q_SLOTS: - void onPosButtonClicked(); - void onCardRemoved(const QString& pReaderName); - void onCardInserted(); -}; - -} /* namespace governikus */ diff --git a/src/widget/ReaderDeviceDialog.cpp b/src/widget/ReaderDeviceDialog.cpp deleted file mode 100644 index 9c81b16..0000000 --- a/src/widget/ReaderDeviceDialog.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/*! - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "ReaderDeviceDialog.h" - -#include "generic/HelpAction.h" -#include "ReaderDeviceWidget.h" -#include "ui_ReaderDeviceDialog.h" - -#include - - -using namespace governikus; - -ReaderDeviceDialog::ReaderDeviceDialog(QWidget* pParent) - : QDialog(pParent) - , mUi(new Ui::ReaderDeviceDialog) - , mReaderDeviceWidget(new ReaderDeviceWidget(pParent)) -{ - mUi->setupUi(this); - - setAttribute(Qt::WA_DeleteOnClose, true); - setWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint); - setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("Reader Driver Integration")); - - installEventFilter(this); - - mUi->readerDriverLayout->addWidget(mReaderDeviceWidget); - - connect(mUi->closeButton, &QAbstractButton::clicked, this, &ReaderDeviceDialog::close); -} - - -ReaderDeviceDialog::~ReaderDeviceDialog() -{ -} - - -bool ReaderDeviceDialog::eventFilter(QObject* pObject, QEvent* pEvent) -{ - if (pEvent->type() == QEvent::KeyPress) - { - QKeyEvent* const keyEvent = static_cast(pEvent); - if (keyEvent->key() == Qt::Key_F1) - { - HelpAction::openContextHelp(); - return true; - } - } - return QDialog::eventFilter(pObject, pEvent); -} - - -void ReaderDeviceDialog::changeEvent(QEvent* pEvent) -{ - if (pEvent->type() == QEvent::LanguageChange) - { - mUi->retranslateUi(this); - } - QWidget::changeEvent(pEvent); -} diff --git a/src/widget/ReaderDeviceDialog.h b/src/widget/ReaderDeviceDialog.h deleted file mode 100644 index 88a1760..0000000 --- a/src/widget/ReaderDeviceDialog.h +++ /dev/null @@ -1,43 +0,0 @@ -/*! - * \brief Dialog for detecting attached card readers or available - * remote card readers. - * - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include - -namespace Ui -{ -class ReaderDeviceDialog; -} - - -namespace governikus -{ - -class ReaderDeviceWidget; - - -class ReaderDeviceDialog - : public QDialog -{ - Q_OBJECT - - public: - ReaderDeviceDialog(QWidget* pParent = nullptr); - virtual ~ReaderDeviceDialog() override; - - protected: - virtual bool eventFilter(QObject* pObject, QEvent* pEvent) override; - virtual void changeEvent(QEvent* pEvent) override; - - private: - QScopedPointer mUi; - ReaderDeviceWidget* mReaderDeviceWidget; -}; - -} /* namespace governikus */ diff --git a/src/widget/ReaderDeviceGui.cpp b/src/widget/ReaderDeviceGui.cpp deleted file mode 100644 index 007c837..0000000 --- a/src/widget/ReaderDeviceGui.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/*! - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "ReaderDeviceGui.h" - -#include "ReaderDeviceDialog.h" - -#include - -using namespace governikus; - -Q_DECLARE_LOGGING_CATEGORY(gui) - -ReaderDeviceGui::ReaderDeviceGui(QWidget* pParentWidget) - : QObject(pParentWidget) - , mDialog(nullptr) -{ -} - - -ReaderDeviceGui::~ReaderDeviceGui() -{ -} - - -void ReaderDeviceGui::activate() -{ - if (!mDialog) - { - QWidget* dialogParent = qobject_cast(parent()); - if (!dialogParent) - { - return; - } - - mDialog = new ReaderDeviceDialog(dialogParent); - connect(mDialog, &ReaderDeviceDialog::finished, this, &ReaderDeviceGui::onFinished); - connect(mDialog, &QDialog::finished, this, &ReaderDeviceGui::fireFinished); - } - reactivate(); -} - - -void ReaderDeviceGui::deactivate() -{ - if (mDialog) - { - mDialog->close(); - } -} - - -void ReaderDeviceGui::reactToReaderCount(int pReaderCount) -{ - if (mDialog && pReaderCount > 0) - { - mDialog->close(); - } -} - - -void ReaderDeviceGui::reactivate() -{ - if (mDialog->isMinimized()) - { - mDialog->showNormal(); - } - if (!mDialog->isVisible()) - { - mDialog->show(); - } - mDialog->activateWindow(); - mDialog->raise(); -} - - -void ReaderDeviceGui::onFinished(int result) -{ - Q_UNUSED(result); - - mDialog = nullptr; -} diff --git a/src/widget/ReaderDeviceGui.h b/src/widget/ReaderDeviceGui.h deleted file mode 100644 index 5afa5f5..0000000 --- a/src/widget/ReaderDeviceGui.h +++ /dev/null @@ -1,43 +0,0 @@ -/*! - * \brief Qt widget based ReaderDriverUi implementation. - * - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include - -class QWidget; - -namespace governikus -{ - -class ReaderDeviceDialog; - - -class ReaderDeviceGui - : public QObject -{ - Q_OBJECT - - public: - ReaderDeviceGui(QWidget* pParentWidget); - virtual ~ReaderDeviceGui(); - - void activate(); - void deactivate(); - void reactToReaderCount(int pReaderCount); - - Q_SIGNALS: - void fireFinished(); - - private: - ReaderDeviceDialog* mDialog; - void reactivate(); - - private Q_SLOTS: - void onFinished(int result); -}; - -} /* namespace governikus */ diff --git a/src/widget/ReaderDeviceWidget.cpp b/src/widget/ReaderDeviceWidget.cpp deleted file mode 100644 index 2a73321..0000000 --- a/src/widget/ReaderDeviceWidget.cpp +++ /dev/null @@ -1,391 +0,0 @@ -/*! - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "ReaderDeviceWidget.h" - -#include "ui_ReaderDeviceWidget.h" - -#include "Env.h" -#include "generic/HelpAction.h" -#include "LanguageLoader.h" -#include "ReaderConfiguration.h" -#include "ReaderManager.h" -#include "RemotePinInputDialog.h" - -#include -#include -#include -#include - - -using namespace governikus; - - -Q_DECLARE_LOGGING_CATEGORY(gui) - - -ReaderDeviceWidget::ReaderDeviceWidget(QWidget* pParent) - : QWidget(pParent) - , mUi(new Ui::ReaderDeviceWidget) - , mLocalReaderDataModel(this) - , mRemoteReaderDataModel(this) -{ - mUi->setupUi(this); - - setDisplayText(); - - mUi->tableViewLocal->setModel(&mLocalReaderDataModel); - mUi->tableViewLocal->horizontalHeader()->setStretchLastSection(true); - mUi->tableViewLocal->verticalHeader()->setVisible(false); - - mUi->tableViewRemote->setModel(&mRemoteReaderDataModel); - mUi->tableViewRemote->horizontalHeader()->setStretchLastSection(true); - mUi->tableViewRemote->verticalHeader()->setVisible(false); - - mUi->infoText->setOpenExternalLinks(true); - - connect(Env::getSingleton(), &ReaderConfiguration::fireUpdated, this, &ReaderDeviceWidget::onAdjustReaderNameColumnWidth); - onAdjustReaderNameColumnWidth(); - - connect(&mLocalReaderDataModel, &ReaderDriverModel::fireModelChanged, this, &ReaderDeviceWidget::onUpdateLocalTableSelection); - connect(&mRemoteReaderDataModel, &RemoteDeviceModel::fireModelChanged, this, &ReaderDeviceWidget::onUpdateRemoteTableSelection); - onUpdateLocalTableSelection(); - onUpdateRemoteTableSelection(); - - connect(mUi->tableViewRemote->selectionModel(), &QItemSelectionModel::selectionChanged, this, &ReaderDeviceWidget::onRemoteSelectionChanged); - connect(mUi->tableViewLocal->selectionModel(), &QItemSelectionModel::selectionChanged, this, &ReaderDeviceWidget::onUpdateInfo); - onRemoteSelectionChanged(); - onUpdateInfo(); - - connect(mUi->connectRemote, &QPushButton::clicked, this, &ReaderDeviceWidget::onConnectClicked); - connect(mUi->forgetRemote, &QPushButton::clicked, this, &ReaderDeviceWidget::onForgetClicked); - connect(mUi->tableViewRemote, &QTableView::doubleClicked, this, &ReaderDeviceWidget::onRemoteDoubleClicked); - - const QSharedPointer& remoteClient = Env::getSingleton()->getRemoteClient(); - connect(remoteClient.data(), &RemoteClient::fireDispatcherDestroyed, &mRemoteReaderDataModel, &RemoteDeviceModel::onDeviceDisconnected); -} - - -ReaderDeviceWidget::~ReaderDeviceWidget() -{ - -} - - -void ReaderDeviceWidget::prependAccessibleName(const QString& pAccessibleNameAddition) -{ - mUi->cardReaderDescription->setAccessibleName(pAccessibleNameAddition + mUi->cardReaderDescription->accessibleName()); -} - - -void ReaderDeviceWidget::onRemoteSelectionChanged() -{ - const QItemSelectionModel* const selectionModel = mUi->tableViewRemote->selectionModel(); - const QModelIndexList& selectionList = selectionModel->selectedRows(); - - if (selectionList.isEmpty()) - { - mUi->connectRemote->setEnabled(false); - mUi->forgetRemote->setEnabled(false); - } - else - { - const QModelIndex& index = selectionList.at(0); - if (mRemoteReaderDataModel.isPaired(index)) - { - mUi->connectRemote->setEnabled(false); - mUi->forgetRemote->setEnabled(true); - } - else - { - mUi->connectRemote->setEnabled(mRemoteReaderDataModel.isSupported(index)); - mUi->forgetRemote->setEnabled(false); - } - } - -} - - -void ReaderDeviceWidget::onUpdateInfo() -{ - updateInfoIcon(); - updateInfoText(); - updateInfoUpdate(); -} - - -void ReaderDeviceWidget::setDisplayText() -{ - const QString& url = HelpAction::getOnlineUrl(QStringLiteral("readerDeviceTab")); - //: Is embedded in a sentence. - const QString hyperlink = QStringLiteral("%2").arg(url, tr("online help")); - - const QString remoteEmptyListDescriptionString = tr("No smartphone with enabled remote service found. See %1 for details of use.").arg(hyperlink); - mUi->remoteEmptyListDescription->setText(remoteEmptyListDescriptionString); - mUi->remoteEmptyListDescription->setAccessibleName(remoteEmptyListDescriptionString); - - const QString localEmptyListDescriptionString = tr("No connected card reader found. See %1 for installation of card readers.").arg(hyperlink); - mUi->localEmptyListDescription->setText(localEmptyListDescriptionString); - mUi->localEmptyListDescription->setAccessibleDescription(localEmptyListDescriptionString); -} - - -void ReaderDeviceWidget::updateInfoIcon() -{ - const QItemSelectionModel* const selectionModel = mUi->tableViewLocal->selectionModel(); - const QModelIndexList& selectionList = selectionModel->selectedRows(); - - QPixmap pixmap; - if (selectionList.isEmpty()) - { - pixmap = QPixmap(ReaderConfiguration::getNoReaderFoundIconPath()); - } - else - { - const QModelIndex& index = selectionList.at(0); - const QString& path = mLocalReaderDataModel.getReaderConfigurationInfo(index).getIcon()->lookupPath(); - pixmap = QPixmap(path); - if (mLocalReaderDataModel.isInstalledSupportedReader(index)) - { - QPixmap checkMark(QStringLiteral(":/images/green_check_mark.svg")); - checkMark = checkMark.scaledToHeight(pixmap.height() / 3, Qt::SmoothTransformation); - - const int insertAtX = pixmap.width() - checkMark.width(); - const int insertAtY = pixmap.height() / 5 - checkMark.height() / 2; - QPainter(&pixmap).drawPixmap(insertAtX, insertAtY, checkMark); - } - } - const int layoutHeight = mUi->detailInfoLayout->geometry().height(); - mUi->readerLabel->setPixmap(pixmap.scaledToHeight(layoutHeight / 2, Qt::SmoothTransformation)); - -} - - -void ReaderDeviceWidget::updateInfoText() -{ - const QItemSelectionModel* const selectionModel = mUi->tableViewLocal->selectionModel(); - const QModelIndexList& selectionList = selectionModel->selectedRows(); - - QString infoText; - if (selectionList.isEmpty()) - { - if (mLocalReaderDataModel.rowCount() == 0) - { - infoText = tr("Please connect suitable card reader"); - } - else - { - infoText = tr("Select a device to display more information about it"); - } - } - else - { - const QModelIndex& index = selectionList.at(0); - infoText = mLocalReaderDataModel.getHTMLDescription(index); - } - - if (infoText.isEmpty()) - { - mUi->stackedWidget->setCurrentWidget(mUi->emptyWidget); - } - else - { - mUi->infoText->setHtml(QStringLiteral("

") + infoText + QStringLiteral("

")); - mUi->stackedWidget->setCurrentWidget(mUi->infoText); - } -} - - -void ReaderDeviceWidget::updateInfoUpdate() -{ - const auto& now = LanguageLoader::getInstance().getUsedLocale().toString(QTime::currentTime(), tr("hh:mm:ss AP")); - const QString& text = tr("The list of card readers was last updated at %1."); - - mUi->updateTimeLabel->setText(text.arg(now)); -} - - -void ReaderDeviceWidget::onUpdateLocalTableSelection() -{ - if (mLocalReaderDataModel.rowCount() > 0) - { - mUi->localEmptyListDescriptionFrame->setVisible(false); - mUi->tableViewLocal->setVisible(true); - QItemSelectionModel* const selectionModel = mUi->tableViewLocal->selectionModel(); - if (selectionModel->selectedRows().isEmpty()) - { - selectionModel->select(mLocalReaderDataModel.index(0, ReaderDriverModel::ColumnId::ReaderName), QItemSelectionModel::Select); - selectionModel->select(mLocalReaderDataModel.index(0, ReaderDriverModel::ColumnId::ReaderStatus), QItemSelectionModel::Select); - } - } - else - { - mUi->tableViewLocal->setVisible(false); - mUi->localEmptyListDescriptionFrame->setVisible(true); - } - onUpdateInfo(); -} - - -void ReaderDeviceWidget::onUpdateRemoteTableSelection() -{ - if (mRemoteReaderDataModel.rowCount() > 0) - { - mUi->remoteEmptyListDescriptionFrame->setVisible(false); - mUi->tableViewRemote->setVisible(true); - QItemSelectionModel* const selectionModel = mUi->tableViewRemote->selectionModel(); - if (selectionModel->selectedRows().isEmpty()) - { - selectionModel->select(mRemoteReaderDataModel.index(0, RemoteDeviceModel::ColumnId::ReaderName), QItemSelectionModel::Select); - selectionModel->select(mRemoteReaderDataModel.index(0, RemoteDeviceModel::ColumnId::ReaderStatus), QItemSelectionModel::Select); - } - } - else - { - mUi->tableViewRemote->setVisible(false); - mUi->remoteEmptyListDescriptionFrame->setVisible(true); - onRemoteSelectionChanged(); - } -} - - -void ReaderDeviceWidget::onAdjustReaderNameColumnWidth() -{ - const auto& infos = Env::getSingleton()->getReaderConfigurationInfos(); - - int maxWidth = 0; - const QFontMetrics metrics(QGuiApplication::font()); - for (const auto& info : infos) - { - const int deviceNameWidth = metrics.width(info.getName()); - if (deviceNameWidth > maxWidth) - { - maxWidth = deviceNameWidth; - } - } - - if (maxWidth <= 0) - { - return; - } - - const int CELL_PADDING = 20; - mUi->tableViewLocal->setColumnWidth(0, maxWidth + CELL_PADDING); - mUi->tableViewRemote->setColumnWidth(0, maxWidth + CELL_PADDING); -} - - -void ReaderDeviceWidget::onConnectClicked() -{ - const auto& selectionModel = mUi->tableViewRemote->selectionModel(); - const QModelIndexList& selectionList = selectionModel->selectedRows(); - - if (!selectionList.isEmpty()) - { - const QModelIndex& index = selectionList.at(0); - - const QSharedPointer remoteDeviceListEntry = mRemoteReaderDataModel.getRemoteDeviceListEntry(index); - if (remoteDeviceListEntry.isNull()) - { - return; - } - - setEnabled(false); - - QMessageBox pairingInfoBox(this); - pairingInfoBox.setText(tr("Please start pairing mode first.")); - pairingInfoBox.setWindowModality(Qt::WindowModal); - pairingInfoBox.setWindowFlags(pairingInfoBox.windowFlags() & ~Qt::WindowContextHelpButtonHint); - pairingInfoBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); - pairingInfoBox.setDefaultButton(QMessageBox::Ok); - pairingInfoBox.button(QMessageBox::Ok)->setFocus(); - pairingInfoBox.setIconPixmap(QIcon(QStringLiteral(":/images/npa.svg")).pixmap(32, 32)); - - if (pairingInfoBox.exec() == QMessageBox::Cancel) - { - setEnabled(true); - return; - } - - const QString pin = RemotePinInputDialog::getPin(this); - if (!pin.isEmpty()) - { - const QSharedPointer& remoteClient = Env::getSingleton()->getRemoteClient(); - connect(remoteClient.data(), &RemoteClient::fireEstablishConnectionDone, this, &ReaderDeviceWidget::onEstablishConnectionDone); - remoteClient->establishConnection(remoteDeviceListEntry, pin); - } - - setEnabled(true); - } -} - - -void ReaderDeviceWidget::onEstablishConnectionDone(const QSharedPointer& pEntry, const GlobalStatus& pStatus) -{ - Q_UNUSED(pEntry); - const QSharedPointer& remoteClient = Env::getSingleton()->getRemoteClient(); - disconnect(remoteClient.data(), &RemoteClient::fireEstablishConnectionDone, this, &ReaderDeviceWidget::onEstablishConnectionDone); - if (pStatus.isError()) - { - QMessageBox box(QApplication::activeWindow()); - box.setIcon(QMessageBox::Critical); - box.setWindowTitle(QApplication::applicationName() + QStringLiteral(" - ") + tr("Pairing")); - box.setWindowIcon(QIcon(QStringLiteral(":/images/npa.svg"))); - box.setWindowModality(Qt::WindowModal); - box.setText(pStatus.toErrorDescription()); - box.setStandardButtons(QMessageBox::Ok); - box.button(QMessageBox::Ok)->setFocus(); - box.exec(); - } -} - - -void ReaderDeviceWidget::onForgetClicked() -{ - const auto& selectionModel = mUi->tableViewRemote->selectionModel(); - const QModelIndexList& selectionList = selectionModel->selectedRows(); - - if (!selectionList.isEmpty()) - { - const QModelIndex& index = selectionList.at(0); - - mRemoteReaderDataModel.forgetDevice(index); - } -} - - -void ReaderDeviceWidget::showEvent(QShowEvent* pEevent) -{ - onUpdateInfo(); - Q_EMIT fireWidgetShown(); - QWidget::showEvent(pEevent); -} - - -void ReaderDeviceWidget::hideEvent(QHideEvent* pEvent) -{ - Q_EMIT fireWidgetHidden(); - QWidget::hideEvent(pEvent); -} - - -void ReaderDeviceWidget::changeEvent(QEvent* pEvent) -{ - if (pEvent->type() == QEvent::LanguageChange) - { - mUi->retranslateUi(this); - setDisplayText(); - onUpdateInfo(); - } - QWidget::changeEvent(pEvent); -} - - -void ReaderDeviceWidget::onRemoteDoubleClicked(const QModelIndex& pIndex) -{ - if (!mRemoteReaderDataModel.isPaired(pIndex) && mRemoteReaderDataModel.isSupported((pIndex))) - { - onConnectClicked(); - } -} diff --git a/src/widget/ReaderDeviceWidget.h b/src/widget/ReaderDeviceWidget.h deleted file mode 100644 index d73b3a2..0000000 --- a/src/widget/ReaderDeviceWidget.h +++ /dev/null @@ -1,72 +0,0 @@ -/*! - * \brief Widget for detecting attached card readers and - * suggesting an appropriate driver to be installed. - * - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "AppSettings.h" -#include "ReaderDriverModel.h" -#include "RemoteDeviceModel.h" - -#include -#include -#include -#include - -namespace Ui -{ -class ReaderDeviceWidget; -} - -namespace governikus -{ - -class ReaderDeviceWidget - : public QWidget -{ - Q_OBJECT - - private: - QScopedPointer mUi; - ReaderDriverModel mLocalReaderDataModel; - RemoteDeviceModel mRemoteReaderDataModel; - - void setDisplayText(); - - void updateInfoIcon(); - void updateInfoText(); - void updateInfoUpdate(); - - static QString askForPin(QWidget* pParent); - - private Q_SLOTS: - void showEvent(QShowEvent* pEevent) override; - void hideEvent(QHideEvent* pEvent) override; - void onUpdateInfo(); - void onAdjustReaderNameColumnWidth(); - void onUpdateLocalTableSelection(); - void onUpdateRemoteTableSelection(); - void onConnectClicked(); - void onEstablishConnectionDone(const QSharedPointer& pEntry, const GlobalStatus& pStatus); - void onRemoteSelectionChanged(); - void onForgetClicked(); - void onRemoteDoubleClicked(const QModelIndex& pIndex); - - protected: - virtual void changeEvent(QEvent* pEvent) override; - - public: - explicit ReaderDeviceWidget(QWidget* pParent = nullptr); - virtual ~ReaderDeviceWidget() override; - - void prependAccessibleName(const QString& pAccessibleNameAddition); - - Q_SIGNALS: - void fireWidgetShown(); - void fireWidgetHidden(); -}; - -} diff --git a/src/widget/ReaderDeviceWidget.ui b/src/widget/ReaderDeviceWidget.ui deleted file mode 100644 index 8362379..0000000 --- a/src/widget/ReaderDeviceWidget.ui +++ /dev/null @@ -1,397 +0,0 @@ - - - ReaderDeviceWidget - - - - 0 - 0 - 649 - 440 - - - - - - - 6 - - - - - Qt::TabFocus - - - In order to use the online identification function of your ID card you need a separate card reader or a suitable smartphone. The following overview shows the status of a connected card reader or connected smartphone. - - - In order to use the online identification function of your ID card you need a separate card reader or a suitable smartphone. The following overview shows the status of a connected card reader or connected smartphone. - - - true - - - - - - - 0 - - - - - Qt::Vertical - - - QSizePolicy::Minimum - - - - 20 - 5 - - - - - - - - - 0 - 0 - - - - - 11 - 75 - true - - - - Smartphone as card reader - - - - - - - - - - 0 - 0 - - - - - 470 - 100 - - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - false - - - - - - - QFrame::StyledPanel - - - QFrame::Plain - - - - 0 - 0 - - - - false - - - - 470 - 100 - - - - - - - $text - - - true - - - true - - - - - - - - - - 10 - - - - - true - - - - 0 - 0 - - - - - 200 - 16777215 - - - - Pair - - - - - - - false - - - - 0 - 0 - - - - - 200 - 16777215 - - - - Forget - - - - - - - - - - - Qt::Vertical - - - QSizePolicy::Minimum - - - - 20 - 20 - - - - - - - - - 11 - 75 - true - - - - - - - Card readers - - - - - - - 6 - - - - - - 0 - 0 - - - - - 470 - 0 - - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - false - - - - - - - QFrame::StyledPanel - - - QFrame::Plain - - - - 0 - 0 - - - - false - - - - 470 - 100 - - - - - - - $text - - - true - - - true - - - - - - - - - - - - - - TextLabel - - - Qt::AlignCenter - - - - - - - - - - 0 - 0 - - - - - 200 - 16777215 - - - - - - 0 - 0 - - - - - 200 - 16777215 - - - - - - - - - - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 10 - 10 - - - - - - - - Qt::TabFocus - - - After connecting a new card reader it may take a few seconds to recognize the driver. It may be necessary to restart your system after installing the driver. - - - After connecting a new card reader it may take a few seconds to recognize the driver. It may be necessary to restart your system after installing the driver. - - - true - - - - - - - TextLabel - - - - - - - - - - diff --git a/src/widget/ReaderDriverModel.cpp b/src/widget/ReaderDriverModel.cpp deleted file mode 100644 index c09fea1..0000000 --- a/src/widget/ReaderDriverModel.cpp +++ /dev/null @@ -1,172 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "ReaderDriverModel.h" - -#include "AppSettings.h" -#include "Env.h" -#include "ReaderConfiguration.h" -#include "ReaderDetector.h" -#include "ReaderManager.h" - - -using namespace governikus; - - -ReaderDriverModel::ReaderDriverModel(QObject* pParent) - : QAbstractTableModel(pParent) - , mKnownDrivers() - , mConnectedReaders() -{ - const ReaderManager* const readerManager = Env::getSingleton(); - - connect(readerManager, &ReaderManager::fireReaderAdded, this, &ReaderDriverModel::onUpdateContent); - connect(readerManager, &ReaderManager::fireReaderRemoved, this, &ReaderDriverModel::onUpdateContent); - connect(Env::getSingleton(), &ReaderConfiguration::fireUpdated, this, &ReaderDriverModel::onUpdateContent); - connect(Env::getSingleton(), &ReaderDetector::fireReaderChangeDetected, this, &ReaderDriverModel::onUpdateContent); - connect(Env::getSingleton(), &AppSettings::fireSettingsChanged, this, &ReaderDriverModel::onUpdateContent); - onUpdateContent(); -} - - -void ReaderDriverModel::collectReaderData() -{ - mConnectedReaders.clear(); - - const QVector installedReaders = Env::getSingleton()->getReaderInfos(ReaderFilter({ - ReaderManagerPlugInType::PCSC - , ReaderManagerPlugInType::BLUETOOTH - , ReaderManagerPlugInType::NFC - })); - - for (const auto& installedReader : installedReaders) - { - const auto& readerSettingsInfo = installedReader.getReaderConfigurationInfo(); - if (!readerSettingsInfo.getUrl().isEmpty()) - { - mKnownDrivers += readerSettingsInfo; - mConnectedReaders += readerSettingsInfo; - } - } - - QVector readersWithoutDriver; - const auto& attachedSupportedDevices = Env::getSingleton()->getAttachedSupportedDevices(); - for (const auto& info : attachedSupportedDevices) - { - if (!mConnectedReaders.contains(info)) - { - readersWithoutDriver.append(info); - } - } - mConnectedReaders += readersWithoutDriver; -} - - -QString ReaderDriverModel::getStatus(const ReaderConfigurationInfo& pReaderConfigurationInfo) const -{ - if (mConnectedReaders.isEmpty()) - { - return tr("Not connected"); - } - - if (mKnownDrivers.contains(pReaderConfigurationInfo)) - { - return tr("Driver installed"); - } - - return tr("No driver installed"); -} - - -void ReaderDriverModel::onUpdateContent() -{ - beginResetModel(); - collectReaderData(); - endResetModel(); - - Q_EMIT fireModelChanged(); -} - - -QVariant ReaderDriverModel::headerData(int pSection, Qt::Orientation pOrientation, int pRole) const -{ - if (pRole == Qt::DisplayRole && pOrientation == Qt::Horizontal) - { - switch (pSection) - { - case ColumnId::ReaderName: - return tr("Device"); - - case ColumnId::ReaderStatus: - return tr("Status"); - - default: - return QVariant(); - } - } - return QVariant(); -} - - -int ReaderDriverModel::rowCount(const QModelIndex&) const -{ - return mConnectedReaders.size(); -} - - -int ReaderDriverModel::columnCount(const QModelIndex&) const -{ - return NUMBER_OF_COLUMNS; -} - - -QVariant ReaderDriverModel::data(const QModelIndex& pIndex, int pRole) const -{ - if (pRole == Qt::DisplayRole) - { - const auto& reader = mConnectedReaders.at(pIndex.row()); - switch (pIndex.column()) - { - case ColumnId::ReaderName: - return reader.getName(); - - case ColumnId::ReaderStatus: - return getStatus(reader); - } - } - - return QVariant(); -} - - -const ReaderConfigurationInfo& ReaderDriverModel::getReaderConfigurationInfo(const QModelIndex& pIndex) const -{ - return mConnectedReaders.at(pIndex.row()); -} - - -QString ReaderDriverModel::getHTMLDescription(const QModelIndex& pIndex) const -{ - if (mConnectedReaders.isEmpty()) - { - return QString(); - } - - if (mKnownDrivers.contains(mConnectedReaders.at(pIndex.row()))) - { - return tr("Card reader ready for use."); - } - - return tr("Please download and install the driver you can find at: %1"). - arg(QStringLiteral("%1").arg(mConnectedReaders.at(pIndex.row()).getUrl())); -} - - -bool ReaderDriverModel::isInstalledSupportedReader(const QModelIndex& pIndex) const -{ - const auto& readerSettingsInfo = mConnectedReaders.at(pIndex.row()); - const bool knownDriver = mKnownDrivers.contains(readerSettingsInfo); - const bool knownReader = readerSettingsInfo.isKnownReader(); - return knownDriver && knownReader; -} diff --git a/src/widget/ReaderDriverModel.h b/src/widget/ReaderDriverModel.h deleted file mode 100644 index a3ea3c1..0000000 --- a/src/widget/ReaderDriverModel.h +++ /dev/null @@ -1,59 +0,0 @@ -/*! - * \brief Model implementation for the reader driver table - * - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "ReaderDetector.h" - -#include -#include -#include - - -namespace governikus -{ - -class ReaderDriverModel - : public QAbstractTableModel -{ - Q_OBJECT - - private: - const int NUMBER_OF_COLUMNS = 2; - - QSet mKnownDrivers; - QVector mConnectedReaders; - - QString getStatus(const ReaderConfigurationInfo& pReaderConfigurationInfo) const; - void collectReaderData(); - - public: - enum ColumnId : int - { - ReaderName = 0, - ReaderStatus = 1 - }; - ReaderDriverModel(QObject* pParent = nullptr); - - virtual QVariant headerData(int pSection, Qt::Orientation pOrientation, int pRole) const override; - virtual int rowCount(const QModelIndex& pParent = QModelIndex()) const override; - virtual int columnCount(const QModelIndex& pParent = QModelIndex()) const override; - virtual QVariant data(const QModelIndex& pIndex, int pRole = Qt::DisplayRole) const override; - - const ReaderConfigurationInfo& getReaderConfigurationInfo(const QModelIndex& pIndex) const; - QString getHTMLDescription(const QModelIndex& pIndex) const; - bool isInstalledSupportedReader(const QModelIndex& pIndex) const; - - public Q_SLOTS: - void onUpdateContent(); - - Q_SIGNALS: - void fireModelChanged(); - -}; - - -} /* namespace governikus */ diff --git a/src/widget/RemotePinInputDialog.cpp b/src/widget/RemotePinInputDialog.cpp deleted file mode 100644 index f0bc975..0000000 --- a/src/widget/RemotePinInputDialog.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "RemotePinInputDialog.h" - -#include "generic/PasswordEdit.h" -#include "ui_RemotePinInputDialog.h" - -#include -#include -#include - -using namespace governikus; - - -Q_DECLARE_LOGGING_CATEGORY(gui) - - -RemotePinInputDialog::RemotePinInputDialog(QWidget* pParent) - : QDialog(pParent, Qt::WindowTitleHint) - , mUi(new Ui::RemotePinInputDialog()) -{ - mUi->setupUi(this); - connect(mUi->buttonBox, &QDialogButtonBox::accepted, this, &RemotePinInputDialog::onOkClicked); - connect(mUi->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); - - mUi->pinEntryLine->setMaxLength(4, false); - mUi->pinEntryLine->setAlignment(Qt::AlignCenter); - QRegularExpression onlyNumbersExpression(QStringLiteral("[0-9]*")); - mUi->pinEntryLine->configureValidation(onlyNumbersExpression, tr("Only digits (0-9) are allowed.")); -} - - -RemotePinInputDialog::~RemotePinInputDialog() -{ - -} - - -const QString RemotePinInputDialog::getPin(QWidget* pParent) -{ - RemotePinInputDialog dialog(pParent); - int result = dialog.exec(); - if (!result) - { - return QString(); - } - - if (isValidPin(dialog.getPinEntry())) - { - return dialog.getPinEntry(); - } - - qCCritical(gui) << "Pairing code entered was not valid (4 Digits)"; - return QString(); -} - - -bool RemotePinInputDialog::isValidPin(const QString& pPin) -{ - return QRegularExpression("[0-9]{4}").match(pPin).hasMatch(); -} - - -QString RemotePinInputDialog::getPinEntry() const -{ - return mUi->pinEntryLine->text(); -} - - -void RemotePinInputDialog::onOkClicked() -{ - if (isValidPin(getPinEntry())) - { - accept(); - } - else - { - QToolTip::showText(mUi->pinEntryLine->mapToGlobal(QPoint(0, 0)), tr("A pairing code has to be 4 digits long."), mUi->pinEntryLine, QRect(), 3000); - } -} diff --git a/src/widget/RemotePinInputDialog.h b/src/widget/RemotePinInputDialog.h deleted file mode 100644 index c839cf4..0000000 --- a/src/widget/RemotePinInputDialog.h +++ /dev/null @@ -1,39 +0,0 @@ -/*! - * \brief Dialog for PIN input for device pairing. - * - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include - -namespace Ui -{ -class RemotePinInputDialog; -} - -namespace governikus -{ - -class RemotePinInputDialog - : public QDialog -{ - Q_OBJECT - - private: - QScopedPointer mUi; - - private Q_SLOTS: - void onOkClicked(); - - public: - RemotePinInputDialog(QWidget* pParent = 0); - virtual ~RemotePinInputDialog() override; - - static const QString getPin(QWidget* pParent); - static bool isValidPin(const QString& pPin); - QString getPinEntry() const; -}; - -} /* namespace governikus */ diff --git a/src/widget/SelfInformationWidget.cpp b/src/widget/SelfInformationWidget.cpp deleted file mode 100644 index a144cd1..0000000 --- a/src/widget/SelfInformationWidget.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "SelfInformationWidget.h" - -#include "AppSettings.h" -#include "ui_SelfInformationWidget.h" - -#include -#include -#include -#include - -using namespace governikus; - -SelfInformationWidget::SelfInformationWidget(QWidget* pParent) - : QWidget(pParent) - , mUi(new Ui::SelfInformationWidget()) -{ - mUi->setupUi(this); - - connect(mUi->selfAuthenticationButton, &QAbstractButton::clicked, this, &SelfInformationWidget::selfAuthenticationRequested); - connect(&AppSettings::getInstance().getGeneralSettings(), &AbstractSettings::fireSettingsChanged, this, &SelfInformationWidget::onSettingsChanged); - - mPixDescLogoLabel.reset(new QPixmap(QStringLiteral(":/images/siteWithLogo.png"))); - mUi->descriptionLogoLabel->setPixmap(mPixDescLogoLabel->scaled(159, 120, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); - - onSettingsChanged(); -} - - -SelfInformationWidget::~SelfInformationWidget() -{ -} - - -void SelfInformationWidget::paintEvent(QPaintEvent*) -{ - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} - - -void SelfInformationWidget::onSettingsChanged() -{ - const auto desc = tr("Use the button 'See my personal data now...' to display the data stored on your ID card. An Internet connection is required to display the data."); - const auto hyperlink = QStringLiteral(R"(%2)").arg(tr("https://www.ausweisapp.bund.de/datenschutz/"), tr("data privacy statement")); - const auto info = tr("Your personal data is neither saved nor processed in any way. Please see our %1 for details on how your personal data is processed.").arg(hyperlink); - mUi->descriptionLabel->setText(desc + QStringLiteral("

") + info); - - if (AppSettings::getInstance().getGeneralSettings().useSelfAuthTestUri()) - { - mUi->selfAuthenticationButton->setStyleSheet(QStringLiteral("QPushButton { background: red; }")); - mUi->selfAuthenticationButton->setToolTip(tr("Test environment")); - } - else - { - mUi->selfAuthenticationButton->setStyleSheet(QString()); - mUi->selfAuthenticationButton->setToolTip(QString()); - } -} - - -void SelfInformationWidget::changeEvent(QEvent* pEvent) -{ - if (pEvent->type() == QEvent::LanguageChange) - { - mUi->retranslateUi(this); - onSettingsChanged(); - } - QWidget::changeEvent(pEvent); -} diff --git a/src/widget/SelfInformationWidget.h b/src/widget/SelfInformationWidget.h deleted file mode 100644 index 4528b4a..0000000 --- a/src/widget/SelfInformationWidget.h +++ /dev/null @@ -1,45 +0,0 @@ -/*! - * \brief Widget for starting the self information workflow. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include - -namespace Ui -{ -class SelfInformationWidget; -} - -namespace governikus -{ - -class SelfInformationWidget - : public QWidget -{ - Q_OBJECT - - public: - SelfInformationWidget(QWidget* pParent = nullptr); - virtual ~SelfInformationWidget() override; - - protected: - virtual void changeEvent(QEvent* pEvent) override; - - Q_SIGNALS: - void selfAuthenticationRequested(); - - private: - QScopedPointer mUi; - QScopedPointer mPixDescLogoLabel; - - virtual void paintEvent(QPaintEvent*) override; - - private Q_SLOTS: - void onSettingsChanged(); -}; - -} /* namespace governikus */ diff --git a/src/widget/SelfInformationWidget.ui b/src/widget/SelfInformationWidget.ui deleted file mode 100644 index 933fe49..0000000 --- a/src/widget/SelfInformationWidget.ui +++ /dev/null @@ -1,196 +0,0 @@ - - - SelfInformationWidget - - - - 0 - 0 - 607 - 375 - - - - - 20 - - - 20 - - - 20 - - - 20 - - - - - - 20 - - - 10 - - - 20 - - - 10 - - - - - - - Qt::TabFocus - - - eID Logo - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - Qt::TabFocus - - - You can use your ID card anywhere you see this logo. - - - true - - - - - - - - - - - - - 20 - - - 20 - - - 20 - - - 20 - - - - - - - Qt::TabFocus - - - - - - See my personal data - - - true - - - - - - - - - Qt::TabFocus - - - true - - - true - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 20 - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - See my personal data now... - - - - - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - diff --git a/src/widget/SettingsWidget.cpp b/src/widget/SettingsWidget.cpp deleted file mode 100644 index e0ac024..0000000 --- a/src/widget/SettingsWidget.cpp +++ /dev/null @@ -1,381 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "SettingsWidget.h" - -#include "AppSettings.h" -#include "ReaderManager.h" -#include "ui_SettingsWidget.h" - -#include -#include -#include -#include -#include - -using namespace governikus; - -SettingsWidget::SettingsWidget(QWidget* pParent) - : QWidget(pParent) - , mUi(new Ui::SettingsWidget()) - , mScanRunning(false) - , mWorkflowRunning(false) - , mSettingsChanged(false) -{ - mUi->setupUi(this); - - connect(mUi->diagnosisButton, &QAbstractButton::clicked, this, &SettingsWidget::diagnosisRequested); - - connect(mUi->generalTab, &GeneralSettingsWidget::settingsChanged, this, &SettingsWidget::onSettingsChanged); - connect(mUi->pinTab, &PinSettingsWidget::firePinButtonEnabledUpdated, this, &SettingsWidget::onUpdateButtonState); - connect(mUi->pinTab, &PinSettingsWidget::firePinButtonEnabledUpdated, this, &SettingsWidget::onUpdateApplyButtonText); - - connect(mUi->cancelButton, &QPushButton::clicked, this, &SettingsWidget::onCancelButtonClicked); - connect(mUi->applyButton, &QPushButton::clicked, this, &SettingsWidget::onApplyButtonClicked); - - connect(mUi->settingsTabWidget, &QTabWidget::currentChanged, this, &SettingsWidget::onTabChanged); - - mUi->pinTab->setUseScreenKeyboard(AppSettings::getInstance().getGeneralSettings().isUseScreenKeyboard()); - connect(this, &SettingsWidget::fireBackspacePressedOnApply, mUi->pinTab, &PinSettingsWidget::onBackspacePressedOnApply); - -#ifndef QT_NO_DEBUG - mDeveloperTab.reset(new DeveloperSettingsWidget()); - mDeveloperTab->setObjectName(QStringLiteral("developerTab")); - mUi->settingsTabWidget->addTab(mDeveloperTab.data(), QString()); - setDeveloperTabName(); - - connect(mDeveloperTab.data(), &DeveloperSettingsWidget::fireSettingsChanged, this, &SettingsWidget::onSettingsChanged); -#endif - - setSettingsChanged(false); - onUpdateApplyButtonText(); -} - - -SettingsWidget::~SettingsWidget() -{ -} - - -void SettingsWidget::keyPressEvent(QKeyEvent* pEvent) -{ - if (pEvent->key() == Qt::Key_Backspace && mUi->applyButton->hasFocus()) - { - Q_EMIT fireBackspacePressedOnApply(); - } - QWidget::keyPressEvent(pEvent); -} - - -void SettingsWidget::workflowStarted() -{ - mWorkflowRunning = true; - - // disable the non-selected tabs - int tabCount = mUi->settingsTabWidget->count(); - for (int i = 0; i < tabCount; ++i) - { - mUi->settingsTabWidget->setTabEnabled(i, i == mUi->settingsTabWidget->currentIndex()); - } - - onUpdateButtonState(); -} - - -void SettingsWidget::workflowFinished() -{ - mWorkflowRunning = false; - - // enable all tabs - int tabCount = mUi->settingsTabWidget->count(); - for (int i = 0; i < tabCount; ++i) - { - mUi->settingsTabWidget->setTabEnabled(i, true); - } - - onUpdateButtonState(); - - if (mUi->pinTab->isVisible()) - { - QMetaObject::invokeMethod(mUi->pinTab, "updateReaders", Qt::QueuedConnection); - } -} - - -void SettingsWidget::switchToGuiModule(GuiModule pModule) -{ - switch (pModule) - { - case GuiModule::START_PAGE: - case GuiModule::IDENTIFY: - // not handled here - break; - - case GuiModule::GENERAL_SETTINGS: - mUi->settingsTabWidget->setCurrentWidget(mUi->generalTab); - break; - - case GuiModule::PIN_SETTINGS: - mUi->settingsTabWidget->setCurrentWidget(mUi->pinTab); - break; - - case GuiModule::DEVICE_SETTINGS: - mUi->settingsTabWidget->setCurrentWidget(mUi->readerDeviceTab); - break; - } -} - - -void SettingsWidget::paintEvent(QPaintEvent*) -{ - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} - - -void SettingsWidget::hideEvent(QHideEvent* pEvent) -{ - onTabChanged(-1); - QWidget::hideEvent(pEvent); -} - - -void SettingsWidget::showEvent(QShowEvent* pEvent) -{ - QWidget::showEvent(pEvent); - - if (mUi->settingsTabWidget->currentWidget() == mUi->pinTab || mUi->settingsTabWidget->currentWidget() == mUi->readerDeviceTab) - { - mScanRunning = true; - ReaderManager& readerManager = ReaderManager::getInstance(); - readerManager.startScanAll(mUi->settingsTabWidget->currentWidget() == mUi->pinTab); - } -} - - -void SettingsWidget::onTabChanged(int pIndex) -{ - QWidget* const currentWidget = mUi->settingsTabWidget->widget(pIndex); - if (currentWidget != mUi->generalTab) - { - if (mSettingsChanged) - { - showSettingsChangedMessage(); - } - mUi->pinTab->setUseScreenKeyboard(AppSettings::getInstance().getGeneralSettings().isUseScreenKeyboard()); - } - else - { - setSettingsChanged(mSettingsChanged); - } - - ReaderManager& readerManager = ReaderManager::getInstance(); - if (currentWidget == mUi->pinTab || currentWidget == mUi->readerDeviceTab) - { - mScanRunning = true; - readerManager.startScanAll(currentWidget == mUi->pinTab); - } - else if (mScanRunning) - { - mScanRunning = false; - readerManager.stopScanAll(); - } - - onUpdateApplyButtonText(); - onUpdateButtonState(); -} - - -void SettingsWidget::showSettingsChangedMessage() -{ - QMessageBox msgBox(this); - msgBox.setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("Apply settings?")); - msgBox.setWindowModality(Qt::WindowModal); - msgBox.setText(tr("Do you want to apply the changes?")); - msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); - msgBox.button(QMessageBox::Yes)->setFocus(); - - if (msgBox.exec() == QMessageBox::Yes) - { - applyAppSettings(); - } - else - { - resetSettings(); - } -} - - -void SettingsWidget::onApplyButtonClicked() -{ - const QWidget* const currentWidget = mUi->settingsTabWidget->currentWidget(); - if (currentWidget == mUi->pinTab) - { - // change PIN button clicked - if (mUi->pinTab->getMode() == PinSettingsWidget::Mode::AfterPinChange) - { - mUi->pinTab->setMode(PinSettingsWidget::Mode::Normal); - mUi->pinTab->updateReaders(); - } - else - { - if (mUi->pinTab->isVisible()) - { - Q_EMIT changePinRequested(); - } - } - } - else if (currentWidget == mUi->generalTab -#ifndef QT_NO_DEBUG - || currentWidget == mDeveloperTab.data() -#endif - ) - { - applyAppSettings(); - } - else if (currentWidget == mUi->readerDeviceTab) - { - mUi->settingsTabWidget->setCurrentWidget(mUi->generalTab); - } - -} - - -bool SettingsWidget::isSettingsChanged() -{ - return mSettingsChanged; -} - - -void SettingsWidget::applyAppSettings() -{ - // apply button clicked - mUi->generalTab->apply(); -#ifndef QT_NO_DEBUG - mDeveloperTab->apply(); -#endif - - setSettingsChanged(false); -} - - -void SettingsWidget::onCancelButtonClicked() -{ - resetSettings(); - Q_EMIT settingsDone(); -} - - -void SettingsWidget::onSettingsChanged() -{ - setSettingsChanged(true); -} - - -void SettingsWidget::resetSettings() -{ - mUi->generalTab->reset(); -#ifndef QT_NO_DEBUG - mDeveloperTab->reset(); -#endif - setSettingsChanged(false); -} - - -QString SettingsWidget::getActiveTabObjectName() -{ - return mUi->settingsTabWidget->currentWidget()->objectName(); -} - - -void SettingsWidget::setSettingsChanged(bool pChanged) -{ - mSettingsChanged = pChanged; - onUpdateButtonState(); -} - - -void SettingsWidget::changeEvent(QEvent* pEvent) -{ - if (pEvent->type() == QEvent::LanguageChange) - { - mUi->retranslateUi(this); - setDeveloperTabName(); - onUpdateApplyButtonText(); - - } - QWidget::changeEvent(pEvent); -} - - -void SettingsWidget::setDeveloperTabName() -{ -#ifndef QT_NO_DEBUG - mUi->settingsTabWidget->setTabText(mUi->settingsTabWidget->indexOf(mDeveloperTab.data()), tr("Developer Settings")); -#endif -} - - -void SettingsWidget::onUpdateApplyButtonText() -{ - const QWidget* const currentWidget = mUi->settingsTabWidget->currentWidget(); - if (currentWidget == mUi->generalTab -#ifndef QT_NO_DEBUG - || currentWidget == mDeveloperTab.data() -#endif - ) - { - mUi->applyButton->setText(tr("Apply")); - } - else if (currentWidget == mUi->pinTab) - { - mUi->applyButton->setText(mUi->pinTab->getButtonText()); - } - else if (currentWidget == mUi->readerDeviceTab) - { - mUi->applyButton->setText(tr("OK")); - } -} - - -void SettingsWidget::onUpdateButtonState() -{ - QPushButton* const applyButton = mUi->applyButton; - QPushButton* const cancelButton = mUi->cancelButton; - if (mWorkflowRunning == true) - { - applyButton->setEnabled(false); - cancelButton->setEnabled(false); - return; - } - - cancelButton->setEnabled(true); - - const QWidget* const currentWidget = mUi->settingsTabWidget->currentWidget(); - if (currentWidget == mUi->generalTab -#ifndef QT_NO_DEBUG - || currentWidget == mDeveloperTab.data() -#endif - ) - { - applyButton->setEnabled(mSettingsChanged); - } - else if (currentWidget == mUi->pinTab) - { - const bool pinButtonEnabled = mUi->pinTab->getPinButtonEnabled(); - mUi->applyButton->setEnabled(pinButtonEnabled); - if (pinButtonEnabled) - { - applyButton->setAutoDefault(true); - applyButton->setDefault(true); - applyButton->setFocus(); - } - } - else if (currentWidget == mUi->readerDeviceTab) - { - applyButton->setEnabled(true); - } -} diff --git a/src/widget/SettingsWidget.h b/src/widget/SettingsWidget.h deleted file mode 100644 index db24be2..0000000 --- a/src/widget/SettingsWidget.h +++ /dev/null @@ -1,89 +0,0 @@ -/*! - * \brief Widget for the settings. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "DeveloperSettingsWidget.h" -#include "generic/GuiModule.h" - -#include - - -namespace Ui -{ -class SettingsWidget; -} - -namespace governikus -{ - -class SettingsWidget - : public QWidget -{ - Q_OBJECT - - private: - QScopedPointer mUi; - bool mScanRunning; - bool mWorkflowRunning; - bool mSettingsChanged; - -#ifndef QT_NO_DEBUG - QScopedPointer mDeveloperTab; -#endif - - void resetSettings(); - void setSettingsChanged(bool pChanged); - void applyAppSettings(); - void setDeveloperTabName(); - - public Q_SLOTS: - void onTabChanged(int pIndex); - - private Q_SLOTS: - void onApplyButtonClicked(); - void onCancelButtonClicked(); - - void onUpdateButtonState(); - void onUpdateApplyButtonText(); - void onSettingsChanged(); - - protected: - virtual void paintEvent(QPaintEvent*) override; - virtual void hideEvent(QHideEvent* pEvent) override; - virtual void showEvent(QShowEvent* pEvent) override; - virtual void changeEvent(QEvent* pEvent) override; - - public: - SettingsWidget(QWidget* pParent = nullptr); - virtual ~SettingsWidget() override; - - void keyPressEvent(QKeyEvent* pEvent) override; - - bool remoteScanRunning() const - { - return mScanRunning; - } - - - void workflowStarted(); - void workflowFinished(); - - void switchToGuiModule(GuiModule pModule); - - QString getActiveTabObjectName(); - - bool isSettingsChanged(); - void showSettingsChangedMessage(); - - Q_SIGNALS: - void changePinRequested(); - void diagnosisRequested(); - void settingsDone(); - void fireBackspacePressedOnApply(); -}; - -} /* namespace governikus */ diff --git a/src/widget/SetupAssistantGui.cpp b/src/widget/SetupAssistantGui.cpp deleted file mode 100644 index d880858..0000000 --- a/src/widget/SetupAssistantGui.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/*! - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "SetupAssistantGui.h" - -#include - - -using namespace governikus; - - -Q_DECLARE_LOGGING_CATEGORY(gui) - - -SetupAssistantGui::SetupAssistantGui(QWidget* pParentWidget) - : QObject(pParentWidget) - , mWizard(nullptr) -{ -} - - -SetupAssistantGui::~SetupAssistantGui() -{ -} - - -void SetupAssistantGui::activate() -{ - if (!mWizard) - { - QWidget* dialogParent = qobject_cast(parent()); - if (!dialogParent) - { - return; - } - - mWizard = new SetupAssistantWizard(dialogParent); - connect(mWizard, &SetupAssistantWizard::fireChangePinButtonClicked, this, &SetupAssistantGui::fireChangePinButtonClicked); - } - - mWizard->exec(); -} - - -void SetupAssistantGui::deactivate() -{ - if (mWizard) - { - mWizard->close(); - } -} diff --git a/src/widget/SetupAssistantGui.h b/src/widget/SetupAssistantGui.h deleted file mode 100644 index e2f7611..0000000 --- a/src/widget/SetupAssistantGui.h +++ /dev/null @@ -1,36 +0,0 @@ -/*! - * \brief Qt widget based SetupAssistantUi implementation. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "SetupAssistantWizard.h" - -class QWidget; - -namespace governikus -{ - -class SetupAssistantGui - : public QObject -{ - Q_OBJECT - - private: - QPointer mWizard; - - public: - SetupAssistantGui(QWidget* pParentWidget); - virtual ~SetupAssistantGui(); - - void activate(); - void deactivate(); - - Q_SIGNALS: - void fireChangePinButtonClicked(); - -}; - -} /* namespace governikus */ diff --git a/src/widget/SetupAssistantWizard.cpp b/src/widget/SetupAssistantWizard.cpp deleted file mode 100644 index 5cb0861..0000000 --- a/src/widget/SetupAssistantWizard.cpp +++ /dev/null @@ -1,245 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "SetupAssistantWizard.h" - -#include "AppSettings.h" -#include "Env.h" -#include "generic/HelpAction.h" -#include "ReaderDeviceWidget.h" -#include "ReaderInfo.h" -#include "ReaderManager.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace governikus; - -CardReaderPage::CardReaderPage(const QString& pTitle, const QString& pAccessibleName) - : mWidget(new ReaderDeviceWidget(this)) -{ - setTitle(pTitle); - mWidget->prependAccessibleName(pAccessibleName); - - QVBoxLayout* cardReaderPageVLayout = new QVBoxLayout(this); - cardReaderPageVLayout->addWidget(mWidget); -} - - -SetupAssistantWizard::SetupAssistantWizard(QWidget* pParent) - : QWizard(pParent) - , mPageCount(3) - , mSaveHistoryCheckBox(new QCheckBox(this)) - , mChangeTransportPinButton() -{ -#ifdef Q_OS_MACOS - static const int MIN_HEIGHT = 700; -#else - static const int MIN_HEIGHT = 500; -#endif - - setObjectName(QStringLiteral("setupAssistant")); - installEventFilter(this); - setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("setup assistant")); - setMinimumSize(700, MIN_HEIGHT); - setWizardStyle(QWizard::ClassicStyle); - setWindowModality(Qt::WindowModal); - setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); - setOption(QWizard::NoCancelButton, false); - setAttribute(Qt::WA_DeleteOnClose); - - addPage(createWizardInitialPinPage()); - addPage(createWizardCardReaderPage()); - addPage(createConclusionPage()); -} - - -SetupAssistantWizard::~SetupAssistantWizard() -{ -} - - -QString SetupAssistantWizard::createAccessibleName(const QString& pName, const QString& pText) const -{ - const QString& stepIndex = QString::number(pageIds().size() + 1); - const QString& pageCount = QString::number(mPageCount); - - auto result = pName + QStringLiteral(". ") + tr("Step %1 of %2").arg(stepIndex, pageCount) + QStringLiteral(". ") + pText; - return result.remove(QLatin1Char('"')).trimmed(); -} - - -QString SetupAssistantWizard::createTitle(const QString& pName) const -{ - const QString& stepIndex = QString::number(pageIds().size() + 1); - const QString& pageCount = QString::number(mPageCount); - - return QStringLiteral("
") - + tr("Step %1 of %2").arg(stepIndex, pageCount) - + QStringLiteral("
%1
").arg(pName); -} - - -QString SetupAssistantWizard::createDescription(const QString& pTitle, const QString& pSummary) const -{ - return QStringLiteral("
%1

%2").arg(pTitle, pSummary); -} - - -QWizardPage* SetupAssistantWizard::createWizardInitialPinPage() -{ - QWizardPage* initialPinPage = new QWizardPage; - const auto& introduction = tr("Introduction"); - initialPinPage->setTitle(createTitle(introduction)); - - const auto& welcome = tr("Welcome to the AusweisApp2 setup assistant." - " This assistant will guide you through the setup process in %1 steps." - " You can cancel the setup assistant at any time. To restart it, go to the tab \"Help\" and select \"Setup assistant\".").arg(mPageCount); - QLabel* label = new QLabel(welcome); - label->setWordWrap(true); - label->setFocusPolicy(Qt::TabFocus); - label->setAccessibleName(createAccessibleName(introduction, welcome)); - - QVBoxLayout* initialPinPageLayout = new QVBoxLayout; - initialPinPageLayout->addWidget(label); - - const auto& historyTitle = tr("History"); - const auto& historySummary = tr("Do you want to save the history of your authentications?"); - QLabel* historyDescLabel = new QLabel(createDescription(historyTitle, historySummary)); - historyDescLabel->setWordWrap(true); - historyDescLabel->setFocusPolicy(Qt::TabFocus); - historyDescLabel->setAccessibleName(createAccessibleName(historyTitle, historySummary)); - - initialPinPageLayout->addWidget(historyDescLabel); - - QWidget* saveHistoryWidget = new QWidget(this); - - QSizePolicy saveHistorySizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); - saveHistorySizePolicy.setHorizontalStretch(0); - saveHistorySizePolicy.setVerticalStretch(0); - saveHistorySizePolicy.setHeightForWidth(saveHistoryWidget->sizePolicy().hasHeightForWidth()); - saveHistoryWidget->setSizePolicy(saveHistorySizePolicy); - - QFormLayout* saveHistoryFormLayout = new QFormLayout(saveHistoryWidget); - saveHistoryFormLayout->setHorizontalSpacing(30); - saveHistoryFormLayout->setContentsMargins(11, 11, 11, 11); - saveHistoryFormLayout->setContentsMargins(0, 5, 0, 20); - - QLabel* saveHistoryLabel = new QLabel(); - saveHistoryLabel->setFocusPolicy(Qt::TabFocus); - saveHistoryLabel->setText(historyTitle + QLatin1Char(':')); - - mSaveHistoryCheckBox->setText(tr("save")); - mSaveHistoryCheckBox->setAccessibleName(tr("save history")); - mSaveHistoryCheckBox->setChecked(AppSettings::getInstance().getHistorySettings().isEnabled()); - - saveHistoryFormLayout->setWidget(0, QFormLayout::LabelRole, saveHistoryLabel); - saveHistoryFormLayout->setWidget(0, QFormLayout::FieldRole, mSaveHistoryCheckBox); - - initialPinPageLayout->addWidget(saveHistoryWidget); - - initialPinPage->setLayout(initialPinPageLayout); - - return initialPinPage; -} - - -QWizardPage* SetupAssistantWizard::createWizardCardReaderPage() -{ - const auto& title = tr("Card Readers"); - const auto& titleField = createTitle(title); - const auto& accessibleField = createAccessibleName(title); - return new CardReaderPage(titleField, accessibleField); -} - - -QWizardPage* SetupAssistantWizard::createConclusionPage() -{ - QWizardPage* conclusionPage = new QWizardPage; - const auto& almostDone = tr("Almost done!"); - conclusionPage->setTitle(createTitle(almostDone)); - - QVBoxLayout* conclusionPageVLayout = new QVBoxLayout(conclusionPage); - - - const auto& title = tr("Personal 6 - digit PIN"); - const auto& desc = tr("Prior to the first use of the online identification function, you have to replace the transport PIN by an individual 6-digit PIN. " - "The transport PIN was sent to you by postal mail."); - QLabel* transportPinLabel = new QLabel(createDescription(title, desc)); - transportPinLabel->setWordWrap(true); - transportPinLabel->setFocusPolicy(Qt::TabFocus); - transportPinLabel->setAccessibleName(createAccessibleName(title, desc)); - - conclusionPageVLayout->addWidget(transportPinLabel); - - mChangeTransportPinButton = new QPushButton(conclusionPage); - mChangeTransportPinButton->setText(tr("Set individual PIN")); - connect(mChangeTransportPinButton.data(), &QAbstractButton::clicked, this, &SetupAssistantWizard::onChangeTransportPinButtonPressed); - - QSizePolicy transportPinSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - transportPinSizePolicy.setHorizontalStretch(0); - transportPinSizePolicy.setVerticalStretch(0); - transportPinSizePolicy.setHeightForWidth(mChangeTransportPinButton->sizePolicy().hasHeightForWidth()); - mChangeTransportPinButton->setSizePolicy(transportPinSizePolicy); - - conclusionPageVLayout->addWidget(mChangeTransportPinButton); - - const QString onlineHelpUrl = HelpAction::getOnlineUrl(QStringLiteral("setupAssistantSetupCompleted")); - const auto conclusionDesc = - tr("AusweisApp2 is now ready for use." - " For more information on the software or the online identification function, visit the %1online help%2.") - .arg(QStringLiteral("").arg(onlineHelpUrl), QStringLiteral("")); - QLabel* conclusionDescLabel = new QLabel(QStringLiteral("
") + conclusionDesc); - conclusionDescLabel->setWordWrap(true); - conclusionDescLabel->setFocusPolicy(Qt::TabFocus); - conclusionDescLabel->setAccessibleName(createAccessibleName(almostDone, conclusionDesc)); - conclusionDescLabel->setOpenExternalLinks(true); - - conclusionPageVLayout->addWidget(conclusionDescLabel); - - QSpacerItem* verticalSpacer = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); - conclusionPageVLayout->addItem(verticalSpacer); - - return conclusionPage; -} - - -void SetupAssistantWizard::onChangeTransportPinButtonPressed() -{ - auto& historySettings = AppSettings::getInstance().getHistorySettings(); - historySettings.setEnabled(mSaveHistoryCheckBox->isChecked()); - historySettings.save(); - Q_EMIT fireChangePinButtonClicked(); - close(); -} - - -void SetupAssistantWizard::accept() -{ - auto& historySettings = AppSettings::getInstance().getHistorySettings(); - historySettings.setEnabled(mSaveHistoryCheckBox->isChecked()); - historySettings.save(); - hide(); -} - - -bool SetupAssistantWizard::eventFilter(QObject* pObject, QEvent* pEvent) -{ - if (pEvent->type() == QEvent::KeyPress) - { - QKeyEvent* keyEvent = static_cast(pEvent); - if (keyEvent->key() == Qt::Key_F1) - { - HelpAction::openContextHelp(objectName()); - return true; - } - } - return QWizard::eventFilter(pObject, pEvent); -} diff --git a/src/widget/SetupAssistantWizard.h b/src/widget/SetupAssistantWizard.h deleted file mode 100644 index 15b823b..0000000 --- a/src/widget/SetupAssistantWizard.h +++ /dev/null @@ -1,75 +0,0 @@ -/*! - * \brief Setup assistant wizard before application startup. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - - -namespace governikus -{ - -class ReaderDeviceWidget; -class SetupAssistantWizard; - - -class CardReaderPage - : public QWizardPage -{ - Q_OBJECT - - private: - ReaderDeviceWidget* const mWidget; - - public: - CardReaderPage(const QString& pTitle, const QString& pAccessibleName); - virtual ~CardReaderPage() = default; -}; - - -class SetupAssistantWizard - : public QWizard -{ - Q_OBJECT - - int mPageCount; - QPointer mSaveHistoryCheckBox; - QPointer mChangeTransportPinButton; - - public: - SetupAssistantWizard(QWidget* pParent = nullptr); - virtual ~SetupAssistantWizard() override; - - virtual void accept() override; - bool isRemindWizardAgain(); - - Q_SIGNALS: - void fireChangePinButtonClicked(); - - private: - QString createAccessibleName(const QString& pName, const QString& pText = QString()) const; - QString createTitle(const QString& pName) const; - QString createDescription(const QString& pTitle, const QString& pSummary) const; - QWizardPage* createWizardInitialPinPage(); - QWizardPage* createWizardCardReaderPage(); - QWizardPage* createConclusionPage(); - - private Q_SLOTS: - void onChangeTransportPinButtonPressed(); - - protected: - virtual bool eventFilter(QObject* pObject, QEvent* pEvent) override; - -}; - -} /* namespace governikus */ diff --git a/src/widget/UIPlugInWidgets.cpp b/src/widget/UIPlugInWidgets.cpp deleted file mode 100644 index f25212a..0000000 --- a/src/widget/UIPlugInWidgets.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/*! - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "UIPlugInWidgets.h" - -#include "workflow/WorkflowAuthenticateQtGui.h" -#include "workflow/WorkflowChangePinQtGui.h" -#include "workflow/WorkflowSelfInfoQtGui.h" - -using namespace governikus; - -UIPlugInWidgets::UIPlugInWidgets() - : UIPlugIn() - , mGui() -{ - connect(&mGui, &AppQtGui::quitApplicationRequested, this, &UIPlugIn::fireQuitApplicationRequest); - connect(&mGui, &AppQtGui::fireChangePinRequested, this, &UIPlugIn::fireChangePinRequest); - connect(&mGui, &AppQtGui::selfAuthenticationRequested, this, &UIPlugIn::fireSelfAuthenticationRequested); - connect(&mGui, &AppQtGui::fireSwitchToReaderSettingsRequested, this, &UIPlugIn::onSwitchToReaderSettingsRequested); - connect(&mGui, &AppQtGui::fireCloseReminderFinished, this, &UIPlugInWidgets::fireCloseReminderFinished); - connect(this, &UIPlugIn::fireShowUserInformation, &mGui, &AppQtGui::onShowUserInformation); - mGui.init(); -} - - -UIPlugInWidgets::~UIPlugInWidgets() -{ -} - - -void UIPlugInWidgets::doShutdown() -{ - mGui.shutdown(); -} - - -void UIPlugInWidgets::onWorkflowStarted(QSharedPointer pContext) -{ - pContext->setReaderPlugInTypes({ReaderManagerPlugInType::PCSC, ReaderManagerPlugInType::REMOTE}); - - QSharedPointer currentWorkflowGui; - if (auto changePinContext = pContext.objectCast()) - { - currentWorkflowGui = mGui.createWorkflowChangePinUi(changePinContext); - mGui.activateWorkflowUi(currentWorkflowGui); - return; - } - - bool allowHideAfterWorklow = true; - if (auto selfAuthContext = pContext.objectCast()) - { - if (mGui.askChangeTransportPinNow()) - { - allowHideAfterWorklow = false; - Q_EMIT pContext->fireCancelWorkflow(); - } - currentWorkflowGui = mGui.createWorkflowSelfInfoUi(selfAuthContext); - } - else if (auto authContext = pContext.objectCast()) - { - if (mGui.askChangeTransportPinNow()) - { - allowHideAfterWorklow = false; - Q_EMIT pContext->fireCancelWorkflow(); - } - currentWorkflowGui = mGui.createWorkflowAuthenticateUi(authContext); - } - - Q_ASSERT(currentWorkflowGui != nullptr); - mGui.activateWorkflowUi(currentWorkflowGui, allowHideAfterWorklow); - pContext->setStateApproved(); -} - - -void UIPlugInWidgets::onWorkflowFinished(QSharedPointer pContext) -{ - Q_UNUSED(pContext) - mGui.deactivateCurrentWorkflowUi(); -} - - -void UIPlugInWidgets::onApplicationStarted() -{ - mGui.onApplicationStarted(); -} - - -void UIPlugInWidgets::onShowUi(UiModule pModule) -{ - mGui.show(pModule); -} - - -void UIPlugInWidgets::onShowReaderSettings() -{ - mGui.switchToReaderSettings(); -} - - -#ifndef QT_NO_NETWORKPROXY -void UIPlugInWidgets::onProxyAuthenticationRequired(const QNetworkProxy& pProxy, QAuthenticator* pAuthenticator) -{ - mGui.onProxyAuthenticationRequired(pProxy, pAuthenticator); -} - - -#endif diff --git a/src/widget/UIPlugInWidgets.h b/src/widget/UIPlugInWidgets.h deleted file mode 100644 index 65c070c..0000000 --- a/src/widget/UIPlugInWidgets.h +++ /dev/null @@ -1,43 +0,0 @@ -/*! - * \brief QWidgets implementation of UIPlugIn. - * - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "AppQtGui.h" -#include "view/UIPlugIn.h" - -namespace governikus -{ - -class AppQtGui; - -class UIPlugInWidgets - : public UIPlugIn -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID "governikus.UIPlugIn" FILE "metadata.json") - Q_INTERFACES(governikus::UIPlugIn) - - private: - AppQtGui mGui; - - public: - UIPlugInWidgets(); - virtual ~UIPlugInWidgets() override; - - public Q_SLOTS: - virtual void doShutdown() override; - virtual void onWorkflowStarted(QSharedPointer pContext) override; - virtual void onWorkflowFinished(QSharedPointer pContext) override; - virtual void onApplicationStarted() override; - virtual void onShowUi(UiModule pModule) override; - virtual void onShowReaderSettings() override; -#ifndef QT_NO_NETWORKPROXY - virtual void onProxyAuthenticationRequired(const QNetworkProxy& pProxy, QAuthenticator* pAuthenticator) override; -#endif -}; - -} /* namespace governikus */ diff --git a/src/widget/UpdateWindow.cpp b/src/widget/UpdateWindow.cpp deleted file mode 100644 index 8e38bef..0000000 --- a/src/widget/UpdateWindow.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/*! - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "Env.h" -#include "Service.h" -#include "UpdateWindow.h" - -#include "ui_UpdateWindow.h" - -#include -#include - -using namespace governikus; - -UpdateWindow::UpdateWindow(QWidget* pParent) - : QDialog(pParent) - , mUi() - , mUpdateData() -{ - setAttribute(Qt::WA_DeleteOnClose, true); - - mUpdateData = Env::getSingleton()->getUpdateData(); - fillData(); - show(); -} - - -UpdateWindow::~UpdateWindow() -{ -} - - -void UpdateWindow::onUpdateClicked() -{ - const auto& url = mUpdateData.getUrl(); - qDebug() << "Download application update:" << url; - if (!QDesktopServices::openUrl(url)) - { - Q_EMIT fireShowUpdateDialog(QMessageBox::Warning, tr("Unable to open this link in a browser. Please copy and paste the link into the address bar of your browser.")); - } - close(); -} - - -void UpdateWindow::onSkipVersionClicked() -{ - Env::getSingleton()->skipVersion(mUpdateData.getVersion()); - close(); -} - - -void UpdateWindow::fillData() -{ - if (!mUi) - { - mUi.reset(new Ui::UpdateWindow()); - mUi->setupUi(this); - - connect(mUi->skipButton, &QAbstractButton::clicked, this, &UpdateWindow::onSkipVersionClicked); - connect(mUi->reminderButton, &QAbstractButton::clicked, this, &QWidget::close); - connect(mUi->downloadButton, &QAbstractButton::clicked, this, &UpdateWindow::onUpdateClicked); - } - - mUi->downloadLabel->setText(mUi->downloadLabel->text().arg(mUpdateData.getVersion(), QApplication::applicationVersion())); - mUi->linkLabel->setText(QStringLiteral("%1").arg(mUpdateData.getUrl().toString())); - mUi->releaseNotes->setHtml(mUpdateData.getNotes().isEmpty() ? tr("

Download of release notes failed

") : mUpdateData.getNotes()); - mUi->releaseNotes->setAccessibleName(mUi->releaseNotes->toPlainText()); -} - - -void UpdateWindow::changeEvent(QEvent* pEvent) -{ - if (pEvent->type() == QEvent::LanguageChange && mUi) - { - mUi->retranslateUi(this); - } - QWidget::changeEvent(pEvent); -} diff --git a/src/widget/UpdateWindow.h b/src/widget/UpdateWindow.h deleted file mode 100644 index 0f8d7d6..0000000 --- a/src/widget/UpdateWindow.h +++ /dev/null @@ -1,49 +0,0 @@ -/*! - * \brief Window for application updates - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "AppUpdateData.h" - -#include -#include - -namespace Ui -{ -class UpdateWindow; -} - -namespace governikus -{ - -class UpdateWindow - : public QDialog -{ - Q_OBJECT - - private: - QScopedPointer mUi; - bool mSilent; - AppUpdateData mUpdateData; - - void fillData(); - - protected: - virtual void changeEvent(QEvent* pEvent) override; - - public: - UpdateWindow(QWidget* pParent = nullptr); - virtual ~UpdateWindow() override; - - private Q_SLOTS: - void onUpdateClicked(); - void onSkipVersionClicked(); - - Q_SIGNALS: - void fireShowUpdateDialog(QMessageBox::Icon pIcon, const QString& pMsg); -}; - -} /* namespace governikus */ diff --git a/src/widget/UpdateWindow.ui b/src/widget/UpdateWindow.ui deleted file mode 100644 index 1f67d82..0000000 --- a/src/widget/UpdateWindow.ui +++ /dev/null @@ -1,182 +0,0 @@ - - - UpdateWindow - - - Qt::ApplicationModal - - - - 0 - 0 - 600 - 450 - - - - Software Update - - - - :/images/npa.svg:/images/npa.svg - - - - - - - - - 75 - true - - - - Qt::TabFocus - - - A new version of AusweisApp2 is available! - - - - - - - Qt::TabFocus - - - AusweisApp2 %1 is now available - you have %2. Would you like to download it now? - - - - - - - Qt::TabFocus - - - The update file is located at: - - - - - - - Qt::TabFocus - - - LinkLabel - - - true - - - Qt::TextBrowserInteraction - - - - - - - - 75 - true - - - - Qt::TabFocus - - - Release Notes: - - - - - - - 160 - 80 - - - - Qt::WheelFocus - - - Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse - - - - - - - - - - Qt::TabFocus - - - Download this update and close current "AusweisApp2". Install the update and start "AusweisApp2" again. - - - - - - - Qt::TabFocus - - - When you click "Download update", this link will be opened in your browser. - - - - - - - - - Skip update - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Remind me later - - - - - - - Download update - - - true - - - true - - - - - - - - - - - - diff --git a/src/widget/generic/BusyOverlay.h b/src/widget/generic/BusyOverlay.h deleted file mode 100644 index d7181c6..0000000 --- a/src/widget/generic/BusyOverlay.h +++ /dev/null @@ -1,45 +0,0 @@ -/*! - * \brief Widget for the settings. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include - -namespace Ui -{ -class BusyOverlay; -} - -class QMovie; - -namespace governikus -{ - -class BusyOverlay - : public QWidget -{ - Q_OBJECT - - public: - BusyOverlay(bool pStart = true, QWidget* pParent = nullptr); - virtual ~BusyOverlay() override; - - void startAnimation(); - void stopAnimation(); - - virtual QSize sizeHint() const override; - - protected: - void paintEvent(QPaintEvent*) override; - void changeEvent(QEvent* pEvent) override; - - private: - QScopedPointer mUi; - QScopedPointer mMovie; -}; - -} /* namespace governikus */ diff --git a/src/widget/generic/BusyOverlayContainer.cpp b/src/widget/generic/BusyOverlayContainer.cpp deleted file mode 100644 index c68b9ca..0000000 --- a/src/widget/generic/BusyOverlayContainer.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "BusyOverlayContainer.h" - -#include -#include - -#include "BusyOverlay.h" - -using namespace governikus; - -BusyOverlayContainer::BusyOverlayContainer(QWidget* pWidgetToOverlay, bool pStart, QWidget* pParent) - : QStackedWidget(pParent) - , mOverlay(new BusyOverlay(pStart)) -{ - QWidget* busyOverlayContainer = new QWidget; - QBoxLayout* overlayContainerLayout = new QVBoxLayout(busyOverlayContainer); - overlayContainerLayout->addWidget(mOverlay, 0, Qt::AlignHCenter | Qt::AlignVCenter); - - QStackedLayout* stackLayout = qobject_cast(layout()); - stackLayout->setStackingMode(QStackedLayout::StackAll); - - stackLayout->addWidget(pWidgetToOverlay); - stackLayout->addWidget(busyOverlayContainer); - stackLayout->setCurrentIndex(1); -} - - -BusyOverlayContainer::~BusyOverlayContainer() -{ -} - - -void BusyOverlayContainer::startAnimation() -{ - mOverlay->startAnimation(); -} - - -void BusyOverlayContainer::stopAnimation() -{ - mOverlay->stopAnimation(); -} diff --git a/src/widget/generic/BusyOverlayContainer.h b/src/widget/generic/BusyOverlayContainer.h deleted file mode 100644 index 14b9b7b..0000000 --- a/src/widget/generic/BusyOverlayContainer.h +++ /dev/null @@ -1,32 +0,0 @@ -/*! - * \brief An overlay to show a busy indicator. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include - -namespace governikus -{ - -class BusyOverlay; - -class BusyOverlayContainer - : public QStackedWidget -{ - Q_OBJECT - - public: - BusyOverlayContainer(QWidget* pWidgetToOverlay, bool pStart = true, QWidget* pParent = nullptr); - virtual ~BusyOverlayContainer(); - - void startAnimation(); - void stopAnimation(); - - private: - BusyOverlay* mOverlay; -}; - -} /* namespace governikus */ diff --git a/src/widget/generic/ButtonState.h b/src/widget/generic/ButtonState.h deleted file mode 100644 index 5366458..0000000 --- a/src/widget/generic/ButtonState.h +++ /dev/null @@ -1,27 +0,0 @@ -/*! - * \brief Defines the ButtonState enum. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -namespace governikus -{ - -enum class ButtonState -{ - /*! Button is visible, enabled, and focussed. */ - FOCUSSED, - - /*! Button is visible and enabled. */ - ENABLED, - - /*! Button is visible and disabled. */ - DISABLED, - - /*! Button is not visible. */ - HIDDEN -}; - -} /* namespace governikus */ diff --git a/src/widget/generic/ExclusiveButtonGroup.cpp b/src/widget/generic/ExclusiveButtonGroup.cpp deleted file mode 100644 index 1f2e8ee..0000000 --- a/src/widget/generic/ExclusiveButtonGroup.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "ExclusiveButtonGroup.h" - -#include -#include - -using namespace governikus; - -ExclusiveButtonGroup::ExclusiveButtonGroup(QObject* pParent) - : QObject(pParent) - , mButtons() -{ -} - - -ExclusiveButtonGroup::~ExclusiveButtonGroup() -{ -} - - -void ExclusiveButtonGroup::addButton(QAbstractButton* pButton) -{ - mButtons += pButton; - - pButton->installEventFilter(this); - - connect(pButton, &QAbstractButton::clicked, this, &ExclusiveButtonGroup::onButtonClicked); - connect(pButton, &QAbstractButton::pressed, this, &ExclusiveButtonGroup::onButtonPressed); - connect(pButton, &QAbstractButton::released, this, &ExclusiveButtonGroup::onButtonReleased); - connect(pButton, &QAbstractButton::toggled, this, &ExclusiveButtonGroup::onButtonToggled); -} - - -void ExclusiveButtonGroup::removeButton(QAbstractButton* pButton) -{ - if (mButtons.removeAll(pButton) == 0) - { - return; - } - - pButton->removeEventFilter(this); - - disconnect(pButton, &QAbstractButton::clicked, this, &ExclusiveButtonGroup::onButtonClicked); - disconnect(pButton, &QAbstractButton::pressed, this, &ExclusiveButtonGroup::onButtonPressed); - disconnect(pButton, &QAbstractButton::released, this, &ExclusiveButtonGroup::onButtonReleased); - disconnect(pButton, &QAbstractButton::toggled, this, &ExclusiveButtonGroup::onButtonToggled); -} - - -bool ExclusiveButtonGroup::eventFilter(QObject* pWatched, QEvent* pEvent) -{ - if (QAbstractButton* button = qobject_cast(pWatched)) - { - if (pEvent->type() == QEvent::MouseButtonPress && button->isChecked()) - { - return true; - } - - if (pEvent->type() == QEvent::KeyPress) - { - QKeyEvent* keyEvent = static_cast(pEvent); - if (keyEvent->key() == Qt::Key_Select || keyEvent->key() == Qt::Key_Space) - { - return true; - } - } - } - - return false; -} - - -void ExclusiveButtonGroup::onButtonClicked(bool /*pChecked*/) -{ - if (QAbstractButton* button = qobject_cast(sender())) - { - Q_EMIT buttonClicked(button); - } -} - - -void ExclusiveButtonGroup::onButtonPressed() -{ - if (QAbstractButton* button = qobject_cast(sender())) - { - Q_EMIT buttonPressed(button); - } -} - - -void ExclusiveButtonGroup::onButtonReleased() -{ - if (QAbstractButton* button = qobject_cast(sender())) - { - Q_EMIT buttonReleased(button); - } -} - - -void ExclusiveButtonGroup::onButtonToggled(bool pChecked) -{ - if (QAbstractButton* button = qobject_cast(sender())) - { - if (pChecked) - { - for (auto otherButton : qAsConst(mButtons)) - { - if (otherButton != button && otherButton->isChecked()) - { - otherButton->setChecked(false); - } - } - } - - Q_EMIT buttonToggled(button, pChecked); - } -} diff --git a/src/widget/generic/ExclusiveButtonGroup.h b/src/widget/generic/ExclusiveButtonGroup.h deleted file mode 100644 index 6b0bbca..0000000 --- a/src/widget/generic/ExclusiveButtonGroup.h +++ /dev/null @@ -1,56 +0,0 @@ -/*! - * \brief Rudimentary replacement for QButtonGroup to work around tab navigation issues. - * - * Bug in Qt 5.2.1: Buttons in a QButtonGroup cannot be navigated via the Tab key. This - * class provides a work-around for simple cases. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include - -class QAbstractButton; - -namespace governikus -{ - -class ExclusiveButtonGroup - : public QObject -{ - Q_OBJECT - - public: - ExclusiveButtonGroup(QObject* pParent = nullptr); - virtual ~ExclusiveButtonGroup() override; - - const QVector& getButtons() const - { - return mButtons; - } - - - void addButton(QAbstractButton* pButton); - void removeButton(QAbstractButton* pButton); - - virtual bool eventFilter(QObject* pWatched, QEvent* pEvent) override; - - Q_SIGNALS: - void buttonClicked(QAbstractButton* pButton); - void buttonPressed(QAbstractButton* pButton); - void buttonReleased(QAbstractButton* pButton); - void buttonToggled(QAbstractButton* pButton, bool pChecked); - - private Q_SLOTS: - void onButtonClicked(bool pChecked); - void onButtonPressed(); - void onButtonReleased(); - void onButtonToggled(bool pChecked); - - private: - QVector mButtons; -}; - -} /* namespace governikus */ diff --git a/src/widget/generic/GuiModule.h b/src/widget/generic/GuiModule.h deleted file mode 100644 index 344b205..0000000 --- a/src/widget/generic/GuiModule.h +++ /dev/null @@ -1,21 +0,0 @@ -/*! - * \brief Defines the GuiModule enum. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -namespace governikus -{ - -enum class GuiModule -{ - START_PAGE, - IDENTIFY, - GENERAL_SETTINGS, - PIN_SETTINGS, - DEVICE_SETTINGS -}; - -} /* namespace governikus */ diff --git a/src/widget/generic/GuiUtils.cpp b/src/widget/generic/GuiUtils.cpp deleted file mode 100644 index 7b8269e..0000000 --- a/src/widget/generic/GuiUtils.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "GuiUtils.h" - -#include -#include -#include -#include -#include - -using namespace governikus; - - -void GuiUtils::showPinCanPukErrorDialog(CardReturnCode pReturnCode, int pRetryCounter, bool pCanAllowedMode, QWidget* pParent) -{ - QMessageBox messageBox(pParent); - QString title; - QString text; - switch (pReturnCode) - { - case CardReturnCode::INVALID_CAN: - title = tr("Wrong card access number (CAN)"); - if (pCanAllowedMode) - { - text = tr("The given card access number (CAN) is not correct."); - } - else - { - text = tr("The given card access number (CAN) is not correct. You have one more try to enter the correct PIN." - " Please mind that you have to acknowledge this last try with your card access" - " number (CAN)."); - } - break; - - case CardReturnCode::INVALID_PUK: - title = tr("Wrong PUK"); - text = tr("Please enter your PUK again."); - break; - - case CardReturnCode::PUK_INOPERATIVE: - title = tr("PUK is inoperative"); - text = tr("You have correctly entered the PUK ten times and have thus reached the maximum count." - " The PUK is now inoperative and can no longer be used for unblocking the PIN. Please address your" - " competent authority that has issued your ID card for unblocking your PIN."); - break; - - case CardReturnCode::INVALID_PIN: - default: - title = tr("Wrong PIN"); - break; - } - - if (text.isEmpty()) - { - switch (pRetryCounter) - { - case 0: - text = tr("After three wrong entries your PIN is blocked. Using the online identification" - " function is no longer possible.

You can unblock your PIN in the" - " following dialog. The program supports you with the steps now required."); - break; - - case 1: - text = tr("The given PIN is not correct. You have one more try to enter the correct PIN." - " Please mind that you have to acknowledge this last try with your card access" - " number (CAN)."); - break; - - default: - text = tr("The given PIN is not correct. You have %1 tries to enter the correct PIN.").arg(pRetryCounter); - break; - } - } - - messageBox.setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + title); - messageBox.setWindowModality(Qt::WindowModal); - messageBox.setWindowFlags(messageBox.windowFlags() & ~Qt::WindowContextHelpButtonHint); - messageBox.setText(QStringLiteral("

%1

%2

").arg(title, text)); - messageBox.setIconPixmap(QIcon(QStringLiteral(":/images/npa.svg")).pixmap(32, 32)); - messageBox.setStandardButtons(QMessageBox::Ok); - messageBox.button(QMessageBox::Ok)->setFocus(); - - messageBox.exec(); -} - - -bool GuiUtils::showWrongPinBlockedDialog(QWidget* pParent) -{ - QMessageBox messageBox(pParent); - - QString title = tr("PIN blocked"); - QString text = tr("After three wrong entries your PIN is blocked. Using the online identification" - " function is no longer possible.
You can unblock the PIN as" - " follows:
  1. Select the \"Settings\" function.
  2. Select the \"PIN" - " Management\" tab.
  3. Follow the instructions on the" - " screen.
Note: You will find the PUK in the letter you received during" - " the application for the ID card in the \"Unblocking key PUK\" section. Further" - " information is available on the site http://www.personalausweisportal.de.
" - "Do you want to unblock the PIN now?"); - messageBox.setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + title); - messageBox.setWindowModality(Qt::WindowModal); - messageBox.setText(QStringLiteral("

%1

%2

").arg(title, text)); - messageBox.setIcon(QMessageBox::Warning); - messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel); - messageBox.button(QMessageBox::Yes)->setFocus(); - - return messageBox.exec() == QMessageBox::Yes; -} diff --git a/src/widget/generic/GuiUtils.h b/src/widget/generic/GuiUtils.h deleted file mode 100644 index 710b2a0..0000000 --- a/src/widget/generic/GuiUtils.h +++ /dev/null @@ -1,29 +0,0 @@ -/*! - * \brief Gui utility functions. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include - -#include "CardReturnCode.h" - -class QFrame; - - -namespace governikus -{ - -class GuiUtils - : private QObject -{ - Q_OBJECT - - public: - static void showPinCanPukErrorDialog(CardReturnCode pReturnCode, int pRetryCounter, bool pCanAllowedMode, QWidget* pParent); - static bool showWrongPinBlockedDialog(QWidget* pParent); -}; - -} /* namespace governikus */ diff --git a/src/widget/generic/HelpAction.cpp b/src/widget/generic/HelpAction.cpp deleted file mode 100644 index a69aabc..0000000 --- a/src/widget/generic/HelpAction.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "HelpAction.h" - -#include "LanguageLoader.h" -#include "SingletonHelper.h" -#include "VersionNumber.h" - -#include -#include -#include -#include -#include -#include -#include - - -using namespace governikus; - -defineSingleton(HelpAction) - -Q_DECLARE_LOGGING_CATEGORY(gui) - -//Mapping object name to help file, \see AppQtMainWidget::onContentActionClicked() -const QMap HelpAction::mHelpMapping = { - {QStringLiteral("setupAssistant"), QStringLiteral("wizard-info.html")}, - {QStringLiteral("setupAssistantSetupCompleted"), QString()}, - {QStringLiteral("ausweisenPage"), QStringLiteral("identify.html")}, - {QStringLiteral("providerPage"), QStringLiteral("provider.html")}, - {QStringLiteral("historyPage"), QStringLiteral("history.html")}, - {QStringLiteral("generalTab"), QStringLiteral("settings-general.html")}, - {QStringLiteral("pinTab"), QStringLiteral("settings-pin-management.html")}, - {QStringLiteral("readerDeviceTab"), QStringLiteral("settings-reader-detection.html")}, - {QStringLiteral("stepChooseCardGui"), QStringLiteral("settings-reader-detection.html")} -}; - - -HelpAction& HelpAction::getInstance() -{ - return *Instance; -} - - -QString HelpAction::getHelpPath(QLocale::Language pLang) const -{ - const QString langDir = QCoreApplication::applicationDirPath() % QStringLiteral("/help/") % QLocale(pLang).bcp47Name().mid(0, 2) % QLatin1Char('/'); - - if (QDir(langDir).exists()) - { - return langDir; - } - - return QString(); -} - - -QLocale::Language HelpAction::getExistingHelpLanguage() const -{ - QLocale::Language lang = LanguageLoader::getInstance().getUsedLocale().language(); - if (!getHelpPath(lang).isNull()) - { - return lang; - } - - lang = LanguageLoader::getInstance().getFallbackLanguage(); - if (!getHelpPath(lang).isNull()) - { - return lang; - } - - return QLocale::AnyLanguage; -} - - -QString HelpAction::getContextMapping(const QString& pObjectName) const -{ - if (mHelpMapping.contains(pObjectName)) - { - return mHelpMapping.value(pObjectName); - } - else - { - qCWarning(gui) << "Cannot find help mapping:" << pObjectName; - } - - return QStringLiteral("index.html"); -} - - -QString HelpAction::getHelpUrl(const QString& pObjectName) const -{ - QLocale::Language lang = getExistingHelpLanguage(); - if (lang == QLocale::AnyLanguage) - { - return getOnlineUrl(); - } - - return QUrl::fromLocalFile(getHelpPath(lang)).toString() + getContextMapping(pObjectName); -} - - -QUrl HelpAction::getHelpUrlWrapper(const QString& pObjectName) const -{ - auto url = getHelpUrl(pObjectName); - if (!url.contains(QLatin1Char('#'))) - { - return url; - } - - QFile file(QDir::tempPath() + QStringLiteral("/AusweisApp2_help.html")); - if (file.open(QIODevice::WriteOnly)) - { - QTextStream stream(&file); - stream << QStringLiteral("").arg(url) << endl; - } - - return QUrl::fromLocalFile(file.fileName()); -} - - -QString HelpAction::getOnlineUrl(const QString& pObjectName) -{ -#ifdef Q_OS_MACOS - const QLatin1String osPath("macOS"); -#else - const QLatin1String osPath("Windows"); -#endif - - const auto& appVersion = VersionNumber::getApplicationVersion().getVersionNumber(); - const QString ver = QString::number(appVersion.majorVersion()) % QLatin1Char('.') % QString::number(appVersion.minorVersion()); - const QString locale = QLocale(LanguageLoader::getInstance().getUsedLocale().language()).bcp47Name().mid(0, 2); - const QString mapping = getInstance().getContextMapping(pObjectName); - return QStringLiteral("https://www.ausweisapp.bund.de/ausweisapp2/handbuch/") % ver % QLatin1Char('/') % locale % QLatin1Char('/') % osPath % QLatin1Char('/') % mapping; -} - - -void HelpAction::openContextHelp(const QString& pObjectName) -{ - //const auto& url = getInstance().getHelpUrlWrapper(pObjectName); - const auto& url = QUrl(getOnlineUrl(pObjectName)); - qCDebug(gui) << "Open online help:" << pObjectName << '|' << url; - QDesktopServices::openUrl(url); -} diff --git a/src/widget/generic/HelpAction.h b/src/widget/generic/HelpAction.h deleted file mode 100644 index e4edcd4..0000000 --- a/src/widget/generic/HelpAction.h +++ /dev/null @@ -1,44 +0,0 @@ -/*! - * \brief Helper class for mapping object name from f1 widget to help file. - * \see AppQtMainWidget::onContentActionClicked() - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include -#include - -class test_HelpAction; - -namespace governikus -{ - -class HelpAction -{ - private: - friend class ::test_HelpAction; - - static const QMap mHelpMapping; - - Q_DISABLE_COPY(HelpAction) - - QLocale::Language getExistingHelpLanguage() const; - QString getContextMapping(const QString& pObjectName) const; - QString getHelpPath(QLocale::Language pLang) const; - QString getHelpUrl(const QString& pObjectName) const; - QUrl getHelpUrlWrapper(const QString& pObjectName) const; - - protected: - static HelpAction& getInstance(); - HelpAction() = default; - ~HelpAction() = default; - - public: - static QString getOnlineUrl(const QString& pObjectName = QString()); - static void openContextHelp(const QString& pObjectName = QStringLiteral("applicationPage")); -}; - -} /* namespace governikus */ diff --git a/src/widget/generic/ListCheckItemWidget.cpp b/src/widget/generic/ListCheckItemWidget.cpp deleted file mode 100644 index 76c3a33..0000000 --- a/src/widget/generic/ListCheckItemWidget.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include -#include -#include -#include -#include -#include - -#include "generic/ListCheckItemWidget.h" -#include "ui_ListCheckItemWidget.h" - -using namespace governikus; - - -ListCheckItemWidget::ListCheckItemWidget(QWidget* pParent) - : QWidget(pParent) - , mUi(new Ui::ListCheckItemWidget()) -{ - mUi->setupUi(this); - mUi->listIcon->hide(); - - connect(mUi->listCheckBox, &QCheckBox::stateChanged, this, &ListCheckItemWidget::onCheckBoxChanged); - - installEventFilter(this); -} - - -ListCheckItemWidget::ListCheckItemWidget(QWidget* pParent, const QPixmap& pPixmap) - : QWidget(pParent) - , mUi(new Ui::ListCheckItemWidget()) -{ - mUi->setupUi(this); - mUi->listIcon->setScaledContents(true); - mUi->listIcon->setPixmap(pPixmap); - - connect(mUi->listCheckBox, &QCheckBox::stateChanged, this, &ListCheckItemWidget::onCheckBoxChanged); - - installEventFilter(this); -} - - -ListCheckItemWidget::~ListCheckItemWidget() -{ -} - - -void ListCheckItemWidget::itemWidgetReleased() -{ - mUi->listItemLayout->setObjectName(QStringLiteral("listItemLayout")); - //qApp->setStyleSheet(qApp->styleSheet()); -} - - -void ListCheckItemWidget::onCheckBoxChanged(int /*pChanged*/) -{ - Q_EMIT listItemWidgetChecked(this); -} - - -void ListCheckItemWidget::itemWidgetPressed() -{ - mUi->listItemLayout->setObjectName(QStringLiteral("listItemLayout_pressed")); - //qApp->setStyleSheet(qApp->styleSheet()); - - Q_EMIT listItemWidgetChecked(this); - -} - - -bool ListCheckItemWidget::eventFilter(QObject* /*pWatched*/, QEvent* pEvent) -{ - switch (pEvent->type()) - { - case QEvent::MouseButtonPress: - itemWidgetPressed(); - return false; - - case QEvent::MouseButtonRelease: - itemWidgetReleased(); - return false; - - default: - return false; - } -} - - -void ListCheckItemWidget::setHeading(const QString& pHeading) -{ - mUi->heading->setText(pHeading); -} - - -void ListCheckItemWidget::setSubHeading(const QString& pSubHeading) -{ - if (!pSubHeading.isNull() || !pSubHeading.isEmpty()) - { - mUi->subHeading->setText(pSubHeading); - } - else - { - mUi->headingFrameLayout->layout()->removeWidget(mUi->subHeading); - delete mUi->subHeading; - } -} - - -void ListCheckItemWidget::paintEvent(QPaintEvent*) -{ - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} - - -QCheckBox* ListCheckItemWidget::getListItemCheckBox() -{ - return mUi->listCheckBox; -} - - -void ListCheckItemWidget::changeEvent(QEvent* pEvent) -{ - if (pEvent->type() == QEvent::LanguageChange) - { - mUi->retranslateUi(this); - } - - QWidget::changeEvent(pEvent); -} diff --git a/src/widget/generic/ListCheckItemWidget.h b/src/widget/generic/ListCheckItemWidget.h deleted file mode 100644 index edd7b29..0000000 --- a/src/widget/generic/ListCheckItemWidget.h +++ /dev/null @@ -1,55 +0,0 @@ -/*! - * \brief List item widget for list actions. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include -#include - -namespace Ui -{ -class ListCheckItemWidget; -} - -namespace governikus -{ - -class ListCheckItemWidget - : public QWidget -{ - Q_OBJECT - - public: - ListCheckItemWidget(QWidget* pParent, const QPixmap& pPixmap); - ListCheckItemWidget(QWidget* pParent); - virtual ~ListCheckItemWidget() override; - - void setHeading(const QString& pHeading); - void setSubHeading(const QString& pSubHeading); - - QCheckBox* getListItemCheckBox(); - - protected: - void changeEvent(QEvent* pEvent) override; - - Q_SIGNALS: - void listItemWidgetChecked(ListCheckItemWidget* pListCheckItemWidget); - - private Q_SLOTS: - void onCheckBoxChanged(int pChanged); - - private: - QScopedPointer mUi; - - virtual bool eventFilter(QObject* pWatched, QEvent* pEvent) override; - virtual void paintEvent(QPaintEvent*) override; - - void itemWidgetReleased(); - void itemWidgetPressed(); -}; - -} /* namespace governikus */ diff --git a/src/widget/generic/ListItem.h b/src/widget/generic/ListItem.h deleted file mode 100644 index a0cf0d9..0000000 --- a/src/widget/generic/ListItem.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include - - -namespace governikus -{ - -class ListItem - : public QWidget -{ - Q_OBJECT - - public: - ListItem(QWidget* pParent = nullptr, Qt::WindowFlags pWindowFlags = 0); - virtual ~ListItem() override; - - void paintEvent(QPaintEvent*) override; -}; - -} diff --git a/src/widget/generic/ListItemIconLeft.h b/src/widget/generic/ListItemIconLeft.h deleted file mode 100644 index 5a2610e..0000000 --- a/src/widget/generic/ListItemIconLeft.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include - -namespace governikus -{ - -class ListItemIconLeft - : public QLabel -{ - Q_OBJECT - - public: - ListItemIconLeft(QWidget* pParent = nullptr); - ListItemIconLeft(const QString& pText, QWidget* pParent = nullptr); - virtual ~ListItemIconLeft(); -}; - -} /* namespace governikus */ diff --git a/src/widget/generic/ListItemIconRight.h b/src/widget/generic/ListItemIconRight.h deleted file mode 100644 index 572b855..0000000 --- a/src/widget/generic/ListItemIconRight.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include - -namespace governikus -{ - -class ListItemIconRight - : public QLabel -{ - Q_OBJECT - - public: - ListItemIconRight(QWidget* pParent = nullptr); - ListItemIconRight(const QString& pText, QWidget* pParent = nullptr); - virtual ~ListItemIconRight(); -}; - -} /* namespace governikus */ diff --git a/src/widget/generic/ListItemSubTitle.h b/src/widget/generic/ListItemSubTitle.h deleted file mode 100644 index 7665612..0000000 --- a/src/widget/generic/ListItemSubTitle.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include - -namespace governikus -{ - -class ListItemSubTitle - : public QLabel -{ - Q_OBJECT - - public: - ListItemSubTitle(QWidget* pParent = nullptr); - ListItemSubTitle(const QString& pText, QWidget* pParent = nullptr); - virtual ~ListItemSubTitle(); -}; - -} /* namespace governikus */ diff --git a/src/widget/generic/ListItemTitle.h b/src/widget/generic/ListItemTitle.h deleted file mode 100644 index 1265916..0000000 --- a/src/widget/generic/ListItemTitle.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include - -namespace governikus -{ - -class ListItemTitle - : public QLabel -{ - Q_OBJECT - - public: - ListItemTitle(QWidget* pParent = nullptr); - ListItemTitle(const QString& pText, QWidget* pParent = nullptr); - virtual ~ListItemTitle(); -}; - -} /* namespace governikus */ diff --git a/src/widget/generic/PasswordEdit.cpp b/src/widget/generic/PasswordEdit.cpp deleted file mode 100644 index e830453..0000000 --- a/src/widget/generic/PasswordEdit.cpp +++ /dev/null @@ -1,187 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - - -#include "PasswordEdit.h" - -#include "ScopeGuard.h" - -#include -#include - - -Q_DECLARE_LOGGING_CATEGORY(gui) - -using namespace governikus; - - -namespace governikus -{ -class RegExValidator - : public QRegularExpressionValidator -{ - Q_OBJECT - - private: - const QString mInvalidValueToolTip; - - public: - RegExValidator(const QRegularExpression& pExpression, const QString& pInvalidValueToolTip, QWidget* pParent) - : QRegularExpressionValidator(pExpression, pParent) - , mInvalidValueToolTip(pInvalidValueToolTip) - { - } - - - virtual QValidator::State validate(QString& pInput, int& pPos) const override - { - QValidator::State state = QRegularExpressionValidator::validate(pInput, pPos); - - if (state == State::Invalid && !mInvalidValueToolTip.isNull()) - { - QWidget* parentWidget = static_cast(parent()); - QToolTip::showText(parentWidget->mapToGlobal(QPoint(0, 0)), mInvalidValueToolTip, parentWidget, QRect(), 3000); - } - - return state; - } - - -}; - -} - - -PasswordEdit::PasswordEdit(QWidget* pParent) - : QWidget(pParent) - , mUi(new Ui::PasswordEdit()) -{ - mUi->setupUi(this); - mUi->lineEdit->installEventFilter(this); - - connect(mUi->lineEdit, &QLineEdit::textEdited, this, &PasswordEdit::textEdited); - connect(mUi->lineEdit, &QLineEdit::selectionChanged, this, &PasswordEdit::selectionChanged); -} - - -int PasswordEdit::determindeWidth(int pNumChars) -{ - QLineEdit* const lineEdit = mUi->lineEdit; - - const QString currentText = lineEdit->text(); - const ScopeGuard resetText([lineEdit, currentText] { - lineEdit->setText(currentText); - }); - - // get the display text for a password of length pWidth - lineEdit->setText(QString(pNumChars, QLatin1Char('6'))); - const int displayTextWidth = lineEdit->fontMetrics().width(lineEdit->displayText()); - - // in QLineEdit::sizeHint() the width is calculated as - // 17th times the size of 'x' plus some magic margins. - // So we calculate this margin by subtraction to set the content size correctly. - const int widthHint = lineEdit->sizeHint().width(); - const int margin = widthHint - 17 * lineEdit->fontMetrics().width(QLatin1Char('x')); - return margin + displayTextWidth; -} - - -bool PasswordEdit::eventFilter(QObject* pObj, QEvent* pEvent) -{ - if (pEvent->type() == QEvent::KeyPress) - { - const QKeyEvent* const keyEvent = static_cast(pEvent); - if (keyEvent->key() == Qt::Key_Backspace && text().isEmpty()) - { - Q_EMIT fireBackspacePressedAndEmpty(); - } - } - return QObject::eventFilter(pObj, pEvent); -} - - -void PasswordEdit::setMaxLength(int pLength, bool pShrink) -{ - if (pShrink) - { - mUi->lineEdit->setFixedWidth(determindeWidth(pLength)); - } - mUi->lineEdit->setMaxLength(pLength); -} - - -void PasswordEdit::configureValidation(const QRegularExpression& pExpression, const QString& pInvalidValueToolTip) -{ - mUi->lineEdit->setValidator(new RegExValidator(pExpression, pInvalidValueToolTip, this)); -} - - -void PasswordEdit::removeLastCharacter() -{ - const QString current = mUi->lineEdit->text(); - if (current.isEmpty()) - { - return; - } - mUi->lineEdit->setText(current.left(current.length() - 1)); -} - - -QString PasswordEdit::text() const -{ - return mUi->lineEdit->text(); -} - - -void PasswordEdit::setDigitFieldInvalid(bool pMakeInvalid, const QString& pInvalidMessage) -{ - if (pMakeInvalid) - { - mUi->lineEdit->setStyleSheet(QStringLiteral("background-color: red;")); - QToolTip::showText(mapToGlobal(QPoint(0, 32)), pInvalidMessage, mUi->lineEdit, QRect(), 3000); - } - else - { - mUi->lineEdit->setStyleSheet(QString()); - } -} - - -void PasswordEdit::clear() -{ - mUi->lineEdit->clear(); -} - - -void PasswordEdit::setText(const QString& pText) -{ - mUi->lineEdit->setText(pText); -} - - -void PasswordEdit::setCursorPosition(int pPosition) -{ - mUi->lineEdit->setCursorPosition(pPosition); -} - - -void PasswordEdit::setAccessibleName(const QString& pName) -{ - mUi->lineEdit->setAccessibleName(pName); -} - - -void PasswordEdit::setFocus() -{ - mUi->lineEdit->setFocus(); -} - - -void PasswordEdit::setAlignment(Qt::Alignment pAlignment) -{ - mUi->lineEdit->setAlignment(pAlignment); -} - - -#include "PasswordEdit.moc" diff --git a/src/widget/generic/PasswordEdit.h b/src/widget/generic/PasswordEdit.h deleted file mode 100644 index d0fd03b..0000000 --- a/src/widget/generic/PasswordEdit.h +++ /dev/null @@ -1,56 +0,0 @@ -/*! - * \brief Widget for entering a password that uses the password echo mode. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "ui_PasswordEdit.h" - - -namespace governikus -{ - -class PasswordEdit - : public QWidget -{ - Q_OBJECT - - private: - QScopedPointer mUi; - - int determindeWidth(int pNumChars); - - protected: - virtual bool eventFilter(QObject* pObj, QEvent* pEvent) override; - - public: - PasswordEdit(QWidget* pParent = nullptr); - - void setMaxLength(int pLength, bool pShrink = true); - void configureValidation(const QRegularExpression& pExpression, const QString& pInvalidValueToolTip); - void removeLastCharacter(); - QString text() const; - void setDigitFieldInvalid(bool pMakeInvalid, const QString& pInvalidMessage); - void clear(); - void setText(const QString& pText); - void setCursorPosition(int pPosition); - void setAccessibleName(const QString& pName); - void setFocus(); - void setAlignment(Qt::Alignment pAlignment); - - Q_SIGNALS: - void textEdited(const QString& pText); - void selectionChanged(); - void fireBackspacePressedAndEmpty(); -}; - -} /* namespace governikus */ diff --git a/src/widget/generic/TabButtonGroup.h b/src/widget/generic/TabButtonGroup.h deleted file mode 100644 index 234b58d..0000000 --- a/src/widget/generic/TabButtonGroup.h +++ /dev/null @@ -1,110 +0,0 @@ -/*! - * \brief Contains the accessibility friendly TabButtonGroup and TabButton classes. - * - * The other classes defined in this header are implementation private. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include - -namespace governikus -{ - -class ExclusiveButtonGroup; - - -/*! - * \brief A push button that is presented to accessibility clients as a page tab. - */ -class TabButton - : public QToolButton -{ - Q_OBJECT - - public: - TabButton(QWidget* pParent = nullptr); - virtual ~TabButton() override; - - protected: - virtual void focusInEvent(QFocusEvent* pEvent) override; - virtual void nextCheckState() override; -}; - -/*! - * \brief A widget that is presented to accessibility clients as a page tab list, - * but uses TabButtons as tabs. - * - * The class is a regular widget. Buttons (that must be checkable) added via - * addButton() are added to an exclusive button group. Only one button can be - * checked at a time. The focus handling and cursor key navigation is overridden - * so that it works like in the tab row of a regular QTabWidget. - */ -class TabButtonGroup - : public QWidget -{ - Q_OBJECT - - public: - TabButtonGroup(QWidget* pParent = nullptr); - virtual ~TabButtonGroup() override; - - void addButton(QAbstractButton* pButton); - - void setWorkflowActive(bool pWorkflowActiv); - - virtual bool eventFilter(QObject* pWatched, QEvent* pEvent) override; - - private Q_SLOTS: - void onButtonToggled(QAbstractButton* pButton, bool pChecked); - - private: - QAbstractButton* getNextPrevFocussableButton(QAbstractButton* pCurrentButton, bool pNext, bool pCycle) const; - void updateFocusPolicies(); - - private: - ExclusiveButtonGroup* mButtonGroup; - bool mWorkflowActive; - void paintEvent(QPaintEvent*) override; - - Q_SIGNALS: - void buttonToggled(QAbstractButton* pButton, bool pChecked); -}; - -/*! - * \brief Implementation private class providing the accessibility functionality - * for TabButton. - */ -class AccessibleTabButton - : public QAccessibleWidget -{ - public: - AccessibleTabButton(QWidget* pWidget); - - TabButton* getTabButton() const; - - virtual QString text(QAccessible::Text pText) const override; - - virtual QStringList actionNames() const override; - virtual void doAction(const QString& pActionName) override; - -}; - -/*! - * \brief Implementation private class providing the accessibility functionality - * for TabButtonGroup. - */ -class AccessibleTabButtonGroup - : public QAccessibleWidget -{ - public: - AccessibleTabButtonGroup(QWidget* pWidget); - virtual ~AccessibleTabButtonGroup(); - - TabButtonGroup* getTabButtonGroup() const; -}; - -} /* namespace governikus */ diff --git a/src/widget/step/AuthenticateStepsWidget.cpp b/src/widget/step/AuthenticateStepsWidget.cpp deleted file mode 100644 index c125c5d..0000000 --- a/src/widget/step/AuthenticateStepsWidget.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "AuthenticateStepsWidget.h" -#include "ui_AuthenticateStepsWidget.h" - -#include -#include - -#include "AppStartPage.h" -#include "generic/BusyOverlayContainer.h" - -using namespace governikus; - -AuthenticateStepsWidget::AuthenticateStepsWidget(QWidget* pParent) - : QStackedWidget(pParent) - , mUi(new Ui::AuthenticateStepsWidget()) - , mProcessingPage(new BusyOverlayContainer(new AppStartPage(), false)) -{ - mUi->setupUi(this); - - addWidget(mProcessingPage); -} - - -AuthenticateStepsWidget::~AuthenticateStepsWidget() -{ -} - - -StepAuthenticationEac1Widget* AuthenticateStepsWidget::getEac1Page() const -{ - return mUi->authenticationEac1Page; -} - - -SelfInfoWidget* AuthenticateStepsWidget::getSelfInfoPage() const -{ - return mUi->selfInfoPage; -} - - -void AuthenticateStepsWidget::paintEvent(QPaintEvent*) -{ - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} - - -void AuthenticateStepsWidget::changeEvent(QEvent* pEvent) -{ - if (pEvent->type() == QEvent::LanguageChange) - { - mUi->retranslateUi(this); - } - QWidget::changeEvent(pEvent); -} diff --git a/src/widget/step/AuthenticateStepsWidget.h b/src/widget/step/AuthenticateStepsWidget.h deleted file mode 100644 index 918d49d..0000000 --- a/src/widget/step/AuthenticateStepsWidget.h +++ /dev/null @@ -1,54 +0,0 @@ -/*! - * \brief A stacked widget containing the widgets for the authentication steps. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include - -namespace Ui -{ -class AuthenticateStepsWidget; -} - -class QAbstractButton; - -namespace governikus -{ - -class BusyOverlayContainer; -class SelfInfoWidget; -class StepAuthenticationEac1Widget; - -class AuthenticateStepsWidget - : public QStackedWidget -{ - Q_OBJECT - - public: - AuthenticateStepsWidget(QWidget* pParent = nullptr); - virtual ~AuthenticateStepsWidget() override; - - BusyOverlayContainer* getProcessingPage() const - { - return mProcessingPage; - } - - - StepAuthenticationEac1Widget* getEac1Page() const; - - SelfInfoWidget* getSelfInfoPage() const; - - protected: - void paintEvent(QPaintEvent*) override; - void changeEvent(QEvent* pEvent) override; - - private: - QScopedPointer mUi; - BusyOverlayContainer* mProcessingPage; -}; - -} /* namespace governikus */ diff --git a/src/widget/step/SelfInfoWidget.cpp b/src/widget/step/SelfInfoWidget.cpp deleted file mode 100644 index 95b073d..0000000 --- a/src/widget/step/SelfInfoWidget.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "SelfInfoWidget.h" - -#include "ui_SelfInfoWidget.h" - -#include "generic/ListItem.h" -#include "generic/ListItemSubTitle.h" -#include "generic/ListItemTitle.h" -#include "PdfExporter.h" - -#include -#include -#include -#include -#include - -using namespace governikus; - - -Q_DECLARE_LOGGING_CATEGORY(gui) - - -SelfInfoWidget::SelfInfoWidget(QWidget* pParent) - : QWidget(pParent) - , mUi(new Ui::SelfInfoWidget()) - , mSelfAuthenticationData() -{ - mUi->setupUi(this); - layout()->setMargin(20); -} - - -SelfInfoWidget::~SelfInfoWidget() -{ -} - - -void SelfInfoWidget::setInfo(const SelfAuthenticationData& pData) -{ - mSelfAuthenticationData = pData; - fillLayout(); -} - - -void SelfInfoWidget::fillLayout() -{ - //delete old data from ui - QLayout* uiLayout = mUi->dataLayout; - cleanLayout(uiLayout); - cleanLayout(mUi->saveLayout); - - const auto& orderedSelfData = mSelfAuthenticationData.getOrderedSelfData(); - for (const auto& entry : orderedSelfData) - { - QLabel* const tmpLabel = new QLabel(entry.first); - if (!entry.first.isEmpty()) - { - tmpLabel->setFocusPolicy(Qt::TabFocus); - } - tmpLabel->setAlignment(Qt::AlignLeading | Qt::AlignLeft | Qt::AlignTop); - - QLabel* const tmpField = new QLabel(entry.second); - tmpField->setFocusPolicy(Qt::TabFocus); - tmpField->setWordWrap(true); - - mUi->dataLayout->insertRow(-1, tmpLabel, tmpField); - } - - QPushButton* exportButton = new QPushButton(tr("Save as PDF...")); - exportButton->setAccessibleName(tr("save id card data as pdf")); - - connect(exportButton, &QPushButton::clicked, this, &SelfInfoWidget::onPrintButtonClicked); - - mUi->saveLayout->addWidget(exportButton); - mUi->saveLayout->addItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum)); -} - - -void SelfInfoWidget::cleanLayout(QLayout* pLayout) -{ - while (QLayoutItem* child = pLayout->itemAt(0)) - { - if (QWidget* childWidget = child->widget()) - { - pLayout->removeWidget(childWidget); - delete childWidget; - } - else - { - pLayout->removeItem(child); - delete child; - } - } -} - - -void SelfInfoWidget::paintEvent(QPaintEvent*) -{ - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} - - -void SelfInfoWidget::onPrintButtonClicked() -{ - const auto& selfData = mSelfAuthenticationData.getOrderedSelfData(); - if (!selfData.isEmpty()) - { - const auto& dataTime = mSelfAuthenticationData.getDateTime(); - QString filename = tr("AusweisApp2.Information.%1.pdf").arg(dataTime.toString(QStringLiteral("yyyy-MM-dd"))); - filename = QFileDialog::getSaveFileName(this, - QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("Save"), - QDir::homePath() + QLatin1Char('/') + filename, -#ifndef Q_OS_MACOS - tr("PDF Documents") + QStringLiteral(" (*.pdf)")); -#else - tr("PDF Documents") + QStringLiteral(" (*.pdf)"), nullptr, QFileDialog::DontUseNativeDialog); -#endif - - PdfExporter exporter(filename); - exporter.exportSelfInfo(dataTime, selfData); - } -} - - -void SelfInfoWidget::changeEvent(QEvent* pEvent) -{ - if (pEvent->type() == QEvent::LanguageChange) - { - mUi->retranslateUi(this); - fillLayout(); - } - QWidget::changeEvent(pEvent); -} diff --git a/src/widget/step/SelfInfoWidget.h b/src/widget/step/SelfInfoWidget.h deleted file mode 100644 index 9f416dd..0000000 --- a/src/widget/step/SelfInfoWidget.h +++ /dev/null @@ -1,49 +0,0 @@ -/*! - * \brief A widget displaying the card data retrieved in the self info workflow. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "SelfAuthenticationData.h" - -#include -#include -#include - -namespace Ui -{ -class SelfInfoWidget; -} - -namespace governikus -{ -class SelfInfoWidget - : public QWidget -{ - Q_OBJECT - - public: - SelfInfoWidget(QWidget* pParent = nullptr); - virtual ~SelfInfoWidget() override; - - void setInfo(const SelfAuthenticationData& pData); - - protected: - void paintEvent(QPaintEvent*) override; - void changeEvent(QEvent* pEvent) override; - - private: - QScopedPointer mUi; - SelfAuthenticationData mSelfAuthenticationData; - - void add(const QString& pKey, const QString& pValue); - void fillLayout(); - void cleanLayout(QLayout* pLayout); - - private Q_SLOTS: - void onPrintButtonClicked(); -}; - -} /* namespace governikus */ diff --git a/src/widget/step/StepAdviseUserToRemoveCardGui.cpp b/src/widget/step/StepAdviseUserToRemoveCardGui.cpp deleted file mode 100644 index 47ce3f9..0000000 --- a/src/widget/step/StepAdviseUserToRemoveCardGui.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "StepAdviseUserToRemoveCardGui.h" - -#include "PinSettingsWidget.h" -#include "ReaderManager.h" - -#include -#include -#include - -using namespace governikus; - - -void StepAdviseUserToRemoveCardGui::onReaderManagerSignal() -{ - if (!ReaderManager::getInstance().getReaderInfo(mContext->getReaderName()).hasCard()) - { - qDebug() << "No more ID cards found -> auto closing card reminder dialog"; - - disconnect(&ReaderManager::getInstance(), &ReaderManager::fireReaderAdded, this, &StepAdviseUserToRemoveCardGui::onReaderManagerSignal); - disconnect(&ReaderManager::getInstance(), &ReaderManager::fireReaderRemoved, this, &StepAdviseUserToRemoveCardGui::onReaderManagerSignal); - disconnect(&ReaderManager::getInstance(), &ReaderManager::fireCardInserted, this, &StepAdviseUserToRemoveCardGui::onReaderManagerSignal); - disconnect(&ReaderManager::getInstance(), &ReaderManager::fireCardRemoved, this, &StepAdviseUserToRemoveCardGui::onReaderManagerSignal); - disconnect(&mMessageTimeoutTimer, &QTimer::timeout, this, &StepAdviseUserToRemoveCardGui::onReaderManagerSignal); - - mMessageBox->reject(); - } -} - - -StepAdviseUserToRemoveCardGui::StepAdviseUserToRemoveCardGui(QSharedPointer pContext, QWidget* pMainWidget) - : StepGui(pContext) - , mContext(pContext) - , mMainWidget(pMainWidget) - , mMessageBox(nullptr) - , mMessageTimeoutTimer() -{ -} - - -StepAdviseUserToRemoveCardGui::~StepAdviseUserToRemoveCardGui() -{ -} - - -void StepAdviseUserToRemoveCardGui::activate() -{ - setCancelButtonState(ButtonState::HIDDEN); - - const QString selectedReaderName = mContext->getReaderName(); - if (selectedReaderName.isEmpty()) - { - return; - } - - ReaderInfo selectedReader = ReaderManager::getInstance().getReaderInfo(mContext->getReaderName()); - if (selectedReader.isConnected()) - { - if (!selectedReader.hasCard()) - { - return; - } - - connect(&ReaderManager::getInstance(), &ReaderManager::fireReaderAdded, this, &StepAdviseUserToRemoveCardGui::onReaderManagerSignal); - connect(&ReaderManager::getInstance(), &ReaderManager::fireReaderRemoved, this, &StepAdviseUserToRemoveCardGui::onReaderManagerSignal); - connect(&ReaderManager::getInstance(), &ReaderManager::fireCardInserted, this, &StepAdviseUserToRemoveCardGui::onReaderManagerSignal); - connect(&ReaderManager::getInstance(), &ReaderManager::fireCardRemoved, this, &StepAdviseUserToRemoveCardGui::onReaderManagerSignal); - } - else - { - mMessageTimeoutTimer.setInterval(3000); - mMessageTimeoutTimer.setSingleShot(true); - connect(&mMessageTimeoutTimer, &QTimer::timeout, this, &StepAdviseUserToRemoveCardGui::onReaderManagerSignal); - mMessageTimeoutTimer.start(); - } - - if (mMessageBox == nullptr) - { - mMessageBox = new QMessageBox(mMainWidget); - mMessageBox->setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("Information")); - mMessageBox->setWindowModality(Qt::WindowModal); - mMessageBox->setWindowFlags(mMessageBox->windowFlags() & ~Qt::WindowContextHelpButtonHint); - mMessageBox->setText(tr("You may now remove your ID card from the card reader.")); - mMessageBox->setIconPixmap(QIcon(QStringLiteral(":/images/npa.svg")).pixmap(32, 32)); - mMessageBox->setStandardButtons(QMessageBox::Ok); - mMessageBox->button(QMessageBox::Ok)->setFocus(); - } - - mMessageBox->exec(); -} diff --git a/src/widget/step/StepAdviseUserToRemoveCardGui.h b/src/widget/step/StepAdviseUserToRemoveCardGui.h deleted file mode 100644 index 521f467..0000000 --- a/src/widget/step/StepAdviseUserToRemoveCardGui.h +++ /dev/null @@ -1,40 +0,0 @@ -/*! - * \brief Qt UI for the advise user to remove card step. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "context/AuthContext.h" -#include "StepGui.h" - -#include -#include -#include - -namespace governikus -{ - -class StepAdviseUserToRemoveCardGui - : public StepGui -{ - Q_OBJECT - - public Q_SLOTS: - void onReaderManagerSignal(); - - public: - StepAdviseUserToRemoveCardGui(QSharedPointer pContext, QWidget* const pMainWidget); - virtual ~StepAdviseUserToRemoveCardGui() override; - - virtual void activate() override; - - private: - QSharedPointer mContext; - QWidget* const mMainWidget; - QMessageBox* mMessageBox; - QTimer mMessageTimeoutTimer; -}; - -} /* namespace governikus */ diff --git a/src/widget/step/StepAuthenticationDoneGui.h b/src/widget/step/StepAuthenticationDoneGui.h deleted file mode 100644 index 8b16156..0000000 --- a/src/widget/step/StepAuthenticationDoneGui.h +++ /dev/null @@ -1,28 +0,0 @@ -/*! - * \brief Qt UI for the authentication done step. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "context/AuthContext.h" -#include "StepGui.h" - - -namespace governikus -{ - -class StepAuthenticationDoneGui - : public StepGui -{ - Q_OBJECT - - public: - StepAuthenticationDoneGui(QSharedPointer pContext); - virtual ~StepAuthenticationDoneGui() override; - - virtual void forwardStep() override; -}; - -} /* namespace governikus */ diff --git a/src/widget/step/StepAuthenticationEac1Gui.cpp b/src/widget/step/StepAuthenticationEac1Gui.cpp deleted file mode 100644 index 2911a21..0000000 --- a/src/widget/step/StepAuthenticationEac1Gui.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "StepAuthenticationEac1Gui.h" - -#include "generic/GuiUtils.h" -#include "step/AuthenticateStepsWidget.h" - -#include - -using namespace governikus; - -StepAuthenticationEac1Gui::StepAuthenticationEac1Gui(QSharedPointer pContext, - AuthenticateStepsWidget* pStepsWidget) - : StepGui(pContext) - , mContext(pContext) - , mStepsWidget(pStepsWidget) - , mWidget(nullptr) - , mState(StepAuthenticationEac1Widget::State::EDIT_CHAT) - , mPayAttentionToReaderMsgBox(new QMessageBox(pStepsWidget->window())) - , mActive(false) -{ - mPayAttentionToReaderMsgBox->setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("Information")); - mPayAttentionToReaderMsgBox->setText(tr("Please observe the display of your card reader.")); - mPayAttentionToReaderMsgBox->setIcon(QMessageBox::Information); - mPayAttentionToReaderMsgBox->setStandardButtons(QMessageBox::NoButton); -} - - -StepAuthenticationEac1Gui::~StepAuthenticationEac1Gui() -{ -} - - -void StepAuthenticationEac1Gui::activate() -{ - mActive = true; - - mWidget = mStepsWidget->getEac1Page(); - - connect(mWidget, &StepAuthenticationEac1Widget::setForwardButtonState, getStepGuiDelegate(), &StepGuiDelegate::setForwardButtonState); - connect(mWidget, &StepAuthenticationEac1Widget::setCancelButtonState, getStepGuiDelegate(), &StepGuiDelegate::setCancelButtonState); - - connect(mWidget, &StepAuthenticationEac1Widget::fireCanUpdated, this, &StepAuthenticationEac1Gui::onCanUpdated); - connect(mWidget, &StepAuthenticationEac1Widget::firePinUpdated, this, &StepAuthenticationEac1Gui::onPinUpdated); - - connect(this, &StepAuthenticationEac1Gui::fireUiFinished, this, &StepAuthenticationEac1Gui::onUiFinished); - - mWidget->setContext(mContext); - mWidget->setState(StepAuthenticationEac1Widget::State::INITIAL); - mStepsWidget->setCurrentWidget(mWidget); -} - - -void StepAuthenticationEac1Gui::deactivate() -{ - mWidget->setContext(QSharedPointer()); - - disconnect(mWidget, &StepAuthenticationEac1Widget::setForwardButtonState, getStepGuiDelegate(), &StepGuiDelegate::setForwardButtonState); - disconnect(mWidget, &StepAuthenticationEac1Widget::setCancelButtonState, getStepGuiDelegate(), &StepGuiDelegate::setCancelButtonState); - - disconnect(mWidget, &StepAuthenticationEac1Widget::fireCanUpdated, this, &StepAuthenticationEac1Gui::onCanUpdated); - disconnect(mWidget, &StepAuthenticationEac1Widget::firePinUpdated, this, &StepAuthenticationEac1Gui::onPinUpdated); - - disconnect(this, &StepAuthenticationEac1Gui::fireUiFinished, this, &StepAuthenticationEac1Gui::onUiFinished); - - mActive = false; -} - - -bool StepAuthenticationEac1Gui::isActive() const -{ - return mActive; -} - - -void StepAuthenticationEac1Gui::setState(StepAuthenticationEac1Widget::State pState) -{ - mState = pState; - - mWidget->setState(pState); - - if (pState == StepAuthenticationEac1Widget::State::FINISHED) - { - forwardStep(); - } -} - - -void StepAuthenticationEac1Gui::incorrectPinError() -{ - mWidget->updateButtonsAndPinWidget(); - - GuiUtils::showPinCanPukErrorDialog(mContext->getLastPaceResult(), mContext->getCardConnection()->getReaderInfo().getRetryCounter(), mContext->isCanAllowedMode(), mStepsWidget->window()); -} - - -void StepAuthenticationEac1Gui::forwardStep() -{ - if (mState == StepAuthenticationEac1Widget::State::FINISHED) - { - Q_EMIT fireUiFinished(); - return; - } - - mWidget->forwardStep(); - - Q_EMIT fireUiFinished(); -} - - -void StepAuthenticationEac1Gui::hidePayAttentionToReader() -{ - mPayAttentionToReaderMsgBox->reject(); -} - - -void StepAuthenticationEac1Gui::onShowPayAttentionToReader() -{ - mPayAttentionToReaderMsgBox->open(); -} - - -void StepAuthenticationEac1Gui::onPinUpdated(const QString& pPin) -{ - if (!mContext->isCanAllowedMode()) - { - mPin = pPin; - } - else - { - mCan = pPin; - } -} - - -void StepAuthenticationEac1Gui::onCanUpdated(const QString& pCan) -{ - mCan = pCan; -} - - -void StepAuthenticationEac1Gui::onUiFinished() -{ - mContext->setCan(mCan); - mContext->setPin(mPin); - mContext->setStateApproved(); -} diff --git a/src/widget/step/StepAuthenticationEac1Gui.h b/src/widget/step/StepAuthenticationEac1Gui.h deleted file mode 100644 index 235112b..0000000 --- a/src/widget/step/StepAuthenticationEac1Gui.h +++ /dev/null @@ -1,57 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "context/AuthContext.h" -#include "step/StepAuthenticationEac1Widget.h" -#include "StepGui.h" - -#include -#include - -namespace governikus -{ - -class AuthenticateStepsWidget; -class StepAuthenticationEac1Widget; - -class StepAuthenticationEac1Gui - : public StepGui -{ - Q_OBJECT - - private: - QSharedPointer mContext; - AuthenticateStepsWidget* mStepsWidget; - StepAuthenticationEac1Widget* mWidget; - StepAuthenticationEac1Widget::State mState; - QPointer mPayAttentionToReaderMsgBox; - QString mPin; - QString mCan; - bool mActive; - - public: - StepAuthenticationEac1Gui(QSharedPointer pContext, AuthenticateStepsWidget* pStepsWidget); - virtual ~StepAuthenticationEac1Gui() override; - - virtual void activate() override; - virtual void deactivate() override; - bool isActive() const; - - virtual void setState(StepAuthenticationEac1Widget::State pState); - virtual void incorrectPinError(); - - virtual void forwardStep() override; - - virtual void hidePayAttentionToReader(); - - public Q_SLOTS: - virtual void onShowPayAttentionToReader(); - void onPinUpdated(const QString& pPin); - void onCanUpdated(const QString& pCan); - void onUiFinished(); -}; - -} /* namespace governikus */ diff --git a/src/widget/step/StepAuthenticationEac1Widget.cpp b/src/widget/step/StepAuthenticationEac1Widget.cpp deleted file mode 100644 index 9345d1c..0000000 --- a/src/widget/step/StepAuthenticationEac1Widget.cpp +++ /dev/null @@ -1,645 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "StepAuthenticationEac1Widget.h" -#include "ui_StepAuthenticationEac1Widget.h" - -#include "AppSettings.h" -#include "CardConnection.h" -#include "DetailDialog.h" -#include "generic/PasswordEdit.h" -#include "RandomPinDialog.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef Q_OS_WIN -#include -#endif - -using namespace governikus; - -Q_DECLARE_LOGGING_CATEGORY(gui) - -StepAuthenticationEac1Widget::StepAuthenticationEac1Widget(QWidget* pParent) - : QWidget(pParent) - , mUi(new Ui::StepAuthenticationEac1Widget()) - , mContext() - , mCANField(nullptr) - , mPINField(nullptr) - , mState(State::INITIAL) - , mProgressBar(nullptr) - , mProgressBarLabel(nullptr) - , mCloseWindowWhenFinished() -#ifdef Q_OS_WIN - , mTaskbarButton(new QWinTaskbarButton(this)) -#endif -{ - mUi->setupUi(this); - setToolTip(); - mUi->listWidgetWest->setAttribute(Qt::WA_MacShowFocusRect, false); - mUi->listWidgetEast->setAttribute(Qt::WA_MacShowFocusRect, false); - - connect(mUi->detailsPushButton, &QPushButton::clicked, this, &StepAuthenticationEac1Widget::onDetailsButtonClicked); -} - - -StepAuthenticationEac1Widget::~StepAuthenticationEac1Widget() -{ -} - - -void StepAuthenticationEac1Widget::setContext(const QSharedPointer& pContext) -{ - mContext = pContext; - -#ifdef Q_OS_WIN - if (mContext) - { - connect(mContext.data(), &AuthContext::fireResultChanged, this, &StepAuthenticationEac1Widget::onResultChanged); - } -#endif -} - - -void StepAuthenticationEac1Widget::setState(State pState) -{ - if (pState != mState) - { - mState = pState; - updateWidget(); - } -} - - -void StepAuthenticationEac1Widget::forwardStep() -{ - Q_EMIT setForwardButtonState(ButtonState::DISABLED, tr("Identify now")); - - if (mState == State::EDIT_CHAT) - { - mUi->detailsPushButton->setEnabled(false); - mUi->listWidgetWest->setEnabled(false); - mUi->listWidgetEast->setEnabled(false); - } - else if (mState == State::ENTER_PIN) - { - if (mContext->getCardConnection() != nullptr && mContext->getCardConnection()->getReaderInfo().isBasicReader()) - { - mUi->pinGroupBox->setVisible(false); - - int childCount = mUi->pinWidgetLayout->count(); - for (int i = 0; i < childCount; ++i) - { - QLayoutItem* child = mUi->pinWidgetLayout->itemAt(i); - if (child->widget() != nullptr) - { - child->widget()->setEnabled(false); - } - } - } - else - { - Q_EMIT setCancelButtonState(ButtonState::DISABLED); - } - } - -} - - -void StepAuthenticationEac1Widget::updateButtonsAndPinWidget() -{ - Q_EMIT setCancelButtonState(ButtonState::ENABLED); - - if (mContext->isCanAllowedMode()) - { - mUi->pinGroupBox->setTitle(tr("Please enter the six-digit card access number (CAN) for identification.")); - } - else if (mContext->getCardConnection()->getReaderInfo().getRetryCounter() == 1) - { - mUi->pinGroupBox->setTitle(tr("Please enter your six-digit card access number (CAN) and your PIN for identification.")); - } - else - { - mUi->pinGroupBox->setTitle(tr("Please enter your six digit PIN for identification")); - } - - if (mContext->getCardConnection()->getReaderInfo().isBasicReader()) - { - clearPinWidgetLayout(); - createBasicReaderWidget(); - focusWidget(); - } - else - { - clearPinWidgetLayout(); - QLabel* label = new QLabel(tr("Please pay attention to the display of your card reader.")); - label->setFocusPolicy(Qt::TabFocus); - label->setObjectName(QStringLiteral("eac1PinInformationLabel")); - mUi->pinWidgetLayout->invalidate(); - mUi->pinWidgetLayout->addWidget(label); - Q_EMIT setCancelButtonState(ButtonState::DISABLED); - } - mUi->pinGroupBox->setVisible(true); - Q_EMIT setForwardButtonState(ButtonState::DISABLED, tr("Identify now")); -} - - -void StepAuthenticationEac1Widget::clearPinWidgetLayout() -{ - while (QLayoutItem* child = mUi->pinWidgetLayout->itemAt(0)) - { - if (QWidget* childWidget = child->widget()) - { - mUi->pinWidgetLayout->removeWidget(childWidget); - delete childWidget; - } - else - { - mUi->pinWidgetLayout->removeItem(child); - delete child; - } - } - - mProgressBar = nullptr; - mProgressBarLabel = nullptr; -} - - -void StepAuthenticationEac1Widget::onDetailsButtonClicked() -{ - DetailDialog d(this); - - auto eac1 = mContext->getDidAuthenticateEac1(); - CVCertificateBody body = eac1->getCvCertificates().at(0)->getBody(); - QString effectiveDate = body.getCertificateEffectiveDate().toString(Qt::DefaultLocaleShortDate); - QString expirationDate = body.getCertificateExpirationDate().toString(Qt::DefaultLocaleShortDate); - auto certificateDescription = eac1->getCertificateDescription(); - - QString details; - details += tr("Service provider:") + QLatin1Char('\n'); - details += certificateDescription->getSubjectName(); - details += QLatin1Char('\n'); - details += certificateDescription->getSubjectUrl(); - - details += QLatin1String("\n\n"); - details += tr("Certificate issuer:") + QLatin1Char('\n'); - details += certificateDescription->getIssuerName(); - details += QLatin1Char('\n'); - details += certificateDescription->getIssuerUrl(); - - details += QLatin1String("\n\n"); - details += certificateDescription->getTermsOfUsage(); - details += QLatin1String("\n\n"); - details += tr("Validity:\n%1 - %2").arg(effectiveDate, expirationDate); - - // collapse multiple blank lines - details.replace(QRegularExpression(QStringLiteral("\n\n\n*")), QStringLiteral("\n\n")); - - d.setDetails(details); - d.exec(); -} - - -void StepAuthenticationEac1Widget::setToolTip() -{ - const auto& align = QStringLiteral("

%1

"); - - const auto& certDesc = tr("Information on the service provider who wants to read out data from your ID card is given here. For further information press the button \"more...\"."); - mUi->certificateDescriptionGroupBox->setToolTip(align.arg(certDesc)); - - const auto& fieldDesc = tr("Here you can select or deselect data fields to be read out. Mandatory data fields required by the service provider cannot be deselected."); - mUi->groupBox->setToolTip(align.arg(fieldDesc)); -} - - -void StepAuthenticationEac1Widget::updateWidget() -{ - if (!mContext) - { - return; - } - - switch (mState) - { - case State::INITIAL: - break; - - case State::EDIT_CHAT: - setupChatView(); - return; - - case State::ENTER_PIN: - updateButtonsAndPinWidget(); - return; - - case State::AUTHENTICATING_ESERVICE: - Q_EMIT setCancelButtonState(ButtonState::ENABLED); - updateProgressPanel(1, tr("Service provider is verified")); - break; - - case State::AUTHENTICATING_CARD: - updateProgressPanel(2, tr("Card is being verified")); - break; - - case State::READING_CARD_DATA: - updateProgressPanel(3, tr("Reading data")); - break; - - case State::REDIRECTING_BROWSER: - updateProgressPanel(4, tr("Service provider is being verified")); - break; - - case State::FINISHED: - updateProgressPanel(); - Q_EMIT setCancelButtonState(ButtonState::HIDDEN); - Q_EMIT setForwardButtonState(ButtonState::FOCUSSED, tr("OK")); - break; - } -} - - -void StepAuthenticationEac1Widget::setupChatView() -{ - auto eac1 = mContext->getDidAuthenticateEac1(); - mUi->subjectName->setText(eac1->getCertificateDescription()->getSubjectName()); - QString purpose = eac1->getCertificateDescription()->getPurpose(); - if (purpose.isEmpty()) - { - purpose = tr("See details under more..."); - } - mUi->usage->setText(purpose); - - if (eac1->getTransactionInfo().isNull() || eac1->getTransactionInfo().isEmpty()) - { - mUi->transactionInfoGroupBox->setVisible(false); - } - else - { - mUi->transactionInfo->setText(eac1->getTransactionInfo()); - mUi->transactionInfoGroupBox->setVisible(true); - } - - - mUi->listWidgetWest->clear(); - mUi->listWidgetEast->clear(); - - prepareChatsForGui(); - - mUi->detailsPushButton->setEnabled(true); - mUi->listWidgetWest->setEnabled(true); - mUi->listWidgetEast->setEnabled(true); - mUi->pinGroupBox->setVisible(false); - - Q_EMIT setCancelButtonState(ButtonState::ENABLED); - Q_EMIT setForwardButtonState(ButtonState::FOCUSSED, tr("Identify now")); -} - - -void StepAuthenticationEac1Widget::prepareChatsForGui() -{ - const double optionalRightsCount = mContext->getOptionalAccessRights().size(); - const double requiredRightsCount = mContext->getRequiredAccessRights().size(); - int listSize = qCeil((optionalRightsCount + requiredRightsCount) / 2.0); - - for (AccessRight orderedRight : AccessRoleAndRightsUtil::allDisplayedOrderedRights()) - { - if (mContext->getOptionalAccessRights().contains(orderedRight)) - { - addChatRightToGui(orderedRight, true, listSize); - } - else if (mContext->getRequiredAccessRights().contains(orderedRight)) - { - addChatRightToGui(orderedRight, false, listSize); - } - } -} - - -void StepAuthenticationEac1Widget::addChatRightToGui(AccessRight pRight, bool pOptional, int pListSize) -{ - QString displayText = AccessRoleAndRightsUtil::toDisplayText(pRight); - if (pRight == AccessRight::AGE_VERIFICATION) - { - displayText += QStringLiteral(" (%1)").arg(mContext->getDidAuthenticateEac1()->getAuthenticatedAuxiliaryData()->getRequiredAge()); - } - QCheckBox* cb = new QCheckBox(displayText); - cb->setEnabled(pOptional); - cb->setChecked(mContext->getEffectiveAccessRights().contains(pRight)); - - mMap.insert(cb, pRight); - - connect(cb, &QCheckBox::stateChanged, this, &StepAuthenticationEac1Widget::checkBoxChanged); - - QListWidgetItem* item = new QListWidgetItem(); - item->setSizeHint(QSize(0, 20)); - item->setData(Qt::AccessibleTextRole, displayText); - if (mUi->listWidgetWest->count() < pListSize) - { - mUi->listWidgetWest->addItem(item); - mUi->listWidgetWest->setItemWidget(item, cb); - } - else - { - mUi->listWidgetEast->addItem(item); - mUi->listWidgetEast->setItemWidget(item, cb); - } -} - - -void StepAuthenticationEac1Widget::createBasicReaderWidget() -{ - QWidget* basicReaderWidget = new QWidget(); - - QHBoxLayout* basicReaderWidgetLayout = new QHBoxLayout(basicReaderWidget); - - AppSettings& appSettings = AppSettings::getInstance(); - - const auto& allowedDigitsMsg = tr("Only digits (0-9) are permitted."); - QRegularExpression onlyNumbersExpression(QStringLiteral("[0-9]*")); - if (mContext->getCardConnection()->getReaderInfo().getRetryCounter() == 1 && !mContext->isCanAllowedMode()) - { - mCANField = new PasswordEdit(); - mCANField->setAccessibleName(tr("please enter your can")); - mCANField->setAccessibleDescription(allowedDigitsMsg); - mCANField->setMaxLength(6); - mCANField->configureValidation(onlyNumbersExpression, allowedDigitsMsg); - connect(mCANField, &PasswordEdit::textEdited, this, &StepAuthenticationEac1Widget::canTextEdited); - - QLabel* canLabel = new QLabel(tr("Card access number (CAN):")); - canLabel->setFocusPolicy(Qt::TabFocus); - basicReaderWidgetLayout->addWidget(canLabel); - basicReaderWidgetLayout->addWidget(mCANField); - - if (appSettings.getGeneralSettings().isUseScreenKeyboard()) - { - QToolButton* button = new QToolButton(); - button->setObjectName(QStringLiteral("canRandomButton")); - button->setAccessibleName(tr("open on screen keyboard")); - button->setAutoRaise(true); - button->setIcon(QPixmap(QStringLiteral(":/images/randompin/screen_keyboard.png"))); - button->setIconSize(QSize(44, 26)); - - connect(button, &QAbstractButton::clicked, this, &StepAuthenticationEac1Widget::onRandomButtonClicked); - - basicReaderWidgetLayout->addWidget(button); - } - - } - - mPINField = new PasswordEdit(); - mPINField->setAccessibleName(tr("please enter your pin")); - mPINField->setAccessibleDescription(allowedDigitsMsg); - mPINField->setMaxLength(6); - mPINField->configureValidation(onlyNumbersExpression, allowedDigitsMsg); - connect(mPINField, &PasswordEdit::textEdited, this, &StepAuthenticationEac1Widget::pinTextEdited); - - if (mContext->getCardConnection()->getReaderInfo().getRetryCounter() == 1 && !mContext->isCanAllowedMode()) - { - mPINField->setEnabled(false); - } - - const QString labelLabel = mContext->isCanAllowedMode() == false ? tr("PIN:") : tr("CAN:"); - QLabel* pinLabel = new QLabel(labelLabel); - pinLabel->setFocusPolicy(Qt::TabFocus); - basicReaderWidgetLayout->addWidget(pinLabel); - basicReaderWidgetLayout->addWidget(mPINField); - - if (appSettings.getGeneralSettings().isUseScreenKeyboard()) - { - QToolButton* button = new QToolButton(); - button->setObjectName(QStringLiteral("pinRandomButton")); - button->setAccessibleName(tr("open on screen keyboard")); - button->setAutoRaise(true); - button->setIcon(QPixmap(QStringLiteral(":/images/randompin/screen_keyboard.png"))); - button->setIconSize(QSize(44, 26)); - - connect(button, &QAbstractButton::clicked, this, &StepAuthenticationEac1Widget::onRandomButtonClicked); - - basicReaderWidgetLayout->addWidget(button); - } - - - mUi->pinWidgetLayout->invalidate(); - mUi->pinWidgetLayout->addWidget(basicReaderWidget); -} - - -void StepAuthenticationEac1Widget::updateProgressPanel(int pProgressValue, const QString& pProgressText) -{ - if (pProgressValue > 0) - { - if (mProgressBar == nullptr) - { - clearPinWidgetLayout(); - QWidget* progressWidget = new QWidget(); - QVBoxLayout* progressWidgetLayout = new QVBoxLayout(progressWidget); - progressWidgetLayout->setMargin(0); - mUi->pinWidgetLayout->addWidget(progressWidget); - - mProgressBar = new QProgressBar(); - mProgressBar->setTextVisible(false); - mProgressBar->setRange(0, 4); - progressWidgetLayout->addWidget(mProgressBar); - - mProgressBarLabel = new QLabel(); - progressWidgetLayout->addWidget(mProgressBarLabel); - - mUi->pinGroupBox->setTitle(tr("Identify")); - mUi->pinGroupBox->setVisible(true); - } - - mProgressBar->setValue(pProgressValue); - mProgressBarLabel->setText(pProgressText); - } - else - { - const bool cancelled = mContext->getStatus().isCancellationByUser(); - clearPinWidgetLayout(); - QWidget* doneWidget = new QWidget(); - QHBoxLayout* doneWidgetLayout = new QHBoxLayout(doneWidget); - doneWidgetLayout->setMargin(0); - mUi->pinWidgetLayout->addWidget(doneWidget); - - doneWidgetLayout->addStretch(); - QLabel* doneIcon = new QLabel; - QLabel* doneText = new QLabel(cancelled ? tr("The process was cancelled by the user") : tr("Identification successful")); - doneIcon->setPixmap(QPixmap(cancelled ? QStringLiteral(":/images/icon_cancelled.png") : QStringLiteral(":/images/icon_ok.png"))); - doneWidgetLayout->addWidget(doneIcon); - doneWidgetLayout->addWidget(doneText); - doneWidgetLayout->addStretch(); - - mUi->pinGroupBox->setTitle(tr("Result")); - } - -#ifdef Q_OS_WIN - if (mTaskbarButton) - { - auto progress = mTaskbarButton->progress(); - progress->setValue(pProgressValue == 0 ? progress->maximum() : pProgressValue); - } -#endif -} - - -void StepAuthenticationEac1Widget::checkBoxChanged(int pCheckState) -{ - QCheckBox* cb = qobject_cast(sender()); - if (cb != nullptr) - { - if (pCheckState == Qt::Unchecked) - { - QMap::ConstIterator i = qAsConst(mMap).find(cb); - bool success = mContext->removeEffectiveAccessRight(i.value()); - qCDebug(gui) << "Removed from effective chat:" << i.value() << "| success:" << success; - } - else - { - QMap::ConstIterator i = qAsConst(mMap).find(cb); - bool success = mContext->addEffectiveAccessRight(i.value()); - qCDebug(gui) << "Added to effective chat:" << i.value() << "| success:" << success; - } - } -} - - -void StepAuthenticationEac1Widget::onRandomButtonClicked() -{ - RandomPinDialog randomPinDialog(6, mContext->getReaderName(), this); - if (randomPinDialog.exec() == QDialog::Accepted && !randomPinDialog.getPin().isEmpty()) - { - QToolButton* pinButton = qobject_cast(sender()); - if (pinButton == nullptr) - { - qCCritical(gui) << "sender == nullptr"; - } - else if (pinButton->objectName() == QLatin1String("canRandomButton")) - { - canTextEdited(randomPinDialog.getPin()); - } - else if (pinButton->objectName() == QLatin1String("pinRandomButton")) - { - pinTextEdited(randomPinDialog.getPin()); - } - } -} - - -void StepAuthenticationEac1Widget::onResultChanged() -{ -#ifdef Q_OS_WIN - if (mTaskbarButton && (mContext.isNull() || mContext->getStatus().isError())) - { - mTaskbarButton->progress()->stop(); - } -#endif -} - - -void StepAuthenticationEac1Widget::hideEvent(QHideEvent* pEvent) -{ -#ifdef Q_OS_WIN - if (mTaskbarButton) - { - mTaskbarButton->progress()->setVisible(false); - } -#endif - QWidget::hideEvent(pEvent); -} - - -void StepAuthenticationEac1Widget::showEvent(QShowEvent* pEvent) -{ -#ifdef Q_OS_WIN - auto window = QApplication::activeWindow(); - if (window) - { - mTaskbarButton->setWindow(window->windowHandle()); - auto progress = mTaskbarButton->progress(); - progress->setVisible(true); - progress->setRange(0, 5); // 5 == count of states in setState - progress->reset(); - progress->resume(); // reset stop() - } -#endif - QWidget::showEvent(pEvent); -} - - -void StepAuthenticationEac1Widget::canTextEdited(const QString& pText) -{ - if (!pText.isNull() && !pText.isEmpty()) - { - mCANField->setText(pText); - } - - if (mCANField->text().size() == 6) - { - mPINField->setEnabled(true); - mPINField->setFocus(); - - Q_EMIT fireCanUpdated(mCANField->text()); - } - else - { - mPINField->setEnabled(false); - } - - mPINField->clear(); - pinTextEdited(QString()); -} - - -void StepAuthenticationEac1Widget::pinTextEdited(const QString& pText) -{ - if (!pText.isNull() && !pText.isEmpty()) - { - mPINField->setText(pText); - } - - if (mPINField->text().size() == 6) - { - Q_EMIT setForwardButtonState(ButtonState::FOCUSSED, tr("Identify now")); - Q_EMIT firePinUpdated(mPINField->text()); - } - else - { - Q_EMIT setForwardButtonState(ButtonState::DISABLED, tr("Identify now")); - } -} - - -void StepAuthenticationEac1Widget::focusWidget() -{ - if (mContext->getCardConnection()->getReaderInfo().getRetryCounter() == 1 && !mContext->isCanAllowedMode()) - { - mCANField->setFocus(); - mCANField->setCursorPosition(0); - } - else - { - mPINField->setFocus(); - mPINField->setCursorPosition(0); - } -} - - -void StepAuthenticationEac1Widget::changeEvent(QEvent* pEvent) -{ - if (pEvent->type() == QEvent::LanguageChange) - { - updateWidget(); - mUi->retranslateUi(this); - setToolTip(); - } - QWidget::changeEvent(pEvent); -} diff --git a/src/widget/step/StepAuthenticationEac1Widget.h b/src/widget/step/StepAuthenticationEac1Widget.h deleted file mode 100644 index 83631f3..0000000 --- a/src/widget/step/StepAuthenticationEac1Widget.h +++ /dev/null @@ -1,114 +0,0 @@ -/*! - * \brief Widget for the desktop StepAuthenticationEac1Gui. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "context/AuthContext.h" -#include "generic/ButtonState.h" - -#include - -#ifdef Q_OS_WIN -#include -#endif - -class QLabel; -class QProgressBar; - -namespace Ui -{ -class StepAuthenticationEac1Widget; -} - - -namespace governikus -{ - -class PasswordEdit; - -class StepAuthenticationEac1Widget - : public QWidget -{ - Q_OBJECT - - public: - enum class State - { - INITIAL, - EDIT_CHAT, - ENTER_PIN, - AUTHENTICATING_ESERVICE, - AUTHENTICATING_CARD, - READING_CARD_DATA, - REDIRECTING_BROWSER, - FINISHED, - }; - Q_ENUM(State); - - StepAuthenticationEac1Widget(QWidget* pParent = nullptr); - virtual ~StepAuthenticationEac1Widget() override; - - void setContext(const QSharedPointer& pContext); - - void setState(State pState); - void forwardStep(); - - void updateButtonsAndPinWidget(); - - Q_SIGNALS: - void setForwardButtonState(ButtonState pState, const QString& pText = QString()); - void setCancelButtonState(ButtonState pState); - - void firePinUpdated(const QString& pPin); - void fireCanUpdated(const QString& pCan); - - private Q_SLOTS: - void focusWidget(); - void onDetailsButtonClicked(); - void checkBoxChanged(int pCheckState); - void canTextEdited(const QString& pText); - void pinTextEdited(const QString& pText); - void onRandomButtonClicked(); - void onResultChanged(); - - protected: - virtual void hideEvent(QHideEvent* pEvent) override; - virtual void showEvent(QShowEvent* pEvent) override; - virtual void changeEvent(QEvent* pEvent) override; - - private: - void setToolTip(); - void updateWidget(); - void setupChatView(); - void prepareChatsForGui(); - void updateProgressPanel(int pProgressValue = 0, const QString& pProgressText = QString()); - void addChatRightToGui(AccessRight pRight, bool pOptional, int pListSize); - void clearPinWidgetLayout(); - void createBasicReaderWidget(); - - private: - QScopedPointer mUi; - QSharedPointer mContext; - QMap mMap; - - PasswordEdit* mCANField; - PasswordEdit* mPINField; - - State mState; - QProgressBar* mProgressBar; - QLabel* mProgressBarLabel; - - bool mCloseWindowWhenFinished; - - #ifdef Q_OS_WIN - QWinTaskbarButton* mTaskbarButton; - #endif -}; - - -defineEnumOperators(StepAuthenticationEac1Widget::State) - -} /* namespace governikus */ diff --git a/src/widget/step/StepAuthenticationEac1Widget.ui b/src/widget/step/StepAuthenticationEac1Widget.ui deleted file mode 100644 index 6622d48..0000000 --- a/src/widget/step/StepAuthenticationEac1Widget.ui +++ /dev/null @@ -1,398 +0,0 @@ - - - StepAuthenticationEac1Widget - - - - 0 - 0 - 515 - 448 - - - - - 0 - 0 - - - - AusweisApp2 - - - - - - Qt::TabFocus - - - Service provider - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - QFormLayout::AllNonFixedFieldsGrow - - - 6 - - - 6 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::TabFocus - - - Purpose for reading out requested data: - - - - - - - Qt::TabFocus - - - true - - - - - - - Qt::TabFocus - - - Name: - - - - - - - Qt::TabFocus - - - true - - - - - - - - - - - 0 - 0 - - - - - 75 - true - - - - details - - - more... - - - false - - - - - - - - - - - - - Qt::TabFocus - - - The following data is required by the service provider. You can deselect the non-mandatory data fields if you do not want this data to be transmitted. - - - true - - - - - - - Qt::TabFocus - - - Data - - - - 9 - - - 9 - - - 9 - - - 9 - - - - - 0 - - - - - true - - - - - - QFrame::NoFrame - - - QFrame::Plain - - - 0 - - - Qt::ScrollBarAsNeeded - - - true - - - - - - - true - - - - - - QFrame::NoFrame - - - QFrame::Plain - - - 0 - - - Qt::ScrollBarAsNeeded - - - 0 - - - true - - - - - - - - - - - - Qt::TabFocus - - - Important transactional information - - - - - - - - QFrame::NoFrame - - - QFrame::Plain - - - 0 - - - true - - - - - 0 - 0 - 475 - 69 - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::TabFocus - - - background-color: white; - - - - - - Qt::PlainText - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - true - - - true - - - - - - - - - - - - - - - - - 0 - - - 9 - - - 9 - - - 9 - - - 9 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - certificateDescriptionGroupBox - subjectNameLabel - subjectName - usageLabel - usage - detailsPushButton - label - groupBox - listWidgetWest - listWidgetEast - transactionInfoGroupBox - - - - diff --git a/src/widget/step/StepChooseCardGui.cpp b/src/widget/step/StepChooseCardGui.cpp deleted file mode 100644 index bd88554..0000000 --- a/src/widget/step/StepChooseCardGui.cpp +++ /dev/null @@ -1,239 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "StepChooseCardGui.h" - -#include "Env.h" -#include "generic/HelpAction.h" -#include "GuiProfile.h" -#include "ReaderConfiguration.h" -#include "step/AuthenticateStepsWidget.h" - -#include -#include -#include -#include -#include - -using namespace governikus; - - -Q_DECLARE_LOGGING_CATEGORY(gui) - - -StepChooseCardGui::StepChooseCardGui(const QSharedPointer& pContext, AuthenticateStepsWidget* pStepsWidget) - : StepGui(pContext) - , mContext(pContext) - , mWidget(pStepsWidget->getEac1Page()) - , mInformationMessageBox(new QMessageBox(pStepsWidget)) - , mReaderDeviceGui(new ReaderDeviceGui(pStepsWidget)) - , mCancelButton(nullptr) - , mDeviceButton(nullptr) - , mSubDialogOpen(false) -{ - mInformationMessageBox->setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("Information")); - mInformationMessageBox->setWindowModality(Qt::WindowModal); - mInformationMessageBox->setWindowFlags(mInformationMessageBox->windowFlags() & ~Qt::WindowContextHelpButtonHint); - mCancelButton = mInformationMessageBox->addButton(tr("Cancel"), QMessageBox::NoRole); - mDeviceButton = mInformationMessageBox->addButton(tr("Settings"), QMessageBox::YesRole); - mDeviceButton->setFocus(); - - connect(mReaderDeviceGui, &ReaderDeviceGui::fireFinished, this, &StepChooseCardGui::onSubDialogFinished); -} - - -StepChooseCardGui::~StepChooseCardGui() -{ -} - - -void StepChooseCardGui::activate() -{ - mWidget->setContext(mContext); - - setCancelButtonState(ButtonState::ENABLED); - - connect(&ReaderManager::getInstance(), &ReaderManager::fireReaderEvent, this, &StepChooseCardGui::onReaderManagerSignal); - onReaderManagerSignal(); -} - - -void StepChooseCardGui::deactivate() -{ - mWidget->setContext(QSharedPointer()); - - disconnect(&ReaderManager::getInstance(), &ReaderManager::fireReaderEvent, this, &StepChooseCardGui::onReaderManagerSignal); -} - - -QString StepChooseCardGui::getCurrentReaderImage(const QVector& pReaderInfos) -{ - if (pReaderInfos.size() == 1) - { - return pReaderInfos.at(0).getReaderConfigurationInfo().getIcon()->lookupPath(); - } - else if (pReaderInfos.size() > 1) - { - return ReaderConfiguration::getMultipleReaderIconPath(); - } - - return ReaderConfiguration::getNoReaderFoundIconPath(); -} - - -QString StepChooseCardGui::formatErrorMessages(const QString& pMessage1, const QString& pMessage2) -{ - const bool oneMessageIsEmpty = pMessage1.isEmpty() || pMessage2.isEmpty(); - - return oneMessageIsEmpty ? pMessage1 + pMessage2 : QStringLiteral("

%1

%2

").arg(pMessage1, pMessage2); -} - - -void StepChooseCardGui::updateErrorMessage(const QString& pTitle, const QString& pMessage1, const QString& pMessage2, bool closeErrorMessage) -{ - if (closeErrorMessage || mContext->getStatus().isError()) - { - mReaderDeviceGui->deactivate(); - mInformationMessageBox->done(QMessageBox::InvalidRole); - return; - } - - QString iconPath = getCurrentReaderImage(Env::getSingleton()->getReaderInfos(ReaderFilter::UniqueReaderTypes)); - if (iconPath.isEmpty()) - { - mInformationMessageBox->setIcon(QMessageBox::Information); - } - else - { - mInformationMessageBox->setIconPixmap(QPixmap(iconPath).scaledToWidth(200, Qt::SmoothTransformation)); - } - mInformationMessageBox->setText(QStringLiteral("%1").arg(pTitle)); - mInformationMessageBox->setInformativeText(formatErrorMessages(pMessage1, pMessage2)); - - if (mInformationMessageBox->isVisible() || mSubDialogOpen) - { - return; - } - - if (mInformationMessageBox->exec() != QMessageBox::InvalidRole) - { - if (mInformationMessageBox->clickedButton() == mCancelButton) - { - Q_EMIT fireCancelled(); - return; - } - - mSubDialogOpen = true; - if (mInformationMessageBox->clickedButton() == mDeviceButton) - { - mReaderDeviceGui->activate(); - } - } - // else: dialog was closed by an onErrorMessage(..., true) call (i.e. card found) -} - - -void StepChooseCardGui::onSubDialogFinished() -{ - mSubDialogOpen = false; - const QSharedPointer& remoteClient = ReaderManager::getInstance().getRemoteClient(); - remoteClient->startDetection(); - QMetaObject::invokeMethod(this, "onReaderManagerSignal", Qt::QueuedConnection); -} - - -const QString StepChooseCardGui::connectedRemoteReaderNames() const -{ - const QSharedPointer& remoteClient = Env::getSingleton()->getRemoteClient(); - const auto deviceInfos = remoteClient->getConnectedDeviceInfos(); - QStringList deviceNames; - for (const auto& info : deviceInfos) - { - deviceNames.append(QLatin1Char('"') + info.getName() + QLatin1Char('"')); - } - return deviceNames.join(QLatin1String(", ")); -} - - -void StepChooseCardGui::onReaderManagerSignal() -{ - const auto readers = ReaderManager::getInstance().getReaderInfos(); - - mReaderDeviceGui->reactToReaderCount(readers.size()); - - QVector readersWithNpa; - QVector remoteReaders; - for (const auto& readerInfo : readers) - { - if (readerInfo.hasEidCard()) - { - readersWithNpa << readerInfo; - } - if (readerInfo.getPlugInType() == ReaderManagerPlugInType::REMOTE) - { - remoteReaders << readerInfo; - } - } - - if (readers.size() == 0) - { - const QString onlineHelpUrl = HelpAction::getOnlineUrl(QStringLiteral("stepChooseCardGui")); - const QString onlineHelpText = tr("If you need help or have problems with your card reader, you can consult the " - "%1online help%2 for futher information.") - .arg(QStringLiteral("").arg(onlineHelpUrl), QStringLiteral("")); - - updateErrorMessage(tr("No card reader found. Please connect card reader first."), - tr("Please choose \"Settings\" to install a card reader or configure your smartphone as a card reader."), - onlineHelpText, - false); - return; - } - - if (readersWithNpa.size() == 0) - { - QString remoteReaderInfo; - if (remoteReaders.size() > 0) - { - remoteReaderInfo = tr("Connected to following remote readers: %1.").arg(connectedRemoteReaderNames()); - } - const QString onlineHelpUrl = HelpAction::getOnlineUrl(QStringLiteral("stepChooseCardGui")); - const QString onlineHelpText = tr("If you have already placed an ID card on your card reader, " - "you can consult the %1online help%2 for futher information.") - .arg(QStringLiteral("").arg(onlineHelpUrl), QStringLiteral("")); - - updateErrorMessage(tr("Please place an ID card on the card reader."), onlineHelpText, remoteReaderInfo, false); - } - else if (readersWithNpa.size() > 1) - { - const QString onlineHelpUrl = HelpAction::getOnlineUrl(QStringLiteral("stepChooseCardGui")); - const QString onlineHelpText = tr("Please make sure that only one card reader with an ID card on it is connected to " - "your computer. If you have already placed an ID card on your card reader, " - "you can consult the %1online help%2 for futher information.") - .arg(QStringLiteral("").arg(onlineHelpUrl), QStringLiteral("")); - - updateErrorMessage(tr("Please place only one ID card on the card reader."), onlineHelpText, QString(), false); - } - else - { - if (!readersWithNpa[0].sufficientApduLength()) - { - updateErrorMessage(tr("Extended Length is not supported."), - tr("Your remote reader does not meet the technical requirements (Extended Length not supported)."), - QString(), - false); - } - else if (readersWithNpa[0].isPinDeactivated() && !mContext->isCanAllowedMode()) - { - updateErrorMessage(tr("Online identification function is disabled."), - tr("This action cannot be performed. The online identification function of your ID card is deactivated." - " Please contact the authority responsible for issuing your identification document to activate the online identification function."), - QString(), - false); - } - else - { - updateErrorMessage(QString(), QString(), QString(), true); - } - } -} diff --git a/src/widget/step/StepChooseCardGui.h b/src/widget/step/StepChooseCardGui.h deleted file mode 100644 index 2f74c46..0000000 --- a/src/widget/step/StepChooseCardGui.h +++ /dev/null @@ -1,57 +0,0 @@ -/*! - * \brief GUI to select reader/card. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "context/AuthContext.h" -#include "DiagnosisGui.h" -#include "ReaderDeviceGui.h" -#include "ReaderManager.h" -#include "step/StepAuthenticationEac1Widget.h" -#include "StepGui.h" - -#include - -class QLabel; - -namespace governikus -{ - -class AuthenticateStepsWidget; - -class StepChooseCardGui - : public StepGui -{ - Q_OBJECT - - private: - const QSharedPointer mContext; - StepAuthenticationEac1Widget* const mWidget; - QPointer mInformationMessageBox; - QPointer mReaderDeviceGui; - QPushButton* mCancelButton, * mDeviceButton; - bool mSubDialogOpen; - - QString getCurrentReaderImage(const QVector& pReaderInfos); - static QString formatErrorMessages(const QString& pMessage1, const QString& pMessage2); - void updateErrorMessage(const QString& pTitle, const QString& pMessage1, const QString& pMessage2 = QString(), bool closeErrorMessage = false); - const QString connectedRemoteReaderNames() const; - - private Q_SLOTS: - void onSubDialogFinished(); - - public Q_SLOTS: - void onReaderManagerSignal(); - - public: - StepChooseCardGui(const QSharedPointer& pContext, AuthenticateStepsWidget* pStepsWidget); - virtual ~StepChooseCardGui() override; - - virtual void activate() override; - virtual void deactivate() override; -}; - -} /* namespace governikus */ diff --git a/src/widget/step/StepErrorGui.cpp b/src/widget/step/StepErrorGui.cpp deleted file mode 100644 index 6e3588b..0000000 --- a/src/widget/step/StepErrorGui.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "StepErrorGui.h" - -#include "AppQtMainWidget.h" -#include "generic/GuiUtils.h" - -#include -#include -#include -#include - - -using namespace governikus; - - -Q_DECLARE_LOGGING_CATEGORY(gui) - - -StepErrorGui::StepErrorGui(QSharedPointer pContext, AppQtMainWidget* const pMainWidget) - : StepGui(pContext) - , mContext(pContext) - , mMainWidget(pMainWidget) -{ -} - - -StepErrorGui::~StepErrorGui() -{ -} - - -void StepErrorGui::reportError() -{ - // Do not close the window automatically in case of errors when the workflow is done. - mMainWidget->setHideWindowAfterWorkflow(false); - - if (mContext->getStatus().is(GlobalStatus::Code::Paos_Error_SAL_Invalid_Key) && !mContext->getCardConnection()->getReaderInfo().getCardInfo().isPinDeactivated()) - { - if (GuiUtils::showWrongPinBlockedDialog(mMainWidget)) - { - mMainWidget->switchToPinSettingsAfterWorkflow(); - Q_EMIT switchedToPinSettings(); - } - else - { - Q_EMIT fireUiFinished(); - } - return; - } - - QString message = mContext->getStatus().toErrorDescription(true); - if (message.isEmpty()) - { - qCCritical(gui) << "No error message determined:" << mContext->getStatus(); - message = tr("Sorry, that should not have happened! Please contact the support team."); - Q_ASSERT(!message.isEmpty()); - } - - QMessageBox box(mMainWidget); - box.setWindowTitle(QApplication::applicationName() + QStringLiteral(" - ") + tr("Error")); - box.setWindowModality(Qt::ApplicationModal); - box.setIcon(QMessageBox::Warning); - box.setWindowFlags(box.windowFlags() & ~Qt::WindowContextHelpButtonHint); - box.setText(message); - box.setStandardButtons(QMessageBox::Ok); - box.button(QMessageBox::Ok)->setFocus(); - box.exec(); - - Q_EMIT fireUiFinished(); -} - - -void StepErrorGui::forwardStep() -{ - Q_EMIT fireUiFinished(); -} diff --git a/src/widget/step/StepErrorGui.h b/src/widget/step/StepErrorGui.h deleted file mode 100644 index 5c44820..0000000 --- a/src/widget/step/StepErrorGui.h +++ /dev/null @@ -1,39 +0,0 @@ -/*! - * \brief GUI for step "Error". - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "context/WorkflowContext.h" -#include "StepGui.h" - -namespace governikus -{ - -class AppQtMainWidget; - -class StepErrorGui - : public StepGui -{ - Q_OBJECT - - public: - StepErrorGui(QSharedPointer pContext, AppQtMainWidget* const pMainWidget); - virtual ~StepErrorGui() override; - - virtual void reportError(); - - public Q_SLOTS: - virtual void forwardStep() override; - - private: - QSharedPointer mContext; - AppQtMainWidget* const mMainWidget; - - Q_SIGNALS: - void switchedToPinSettings(); -}; - -} /* namespace governikus */ diff --git a/src/widget/step/StepGui.h b/src/widget/step/StepGui.h deleted file mode 100644 index 5ee5eab..0000000 --- a/src/widget/step/StepGui.h +++ /dev/null @@ -1,83 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "generic/ButtonState.h" - -#include -#include -#include - -namespace governikus -{ - -class WorkflowContext; - - -class StepGuiDelegate - : public QObject -{ - Q_OBJECT - - public: - StepGuiDelegate(); - - Q_SIGNALS: - void setForwardButtonState(ButtonState pState, const QString& pText); - void setCancelButtonState(ButtonState pState); -}; - - -class StepGui - : public QObject -{ - Q_OBJECT - - public: - StepGui(const QSharedPointer& pContext); - virtual ~StepGui(); - - StepGuiDelegate* getStepGuiDelegate() const - { - return mDelegate.data(); - } - - - virtual void activate() - { - } - - - virtual void deactivate() - { - } - - - virtual void forwardStep(); - - protected: - void setForwardButtonState(ButtonState pState, const QString& pText = QString()) - { - Q_EMIT mDelegate->setForwardButtonState(pState, pText); - } - - - void setCancelButtonState(ButtonState pState) - { - Q_EMIT mDelegate->setCancelButtonState(pState); - } - - - protected: - QScopedPointer mDelegate; - - Q_SIGNALS: - //void fireErrorMessage(QString pTitle, QString pMessage, QVector pReaderInfos, bool closeErrorMessage = false); - void fireUiFinished(); - void fireCancelled(); - -}; - -} /* namespace governikus */ diff --git a/src/widget/step/StepProcessingGui.h b/src/widget/step/StepProcessingGui.h deleted file mode 100644 index 179ff97..0000000 --- a/src/widget/step/StepProcessingGui.h +++ /dev/null @@ -1,31 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "StepGui.h" - - -namespace governikus -{ - -class AuthenticateStepsWidget; - -class StepProcessingGui - : public StepGui -{ - Q_OBJECT - - public: - StepProcessingGui(const QSharedPointer& pContext, AuthenticateStepsWidget* pStepsWidget); - virtual ~StepProcessingGui() override; - - virtual void activate() override; - virtual void deactivate() override; - - private: - AuthenticateStepsWidget* mStepsWidget; -}; - -} /* namespace governikus */ diff --git a/src/widget/step/StepShowSelfAuthenticationDataGui.h b/src/widget/step/StepShowSelfAuthenticationDataGui.h deleted file mode 100644 index 2868a2e..0000000 --- a/src/widget/step/StepShowSelfAuthenticationDataGui.h +++ /dev/null @@ -1,37 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "context/SelfAuthContext.h" -#include "StepGui.h" - -#include - -namespace governikus -{ - -class AuthenticateStepsWidget; - -class StepShowSelfAuthenticationDataGui - : public StepGui -{ - Q_OBJECT - - public: - StepShowSelfAuthenticationDataGui(QSharedPointer pContext, AuthenticateStepsWidget* pStepsWidget); - virtual ~StepShowSelfAuthenticationDataGui() override; - - virtual void activate() override; - virtual void deactivate() override; - - private Q_SLOTS: - virtual void forwardStep() override; - - private: - QSharedPointer mContext; - AuthenticateStepsWidget* mStepsWidget; -}; - -} /* namespace governikus */ diff --git a/src/widget/workflow/GenericWorkflowGui.h b/src/widget/workflow/GenericWorkflowGui.h deleted file mode 100644 index cb9564b..0000000 --- a/src/widget/workflow/GenericWorkflowGui.h +++ /dev/null @@ -1,91 +0,0 @@ -/*! - * \brief Generic base class for Qt based WorkflowUi implementations. - * - * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "AppQtMainWidget.h" -#include "context/WorkflowContext.h" -#include "step/StepGui.h" -#include "WorkflowGui.h" -#include "WorkflowQtWidget.h" - -#include -#include - - -namespace governikus -{ - -template -class GenericWorkflowGui - : public WorkflowGui -{ - protected: - AppQtMainWidget* mParentWidget; - WorkflowQtWidget* mWidget; - QSharedPointer mStepGui; - QSharedPointer mContext; - - public: - GenericWorkflowGui(const QSharedPointer& pContext, AppQtMainWidget* pParentWidget, WorkflowQtWidget* pWidget) - : WorkflowGui() - , mParentWidget(pParentWidget) - , mWidget(pWidget) - , mStepGui(nullptr) - , mContext(pContext.objectCast()) - { - Q_ASSERT(mContext != nullptr); - connect(this, &WorkflowGui::fireUserCancelled, mContext.data(), &WorkflowContext::fireCancelWorkflow); - } - - - virtual void deactivate() override - { - deactivateCurrentStepUi(); - } - - - virtual void activateStepUi(const QSharedPointer& pStepUi) - { - Q_ASSERT(pStepUi); - if (mStepGui == pStepUi) - { - return; - } - - deactivateCurrentStepUi(); - - mStepGui = pStepUi; - if (mWidget != nullptr) - { - QObject::connect(mStepGui->getStepGuiDelegate(), &StepGuiDelegate::setForwardButtonState, mWidget, &WorkflowQtWidget::setForwardButtonState); - QObject::connect(mStepGui->getStepGuiDelegate(), &StepGuiDelegate::setCancelButtonState, mWidget, &WorkflowQtWidget::setCancelButtonState); - } - pStepUi->activate(); - } - - - private: - void deactivateCurrentStepUi() - { - if (mStepGui == nullptr) - { - return; - } - - mStepGui->deactivate(); - if (mWidget != nullptr) - { - QObject::disconnect(mStepGui->getStepGuiDelegate(), &StepGuiDelegate::setForwardButtonState, mWidget, &WorkflowQtWidget::setForwardButtonState); - QObject::disconnect(mStepGui->getStepGuiDelegate(), &StepGuiDelegate::setCancelButtonState, mWidget, &WorkflowQtWidget::setCancelButtonState); - } - mStepGui.clear(); - } - - -}; - -} /* namespace governikus */ diff --git a/src/widget/workflow/WorkflowAuthenticateQtGui.cpp b/src/widget/workflow/WorkflowAuthenticateQtGui.cpp deleted file mode 100644 index 4276c84..0000000 --- a/src/widget/workflow/WorkflowAuthenticateQtGui.cpp +++ /dev/null @@ -1,184 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "WorkflowAuthenticateQtGui.h" - -#include "AppSettings.h" -#include "generic/GuiUtils.h" -#include "states/FinalState.h" -#include "states/StateCheckRefreshAddress.h" -#include "states/StateDidAuthenticateEac1.h" -#include "states/StateDidAuthenticateEac2.h" -#include "states/StateEditAccessRights.h" -#include "states/StateEstablishPaceCan.h" -#include "states/StateEstablishPacePin.h" -#include "states/StateEstablishPacePuk.h" -#include "states/StateProcessing.h" -#include "states/StateSelectReader.h" -#include "states/StateTransmit.h" -#include "states/StateWriteHistory.h" -#include "step/AuthenticateStepsWidget.h" -#include "step/StepAdviseUserToRemoveCardGui.h" -#include "step/StepAuthenticationDoneGui.h" -#include "step/StepAuthenticationEac1Gui.h" -#include "step/StepChooseCardGui.h" -#include "step/StepErrorGui.h" -#include "step/StepProcessingGui.h" -#include "workflow/WorkflowQtWidget.h" - - -using namespace governikus; - - -WorkflowAuthenticateQtGui::WorkflowAuthenticateQtGui(const QSharedPointer& pContext, AppQtMainWidget* const pParentWidget) - : GenericWorkflowGui(pContext, pParentWidget, pParentWidget->getAuthenticationWorkflowWidget()) - , mCanEntered(false) - , mAuthenticateStepsWidget(mParentWidget->findChild()) - , mAdviseUserToRemoveCardGui(new StepAdviseUserToRemoveCardGui(mContext, mParentWidget)) - , mDidAuthenticateGui(new StepAuthenticationEac1Gui(mContext, mAuthenticateStepsWidget)) - , mChooseCardGui(new StepChooseCardGui(mContext, mAuthenticateStepsWidget)) - , mErrorGui(new StepErrorGui(mContext, mParentWidget)) - , mProcessingGui(new StepProcessingGui(mContext, mAuthenticateStepsWidget)) - , mAuthenticationDoneGui(new StepAuthenticationDoneGui(mContext)) -{ - Q_ASSERT(mAuthenticateStepsWidget != nullptr); - connect(mWidget, &WorkflowQtWidget::fireUserCancelled, this, &WorkflowGui::fireUserCancelled); - connect(mWidget, &WorkflowQtWidget::forwardStep, this, &WorkflowAuthenticateQtGui::onForwardStep); -} - - -WorkflowAuthenticateQtGui::~WorkflowAuthenticateQtGui() -{ -} - - -void WorkflowAuthenticateQtGui::activate() -{ - activateStepUi(mProcessingGui); - mParentWidget->workflowActivated(WorkflowWidgetParent::Authentication, tr("Identify")); - connect(mContext.data(), &WorkflowContext::fireStateChanged, this, &WorkflowAuthenticateQtGui::onStateChanged); -} - - -void WorkflowAuthenticateQtGui::deactivate() -{ - mParentWidget->workflowDeactivated(); -} - - -bool WorkflowAuthenticateQtGui::verifyAbortWorkflow() -{ - QMessageBox msgBox(mParentWidget); - msgBox.setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("Cancel")); - msgBox.setWindowModality(Qt::WindowModal); - msgBox.setText(tr("Do you really want to cancel?")); - msgBox.setInformativeText(tr("You can as well identity later by calling the service provider's Internet page" - " again.")); - msgBox.setIconPixmap(QIcon(QStringLiteral(":/images/npa.svg")).pixmap(32, 32)); - msgBox.setWindowFlags(msgBox.windowFlags() & ~Qt::WindowContextHelpButtonHint); - msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); - msgBox.button(QMessageBox::Yes)->setFocus(); - - return msgBox.exec() == QMessageBox::Yes; -} - - -void WorkflowAuthenticateQtGui::onForwardStep() -{ - if (mStepGui != nullptr) - { - mStepGui->forwardStep(); - } -} - - -void WorkflowAuthenticateQtGui::onStateChanged(const QString& pNewState) -{ - if (mContext->getStatus().isError() && !mContext->isErrorReportedToUser()) - { - if (!mContext->getStatus().isCancellationByUser()) - { - activateStepUi(mErrorGui); - mErrorGui->reportError(); - } - mContext->setErrorReportedToUser(); - } - - - bool approveNewState = true; - if (AbstractState::isState(pNewState)) - { - activateStepUi(mProcessingGui); - - GeneralSettings& settings = AppSettings::getInstance().getGeneralSettings(); - approveNewState = !settings.isTransportPinReminder(); - } - else if (AbstractState::isState(pNewState)) - { - approveNewState = false; - activateStepUi(mDidAuthenticateGui); - mDidAuthenticateGui->setState(StepAuthenticationEac1Widget::State::EDIT_CHAT); - } - else if (AbstractState::isState(pNewState)) - { - mContext->setStateApproved(true); - activateStepUi(mChooseCardGui); - return; - } - else if (AbstractState::isState(pNewState) || AbstractState::isState(pNewState)) - { - if (AbstractState::isState(pNewState)) - { - approveNewState = !mContext->getCardConnection()->getReaderInfo().isBasicReader(); - mCanEntered = true; - } - else if (AbstractState::isState(pNewState)) - { - // PIN entry after CAN entry is done without user interaction - approveNewState = mCanEntered || !mContext->getCardConnection()->getReaderInfo().isBasicReader(); - } - activateStepUi(mDidAuthenticateGui); - mDidAuthenticateGui->setState(StepAuthenticationEac1Widget::State::ENTER_PIN); - if (mContext->getLastPaceResult() != CardReturnCode::OK) - { - mDidAuthenticateGui->incorrectPinError(); - } - } - else if (AbstractState::isState(pNewState)) - { - approveNewState = false; - Q_EMIT mContext->fireCancelWorkflow(); - if (GuiUtils::showWrongPinBlockedDialog(mWidget)) - { - mContext->setReaderName(QString()); - mParentWidget->switchToPinSettingsAfterWorkflow(); - } - } - else if (AbstractState::isState(pNewState)) - { - mDidAuthenticateGui->setState(StepAuthenticationEac1Widget::State::AUTHENTICATING_ESERVICE); - } - else if (AbstractState::isState(pNewState)) - { - mDidAuthenticateGui->setState(StepAuthenticationEac1Widget::State::AUTHENTICATING_CARD); - } - else if (AbstractState::isState(pNewState)) - { - mDidAuthenticateGui->setState(StepAuthenticationEac1Widget::State::READING_CARD_DATA); - } - else if (AbstractState::isState(pNewState) && mDidAuthenticateGui->isActive()) - { - mDidAuthenticateGui->setState(StepAuthenticationEac1Widget::State::REDIRECTING_BROWSER); - } - else if (AbstractState::isState(pNewState) && mDidAuthenticateGui->isActive()) - { - mDidAuthenticateGui->setState(StepAuthenticationEac1Widget::State::FINISHED); - } - else if (AbstractState::isState(pNewState)) - { - activateStepUi(mAdviseUserToRemoveCardGui); - activateStepUi(mAuthenticationDoneGui); - } - mContext->setStateApproved(approveNewState); -} diff --git a/src/widget/workflow/WorkflowAuthenticateQtGui.h b/src/widget/workflow/WorkflowAuthenticateQtGui.h deleted file mode 100644 index ed2e14d..0000000 --- a/src/widget/workflow/WorkflowAuthenticateQtGui.h +++ /dev/null @@ -1,53 +0,0 @@ -/*! - * \brief Qt widget based WorkflowAuthenticateUi implementation. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "context/AuthContext.h" -#include "GenericWorkflowGui.h" - - -namespace governikus -{ - -class AuthenticateStepsWidget; -class StepAdviseUserToRemoveCardGui; -class StepAuthenticationDoneGui; -class StepAuthenticationEac1Gui; -class StepChooseCardGui; -class StepErrorGui; -class StepProcessingGui; - - -class WorkflowAuthenticateQtGui - : public GenericWorkflowGui -{ - Q_OBJECT - - private: - bool mCanEntered; - AuthenticateStepsWidget* mAuthenticateStepsWidget; - QSharedPointer mAdviseUserToRemoveCardGui; - QSharedPointer mDidAuthenticateGui; - QSharedPointer mChooseCardGui; - QSharedPointer mErrorGui; - QSharedPointer mProcessingGui; - QSharedPointer mAuthenticationDoneGui; - - private Q_SLOTS: - void onForwardStep(); - void onStateChanged(const QString& pNewState); - - public: - WorkflowAuthenticateQtGui(const QSharedPointer& pContext, AppQtMainWidget* const pParentWidget); - virtual ~WorkflowAuthenticateQtGui() override; - - virtual void activate() override; - virtual void deactivate() override; - virtual bool verifyAbortWorkflow() override; -}; - -} /* namespace governikus */ diff --git a/src/widget/workflow/WorkflowChangePinQtGui.cpp b/src/widget/workflow/WorkflowChangePinQtGui.cpp deleted file mode 100644 index de34182..0000000 --- a/src/widget/workflow/WorkflowChangePinQtGui.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "WorkflowChangePinQtGui.h" - -#include "AppQtMainWidget.h" -#include "generic/GuiUtils.h" -#include "PinSettingsWidget.h" -#include "states/StateChangePin.h" -#include "states/StateCleanUpReaderManager.h" -#include "states/StateEstablishPaceCan.h" -#include "states/StateEstablishPacePin.h" -#include "states/StateEstablishPacePuk.h" -#include "states/StateHandleRetryCounter.h" -#include "step/StepErrorGui.h" - -using namespace governikus; - -WorkflowChangePinQtGui::WorkflowChangePinQtGui(QSharedPointer pContext, AppQtMainWidget* const pParentWidget) - : GenericWorkflowGui(pContext, pParentWidget, nullptr) - , mPinSettingsWidget(mParentWidget->findChild()) - , mErrorGui(new StepErrorGui(mContext, mParentWidget)) -{ - Q_ASSERT(mPinSettingsWidget); - connect(mContext.data(), &WorkflowContext::fireStateChanged, this, &WorkflowChangePinQtGui::onStateChanged); -} - - -WorkflowChangePinQtGui::~WorkflowChangePinQtGui() -{ -} - - -void WorkflowChangePinQtGui::activate() -{ - mParentWidget->workflowActivated(WorkflowWidgetParent::SettingsChangePin, QString()); - mPinSettingsWidget->setInProgress(true); -} - - -void WorkflowChangePinQtGui::deactivate() -{ - mPinSettingsWidget->setInProgress(false); - mParentWidget->workflowDeactivated(); -} - - -bool WorkflowChangePinQtGui::verifyAbortWorkflow() -{ - // not really necessary to notify the user - return true; -} - - -void WorkflowChangePinQtGui::onStateChanged(const QString& pNextState) -{ - if (mContext->getStatus().isError() && !mContext->isErrorReportedToUser()) - { - if (!mContext->getStatus().isCancellationByUser()) - { - activateStepUi(mErrorGui); - mErrorGui->reportError(); - } - mContext->setErrorReportedToUser(); - } - - if (AbstractState::isState(pNextState)) - { - if (mContext->getLastPaceResult() != CardReturnCode::OK) - { - auto newRetryCounter = mContext->getCardConnection()->getReaderInfo().getRetryCounter(); - GuiUtils::showPinCanPukErrorDialog(mContext->getLastPaceResult(), newRetryCounter, mContext->isCanAllowedMode(), mPinSettingsWidget); - - /* - * In the desktop version we cancel the workflow after a wrong user input. - * If the user wants to try again, the workflow must be started again. - */ - Q_EMIT mContext->fireCancelWorkflow(); - return; - } - } - else if (AbstractState::isState(pNextState)) - { - mContext->setPin(mPinSettingsWidget->getPin()); - } - else if (AbstractState::isState(pNextState)) - { - mContext->setCan(mPinSettingsWidget->getCan()); - } - else if (AbstractState::isState(pNextState)) - { - mContext->setPuk(mPinSettingsWidget->getPuk()); - } - else if (AbstractState::isState(pNextState)) - { - mContext->setNewPin(mPinSettingsWidget->getNewPin()); - } - else if (AbstractState::isState(pNextState) && mContext->getStatus().isNoError()) - { - bool pinBlocked = (mContext->getCardConnection()->getReaderInfo().getRetryCounter() == 0); - mPinSettingsWidget->setMode(pinBlocked ? PinSettingsWidget::Mode::AfterPinUnblock : PinSettingsWidget::Mode::AfterPinChange); - } - - mContext->setStateApproved(); -} diff --git a/src/widget/workflow/WorkflowChangePinQtGui.h b/src/widget/workflow/WorkflowChangePinQtGui.h deleted file mode 100644 index 1438ead..0000000 --- a/src/widget/workflow/WorkflowChangePinQtGui.h +++ /dev/null @@ -1,41 +0,0 @@ -/*! - * \brief Qt widget based WorkflowChangePinUi implementation. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "context/ChangePinContext.h" -#include "GenericWorkflowGui.h" - -namespace governikus -{ - -class AppQtMainWidget; -class PinSettingsWidget; -class StepErrorGui; - - -class WorkflowChangePinQtGui - : public GenericWorkflowGui -{ - Q_OBJECT - - public: - WorkflowChangePinQtGui(QSharedPointer pContext, AppQtMainWidget* const pParentWidget); - virtual ~WorkflowChangePinQtGui() override; - - virtual void activate() override; - virtual void deactivate() override; - virtual bool verifyAbortWorkflow() override; - - private Q_SLOTS: - void onStateChanged(const QString& pNextState); - - private: - PinSettingsWidget* mPinSettingsWidget; - QSharedPointer mErrorGui; -}; - -} /* namespace governikus */ diff --git a/src/widget/workflow/WorkflowGui.cpp b/src/widget/workflow/WorkflowGui.cpp deleted file mode 100644 index 529314c..0000000 --- a/src/widget/workflow/WorkflowGui.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "WorkflowGui.h" - -#include "step/StepGui.h" - -using namespace governikus; - -WorkflowGuiDelegate::WorkflowGuiDelegate() -{ -} - - -WorkflowGui::WorkflowGui() - : mDelegate(new WorkflowGuiDelegate) -{ -} - - -WorkflowGui::~WorkflowGui() -{ -} diff --git a/src/widget/workflow/WorkflowGui.h b/src/widget/workflow/WorkflowGui.h deleted file mode 100644 index 442132e..0000000 --- a/src/widget/workflow/WorkflowGui.h +++ /dev/null @@ -1,70 +0,0 @@ -/*! - * \brief Base class for Qt based WorkflowUi implementations. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include - -#include "AppQtMainWidget.h" -#include "context/WorkflowContext.h" -#include "step/StepGui.h" -#include "WorkflowQtWidget.h" -#include "WorkflowWidgetParent.h" - -namespace governikus -{ - -class WorkflowQtWidget; - -class WorkflowGuiDelegate - : public QObject -{ - Q_OBJECT - - public: - WorkflowGuiDelegate(); -}; - - -class WorkflowGui - : public QObject -{ - Q_OBJECT - - protected: - QScopedPointer mDelegate; - - public: - WorkflowGui(); - virtual ~WorkflowGui(); - - WorkflowGuiDelegate* getWorkflowGuiDelegate() const - { - return mDelegate.data(); - } - - - virtual bool verifyAbortWorkflow() = 0; - - virtual void activate() - { - } - - - virtual void deactivate() - { - } - - - Q_SIGNALS: - void fireUserCancelled(); - void fireChangePinRequest(); - void fireSwitchToReaderSettingsRequested(); - -}; - -} /* namespace governikus */ diff --git a/src/widget/workflow/WorkflowQtWidget.cpp b/src/widget/workflow/WorkflowQtWidget.cpp deleted file mode 100644 index dc4b74d..0000000 --- a/src/widget/workflow/WorkflowQtWidget.cpp +++ /dev/null @@ -1,118 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "WorkflowQtWidget.h" - -#include -#include -#include - -using namespace governikus; - -WorkflowQtWidget::WorkflowQtWidget(QWidget* pParent) - : QWidget(pParent) - , mMainLayout(new QVBoxLayout(this)) - , mStepWidgetArea(new QWidget(this)) - , mCancelButton(nullptr) - , mForwardButton(nullptr) -{ - (new QVBoxLayout(mStepWidgetArea))->setMargin(0); - mMainLayout->addWidget(mStepWidgetArea); - mMainLayout->setMargin(0); - - QHBoxLayout* buttonLayout = new QHBoxLayout(); - buttonLayout->setMargin(0); - mMainLayout->addLayout(buttonLayout); - - QSpacerItem* horizontalSpacer = new QSpacerItem(40, 1, QSizePolicy::Expanding, QSizePolicy::Ignored); - - buttonLayout->addItem(horizontalSpacer); - - mCancelButton = new QPushButton(this); - connect(mCancelButton, &QPushButton::clicked, this, &WorkflowQtWidget::onCancelButtonClicked); - mCancelButton->setVisible(false); - buttonLayout->addWidget(mCancelButton); - setCancelButtonState(ButtonState::HIDDEN); - - mForwardButton = new QPushButton(this); - connect(mForwardButton, &QPushButton::clicked, this, &WorkflowQtWidget::forwardStep); - mForwardButton->setVisible(false); - buttonLayout->addWidget(mForwardButton); - setForwardButtonState(ButtonState::HIDDEN); -} - - -WorkflowQtWidget::~WorkflowQtWidget() -{ -} - - -void WorkflowQtWidget::addStepWidget(QWidget* pStepWidget) -{ - mStepWidgetArea->layout()->addWidget(pStepWidget); -} - - -void WorkflowQtWidget::removeStepWidget(QWidget* pStepWidget) -{ - mStepWidgetArea->layout()->removeWidget(pStepWidget); - pStepWidget->setParent(nullptr); // Remove widget from display - // Note: The Step* widgets are deleted in the Step* dtors. -} - - -void WorkflowQtWidget::setForwardButtonState(ButtonState pState, const QString& pText) -{ - setButtonState(mForwardButton, pState, pText.isEmpty() ? tr("Next") : pText); -} - - -void WorkflowQtWidget::setCancelButtonState(ButtonState pState) -{ - setButtonState(mCancelButton, pState, tr("Cancel")); -} - - -void WorkflowQtWidget::onCancelButtonClicked() -{ - setCancelButtonState(ButtonState::DISABLED); - Q_EMIT fireUserCancelled(); -} - - -void WorkflowQtWidget::setButtonState(QAbstractButton* pButton, ButtonState pState, const QString& pText) -{ - pButton->setText(pText); - - switch (pState) - { - case ButtonState::ENABLED: - pButton->setVisible(true); - pButton->setEnabled(true); - break; - - case ButtonState::DISABLED: - pButton->setVisible(true); - pButton->setEnabled(false); - break; - - case ButtonState::HIDDEN: - pButton->setVisible(false); - break; - - case ButtonState::FOCUSSED: - pButton->setVisible(true); - pButton->setEnabled(true); - focusForwardButton(); - break; - } -} - - -void WorkflowQtWidget::focusForwardButton() -{ - mForwardButton->setAutoDefault(true); - mForwardButton->setDefault(true); - mForwardButton->setFocus(); -} diff --git a/src/widget/workflow/WorkflowQtWidget.h b/src/widget/workflow/WorkflowQtWidget.h deleted file mode 100644 index 5545e59..0000000 --- a/src/widget/workflow/WorkflowQtWidget.h +++ /dev/null @@ -1,65 +0,0 @@ -/*! - * \brief Base class for workflow Qt GUI widgets. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include -#include - -#include "step/StepGui.h" - -class QVBoxLayout; - -namespace governikus -{ - -class WorkflowQtWidget - : public QWidget -{ - Q_OBJECT - - public: - WorkflowQtWidget(QWidget* pParent = nullptr); - virtual ~WorkflowQtWidget(); - - QWidget* getStepWidgetArea() const - { - return mStepWidgetArea; - } - - - void addStepWidget(QWidget* widget); - void removeStepWidget(QWidget* widget); - - Q_SIGNALS: - /*! - * This signal is sent when the user presses the "Cancel" button. - */ - void fireUserCancelled(); - - /*! - * This signal is sent when the user presses the "Continue" button. - */ - void forwardStep(); - - public Q_SLOTS: - void onCancelButtonClicked(); - void setForwardButtonState(ButtonState pState, const QString& pText = QString()); - void setCancelButtonState(ButtonState pState); - - private: - void setButtonState(QAbstractButton* pButton, ButtonState pState, const QString& pText); - void focusForwardButton(); - - private: - QVBoxLayout* mMainLayout; - QWidget* mStepWidgetArea; - QPushButton* mCancelButton; - QPushButton* mForwardButton; -}; - -} /* namespace governikus */ diff --git a/src/widget/workflow/WorkflowSelfInfoQtGui.cpp b/src/widget/workflow/WorkflowSelfInfoQtGui.cpp deleted file mode 100644 index 815689e..0000000 --- a/src/widget/workflow/WorkflowSelfInfoQtGui.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "WorkflowSelfInfoQtGui.h" - -#include "AppSettings.h" -#include "generic/GuiUtils.h" -#include "states/FinalState.h" -#include "states/StateDidAuthenticateEac1.h" -#include "states/StateDidAuthenticateEac2.h" -#include "states/StateEditAccessRights.h" -#include "states/StateEstablishPaceCan.h" -#include "states/StateEstablishPacePin.h" -#include "states/StateEstablishPacePuk.h" -#include "states/StateLoadTcTokenUrl.h" -#include "states/StateSelectReader.h" -#include "states/StateTransmit.h" -#include "states/StateWriteHistory.h" -#include "step/AuthenticateStepsWidget.h" -#include "step/StepAdviseUserToRemoveCardGui.h" -#include "step/StepAuthenticationEac1Gui.h" -#include "step/StepChooseCardGui.h" -#include "step/StepErrorGui.h" -#include "step/StepProcessingGui.h" -#include "step/StepShowSelfAuthenticationDataGui.h" -#include "workflow/WorkflowQtWidget.h" - - -using namespace governikus; - - -WorkflowSelfInfoQtGui::WorkflowSelfInfoQtGui(const QSharedPointer& pContext, AppQtMainWidget* const pParentWidget) - : GenericWorkflowGui(pContext, pParentWidget, pParentWidget->getAuthenticationWorkflowWidget()) - , mCanEntered(false) - , mAuthenticateStepsWidget(pParentWidget->findChild()) - , mAdviseUserToRemoveCardGui(new StepAdviseUserToRemoveCardGui(mContext, mParentWidget)) - , mDidAuthenticateGui(new StepAuthenticationEac1Gui(mContext, mAuthenticateStepsWidget)) - , mChooseCardGui(new StepChooseCardGui(mContext, mAuthenticateStepsWidget)) - , mErrorGui(new StepErrorGui(mContext, mParentWidget)) - , mProcessingGui(new StepProcessingGui(mContext, mAuthenticateStepsWidget)) - , mShowSelfAuthenticationDataGui(new StepShowSelfAuthenticationDataGui(mContext, mAuthenticateStepsWidget)) -{ - Q_ASSERT(mAuthenticateStepsWidget != nullptr); - connect(mWidget, &WorkflowQtWidget::fireUserCancelled, this, &WorkflowGui::fireUserCancelled); - connect(mWidget, &WorkflowQtWidget::forwardStep, this, &WorkflowSelfInfoQtGui::onForwardStep); -} - - -WorkflowSelfInfoQtGui::~WorkflowSelfInfoQtGui() -{ -} - - -void WorkflowSelfInfoQtGui::activate() -{ - activateStepUi(mProcessingGui); - mParentWidget->workflowActivated(WorkflowWidgetParent::SelfAuthentication, tr("Identify")); - connect(mContext.data(), &SelfAuthContext::fireStateChanged, this, &WorkflowSelfInfoQtGui::onStateChanged); -} - - -void WorkflowSelfInfoQtGui::deactivate() -{ - mParentWidget->workflowDeactivated(); -} - - -bool WorkflowSelfInfoQtGui::verifyAbortWorkflow() -{ - // not really necessary to notify the user - return true; -} - - -void WorkflowSelfInfoQtGui::onForwardStep() -{ - if (mStepGui) - { - mStepGui->forwardStep(); - } -} - - -void WorkflowSelfInfoQtGui::onStateChanged(const QString& pNewState) -{ - if (mContext->getStatus().isError() && !mContext->isErrorReportedToUser()) - { - if (!mContext->getStatus().isCancellationByUser()) - { - activateStepUi(mErrorGui); - mErrorGui->reportError(); - } - mContext->setErrorReportedToUser(); - } - - - bool approveNewState = true; - if (AbstractState::isState(pNewState)) - { - GeneralSettings& settings = AppSettings::getInstance().getGeneralSettings(); - approveNewState = !settings.isTransportPinReminder(); - } - else if (AbstractState::isState(pNewState)) - { - approveNewState = false; - activateStepUi(mDidAuthenticateGui); - mDidAuthenticateGui->setState(StepAuthenticationEac1Widget::State::EDIT_CHAT); - } - else if (AbstractState::isState(pNewState)) - { - mContext->setStateApproved(true); - activateStepUi(mChooseCardGui); - return; - } - else if (AbstractState::isState(pNewState) || AbstractState::isState(pNewState)) - { - if (AbstractState::isState(pNewState)) - { - approveNewState = !mContext->getCardConnection()->getReaderInfo().isBasicReader(); - mCanEntered = true; - } - else if (AbstractState::isState(pNewState)) - { - // PIN entry after CAN entry is done without user interaction - approveNewState = mCanEntered || !mContext->getCardConnection()->getReaderInfo().isBasicReader(); - } - activateStepUi(mDidAuthenticateGui); - mDidAuthenticateGui->setState(StepAuthenticationEac1Widget::State::ENTER_PIN); - if (mContext->getLastPaceResult() != CardReturnCode::OK) - { - mDidAuthenticateGui->incorrectPinError(); - } - } - else if (AbstractState::isState(pNewState)) - { - approveNewState = false; - Q_EMIT mContext->fireCancelWorkflow(); - if (GuiUtils::showWrongPinBlockedDialog(mWidget)) - { - mContext->setReaderName(QString()); - mParentWidget->switchToPinSettingsAfterWorkflow(); - } - } - else if (AbstractState::isState(pNewState)) - { - mDidAuthenticateGui->setState(StepAuthenticationEac1Widget::State::AUTHENTICATING_ESERVICE); - } - else if (AbstractState::isState(pNewState)) - { - mDidAuthenticateGui->setState(StepAuthenticationEac1Widget::State::AUTHENTICATING_CARD); - } - else if (AbstractState::isState(pNewState)) - { - mDidAuthenticateGui->setState(StepAuthenticationEac1Widget::State::READING_CARD_DATA); - } - else if (AbstractState::isState(pNewState) && mDidAuthenticateGui->isActive()) - { - mDidAuthenticateGui->setState(StepAuthenticationEac1Widget::State::FINISHED); - } - else if (AbstractState::isState(pNewState)) - { - activateStepUi(mAdviseUserToRemoveCardGui); - if (mContext->getStatus().isNoError()) - { - approveNewState = false; - activateStepUi(mShowSelfAuthenticationDataGui); - } - } - mContext->setStateApproved(approveNewState); -} diff --git a/src/widget/workflow/WorkflowSelfInfoQtGui.h b/src/widget/workflow/WorkflowSelfInfoQtGui.h deleted file mode 100644 index 462f6d2..0000000 --- a/src/widget/workflow/WorkflowSelfInfoQtGui.h +++ /dev/null @@ -1,51 +0,0 @@ -/*! - * \brief Qt widget based WorkflowSelfInfoUi implementation. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "context/SelfAuthContext.h" -#include "GenericWorkflowGui.h" - -namespace governikus -{ - -class AuthenticateStepsWidget; -class StepAdviseUserToRemoveCardGui; -class StepShowSelfAuthenticationDataGui; -class StepAuthenticationEac1Gui; -class StepChooseCardGui; -class StepErrorGui; -class StepProcessingGui; - -class WorkflowSelfInfoQtGui - : public GenericWorkflowGui -{ - Q_OBJECT - - private: - bool mCanEntered; - AuthenticateStepsWidget* mAuthenticateStepsWidget; - QSharedPointer mAdviseUserToRemoveCardGui; - QSharedPointer mDidAuthenticateGui; - QSharedPointer mChooseCardGui; - QSharedPointer mErrorGui; - QSharedPointer mProcessingGui; - QSharedPointer mShowSelfAuthenticationDataGui; - - private Q_SLOTS: - void onForwardStep(); - void onStateChanged(const QString& pNewState); - - public: - WorkflowSelfInfoQtGui(const QSharedPointer& pContext, AppQtMainWidget* const pParentWidget); - virtual ~WorkflowSelfInfoQtGui() override; - - virtual void activate() override; - virtual void deactivate() override; - virtual bool verifyAbortWorkflow() override; -}; - -} /* namespace governikus */ diff --git a/src/widget/workflow/WorkflowWidgetParent.h b/src/widget/workflow/WorkflowWidgetParent.h deleted file mode 100644 index 69c71ac..0000000 --- a/src/widget/workflow/WorkflowWidgetParent.h +++ /dev/null @@ -1,21 +0,0 @@ -/*! - * \brief Enum identifying the containers in the application GUI which can be - * parent to a workflow widget. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -namespace governikus -{ - -enum class WorkflowWidgetParent -{ - Authentication, - SettingsChangePin, - SelfAuthentication -}; - - -} /* namespace governikus */ diff --git a/src/widgetDesignerPlugin/CMakeLists.txt b/src/widgetDesignerPlugin/CMakeLists.txt deleted file mode 100644 index 40a4b5d..0000000 --- a/src/widgetDesignerPlugin/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -ADD_PLATFORM_LIBRARY(AusweisAppWidgetDesignerPlugin) - -TARGET_LINK_LIBRARIES(AusweisAppWidgetDesignerPlugin Qt5::Core Qt5::UiPlugin AusweisAppWidget) diff --git a/src/widgetDesignerPlugin/GovernikusPluginCollection.cpp b/src/widgetDesignerPlugin/GovernikusPluginCollection.cpp deleted file mode 100644 index e17d835..0000000 --- a/src/widgetDesignerPlugin/GovernikusPluginCollection.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "GovernikusPluginCollection.h" - -#include "plugins/GeneralSettingsWidgetDesignerPlugin.h" -#include "plugins/HistoryWidgetDesignerPlugin.h" -#include "plugins/PinSettingsWidgetDesignerPlugin.h" -#include "plugins/ProviderWidgetDesignerPlugin.h" -#include "plugins/SelfInformationWidgetDesignerPlugin.h" -#include "plugins/SettingsWidgetDesignerPlugin.h" - -using namespace governikus; - - -GovernikusPluginCollection::GovernikusPluginCollection(QObject* pParent) - : QObject(pParent) -{ - mWidgets += new SelfInformationWidgetDesignerPlugin(this); - - mWidgets += new ProviderWidgetDesignerPlugin(this); - - mWidgets += new HistoryWidgetDesignerPlugin(this); - - mWidgets += new GeneralSettingsWidgetDesignerPlugin(this); - mWidgets += new PinSettingsWidgetDesignerPlugin(this); - mWidgets += new SettingsWidgetDesignerPlugin(this); -} - - -QList GovernikusPluginCollection::customWidgets() const -{ - return mWidgets; -} diff --git a/src/widgetDesignerPlugin/GovernikusPluginCollection.h b/src/widgetDesignerPlugin/GovernikusPluginCollection.h deleted file mode 100644 index 5a5412f..0000000 --- a/src/widgetDesignerPlugin/GovernikusPluginCollection.h +++ /dev/null @@ -1,27 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include - -namespace governikus -{ -class GovernikusPluginCollection - : public QObject - , public QDesignerCustomWidgetCollectionInterface -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QDesignerCustomWidgetCollectionInterface") - Q_INTERFACES(QDesignerCustomWidgetCollectionInterface) - - public: - GovernikusPluginCollection(QObject* pParent = 0); - QList customWidgets() const override; - - private: - QList mWidgets; -}; - -} diff --git a/src/widgetDesignerPlugin/README.rst b/src/widgetDesignerPlugin/README.rst deleted file mode 100644 index c8ea00c..0000000 --- a/src/widgetDesignerPlugin/README.rst +++ /dev/null @@ -1,12 +0,0 @@ -widgetDesignerPlugin -#################### - -This is a plugin for Qt Designer. It allows the visualization of custom Governikus widgets. -In order to be loaded, the plugin needs to be placed in the Designers plugin path. - -It can be placed there in the following manner: - -:: - - cd /usr/lib/qt/plugins/designer - ln -s /home/user/AusweisApp2.build/src/widgetDesignerPlugin/libAusweisAppWidgetDesignerPlugin.so diff --git a/src/widgetDesignerPlugin/plugins/GeneralSettingsWidgetDesignerPlugin.cpp b/src/widgetDesignerPlugin/plugins/GeneralSettingsWidgetDesignerPlugin.cpp deleted file mode 100644 index 149fb1c..0000000 --- a/src/widgetDesignerPlugin/plugins/GeneralSettingsWidgetDesignerPlugin.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "GeneralSettingsWidgetDesignerPlugin.h" - -#include "GeneralSettingsWidget.h" - -using namespace governikus; - - -GeneralSettingsWidgetDesignerPlugin::GeneralSettingsWidgetDesignerPlugin(QObject* pParent) - : QObject(pParent) - , mInitialized(false) -{ - -} - - -QWidget* GeneralSettingsWidgetDesignerPlugin::createWidget(QWidget* pParent) -{ - return new GeneralSettingsWidget(pParent); -} - - -QString GeneralSettingsWidgetDesignerPlugin::domXml() const -{ - return QStringLiteral( - "\n" - " \n" - " \n" - "\n"); -} - - -QString GeneralSettingsWidgetDesignerPlugin::includeFile() const -{ - return QStringLiteral("GeneralSettingsWidget.h"); -} - - -QString GeneralSettingsWidgetDesignerPlugin::name() const -{ - return QStringLiteral("governikus::GeneralSettingsWidget"); -} - - -QString GeneralSettingsWidgetDesignerPlugin::group() const -{ - return QStringLiteral("Governikus"); -} - - -QString GeneralSettingsWidgetDesignerPlugin::toolTip() const -{ - return QString(); -} - - -QString GeneralSettingsWidgetDesignerPlugin::whatsThis() const -{ - return QString(); -} - - -bool GeneralSettingsWidgetDesignerPlugin::isContainer() const -{ - return false; -} - - -bool GeneralSettingsWidgetDesignerPlugin::isInitialized() const -{ - return mInitialized; -} - - -QIcon GeneralSettingsWidgetDesignerPlugin::icon() const -{ - return QIcon(); -} - - -void GeneralSettingsWidgetDesignerPlugin::initialize(QDesignerFormEditorInterface*) -{ - if (mInitialized) - { - return; - } - - mInitialized = true; -} diff --git a/src/widgetDesignerPlugin/plugins/GeneralSettingsWidgetDesignerPlugin.h b/src/widgetDesignerPlugin/plugins/GeneralSettingsWidgetDesignerPlugin.h deleted file mode 100644 index 208c39d..0000000 --- a/src/widgetDesignerPlugin/plugins/GeneralSettingsWidgetDesignerPlugin.h +++ /dev/null @@ -1,38 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include - -namespace governikus -{ - -class GeneralSettingsWidgetDesignerPlugin - : public QObject - , public QDesignerCustomWidgetInterface -{ - Q_OBJECT - Q_INTERFACES(QDesignerCustomWidgetInterface) - - private: - bool mInitialized; - - public: - GeneralSettingsWidgetDesignerPlugin(QObject* pParent = nullptr); - - QWidget* createWidget(QWidget* pParent) override; - QString domXml() const override; - QString includeFile() const override; - QString name() const override; - QString group() const override; - QString toolTip() const override; - QString whatsThis() const override; - bool isContainer() const override; - bool isInitialized() const override; - QIcon icon() const override; - void initialize(QDesignerFormEditorInterface*) override; -}; - -} diff --git a/src/widgetDesignerPlugin/plugins/HistoryWidgetDesignerPlugin.cpp b/src/widgetDesignerPlugin/plugins/HistoryWidgetDesignerPlugin.cpp deleted file mode 100644 index 5b0fcbb..0000000 --- a/src/widgetDesignerPlugin/plugins/HistoryWidgetDesignerPlugin.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "HistoryWidgetDesignerPlugin.h" - -#include "HistoryWidget.h" - -using namespace governikus; - - -HistoryWidgetDesignerPlugin::HistoryWidgetDesignerPlugin(QObject* pParent) - : QObject(pParent) - , mInitialized(false) -{ - -} - - -QWidget* HistoryWidgetDesignerPlugin::createWidget(QWidget* pParent) -{ - return new HistoryWidget(pParent); -} - - -QString HistoryWidgetDesignerPlugin::domXml() const -{ - return QStringLiteral( - "\n" - " \n" - " \n" - "\n"); -} - - -QString HistoryWidgetDesignerPlugin::includeFile() const -{ - return QStringLiteral("HistoryWidget.h"); -} - - -QString HistoryWidgetDesignerPlugin::name() const -{ - return QStringLiteral("governikus::HistoryWidget"); -} - - -QString HistoryWidgetDesignerPlugin::group() const -{ - return QStringLiteral("Governikus"); -} - - -QString HistoryWidgetDesignerPlugin::toolTip() const -{ - return QString(); -} - - -QString HistoryWidgetDesignerPlugin::whatsThis() const -{ - return QString(); -} - - -bool HistoryWidgetDesignerPlugin::isContainer() const -{ - return false; -} - - -bool HistoryWidgetDesignerPlugin::isInitialized() const -{ - return mInitialized; -} - - -QIcon HistoryWidgetDesignerPlugin::icon() const -{ - return QIcon(); -} - - -void HistoryWidgetDesignerPlugin::initialize(QDesignerFormEditorInterface*) -{ - if (mInitialized) - { - return; - } - - mInitialized = true; -} diff --git a/src/widgetDesignerPlugin/plugins/HistoryWidgetDesignerPlugin.h b/src/widgetDesignerPlugin/plugins/HistoryWidgetDesignerPlugin.h deleted file mode 100644 index cacfe87..0000000 --- a/src/widgetDesignerPlugin/plugins/HistoryWidgetDesignerPlugin.h +++ /dev/null @@ -1,38 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include - -namespace governikus -{ - -class HistoryWidgetDesignerPlugin - : public QObject - , public QDesignerCustomWidgetInterface -{ - Q_OBJECT - Q_INTERFACES(QDesignerCustomWidgetInterface) - - private: - bool mInitialized; - - public: - HistoryWidgetDesignerPlugin(QObject* pParent = nullptr); - - QWidget* createWidget(QWidget* pParent) override; - QString domXml() const override; - QString includeFile() const override; - QString name() const override; - QString group() const override; - QString toolTip() const override; - QString whatsThis() const override; - bool isContainer() const override; - bool isInitialized() const override; - QIcon icon() const override; - void initialize(QDesignerFormEditorInterface*) override; -}; - -} diff --git a/src/widgetDesignerPlugin/plugins/PinSettingsWidgetDesignerPlugin.cpp b/src/widgetDesignerPlugin/plugins/PinSettingsWidgetDesignerPlugin.cpp deleted file mode 100644 index 86a7484..0000000 --- a/src/widgetDesignerPlugin/plugins/PinSettingsWidgetDesignerPlugin.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "PinSettingsWidgetDesignerPlugin.h" - -#include "PinSettingsWidget.h" - -using namespace governikus; - - -PinSettingsWidgetDesignerPlugin::PinSettingsWidgetDesignerPlugin(QObject* pParent) - : QObject(pParent) - , mInitialized(false) -{ - -} - - -QWidget* PinSettingsWidgetDesignerPlugin::createWidget(QWidget* pParent) -{ - return new PinSettingsWidget(pParent); -} - - -QString PinSettingsWidgetDesignerPlugin::domXml() const -{ - return QStringLiteral( - "\n" - " \n" - " \n" - "\n"); -} - - -QString PinSettingsWidgetDesignerPlugin::includeFile() const -{ - return QStringLiteral("PinSettingsWidget.h"); -} - - -QString PinSettingsWidgetDesignerPlugin::name() const -{ - return QStringLiteral("governikus::PinSettingsWidget"); -} - - -QString PinSettingsWidgetDesignerPlugin::group() const -{ - return QStringLiteral("Governikus"); -} - - -QString PinSettingsWidgetDesignerPlugin::toolTip() const -{ - return QString(); -} - - -QString PinSettingsWidgetDesignerPlugin::whatsThis() const -{ - return QString(); -} - - -bool PinSettingsWidgetDesignerPlugin::isContainer() const -{ - return false; -} - - -bool PinSettingsWidgetDesignerPlugin::isInitialized() const -{ - return mInitialized; -} - - -QIcon PinSettingsWidgetDesignerPlugin::icon() const -{ - return QIcon(); -} - - -void PinSettingsWidgetDesignerPlugin::initialize(QDesignerFormEditorInterface*) -{ - if (mInitialized) - { - return; - } - - mInitialized = true; -} diff --git a/src/widgetDesignerPlugin/plugins/PinSettingsWidgetDesignerPlugin.h b/src/widgetDesignerPlugin/plugins/PinSettingsWidgetDesignerPlugin.h deleted file mode 100644 index 9077f8d..0000000 --- a/src/widgetDesignerPlugin/plugins/PinSettingsWidgetDesignerPlugin.h +++ /dev/null @@ -1,38 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include - -namespace governikus -{ - -class PinSettingsWidgetDesignerPlugin - : public QObject - , public QDesignerCustomWidgetInterface -{ - Q_OBJECT - Q_INTERFACES(QDesignerCustomWidgetInterface) - - private: - bool mInitialized; - - public: - PinSettingsWidgetDesignerPlugin(QObject* pParent = nullptr); - - QWidget* createWidget(QWidget* pParent) override; - QString domXml() const override; - QString includeFile() const override; - QString name() const override; - QString group() const override; - QString toolTip() const override; - QString whatsThis() const override; - bool isContainer() const override; - bool isInitialized() const override; - QIcon icon() const override; - void initialize(QDesignerFormEditorInterface*) override; -}; - -} diff --git a/src/widgetDesignerPlugin/plugins/ProviderWidgetDesignerPlugin.cpp b/src/widgetDesignerPlugin/plugins/ProviderWidgetDesignerPlugin.cpp deleted file mode 100644 index 1ef1dcb..0000000 --- a/src/widgetDesignerPlugin/plugins/ProviderWidgetDesignerPlugin.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "ProviderWidgetDesignerPlugin.h" - -#include "ProviderWidget.h" - -using namespace governikus; - - -ProviderWidgetDesignerPlugin::ProviderWidgetDesignerPlugin(QObject* pParent) - : QObject(pParent) - , mInitialized(false) -{ - -} - - -QWidget* ProviderWidgetDesignerPlugin::createWidget(QWidget* pParent) -{ - return new ProviderWidget(pParent); -} - - -QString ProviderWidgetDesignerPlugin::domXml() const -{ - return QStringLiteral( - "\n" - " \n" - " \n" - "\n"); -} - - -QString ProviderWidgetDesignerPlugin::includeFile() const -{ - return QStringLiteral("ProviderWidget.h"); -} - - -QString ProviderWidgetDesignerPlugin::name() const -{ - return QStringLiteral("governikus::ProviderWidget"); -} - - -QString ProviderWidgetDesignerPlugin::group() const -{ - return QStringLiteral("Governikus"); -} - - -QString ProviderWidgetDesignerPlugin::toolTip() const -{ - return QString(); -} - - -QString ProviderWidgetDesignerPlugin::whatsThis() const -{ - return QString(); -} - - -bool ProviderWidgetDesignerPlugin::isContainer() const -{ - return false; -} - - -bool ProviderWidgetDesignerPlugin::isInitialized() const -{ - return mInitialized; -} - - -QIcon ProviderWidgetDesignerPlugin::icon() const -{ - return QIcon(); -} - - -void ProviderWidgetDesignerPlugin::initialize(QDesignerFormEditorInterface*) -{ - if (mInitialized) - { - return; - } - - mInitialized = true; -} diff --git a/src/widgetDesignerPlugin/plugins/ProviderWidgetDesignerPlugin.h b/src/widgetDesignerPlugin/plugins/ProviderWidgetDesignerPlugin.h deleted file mode 100644 index 970b19a..0000000 --- a/src/widgetDesignerPlugin/plugins/ProviderWidgetDesignerPlugin.h +++ /dev/null @@ -1,38 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include - -namespace governikus -{ - -class ProviderWidgetDesignerPlugin - : public QObject - , public QDesignerCustomWidgetInterface -{ - Q_OBJECT - Q_INTERFACES(QDesignerCustomWidgetInterface) - - private: - bool mInitialized; - - public: - ProviderWidgetDesignerPlugin(QObject* pParent = nullptr); - - QWidget* createWidget(QWidget* pParent) override; - QString domXml() const override; - QString includeFile() const override; - QString name() const override; - QString group() const override; - QString toolTip() const override; - QString whatsThis() const override; - bool isContainer() const override; - bool isInitialized() const override; - QIcon icon() const override; - void initialize(QDesignerFormEditorInterface*) override; -}; - -} diff --git a/src/widgetDesignerPlugin/plugins/SelfInformationWidgetDesignerPlugin.cpp b/src/widgetDesignerPlugin/plugins/SelfInformationWidgetDesignerPlugin.cpp deleted file mode 100644 index f4a7101..0000000 --- a/src/widgetDesignerPlugin/plugins/SelfInformationWidgetDesignerPlugin.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "SelfInformationWidgetDesignerPlugin.h" - -#include "SelfInformationWidget.h" - -using namespace governikus; - - -SelfInformationWidgetDesignerPlugin::SelfInformationWidgetDesignerPlugin(QObject* pParent) - : QObject(pParent) - , mInitialized(false) -{ - -} - - -QWidget* SelfInformationWidgetDesignerPlugin::createWidget(QWidget* pParent) -{ - return new SelfInformationWidget(pParent); -} - - -QString SelfInformationWidgetDesignerPlugin::domXml() const -{ - return QStringLiteral( - "\n" - " \n" - " \n" - "\n"); -} - - -QString SelfInformationWidgetDesignerPlugin::includeFile() const -{ - return QStringLiteral("SelfInformationWidget.h"); -} - - -QString SelfInformationWidgetDesignerPlugin::name() const -{ - return QStringLiteral("governikus::SelfInformationWidget"); -} - - -QString SelfInformationWidgetDesignerPlugin::group() const -{ - return QStringLiteral("Governikus"); -} - - -QString SelfInformationWidgetDesignerPlugin::toolTip() const -{ - return QString(); -} - - -QString SelfInformationWidgetDesignerPlugin::whatsThis() const -{ - return QString(); -} - - -bool SelfInformationWidgetDesignerPlugin::isContainer() const -{ - return false; -} - - -bool SelfInformationWidgetDesignerPlugin::isInitialized() const -{ - return mInitialized; -} - - -QIcon SelfInformationWidgetDesignerPlugin::icon() const -{ - return QIcon(); -} - - -void SelfInformationWidgetDesignerPlugin::initialize(QDesignerFormEditorInterface*) -{ - if (mInitialized) - { - return; - } - - mInitialized = true; -} diff --git a/src/widgetDesignerPlugin/plugins/SelfInformationWidgetDesignerPlugin.h b/src/widgetDesignerPlugin/plugins/SelfInformationWidgetDesignerPlugin.h deleted file mode 100644 index 6c8937a..0000000 --- a/src/widgetDesignerPlugin/plugins/SelfInformationWidgetDesignerPlugin.h +++ /dev/null @@ -1,38 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include - -namespace governikus -{ - -class SelfInformationWidgetDesignerPlugin - : public QObject - , public QDesignerCustomWidgetInterface -{ - Q_OBJECT - Q_INTERFACES(QDesignerCustomWidgetInterface) - - private: - bool mInitialized; - - public: - SelfInformationWidgetDesignerPlugin(QObject* pParent = nullptr); - - QWidget* createWidget(QWidget* pParent) override; - QString domXml() const override; - QString includeFile() const override; - QString name() const override; - QString group() const override; - QString toolTip() const override; - QString whatsThis() const override; - bool isContainer() const override; - bool isInitialized() const override; - QIcon icon() const override; - void initialize(QDesignerFormEditorInterface*) override; -}; - -} diff --git a/src/widgetDesignerPlugin/plugins/SettingsWidgetDesignerPlugin.cpp b/src/widgetDesignerPlugin/plugins/SettingsWidgetDesignerPlugin.cpp deleted file mode 100644 index 76cafce..0000000 --- a/src/widgetDesignerPlugin/plugins/SettingsWidgetDesignerPlugin.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "SettingsWidgetDesignerPlugin.h" - -#include "SettingsWidget.h" - -using namespace governikus; - - -SettingsWidgetDesignerPlugin::SettingsWidgetDesignerPlugin(QObject* pParent) - : QObject(pParent) - , mInitialized(false) -{ - -} - - -QWidget* SettingsWidgetDesignerPlugin::createWidget(QWidget* pParent) -{ - return new SettingsWidget(pParent); -} - - -QString SettingsWidgetDesignerPlugin::domXml() const -{ - return QStringLiteral( - "\n" - " \n" - " \n" - "\n"); -} - - -QString SettingsWidgetDesignerPlugin::includeFile() const -{ - return QStringLiteral("SettingsWidget.h"); -} - - -QString SettingsWidgetDesignerPlugin::name() const -{ - return QStringLiteral("governikus::SettingsWidget"); -} - - -QString SettingsWidgetDesignerPlugin::group() const -{ - return QStringLiteral("Governikus"); -} - - -QString SettingsWidgetDesignerPlugin::toolTip() const -{ - return QString(); -} - - -QString SettingsWidgetDesignerPlugin::whatsThis() const -{ - return QString(); -} - - -bool SettingsWidgetDesignerPlugin::isContainer() const -{ - return false; -} - - -bool SettingsWidgetDesignerPlugin::isInitialized() const -{ - return mInitialized; -} - - -QIcon SettingsWidgetDesignerPlugin::icon() const -{ - return QIcon(); -} - - -void SettingsWidgetDesignerPlugin::initialize(QDesignerFormEditorInterface*) -{ - if (mInitialized) - { - return; - } - - mInitialized = true; -} diff --git a/src/widgetDesignerPlugin/plugins/SettingsWidgetDesignerPlugin.h b/src/widgetDesignerPlugin/plugins/SettingsWidgetDesignerPlugin.h deleted file mode 100644 index 211d727..0000000 --- a/src/widgetDesignerPlugin/plugins/SettingsWidgetDesignerPlugin.h +++ /dev/null @@ -1,38 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include - -namespace governikus -{ - -class SettingsWidgetDesignerPlugin - : public QObject - , public QDesignerCustomWidgetInterface -{ - Q_OBJECT - Q_INTERFACES(QDesignerCustomWidgetInterface) - - private: - bool mInitialized; - - public: - SettingsWidgetDesignerPlugin(QObject* pParent = nullptr); - - QWidget* createWidget(QWidget* pParent) override; - QString domXml() const override; - QString includeFile() const override; - QString name() const override; - QString group() const override; - QString toolTip() const override; - QString whatsThis() const override; - bool isContainer() const override; - bool isInitialized() const override; - QIcon icon() const override; - void initialize(QDesignerFormEditorInterface*) override; -}; - -} diff --git a/src/windows.rc b/src/windows.rc index 75cef6b..a303371 100644 --- a/src/windows.rc +++ b/src/windows.rc @@ -6,6 +6,7 @@ #define COPYRIGHT "2014-2017 " VENDOR IDR_MAINFRAME ICON "..\\resources\\images\\npa.ico" +IDI_ICON1 ICON DISCARDABLE "..\\resources\\images\\npa.ico" 1 VERSIONINFO FILEVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_PATCH,VERSION_TWEAK diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8b5d7ca..4ebf8cd 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -12,6 +12,8 @@ ENDFUNCTION() FUNCTION(GET_TEST_CMDLINE cmdline testname) + cmake_parse_arguments(_PARAM "" "" "SELECTORS" ${ARGN}) + IF(NOT WIN32 AND ( "${testname}" MATCHES "export" @@ -21,22 +23,55 @@ FUNCTION(GET_TEST_CMDLINE cmdline testname) SET(PLATFORM -platform offscreen) ENDIF() - SET(${cmdline} ${PLATFORM} -v2 -o ${CMAKE_CURRENT_BINARY_DIR}/results.${testname}.log.xml,xml PARENT_SCOPE) + FOREACH(SELECTOR ${_PARAM_SELECTORS}) + SET(testname "${testname}_${SELECTOR}") + ENDFOREACH() + + SET(${cmdline} ${PLATFORM} -v2 -o ${CMAKE_CURRENT_BINARY_DIR}/results.${testname}.log.xml,xml -o -,txt PARENT_SCOPE) +ENDFUNCTION() + + +FUNCTION(ADD_QML_TEST _sourcefile) + cmake_parse_arguments(_PARAM "" "" "SELECTORS" ${ARGN}) + + EXTRACT_TESTNAME(TESTNAME ${_sourcefile}) + GET_TEST_CMDLINE(CMD_PARAMS ${TESTNAME} SELECTORS ${_PARAM_SELECTORS}) + SET(CMD $ ${CMD_PARAMS} -input ${sourcefile} -import "${_import_path}") + SET(ENV_FAIL_ON_WARNING "QT_FATAL_WARNINGS=true") + # Emasculate ASAN since QtQuick causes problems + SET(ENV_EMASCULATE_ASAN "ASAN_OPTIONS=detect_leaks=0,new_delete_type_mismatch=0") + + IF(_PARAM_SELECTORS) + FOREACH(_PARAM_SELECTOR_LIST_ENTRY ${_PARAM_SELECTORS}) + SET(TESTNAME ${TESTNAME}_${_PARAM_SELECTOR_LIST_ENTRY}) + SET(CMD ${CMD} -file-selector ${_PARAM_SELECTOR_LIST_ENTRY}) + ENDFOREACH() + ENDIF() + + ADD_TEST(NAME ${TESTNAME} COMMAND ${CMD}) + SET_TESTS_PROPERTIES(${TESTNAME} PROPERTIES LABELS "qml" ENVIRONMENT "${ENV_FAIL_ON_WARNING}" ENVIRONMENT "${ENV_EMASCULATE_ASAN}") ENDFUNCTION() FUNCTION(ADD_QML_TEST_FILES _import_path) + cmake_parse_arguments(_PARAM "" "" "SELECTORS" ${ARGN}) + IF(NOT BSD AND (NOT Qt5Core_VERSION VERSION_LESS 5.11.0 OR QT_VENDOR STREQUAL "Governikus")) + SET(HAS_FILE_SELECTOR_PATCH TRUE) + ELSE() + SET(HAS_FILE_SELECTOR_PATCH FALSE) + ENDIF() + FILE(GLOB_RECURSE TEST_SUBFILES "${CMAKE_CURRENT_SOURCE_DIR}/test_*.qml") FOREACH(sourcefile ${TEST_SUBFILES}) - EXTRACT_TESTNAME(TESTNAME ${sourcefile}) - GET_TEST_CMDLINE(CMD_PARAMS ${TESTNAME}) - ADD_TEST(NAME ${TESTNAME} COMMAND $ ${CMD_PARAMS} -input ${sourcefile} -import "${_import_path}") - SET_TESTS_PROPERTIES(${TESTNAME} PROPERTIES LABELS "qml") + IF(NOT _PARAM_SELECTORS) + ADD_QML_TEST(${sourcefile}) + ELSEIF(HAS_FILE_SELECTOR_PATCH) + ADD_QML_TEST(${sourcefile} SELECTORS ${_PARAM_SELECTORS}) + ENDIF() ENDFOREACH() ENDFUNCTION() ADD_SUBDIRECTORY(helper) ADD_SUBDIRECTORY(qml) -ADD_SUBDIRECTORY(qml_stationary) ADD_SUBDIRECTORY(qt) diff --git a/test/fixture/card/EstablishPACEChannelOutput.hex b/test/fixture/card/EstablishPaceChannelOutput.hex similarity index 100% rename from test/fixture/card/EstablishPACEChannelOutput.hex rename to test/fixture/card/EstablishPaceChannelOutput.hex diff --git a/test/fixture/card/EstablishPACEChannelOutput2.hex b/test/fixture/card/EstablishPaceChannelOutput2.hex similarity index 100% rename from test/fixture/card/EstablishPACEChannelOutput2.hex rename to test/fixture/card/EstablishPaceChannelOutput2.hex diff --git a/test/fixture/card/EstablishPACEChannelOutput_fromCcid.hex b/test/fixture/card/EstablishPaceChannelOutput_fromCcid.hex similarity index 100% rename from test/fixture/card/EstablishPACEChannelOutput_fromCcid.hex rename to test/fixture/card/EstablishPaceChannelOutput_fromCcid.hex diff --git a/test/fixture/card/EstablishPACEChannelOutput_fromCcid2.hex b/test/fixture/card/EstablishPaceChannelOutput_fromCcid2.hex similarity index 100% rename from test/fixture/card/EstablishPACEChannelOutput_fromCcid2.hex rename to test/fixture/card/EstablishPaceChannelOutput_fromCcid2.hex diff --git a/test/fixture/card/EstablishPACEChannelOutput_wrongPIN.hex b/test/fixture/card/EstablishPaceChannelOutput_wrongPIN.hex similarity index 100% rename from test/fixture/card/EstablishPACEChannelOutput_wrongPIN.hex rename to test/fixture/card/EstablishPaceChannelOutput_wrongPIN.hex diff --git a/test/fixture/core/http/DIDAuthenticateEAC1 b/test/fixture/core/http/DIDAuthenticateEAC1 deleted file mode 100644 index 86826ae..0000000 --- a/test/fixture/core/http/DIDAuthenticateEAC1 +++ /dev/null @@ -1,38 +0,0 @@ -HTTP/1.1 200 OK -Server: Apache-Coyote/1.1 -Content-Type: application/vnd.paos+xml;charset=ISO-8859-1 -Transfer-Encoding: chunked -Date: Fri, 25 Apr 2014 11:19:40 GMT - -18a7 - - - - - - - -58F97CA4 -SDI010 USB Smart Card Reader [Vendor Interface] (21120837205702) 00 01 -0 -E80704007F00070302 -353533313636353638 - - -PIN - -7F218201487F4E8201005F2901004210444544566549444450535430303033367F494F060A04007F0007020202020386410419067885AE3FD9551CD6CC50B72DAD3172A1190188C5FAB7BA968E3E44CB1223580405D000637C556740988C70F56C4F291D226802344A8987D77C95CAAA08485F200E444544454D4F50414130303036307F4C12060904007F0007030102025305000101DB055F25060104000401065F2406010400050106655E732D060904007F0007030103018020B0BD2BFDD7FC7CFAA27D7A87D2E89ACE2F6EE1AB2AD0BD760FF0042C5093C734732D060904007F00070301030280200C6B3CC04540AC815A39484EEDC39AA63DB1C837D73A550B03394C552E9D7D865F374067DD0C144EC8607068BA931F237C44718A821C39249D5E7EC0EAC597B560BC8762904AAF285CAB78E6BCECF86AC73F7C9932E8D302990D35024E6F4CEE6E5140 -7F2181E77F4E81A05F290100420E44455445535465494430303030347F494F060A04007F0007020202020386410422999BD1809A44C9C6B4A2D9EBBA666CEE8181EC739B610B575292F908D07EAD8FAB76CBC044F4D3605776BD97FAD3D8F0FD1EB83FC73E0883C3114B7FC8AF1D5F2010444544566549444450535430303033367F4C12060904007F0007030102025305400513FF875F25060104000400095F24060104000700085F374014066EB0ADABD590528639C4642862071E4BC184D20408603F1DEC960A12A5468AB2C0AA1502B893838CB74A0318D547AC9DC5FFFEC1EC9DB27B9D2A8C63FC38 -7F218201B67F4E82016E5F290100420E44455445535465494430303030327F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A786410474FF63AB838C73C303AC003DFEE95CF8BF55F91E8FEBCB7395D942036E47CF1845EC786EC95BB453AAC288AD023B6067913CF9B63F908F49304E5CFC8B3050DD8701015F200E44455445535465494430303030347F4C12060904007F0007030102025305FC0F13FFFF5F25060102000501015F24060105000501015F37405C035A0611B6C58F0B5261FDD009DECAB7DC7A79482D5248CCA119059B7D82B2157CF0C4A499BCF441EFDD35E294A58C0AF19A34A0762159533285ACF170A505 -7F218201B67F4E82016E5F290100420E44455445535465494430303030317F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7864104096EB58BFD86252238EC2652185C43C3A56C320681A21E37A8E69DDC387C0C5F5513856EFE2FDC656E604893212E29449B365E304605AC5413E75BE31E641F128701015F200E44455445535465494430303030327F4C12060904007F0007030102025305FE0F01FFFF5F25060100000902015F24060103000902015F3740141120A0FDFC011A52F3F72B387A3DC7ACA88B4868D5AE9741780B6FF8A0B49E5F55169A2D298EF5CF95935DCA0C3DF3E9D42DC45F74F2066317154961E6C746 -308203C1060A04007F00070301030101A1270C25446575747363686520506F7374205369676E747275737420756E6420444D444120476D6248A2191317687474703A2F2F7777772E7369676E74727573742E6465A3260C246272656D656E206F6E6C696E6520736572766963657320476D6248202620436F2E204B47A420131E68747470733A2F2F746573742E676F7665726E696B75732D6569642E6465A58201580C820154416E736368726966743A0D0A6272656D656E206F6E6C696E6520736572766963657320476D6248202620436F2E204B470D0A416D2046616C6C7475726D20390D0A3238333539204272656D656E0D0A0D0A452D4D61696C2D416472657373653A0D0A686240626F732D6272656D656E2E64650D0A0D0A5A7765636B20646573204175736C657365766F7267616E67733A0D0A44656D6F6E7374726174696F6E20646573206549442D536572766963650D0A0D0A5A757374C3A46E6469676520446174656E73636875747A61756673696368743A0D0A446965204C616E64657362656175667472616774652066C3BC7220446174656E73636875747A20756E6420496E666F726D6174696F6E736672656968656974206465722046726569656E2048616E73657374616474204272656D656E0D0A41726E647473747261C39F6520310D0A3237353730204272656D6572686176656EA64B134968747470733A2F2F746573742E676F7665726E696B75732D6569642E64653A3434332F417574656E742D44656D6F4170706C69636174696F6E2F5265636569766572536572766C6574A782017A31820176042000583CECA5C96F453E3E69DD6FFB904579A2A23E8EE69B5369B9D1E601C412D504200858AE46EB99F5C77493920301534B2861C2C866174C6355529E4E3C58F66F1304200EAC8777A3C9E72513C310EE20E02D8E5DAF9529DFE689DE1E40E2E6008CE8A204207324E5725B4196C9D333051F84B52ED59A946976F12B68312024B20DAC9A2D2A042088841E28F24848A37384608C35A00FFB049792DF87BE9328B632EB44B6380AD20420CB2B13DC10DE954A452CA6253DACFDBC3DD827AF13FD1B983CAEF47F369A48F90420D2E54E1D26FC5DFC3408609831BBE4CFE3204365604849E7B094623566B54A760420EC8A042FF2D8C4334FA678D24E6A03E628CC880F565722F7D27FC38366C070380420F705317FE1F5DDF52235F11B408442CD32392CCEB44DD6459AA55E587BDC54530420FC4F2684ADD86889B0A55BF6A763F88707A371416EC25620E3F56743DC96B5A40420FED04BA2860895AC678CB9BA09A3F62A810DDB0315A93EF5ECCC9E55035B6B59 -7F4C12060904007F00070301020253050001015B05 -7F4C12060904007F00070301020253050000008000 -672E7315060904007F000703010401530831393936303432357315060904007F00070301040253083230313430343235 - - - - - -0 - diff --git a/test/fixture/core/http/DIDAuthenticateEAC2 b/test/fixture/core/http/DIDAuthenticateEAC2 deleted file mode 100644 index 051a449..0000000 --- a/test/fixture/core/http/DIDAuthenticateEAC2 +++ /dev/null @@ -1,33 +0,0 @@ -HTTP/1.1 200 OK -Server: Apache-Coyote/1.1 -Content-Type: application/vnd.paos+xml;charset=ISO-8859-1 -Transfer-Encoding: chunked -Date: Fri, 25 Apr 2014 11:19:44 GMT - -562 - - - - - - - -58F97CA4 -SDI010 USB Smart Card Reader [Vendor Interface] (21120837205702) 00 01 -0 -E80704007F00070302 -353533313636353638 - - -PIN - - -0458870A9A58B3CDC1F3196F4A8DC2125463FF767F77B933281FF8FA0ED981D8F02F81C690DE4DBED8BDB48C7D2914D4D7D966E33433C21F71814E0050D071A036 -6BCED70AF21C9F2ED9819A7F38CE9BA06F5466215FAAD56388FA23602D52C74F9B457F904C9EFA9BEF1C9E50C1C40DBE5EE6837AE362CE6F06B4457FE74B4CDD - - - - - -0 - diff --git a/test/fixture/core/http/DIDList b/test/fixture/core/http/DIDList deleted file mode 100644 index 12dcf41..0000000 --- a/test/fixture/core/http/DIDList +++ /dev/null @@ -1,27 +0,0 @@ -HTTP/1.1 200 OK -Server: Apache-Coyote/1.1 -Content-Type: application/vnd.paos+xml;charset=ISO-8859-1 -Transfer-Encoding: chunked -Date: Fri, 25 Apr 2014 11:19:40 GMT - -31a - - - - - - - -58F97CA4 -SDI010 USB Smart Card Reader [Vendor Interface] (21120837205702) 00 01 -0 -E80704007F00070302 -353533313636353638 - - - - - - -0 - diff --git a/test/fixture/core/http/Disconnect b/test/fixture/core/http/Disconnect deleted file mode 100644 index 392f6f5..0000000 --- a/test/fixture/core/http/Disconnect +++ /dev/null @@ -1,20 +0,0 @@ -HTTP/1.1 200 OK -Server: Apache-Coyote/1.1 -Content-Type: application/vnd.paos+xml;charset=ISO-8859-1 -Transfer-Encoding: chunked -Date: Fri, 25 Apr 2014 11:19:47 GMT - -1c3 - - - - - - -353533313636353638 - - - - -0 - diff --git a/test/fixture/core/http/InitializeFramework b/test/fixture/core/http/InitializeFramework deleted file mode 100644 index 1529307..0000000 --- a/test/fixture/core/http/InitializeFramework +++ /dev/null @@ -1,18 +0,0 @@ -HTTP/1.1 200 OK -Server: Apache-Coyote/1.1 -Content-Type: application/vnd.paos+xml;charset=ISO-8859-1 -Transfer-Encoding: chunked -Date: Fri, 25 Apr 2014 11:19:40 GMT - -192 - - - - - - - - - -0 - diff --git a/test/fixture/core/http/StartPaosResponse b/test/fixture/core/http/StartPaosResponse deleted file mode 100644 index f46b22f..0000000 --- a/test/fixture/core/http/StartPaosResponse +++ /dev/null @@ -1,22 +0,0 @@ -HTTP/1.1 200 OK -Server: Apache-Coyote/1.1 -Content-Type: application/vnd.paos+xml;charset=ISO-8859-1 -Transfer-Encoding: chunked -Date: Fri, 25 Apr 2014 11:19:47 GMT - -265 - - - - - - - -http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok - - - - - -0 - diff --git a/test/fixture/core/http/Transmit b/test/fixture/core/http/Transmit deleted file mode 100644 index 99ddf71..0000000 --- a/test/fixture/core/http/Transmit +++ /dev/null @@ -1,45 +0,0 @@ -HTTP/1.1 200 OK -Server: Apache-Coyote/1.1 -Content-Type: application/vnd.paos+xml;charset=ISO-8859-1 -Transfer-Encoding: chunked -Date: Fri, 25 Apr 2014 11:19:45 GMT - -aa0 - - - - - - -353533313636353638 - -0CA4040C1D871101E91313250DB6702B2115369CAE9AC09C8E08067123CEBA49BF5000 - - -8C2080001D871101AEBAC51E46EAE0B95DAD52B40CC66E038E08069E1026E796A3A600 - - -0C2241A41D87110139BF2DC7A92C9FB5E674D77A5CAC633A8E08672182FDE3FE16B200 -9000 - - -0C8600000001428782013101B1485D0C45553ABD4F2D9E9EAA85F69639EAB7D6D2FE2CA6E26736FEA2F039C891263CD50B05392942FAA2F7A2F080B9D9040E1A97A20C0917F4A392D5CCEE17F606918A6E1F03054BCFC1EB630405F788CDA7BD55E140D142902BA3983631922FB6F3B52558E3A08A9FB5894B059A897AE46FFFFF7711484974F150C817CA166B0CD230592DA5145D5A7D0AFFFF1873562A72A72329929661088B5D098BCE42AB497F21680FBA0049B6249F955744660C6B9E4411653FD007537868E6764CC14123CC15B05A32058165200F8C9A1937E583E8E968C293E4DE75F785FF24255809F08251383F7F0593E271758CDAD9711A30B2DE92627C9690FE3A736BA6397CD4E6EB7D61274426EFBB8762ACED744286A8B10CA7757548B1CA97EBA6F026D35EED85E7C25CC5096C154D586342B2E79701248E08061CB42E130422000000 -9000 - - -8C2080001D8711013B3FC7F37BD42F564DD15D1A36620BBC8E080BE26315883C974C00 - - -0C2241A41D87110100EFD79B3C74F54BFE8E9FDB2D8C5DE08E08A66026922507547800 -9000 - - -0C8600000001428782013101A0808DFE83AABC2D04BAB837BBE9493682B550B2D06F80BC78506B5ADBC51D88555E9A91B0A3611C9102A5EDA5DF9616523E75A4A0B12705C3EE87084325865259A11A90D3189D7129FB0EF1AA347A338A647A79234F5C778DAC5C4A9D64FF819B9FF0CCA8180C76FDCB7EA52BE18EEC03582D467D1BED6155D99FF3D8769A043B6E637846BE7ADAFD51A1A1847FAC38FF2F8E28F542EEDE4911698228969825F868B661FA4BA4B008F7D468CBE7C150C1DE6CBF8CF8B6668731E1267E78A02F11572DD9B105FFD4434E184E8BD9EA308EC368FB5B3595DDA6FE92BAFBBEFE26967EF32FB2B5FC80F44E943DDE132A84C4D978ED6E3F1E0F695F052D9690C49757C68309F2D992A93AF096605076562B2189B4BD60C87D9E05B42B773E4BBAB45EFFDAB205FEF46E3A2636431FE4C1329701248E08AB78DA9EF0380C7B0000 -9000 - - - - - -0 - diff --git a/test/fixture/core/http/Transmit2 b/test/fixture/core/http/Transmit2 deleted file mode 100644 index 2b51c61..0000000 --- a/test/fixture/core/http/Transmit2 +++ /dev/null @@ -1,68 +0,0 @@ -HTTP/1.1 200 OK -Server: Apache-Coyote/1.1 -Content-Type: application/vnd.paos+xml;charset=ISO-8859-1 -Transfer-Encoding: chunked -Date: Fri, 25 Apr 2014 11:19:45 GMT - -8ef - - - - - - -353533313636353638 - -0CA4020C1D8711011DD05C291E26943D9E893FBB4425CB378E08B45699566295302200 - - -0CB0000000000E970200008E0851802EB2FE698C7A0000 - - -0CA4020C1D871101EF30406C7488981BB2619F0B24D31F4E8E081A619A80B7E55C5A00 - - -0CB0000000000E970200008E08527311B5050ED6670000 - - -0CA4020C1D871101695988CC962F2B1C48E23C939056B2C28E08C3073A94F51BEA0700 - - -0CB0000000000E970200008E0886FC6AC9422239670000 - - -0CA4020C1D8711016C180275369F2AE341ABC80A81DF49E88E08D34B410004BCA6A800 - - -0CB0000000000E970200008E080FC970CC140303AD0000 - - -0CA4020C1D871101DA9234A4B09E7DB1B8024434A92D65778E088825C5CFE0206DBA00 - - -0CB0000000000E970200008E086FB1AE8BF37F3E6C0000 - - -0CA4020C1D871101ED70D6529C68B20CE8805A0A24683FB98E081C675FD64358539E00 - - -0CB0000000000E970200008E08C1928AAD7AE9041D0000 - - -0CA4020C1D871101A4C2958DD59BEE6C01978323CFC812038E08567F22EE3B30836F00 - - -0CB0000000000E970200008E08D386B5334DA527E50000 - - -0CA4020C1D871101D72167B26C29DD0447444999442A8FAA8E08C4763527ED3035FD00 - - -0CB0000000000E970200008E08D8AD0CEEACF84DF90000 - - - - - -0 - diff --git a/test/fixture/fixture.qrc b/test/fixture/fixture.qrc index 59331c7..5248394 100644 --- a/test/fixture/fixture.qrc +++ b/test/fixture/fixture.qrc @@ -42,19 +42,11 @@ card/cvdv-DEDVtIDGVNK00005.hex card/efCardAccess.hex card/efCardSecurity.hex - card/EstablishPACEChannelOutput_fromCcid.hex - card/EstablishPACEChannelOutput_fromCcid2.hex - card/EstablishPACEChannelOutput.hex - card/EstablishPACEChannelOutput2.hex - card/EstablishPACEChannelOutput_wrongPIN.hex - core/http/DIDAuthenticateEAC1 - core/http/DIDAuthenticateEAC2 - core/http/DIDList - core/http/Disconnect - core/http/InitializeFramework - core/http/StartPaosResponse - core/http/Transmit - core/http/Transmit2 + card/EstablishPaceChannelOutput_fromCcid.hex + card/EstablishPaceChannelOutput_fromCcid2.hex + card/EstablishPaceChannelOutput.hex + card/EstablishPaceChannelOutput2.hex + card/EstablishPaceChannelOutput_wrongPIN.hex core/network/CERT_TLS_ESERVICE_1.der updatable-files/reader/img_ACS_ACR1252U.png tctoken/ok.xml @@ -77,9 +69,19 @@ paos/StartPAOSResponse3.xml paos/Transmit.xml paos/Transmit2.xml - self/SelfAuthenticationData.xml - self/SelfAuthenticationDataNoAddress.xml - self/SelfAuthenticationDataNoStreet.xml + self/SelfAuthenticationData.json + self/SelfAuthenticationDataNoAddress.json + self/SelfAuthenticationDataNoStreet.json paos/Transmit3.xml - + paos/StartPAOS.xml + paos/TransmitResponse.xml + paos/DIDAuthenticateResponse.xml + paos/DIDAuthenticateResponse2.xml + logfiles/empty.txt + logfiles/size1.txt + logfiles/size78.txt + logfiles/size80.txt + logfiles/size82.txt + logfiles/size160.txt + diff --git a/test/fixture/logfiles/empty.txt b/test/fixture/logfiles/empty.txt new file mode 100644 index 0000000..e69de29 diff --git a/test/fixture/logfiles/size1.txt b/test/fixture/logfiles/size1.txt new file mode 100644 index 0000000..65f663f --- /dev/null +++ b/test/fixture/logfiles/size1.txt @@ -0,0 +1 @@ +1 input : 1 test \ No newline at end of file diff --git a/test/fixture/logfiles/size160.txt b/test/fixture/logfiles/size160.txt new file mode 100644 index 0000000..3ea13de --- /dev/null +++ b/test/fixture/logfiles/size160.txt @@ -0,0 +1,80 @@ +1 input : 1 test +2 input : 2 test +3 input : 3 test +4 input : 4 test +5 input : 5 test +6 input : 6 test +7 input : 7 test +8 input : 8 test +9 input : 9 test +10 input : 10test +11 input : 11 test +12 input : 12 test +13 input : 13 test +14 input : 14 test +15 input : 15 test +16 input : 16 test +17 input : 17 test +18 input : 18 test +19 input : 19 test +20 input : 20 test +21 input : 21 test +22 input : 22 test +23 input : 23 test +24 input : 24 test +25 input : 25 test +26 input : 26 test +27 input : 27 test +28 input : 28 test +29 input : 29 test +30 input : 30 test +31 input : 31 test +32 input : 32 test +33 input : 33 test +34 input : 34 test +35 input : 35 test +36 input : 36 test +37 input : 37 test +38 input : 38 test +39 input : 39 test +40 input : 40 test +41 input : 41 test +42 input : 42 test +43 input : 43 test +44 input : 44 test +45 input : 45 test +46 input : 46 test +47 input : 47 test +48 input : 48 test +49 input : 49 test +50 input : 50 test +51 input : 51 test +52 input : 52 test +53 input : 53 test +54 input : 54 test +55 input : 55 test +56 input : 56 test +57 input : 57 test +58 input : 58 test +59 input : 59 test +60 input : 60 test +61 input : 61 test +62 input : 62 test +63 input : 63 test +64 input : 64 test +65 input : 65 test +66 input : 66 test +67 input : 67 test +68 input : 68 test +69 input : 69 test +70 input : 70 test +71 input : 71 test +72 input : 72 test +73 input : 73 test +74 input : 74 test +75 input : 75 test +76 input : 76 test +77 input : 77 test +78 input : 78 test +79 input : 79 test +80 input : 80 test diff --git a/test/fixture/logfiles/size78.txt b/test/fixture/logfiles/size78.txt new file mode 100644 index 0000000..845a016 --- /dev/null +++ b/test/fixture/logfiles/size78.txt @@ -0,0 +1,39 @@ +1 input : 1 test +2 input : 2 test +3 input : 3 test +4 input : 4 test +5 input : 5 test +6 input : 6 test +7 input : 7 test +8 input : 8 test +9 input : 9 test +10 input : 10test +11 input : 11 test +12 input : 12 test +13 input : 13 test +14 input : 14 test +15 input : 15 test +16 input : 16 test +17 input : 17 test +18 input : 18 test +19 input : 19 test +20 input : 20 test +21 input : 21 test +22 input : 22 test +23 input : 23 test +24 input : 24 test +25 input : 25 test +26 input : 26 test +27 input : 27 test +28 input : 28 test +29 input : 29 test +30 input : 30 test +31 input : 31 test +32 input : 32 test +33 input : 33 test +34 input : 34 test +35 input : 35 test +36 input : 36 test +37 input : 37 test +38 input : 38 test +39 input : 39 test diff --git a/test/fixture/logfiles/size80.txt b/test/fixture/logfiles/size80.txt new file mode 100644 index 0000000..dacf7e0 --- /dev/null +++ b/test/fixture/logfiles/size80.txt @@ -0,0 +1,40 @@ +1 input : 1 test +2 input : 2 test +3 input : 3 test +4 input : 4 test +5 input : 5 test +6 input : 6 test +7 input : 7 test +8 input : 8 test +9 input : 9 test +10 input : 10test +11 input : 11 test +12 input : 12 test +13 input : 13 test +14 input : 14 test +15 input : 15 test +16 input : 16 test +17 input : 17 test +18 input : 18 test +19 input : 19 test +20 input : 20 test +21 input : 21 test +22 input : 22 test +23 input : 23 test +24 input : 24 test +25 input : 25 test +26 input : 26 test +27 input : 27 test +28 input : 28 test +29 input : 29 test +30 input : 30 test +31 input : 31 test +32 input : 32 test +33 input : 33 test +34 input : 34 test +35 input : 35 test +36 input : 36 test +37 input : 37 test +38 input : 38 test +39 input : 39 test +40 input : 40 test diff --git a/test/fixture/logfiles/size82.txt b/test/fixture/logfiles/size82.txt new file mode 100644 index 0000000..e62f7c9 --- /dev/null +++ b/test/fixture/logfiles/size82.txt @@ -0,0 +1,41 @@ +1 input : 1 test +2 input : 2 test +3 input : 3 test +4 input : 4 test +5 input : 5 test +6 input : 6 test +7 input : 7 test +8 input : 8 test +9 input : 9 test +10 input : 10 test +11 input : 11 test +12 input : 12 test +13 input : 13 test +14 input : 14 test +15 input : 15 test +16 input : 16 test +17 input : 17 test +18 input : 18 test +19 input : 19 test +20 input : 20 test +21 input : 21 test +22 input : 22 test +23 input : 23 test +24 input : 24 test +25 input : 25 test +26 input : 26 test +27 input : 27 test +28 input : 28 test +29 input : 29 test +30 input : 30 test +31 input : 31 test +32 input : 32 test +33 input : 33 test +34 input : 34 test +35 input : 35 test +36 input : 36 test +37 input : 37 test +38 input : 38 test +39 input : 39 test +40 input : 40 test +41 input : 41 test diff --git a/test/fixture/paos/DIDAuthenticateResponse.xml b/test/fixture/paos/DIDAuthenticateResponse.xml new file mode 100644 index 0000000..2829ed3 --- /dev/null +++ b/test/fixture/paos/DIDAuthenticateResponse.xml @@ -0,0 +1,30 @@ + + + + + urn:liberty:paos:2006-08 + + http://www.projectliberty.org/2006/01/role/paos + + http://www.bsi.bund.de/ecard/api/1.1/PAOS/GetNextCommand + + + + + http://www.projectliberty.org/2006/02/role/paos + + STRIP ME + + + + + http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok + + + a + b + c + + + + diff --git a/test/fixture/paos/DIDAuthenticateResponse2.xml b/test/fixture/paos/DIDAuthenticateResponse2.xml new file mode 100644 index 0000000..26217a1 --- /dev/null +++ b/test/fixture/paos/DIDAuthenticateResponse2.xml @@ -0,0 +1,33 @@ + + + + + urn:liberty:paos:2006-08 + + http://www.projectliberty.org/2006/01/role/paos + + http://www.bsi.bund.de/ecard/api/1.1/PAOS/GetNextCommand + + + + + http://www.projectliberty.org/2006/02/role/paos + + urn:uuid:A9CF4F0B8BFE483B8A5C7E6738C178FE + STRIP ME + + + + + http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok + + + a + b + c + d + e + + + + diff --git a/test/fixture/paos/StartPAOS.xml b/test/fixture/paos/StartPAOS.xml new file mode 100644 index 0000000..4cd3d9c --- /dev/null +++ b/test/fixture/paos/StartPAOS.xml @@ -0,0 +1,39 @@ + + + + + urn:liberty:paos:2006-08 + + http://www.projectliberty.org/2006/01/role/paos + + http://www.bsi.bund.de/ecard/api/1.1/PAOS/GetNextCommand + + + + + http://www.projectliberty.org/2006/02/role/paos + + urn:uuid:dummy + STRIP ME + + + + abcd + + e80704007f00070302 + 00 + + + Test_core_paos_invoke_StartPaos + 123 + 456 + 789 + + + 1 + 1 + 5 + + + + diff --git a/test/fixture/paos/TransmitResponse.xml b/test/fixture/paos/TransmitResponse.xml new file mode 100644 index 0000000..6c4af92 --- /dev/null +++ b/test/fixture/paos/TransmitResponse.xml @@ -0,0 +1,30 @@ + + + + + urn:liberty:paos:2006-08 + + http://www.projectliberty.org/2006/01/role/paos + + http://www.bsi.bund.de/ecard/api/1.1/PAOS/GetNextCommand + + + + + http://www.projectliberty.org/2006/02/role/paos + + STRIP ME + + + + + http://www.bsi.bund.de/ecard/api/1.1/resultmajor#error + http://www.bsi.bund.de/ecard/api/1.1/resultminor/al/common#communicationError + Application was invoked with wrong parameters. + + a + b + c + + + diff --git a/test/fixture/self/SelfAuthenticationData.json b/test/fixture/self/SelfAuthenticationData.json new file mode 100644 index 0000000..1defad4 --- /dev/null +++ b/test/fixture/self/SelfAuthenticationData.json @@ -0,0 +1,51 @@ +{ + "PersonalData": { + "DocumentType": "TA", + "IssuingState": "D", + "DateOfExpiry": "2020-10-31+01:00", + "GivenNames": "ANDRÉ", + "FamilyNames": "MUSTERMANN", + "ArtisticName": "", + "AcademicTitle": "", + "PlaceOfBirth": { + "FreetextPlace": "FRANKFURT (ODER)" + }, + "Nationality": "AZE", + "BirthName": "", + "PlaceOfResidence": { + "StructuredPlace": { + "Street": "EHM-WELK-STRAßE 33", + "City": "LÜBBENAU/SPREEWALD", + "Country": "D", + "ZipCode": "03222" + } + }, + "ResidencePermitI": "ERWERBSTÄTIGKEIT/BESCHÄFTIGUNG GESTATTET.", + "DateOfBirth": "1981-06-17+02:00" + }, + "OperationsAllowedByUser": { + "DocumentType": "ALLOWED", + "IssuingState": "ALLOWED", + "DateOfExpiry": "ALLOWED", + "GivenNames": "ALLOWED", + "FamilyNames": "ALLOWED", + "ArtisticName": "ALLOWED", + "AcademicTitle": "ALLOWED", + "DateOfBirth": "ALLOWED", + "PlaceOfBirth": "ALLOWED", + "Nationality": "ALLOWED", + "BirthName": "ALLOWED", + "PlaceOfResidence": "ALLOWED", + "ResidencePermitI": "ALLOWED", + "RestrictedID": "PROHIBITED", + "AgeVerification": "PROHIBITED", + "PlaceVerification": "PROHIBITED" + }, + "Result": { + "ResultMajor": "http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok", + "ResultMessage": { + "Value": "", + "Lang": "en" + } + } +} diff --git a/test/fixture/self/SelfAuthenticationData.xml b/test/fixture/self/SelfAuthenticationData.xml deleted file mode 100644 index 70aecb8..0000000 --- a/test/fixture/self/SelfAuthenticationData.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - - TP - D - 2020-10-31+01:00 - ERIKA - MUSTERMANN - - - - 19640812 - 1964-08-12+01:00 - - - BERLIN - - GABLER - - - HEIDESTRASSE 17 - KÖLN - D - 51147 - - - - - ALLOWED - ALLOWED - ALLOWED - ALLOWED - ALLOWED - ALLOWED - ALLOWED - ALLOWED - ALLOWED - NOTONCHIP - ALLOWED - ALLOWED - NOTONCHIP - PROHIBITED - PROHIBITED - PROHIBITED - - - http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok - - - diff --git a/test/fixture/self/SelfAuthenticationDataNoAddress.json b/test/fixture/self/SelfAuthenticationDataNoAddress.json new file mode 100644 index 0000000..2e61439 --- /dev/null +++ b/test/fixture/self/SelfAuthenticationDataNoAddress.json @@ -0,0 +1,45 @@ +{ + "PersonalData": { + "DocumentType": "TP", + "IssuingState": "D", + "DateOfExpiry": "2020-10-31+01:00", + "GivenNames": "KARL", + "FamilyNames": "HILLEBRANDT", + "ArtisticName": "GRAF V. LÝSKY", + "AcademicTitle": "DR.HC.", + "PlaceOfBirth": { + "FreetextPlace": "TRIER" + }, + "Nationality": "", + "PlaceOfResidence": { + "NoPlaceInfo": "keine Hauptwohnung in Deutschland" + }, + "ResidencePermitI": "ERWERBSTÄTIGKEIT/BESCHÄFTIGUNG GESTATTET.", + "DateOfBirth": "1952-06-17+01:00" + }, + "OperationsAllowedByUser": { + "DocumentType": "ALLOWED", + "IssuingState": "ALLOWED", + "DateOfExpiry": "ALLOWED", + "GivenNames": "ALLOWED", + "FamilyNames": "ALLOWED", + "ArtisticName": "ALLOWED", + "AcademicTitle": "ALLOWED", + "DateOfBirth": "ALLOWED", + "PlaceOfBirth": "ALLOWED", + "Nationality": "ALLOWED", + "BirthName": "NOTONCHIP", + "PlaceOfResidence": "ALLOWED", + "ResidencePermitI": "ALLOWED", + "RestrictedID": "PROHIBITED", + "AgeVerification": "PROHIBITED", + "PlaceVerification": "PROHIBITED" + }, + "Result": { + "ResultMajor": "http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok", + "ResultMessage": { + "Value": "", + "Lang": "en" + } + } +} diff --git a/test/fixture/self/SelfAuthenticationDataNoAddress.xml b/test/fixture/self/SelfAuthenticationDataNoAddress.xml deleted file mode 100644 index 438526f..0000000 --- a/test/fixture/self/SelfAuthenticationDataNoAddress.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - TP - D - 2020-10-31+01:00 - KARL - HILLEBRANDT - GRAF V. LÝSKY - DR.HC. - - 19520617 - 1952-06-17+01:00 - - - TRIER - - - keine Hauptwohnung in Deutschland - - - - ALLOWED - ALLOWED - ALLOWED - ALLOWED - ALLOWED - ALLOWED - ALLOWED - ALLOWED - ALLOWED - NOTONCHIP - NOTONCHIP - ALLOWED - NOTONCHIP - PROHIBITED - PROHIBITED - PROHIBITED - - - http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok - - - \ No newline at end of file diff --git a/test/fixture/self/SelfAuthenticationDataNoStreet.json b/test/fixture/self/SelfAuthenticationDataNoStreet.json new file mode 100644 index 0000000..cffe5bf --- /dev/null +++ b/test/fixture/self/SelfAuthenticationDataNoStreet.json @@ -0,0 +1,50 @@ +{ + "PersonalData": { + "DocumentType": "TP", + "IssuingState": "D", + "DateOfExpiry": "2020-10-31+01:00", + "GivenNames": "ANNEKATHRIN", + "FamilyNames": "LERCH", + "ArtisticName": "", + "AcademicTitle": "", + "PlaceOfBirth": { + "FreetextPlace": "BAD KÖNIGSHOFEN I. GRABFELD" + }, + "Nationality": "AZE", + "BirthName": "", + "PlaceOfResidence": { + "StructuredPlace": { + "City": "LÜBBENAU/SPREEWALD", + "Country": "D", + "ZipCode": "06108" + } + }, + "ResidencePermitI": "ERWERBSTÄTIGKEIT/BESCHÄFTIGUNG GESTATTET.", + "DateOfBirth": "1976-07-05+01:00" + }, + "OperationsAllowedByUser": { + "DocumentType": "ALLOWED", + "IssuingState": "ALLOWED", + "DateOfExpiry": "ALLOWED", + "GivenNames": "ALLOWED", + "FamilyNames": "ALLOWED", + "ArtisticName": "ALLOWED", + "AcademicTitle": "ALLOWED", + "DateOfBirth": "ALLOWED", + "PlaceOfBirth": "ALLOWED", + "Nationality": "ALLOWED", + "BirthName": "ALLOWED", + "PlaceOfResidence": "ALLOWED", + "ResidencePermitI": "ALLOWED", + "RestrictedID": "PROHIBITED", + "AgeVerification": "PROHIBITED", + "PlaceVerification": "PROHIBITED" + }, + "Result": { + "ResultMajor": "http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok", + "ResultMessage": { + "Value": "", + "Lang": "en" + } + } +} diff --git a/test/fixture/self/SelfAuthenticationDataNoStreet.xml b/test/fixture/self/SelfAuthenticationDataNoStreet.xml deleted file mode 100644 index 05be620..0000000 --- a/test/fixture/self/SelfAuthenticationDataNoStreet.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - TP - D - 2020-10-31+01:00 - ANNEKATHRIN - LERCH - - - - 19760705 - 1976-07-05+01:00 - - - BAD KÖNIGSHOFEN I. GRABFELD - - - - HALLE (SAALE) - D - 06108 - - - - - ALLOWED - ALLOWED - ALLOWED - ALLOWED - ALLOWED - ALLOWED - ALLOWED - ALLOWED - ALLOWED - NOTONCHIP - NOTONCHIP - ALLOWED - NOTONCHIP - PROHIBITED - PROHIBITED - PROHIBITED - - - http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok - - - diff --git a/test/helper/CMakeLists.txt b/test/helper/CMakeLists.txt index a3944cf..6e7ab9e 100644 --- a/test/helper/CMakeLists.txt +++ b/test/helper/CMakeLists.txt @@ -1,7 +1,7 @@ ADD_PLATFORM_LIBRARY(AusweisAppTestHelper) TARGET_INCLUDE_DIRECTORIES(AusweisAppTestHelper SYSTEM PUBLIC ${PCSC_INCLUDE_DIRS}) -TARGET_LINK_LIBRARIES(AusweisAppTestHelper Qt5::Network Qt5::Xml Qt5::Test AusweisAppActivation AusweisAppCard AusweisAppCardDrivers AusweisAppCardRemote AusweisAppNetwork AusweisAppCore AusweisAppWebSocket) +TARGET_LINK_LIBRARIES(AusweisAppTestHelper Qt5::Network Qt5::Test AusweisAppExternal::HttpParser AusweisAppActivation AusweisAppCard AusweisAppCardDrivers AusweisAppRemoteDevice AusweisAppNetwork AusweisAppCore AusweisAppUiWebsocket) TARGET_COMPILE_DEFINITIONS(AusweisAppTestHelper PRIVATE QT_STATICPLUGIN) IF(DESKTOP) diff --git a/test/helper/CliHelper.h b/test/helper/CliHelper.h index af9e6e0..fe38818 100644 --- a/test/helper/CliHelper.h +++ b/test/helper/CliHelper.h @@ -55,4 +55,4 @@ class CliHelper quint16 getServerPort(); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/test/helper/MockActivationContext.h b/test/helper/MockActivationContext.h index 8b48512..15b2922 100644 --- a/test/helper/MockActivationContext.h +++ b/test/helper/MockActivationContext.h @@ -14,7 +14,6 @@ namespace governikus { - class MockActivationContext : public ActivationContext { @@ -51,7 +50,7 @@ class MockActivationContext } - virtual bool sendErrorPage(HttpStatusCode pStatusCode, const GlobalStatus& pStatus) override + virtual bool sendErrorPage(http_status pStatusCode, const GlobalStatus& pStatus) override { Q_UNUSED(pStatusCode); Q_UNUSED(pStatus); @@ -98,4 +97,4 @@ class MockActivationContext }; -} /* namespace governikus */ +} // namespace governikus diff --git a/test/helper/MockCard.cpp b/test/helper/MockCard.cpp index 8531c0a..e17336e 100644 --- a/test/helper/MockCard.cpp +++ b/test/helper/MockCard.cpp @@ -43,3 +43,9 @@ CardReturnCode MockCard::transmit(const CommandApdu& pCmd, ResponseApdu& pRes) pRes.setBuffer(config.second); return config.first; } + + +void MockCard::setConnected(bool pConnected) +{ + mConnected = pConnected; +} diff --git a/test/helper/MockCard.h b/test/helper/MockCard.h index 104d5b3..4e651ce 100644 --- a/test/helper/MockCard.h +++ b/test/helper/MockCard.h @@ -15,7 +15,6 @@ namespace governikus { - typedef QPair TransmitConfig; @@ -57,7 +56,9 @@ class MockCard CardReturnCode transmit(const CommandApdu& pCmd, ResponseApdu& pRes) override; + + void setConnected(bool pConnected); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/test/helper/MockCardConnectionWorker.cpp b/test/helper/MockCardConnectionWorker.cpp new file mode 100644 index 0000000..2060413 --- /dev/null +++ b/test/helper/MockCardConnectionWorker.cpp @@ -0,0 +1,87 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "MockCardConnectionWorker.h" + +using namespace governikus; + + +MockCardConnectionWorker::MockCardConnectionWorker(Reader* pReader) + : CardConnectionWorker(pReader) + , mReader(pReader) + , mResponseCodes() + , mResponseData() + , mPaceCodes() +{ +} + + +MockCardConnectionWorker::~MockCardConnectionWorker() +{ + if (!mReader.isNull()) + { + mReader->deleteLater(); + } +} + + +void MockCardConnectionWorker::addResponse(CardReturnCode pCode, const QByteArray& pData) +{ + mResponseCodes += pCode; + mResponseData += pData; +} + + +void MockCardConnectionWorker::addPaceCode(CardReturnCode pCode) +{ + mPaceCodes += pCode; +} + + +CardReturnCode MockCardConnectionWorker::transmit(const CommandApdu& pCommandApdu, ResponseApdu& pResponseApdu) +{ + Q_UNUSED(pCommandApdu); + pResponseApdu.setBuffer(mResponseData.empty() ? QByteArray() : mResponseData.takeFirst()); + return mResponseCodes.empty() ? CardReturnCode::UNDEFINED : mResponseCodes.takeFirst(); +} + + +CardReturnCode MockCardConnectionWorker::updateRetryCounter() +{ + if (!mReader->getReaderInfo().hasCard()) + { + return CardReturnCode::CARD_NOT_FOUND; + } + return CardReturnCode::OK; +} + + +CardReturnCode MockCardConnectionWorker::establishPaceChannel(PacePasswordId pPasswordId, + const QString& pPasswordValue, + const QByteArray& pChat, + const QByteArray& pCertificateDescription, + EstablishPaceChannelOutput& pChannelOutput) +{ + Q_UNUSED(pPasswordId); + Q_UNUSED(pPasswordValue); + Q_UNUSED(pChat); + Q_UNUSED(pCertificateDescription); + Q_UNUSED(pChannelOutput); + return mPaceCodes.empty() ? CardReturnCode::UNDEFINED : mPaceCodes.takeFirst(); +} + + +CardReturnCode MockCardConnectionWorker::destroyPaceChannel() +{ + return mPaceCodes.empty() ? CardReturnCode::UNDEFINED : mPaceCodes.takeFirst(); +} + + +CardReturnCode MockCardConnectionWorker::setEidPin(const QString& pNewPin, quint8 pTimeoutSeconds, ResponseApdu& pResponseApdu) +{ + Q_UNUSED(pNewPin); + Q_UNUSED(pTimeoutSeconds); + pResponseApdu.setBuffer(mResponseData.empty() ? QByteArray() : mResponseData.takeFirst()); + return mResponseCodes.empty() ? CardReturnCode::UNDEFINED : mResponseCodes.takeFirst(); +} diff --git a/test/helper/MockCardConnectionWorker.h b/test/helper/MockCardConnectionWorker.h new file mode 100644 index 0000000..65171e7 --- /dev/null +++ b/test/helper/MockCardConnectionWorker.h @@ -0,0 +1,47 @@ +/*! + * \brief CardConnectionWorker mock for tests + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + + +#include "CardConnectionWorker.h" +#include "MockReader.h" + +#include + +namespace governikus +{ + +class MockCardConnectionWorker + : public CardConnectionWorker +{ + Q_OBJECT + + private: + QPointer mReader; + QList mResponseCodes; + QByteArrayList mResponseData; + QList mPaceCodes; + + public: + explicit MockCardConnectionWorker(Reader* pReader = new MockReader()); + virtual ~MockCardConnectionWorker() override; + + void addResponse(CardReturnCode pCode, const QByteArray& pData = QByteArray()); + void addPaceCode(CardReturnCode pCode); + + virtual CardReturnCode transmit(const CommandApdu& pCommandApdu, ResponseApdu& pResponseApdu) override; + virtual CardReturnCode updateRetryCounter() override; + virtual CardReturnCode establishPaceChannel(PacePasswordId pPasswordId, + const QString& pPasswordValue, + const QByteArray& pChat, + const QByteArray& pCertificateDescription, + EstablishPaceChannelOutput& pChannelOutput) override; + virtual CardReturnCode destroyPaceChannel() override; + virtual CardReturnCode setEidPin(const QString& pNewPin, quint8 pTimeoutSeconds, ResponseApdu& pResponseApdu) override; +}; + +} // namespace governikus diff --git a/test/helper/MockDataChannel.cpp b/test/helper/MockDataChannel.cpp index f3ae8c0..1bfa72a 100644 --- a/test/helper/MockDataChannel.cpp +++ b/test/helper/MockDataChannel.cpp @@ -4,11 +4,15 @@ #include "MockDataChannel.h" +#include + + using namespace governikus; MockDataChannel::MockDataChannel() - : mReceivedDataBlocks() + : mId(QUuid::createUuid().toString()) + , mReceivedDataBlocks() { } @@ -24,6 +28,12 @@ void MockDataChannel::close() } +const QString& MockDataChannel::getId() const +{ + return mId; +} + + void MockDataChannel::closeAbnormal() { Q_EMIT fireClosed(GlobalStatus::Code::RemoteReader_CloseCode_AbnormalClose); diff --git a/test/helper/MockDataChannel.h b/test/helper/MockDataChannel.h index 360c587..1dfd0f8 100644 --- a/test/helper/MockDataChannel.h +++ b/test/helper/MockDataChannel.h @@ -20,6 +20,7 @@ class MockDataChannel Q_OBJECT private: + QString mId; QVector mReceivedDataBlocks; public: @@ -28,6 +29,7 @@ class MockDataChannel virtual void send(const QByteArray& pDataBlock) override; virtual void close() override; + virtual const QString& getId() const override; void closeAbnormal(); const QVector& getReceivedDataBlocks() const; @@ -40,4 +42,4 @@ class MockDataChannel }; -} /* namespace governikus */ +} // namespace governikus diff --git a/test/helper/MockDownloader.cpp b/test/helper/MockDownloader.cpp index 6d316ec..dd4087f 100644 --- a/test/helper/MockDownloader.cpp +++ b/test/helper/MockDownloader.cpp @@ -13,7 +13,6 @@ MockDownloader::MockDownloader(GlobalStatus::Code pErrorCode) , mTime(QTime(11, 57, 21)) , mTestData() { - } diff --git a/test/helper/MockDownloader.h b/test/helper/MockDownloader.h index f0a237d..02fe855 100644 --- a/test/helper/MockDownloader.h +++ b/test/helper/MockDownloader.h @@ -39,4 +39,4 @@ class MockDownloader const QDateTime& pCurrentTimestamp) override; }; -} +} // namespace governikus diff --git a/test/helper/MockHttpServer.cpp b/test/helper/MockHttpServer.cpp index d9598c7..4995b7c 100644 --- a/test/helper/MockHttpServer.cpp +++ b/test/helper/MockHttpServer.cpp @@ -6,6 +6,8 @@ #include "Env.h" +#include + using namespace governikus; MockHttpServer::MockHttpServer() @@ -43,12 +45,5 @@ void MockHttpServer::onNewHttpRequest(const QSharedPointer& pReques { QVERIFY(pRequest); const auto& response = mMock.value(pRequest->getUrl().toEncoded()); - if (response.isValid()) - { - pRequest->send(response); - } - else - { - pRequest->send(HttpStatusCode::INTERNAL_SERVER_ERROR); - } + pRequest->send(response); } diff --git a/test/helper/MockHttpServer.h b/test/helper/MockHttpServer.h index 336414a..a75847a 100644 --- a/test/helper/MockHttpServer.h +++ b/test/helper/MockHttpServer.h @@ -35,4 +35,4 @@ class MockHttpServer void onNewHttpRequest(const QSharedPointer& pRequest); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/test/helper/MockIfdStatus.cpp b/test/helper/MockIfdStatus.cpp deleted file mode 100644 index fa0c0fb..0000000 --- a/test/helper/MockIfdStatus.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "MockIfdStatus.h" - - -using namespace governikus; - - -namespace -{ -VALUE_NAME(MSG_TYPE, "msg") -VALUE_NAME(CONTEXT_HANDLE, "ContextHandle") -VALUE_NAME(SLOT_NAME, "SlotName") -VALUE_NAME(PIN_CAPABILITIES, "PINCapabilities") -VALUE_NAME(MAX_APDU_LENGTH, "MaxAPDULength") -VALUE_NAME(CONNECTED_READER, "ConnectedReader") -VALUE_NAME(CARD_AVAILABLE, "CardAvailable") -VALUE_NAME(EF_ATR, "EFATR") -VALUE_NAME(EF_DIR, "EFDIR") -} - - -MockIfdStatus::MockIfdStatus(const QString& pSlotName, - const PaceCapabilities& pPaceCapabilities, - int pMaxApduLength, - bool pConnected, - bool pCardAvailable) - : IfdStatus(createJsonObject( - pSlotName, - pPaceCapabilities, - pMaxApduLength, - pConnected, - pCardAvailable)) -{ -} - - -MockIfdStatus::~MockIfdStatus() -{ -} - - -QJsonObject MockIfdStatus::createJsonObject(const QString& pSlotName, - const PaceCapabilities& pPaceCapabilities, - int pMaxApduLength, - bool pConnectedReader, - bool pCardAvailable) -{ - QJsonObject result; - result[MSG_TYPE()] = getEnumName(RemoteCardMessageType::IFDStatus); - result[CONTEXT_HANDLE()] = QStringLiteral("contextHandle"); - result[SLOT_NAME()] = pSlotName; - result[PIN_CAPABILITIES()] = pPaceCapabilities.toJson(); - result[MAX_APDU_LENGTH()] = pMaxApduLength; - result[CONNECTED_READER()] = pConnectedReader; - result[CARD_AVAILABLE()] = pCardAvailable; - result[EF_ATR()] = QJsonValue(); - result[EF_DIR()] = QJsonValue(); - return result; -} diff --git a/test/helper/MockIfdStatus.h b/test/helper/MockIfdStatus.h deleted file mode 100644 index e426a28..0000000 --- a/test/helper/MockIfdStatus.h +++ /dev/null @@ -1,37 +0,0 @@ -/*! - * \brief IfdStatus mock for tests - * - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - - -#include "messages/IfdStatus.h" - - -namespace governikus -{ - -class MockIfdStatus - : public IfdStatus -{ - public: - MockIfdStatus(const QString& pSlotName, - const PaceCapabilities& pPaceCapabilities, - int pMaxApduLength, - bool pConnected, - bool pCardAvailable = false); - ~MockIfdStatus(); - - private: - QJsonObject createJsonObject(const QString& pSlotName, - const PaceCapabilities& pPaceCapabilities, - int pMaxApduLength, - bool pConnectedReader, - bool pCardAvailable); - - -}; - -} /* namespace governikus */ diff --git a/test/helper/MockNetworkManager.cpp b/test/helper/MockNetworkManager.cpp index 9beaedf..e5b708c 100644 --- a/test/helper/MockNetworkManager.cpp +++ b/test/helper/MockNetworkManager.cpp @@ -4,8 +4,7 @@ #include "MockNetworkManager.h" -#include "HttpStatusCode.h" - +#include #include using namespace governikus; @@ -43,7 +42,7 @@ MockNetworkReply* MockNetworkManager::getReply(const QNetworkRequest& pRequest) msgFile.close(); } - mLastReply = new MockNetworkReply(content, HttpStatusCode::OK); + mLastReply = new MockNetworkReply(content, HTTP_STATUS_OK); } else { diff --git a/test/helper/MockNetworkManager.h b/test/helper/MockNetworkManager.h index bc79b6b..7ea852d 100644 --- a/test/helper/MockNetworkManager.h +++ b/test/helper/MockNetworkManager.h @@ -69,4 +69,4 @@ class MockNetworkManager void fireReply(); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/test/helper/MockNetworkReply.cpp b/test/helper/MockNetworkReply.cpp index 5e854b8..382cff7 100644 --- a/test/helper/MockNetworkReply.cpp +++ b/test/helper/MockNetworkReply.cpp @@ -7,13 +7,13 @@ using namespace governikus; -MockNetworkReply::MockNetworkReply(const QByteArray& pData, HttpStatusCode pStatusCode, QObject* pParent) +MockNetworkReply::MockNetworkReply(const QByteArray& pData, http_status pStatusCode, QObject* pParent) : QNetworkReply(pParent) , mSocket() { mSocket.mReadBuffer = pData; setOpenMode(QIODevice::ReadOnly); - setAttribute(QNetworkRequest::HttpStatusCodeAttribute, QVariant(Enum::getValue(pStatusCode))); + setAttribute(QNetworkRequest::HttpStatusCodeAttribute, QVariant(pStatusCode)); } diff --git a/test/helper/MockNetworkReply.h b/test/helper/MockNetworkReply.h index 8eb4de8..c362d3c 100644 --- a/test/helper/MockNetworkReply.h +++ b/test/helper/MockNetworkReply.h @@ -6,11 +6,13 @@ #pragma once -#include "HttpStatusCode.h" #include "MockSocket.h" +#include #include +class test_StateCheckRefreshAddress; + namespace governikus { @@ -20,14 +22,14 @@ class MockNetworkReply Q_OBJECT private: + friend class ::test_StateCheckRefreshAddress; MockSocket mSocket; public: - MockNetworkReply(const QByteArray& pData = QByteArray(), HttpStatusCode pStatusCode = HttpStatusCode::UNDEFINED, QObject* pParent = nullptr); + MockNetworkReply(const QByteArray& pData = QByteArray(), http_status pStatusCode = HTTP_STATUS_OK, QObject* pParent = nullptr); virtual ~MockNetworkReply() override; virtual void abort() override { - } @@ -59,4 +61,4 @@ class MockNetworkReply }; -} /* namespace governikus */ +} // namespace governikus diff --git a/test/helper/MockReader.h b/test/helper/MockReader.h index e40a2c1..76a8eef 100644 --- a/test/helper/MockReader.h +++ b/test/helper/MockReader.h @@ -52,6 +52,12 @@ class MockReader } + void setReaderInfo(const ReaderInfo& pReaderInfo) + { + mReaderInfo = pReaderInfo; + } + + private: virtual Reader::CardEvent updateCard() override { @@ -61,4 +67,4 @@ class MockReader }; -} /* namespace governikus */ +} // namespace governikus diff --git a/test/helper/MockReaderConfiguration.h b/test/helper/MockReaderConfiguration.h index 56eaac7..261bf48 100644 --- a/test/helper/MockReaderConfiguration.h +++ b/test/helper/MockReaderConfiguration.h @@ -24,4 +24,4 @@ class MockReaderConfiguration void clearReaderConfiguration(); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/test/helper/MockReaderDetector.h b/test/helper/MockReaderDetector.h index ae29982..6589cb6 100644 --- a/test/helper/MockReaderDetector.h +++ b/test/helper/MockReaderDetector.h @@ -29,4 +29,4 @@ class MockReaderDetector const QVector& mDevIds; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/test/helper/MockReaderManagerPlugIn.cpp b/test/helper/MockReaderManagerPlugIn.cpp index cbdf78e..394999e 100644 --- a/test/helper/MockReaderManagerPlugIn.cpp +++ b/test/helper/MockReaderManagerPlugIn.cpp @@ -20,6 +20,12 @@ MockReaderManagerPlugIn::MockReaderManagerPlugIn() } +MockReaderManagerPlugIn::~MockReaderManagerPlugIn() +{ + removeAllReader(); +} + + MockReaderManagerPlugIn& MockReaderManagerPlugIn::getInstance() { if (!mInstance) @@ -65,3 +71,13 @@ void MockReaderManagerPlugIn::removeReader(const QString& pReaderName) delete reader; } } + + +void MockReaderManagerPlugIn::removeAllReader() +{ + const auto& readerList = getReaders(); + for (auto reader : readerList) + { + removeReader(reader->getName()); + } +} diff --git a/test/helper/MockReaderManagerPlugIn.h b/test/helper/MockReaderManagerPlugIn.h index 313707f..bc8c814 100644 --- a/test/helper/MockReaderManagerPlugIn.h +++ b/test/helper/MockReaderManagerPlugIn.h @@ -22,22 +22,21 @@ class MockReaderManagerPlugIn Q_PLUGIN_METADATA(IID "governikus.ReaderManagerPlugIn" FILE "MockReaderManagerPlugIn.metadata.json") Q_INTERFACES(governikus::ReaderManagerPlugIn) - public: + private: static MockReaderManagerPlugIn* mInstance; QMap mReaders; + public: MockReaderManagerPlugIn(); + virtual ~MockReaderManagerPlugIn() override; static MockReaderManagerPlugIn& getInstance(); virtual QList getReaders() const override; - MockReader* addReader(const QString& pReaderName); - void removeReader(const QString& pReaderName); - - + void removeAllReader(); }; -} +} // namespace governikus diff --git a/test/helper/MockRemoteDispatcher.cpp b/test/helper/MockRemoteDispatcher.cpp index 7dbb1e6..5d84ca4 100644 --- a/test/helper/MockRemoteDispatcher.cpp +++ b/test/helper/MockRemoteDispatcher.cpp @@ -11,23 +11,24 @@ #include "messages/IfdStatus.h" #include "messages/IfdTransmit.h" #include "messages/IfdTransmitResponse.h" -#include "MockIfdStatus.h" #include +#include using namespace governikus; MockRemoteDispatcher::MockRemoteDispatcher(DispatcherState pState) - : mState(pState) - , mId() - , mContextHandle() + : RemoteDispatcherClient(IfdVersion::Version::v_test, QSharedPointer()) + , mState(pState) + , mId(QUuid::createUuid().toString()) + , mContextHandle(QStringLiteral("#TestContext")) { } -const QString& MockRemoteDispatcher::getId() const +QString MockRemoteDispatcher::getId() const { return mId; } @@ -53,35 +54,35 @@ void MockRemoteDispatcher::send(const QSharedPointer& pMess } bool withCard = (mState == DispatcherState::ReaderWithCard || mState == DispatcherState::ReaderWithCardError); - const QSharedPointer message(new MockIfdStatus(QStringLiteral("NFC Reader"), PaceCapabilities(false), 500, true, withCard)); - Q_EMIT fireReceived(message, sharedFromThis()); + const QSharedPointer message(new IfdStatus(QStringLiteral("NFC Reader"), PaceCapabilities(false), 500, true, withCard)); + Q_EMIT fireReceived(message->getType(), QJsonDocument::fromJson(message->toByteArray(mContextHandle)).object(), mId); return; } - const QString errorMsg = mState == DispatcherState::ReaderWithCardError ? QStringLiteral("Error requested by the test") : QString(); + const ECardApiResult::Minor resultMinor = (mState == DispatcherState::ReaderWithCardError ? ECardApiResult::Minor::AL_Unknown_Error : ECardApiResult::Minor::null); if (pMessage->getType() == RemoteCardMessageType::IFDConnect) { const QSharedPointer request = pMessage.dynamicCast(); const QString readerName = request->getSlotName(); - const QSharedPointer message(new IfdConnectResponse(readerName, errorMsg)); - Q_EMIT fireReceived(message, sharedFromThis()); + const QSharedPointer message(new IfdConnectResponse(readerName, resultMinor)); + Q_EMIT fireReceived(message->getType(), QJsonDocument::fromJson(message->toByteArray(mContextHandle)).object(), mId); } if (pMessage->getType() == RemoteCardMessageType::IFDTransmit) { const QSharedPointer request = pMessage.dynamicCast(); const QString readerName = request->getSlotHandle(); - const QSharedPointer message(new IfdTransmitResponse(readerName, errorMsg.isEmpty() ? QByteArray("pong") : QByteArray(), errorMsg)); - Q_EMIT fireReceived(message, sharedFromThis()); + const QSharedPointer message(new IfdTransmitResponse(readerName, resultMinor == ECardApiResult::Minor::null ? QByteArray("pong") : QByteArray(), resultMinor)); + Q_EMIT fireReceived(message->getType(), QJsonDocument::fromJson(message->toByteArray(mContextHandle)).object(), mId); } if (pMessage->getType() == RemoteCardMessageType::IFDDisconnect) { const QSharedPointer request = pMessage.dynamicCast(); const QString readerName = request->getSlotHandle(); - const QSharedPointer message(new IfdDisconnectResponse(readerName, errorMsg)); - Q_EMIT fireReceived(message, sharedFromThis()); + const QSharedPointer message(new IfdDisconnectResponse(readerName, resultMinor)); + Q_EMIT fireReceived(message->getType(), QJsonDocument::fromJson(message->toByteArray(mContextHandle)).object(), mId); } } @@ -100,16 +101,11 @@ void MockRemoteDispatcher::setState(DispatcherState pState) void MockRemoteDispatcher::onClosed() { - Q_EMIT fireClosed(GlobalStatus::Code::RemoteReader_CloseCode_NormalClose, sharedFromThis()); + Q_EMIT fireClosed(GlobalStatus::Code::RemoteReader_CloseCode_NormalClose, mId); } void MockRemoteDispatcher::onReceived(const QSharedPointer& pMessage) { - Q_EMIT fireReceived(pMessage, sharedFromThis()); -} - - -void MockRemoteDispatcher::close() -{ + Q_EMIT fireReceived(pMessage->getType(), QJsonDocument::fromJson(pMessage->toByteArray(mContextHandle)).object(), mId); } diff --git a/test/helper/MockRemoteDispatcher.h b/test/helper/MockRemoteDispatcher.h index 81d3b16..2d79381 100644 --- a/test/helper/MockRemoteDispatcher.h +++ b/test/helper/MockRemoteDispatcher.h @@ -6,13 +6,14 @@ #pragma once -#include "RemoteDispatcher.h" +#include "RemoteDispatcherClient.h" + namespace governikus { class MockRemoteDispatcher - : public RemoteDispatcher + : public RemoteDispatcherClient { Q_OBJECT @@ -32,12 +33,10 @@ class MockRemoteDispatcher public: MockRemoteDispatcher(DispatcherState pState = DispatcherState::WithoutReader); - virtual ~MockRemoteDispatcher() override = default; - virtual const QString& getId() const override; + virtual QString getId() const override; virtual const QString& getContextHandle() const override; - virtual void send(const QSharedPointer& pMessage) override; - virtual void close() override; + Q_INVOKABLE virtual void send(const QSharedPointer& pMessage) override; DispatcherState getState() const; void setState(DispatcherState pState); @@ -52,4 +51,4 @@ class MockRemoteDispatcher }; -} /* namespace governikus */ +} // namespace governikus diff --git a/test/helper/MockRemoteServer.cpp b/test/helper/MockRemoteServer.cpp new file mode 100644 index 0000000..2ea16a3 --- /dev/null +++ b/test/helper/MockRemoteServer.cpp @@ -0,0 +1,72 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "MockRemoteServer.h" + +using namespace governikus; + +MockRemoteServer::MockRemoteServer() + : RemoteServer() + , mRunning(false) + , mConnected(false) + , mPairing(false) + , mServerMessageHandler(QSharedPointer(new ServerMessageHandlerImpl(nullptr))) +{ +} + + +bool MockRemoteServer::isRunning() const +{ + return mRunning; +} + + +bool MockRemoteServer::start(const QString& pServerName) +{ + Q_UNUSED(pServerName); + mRunning = true; + return true; +} + + +void MockRemoteServer::stop() +{ + mRunning = false; +} + + +void MockRemoteServer::setPairing(bool pEnable) +{ + mPairing = pEnable; +} + + +bool MockRemoteServer::getPairing() +{ + return mPairing; +} + + +bool MockRemoteServer::isConnected() const +{ + return mConnected; +} + + +void MockRemoteServer::setConnected(bool pConnected) +{ + mConnected = pConnected; +} + + +QSslCertificate MockRemoteServer::getCurrentCertificate() const +{ + return QSslCertificate(QByteArray(), QSsl::Pem); +} + + +const QSharedPointer& MockRemoteServer::getMessageHandler() const +{ + return mServerMessageHandler; +} diff --git a/test/helper/MockRemoteServer.h b/test/helper/MockRemoteServer.h new file mode 100644 index 0000000..c0e3686 --- /dev/null +++ b/test/helper/MockRemoteServer.h @@ -0,0 +1,41 @@ +/*! + * \brief Provide a RemoteServer for tests + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "RemoteServer.h" + +#include + +namespace governikus +{ + +class MockRemoteServer + : public RemoteServer +{ + Q_OBJECT + + private: + bool mRunning; + bool mConnected; + bool mPairing; + QSharedPointer mServerMessageHandler; + + public: + MockRemoteServer(); + + virtual bool isRunning() const override; + virtual bool start(const QString& pServerName) override; + virtual void stop() override; + virtual void setPairing(bool pEnable) override; + bool getPairing(); + virtual bool isConnected() const override; + void setConnected(bool pConnected); + virtual QSslCertificate getCurrentCertificate() const override; + virtual const QSharedPointer& getMessageHandler() const override; +}; + +} // namespace governikus diff --git a/test/helper/MockSocket.h b/test/helper/MockSocket.h index c59c5f5..e0acd68 100644 --- a/test/helper/MockSocket.h +++ b/test/helper/MockSocket.h @@ -9,6 +9,8 @@ #include #include +class test_WebserviceActivationContext; + namespace governikus { @@ -16,6 +18,7 @@ class MockSocket : public QTcpSocket { Q_OBJECT + friend class ::test_WebserviceActivationContext; public: QByteArray mReadBuffer; @@ -31,4 +34,4 @@ class MockSocket qint64 writeData(const char* pData, qint64 pMaxSize) override; }; -} /* namespace governikus */ +} // namespace governikus diff --git a/test/helper/MockWorkflowAuthenticateUi.h b/test/helper/MockWorkflowAuthenticateUi.h index 1f46578..79be60f 100644 --- a/test/helper/MockWorkflowAuthenticateUi.h +++ b/test/helper/MockWorkflowAuthenticateUi.h @@ -65,4 +65,4 @@ class MockWorkflowAuthenticateUi }; -} /* namespace governikus */ +} // namespace governikus diff --git a/test/helper/MockWorkflowChangePinUi.h b/test/helper/MockWorkflowChangePinUi.h index c9f508d..f9d6646 100644 --- a/test/helper/MockWorkflowChangePinUi.h +++ b/test/helper/MockWorkflowChangePinUi.h @@ -41,4 +41,4 @@ class MockWorkflowChangePinUi }; -} /* namespace governikus */ +} // namespace governikus diff --git a/test/helper/MsgHandlerEnterPassword.cpp b/test/helper/MsgHandlerEnterPassword.cpp new file mode 100644 index 0000000..7706fac --- /dev/null +++ b/test/helper/MsgHandlerEnterPassword.cpp @@ -0,0 +1,60 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "MsgHandlerEnterPassword.h" + +#include "MockReaderManagerPlugIn.h" +#include "ReaderManager.h" + +#include + +using namespace governikus; + +void governikus::setValidState(MessageDispatcher& pDispatcher, + bool pSelectReader, + bool pBasicReader, + const PacePasswordId pPasswordID) +{ + QSharedPointer context(new WorkflowContext()); + pDispatcher.init(context); + context->setEstablishPaceChannelType(pPasswordID); + + QByteArray expected; + if (pPasswordID == PacePasswordId::PACE_PIN) + { + expected = R"({"msg":"ENTER_PIN"})"; + } + + if (pPasswordID == PacePasswordId::PACE_CAN) + { + expected = R"({"msg":"ENTER_CAN"})"; + } + + if (pPasswordID == PacePasswordId::PACE_PUK) + { + expected = R"({"msg":"ENTER_PUK"})"; + } + + QCOMPARE(pDispatcher.processStateChange(QLatin1String("StateEnterPacePassword")), expected); + + if (pSelectReader) + { + auto* reader = MockReaderManagerPlugIn::getInstance().addReader("MockReader CARD"); + auto info = reader->getReaderInfo(); + info.setBasicReader(pBasicReader); + reader->setReaderInfo(info); + reader->setCard(MockCardConfig()); + context->setReaderName("MockReader CARD"); + } +} + + +QByteArray governikus::addReaderData(const char* pData, bool pKeyPad) +{ + const QByteArray data(pData); + const QByteArray part1(R"(,"reader":{"attached":true,"card":{"deactivated":false,"inoperative":false,"retryCounter":-1},"keypad":)"); + const QByteArray keypad = pKeyPad ? QByteArrayLiteral("true") : QByteArrayLiteral("false"); + const QByteArray part2(R"(,"name":"MockReader CARD"})"); + return data.mid(0, data.size() - 1) + part1 + keypad + part2 + QByteArrayLiteral("}"); +} diff --git a/test/helper/MsgHandlerEnterPassword.h b/test/helper/MsgHandlerEnterPassword.h new file mode 100644 index 0000000..d99ad43 --- /dev/null +++ b/test/helper/MsgHandlerEnterPassword.h @@ -0,0 +1,25 @@ +/*! + * \brief Helper static methods for \ref test_MsgHandlerEnterPin, \ref test_MsgHandlerEnterCan and \ref test_MsgHandlerEnterPuk + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "MessageDispatcher.h" + +#include +#include + +namespace governikus +{ + +void setValidState(MessageDispatcher& pDispatcher, + bool pSelectReader, + bool pBasicReader, + const PacePasswordId pPasswordID); + +QByteArray addReaderData(const char* pData, bool pKeyPad = false); + + +} // namespace governikus diff --git a/test/helper/PersoSimController.h b/test/helper/PersoSimController.h index 54d3487..6b1904d 100644 --- a/test/helper/PersoSimController.h +++ b/test/helper/PersoSimController.h @@ -38,4 +38,4 @@ class PersoSimController }; -} /* namespace governikus */ +} // namespace governikus diff --git a/test/helper/RemoteMessageChecker.cpp b/test/helper/RemoteMessageChecker.cpp deleted file mode 100644 index a40afaf..0000000 --- a/test/helper/RemoteMessageChecker.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - - -#include "RemoteMessageChecker.h" - -#include "messages/Discovery.h" -#include "messages/GetIfdStatus.h" -#include "messages/IfdConnect.h" -#include "messages/IfdConnectResponse.h" -#include "messages/IfdDisconnect.h" -#include "messages/IfdDisconnectResponse.h" -#include "messages/IfdError.h" -#include "messages/IfdEstablishContext.h" -#include "messages/IfdEstablishContextResponse.h" -#include "messages/IfdEstablishPaceChannel.h" -#include "messages/IfdEstablishPaceChannelResponse.h" -#include "messages/IfdStatus.h" -#include "messages/IfdTransmit.h" -#include "messages/IfdTransmitResponse.h" - -#include -#include - - -using namespace governikus; - - -void RemoteMessageChecker::processDiscovery(const QSharedPointer& pMessage) -{ - QCOMPARE(pMessage->getIfdName(), QStringLiteral("Sony Xperia Z5 compact")); - QCOMPARE(pMessage->getIfdId(), QStringLiteral("0123456789ABCDEF")); - QVERIFY(pMessage->getPort() == static_cast(24728)); - QCOMPARE(pMessage->getSupportedApis(), {IfdVersion::Version::v0}); -} - - -void RemoteMessageChecker::process(const QSharedPointer& pMessage) -{ - QCOMPARE(pMessage->getType(), RemoteCardMessageType::IFDEstablishContext); - QCOMPARE(pMessage->getProtocol().toString(), QStringLiteral("IFDInterface_WebSocket_v0")); -} - - -void RemoteMessageChecker::process(const QSharedPointer& pMessage) -{ - QCOMPARE(pMessage->getType(), RemoteCardMessageType::IFDEstablishContextResponse); - QCOMPARE(pMessage->resultHasError(), true); - QCOMPARE(pMessage->getResultMinor(), QStringLiteral("http://www.bsi.bund.de/ecard/api/1.1/resultminor/minorResult")); -} - - -void RemoteMessageChecker::process(const QSharedPointer& pMessage) -{ - QCOMPARE(pMessage->getType(), RemoteCardMessageType::IFDGetStatus); -} - - -void RemoteMessageChecker::process(const QSharedPointer& pMessage) -{ - QCOMPARE(pMessage->getType(), RemoteCardMessageType::IFDStatus); - - QCOMPARE(pMessage->getSlotName(), QStringLiteral("NFC Reader")); - QVERIFY(pMessage->getConnectedReader()); - QCOMPARE(pMessage->getMaxApduLength(), 500); - - const PaceCapabilities paceCapabilities = pMessage->getPaceCapabilities(); - Q_UNUSED(paceCapabilities); - - QVERIFY(!pMessage->getCardAvailable()); -} - - -void RemoteMessageChecker::process(const QSharedPointer& pMessage) -{ - QCOMPARE(pMessage->getType(), RemoteCardMessageType::IFDConnect); - QCOMPARE(pMessage->getSlotName(), QStringLiteral("NFC Reader")); -} - - -void RemoteMessageChecker::process(const QSharedPointer& pMessage) -{ - QCOMPARE(pMessage->getType(), RemoteCardMessageType::IFDConnectResponse); - QCOMPARE(pMessage->getSlotHandle(), QStringLiteral("NFC Reader")); - QCOMPARE(pMessage->resultHasError(), true); - QCOMPARE(pMessage->getResultMinor(), QStringLiteral("http://www.bsi.bund.de/ecard/api/1.1/resultminor/minorResult")); -} - - -void RemoteMessageChecker::process(const QSharedPointer& pMessage) -{ - QCOMPARE(pMessage->getType(), RemoteCardMessageType::IFDDisconnect); - QCOMPARE(pMessage->getSlotHandle(), QStringLiteral("NFC Reader")); -} - - -void RemoteMessageChecker::process(const QSharedPointer& pMessage) -{ - QCOMPARE(pMessage->getType(), RemoteCardMessageType::IFDDisconnectResponse); - QCOMPARE(pMessage->getSlotHandle(), QStringLiteral("NFC Reader")); - QCOMPARE(pMessage->resultHasError(), true); - QCOMPARE(pMessage->getResultMinor(), QStringLiteral("http://www.bsi.bund.de/ecard/api/1.1/resultminor/minorResult")); -} - - -void RemoteMessageChecker::process(const QSharedPointer& pMessage) -{ - QCOMPARE(pMessage->getType(), RemoteCardMessageType::IFDError); - QCOMPARE(pMessage->getSlotHandle(), QStringLiteral("NFC Reader")); - QCOMPARE(pMessage->resultHasError(), true); - QCOMPARE(pMessage->getResultMinor(), QStringLiteral("http://www.bsi.bund.de/ecard/api/1.1/resultminor/minorResult")); -} - - -void RemoteMessageChecker::process(const QSharedPointer& pMessage) -{ - QCOMPARE(pMessage->getType(), RemoteCardMessageType::IFDTransmit); - QCOMPARE(pMessage->getSlotHandle(), QStringLiteral("NFC Reader")); - QCOMPARE(pMessage->getInputApdu(), QByteArray::fromHex("00A402022F00")); -} - - -void RemoteMessageChecker::process(const QSharedPointer& pMessage) -{ - QCOMPARE(pMessage->getType(), RemoteCardMessageType::IFDTransmitResponse); - QCOMPARE(pMessage->getSlotHandle(), QStringLiteral("NFC Reader")); - QCOMPARE(pMessage->resultHasError(), true); - QCOMPARE(pMessage->getResultMinor(), QStringLiteral("http://www.bsi.bund.de/ecard/api/1.1/resultminor/minorResult")); - QCOMPARE(pMessage->getResponseApdu(), QByteArray::fromHex("9000")); -} - - -void RemoteMessageChecker::process(const QSharedPointer& pMessage) -{ - QCOMPARE(pMessage->getSlotHandle(), QStringLiteral("My little Reader")); - QCOMPARE(pMessage->getInputData(), QByteArray::fromHex("ABCD1234")); -} - - -void RemoteMessageChecker::process(const QSharedPointer& pMessage) -{ - QCOMPARE(pMessage->getSlotHandle(), QStringLiteral("My little Reader")); - QCOMPARE(pMessage->getOutputData(), QByteArray::fromHex("ABCD1234")); -} diff --git a/test/helper/RemoteMessageChecker.h b/test/helper/RemoteMessageChecker.h deleted file mode 100644 index d1e942a..0000000 --- a/test/helper/RemoteMessageChecker.h +++ /dev/null @@ -1,41 +0,0 @@ -/*! - * \brief Helper class to verify the content of remote messages. - * - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "messages/MessageReceiver.h" - - -namespace governikus -{ -class Discovery; - - -class RemoteMessageChecker - : public MessageReceiver -{ - public: - RemoteMessageChecker() = default; - virtual ~RemoteMessageChecker() override = default; - - void processDiscovery(const QSharedPointer& pMessage); - virtual void process(const QSharedPointer& pMessage) override; - virtual void process(const QSharedPointer& pMessage) override; - virtual void process(const QSharedPointer& pMessage) override; - virtual void process(const QSharedPointer& pMessage) override; - virtual void process(const QSharedPointer& pMessage) override; - virtual void process(const QSharedPointer& pMessage) override; - virtual void process(const QSharedPointer& pMessage) override; - virtual void process(const QSharedPointer& pMessage) override; - virtual void process(const QSharedPointer& pMessage) override; - virtual void process(const QSharedPointer& pMessage) override; - virtual void process(const QSharedPointer& pMessage) override; - virtual void process(const QSharedPointer& pMessage) override; - virtual void process(const QSharedPointer& pMessage) override; - -}; - -} /* namespace governikus */ diff --git a/test/helper/TestAuthContext.h b/test/helper/TestAuthContext.h index 189c34a..d86fe36 100644 --- a/test/helper/TestAuthContext.h +++ b/test/helper/TestAuthContext.h @@ -7,7 +7,6 @@ #pragma once #include "context/AuthContext.h" -#include "paos/retrieve/DidAuthenticateEac1.h" #include @@ -30,4 +29,4 @@ class TestAuthContext void setOptionalAccessRights(const QSet& pAccessRights); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/test/helper/TestFileHelper.cpp b/test/helper/TestFileHelper.cpp index ae05bf9..f479e00 100644 --- a/test/helper/TestFileHelper.cpp +++ b/test/helper/TestFileHelper.cpp @@ -4,8 +4,6 @@ #include "TestFileHelper.h" -#include "FileDestination.h" - #include #include diff --git a/test/helper/TestFileHelper.h b/test/helper/TestFileHelper.h index 38a1c89..457aecc 100644 --- a/test/helper/TestFileHelper.h +++ b/test/helper/TestFileHelper.h @@ -21,4 +21,4 @@ class TestFileHelper static bool containsLog(const QSignalSpy& pSpy, const QLatin1String pStr); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/test/helper/WebSocketHelper.cpp b/test/helper/WebSocketHelper.cpp index 1a4f3cc..27a3f28 100644 --- a/test/helper/WebSocketHelper.cpp +++ b/test/helper/WebSocketHelper.cpp @@ -19,7 +19,7 @@ void WebSocketHelper::connectWebsocket(int pPort) connect(&mWebSocket, &QWebSocket::disconnected, &eventLoop, &QEventLoop::quit); connect(&mWebSocket, QOverload::of(&QWebSocket::error), &eventLoop, &QEventLoop::quit); - const QString address = QStringLiteral("ws://localhost:%1").arg(pPort); + const QString address = QStringLiteral("ws://localhost:%1/eID-Kernel").arg(pPort); mWebSocket.open(QUrl(address)); QTimer timer; diff --git a/test/helper/WebSocketHelper.h b/test/helper/WebSocketHelper.h index aafe1e8..1c3be7d 100644 --- a/test/helper/WebSocketHelper.h +++ b/test/helper/WebSocketHelper.h @@ -35,4 +35,4 @@ class WebSocketHelper void sendMessage(const QString& pMessage); }; -} /* namespace governikus */ +} // namespace governikus diff --git a/test/qml/CMakeLists.txt b/test/qml/CMakeLists.txt index 1acbcbb..2fcd6e4 100644 --- a/test/qml/CMakeLists.txt +++ b/test/qml/CMakeLists.txt @@ -1,4 +1,12 @@ -ADD_QML_TEST_FILES(${RESOURCES_DIR}/qml) +ADD_SUBDIRECTORY(mobile) +ADD_SUBDIRECTORY(desktop) +ADD_SUBDIRECTORY(generic) -ADD_EXECUTABLE(QmlTestRunner QmlTestRunner.cpp) -TARGET_LINK_LIBRARIES(QmlTestRunner Qt5::QuickTest Qt5::Gui) +SET(MAIN_FILES QmlTestRunner.cpp) +IF(ANDROID) + ADD_LIBRARY(QmlTestRunner SHARED ${MAIN_FILES}) +ELSE() + ADD_EXECUTABLE(QmlTestRunner ${MAIN_FILES}) +ENDIF() + +TARGET_LINK_LIBRARIES(QmlTestRunner Qt5::QuickTest Qt5::Gui AusweisAppGlobal AusweisAppUiQml) diff --git a/test/qml/QmlTestRunner.cpp b/test/qml/QmlTestRunner.cpp index ef5fda6..37f22d4 100644 --- a/test/qml/QmlTestRunner.cpp +++ b/test/qml/QmlTestRunner.cpp @@ -2,6 +2,24 @@ * \copyright Copyright (c) 2016-2018 Governikus GmbH & Co. KG, Germany */ +#include "ResourceLoader.h" +#include "UIPlugInQml.h" + #include #include + + +using namespace governikus; + + +static void preRoutine() +{ + ResourceLoader::getInstance().init(); + + UIPlugInQml::registerQmlTypes(); +} + + +Q_COREAPP_STARTUP_FUNCTION(preRoutine) + QUICK_TEST_MAIN(qml) diff --git a/test/qml/desktop/CMakeLists.txt b/test/qml/desktop/CMakeLists.txt new file mode 100644 index 0000000..82152b4 --- /dev/null +++ b/test/qml/desktop/CMakeLists.txt @@ -0,0 +1,2 @@ +ADD_QML_TEST_FILES(${RESOURCES_DIR}/qml SELECTORS "desktop;win") +ADD_QML_TEST_FILES(${RESOURCES_DIR}/qml SELECTORS "desktop;mac") diff --git a/test/qml/desktop/test_Global.qml b/test/qml/desktop/test_Global.qml new file mode 100644 index 0000000..7b85c78 --- /dev/null +++ b/test/qml/desktop/test_Global.qml @@ -0,0 +1,14 @@ +import QtTest 1.10 + +TestCase { + name: "ModuleImportTest" + id: parent + + function test_load_ContinueButton() { + var item = createTemporaryQmlObject(" + import Governikus.Global 1.0; + ContinueButton {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/desktop/test_TitleBar.qml b/test/qml/desktop/test_TitleBar.qml new file mode 100644 index 0000000..e0b14bb --- /dev/null +++ b/test/qml/desktop/test_TitleBar.qml @@ -0,0 +1,14 @@ +import QtTest 1.10 + +TestCase { + name: "ModuleImportTest" + id: parent + + function test_load_CancelAction() { + var item = createTemporaryQmlObject(" + import Governikus.TitleBar 1.0; + CancelAction {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/generic/CMakeLists.txt b/test/qml/generic/CMakeLists.txt new file mode 100644 index 0000000..1a779a3 --- /dev/null +++ b/test/qml/generic/CMakeLists.txt @@ -0,0 +1,6 @@ +ADD_QML_TEST_FILES(${RESOURCES_DIR}/qml SELECTORS "mobile;phone;android") +ADD_QML_TEST_FILES(${RESOURCES_DIR}/qml SELECTORS "mobile;tablet;android") +ADD_QML_TEST_FILES(${RESOURCES_DIR}/qml SELECTORS "mobile;phone;ios") +ADD_QML_TEST_FILES(${RESOURCES_DIR}/qml SELECTORS "mobile;tablet;ios") +ADD_QML_TEST_FILES(${RESOURCES_DIR}/qml SELECTORS "desktop;win") +ADD_QML_TEST_FILES(${RESOURCES_DIR}/qml SELECTORS "desktop;mac") diff --git a/test/qml/generic/test_Global.qml b/test/qml/generic/test_Global.qml new file mode 100644 index 0000000..2989887 --- /dev/null +++ b/test/qml/generic/test_Global.qml @@ -0,0 +1,74 @@ +import QtTest 1.10 + +import Governikus.Global 1.0 + +TestCase { + name: "ModuleImportTest" + id: parent + + + function test_load_Global() { + var item = createTemporaryQmlObject(" + import QtQuick 2.0; + import Governikus.Global 1.0; + Item {} + ", parent); + item.destroy(); + } + + function test_Constants() { + compare(Constants.black, "#000000") + compare(Constants.white, "#ffffff") + + verify(Constants.titlebar_padding > 0) + verify(Constants.titlebar_spacing > 0) + } + + function test_load_StatusIcon() { + var item = createTemporaryQmlObject(" + import Governikus.Global 1.0; + StatusIcon {} + ", parent); + item.destroy(); + } + + function test_load_LabeledText() { + var item = createTemporaryQmlObject(" + import Governikus.Global 1.0; + LabeledText {} + ", parent); + item.destroy(); + } + + function test_load_GButton() { + var item = createTemporaryQmlObject(" + import Governikus.Global 1.0; + GButton {} + ", parent); + item.destroy(); + } + + function test_load_PaneTitle() { + var item = createTemporaryQmlObject(" + import Governikus.Global 1.0; + PaneTitle {} + ", parent); + item.destroy(); + } + + function test_load_Pane() { + var item = createTemporaryQmlObject(" + import Governikus.Global 1.0; + Pane {} + ", parent); + item.destroy(); + } + + function test_load_GCheckBox() { + var item = createTemporaryQmlObject(" + import Governikus.Global 1.0; + GCheckBox {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/generic/test_IdentifyView.qml b/test/qml/generic/test_IdentifyView.qml new file mode 100644 index 0000000..f5a3ee2 --- /dev/null +++ b/test/qml/generic/test_IdentifyView.qml @@ -0,0 +1,14 @@ +import QtTest 1.10 + +TestCase { + name: "ModuleImportTest" + id: parent + + function test_load_IdentifyView() { + var item = createTemporaryQmlObject(" + import Governikus.IdentifyView 1.0; + IdentifyView {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/generic/test_MainView.qml b/test/qml/generic/test_MainView.qml new file mode 100644 index 0000000..0fe5782 --- /dev/null +++ b/test/qml/generic/test_MainView.qml @@ -0,0 +1,14 @@ +import QtTest 1.10 + +TestCase { + name: "ModuleImportTest" + id: parent + + function test_load_MainView() { + var item = createTemporaryQmlObject(" + import Governikus.MainView 1.0; + MainView {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/generic/test_ProgressView.qml b/test/qml/generic/test_ProgressView.qml new file mode 100644 index 0000000..0a26824 --- /dev/null +++ b/test/qml/generic/test_ProgressView.qml @@ -0,0 +1,14 @@ +import QtTest 1.10 + +TestCase { + name: "ModuleImportTest" + id: parent + + function test_load_ProgressView() { + var item = createTemporaryQmlObject(" + import Governikus.ProgressView 1.0; + ProgressView {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/generic/test_Provider.qml b/test/qml/generic/test_Provider.qml new file mode 100644 index 0000000..d16a3a7 --- /dev/null +++ b/test/qml/generic/test_Provider.qml @@ -0,0 +1,14 @@ +import QtTest 1.10 + +TestCase { + name: "ModuleLoadingProvider" + id: parent + + function test_load_ProviderInfoSection() { + var item = createTemporaryQmlObject(" + import Governikus.Provider 1.0; + ProviderInfoSection {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/generic/test_ResultView.qml b/test/qml/generic/test_ResultView.qml new file mode 100644 index 0000000..0773c74 --- /dev/null +++ b/test/qml/generic/test_ResultView.qml @@ -0,0 +1,14 @@ +import QtTest 1.10 + +TestCase { + name: "ModuleImportTest" + id: parent + + function test_load_ResultView() { + var item = createTemporaryQmlObject(" + import Governikus.ResultView 1.0; + ResultView {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/generic/test_Style.qml b/test/qml/generic/test_Style.qml new file mode 100644 index 0000000..6aa0cd4 --- /dev/null +++ b/test/qml/generic/test_Style.qml @@ -0,0 +1,23 @@ +import QtTest 1.10 + +TestCase { + name: "ModuleLoadingStyle" + id: parent + + function test_load_NpaBusyIndicatorStyle() { + var item = createTemporaryQmlObject(" + import Governikus.Style 1.0; + NpaBusyIndicatorStyle {} + ", parent); + item.destroy(); + } + + function test_load_ProviderStyle() { + var item = createTemporaryQmlObject(" + import QtQuick 2.0; + import Governikus.Style 1.0; + Item {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/generic/test_TitleBar.qml b/test/qml/generic/test_TitleBar.qml new file mode 100644 index 0000000..1e9d58b --- /dev/null +++ b/test/qml/generic/test_TitleBar.qml @@ -0,0 +1,14 @@ +import QtTest 1.10 + +TestCase { + name: "ModuleImportTest" + id: parent + + function test_load_TitleBar() { + var item = createTemporaryQmlObject(" + import Governikus.TitleBar 1.0; + TitleBar {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/generic/test_UiPluginQml.qml b/test/qml/generic/test_UiPluginQml.qml new file mode 100644 index 0000000..df6827b --- /dev/null +++ b/test/qml/generic/test_UiPluginQml.qml @@ -0,0 +1,30 @@ +import QtTest 1.10 + +import Governikus.Type.UiModule 1.0 + +TestCase { + name: "UiModule enumeration" + id: parent + + function test_load_UiModule() { + var item = createTemporaryQmlObject(" + import QtQuick 2.10 + import Governikus.Type.UiModule 1.0 + Item { + readonly property int v1: UiModule.CURRENT + readonly property int v2: UiModule.DEFAULT + readonly property int v3: UiModule.IDENTIFY + readonly property int v4: UiModule.PINMANAGEMENT + readonly property int v5: UiModule.SETTINGS + } + ", parent); + item.destroy(); + } + + function test_enumeration() { + verify(UiModule.CURRENT !== UiModule.DEFAULT) + verify(UiModule.DEFAULT !== UiModule.IDENTIFY) + verify(UiModule.IDENTIFY !== UiModule.PINMANAGEMENT) + verify(UiModule.PINMANAGEMENT !== UiModule.SETTINGS) + } +} diff --git a/test/qml/generic/test_Utils.qml b/test/qml/generic/test_Utils.qml new file mode 100644 index 0000000..1f03fe4 --- /dev/null +++ b/test/qml/generic/test_Utils.qml @@ -0,0 +1,14 @@ +import QtTest 1.10 + +import Governikus.Global 1.0 + +TestCase { + name: "MathTests" + + + function test_escapeHtml() { + compare(Utils.escapeHtml("a&b"), "a&b", "escape &") + compare(Utils.escapeHtml("
"), "<br/>", "escape < and >") + compare(Utils.escapeHtml("\"Hello\""), ""Hello"", "escape \"") + } +} diff --git a/test/qml/mobile/CMakeLists.txt b/test/qml/mobile/CMakeLists.txt new file mode 100644 index 0000000..a1a4e5f --- /dev/null +++ b/test/qml/mobile/CMakeLists.txt @@ -0,0 +1,4 @@ +ADD_QML_TEST_FILES(${RESOURCES_DIR}/qml SELECTORS "mobile;phone;android") +ADD_QML_TEST_FILES(${RESOURCES_DIR}/qml SELECTORS "mobile;tablet;android") +ADD_QML_TEST_FILES(${RESOURCES_DIR}/qml SELECTORS "mobile;phone;ios") +ADD_QML_TEST_FILES(${RESOURCES_DIR}/qml SELECTORS "mobile;tablet;ios") diff --git a/test/qml/mobile/test_ChangePinView.qml b/test/qml/mobile/test_ChangePinView.qml new file mode 100644 index 0000000..37b8ba0 --- /dev/null +++ b/test/qml/mobile/test_ChangePinView.qml @@ -0,0 +1,14 @@ +import QtTest 1.10 + +TestCase { + name: "ModuleImportTest" + id: parent + + function test_load_ChangePinView() { + var item = createTemporaryQmlObject(" + import Governikus.ChangePinView 1.0; + ChangePinView {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/mobile/test_DeveloperView.qml b/test/qml/mobile/test_DeveloperView.qml new file mode 100644 index 0000000..beb72af --- /dev/null +++ b/test/qml/mobile/test_DeveloperView.qml @@ -0,0 +1,14 @@ +import QtTest 1.10 + +TestCase { + name: "ModuleImportTest" + id: parent + + function test_load_DeveloperView() { + var item = createTemporaryQmlObject(" + import Governikus.DeveloperView 1.0; + DeveloperView {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/mobile/test_EnterPinView.qml b/test/qml/mobile/test_EnterPinView.qml new file mode 100644 index 0000000..fa1b9d3 --- /dev/null +++ b/test/qml/mobile/test_EnterPinView.qml @@ -0,0 +1,14 @@ +import QtTest 1.10 + +TestCase { + name: "ModuleImportTest" + id: parent + + function test_load_EnterPinView() { + var item = createTemporaryQmlObject(" + import Governikus.EnterPinView 1.0; + EnterPinView {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/mobile/test_FeedbackView.qml b/test/qml/mobile/test_FeedbackView.qml new file mode 100644 index 0000000..ba5801f --- /dev/null +++ b/test/qml/mobile/test_FeedbackView.qml @@ -0,0 +1,22 @@ +import QtTest 1.10 + +TestCase { + name: "ModuleImportTest" + id: parent + + function test_load_FeedbackView() { + var item = createTemporaryQmlObject(" + import Governikus.FeedbackView 1.0; + Feedback {} + ", parent); + item.destroy(); + } + + function test_load_Log() { + var item = createTemporaryQmlObject(" + import Governikus.FeedbackView 1.0; + Log {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/mobile/test_Global.qml b/test/qml/mobile/test_Global.qml new file mode 100644 index 0000000..ba2c5aa --- /dev/null +++ b/test/qml/mobile/test_Global.qml @@ -0,0 +1,54 @@ +import QtTest 1.10 + +TestCase { + name: "ModuleImportTest" + id: parent + + function test_load_GComboBox() { + var item = createTemporaryQmlObject(" + import Governikus.Global 1.0; + GComboBox {} + ", parent); + item.destroy(); + } + + function test_load_ConfirmationPopup() { + var item = createTemporaryQmlObject(" + import Governikus.Global 1.0; + ConfirmationPopup {} + ", parent); + item.destroy(); + } + + function test_load_GRadioButton() { + var item = createTemporaryQmlObject(" + import Governikus.Global 1.0; + GRadioButton {} + ", parent); + item.destroy(); + } + + function test_load_GSwitch() { + var item = createTemporaryQmlObject(" + import Governikus.Global 1.0; + GSwitch {} + ", parent); + item.destroy(); + } + + function test_load_GTextField() { + var item = createTemporaryQmlObject(" + import Governikus.Global 1.0; + GTextField {} + ", parent); + item.destroy(); + } + + function test_load_LocationButton() { + var item = createTemporaryQmlObject(" + import Governikus.Global 1.0; + LocationButton {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/mobile/test_HistoryView.qml b/test/qml/mobile/test_HistoryView.qml new file mode 100644 index 0000000..e25cd6f --- /dev/null +++ b/test/qml/mobile/test_HistoryView.qml @@ -0,0 +1,14 @@ +import QtTest 1.10 + +TestCase { + name: "ModuleImportTest" + id: parent + + function test_load_HistoryView() { + var item = createTemporaryQmlObject(" + import Governikus.HistoryView 1.0; + HistoryView {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/mobile/test_InformationView.qml b/test/qml/mobile/test_InformationView.qml new file mode 100644 index 0000000..5d8c40b --- /dev/null +++ b/test/qml/mobile/test_InformationView.qml @@ -0,0 +1,22 @@ +import QtTest 1.10 + +TestCase { + name: "ModuleLoadingInformationView" + id: parent + + function test_load_Information() { + var item = createTemporaryQmlObject(" + import Governikus.InformationView 1.0; + Information {} + ", parent); + item.destroy(); + } + + function test_load_VersionInformationView() { + var item = createTemporaryQmlObject(" + import Governikus.InformationView 1.0; + VersionInformation {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/mobile/test_MoreView.qml b/test/qml/mobile/test_MoreView.qml new file mode 100644 index 0000000..06e0aaa --- /dev/null +++ b/test/qml/mobile/test_MoreView.qml @@ -0,0 +1,14 @@ +import QtTest 1.10 + +TestCase { + name: "ModuleImportTest" + id: parent + + function test_load_MoreView() { + var item = createTemporaryQmlObject(" + import Governikus.MoreView 1.0; + MoreView {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/mobile/test_Navigation.qml b/test/qml/mobile/test_Navigation.qml new file mode 100644 index 0000000..14a2743 --- /dev/null +++ b/test/qml/mobile/test_Navigation.qml @@ -0,0 +1,14 @@ +import QtTest 1.10 + +TestCase { + name: "ModuleImportTest" + id: parent + + function test_load_Navigation() { + var item = createTemporaryQmlObject(" + import Governikus.Navigation 1.0; + Navigation {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/mobile/test_Provider.qml b/test/qml/mobile/test_Provider.qml new file mode 100644 index 0000000..98f7bfd --- /dev/null +++ b/test/qml/mobile/test_Provider.qml @@ -0,0 +1,46 @@ +import QtTest 1.10 + +TestCase { + name: "ModuleLoadingProvider" + id: parent + + function test_load_ProviderContactTab() { + var item = createTemporaryQmlObject(" + import Governikus.Provider 1.0; + ProviderContactTab {} + ", parent); + item.destroy(); + } + + function test_load_ProviderDetailView() { + var item = createTemporaryQmlObject(" + import Governikus.Provider 1.0; + ProviderDetailView {} + ", parent); + item.destroy(); + } + + function test_load_ProviderHeader() { + var item = createTemporaryQmlObject(" + import Governikus.Provider 1.0; + ProviderHeader {} + ", parent); + item.destroy(); + } + + function test_load_ProviderModelItem() { + var item = createTemporaryQmlObject(" + import Governikus.Provider 1.0; + ProviderModelItem {} + ", parent); + item.destroy(); + } + + function test_load_ProviderViewDelegate() { + var item = createTemporaryQmlObject(" + import Governikus.Provider 1.0; + ProviderViewDelegate {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/mobile/test_ProviderView.qml b/test/qml/mobile/test_ProviderView.qml new file mode 100644 index 0000000..f79527d --- /dev/null +++ b/test/qml/mobile/test_ProviderView.qml @@ -0,0 +1,14 @@ +import QtTest 1.10 + +TestCase { + name: "ModuleImportTest" + id: parent + + function test_load_ProviderView() { + var item = createTemporaryQmlObject(" + import Governikus.ProviderView 1.0; + ProviderView {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/mobile/test_RemoteServiceView.qml b/test/qml/mobile/test_RemoteServiceView.qml new file mode 100644 index 0000000..05c6428 --- /dev/null +++ b/test/qml/mobile/test_RemoteServiceView.qml @@ -0,0 +1,14 @@ +import QtTest 1.10 + +TestCase { + name: "ModuleImportTest" + id: parent + + function test_load_RemoteServiceView() { + var item = createTemporaryQmlObject(" + import Governikus.RemoteServiceView 1.0; + RemoteServiceView {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/mobile/test_SplashScreen.qml b/test/qml/mobile/test_SplashScreen.qml new file mode 100644 index 0000000..1e89376 --- /dev/null +++ b/test/qml/mobile/test_SplashScreen.qml @@ -0,0 +1,14 @@ +import QtTest 1.10 + +TestCase { + name: "ModuleImportTest" + id: parent + + function test_load_SplashScreen() { + var item = createTemporaryQmlObject(" + import Governikus.SplashScreen 1.0; + SplashScreen {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/mobile/test_TechnologyInfo.qml b/test/qml/mobile/test_TechnologyInfo.qml new file mode 100644 index 0000000..ec629f4 --- /dev/null +++ b/test/qml/mobile/test_TechnologyInfo.qml @@ -0,0 +1,14 @@ +import QtTest 1.10 + +TestCase { + name: "ModuleImportTest" + id: parent + + function test_load_TechnologyInfo() { + var item = createTemporaryQmlObject(" + import Governikus.TechnologyInfo 1.0; + TechnologyInfo {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/mobile/test_TutorialView.qml b/test/qml/mobile/test_TutorialView.qml new file mode 100644 index 0000000..46250a6 --- /dev/null +++ b/test/qml/mobile/test_TutorialView.qml @@ -0,0 +1,14 @@ +import QtTest 1.10 + +TestCase { + name: "ModuleImportTest" + id: parent + + function test_load_TutorialView() { + var item = createTemporaryQmlObject(" + import Governikus.TutorialView 1.0; + TutorialView {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/mobile/test_View.qml b/test/qml/mobile/test_View.qml new file mode 100644 index 0000000..d542e45 --- /dev/null +++ b/test/qml/mobile/test_View.qml @@ -0,0 +1,22 @@ +import QtTest 1.10 + +TestCase { + name: "ModuleLoadingView" + id: parent + + function test_load_TabBarView() { + var item = createTemporaryQmlObject(" + import Governikus.View 1.0; + TabBarView {} + ", parent); + item.destroy(); + } + + function test_load_ContentArea() { + var item = createTemporaryQmlObject(" + import Governikus.View 1.0; + ContentArea {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/mobile/test_WhiteListClientView.qml b/test/qml/mobile/test_WhiteListClientView.qml new file mode 100644 index 0000000..c0f07f1 --- /dev/null +++ b/test/qml/mobile/test_WhiteListClientView.qml @@ -0,0 +1,14 @@ +import QtTest 1.10 + +TestCase { + name: "ModuleImportTest" + id: parent + + function test_load_WhiteListSurveyView() { + var item = createTemporaryQmlObject(" + import Governikus.WhiteListClient 1.0; + WhiteListSurveyView {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/mobile/test_Workflow.qml b/test/qml/mobile/test_Workflow.qml new file mode 100644 index 0000000..a8b3d43 --- /dev/null +++ b/test/qml/mobile/test_Workflow.qml @@ -0,0 +1,22 @@ +import QtTest 1.10 + +TestCase { + name: "ModuleLoadingWorkflow" + id: parent + + function test_load_Workflow() { + var item = createTemporaryQmlObject(" + import Governikus.Workflow 1.0; + Workflow {} + ", parent); + item.destroy(); + } + + function test_load_GeneralWorkflow() { + var item = createTemporaryQmlObject(" + import Governikus.Workflow 1.0; + GeneralWorkflow {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/test_Utils.qml b/test/qml/test_Utils.qml deleted file mode 100644 index f2d9cad..0000000 --- a/test/qml/test_Utils.qml +++ /dev/null @@ -1,16 +0,0 @@ -import QtQuick 2.3 -import QtTest 1.0 - -// import Governikus.Global 1.0 - -TestCase { - name: "MathTests" - - - function test_escapeHtml() { - // compare(Utils.escapeHtml("a&b"), "a&b", "escape &") - // compare(Utils.escapeHtml("
"), "<br/>", "escape < and >") - // compare(Utils.escapeHtml("\"Hello\""), ""Hello"", "escape \"") - } - -} diff --git a/test/qml_stationary/AusweisApp2/Global/test_Utils.qml b/test/qml_stationary/AusweisApp2/Global/test_Utils.qml deleted file mode 100644 index 46ba91d..0000000 --- a/test/qml_stationary/AusweisApp2/Global/test_Utils.qml +++ /dev/null @@ -1,16 +0,0 @@ -import QtQuick 2.3 -import QtTest 1.0 - -import AusweisApp2.Global 1.0 - -TestCase { - name: "MathTests" - - - function test_escapeHtml() { - compare(Utils.escapeHtml("a&b"), "a&b", "escape &") - compare(Utils.escapeHtml("
"), "<br/>", "escape < and >") - compare(Utils.escapeHtml("\"Hello\""), ""Hello"", "escape \"") - } - -} diff --git a/test/qml_stationary/CMakeLists.txt b/test/qml_stationary/CMakeLists.txt deleted file mode 100644 index 5242c49..0000000 --- a/test/qml_stationary/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -ADD_QML_TEST_FILES(${RESOURCES_DIR}/qml_stationary) diff --git a/test/qt/CMakeLists.txt b/test/qt/CMakeLists.txt index 00fac9f..40e2ab6 100644 --- a/test/qt/CMakeLists.txt +++ b/test/qt/CMakeLists.txt @@ -1,21 +1,27 @@ FUNCTION(ADD_TEST_EXECUTABLE testname) - ADD_EXECUTABLE(${testname} ${ARGN}) + IF(ANDROID) + ADD_LIBRARY(${testname} SHARED ${ARGN}) + ELSE() + ADD_EXECUTABLE(${testname} ${ARGN}) + ENDIF() - TARGET_LINK_LIBRARIES(${testname} Qt5::Network Qt5::Xml Qt5::Test OpenSSL::Crypto) + TARGET_LINK_LIBRARIES(${testname} Qt5::Network Qt5::Test OpenSSL::Crypto) TARGET_LINK_LIBRARIES(${testname} AusweisAppTestHelper AusweisAppCore AusweisAppCard AusweisAppGlobal AusweisAppCardDrivers AusweisAppServices AusweisAppSettings AusweisAppNetwork) - TARGET_LINK_LIBRARIES(${testname} AusweisAppActivationInternal AusweisAppJsonApi AusweisAppAidl AusweisAppQml) - TARGET_LINK_LIBRARIES(${testname} AusweisAppCardRemote AusweisAppExport) + TARGET_LINK_LIBRARIES(${testname} AusweisAppActivationInternal AusweisAppUiJsonApi AusweisAppUiAidl AusweisAppUiQml) + TARGET_LINK_LIBRARIES(${testname} AusweisAppRemoteDevice AusweisAppExport) TARGET_LINK_LIBRARIES(${testname} AusweisAppSecureStorage AusweisAppConfiguration AusweisAppFileProvider) TARGET_LINK_LIBRARIES(${testname} QRC_FIXTURE_OBJ) IF(DESKTOP) - TARGET_LINK_LIBRARIES(${testname} Qt5::Widgets AusweisAppWidget AusweisAppCardPcsc AusweisAppActivationWebservice) + TARGET_LINK_LIBRARIES(${testname} Qt5::Widgets AusweisAppUiWidget AusweisAppCardPcsc AusweisAppActivationWebservice) ENDIF() IF(LINUX OR ANDROID OR IOS) TARGET_LINK_LIBRARIES(${testname} AusweisAppCardBluetooth) ENDIF() + TARGET_LINK_LIBRARIES(${testname} AusweisAppWhitelistClient) + IF(WIN32) TARGET_LINK_LIBRARIES(${testname} ${WIN_DEFAULT_LIBS}) ELSEIF(MAC) diff --git a/test/qt/activation_webservice/test_WebserviceActivationContext.cpp b/test/qt/activation_webservice/test_WebserviceActivationContext.cpp new file mode 100644 index 0000000..0fad071 --- /dev/null +++ b/test/qt/activation_webservice/test_WebserviceActivationContext.cpp @@ -0,0 +1,102 @@ +/*! + * \brief Unit tests for \ref WebserviceActivationContext + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "WebserviceActivationContext.h" + +#include "LogHandler.h" +#include "MockSocket.h" +#include "ResourceLoader.h" + +#include +#include + +using namespace governikus; + +class test_WebserviceActivationContext + : public QObject +{ + Q_OBJECT + QPointer mSocket; + QSharedPointer mRequest; + + private Q_SLOTS: + void initTestCase() + { + mSocket = new MockSocket(); + mRequest.reset(new HttpRequest(mSocket)); + + ResourceLoader::getInstance().init(); + } + + + void test_SetCommonHeaders() + { + HttpResponse response; + WebserviceActivationContext context(mRequest); + context.setCommonHeaders(response); + + QVERIFY(response.getHeaders().keys().contains(QByteArrayLiteral("Connection"))); + QVERIFY(response.getHeaders().keys().contains(QByteArrayLiteral("Cache-Control"))); + QVERIFY(response.getHeaders().keys().contains(QByteArrayLiteral("Pragma"))); + + QMap headers = response.getHeaders(); + QCOMPARE(headers.take(QByteArrayLiteral("Connection")), QByteArrayLiteral("close")); + QCOMPARE(headers.take(QByteArrayLiteral("Cache-Control")), QByteArrayLiteral("no-cache, no-store")); + QCOMPARE(headers.take(QByteArrayLiteral("Pragma")), QByteArrayLiteral("no-cache")); + } + + + void test_GetActivationUrl() + { + WebserviceActivationContext context(mRequest); + const QByteArray testUrl("testUrl"); + mRequest->mUrl = testUrl; + QCOMPARE(context.getActivationURL().toEncoded(), testUrl); + } + + + void test_SendProcessing() + { + WebserviceActivationContext context(mRequest); + QVERIFY(!context.sendProcessing()); + QCOMPARE(context.getSendError(), QString("The browser connection was lost.")); + + mSocket->setSocketState(QAbstractSocket::ConnectedState); + QVERIFY(context.sendProcessing()); + } + + + void test_SendActivationAlreadyActive() + { + WebserviceActivationContext context(mRequest); + + mSocket->setSocketState(QAbstractSocket::UnconnectedState); + QVERIFY(!context.sendOperationAlreadyActive()); + QCOMPARE(context.getSendError(), QString("The browser connection was lost.")); + + mSocket->setSocketState(QAbstractSocket::ConnectedState); + QVERIFY(context.sendOperationAlreadyActive()); + } + + + void test_SendRedirect() + { + WebserviceActivationContext context(mRequest); + + mSocket->setSocketState(QAbstractSocket::UnconnectedState); + QTest::ignoreMessage(QtDebugMsg, "Redirect URL: QUrl(\"?ResultMajor=ok\")"); + QVERIFY(!context.sendRedirect(QUrl(), GlobalStatus::Code::No_Error)); + QCOMPARE(context.getSendError(), QString("The connection to the browser was lost. No forwarding was executed. Please try to call the URL again manually: ")); + + mSocket->setSocketState(QAbstractSocket::ConnectedState); + QVERIFY(context.sendRedirect(QUrl(), GlobalStatus::Code::No_Error)); + } + + +}; + +QTEST_GUILESS_MAIN(test_WebserviceActivationContext) +#include "test_WebserviceActivationContext.moc" diff --git a/test/qt/activation_webservice/test_WebserviceActivationHandler.cpp b/test/qt/activation_webservice/test_WebserviceActivationHandler.cpp index 466ef87..0d53991 100644 --- a/test/qt/activation_webservice/test_WebserviceActivationHandler.cpp +++ b/test/qt/activation_webservice/test_WebserviceActivationHandler.cpp @@ -28,7 +28,7 @@ class test_WebserviceActivationHandler private Q_SLOTS: void initTestCase() { - LogHandler::getInstance().init(); + Env::getSingleton()->init(); ResourceLoader::getInstance().init(); } @@ -45,7 +45,7 @@ class test_WebserviceActivationHandler void cleanup() { - LogHandler::getInstance().resetBacklog(); + Env::getSingleton()->resetBacklog(); } @@ -183,7 +183,7 @@ class test_WebserviceActivationHandler mRequest->mUrl = QByteArray("http://localhost:24727/eID-Client?unknownRequest"); mHandler.onNewRequest(mRequest); - QVERIFY(mSocket->mWriteBuffer.contains("HTTP/1.0 404 NOT FOUND")); + QVERIFY(mSocket->mWriteBuffer.contains("HTTP/1.0 404 Not Found")); } @@ -203,7 +203,7 @@ class test_WebserviceActivationHandler void currentHigherThanCallerUserAgent() { - QSignalSpy spy(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); QCoreApplication::setApplicationVersion("1.0.0"); mRequest->mUrl = QByteArray("http://localhost:24727/eID-Client?ShowUI"); mRequest->mHeader.insert("user-agent", QCoreApplication::applicationName().toUtf8() + "/0.0.0 (TR-03124-1/1.2)"); @@ -223,7 +223,7 @@ class test_WebserviceActivationHandler void currentLowerThanCallerUserAgent() { - QSignalSpy spy(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); QCoreApplication::setApplicationVersion("1.0.0"); mRequest->mUrl = QByteArray("http://localhost:24727/eID-Client?ShowUI"); mRequest->mHeader.insert("user-agent", QCoreApplication::applicationName().toUtf8() + "/2.0.0 (TR-03124-1/1.2)"); @@ -241,6 +241,34 @@ class test_WebserviceActivationHandler } + void test_GuessImageContentType_data() + { + QTest::addColumn("fileName"); + QTest::addColumn("output"); + + QTest::newRow("x-icon") << QString("test.ico") << QByteArrayLiteral("image/x-icon"); + QTest::newRow("X-ICON") << QString("test.iCo") << QByteArrayLiteral("image/x-icon"); + QTest::newRow("jpg") << QString("test.jpg") << QByteArrayLiteral("image/jpeg"); + QTest::newRow("JPG") << QString("test.JPG") << QByteArrayLiteral("image/jpeg"); + QTest::newRow("jpeg") << QString("test.jpeg") << QByteArrayLiteral("image/jpeg"); + QTest::newRow("JPEG") << QString("test.Jpeg") << QByteArrayLiteral("image/jpeg"); + QTest::newRow("png") << QString("test.png") << QByteArrayLiteral("image/png"); + QTest::newRow("PNG") << QString("test.pNG") << QByteArrayLiteral("image/png"); + QTest::newRow("svg") << QString("test.svg") << QByteArrayLiteral("image/svg+xml"); + QTest::newRow("SVG") << QString("TEST.SVG") << QByteArrayLiteral("image/svg+xml"); + QTest::newRow("other") << QString("test.test") << QByteArrayLiteral("image"); + } + + + void test_GuessImageContentType() + { + QFETCH(QString, fileName); + QFETCH(QByteArray, output); + + QCOMPARE(mHandler.guessImageContentType(fileName), output); + } + + }; QTEST_GUILESS_MAIN(test_WebserviceActivationHandler) diff --git a/test/qt/card/asn1/test_AccessRoleAndRight.cpp b/test/qt/card/asn1/test_AccessRoleAndRight.cpp index 588374f..30674e1 100644 --- a/test/qt/card/asn1/test_AccessRoleAndRight.cpp +++ b/test/qt/card/asn1/test_AccessRoleAndRight.cpp @@ -73,7 +73,7 @@ class test_AccessRoleAndRight right = pRight; }; - bool success = AccessRoleAndRightsUtil::fromTechnicalName(QTest::currentDataTag(), func); + bool success = AccessRoleAndRightsUtil::fromTechnicalName(QString::fromLatin1(QTest::currentDataTag()), func); if (right != value) { QVERIFY(!success); @@ -93,20 +93,30 @@ class test_AccessRoleAndRight right = pRight; }; - QVERIFY(!AccessRoleAndRightsUtil::fromTechnicalName("crap", func)); + QVERIFY(!AccessRoleAndRightsUtil::fromTechnicalName(QString("crap"), func)); QCOMPARE(right, undefined); - QVERIFY(!AccessRoleAndRightsUtil::fromTechnicalName("CAN_ALLOWED", func)); + QVERIFY(!AccessRoleAndRightsUtil::fromTechnicalName(QString("CAN_ALLOWED"), func)); QCOMPARE(right, undefined); - QVERIFY(!AccessRoleAndRightsUtil::fromTechnicalName("CanAllowed", func)); + QVERIFY(!AccessRoleAndRightsUtil::fromTechnicalName(QString("CanAllowed"), func)); QCOMPARE(right, undefined); - QVERIFY(!AccessRoleAndRightsUtil::fromTechnicalName("familyname", func)); + QVERIFY(!AccessRoleAndRightsUtil::fromTechnicalName(QString("familyname"), func)); QCOMPARE(right, undefined); } + void checkJoinFromTechnicalName() + { + auto data = AccessRoleAndRightsUtil::joinFromTechnicalName({"DocumentType", "DoctoralDegree"}); + QCOMPARE(data, QStringLiteral("Document type, Doctoral degree")); + + data = AccessRoleAndRightsUtil::joinFromTechnicalName({"crap", "FamilyName"}); + QCOMPARE(data, QStringLiteral("crap, Family name")); + } + + /** * We use UINT_MAX as "undefined" state in AccessRoleAndRightsUtil::fromTechnicalName. * Ensure that we never use UINT_MAX as a valid value! @@ -127,6 +137,65 @@ class test_AccessRoleAndRight } + void test_ToDisplayText_data() + { + QTest::addColumn("input"); + QTest::addColumn("output"); + + const auto& reserved = QStringLiteral("Unknown (reserved)"); + QTest::newRow("RFU_29") << AccessRight::RFU_29 << reserved; + QTest::newRow("RFU_30") << AccessRight::RFU_30 << reserved; + QTest::newRow("RFU_31") << AccessRight::RFU_31 << reserved; + QTest::newRow("RFU_32") << AccessRight::RFU_32 << reserved; + + QTest::newRow("writeDG17") << AccessRight::WRITE_DG17 << QString("WRITE_DG17"); + QTest::newRow("writeDG18") << AccessRight::WRITE_DG18 << QString("WRITE_DG18"); + QTest::newRow("writeDG19") << AccessRight::WRITE_DG19 << QString("WRITE_DG19"); + QTest::newRow("writeDG20") << AccessRight::WRITE_DG20 << QString("WRITE_DG20"); + QTest::newRow("writeDG21") << AccessRight::WRITE_DG21 << QString("WRITE_DG21"); + QTest::newRow("readDG21") << AccessRight::READ_DG21 << QString("Optional data"); + QTest::newRow("readDG20") << AccessRight::READ_DG20 << QString("Residence permit II"); + QTest::newRow("readDG19") << AccessRight::READ_DG19 << QString("Residence permit I"); + QTest::newRow("readDG18") << AccessRight::READ_DG18 << QString("Community-ID"); + QTest::newRow("readDG17") << AccessRight::READ_DG17 << QString("Address"); + QTest::newRow("readDG16") << AccessRight::READ_DG16 << QString("RFU"); + QTest::newRow("readDG15") << AccessRight::READ_DG15 << QString("RFU"); + QTest::newRow("readDG14") << AccessRight::READ_DG14 << QString("RFU"); + QTest::newRow("readDG13") << AccessRight::READ_DG13 << QString("Birth name"); + QTest::newRow("readDG12") << AccessRight::READ_DG12 << QString("Optional data"); + QTest::newRow("readDG11") << AccessRight::READ_DG11 << QString("Gender"); + QTest::newRow("readDG10") << AccessRight::READ_DG10 << QString("Nationality"); + QTest::newRow("readDG09") << AccessRight::READ_DG09 << QString("Place of birth"); + QTest::newRow("readDG08") << AccessRight::READ_DG08 << QString("Date of birth"); + QTest::newRow("readDG07") << AccessRight::READ_DG07 << QString("Doctoral degree"); + QTest::newRow("readDG06") << AccessRight::READ_DG06 << QString("Religious / artistic name"); + QTest::newRow("readDG05") << AccessRight::READ_DG05 << QString("Family name"); + QTest::newRow("readDG04") << AccessRight::READ_DG04 << QString("Given name(s)"); + QTest::newRow("readDG03") << AccessRight::READ_DG03 << QString("Valid until"); + QTest::newRow("readDG02") << AccessRight::READ_DG02 << QString("Issuing country"); + QTest::newRow("readDG01") << AccessRight::READ_DG01 << QString("Document type"); + QTest::newRow("installQualCert") << AccessRight::INSTALL_QUAL_CERT << QString("Installation of qualified signature certificates"); + QTest::newRow("installCert") << AccessRight::INSTALL_CERT << QString("Installation of signature certificates"); + QTest::newRow("pinManagement") << AccessRight::PIN_MANAGEMENT << QString("PIN Management"); + QTest::newRow("canAllowed") << AccessRight::CAN_ALLOWED << QString("CAN allowed"); + QTest::newRow("privilegedTerminal") << AccessRight::PRIVILEGED_TERMINAL << QString("Privileged terminal"); + QTest::newRow("restrictedIdentification") << AccessRight::RESTRICTED_IDENTIFICATION << QString("Pseudonym"); + QTest::newRow("comunityIdVerification") << AccessRight::COMMUNITY_ID_VERIFICATION << QString("Address verification"); + QTest::newRow("ageVerification") << AccessRight::AGE_VERIFICATION << QString("Age verification"); + + QTest::newRow("default") << static_cast(100) << QString("Unknown"); + } + + + void test_ToDisplayText() + { + QFETCH(AccessRight, input); + QFETCH(QString, output); + + QCOMPARE(AccessRoleAndRightsUtil::toDisplayText(input), output); + } + + }; diff --git a/test/qt/card/asn1/test_CVCertificate.cpp b/test/qt/card/asn1/test_CVCertificate.cpp index efae3e5..cd9d529 100644 --- a/test/qt/card/asn1/test_CVCertificate.cpp +++ b/test/qt/card/asn1/test_CVCertificate.cpp @@ -12,6 +12,7 @@ #include "LogHandler.h" #include "TestFileHelper.h" +#include #include using namespace governikus; @@ -37,13 +38,13 @@ class test_CVCertificate private Q_SLOTS: void initTestCase() { - LogHandler::getInstance().init(); + Env::getSingleton()->init(); } void cleanup() { - LogHandler::getInstance().resetBacklog(); + Env::getSingleton()->resetBacklog(); } @@ -133,7 +134,7 @@ class test_CVCertificate auto ecdsaSignature = cvca1->getEcdsaSignature(); -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) const BIGNUM* r = ecdsaSignature->r; const BIGNUM* s = ecdsaSignature->s; #else @@ -149,7 +150,7 @@ class test_CVCertificate void debugStream() { - QSignalSpy spy(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); QLatin1String output(R"(CVC(type=CVCA, car="DETESTeID00001", chr="DETESTeID00001", valid=["2010-08-13","2013-08-13"])"); QSharedPointer cvca = CVCertificate::fromHex(TestFileHelper::readFile(":/card/cvca-DETESTeID00001.hex")); diff --git a/test/qt/card/asn1/test_CVCertificateChainBuilder.cpp b/test/qt/card/asn1/test_CVCertificateChainBuilder.cpp index 33b8f71..ca93d32 100644 --- a/test/qt/card/asn1/test_CVCertificateChainBuilder.cpp +++ b/test/qt/card/asn1/test_CVCertificateChainBuilder.cpp @@ -39,11 +39,11 @@ class test_CVCertificateChainBuilder /*! - * Creates an object of EstablishPACEChannelOutput with CARcurr, and CARprev equal to + * Creates an object of EstablishPaceChannelOutput with CARcurr, and CARprev equal to */ - static EstablishPACEChannelOutput createPaceOutput(QSharedPointer pCvcCarCurr, QSharedPointer pCvcCarPrev) + static EstablishPaceChannelOutput createPaceOutput(QSharedPointer pCvcCarCurr, QSharedPointer pCvcCarPrev) { - EstablishPACEChannelOutput output; + EstablishPaceChannelOutput output; if (pCvcCarCurr != nullptr) { output.setCarCurr(pCvcCarCurr->getBody().getCertificateHolderReference()); @@ -392,7 +392,7 @@ class test_CVCertificateChainBuilder list.append(mCvdv_DEDVeIDDPST00035); list.append(mCvat_DEDEMODEV00038); CVCertificateChainBuilder builder(list, false); - EstablishPACEChannelOutput output = createPaceOutput(mCvca_DETESTeID00004_DETESTeID00002, mCvca_DETESTeID00002); + EstablishPaceChannelOutput output = createPaceOutput(mCvca_DETESTeID00004_DETESTeID00002, mCvca_DETESTeID00002); CVCertificateChain chain = builder.getChainForCertificationAuthority(output); @@ -415,7 +415,7 @@ class test_CVCertificateChainBuilder list.append(mCvdv_DEDVeIDDPST00035); list.append(mCvat_DEDEMODEV00038); CVCertificateChainBuilder builder(list, false); - EstablishPACEChannelOutput output = createPaceOutput(mCvca_DETESTeID00005, mCvca_DETESTeID00002); + EstablishPaceChannelOutput output = createPaceOutput(mCvca_DETESTeID00005, mCvca_DETESTeID00002); CVCertificateChain chain = builder.getChainForCertificationAuthority(output); diff --git a/test/qt/card/asn1/test_CertificateDescription.cpp b/test/qt/card/asn1/test_CertificateDescription.cpp index a1c9f2c..ca742f6 100644 --- a/test/qt/card/asn1/test_CertificateDescription.cpp +++ b/test/qt/card/asn1/test_CertificateDescription.cpp @@ -35,7 +35,7 @@ const char* HEX_STRING("30 8202E0" ""); const char* SELF_AUTH_CERT_2017 = "308202e6060a04007f00070301030101a10e0c0c442d547275737420476d6248a2181316687474703a2f2f7777772e642d74727573742e6e6574a31a0c18476f7665726e696b757320476d6248202620436f2e204b47a41a131868747470733a2f2f7777772e617574656e746170702e6465a582020a0c820206efbbbf4e616d652c20416e7363687269667420756e6420452d4d61696c2d4164726573736520646573204469656e737465616e626965746572733a0d0a476f7665726e696b757320476d6248202620436f2e204b470d0a416d2046616c6c7475726d20390d0a3238333539204272656d656e0d0a6b6f6e74616b7440676f7665726e696b75732e636f6d0d0a0d0a4765736368c3a46674737a7765636b3a200d0a53656c6273746175736b756e66740d0a0d0a48696e7765697320617566206469652066c3bc722064656e204469656e737465616e626965746572207a757374c3a46e646967656e205374656c6c656e2c20646965206469652045696e68616c74756e672064657220566f7273636872696674656e207a756d20446174656e73636875747a206b6f6e74726f6c6c696572656e3a0d0a446965204c616e64657362656175667472616774652066c3bc7220446174656e73636875747a20756e6420496e666f726d6174696f6e736672656968656974206465722046726569656e2048616e73657374616474204272656d656e0d0a41726e647473747261c39f6520310d0a3237353730204272656d6572686176656e0d0a303432312f3539362d323031300d0a6f666669636540646174656e73636875747a2e6272656d656e2e64650d0a687474703a2f2f7777772e646174656e73636875747a2e6272656d656e2e64650d0aa76831660420a30a9a4617dc153926f731064043bba624b0cdd3b458ed8723c1cda33f1ffdd70420ab9fce5da4ba24d0b2664450fcced618f68fe9cbcdc4ee6e0bb0c59bd2aa86b60420fbf9f26b56b74cdf1c6e5cb1811bec1a8283a174c629b1974de17dc058b31bda"; -} +} // namespace class test_CertificateDescription @@ -57,6 +57,18 @@ class test_CertificateDescription */ private Q_SLOTS: + void init() + { + ERR_clear_error(); + } + + + void cleanup() + { + QCOMPARE(ERR_get_error(), 0); + } + + void parseCrap() { QByteArray hexString("30 8202A4"); @@ -240,7 +252,6 @@ class test_CertificateDescription certDescr = CertificateDescription::fromHex(SELF_AUTH_CERT_2017); QVERIFY(certDescr); QCOMPARE(certDescr->getServiceProviderAddress(), providerAddress2017); - } @@ -286,15 +297,7 @@ class test_CertificateDescription void opensslEncode() { - CertificateDescription* certDescr = nullptr; - if (!(certDescr = CertificateDescription_new())) - { - BIO* bio_err = BIO_new_fp(stderr, BIO_NOCLOSE); - ERR_print_errors(bio_err); - BIO_free(bio_err); - return; - } - + CertificateDescription* certDescr = CertificateDescription_new(); certDescr->setDescriptionType("0.4.0.127.0.7.3.1.3.1.3"); certDescr->setIssuerName("D-Trust GmbH"); @@ -316,7 +319,7 @@ class test_CertificateDescription QVERIFY(byteBuf.contains("Name, Anschrift und E-Mail-Adresse des Diensteanbieters:")); } -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) certDescr->mCommCertificates = SKM_sk_new(ASN1_OCTET_STRING, 0); #else certDescr->mCommCertificates = sk_ASN1_OCTET_STRING_new(0); @@ -330,7 +333,7 @@ class test_CertificateDescription { ASN1_OCTET_STRING* octetString = ASN1_OCTET_STRING_new(); ASN1_OCTET_STRING_set(octetString, reinterpret_cast(commCertByte.constData()), commCertByte.length()); -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) SKM_sk_push(ASN1_OCTET_STRING, certDescr->mCommCertificates, octetString); #else sk_ASN1_OCTET_STRING_push(certDescr->mCommCertificates, octetString); @@ -345,7 +348,6 @@ class test_CertificateDescription } } - QByteArray hexString("30 82038D" " 06 0A 04007F00070301030103" " A1 0E 0C0C442D547275737420476D6248" diff --git a/test/qt/card/asn1/test_EcdsaPublicKey.cpp b/test/qt/card/asn1/test_EcdsaPublicKey.cpp index c42744f..ce8479c 100644 --- a/test/qt/card/asn1/test_EcdsaPublicKey.cpp +++ b/test/qt/card/asn1/test_EcdsaPublicKey.cpp @@ -6,11 +6,11 @@ #include "asn1/CVCertificate.h" #include "asn1/EcdsaPublicKey.h" -#include "TestFileHelper.h" #include #include +#include #include #include @@ -217,7 +217,6 @@ class test_EcdsaPublicKey QCOMPARE(convert(order).toHex().toUpper(), QByteArray("A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7")); BN_clear_free(order); - const EC_POINT* generator = EC_GROUP_get0_generator(ecGroup); auto bufLen = EC_POINT_point2oct(ecGroup, generator, point_conversion_form_t::POINT_CONVERSION_UNCOMPRESSED, nullptr, 0, nullptr); @@ -229,7 +228,6 @@ class test_EcdsaPublicKey QCOMPARE(ecdsaPublicKey->getPublicKeyOid(), QByteArray("0.4.0.127.0.7.2.2.2.2.3")); QCOMPARE(ecdsaPublicKey->getPublicKeyOidValueBytes().toHex().toUpper(), QByteArray("04007F00070202020203")); QCOMPARE(ecdsaPublicKey->getUncompressedPublicPoint().toHex().toUpper(), QByteArray("043347ECF96FFB4BD9B8554EFBCCFC7D0B242F1071E29B4C9C622C79E339D840AF67BEB9B912692265D9C16C62573F4579FFD4DE2DE92BAB409DD5C5D48244A9F7")); - } @@ -247,7 +245,6 @@ class test_EcdsaPublicKey auto ecdsaPublicKey = EcdsaPublicKey::fromHex(hexString); - QVERIFY(ecdsaPublicKey != nullptr); QVERIFY(ecdsaPublicKey->getEcKey() != nullptr); @@ -272,7 +269,6 @@ class test_EcdsaPublicKey QCOMPARE(convert(order).toHex().toUpper(), QByteArray("A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7")); BN_clear_free(order); - const EC_POINT* generator = EC_GROUP_get0_generator(ecGroup); auto bufLen = EC_POINT_point2oct(ecGroup, generator, point_conversion_form_t::POINT_CONVERSION_UNCOMPRESSED, nullptr, 0, nullptr); Q_ASSERT(bufLen <= INT_MAX); diff --git a/test/qt/card/asn1/test_PACEInfo.cpp b/test/qt/card/asn1/test_PACEInfo.cpp deleted file mode 100644 index 2c0cbb6..0000000 --- a/test/qt/card/asn1/test_PACEInfo.cpp +++ /dev/null @@ -1,306 +0,0 @@ -/*! - * \brief Unit tests for \ref PACEInfo - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include -#include - -#include "asn1/KnownOIDs.h" -#include "asn1/PACEInfo.h" - - -using namespace governikus; - -class test_PACEInfo - : public QObject -{ - Q_OBJECT - - private Q_SLOTS: - void parseCrap() - { - QVERIFY(PACEInfo::decode("3003020101") == nullptr); - } - - - void parseChipAuthenticationInfo() - { - QByteArray bytes = QByteArray::fromHex("30 12" - " 06 0A 04007F00070202030202" - " 02 01 02" - " 02 01 08"); - - QVERIFY(PACEInfo::decode(bytes) == nullptr); - } - - - void parsePACEInfoWithWrongContent() - { - QByteArray bytes = QByteArray::fromHex("30 12" - " 06 0A 04007F00070202040202" - " 04 01 02" - " 04 01 08"); - - auto paceInfo = PACEInfo::decode(bytes); - - QVERIFY(paceInfo == nullptr); - } - - - void missingOid() - { - QByteArray bytes = QByteArray::fromHex("30 06" - " 02 01 02" - " 02 01 08"); - - QVERIFY(PACEInfo::decode(bytes) == nullptr); - } - - - void missingVersion() - { - QByteArray bytes = QByteArray::fromHex("30 0C" - " 06 0A 04007F00070202040202"); - - QVERIFY(PACEInfo::decode(bytes) == nullptr); - } - - - void withoutOptionalParameter() - { - QByteArray bytes = QByteArray::fromHex("30 0F" - " 06 0A 04007F00070202040202" - " 02 01 02"); - - auto paceInfo = PACEInfo::decode(bytes); - - QVERIFY(paceInfo != nullptr); - QCOMPARE(paceInfo->getParameterIdAsInt(), -1); - } - - - void getParameterId() - { - QByteArray bytes = QByteArray::fromHex("30 12" - " 06 0A 04007F00070202040202" - " 02 01 02" - " 02 01 08"); - - auto paceInfo = PACEInfo::decode(bytes); - - QVERIFY(paceInfo != nullptr); - QCOMPARE(paceInfo->getParameterId(), QByteArray::fromHex("08")); - QCOMPARE(paceInfo->getParameterIdAsInt(), 8); - } - - - void getVersion() - { - QByteArray bytes; - QSharedPointer paceInfo; - - bytes = QByteArray::fromHex("30 12" - " 06 0A 04007F00070202040202" - " 02 01 01" - " 02 01 08"); - - paceInfo = PACEInfo::decode(bytes); - - QVERIFY(paceInfo != nullptr); - QCOMPARE(paceInfo->getVersion(), 1); - - bytes = QByteArray::fromHex("30 12" - " 06 0A 04007F00070202040202" - " 02 01 02" - " 02 01 08"); - - paceInfo = PACEInfo::decode(bytes); - - QVERIFY(paceInfo != nullptr); - QCOMPARE(paceInfo->getVersion(), 2); - } - - - void getEnumString() - { - QCOMPARE(getEnumName(KeyAgreementType::DH), QStringLiteral("DH")); - QCOMPARE(getEnumName(MappingType::IM), QStringLiteral("IM")); - } - - - void getKeyAgreementType() - { - QCOMPARE(PACEInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040101 020102"))->getKeyAgreementType(), KeyAgreementType::DH); - QCOMPARE(PACEInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040102 020102"))->getKeyAgreementType(), KeyAgreementType::DH); - QCOMPARE(PACEInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040103 020102"))->getKeyAgreementType(), KeyAgreementType::DH); - QCOMPARE(PACEInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040104 020102"))->getKeyAgreementType(), KeyAgreementType::DH); - - QCOMPARE(PACEInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040201 020102"))->getKeyAgreementType(), KeyAgreementType::ECDH); - QCOMPARE(PACEInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040202 020102"))->getKeyAgreementType(), KeyAgreementType::ECDH); - QCOMPARE(PACEInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040203 020102"))->getKeyAgreementType(), KeyAgreementType::ECDH); - QCOMPARE(PACEInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040204 020102"))->getKeyAgreementType(), KeyAgreementType::ECDH); - - QCOMPARE(PACEInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040301 020102"))->getKeyAgreementType(), KeyAgreementType::DH); - QCOMPARE(PACEInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040302 020102"))->getKeyAgreementType(), KeyAgreementType::DH); - QCOMPARE(PACEInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040303 020102"))->getKeyAgreementType(), KeyAgreementType::DH); - QCOMPARE(PACEInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040304 020102"))->getKeyAgreementType(), KeyAgreementType::DH); - - QCOMPARE(PACEInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040401 020102"))->getKeyAgreementType(), KeyAgreementType::ECDH); - QCOMPARE(PACEInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040402 020102"))->getKeyAgreementType(), KeyAgreementType::ECDH); - QCOMPARE(PACEInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040403 020102"))->getKeyAgreementType(), KeyAgreementType::ECDH); - QCOMPARE(PACEInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040404 020102"))->getKeyAgreementType(), KeyAgreementType::ECDH); - - } - - - void getMappingType() - { - QCOMPARE(PACEInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040101 020102"))->getMappingType(), MappingType::GM); - QCOMPARE(PACEInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040102 020102"))->getMappingType(), MappingType::GM); - QCOMPARE(PACEInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040103 020102"))->getMappingType(), MappingType::GM); - QCOMPARE(PACEInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040104 020102"))->getMappingType(), MappingType::GM); - - QCOMPARE(PACEInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040201 020102"))->getMappingType(), MappingType::GM); - QCOMPARE(PACEInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040202 020102"))->getMappingType(), MappingType::GM); - QCOMPARE(PACEInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040203 020102"))->getMappingType(), MappingType::GM); - QCOMPARE(PACEInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040204 020102"))->getMappingType(), MappingType::GM); - - QCOMPARE(PACEInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040301 020102"))->getMappingType(), MappingType::IM); - QCOMPARE(PACEInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040302 020102"))->getMappingType(), MappingType::IM); - QCOMPARE(PACEInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040303 020102"))->getMappingType(), MappingType::IM); - QCOMPARE(PACEInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040304 020102"))->getMappingType(), MappingType::IM); - - QCOMPARE(PACEInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040401 020102"))->getMappingType(), MappingType::IM); - QCOMPARE(PACEInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040402 020102"))->getMappingType(), MappingType::IM); - QCOMPARE(PACEInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040403 020102"))->getMappingType(), MappingType::IM); - QCOMPARE(PACEInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040404 020102"))->getMappingType(), MappingType::IM); - - } - - - void isStandardizedECDH_GM() - { - // version = 2, parameterId = 8 - QByteArray bytes = QByteArray::fromHex("30 12" - " 06 0A 04007F00070202040202" - " 02 01 02" - " 02 01 08"); - - auto paceInfo = PACEInfo::decode(bytes); - - QVERIFY(paceInfo != nullptr); - QVERIFY(paceInfo->isStandardizedDomainParameters()); - } - - - void isNotStandardizedECDH_GM() - { - // version = 2, parameterId = 7 - QByteArray bytes = QByteArray::fromHex("30 12" - " 06 0A 04007F00070202040202" - " 02 01 02" - " 02 01 07"); - - auto paceInfo = PACEInfo::decode(bytes); - - QVERIFY(paceInfo != nullptr); - QVERIFY(!paceInfo->isStandardizedDomainParameters()); - } - - - void isStandardizedECDH_IM() - { - // version = 2, parameterId = 8 - QByteArray bytes = QByteArray::fromHex("30 12" - " 06 0A 04007F00070202040402" - " 02 01 02" - " 02 01 08"); - - auto paceInfo = PACEInfo::decode(bytes); - - QVERIFY(paceInfo != nullptr); - QVERIFY(paceInfo->isStandardizedDomainParameters()); - } - - - void isNotStandardizedECDH_IM() - { - // version = 2, parameterId = 10 - QByteArray bytes = QByteArray::fromHex("30 12" - " 06 0A 04007F00070202040402" - " 02 01 02" - " 02 01 0A"); - - auto paceInfo = PACEInfo::decode(bytes); - - QVERIFY(paceInfo != nullptr); - QVERIFY(!paceInfo->isStandardizedDomainParameters()); - } - - - void isStandardizedDH_GM() - { - // version = 2, parameterId = 0 - QByteArray bytes = QByteArray::fromHex("30 12" - " 06 0A 04007F00070202040102" - " 02 01 02" - " 02 01 00"); - - auto paceInfo = PACEInfo::decode(bytes); - - QVERIFY(paceInfo != nullptr); - QVERIFY(paceInfo->isStandardizedDomainParameters()); - } - - - void isNotStandardizedDH_GM() - { - // version = 2, parameterId = 7 - QByteArray bytes = QByteArray::fromHex("30 12" - " 06 0A 04007F00070202040102" - " 02 01 02" - " 02 01 07"); - - auto paceInfo = PACEInfo::decode(bytes); - - QVERIFY(paceInfo != nullptr); - QVERIFY(!paceInfo->isStandardizedDomainParameters()); - } - - - void isStandardizedDH_IM() - { - // version = 2, parameterId = 0 - QByteArray bytes = QByteArray::fromHex("30 12" - " 06 0A 04007F00070202040302" - " 02 01 02" - " 02 01 00"); - - auto paceInfo = PACEInfo::decode(bytes); - - QVERIFY(paceInfo != nullptr); - QVERIFY(paceInfo->isStandardizedDomainParameters()); - } - - - void isNotStandardizedDH_IM() - { - // version = 2, parameterId = 8 - QByteArray bytes = QByteArray::fromHex("30 12" - " 06 0A 04007F00070202040302" - " 02 01 02" - " 02 01 08"); - - auto paceInfo = PACEInfo::decode(bytes); - - QVERIFY(paceInfo != nullptr); - QVERIFY(!paceInfo->isStandardizedDomainParameters()); - } - - -}; - -QTEST_GUILESS_MAIN(test_PACEInfo) -#include "test_PACEInfo.moc" diff --git a/test/qt/card/asn1/test_PaceInfo.cpp b/test/qt/card/asn1/test_PaceInfo.cpp new file mode 100644 index 0000000..a85a61b --- /dev/null +++ b/test/qt/card/asn1/test_PaceInfo.cpp @@ -0,0 +1,304 @@ +/*! + * \brief Unit tests for \ref PACEInfo + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include +#include + +#include "asn1/KnownOIDs.h" +#include "asn1/PaceInfo.h" + + +using namespace governikus; + +class test_PaceInfo + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void parseCrap() + { + QVERIFY(PaceInfo::decode("3003020101") == nullptr); + } + + + void parseChipAuthenticationInfo() + { + QByteArray bytes = QByteArray::fromHex("30 12" + " 06 0A 04007F00070202030202" + " 02 01 02" + " 02 01 08"); + + QVERIFY(PaceInfo::decode(bytes) == nullptr); + } + + + void parsePaceInfoWithWrongContent() + { + QByteArray bytes = QByteArray::fromHex("30 12" + " 06 0A 04007F00070202040202" + " 04 01 02" + " 04 01 08"); + + auto paceInfo = PaceInfo::decode(bytes); + + QVERIFY(paceInfo == nullptr); + } + + + void missingOid() + { + QByteArray bytes = QByteArray::fromHex("30 06" + " 02 01 02" + " 02 01 08"); + + QVERIFY(PaceInfo::decode(bytes) == nullptr); + } + + + void missingVersion() + { + QByteArray bytes = QByteArray::fromHex("30 0C" + " 06 0A 04007F00070202040202"); + + QVERIFY(PaceInfo::decode(bytes) == nullptr); + } + + + void withoutOptionalParameter() + { + QByteArray bytes = QByteArray::fromHex("30 0F" + " 06 0A 04007F00070202040202" + " 02 01 02"); + + auto paceInfo = PaceInfo::decode(bytes); + + QVERIFY(paceInfo != nullptr); + QCOMPARE(paceInfo->getParameterIdAsInt(), -1); + } + + + void getParameterId() + { + QByteArray bytes = QByteArray::fromHex("30 12" + " 06 0A 04007F00070202040202" + " 02 01 02" + " 02 01 08"); + + auto paceInfo = PaceInfo::decode(bytes); + + QVERIFY(paceInfo != nullptr); + QCOMPARE(paceInfo->getParameterId(), QByteArray::fromHex("08")); + QCOMPARE(paceInfo->getParameterIdAsInt(), 8); + } + + + void getVersion() + { + QByteArray bytes; + QSharedPointer paceInfo; + + bytes = QByteArray::fromHex("30 12" + " 06 0A 04007F00070202040202" + " 02 01 01" + " 02 01 08"); + + paceInfo = PaceInfo::decode(bytes); + + QVERIFY(paceInfo != nullptr); + QCOMPARE(paceInfo->getVersion(), 1); + + bytes = QByteArray::fromHex("30 12" + " 06 0A 04007F00070202040202" + " 02 01 02" + " 02 01 08"); + + paceInfo = PaceInfo::decode(bytes); + + QVERIFY(paceInfo != nullptr); + QCOMPARE(paceInfo->getVersion(), 2); + } + + + void getEnumString() + { + QCOMPARE(getEnumName(KeyAgreementType::DH), QStringLiteral("DH")); + QCOMPARE(getEnumName(MappingType::IM), QStringLiteral("IM")); + } + + + void getKeyAgreementType() + { + QCOMPARE(PaceInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040101 020102"))->getKeyAgreementType(), KeyAgreementType::DH); + QCOMPARE(PaceInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040102 020102"))->getKeyAgreementType(), KeyAgreementType::DH); + QCOMPARE(PaceInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040103 020102"))->getKeyAgreementType(), KeyAgreementType::DH); + QCOMPARE(PaceInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040104 020102"))->getKeyAgreementType(), KeyAgreementType::DH); + + QCOMPARE(PaceInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040201 020102"))->getKeyAgreementType(), KeyAgreementType::ECDH); + QCOMPARE(PaceInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040202 020102"))->getKeyAgreementType(), KeyAgreementType::ECDH); + QCOMPARE(PaceInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040203 020102"))->getKeyAgreementType(), KeyAgreementType::ECDH); + QCOMPARE(PaceInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040204 020102"))->getKeyAgreementType(), KeyAgreementType::ECDH); + + QCOMPARE(PaceInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040301 020102"))->getKeyAgreementType(), KeyAgreementType::DH); + QCOMPARE(PaceInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040302 020102"))->getKeyAgreementType(), KeyAgreementType::DH); + QCOMPARE(PaceInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040303 020102"))->getKeyAgreementType(), KeyAgreementType::DH); + QCOMPARE(PaceInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040304 020102"))->getKeyAgreementType(), KeyAgreementType::DH); + + QCOMPARE(PaceInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040401 020102"))->getKeyAgreementType(), KeyAgreementType::ECDH); + QCOMPARE(PaceInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040402 020102"))->getKeyAgreementType(), KeyAgreementType::ECDH); + QCOMPARE(PaceInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040403 020102"))->getKeyAgreementType(), KeyAgreementType::ECDH); + QCOMPARE(PaceInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040404 020102"))->getKeyAgreementType(), KeyAgreementType::ECDH); + } + + + void getMappingType() + { + QCOMPARE(PaceInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040101 020102"))->getMappingType(), MappingType::GM); + QCOMPARE(PaceInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040102 020102"))->getMappingType(), MappingType::GM); + QCOMPARE(PaceInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040103 020102"))->getMappingType(), MappingType::GM); + QCOMPARE(PaceInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040104 020102"))->getMappingType(), MappingType::GM); + + QCOMPARE(PaceInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040201 020102"))->getMappingType(), MappingType::GM); + QCOMPARE(PaceInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040202 020102"))->getMappingType(), MappingType::GM); + QCOMPARE(PaceInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040203 020102"))->getMappingType(), MappingType::GM); + QCOMPARE(PaceInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040204 020102"))->getMappingType(), MappingType::GM); + + QCOMPARE(PaceInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040301 020102"))->getMappingType(), MappingType::IM); + QCOMPARE(PaceInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040302 020102"))->getMappingType(), MappingType::IM); + QCOMPARE(PaceInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040303 020102"))->getMappingType(), MappingType::IM); + QCOMPARE(PaceInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040304 020102"))->getMappingType(), MappingType::IM); + + QCOMPARE(PaceInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040401 020102"))->getMappingType(), MappingType::IM); + QCOMPARE(PaceInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040402 020102"))->getMappingType(), MappingType::IM); + QCOMPARE(PaceInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040403 020102"))->getMappingType(), MappingType::IM); + QCOMPARE(PaceInfo::decode(QByteArray::fromHex("300F 060A04007F00070202040404 020102"))->getMappingType(), MappingType::IM); + } + + + void isStandardizedECDH_GM() + { + // version = 2, parameterId = 8 + QByteArray bytes = QByteArray::fromHex("30 12" + " 06 0A 04007F00070202040202" + " 02 01 02" + " 02 01 08"); + + auto paceInfo = PaceInfo::decode(bytes); + + QVERIFY(paceInfo != nullptr); + QVERIFY(paceInfo->isStandardizedDomainParameters()); + } + + + void isNotStandardizedECDH_GM() + { + // version = 2, parameterId = 7 + QByteArray bytes = QByteArray::fromHex("30 12" + " 06 0A 04007F00070202040202" + " 02 01 02" + " 02 01 07"); + + auto paceInfo = PaceInfo::decode(bytes); + + QVERIFY(paceInfo != nullptr); + QVERIFY(!paceInfo->isStandardizedDomainParameters()); + } + + + void isStandardizedECDH_IM() + { + // version = 2, parameterId = 8 + QByteArray bytes = QByteArray::fromHex("30 12" + " 06 0A 04007F00070202040402" + " 02 01 02" + " 02 01 08"); + + auto paceInfo = PaceInfo::decode(bytes); + + QVERIFY(paceInfo != nullptr); + QVERIFY(paceInfo->isStandardizedDomainParameters()); + } + + + void isNotStandardizedECDH_IM() + { + // version = 2, parameterId = 10 + QByteArray bytes = QByteArray::fromHex("30 12" + " 06 0A 04007F00070202040402" + " 02 01 02" + " 02 01 0A"); + + auto paceInfo = PaceInfo::decode(bytes); + + QVERIFY(paceInfo != nullptr); + QVERIFY(!paceInfo->isStandardizedDomainParameters()); + } + + + void isStandardizedDH_GM() + { + // version = 2, parameterId = 0 + QByteArray bytes = QByteArray::fromHex("30 12" + " 06 0A 04007F00070202040102" + " 02 01 02" + " 02 01 00"); + + auto paceInfo = PaceInfo::decode(bytes); + + QVERIFY(paceInfo != nullptr); + QVERIFY(paceInfo->isStandardizedDomainParameters()); + } + + + void isNotStandardizedDH_GM() + { + // version = 2, parameterId = 7 + QByteArray bytes = QByteArray::fromHex("30 12" + " 06 0A 04007F00070202040102" + " 02 01 02" + " 02 01 07"); + + auto paceInfo = PaceInfo::decode(bytes); + + QVERIFY(paceInfo != nullptr); + QVERIFY(!paceInfo->isStandardizedDomainParameters()); + } + + + void isStandardizedDH_IM() + { + // version = 2, parameterId = 0 + QByteArray bytes = QByteArray::fromHex("30 12" + " 06 0A 04007F00070202040302" + " 02 01 02" + " 02 01 00"); + + auto paceInfo = PaceInfo::decode(bytes); + + QVERIFY(paceInfo != nullptr); + QVERIFY(paceInfo->isStandardizedDomainParameters()); + } + + + void isNotStandardizedDH_IM() + { + // version = 2, parameterId = 8 + QByteArray bytes = QByteArray::fromHex("30 12" + " 06 0A 04007F00070202040302" + " 02 01 02" + " 02 01 08"); + + auto paceInfo = PaceInfo::decode(bytes); + + QVERIFY(paceInfo != nullptr); + QVERIFY(!paceInfo->isStandardizedDomainParameters()); + } + + +}; + +QTEST_GUILESS_MAIN(test_PaceInfo) +#include "test_PaceInfo.moc" diff --git a/test/qt/card/asn1/test_SecurityInfos.cpp b/test/qt/card/asn1/test_SecurityInfos.cpp index 2af00ac..e602dc7 100644 --- a/test/qt/card/asn1/test_SecurityInfos.cpp +++ b/test/qt/card/asn1/test_SecurityInfos.cpp @@ -8,7 +8,7 @@ #include #include "asn1/ChipAuthenticationInfo.h" -#include "asn1/PACEInfo.h" +#include "asn1/PaceInfo.h" #include "asn1/SecurityInfos.h" @@ -48,7 +48,7 @@ class test_SecurityInfos } - void setWithPACEInfo() + void setWithPaceInfo() { QByteArray hexString("31 14" " 30 12" @@ -60,7 +60,7 @@ class test_SecurityInfos QVERIFY(securityInfos != nullptr); QCOMPARE(securityInfos->getSecurityInfos().size(), 1); - QCOMPARE(securityInfos->getPACEInfos().size(), 1); + QCOMPARE(securityInfos->getPaceInfos().size(), 1); QCOMPARE(securityInfos->getChipAuthenticationInfos().size(), 0); } @@ -77,7 +77,7 @@ class test_SecurityInfos QVERIFY(securityInfos != nullptr); QCOMPARE(securityInfos->getSecurityInfos().size(), 1); - QCOMPARE(securityInfos->getPACEInfos().size(), 0); + QCOMPARE(securityInfos->getPaceInfos().size(), 0); QCOMPARE(securityInfos->getChipAuthenticationInfos().size(), 1); } @@ -102,7 +102,7 @@ class test_SecurityInfos QVERIFY(securityInfos != nullptr); QCOMPARE(securityInfos->getSecurityInfos().size(), 3); - QCOMPARE(securityInfos->getPACEInfos().size(), 1); + QCOMPARE(securityInfos->getPaceInfos().size(), 1); QCOMPARE(securityInfos->getChipAuthenticationInfos().size(), 1); } diff --git a/test/qt/card/asn1/test_SignatureChecker.cpp b/test/qt/card/asn1/test_SignatureChecker.cpp index 27cc387..4d42acb 100644 --- a/test/qt/card/asn1/test_SignatureChecker.cpp +++ b/test/qt/card/asn1/test_SignatureChecker.cpp @@ -47,6 +47,13 @@ class test_SignatureChecker cvcs.append(load(":/card/cvca-DETESTeID00004_DETESTeID00002.hex")); cvcs.append(load(":/card/cvdv-DEDVeIDDPST00035.hex")); cvcs.append(load(":/card/cvat-DEDEMODEV00038.hex")); + ERR_clear_error(); + } + + + void cleanup() + { + QCOMPARE(ERR_get_error(), 0); } diff --git a/test/qt/card/asn1/test_efCardAccess.cpp b/test/qt/card/asn1/test_efCardAccess.cpp index 89b547b..707f53b 100644 --- a/test/qt/card/asn1/test_efCardAccess.cpp +++ b/test/qt/card/asn1/test_efCardAccess.cpp @@ -3,7 +3,7 @@ */ #include "asn1/ChipAuthenticationInfo.h" -#include "asn1/PACEInfo.h" +#include "asn1/PaceInfo.h" #include "asn1/SecurityInfos.h" #include "TestFileHelper.h" @@ -30,7 +30,7 @@ class test_efCardAccess QVERIFY(efCardAccess != nullptr); QCOMPARE(efCardAccess->getChipAuthenticationInfos().size(), 1); - QCOMPARE(efCardAccess->getPACEInfos().size(), 1); + QCOMPARE(efCardAccess->getPaceInfos().size(), 1); QCOMPARE(efCardAccess->getContentBytes(), bytes); } diff --git a/test/qt/card/base/command/test_BaseCardCommand.cpp b/test/qt/card/base/command/test_BaseCardCommand.cpp index 28a3147..f198e2a 100644 --- a/test/qt/card/base/command/test_BaseCardCommand.cpp +++ b/test/qt/card/base/command/test_BaseCardCommand.cpp @@ -6,7 +6,8 @@ #include "command/BaseCardCommand.h" -#include "MockReader.h" +#include "LogHandler.h" +#include "MockCardConnectionWorker.h" #include #include @@ -22,7 +23,6 @@ class BaseCardCommandDummy BaseCardCommandDummy(Reader* pReader) : BaseCardCommand(CardConnectionWorker::create(pReader)) { - } @@ -40,6 +40,18 @@ class test_BaseCardCommand Q_OBJECT private Q_SLOTS: + void initTestCase() + { + Env::getSingleton()->init(); + } + + + void cleanup() + { + Env::getSingleton()->resetBacklog(); + } + + void commandDone() { MockReader reader("dummy reader"); @@ -47,9 +59,9 @@ class test_BaseCardCommand QCOMPARE(command.getReturnCode(), CardReturnCode::UNKNOWN); QSignalSpy spy(&command, &BaseCardCommand::commandDone); - QMetaObject::invokeMethod(&command, "execute"); + command.run(); - QCOMPARE(spy.count(), 1); + QTRY_COMPARE(spy.count(), 1); auto param = spy.takeFirst(); QSharedPointer sharedCommand = param.at(0).value >(); QCOMPARE(sharedCommand.data(), &command); @@ -57,14 +69,6 @@ class test_BaseCardCommand } - void checkRetryCounterAndPrepareForPaceNoCard() - { - MockReader reader("dummy reader"); - BaseCardCommandDummy command(&reader); - QCOMPARE(command.checkRetryCounterAndPrepareForPace("test"), CardReturnCode::CARD_NOT_FOUND); - } - - }; QTEST_GUILESS_MAIN(test_BaseCardCommand) diff --git a/test/qt/card/base/command/test_DestroyPaceChannelCommand.cpp b/test/qt/card/base/command/test_DestroyPaceChannelCommand.cpp new file mode 100644 index 0000000..12a1a6d --- /dev/null +++ b/test/qt/card/base/command/test_DestroyPaceChannelCommand.cpp @@ -0,0 +1,42 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "command/DestroyPaceChannelCommand.h" + +#include "LogHandler.h" +#include "MockCardConnectionWorker.h" + +#include +#include + + +using namespace governikus; + + +class test_DestroyPaceChannelCommand + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void test_InternalExecute() + { + QSharedPointer worker1(new MockCardConnectionWorker()); + worker1->addPaceCode(CardReturnCode::OK); + DestroyPaceChannelCommand command1(worker1); + command1.internalExecute(); + QCOMPARE(command1.getReturnCode(), CardReturnCode::OK); + + QSharedPointer worker2(new MockCardConnectionWorker()); + worker2->addPaceCode(CardReturnCode::UNKNOWN); + DestroyPaceChannelCommand command2(worker2); + command2.internalExecute(); + QCOMPARE(command2.getReturnCode(), CardReturnCode::UNKNOWN); + } + + +}; + +QTEST_GUILESS_MAIN(test_DestroyPaceChannelCommand) +#include "test_DestroyPaceChannelCommand.moc" diff --git a/test/qt/card/base/command/test_DidAuthenticateEAC1Command.cpp b/test/qt/card/base/command/test_DidAuthenticateEAC1Command.cpp new file mode 100644 index 0000000..020c2d6 --- /dev/null +++ b/test/qt/card/base/command/test_DidAuthenticateEAC1Command.cpp @@ -0,0 +1,105 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "command/DidAuthenticateEAC1Command.h" + +#include "LogHandler.h" +#include "MockCardConnectionWorker.h" + +#include +#include + + +using namespace governikus; + + +class test_DidAuthenticateEAC1Command + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void initTestCase() + { + Env::getSingleton()->init(); + } + + + void cleanup() + { + Env::getSingleton()->resetBacklog(); + } + + + void test_InternalExecuteOKEmptyChallenge() + { + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); + QSharedPointer worker(new MockCardConnectionWorker()); + + ResponseApdu response(QByteArray::fromHex("9000")); + worker->addResponse(CardReturnCode::OK, response.getBuffer()); + DidAuthenticateEAC1Command command(worker); + command.internalExecute(); + QCOMPARE(command.getReturnCode(), CardReturnCode::OK); + QCOMPARE(response.getReturnCode(), StatusCode::SUCCESS); + QCOMPARE(command.getChallenge(), QByteArray()); + QCOMPARE(spy.count(), 1); + QVERIFY(spy.takeFirst().at(0).toString().contains("Challenge has wrong size. Expect 8 bytes, got")); + } + + + void test_InternalExecuteOK() + { + QSharedPointer worker(new MockCardConnectionWorker()); + + ResponseApdu response(QByteArray::fromHex("00000000000000009000")); + worker->addResponse(CardReturnCode::OK, response.getBuffer()); + DidAuthenticateEAC1Command command(worker); + command.internalExecute(); + QCOMPARE(command.getReturnCode(), CardReturnCode::OK); + QCOMPARE(response.getReturnCode(), StatusCode::SUCCESS); + QCOMPARE(command.getChallenge(), QByteArray::fromHex("0000000000000000")); + } + + + void test_InternalExecuteFailed() + { + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); + QSharedPointer worker(new MockCardConnectionWorker()); + + ResponseApdu response1(QByteArray::fromHex("63c0")); + worker->addResponse(CardReturnCode::PIN_BLOCKED, response1.getBuffer()); + DidAuthenticateEAC1Command command1(worker); + command1.internalExecute(); + QCOMPARE(command1.getReturnCode(), CardReturnCode::PIN_BLOCKED); + QCOMPARE(response1.getReturnCode(), StatusCode::PIN_BLOCKED); + QCOMPARE(command1.getChallenge(), QByteArray()); + QCOMPARE(spy.count(), 1); + QVERIFY(spy.takeFirst().at(0).toString().contains("GetChallenge failed")); + + ResponseApdu response2(QByteArray::fromHex("19191919191919")); + worker->addResponse(CardReturnCode::PROTOCOL_ERROR, response2.getBuffer()); + DidAuthenticateEAC1Command command2(worker); + command2.internalExecute(); + QCOMPARE(command2.getReturnCode(), CardReturnCode::PROTOCOL_ERROR); + QCOMPARE(response2.getReturnCode(), StatusCode::INVALID); + QCOMPARE(command2.getChallenge(), QByteArray()); + QCOMPARE(spy.count(), 1); + QVERIFY(spy.takeFirst().at(0).toString().contains("GetChallenge failed")); + } + + + void test_GetChallenge() + { + QSharedPointer worker(new MockCardConnectionWorker()); + DidAuthenticateEAC1Command command(worker); + command.mChallenge = QByteArray("abc"); + QCOMPARE(command.getChallenge(), QByteArray("abc")); + } + + +}; + +QTEST_GUILESS_MAIN(test_DidAuthenticateEAC1Command) +#include "test_DidAuthenticateEAC1Command.moc" diff --git a/test/qt/card/base/command/test_EstablishPaceChannelCommand.cpp b/test/qt/card/base/command/test_EstablishPaceChannelCommand.cpp new file mode 100644 index 0000000..b4ff885 --- /dev/null +++ b/test/qt/card/base/command/test_EstablishPaceChannelCommand.cpp @@ -0,0 +1,81 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "command/EstablishPaceChannelCommand.h" + +#include "LogHandler.h" +#include "MockCardConnectionWorker.h" + +#include +#include + + +using namespace governikus; + + +class test_EstablishPaceChannelCommand + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void test_GetPaceOutput() + { + QSharedPointer worker(new MockCardConnectionWorker()); + const QString passwort = QStringLiteral("passwort"); + QByteArray chat("chat"); + QByteArray description("description"); + EstablishPaceChannelCommand command(worker, PacePasswordId::PACE_PIN, + passwort, chat, description); + + EstablishPaceChannelOutput output; + output.setPaceReturnCode(CardReturnCode::OK); + output.setCarCurr(QByteArray("carCurr")); + output.setCarPrev(QByteArray("carPrev")); + output.setEfCardAccess(QByteArray("cardAccess")); + output.setIdIcc(QByteArray("IdIcc")); + output.setStatusMseSetAt(QByteArray("status")); + command.mPaceOutput = output; + + QCOMPARE(command.getPaceOutput().getPaceReturnCode(), CardReturnCode::OK); + QCOMPARE(command.getPaceOutput().getCARcurr(), QByteArray("carCurr")); + QCOMPARE(command.getPaceOutput().getCARprev(), QByteArray("carPrev")); + QCOMPARE(command.getPaceOutput().getEfCardAccess(), QByteArray("cardAccess")); + QCOMPARE(command.getPaceOutput().getIDicc(), QByteArray("IdIcc")); + QCOMPARE(command.getPaceOutput().getMseStatusSetAt(), QByteArray("status")); + } + + + void test_InternalExecute() + { + const QString passwort = QStringLiteral("passwort"); + QByteArray chat("chat"); + QByteArray description("description"); + QSharedPointer worker(new MockCardConnectionWorker()); + + worker->addPaceCode(CardReturnCode::OK); + worker->addPaceCode(CardReturnCode::OK); + worker->addPaceCode(CardReturnCode::UNKNOWN); + worker->addPaceCode(CardReturnCode::PROTOCOL_ERROR); + EstablishPaceChannelCommand command(worker, PacePasswordId::PACE_PIN, + passwort, chat, description); + + command.internalExecute(); + QCOMPARE(command.getReturnCode(), CardReturnCode::OK); + + command.internalExecute(); + QCOMPARE(command.getReturnCode(), CardReturnCode::OK); + + command.internalExecute(); + QCOMPARE(command.getReturnCode(), CardReturnCode::UNKNOWN); + + command.internalExecute(); + QCOMPARE(command.getReturnCode(), CardReturnCode::PROTOCOL_ERROR); + } + + +}; + +QTEST_GUILESS_MAIN(test_EstablishPaceChannelCommand) +#include "test_EstablishPaceChannelCommand.moc" diff --git a/test/qt/card/base/command/test_SetEidPinCommand.cpp b/test/qt/card/base/command/test_SetEidPinCommand.cpp new file mode 100644 index 0000000..36fed52 --- /dev/null +++ b/test/qt/card/base/command/test_SetEidPinCommand.cpp @@ -0,0 +1,65 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "command/SetEidPinCommand.h" + +#include "LogHandler.h" +#include "MockCardConnectionWorker.h" + +#include +#include + + +using namespace governikus; + + +class test_SetEidPinCommand + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void initTestCase() + { + Env::getSingleton()->init(); + } + + + void cleanup() + { + Env::getSingleton()->resetBacklog(); + } + + + void test_InternalExecuteAndResponseApdu() + { + QSharedPointer worker(new MockCardConnectionWorker()); + worker->addResponse(CardReturnCode::OK, QByteArray::fromHex(("9000"))); + worker->addResponse(CardReturnCode::PROTOCOL_ERROR, QByteArray::fromHex("1919")); + worker->addResponse(CardReturnCode::PIN_BLOCKED, QByteArray::fromHex("63c0")); + const QString pin = QStringLiteral("111111"); + SetEidPinCommand command(worker, pin, 8); + + ResponseApdu apdu1(QByteArray("apdu")); + command.mResponseApdu = apdu1; + QCOMPARE(command.getResponseApdu().getBuffer(), QByteArray("apdu")); + + command.internalExecute(); + QCOMPARE(command.getReturnCode(), CardReturnCode::OK); + QCOMPARE(command.getResponseApdu().getBuffer(), QByteArray::fromHex("9000")); + + command.internalExecute(); + QCOMPARE(command.getReturnCode(), CardReturnCode::PROTOCOL_ERROR); + QCOMPARE(command.getResponseApdu().getBuffer(), QByteArray::fromHex("1919")); + + command.internalExecute(); + QCOMPARE(command.getReturnCode(), CardReturnCode::PIN_BLOCKED); + QCOMPARE(command.getResponseApdu().getBuffer(), QByteArray::fromHex("63c0")); + } + + +}; + +QTEST_GUILESS_MAIN(test_SetEidPinCommand) +#include "test_SetEidPinCommand.moc" diff --git a/test/qt/card/base/command/test_TransmitCommand.cpp b/test/qt/card/base/command/test_TransmitCommand.cpp index 47d7f35..ab6d371 100644 --- a/test/qt/card/base/command/test_TransmitCommand.cpp +++ b/test/qt/card/base/command/test_TransmitCommand.cpp @@ -4,6 +4,9 @@ #include "command/TransmitCommand.h" +#include "LogHandler.h" +#include "MockCardConnectionWorker.h" + #include #include @@ -17,6 +20,12 @@ class test_TransmitCommand Q_OBJECT private Q_SLOTS: + void initTestCase() + { + Env::getSingleton()->init(); + } + + void isAcceptable_data() { QTest::addColumn("acceptable"); @@ -61,6 +70,160 @@ class test_TransmitCommand } + void test_InternalExecuteOkSingleCommandWithoutAcceptableStatusCode() + { + QVector inputApduInfos(1); + QSharedPointer worker(new MockCardConnectionWorker()); + + worker->addResponse(CardReturnCode::OK, QByteArray::fromHex("9000")); + TransmitCommand command(worker, inputApduInfos, QStringLiteral("slotname")); + command.internalExecute(); + QCOMPARE(command.getOutputApduAsHex().size(), 1); + QCOMPARE(command.getOutputApduAsHex()[0], QByteArray("9000")); + QCOMPARE(command.getReturnCode(), CardReturnCode::OK); + } + + + void test_InternalExecuteOkSingleCommandWithAcceptableStatusCode() + { + QVector inputApduInfos(1); + QSharedPointer worker(new MockCardConnectionWorker()); + + inputApduInfos[0].addAcceptableStatusCode(QByteArray("90")); + worker->addResponse(CardReturnCode::OK, QByteArray::fromHex("9000")); + TransmitCommand command(worker, inputApduInfos, QStringLiteral("slotname")); + command.internalExecute(); + QCOMPARE(command.getOutputApduAsHex().size(), 1); + QCOMPARE(command.getOutputApduAsHex()[0], QByteArray("9000")); + QCOMPARE(command.getReturnCode(), CardReturnCode::OK); + } + + + void test_InternalExecuteUnsuccesfulSingleCommand() + { + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); + + QVector inputApduInfos(1); + QSharedPointer worker(new MockCardConnectionWorker()); + + worker->addResponse(CardReturnCode::PROTOCOL_ERROR, QByteArray::fromHex("1919")); + TransmitCommand command1(worker, inputApduInfos, QStringLiteral("slotname")); + command1.internalExecute(); + QVERIFY(command1.getOutputApduAsHex().isEmpty()); + QCOMPARE(command1.getReturnCode(), CardReturnCode::PROTOCOL_ERROR); + QVERIFY(spy.takeFirst().at(0).toString().contains("Transmit unsuccessful. Return code:")); + + worker->addResponse(CardReturnCode::PIN_BLOCKED, QByteArray::fromHex("63c0")); + TransmitCommand command2(worker, inputApduInfos, QStringLiteral("slotname")); + command2.internalExecute(); + QVERIFY(command2.getOutputApduAsHex().isEmpty()); + QCOMPARE(command2.getReturnCode(), CardReturnCode::PIN_BLOCKED); + QVERIFY(spy.takeFirst().at(0).toString().contains("Transmit unsuccessful. Return code:")); + } + + + void test_InternalExecuteUnexpectedStatusSingleCommand() + { + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); + QVector inputApduInfos(1); + QSharedPointer worker(new MockCardConnectionWorker()); + + inputApduInfos[0].addAcceptableStatusCode(QByteArray("1010")); + worker->addResponse(CardReturnCode::OK, QByteArray::fromHex("9000")); + TransmitCommand command(worker, inputApduInfos, QStringLiteral("slotname")); + command.internalExecute(); + QCOMPARE(command.getOutputApduAsHex().size(), 1); + QCOMPARE(command.getOutputApduAsHex()[0], QByteArray("9000")); + QCOMPARE(command.getReturnCode(), CardReturnCode::UNEXPECTED_TRANSMIT_STATUS); + QVERIFY(spy.takeFirst().at(0).toString().contains("Transmit unsuccessful. StatusCode does not start with acceptable status code")); + } + + + void test_InternalExecuteOkMultipleCommand() + { + QVector inputApduInfos(2); + QSharedPointer worker(new MockCardConnectionWorker()); + + worker->addResponse(CardReturnCode::OK, QByteArray::fromHex("9000")); + worker->addResponse(CardReturnCode::OK, QByteArray::fromHex("9000")); + TransmitCommand command(worker, inputApduInfos, QStringLiteral("slotname")); + command.internalExecute(); + QCOMPARE(command.getOutputApduAsHex().size(), 2); + QCOMPARE(command.getOutputApduAsHex()[0], QByteArray("9000")); + QCOMPARE(command.getOutputApduAsHex()[1], QByteArray("9000")); + QCOMPARE(command.getReturnCode(), CardReturnCode::OK); + } + + + void test_InternalExecuteUnsuccessfulMultipleCommand() + { + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); + + QVector inputApduInfos(2); + QSharedPointer worker(new MockCardConnectionWorker()); + + worker->addResponse(CardReturnCode::OK, QByteArray::fromHex("9000")); + worker->addResponse(CardReturnCode::PROTOCOL_ERROR, QByteArray::fromHex("1919")); + TransmitCommand command1(worker, inputApduInfos, QStringLiteral("slotname")); + command1.internalExecute(); + QCOMPARE(command1.getOutputApduAsHex().size(), 1); + QCOMPARE(command1.getOutputApduAsHex()[0], QByteArray("9000")); + QCOMPARE(command1.getReturnCode(), CardReturnCode::PROTOCOL_ERROR); + QVERIFY(spy.takeFirst().at(0).toString().contains("Transmit unsuccessful. Return code")); + + worker->addResponse(CardReturnCode::PROTOCOL_ERROR, QByteArray::fromHex("1919")); + worker->addResponse(CardReturnCode::OK, QByteArray::fromHex("9000")); + TransmitCommand command2(worker, inputApduInfos, QStringLiteral("slotname")); + command2.internalExecute(); + QCOMPARE(command2.getOutputApduAsHex().size(), 0); + QCOMPARE(command2.getReturnCode(), CardReturnCode::PROTOCOL_ERROR); + QVERIFY(spy.takeFirst().at(0).toString().contains("Transmit unsuccessful. Return code")); + } + + + void test_InternalExecuteUnexpectedStatusMultipleCommand() + { + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); + + QVector inputApduInfos1(2); + QVector inputApduInfos2(2); + QSharedPointer worker(new MockCardConnectionWorker()); + + inputApduInfos1[0].addAcceptableStatusCode(QByteArray("90")); + inputApduInfos1[1].addAcceptableStatusCode(QByteArray("1010")); + worker->addResponse(CardReturnCode::OK, QByteArray::fromHex("9000")); + worker->addResponse(CardReturnCode::OK, QByteArray::fromHex("9000")); + TransmitCommand command1(worker, inputApduInfos1, QStringLiteral("slotname")); + command1.internalExecute(); + QCOMPARE(command1.getOutputApduAsHex().size(), 2); + QCOMPARE(command1.getOutputApduAsHex()[0], QByteArray("9000")); + QCOMPARE(command1.getOutputApduAsHex()[1], QByteArray("9000")); + QCOMPARE(command1.getReturnCode(), CardReturnCode::UNEXPECTED_TRANSMIT_STATUS); + QVERIFY(spy.takeFirst().at(0).toString().contains("Transmit unsuccessful. StatusCode does not start with acceptable status code")); + + inputApduInfos2[0].addAcceptableStatusCode(QByteArray("1010")); + inputApduInfos2[1].addAcceptableStatusCode(QByteArray("9000")); + worker->addResponse(CardReturnCode::OK, QByteArray::fromHex("9000")); + worker->addResponse(CardReturnCode::OK, QByteArray::fromHex("9000")); + TransmitCommand command2(worker, inputApduInfos2, QStringLiteral("slotname")); + command2.internalExecute(); + QCOMPARE(command2.getOutputApduAsHex().size(), 1); + QCOMPARE(command2.getOutputApduAsHex()[0], QByteArray("9000")); + QCOMPARE(command2.getReturnCode(), CardReturnCode::UNEXPECTED_TRANSMIT_STATUS); + QVERIFY(spy.takeFirst().at(0).toString().contains("Transmit unsuccessful. StatusCode does not start with acceptable status code")); + } + + + void test_SlotHandle() + { + QVector inputApduInfos(1); + QSharedPointer worker(new MockCardConnectionWorker()); + const QString name = QStringLiteral("slotname"); + TransmitCommand command(worker, inputApduInfos, name); + QCOMPARE(command.getSlotHandle(), name); + } + + }; QTEST_GUILESS_MAIN(test_TransmitCommand) diff --git a/test/qt/card/base/command/test_UnblockPinCommand.cpp b/test/qt/card/base/command/test_UnblockPinCommand.cpp new file mode 100644 index 0000000..620938b --- /dev/null +++ b/test/qt/card/base/command/test_UnblockPinCommand.cpp @@ -0,0 +1,111 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "command/UnblockPinCommand.h" + +#include "LogHandler.h" +#include "MockCardConnectionWorker.h" + +#include +#include + + +using namespace governikus; + + +class test_UnblockPinCommand + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void test_InternalExecuteNoCard() + { + MockReader reader(QStringLiteral("reader")); + QSharedPointer worker(new MockCardConnectionWorker(&reader)); + const QString puk = QStringLiteral("00000000000"); + + UnblockPinCommand command(worker, puk); + command.internalExecute(); + QCOMPARE(command.getReturnCode(), CardReturnCode::CARD_NOT_FOUND); + } + + + void test_InternalExecuteNoReader() + { + QSharedPointer worker(new MockCardConnectionWorker()); + const QString puk = QStringLiteral("00000000000"); + + UnblockPinCommand command(worker, puk); + command.internalExecute(); + QCOMPARE(command.getReturnCode(), CardReturnCode::CARD_NOT_FOUND); + } + + + void test_InternalExecutePinNotBlocked() + { + MockReader reader(QStringLiteral("reader")); + CardInfo info(CardType::EID_CARD, QSharedPointer(), 0, true, true); + reader.getReaderInfo().setCardInfo(info); + + QSharedPointer worker(new MockCardConnectionWorker(&reader)); + const QString puk = QStringLiteral("00000000000"); + UnblockPinCommand command(worker, puk); + command.internalExecute(); + QCOMPARE(command.getReturnCode(), CardReturnCode::PIN_NOT_BLOCKED); + } + + + void test_InternalExecuteProtocolError() + { + MockReader reader(QStringLiteral("reader")); + CardInfo info(CardType::EID_CARD, QSharedPointer(), 0, false, true); + reader.getReaderInfo().setCardInfo(info); + QSharedPointer worker(new MockCardConnectionWorker(&reader)); + + worker->addPaceCode(CardReturnCode::PROTOCOL_ERROR); + const QString puk = QStringLiteral("12131415"); + UnblockPinCommand command(worker, puk); + command.internalExecute(); + QCOMPARE(command.getReturnCode(), CardReturnCode::PROTOCOL_ERROR); + } + + + void test_InternalExecuteOK() + { + MockReader reader(QStringLiteral("reader")); + CardInfo info(CardType::EID_CARD, QSharedPointer(), 0, false, true); + reader.getReaderInfo().setCardInfo(info); + QSharedPointer worker(new MockCardConnectionWorker(&reader)); + + worker->addResponse(CardReturnCode::OK, QByteArray::fromHex("9000")); + worker->addPaceCode(CardReturnCode::OK); + const QString puk = QStringLiteral("00000000000"); + UnblockPinCommand command(worker, puk); + command.internalExecute(); + QCOMPARE(command.getReturnCode(), CardReturnCode::OK); + } + + + void test_InternalExecutePukInoperative() + { + MockReader reader(QStringLiteral("reader")); + CardInfo info(CardType::EID_CARD, QSharedPointer(), 0, false, true); + reader.getReaderInfo().setCardInfo(info); + QSharedPointer worker(new MockCardConnectionWorker(&reader)); + + worker->addResponse(CardReturnCode::OK, QByteArray::fromHex("6900")); + worker->addPaceCode(CardReturnCode::OK); + const QString puk = QStringLiteral("00000000000"); + UnblockPinCommand command(worker, puk); + command.internalExecute(); + QCOMPARE(command.getReturnCode(), CardReturnCode::PUK_INOPERATIVE); + QVERIFY(worker->getReaderInfo().getCardInfo().isPukInoperative()); + } + + +}; + +QTEST_GUILESS_MAIN(test_UnblockPinCommand) +#include "test_UnblockPinCommand.moc" diff --git a/test/qt/card/base/command/test_UpdRetryCounterCommand.cpp b/test/qt/card/base/command/test_UpdRetryCounterCommand.cpp new file mode 100644 index 0000000..5a06b91 --- /dev/null +++ b/test/qt/card/base/command/test_UpdRetryCounterCommand.cpp @@ -0,0 +1,49 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "command/UpdateRetryCounterCommand.h" + +#include "LogHandler.h" +#include "MockCardConnectionWorker.h" + +#include +#include + + +using namespace governikus; + + +class test_UpdateRetryCounterCommand + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void test_InternalExecuteNoCard() + { + MockReader reader(QString("name")); + QSharedPointer worker(new MockCardConnectionWorker(&reader)); + UpdateRetryCounterCommand command(worker); + command.internalExecute(); + QCOMPARE(command.getReturnCode(), CardReturnCode::CARD_NOT_FOUND); + } + + + void test_InternalExecuteWithCard() + { + MockReader reader(QStringLiteral("reader")); + CardInfo info(CardType::EID_CARD, QSharedPointer(), 1, true, true); + reader.getReaderInfo().setCardInfo(info); + + QSharedPointer worker(new MockCardConnectionWorker(&reader)); + UpdateRetryCounterCommand command(worker); + command.internalExecute(); + QCOMPARE(command.getReturnCode(), CardReturnCode::OK); + } + + +}; + +QTEST_GUILESS_MAIN(test_UpdateRetryCounterCommand) +#include "test_UpdRetryCounterCommand.moc" diff --git a/test/qt/card/base/test_Apdu.cpp b/test/qt/card/base/test_Apdu.cpp deleted file mode 100644 index 3ada463..0000000 --- a/test/qt/card/base/test_Apdu.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/*! - * \brief Unit tests for \ref Apdu - * - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "Apdu.h" - -#include -#include - - -using namespace governikus; - - -class test_Apdu - : public QObject -{ - Q_OBJECT - - private Q_SLOTS: - void testRetryCounter0() - { - ResponseApdu apdu(QByteArray::fromHex("9000")); - QCOMPARE(apdu.getRetryCounter(), 3); - - apdu.setBuffer(QByteArray::fromHex("63c2")); - QCOMPARE(apdu.getRetryCounter(), 2); - - apdu.setBuffer(QByteArray::fromHex("63c1")); - QCOMPARE(apdu.getRetryCounter(), 1); - - apdu.setBuffer(QByteArray::fromHex("63c0")); - QCOMPARE(apdu.getRetryCounter(), 0); - - apdu.setBuffer(QByteArray::fromHex("6400")); - QCOMPARE(apdu.getRetryCounter(), -1); - - apdu.setBuffer(QByteArray::fromHex("1234")); - QCOMPARE(apdu.getRetryCounter(), -1); - - apdu.setBuffer(QByteArray::fromHex("12")); - QCOMPARE(apdu.getRetryCounter(), -1); - - apdu.setBuffer(QByteArray()); - QCOMPARE(apdu.getRetryCounter(), -1); - } - - - void testReturnCode_data() - { - QTest::addColumn("statusCode"); - QTest::addColumn("bufferIn"); - QTest::addColumn("bufferOut"); - - QTest::newRow("empty") << StatusCode::EMPTY << QByteArray() << QByteArray::fromHex("0000"); - QTest::newRow("01") << StatusCode::INVALID << QByteArray::fromHex("01") << QByteArray::fromHex("0001"); - QTest::newRow("63c2") << StatusCode::PIN_RETRY_COUNT_2 << QByteArray::fromHex("63c2") << QByteArray::fromHex("63c2"); - QTest::newRow("6401") << StatusCode::INPUT_CANCELLED << QByteArray::fromHex("6401") << QByteArray::fromHex("6401"); - QTest::newRow("73c2") << StatusCode::INVALID << QByteArray::fromHex("73c2") << QByteArray::fromHex("0001"); - } - - - void testReturnCode() - { - QFETCH(StatusCode, statusCode); - QFETCH(QByteArray, bufferIn); - QFETCH(QByteArray, bufferOut); - - ResponseApdu apdu = ResponseApdu(statusCode); - QCOMPARE(apdu.getReturnCode(), statusCode); - QCOMPARE(apdu.getBuffer(), bufferOut); - - apdu.setBuffer(bufferIn); - QCOMPARE(apdu.getReturnCode(), statusCode); - QCOMPARE(apdu.getBuffer(), bufferIn); - } - - -}; - - -QTEST_GUILESS_MAIN(test_Apdu) -#include "test_Apdu.moc" diff --git a/test/qt/card/base/test_CardConnection.cpp b/test/qt/card/base/test_CardConnection.cpp new file mode 100644 index 0000000..3b89ea0 --- /dev/null +++ b/test/qt/card/base/test_CardConnection.cpp @@ -0,0 +1,104 @@ +/*! + * \brief Tests for the class CardConnection. + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "CardConnection.h" + +#include "MockCardConnectionWorker.h" + +#include + +using namespace governikus; + +class test_CardConnection + : public QObject +{ + Q_OBJECT + QThread workerThread; + + private Q_SLOTS: + void init() + { + workerThread.start(); + } + + + void cleanup() + { + workerThread.quit(); + workerThread.wait(); + } + + + void test_CreateUpdateRetryCounterCommand() + { + QSharedPointer worker(new MockCardConnectionWorker()); + worker->moveToThread(&workerThread); + CardConnection connection(worker); + UpdateRetryCounterCommand* command; + command = connection.createUpdateRetryCounterCommand(); + QCOMPARE(command->mCardConnectionWorker, worker); + } + + + void test_CreateDestroyPaceChannelCommand() + { + QSharedPointer worker(new MockCardConnectionWorker()); + worker->moveToThread(&workerThread); + CardConnection connection(worker); + DestroyPaceChannelCommand* command; + command = connection.createDestroyPaceChannelCommand(); + QCOMPARE(command->mCardConnectionWorker, worker); + } + + + void test_CreateDidAuthenticateEAC1Command() + { + QSharedPointer worker(new MockCardConnectionWorker()); + worker->moveToThread(&workerThread); + CardConnection connection(worker); + DidAuthenticateEAC1Command* command; + command = connection.createDidAuthenticateEAC1Command(); + QCOMPARE(command->mCardConnectionWorker, worker); + } + + + void test_CreateDidAuthenticateEAC2Command() + { + QSharedPointer worker(new MockCardConnectionWorker()); + worker->moveToThread(&workerThread); + CardConnection connection(worker); + const CVCertificateChain chain; + const QString publicKey = QStringLiteral("0000"); + const QString signature = QStringLiteral("0000"); + const QByteArray authenticatedAuxiliaryData("0000"); + DidAuthenticateEAC2Command* command; + command = connection.createDidAuthenticateEAC2Command(chain, publicKey, signature, authenticatedAuxiliaryData); + QCOMPARE(command->mCardConnectionWorker, worker); + QCOMPARE(command->mCvcChain, chain); + QCOMPARE(command->mEphemeralPublicKeyAsHex, publicKey); + QCOMPARE(command->mSignatureAsHex, signature); + QCOMPARE(command->mAuthenticatedAuxiliaryDataAsBinary, authenticatedAuxiliaryData); + } + + + void test_CreateTransmitCommand() + { + QSharedPointer worker(new MockCardConnectionWorker()); + worker->moveToThread(&workerThread); + CardConnection connection(worker); + const QVector apduInfos; + const QString slotHandle = QStringLiteral("slot handle"); + TransmitCommand* command; + command = connection.createTransmitCommand(apduInfos, slotHandle); + QCOMPARE(command->mCardConnectionWorker, worker); + QCOMPARE(command->mSlotHandle, slotHandle); + } + + +}; + +QTEST_GUILESS_MAIN(test_CardConnection) +#include "test_CardConnection.moc" diff --git a/test/qt/card/base/test_CardInfo.cpp b/test/qt/card/base/test_CardInfo.cpp new file mode 100644 index 0000000..984dcc8 --- /dev/null +++ b/test/qt/card/base/test_CardInfo.cpp @@ -0,0 +1,65 @@ +/*! + * \brief Tests for \ref CardInfo. + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "CardInfo.h" + + +#include + +using namespace governikus; + +class test_CardInfo + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void test_CardTypeString_data() + { + QTest::addColumn("type"); + QTest::addColumn("output"); + + QTest::newRow("none") << CardType::NONE << QString("not inserted"); + QTest::newRow("unknown") << CardType::UNKNOWN << QString("unknown type"); + QTest::newRow("eid") << CardType::EID_CARD << QString("ID card (PA/eAT)"); + } + + + void test_CardTypeString() + { + QFETCH(CardType, type); + QFETCH(QString, output); + + const CardInfo info(type, QSharedPointer(), 3, false, false); + + QCOMPARE(info.getCardTypeString(), output); + } + + + void test_GetEidApplicationPath() + { + const CardInfo info1(CardType::NONE, QSharedPointer(), 3, false, false); + QCOMPARE(info1.getEidApplicationPath(), QString()); + + const CardInfo info2(CardType::EID_CARD, QSharedPointer(), 3, false, false); + QCOMPARE(info2.getEidApplicationPath(), QStringLiteral("e80704007f00070302")); + } + + + void test_RetryCounterDeterminated() + { + const CardInfo info1(CardType::EID_CARD, QSharedPointer(), CardInfo::UNDEFINED_RETRY_COUNTER, false, false); + QVERIFY(!info1.isRetryCounterDetermined()); + + const CardInfo info2(CardType::EID_CARD, QSharedPointer(), 3, false, false); + QVERIFY(info2.isRetryCounterDetermined()); + } + + +}; + +QTEST_GUILESS_MAIN(test_CardInfo) +#include "test_CardInfo.moc" diff --git a/test/qt/card/base/test_CommandApdu.cpp b/test/qt/card/base/test_CommandApdu.cpp new file mode 100644 index 0000000..5efc9a7 --- /dev/null +++ b/test/qt/card/base/test_CommandApdu.cpp @@ -0,0 +1,218 @@ +/*! + * \brief Tests for the class CommandApdu. + * + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "CommandApdu.h" + +#include "LogHandler.h" + +#include +#include + +using namespace governikus; + +class test_CommandApdu + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void initTestCase() + { + Env::getSingleton()->init(); + } + + + void cleanup() + { + Env::getSingleton()->resetBacklog(); + } + + + void testGetLc() + { + QByteArray data1 = QByteArray::fromHex( + "7f4e82016e5f290100420e44454356434165494430303130327f4982011d060a04007f000702020202038120a9fb57d" + "ba1eea9bc3e660a909d838d726e3bf623d52620282013481d1f6e537782207d5a0975fc2c3057eef67530417affe7fb" + "8055c126dc5c6ce94a4b44f330b5d9832026dc5c6ce94a4b44f330b5d9bbd77cbf958416295cf7e1ce6bccdc18ff8c0" + "7b68441048bd2aeb9cb7e57cb2c4b482ffc81b7afb9de27e1e3bd23c23a4453bd9ace3262547ef835c3dac4fd97f846" + "1a14611dc9c27745132ded8e545c1d54c72f0469978520a9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f79" + "01e0e82974856a78641048925419fc7f194922cfc6b8dd25ae6a19c1b59216e6cf06270e5d75cfd64205f55cf867bbf" + "efeefd6e680e1fd197f18ab684484901362568efc9adb5c6018d728701015f200e44454356434165494430303130337" + "f4c12060904007f0007030102025305fc0f13ffff5f25060102010200035f24060105010200035f37404d6f08a86a4f" + "18409f6685387dd3c6a7ff5d68ea4f7714a861bbb3bb721d05d3014adf1763c9292f715d8e94ee9b3e1b73ab1382414" + "ebf39dfb3b0fb6c09dbeb"); + + CommandApdu apdu1(CommandApdu::CLA, 0x2a, 0, char(190), data1); + + QCOMPARE(apdu1.getLc(), 438); + QCOMPARE(apdu1.getLe(), 0); + } + + + void test_getLc_data() + { + QTest::addColumn("data"); + QTest::addColumn("lc"); + + QTest::newRow("abc") << QByteArray("abc") << 0; + QTest::newRow("abcdef") << QByteArray("abcdef") << 101; + } + + + void test_getLc() + { + QFETCH(QByteArray, data); + QFETCH(int, lc); + + CommandApdu apdu = CommandApdu(data); + QCOMPARE(apdu.getLc(), lc); + } + + + void test_LcExtendedLength() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray array = QByteArray::fromHex("010101010001"); + CommandApdu apdu = CommandApdu(array); + QCOMPARE(apdu.getLc(), 0); + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.takeFirst().at(0).toString().contains("Cannot determine Lc, returning 0")); + } + + + void test_getLe() + { + QByteArray array(7, 0); + CommandApdu apdu = CommandApdu(array); + QCOMPARE(apdu.getLe(), 0x010000); + } + + + void test_getP2_data() + { + QTest::addColumn("p2"); + QTest::addColumn("data"); + + QTest::newRow("empty") << '\0' << QByteArray(); + QTest::newRow("abc") << '\0' << QByteArray("abc"); + QTest::newRow("input") << 'u' << QByteArray("input"); + } + + + void test_getP2() + { + QFETCH(QByteArray, data); + QFETCH(char, p2); + + CommandApdu apdu = CommandApdu(data); + QCOMPARE(apdu.getP2(), p2); + } + + + void test_getP1_data() + { + QTest::addColumn("p1"); + QTest::addColumn("data"); + + QTest::newRow("empty") << '\0' << QByteArray(); + QTest::newRow("abc") << 'c' << QByteArray("abc"); + QTest::newRow("input") << 'p' << QByteArray("input"); + } + + + void test_getP1() + { + QFETCH(QByteArray, data); + QFETCH(char, p1); + + CommandApdu apdu = CommandApdu(data); + QCOMPARE(apdu.getP1(), p1); + } + + + void test_getINS_data() + { + QTest::addColumn("ins"); + QTest::addColumn("data"); + + QTest::newRow("empty") << '\0' << QByteArray(); + QTest::newRow("abc") << 'b' << QByteArray("abc"); + QTest::newRow("input") << 'n' << QByteArray("input"); + } + + + void test_getINS() + { + QFETCH(QByteArray, data); + QFETCH(char, ins); + + CommandApdu apdu = CommandApdu(data); + QCOMPARE(apdu.getINS(), ins); + } + + + void test_getCLA_data() + { + QTest::addColumn("cla"); + QTest::addColumn("data"); + + QTest::newRow("empty") << '\0' << QByteArray(); + QTest::newRow("abc") << 'a' << QByteArray("abc"); + QTest::newRow("input") << 'i' << QByteArray("input"); + } + + + void test_getCLA() + { + QFETCH(QByteArray, data); + QFETCH(char, cla); + + CommandApdu apdu = CommandApdu(data); + QCOMPARE(apdu.getCLA(), cla); + } + + + void test_SecureMessaging_data() + { + QTest::addColumn("secure"); + QTest::addColumn("data"); + + QTest::newRow("null") << false << QByteArray(); + QTest::newRow("empty") << false << QByteArray(""); + QTest::newRow("data") << false << QByteArray("data"); + QTest::newRow("0c") << true << QByteArray::fromHex("0c"); + } + + + void test_SecureMessaging() + { + CommandApdu apdu = CommandApdu(QByteArray()); + QFETCH(bool, secure); + QFETCH(QByteArray, data); + + QCOMPARE(apdu.isSecureMessaging(data), secure); + } + + + void test_InvalidCommandApdu() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + CommandApdu apdu1 = CommandApdu('a', 'i', 'c', 'd', QByteArray(65536, 'w'), 1); + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.takeFirst().at(0).toString().contains("Command data exceeds maximum of 0xFFFF bytes")); + + CommandApdu apdu2 = CommandApdu('a', 'i', 'c', 'd', QByteArray("abc"), 65539); + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.takeFirst().at(0).toString().contains("Expected length exceeds maximum value of 0x010000")); + } + + +}; + +QTEST_GUILESS_MAIN(test_CommandApdu) +#include "test_CommandApdu.moc" diff --git a/test/qt/card/base/test_Commands.cpp b/test/qt/card/base/test_Commands.cpp new file mode 100644 index 0000000..b455984 --- /dev/null +++ b/test/qt/card/base/test_Commands.cpp @@ -0,0 +1,119 @@ +/*! + * \brief Tests for the class Commands. + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "Commands.h" + +#include + +using namespace governikus; + +class test_Commands + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void test_SetCertificateBody_PSOBuilder() + { + PSOBuilder builder(PSOBuilder::P1::VERIFY, PSOBuilder::P2::CERTIFICATE); + const QByteArray data("data"); + + builder.setCertificateBody(data); + QCOMPARE(builder.mCertificateBody, data); + } + + + void test_SetSignature_PSOBuilder() + { + PSOBuilder builder(PSOBuilder::P1::VERIFY, PSOBuilder::P2::CERTIFICATE); + const QByteArray data("data"); + + builder.setSignature(data); + QCOMPARE(builder.mSignature, data); + } + + + void test_Build_PSOBuilder() + { + PSOBuilder builder(PSOBuilder::P1::VERIFY, PSOBuilder::P2::CERTIFICATE); + + CommandApdu apdu = builder.build(); + QCOMPARE(apdu.getINS(), 0x2a); + QCOMPARE(apdu.getP1(), 0x00); + QCOMPARE(apdu.getP2(), '\xbe'); + QCOMPARE(apdu.getData(), QByteArray()); + } + + + void test_SetSignatureEABuilder() + { + EABuilder builder; + const QByteArray data("data"); + + builder.setSignature(data); + QCOMPARE(builder.mSignature, data); + } + + + void test_Build_EABuilder() + { + EABuilder builder; + const QByteArray data("data"); + + builder.setSignature(data); + CommandApdu apdu = builder.build(); + + QCOMPARE(apdu.getINS(), char(0x82)); + QCOMPARE(apdu.getP1(), 0); + QCOMPARE(apdu.getP2(), 0); + QCOMPARE(apdu.getData(), data); + } + + + void test_SetCaEphemeralPublicKey_GABuilder() + { + GABuilder builder; + const QByteArray data(""); + + builder.setCaEphemeralPublicKey(data); + QCOMPARE(builder.mCaEphemeralPublicKey, QByteArray::fromHex("8000")); + } + + + void test_SetPaceMappingData_GABuilder() + { + GABuilder builder; + const QByteArray data(""); + + builder.setPaceMappingData(data); + QCOMPARE(builder.mPaceMappingData, QByteArray::fromHex("8100")); + } + + + void test_SetPaceEphemeralPublicKey_GABuilder() + { + GABuilder builder; + const QByteArray data(""); + + builder.setPaceEphemeralPublicKey(data); + QCOMPARE(builder.mPaceEphemeralPublicKey, QByteArray::fromHex("8300")); + } + + + void test_SetPaceAuthenticationToken_GABuilder() + { + GABuilder builder; + const QByteArray data(""); + + builder.setPaceAuthenticationToken(data); + QCOMPARE(builder.mPaceAuthenticationToken, QByteArray::fromHex("8500")); + } + + +}; + +QTEST_GUILESS_MAIN(test_Commands) +#include "test_Commands.moc" diff --git a/test/qt/card/base/test_Reader.cpp b/test/qt/card/base/test_Reader.cpp new file mode 100644 index 0000000..d8146d7 --- /dev/null +++ b/test/qt/card/base/test_Reader.cpp @@ -0,0 +1,104 @@ +/*! + * \brief Unit tests for \ref Reader + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "Reader.h" + +#include "MockCardConnectionWorker.h" +#include "MockReader.h" + +#include + + +using namespace governikus; + + +class test_Reader + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void test_CreateCardConnectionWorkerNoCard() + { + const QString name = QStringLiteral("name"); + MockReader reader(name); + + QTest::ignoreMessage(QtWarningMsg, "No card available"); + QCOMPARE(reader.createCardConnectionWorker(), QSharedPointer()); + } + + + void test_CreateCardConnectionWorkerConnected() + { + const QString name = QStringLiteral("name"); + MockReader reader(name); + MockCard* card = reader.setCard(MockCardConfig(), QSharedPointer()); + card->setConnected(true); + + QTest::ignoreMessage(QtWarningMsg, "Card is already connected"); + QCOMPARE(reader.createCardConnectionWorker(), QSharedPointer()); + } + + + void test_UpdateRetryCounterCommandFailed() + { + const QString name = QStringLiteral("name"); + MockReader reader(name); + CardInfo cInfo(CardType::UNKNOWN, QSharedPointer(), 3, false, false); + ReaderInfo rInfo(name, ReaderManagerPlugInType::UNKNOWN, cInfo); + reader.mReaderInfo = rInfo; + QSharedPointer worker(new MockCardConnectionWorker()); + + QCOMPARE(reader.updateRetryCounter(worker), CardReturnCode::COMMAND_FAILED); + } + + + void test_GetRetryCounterNoEfCardAccess() + { + const QString name = QStringLiteral("name"); + MockReader reader(name); + CardInfo cInfo(CardType::UNKNOWN, QSharedPointer(), 3, false, false); + ReaderInfo rInfo(name, ReaderManagerPlugInType::UNKNOWN, cInfo); + reader.mReaderInfo = rInfo; + QSharedPointer worker(new MockCardConnectionWorker()); + + QTest::ignoreMessage(QtCriticalMsg, "Cannot get EF.CardAccess"); + int counter = 3; + bool deactivated = false; + QCOMPARE(reader.getRetryCounter(worker, counter, deactivated), CardReturnCode::COMMAND_FAILED); + } + + + void test_FireUpdateSignal() + { + const QString name = QStringLiteral("name"); + MockReader reader(name); + CardInfo cInfo(CardType::UNKNOWN, QSharedPointer(), 3, false, false); + ReaderInfo rInfo(name, ReaderManagerPlugInType::UNKNOWN, cInfo); + reader.mReaderInfo = rInfo; + + QSignalSpy spyCardInserted(&reader, &Reader::fireCardInserted); + QSignalSpy spyCardRemoved(&reader, &Reader::fireCardRemoved); + + reader.fireUpdateSignal(Reader::CardEvent::NONE); + QCOMPARE(spyCardInserted.count(), 0); + QCOMPARE(spyCardRemoved.count(), 0); + + QTest::ignoreMessage(QtInfoMsg, "Card inserted: {Type: UNKNOWN, Retry counter: 3, Pin deactivated: false}"); + reader.fireUpdateSignal(Reader::CardEvent::CARD_INSERTED); + QCOMPARE(spyCardInserted.count(), 1); + + QTest::ignoreMessage(QtInfoMsg, "Card removed"); + reader.fireUpdateSignal(Reader::CardEvent::CARD_REMOVED); + QCOMPARE(spyCardRemoved.count(), 1); + } + + +}; + + +QTEST_GUILESS_MAIN(test_Reader) +#include "test_Reader.moc" diff --git a/test/qt/card/base/test_ResponseApdu.cpp b/test/qt/card/base/test_ResponseApdu.cpp new file mode 100644 index 0000000..027c795 --- /dev/null +++ b/test/qt/card/base/test_ResponseApdu.cpp @@ -0,0 +1,173 @@ +/*! + * \brief Unit tests for \ref Apdu + * + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "ResponseApdu.h" + +#include "LogHandler.h" + +#include +#include + + +using namespace governikus; + + +class test_ResponceApdu + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void initTestCase() + { + Env::getSingleton()->init(); + } + + + void cleanup() + { + Env::getSingleton()->resetBacklog(); + } + + + void testRetryCounter0() + { + ResponseApdu apdu(QByteArray::fromHex("9000")); + QCOMPARE(apdu.getRetryCounter(), 3); + + apdu.setBuffer(QByteArray::fromHex("63c2")); + QCOMPARE(apdu.getRetryCounter(), 2); + + apdu.setBuffer(QByteArray::fromHex("63c1")); + QCOMPARE(apdu.getRetryCounter(), 1); + + apdu.setBuffer(QByteArray::fromHex("63c0")); + QCOMPARE(apdu.getRetryCounter(), 0); + + apdu.setBuffer(QByteArray::fromHex("6400")); + QCOMPARE(apdu.getRetryCounter(), -1); + + apdu.setBuffer(QByteArray::fromHex("1234")); + QCOMPARE(apdu.getRetryCounter(), -1); + + apdu.setBuffer(QByteArray::fromHex("12")); + QCOMPARE(apdu.getRetryCounter(), -1); + + apdu.setBuffer(QByteArray::fromHex("6283")); + QCOMPARE(apdu.getRetryCounter(), 0); + + apdu.setBuffer(QByteArray()); + QCOMPARE(apdu.getRetryCounter(), -1); + } + + + void testReturnCode_data() + { + QTest::addColumn("statusCode"); + QTest::addColumn("bufferIn"); + QTest::addColumn("bufferOut"); + + QTest::newRow("empty") << StatusCode::EMPTY << QByteArray() << QByteArray::fromHex("0000"); + QTest::newRow("01") << StatusCode::INVALID << QByteArray::fromHex("01") << QByteArray::fromHex("0001"); + QTest::newRow("63c2") << StatusCode::PIN_RETRY_COUNT_2 << QByteArray::fromHex("63c2") << QByteArray::fromHex("63c2"); + QTest::newRow("6401") << StatusCode::INPUT_CANCELLED << QByteArray::fromHex("6401") << QByteArray::fromHex("6401"); + QTest::newRow("73c2") << StatusCode::INVALID << QByteArray::fromHex("73c2") << QByteArray::fromHex("0001"); + } + + + void testReturnCode() + { + QFETCH(StatusCode, statusCode); + QFETCH(QByteArray, bufferIn); + QFETCH(QByteArray, bufferOut); + + ResponseApdu apdu = ResponseApdu(statusCode); + QCOMPARE(apdu.getReturnCode(), statusCode); + QCOMPARE(apdu.getBuffer(), bufferOut); + + apdu.setBuffer(bufferIn); + QCOMPARE(apdu.getReturnCode(), statusCode); + QCOMPARE(apdu.getBuffer(), bufferIn); + } + + + void testCardReturnCode_data() + { + QTest::addColumn("cardReturnCode"); + QTest::addColumn("statCode"); + + QTest::newRow("SUCCESS") << CardReturnCode::OK << StatusCode::SUCCESS; + QTest::newRow("INPUT_TIMEOUT") << CardReturnCode::INPUT_TIME_OUT << StatusCode::INPUT_TIMEOUT; + QTest::newRow("INPUT_CANCELLED") << CardReturnCode::CANCELLATION_BY_USER << StatusCode::INPUT_CANCELLED; + QTest::newRow("PASSWORDS_DIFFER") << CardReturnCode::NEW_PIN_MISMATCH << StatusCode::PASSWORDS_DIFFER; + QTest::newRow("PASSWORD_OUTOF_RANGE") << CardReturnCode::NEW_PIN_INVALID_LENGTH << StatusCode::PASSWORD_OUTOF_RANGE; + QTest::newRow("PIN_BLOCKED") << CardReturnCode::PIN_BLOCKED << StatusCode::PIN_BLOCKED; + QTest::newRow("default") << CardReturnCode::PROTOCOL_ERROR << StatusCode::NOT_YET_INITIALIZED; + } + + + void testCardReturnCode() + { + QFETCH(CardReturnCode, cardReturnCode); + QFETCH(StatusCode, statCode); + + ResponseApdu apdu = ResponseApdu(statCode); + QCOMPARE(apdu.getCardReturnCode(), cardReturnCode); + } + + + void test_getSW2() + { + ResponseApdu apdu1 = ResponseApdu(QByteArray()); + ResponseApdu apdu2 = ResponseApdu(QByteArray("a")); + ResponseApdu apdu3 = ResponseApdu(QByteArray("date")); + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QCOMPARE(apdu1.getSW2(), '\0'); + QCOMPARE(apdu2.getSW2(), 'a'); + QCOMPARE(apdu3.getSW2(), 'e'); + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.at(0).at(0).toString().contains("Buffer too short, returning 0")); + } + + + void testGetSW1_data() + { + QTest::addColumn("sw1"); + QTest::addColumn("buffer"); + QTest::addColumn("warning"); + + QTest::newRow("empty") << SW1::INVALID << QByteArray() << QString("Buffer too short, returning 0"); + QTest::newRow("0000") << SW1::INVALID << QByteArray::fromHex("0000") << QString(); + QTest::newRow("6100") << SW1::MORE_DATA_AVAILABLE << QByteArray::fromHex("6100") << QString(); + QTest::newRow("6900") << SW1::ERROR_COMMAND_NOT_ALLOWED << QByteArray::fromHex("6900") << QString(); + QTest::newRow("6c00") << SW1::WRONG_LE_FIELD << QByteArray::fromHex("6c00") << QString(); + QTest::newRow("invalid") << SW1::INVALID << QByteArray::fromHex("3300") << QString("Unknown SW1 value, returning INVALID, value: \"33\""); + } + + + void testGetSW1() + { + QFETCH(SW1, sw1); + QFETCH(QByteArray, buffer); + QFETCH(QString, warning); + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + ResponseApdu apdu = ResponseApdu(buffer); + QCOMPARE(apdu.getSW1(), sw1); + if (!warning.isEmpty()) + { + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.takeFirst().at(0).toString().contains(warning)); + } + } + + +}; + + +QTEST_GUILESS_MAIN(test_ResponceApdu) +#include "test_ResponseApdu.moc" diff --git a/test/qt/card/bluetooth/test_BluetoothMessageParameterCardReaderStatus.cpp b/test/qt/card/bluetooth/test_BluetoothMessageParameterCardReaderStatus.cpp new file mode 100644 index 0000000..10f11b0 --- /dev/null +++ b/test/qt/card/bluetooth/test_BluetoothMessageParameterCardReaderStatus.cpp @@ -0,0 +1,59 @@ +/*! + * \brief Unit tests for \ref BluetoothMessageParameterCardReaderStatus + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "messages/parameter/BluetoothMessageParameterCardReaderStatus.h" + +#include +#include + +using namespace governikus; + +class test_BluetoothMessageParameterCardReaderStatus + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void test_GetStatusChange() + { + const QByteArray value("value"); + BluetoothMessageParameterCardReaderStatus status(value); + + QCOMPARE(status.getStatusChange(), BluetoothStatusChange::Unknown); + + status.mCardReaderStatus = BluetoothCardReaderStatus::CardInserted; + QCOMPARE(status.getStatusChange(), BluetoothStatusChange::CardInserted); + + status.mCardReaderStatus = BluetoothCardReaderStatus::CardRemoved; + QCOMPARE(status.getStatusChange(), BluetoothStatusChange::CardRemoved); + } + + + void test_GetReaderStatus() + { + const QByteArray value("value"); + BluetoothMessageParameterCardReaderStatus status(value); + + QCOMPARE(status.getCardReaderStatus(), BluetoothCardReaderStatus::Unknown); + + status.mCardReaderStatus = BluetoothCardReaderStatus::CardInserted; + QCOMPARE(status.getCardReaderStatus(), BluetoothCardReaderStatus::CardInserted); + } + + + void test_ToStringValue() + { + const QByteArray value("value"); + BluetoothMessageParameterCardReaderStatus status(value); + + QCOMPARE(status.toStringValue(), QString("Unknown")); + } + + +}; + +QTEST_GUILESS_MAIN(test_BluetoothMessageParameterCardReaderStatus) +#include "test_BluetoothMessageParameterCardReaderStatus.moc" diff --git a/test/qt/card/bluetooth/test_BluetoothMessageParser.cpp b/test/qt/card/bluetooth/test_BluetoothMessageParser.cpp index 92b149d..10d772a 100644 --- a/test/qt/card/bluetooth/test_BluetoothMessageParser.cpp +++ b/test/qt/card/bluetooth/test_BluetoothMessageParser.cpp @@ -31,7 +31,7 @@ class test_BluetoothMessageParser QByteArray::fromHex("060200000200000100000000050000fe3081f9a106040400000000a20404029000a381c43181c1300d060804007f00070202020201023012060a04007f000702020302020201020201413012060a04007f0007020204020202010202010d301c060904007f000702020302300c060704007f0007010202010d020141302a060804007f0007020206161e687474703a2f2f6273692e62756e642e64652f6369662f6e70612e786d6c303e060804007f000702020831323012060a04007f00070202030202020102020145301c060904007f000702020302300c060704007f0007010202010d020145a42204209ee959c9f063c6e144b463e04fa19ef44da0f5059d817a7db2240bbee8b218a290000000"); BluetoothMessageParser parser1(response); QCOMPARE(parser1.getMessages().size(), 1); - BluetoothMessage::Ptr message = parser1.getMessages().at(0); + auto message = parser1.getMessages().at(0); QCOMPARE(message->getBluetoothMsgId(), BluetoothMsgId::TransferApduResponse); QCOMPARE(message->mMessageParameter.size(), 2); @@ -54,7 +54,7 @@ class test_BluetoothMessageParser response = QByteArray::fromHex("110100000800000102000000"); BluetoothMessageParser parser1(response); QCOMPARE(parser1.getMessages().size(), 1); - BluetoothMessage::Ptr message = parser1.getMessages().at(0); + auto message = parser1.getMessages().at(0); QCOMPARE(message->getBluetoothMsgId(), BluetoothMsgId::StatusInd); auto msgInd = message.staticCast(); diff --git a/test/qt/card/pace/test_EcdhKeyAgreement.cpp b/test/qt/card/pace/test_EcdhKeyAgreement.cpp index e8206f8..d95e973 100644 --- a/test/qt/card/pace/test_EcdhKeyAgreement.cpp +++ b/test/qt/card/pace/test_EcdhKeyAgreement.cpp @@ -6,7 +6,7 @@ #include "pace/ec/EcdhKeyAgreement.h" -#include "asn1/PACEInfo.h" +#include "asn1/PaceInfo.h" #include "MockReader.h" #include "pace/ec/EcUtil.h" #include "pace/ec/EllipticCurveFactory.h" @@ -41,7 +41,7 @@ class test_EcdhKeyAgreement QVector transmitConfigs; transmitConfigs.append(TransmitConfig(CardReturnCode::OK, QByteArray::fromHex("6982"))); QScopedPointer reader(MockReader::createMockReader(transmitConfigs, mEfCardAccess)); - QSharedPointer paceInfo = mEfCardAccess->getPACEInfos().at(0); + QSharedPointer paceInfo = mEfCardAccess->getPaceInfos().at(0); QScopedPointer keyAgreement(new EcdhKeyAgreement(paceInfo, reader->createCardConnectionWorker())); KeyAgreementStatus result = keyAgreement->perform("123456"); @@ -53,7 +53,7 @@ class test_EcdhKeyAgreement void encodeUncompressedPublicKey() { QByteArray protocolValue = QByteArray::fromHex("04007F00070202040202"); - QSharedPointer paceInfo = PACEInfo::decode(QByteArray::fromHex("300F060A") + protocolValue + QByteArray::fromHex("020102")); + QSharedPointer paceInfo = PaceInfo::decode(QByteArray::fromHex("300F060A") + protocolValue + QByteArray::fromHex("020102")); QSharedPointer ecGroup = EllipticCurveFactory::create(13); QByteArray ecPointBytes = QByteArray::fromHex("04a9024a1ce8f02db4463cd359be3a6946fa24fdbed7d19b04bea2f0aa2ff63c6d2a05b17a66edbc15875611a209cb87c972a141263bd0843cc64b8b884c52f725"); QSharedPointer ecPoint = EcUtil::oct2point(ecGroup, ecPointBytes); diff --git a/test/qt/card/pace/test_KeyDerivationFunction.cpp b/test/qt/card/pace/test_KeyDerivationFunction.cpp index a127240..64ec907 100644 --- a/test/qt/card/pace/test_KeyDerivationFunction.cpp +++ b/test/qt/card/pace/test_KeyDerivationFunction.cpp @@ -23,13 +23,13 @@ class test_KeyDerivationFunction private Q_SLOTS: void initTestCase() { - LogHandler::getInstance().init(); + Env::getSingleton()->init(); } void cleanup() { - LogHandler::getInstance().resetBacklog(); + Env::getSingleton()->resetBacklog(); } @@ -46,7 +46,7 @@ class test_KeyDerivationFunction void desKey() { - QSignalSpy spyLog(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy spyLog(Env::getSingleton(), &LogHandler::fireLog); KeyDerivationFunction kdf = toByteArray(KnownOIDs::id_PACE::ECDH::GM_3DES_CBC_CBC); diff --git a/test/qt/card/pace/test_PaceHandler.cpp b/test/qt/card/pace/test_PaceHandler.cpp index f865d1c..dda5482 100644 --- a/test/qt/card/pace/test_PaceHandler.cpp +++ b/test/qt/card/pace/test_PaceHandler.cpp @@ -50,7 +50,7 @@ class test_PaceHandler QScopedPointer reader(MockReader::createMockReader()); QScopedPointer paceHandler(new PaceHandler(reader->createCardConnectionWorker())); - CardReturnCode status = paceHandler->establishPaceChannel(PACE_PASSWORD_ID::PACE_PIN, "123456"); + CardReturnCode status = paceHandler->establishPaceChannel(PacePasswordId::PACE_PIN, "123456"); QCOMPARE(status, CardReturnCode::PROTOCOL_ERROR); } @@ -62,7 +62,7 @@ class test_PaceHandler QScopedPointer reader(MockReader::createMockReader(QVector(), efCardAccess)); QScopedPointer paceHandler(new PaceHandler(reader->createCardConnectionWorker())); - CardReturnCode status = paceHandler->establishPaceChannel(PACE_PASSWORD_ID::PACE_PIN, "123456"); + CardReturnCode status = paceHandler->establishPaceChannel(PacePasswordId::PACE_PIN, "123456"); QCOMPARE(status, CardReturnCode::PROTOCOL_ERROR); } @@ -74,7 +74,7 @@ class test_PaceHandler QScopedPointer reader(MockReader::createMockReader(QVector(), efCardAccess)); QScopedPointer paceHandler(new PaceHandler(reader->createCardConnectionWorker())); - CardReturnCode status = paceHandler->establishPaceChannel(PACE_PASSWORD_ID::PACE_PIN, "123456"); + CardReturnCode status = paceHandler->establishPaceChannel(PacePasswordId::PACE_PIN, "123456"); QCOMPARE(status, CardReturnCode::PROTOCOL_ERROR); } @@ -86,7 +86,7 @@ class test_PaceHandler QScopedPointer reader(MockReader::createMockReader(QVector(), efCardAccess)); QScopedPointer paceHandler(new PaceHandler(reader->createCardConnectionWorker())); - CardReturnCode status = paceHandler->establishPaceChannel(PACE_PASSWORD_ID::PACE_PIN, "123456"); + CardReturnCode status = paceHandler->establishPaceChannel(PacePasswordId::PACE_PIN, "123456"); QCOMPARE(status, CardReturnCode::PROTOCOL_ERROR); } @@ -98,7 +98,7 @@ class test_PaceHandler QScopedPointer reader(MockReader::createMockReader(QVector(), efCardAccess)); QScopedPointer paceHandler(new PaceHandler(reader->createCardConnectionWorker())); - CardReturnCode status = paceHandler->establishPaceChannel(PACE_PASSWORD_ID::PACE_PIN, "123456"); + CardReturnCode status = paceHandler->establishPaceChannel(PacePasswordId::PACE_PIN, "123456"); QCOMPARE(status, CardReturnCode::PROTOCOL_ERROR); } @@ -110,7 +110,7 @@ class test_PaceHandler QScopedPointer reader(MockReader::createMockReader(QVector(), efCardAccess)); QScopedPointer paceHandler(new PaceHandler(reader->createCardConnectionWorker())); - CardReturnCode status = paceHandler->establishPaceChannel(PACE_PASSWORD_ID::PACE_PIN, "123456"); + CardReturnCode status = paceHandler->establishPaceChannel(PacePasswordId::PACE_PIN, "123456"); QCOMPARE(status, CardReturnCode::PROTOCOL_ERROR); } @@ -122,7 +122,7 @@ class test_PaceHandler QScopedPointer reader(MockReader::createMockReader(QVector(), efCardAccess)); QScopedPointer paceHandler(new PaceHandler(reader->createCardConnectionWorker())); - CardReturnCode status = paceHandler->establishPaceChannel(PACE_PASSWORD_ID::PACE_PIN, "123456"); + CardReturnCode status = paceHandler->establishPaceChannel(PacePasswordId::PACE_PIN, "123456"); QCOMPARE(status, CardReturnCode::PROTOCOL_ERROR); } @@ -134,7 +134,7 @@ class test_PaceHandler QScopedPointer reader(MockReader::createMockReader(QVector(), efCardAccess)); QScopedPointer paceHandler(new PaceHandler(reader->createCardConnectionWorker())); - CardReturnCode status = paceHandler->establishPaceChannel(PACE_PASSWORD_ID::PACE_PIN, "123456"); + CardReturnCode status = paceHandler->establishPaceChannel(PacePasswordId::PACE_PIN, "123456"); QCOMPARE(status, CardReturnCode::PROTOCOL_ERROR); } @@ -146,7 +146,7 @@ class test_PaceHandler QScopedPointer reader(MockReader::createMockReader(QVector(), efCardAccess)); QScopedPointer paceHandler(new PaceHandler(reader->createCardConnectionWorker())); - CardReturnCode status = paceHandler->establishPaceChannel(PACE_PASSWORD_ID::PACE_PIN, "123456"); + CardReturnCode status = paceHandler->establishPaceChannel(PacePasswordId::PACE_PIN, "123456"); QCOMPARE(status, CardReturnCode::PROTOCOL_ERROR); } @@ -158,7 +158,7 @@ class test_PaceHandler QScopedPointer reader(MockReader::createMockReader(QVector(), efCardAccess)); QScopedPointer paceHandler(new PaceHandler(reader->createCardConnectionWorker())); - CardReturnCode status = paceHandler->establishPaceChannel(PACE_PASSWORD_ID::PACE_PIN, "123456"); + CardReturnCode status = paceHandler->establishPaceChannel(PacePasswordId::PACE_PIN, "123456"); QCOMPARE(status, CardReturnCode::PROTOCOL_ERROR); } @@ -170,7 +170,7 @@ class test_PaceHandler QScopedPointer reader(MockReader::createMockReader(QVector(), efCardAccess)); QScopedPointer paceHandler(new PaceHandler(reader->createCardConnectionWorker())); - CardReturnCode status = paceHandler->establishPaceChannel(PACE_PASSWORD_ID::PACE_PIN, "123456"); + CardReturnCode status = paceHandler->establishPaceChannel(PacePasswordId::PACE_PIN, "123456"); QCOMPARE(status, CardReturnCode::PROTOCOL_ERROR); } @@ -182,7 +182,7 @@ class test_PaceHandler QScopedPointer reader(MockReader::createMockReader(QVector(), efCardAccess)); QScopedPointer paceHandler(new PaceHandler(reader->createCardConnectionWorker())); - CardReturnCode status = paceHandler->establishPaceChannel(PACE_PASSWORD_ID::PACE_PIN, "123456"); + CardReturnCode status = paceHandler->establishPaceChannel(PacePasswordId::PACE_PIN, "123456"); QCOMPARE(status, CardReturnCode::PROTOCOL_ERROR); } @@ -194,7 +194,7 @@ class test_PaceHandler QScopedPointer reader(MockReader::createMockReader(QVector(), efCardAccess)); QScopedPointer paceHandler(new PaceHandler(reader->createCardConnectionWorker())); - CardReturnCode status = paceHandler->establishPaceChannel(PACE_PASSWORD_ID::PACE_PIN, "123456"); + CardReturnCode status = paceHandler->establishPaceChannel(PacePasswordId::PACE_PIN, "123456"); QCOMPARE(status, CardReturnCode::PROTOCOL_ERROR); } @@ -206,7 +206,7 @@ class test_PaceHandler QScopedPointer reader(MockReader::createMockReader(QVector(), efCardAccess)); QScopedPointer paceHandler(new PaceHandler(reader->createCardConnectionWorker())); - CardReturnCode status = paceHandler->establishPaceChannel(PACE_PASSWORD_ID::PACE_PIN, "123456"); + CardReturnCode status = paceHandler->establishPaceChannel(PacePasswordId::PACE_PIN, "123456"); QCOMPARE(status, CardReturnCode::PROTOCOL_ERROR); } @@ -221,7 +221,7 @@ class test_PaceHandler QScopedPointer reader(MockReader::createMockReader(transmitConfigs, mEfCardAccessBytes)); QScopedPointer paceHandler(new PaceHandler(reader->createCardConnectionWorker())); - CardReturnCode status = paceHandler->establishPaceChannel(PACE_PASSWORD_ID::PACE_PIN, "123456"); + CardReturnCode status = paceHandler->establishPaceChannel(PacePasswordId::PACE_PIN, "123456"); QCOMPARE(status, CardReturnCode::PROTOCOL_ERROR); } diff --git a/test/qt/card/pace/test_SymmetricCipher.cpp b/test/qt/card/pace/test_SymmetricCipher.cpp index 2887254..ba13182 100644 --- a/test/qt/card/pace/test_SymmetricCipher.cpp +++ b/test/qt/card/pace/test_SymmetricCipher.cpp @@ -17,6 +17,7 @@ class test_SymmetricCipher Q_OBJECT private: + const QString PIN = QStringLiteral("123456"); const QByteArray DATA = QByteArray::fromRawData("Moin Moin Anton!", 16); private Q_SLOTS: @@ -24,10 +25,9 @@ class test_SymmetricCipher { QByteArray paceAlgo("unknown-algorithm"); KeyDerivationFunction kdf(paceAlgo); - QByteArray key = kdf.pi("123456"); + QByteArray key = kdf.pi(PIN); SymmetricCipher sc(paceAlgo, key); - QVERIFY(!sc.isInitialized()); QVERIFY(sc.encrypt(QByteArray()).isEmpty()); QVERIFY(sc.decrypt(QByteArray()).isEmpty()); @@ -38,7 +38,7 @@ class test_SymmetricCipher { QByteArray paceAlgo = toByteArray(KnownOIDs::id_PACE::ECDH::GM_3DES_CBC_CBC); KeyDerivationFunction kdf(paceAlgo); - QByteArray key = kdf.pi("123456"); + QByteArray key = kdf.pi(PIN); SymmetricCipher sc(paceAlgo, key); QVERIFY(!sc.isInitialized()); @@ -50,8 +50,7 @@ class test_SymmetricCipher void wrongKeySize() { QByteArray paceAlgo = toByteArray(KnownOIDs::id_PACE::ECDH::GM_AES_CBC_CMAC_128); - QByteArray key("123456"); - SymmetricCipher sc(paceAlgo, key); + SymmetricCipher sc(paceAlgo, "dummy"); QVERIFY(!sc.isInitialized()); QVERIFY(sc.encrypt(QByteArray()).isEmpty()); @@ -63,7 +62,7 @@ class test_SymmetricCipher { QByteArray paceAlgo = toByteArray(KnownOIDs::id_PACE::ECDH::GM_AES_CBC_CMAC_128); KeyDerivationFunction kdf(paceAlgo); - QByteArray key = kdf.pi("123456"); + QByteArray key = kdf.pi(PIN); SymmetricCipher sc(paceAlgo, key); QVERIFY(sc.encrypt(QByteArray()).isEmpty()); @@ -75,7 +74,7 @@ class test_SymmetricCipher { QByteArray paceAlgo = toByteArray(KnownOIDs::id_PACE::ECDH::GM_AES_CBC_CMAC_128); KeyDerivationFunction kdf(paceAlgo); - QByteArray key = kdf.pi("123456"); + QByteArray key = kdf.pi(PIN); SymmetricCipher sc(paceAlgo, key); QByteArray encryptedData = sc.encrypt(DATA); @@ -90,7 +89,7 @@ class test_SymmetricCipher { QByteArray paceAlgo = toByteArray(KnownOIDs::id_PACE::ECDH::GM_AES_CBC_CMAC_192); KeyDerivationFunction kdf(paceAlgo); - QByteArray key = kdf.pi("123456"); + QByteArray key = kdf.pi(PIN); SymmetricCipher sc(paceAlgo, key); QByteArray encryptedData = sc.encrypt(DATA); @@ -105,7 +104,7 @@ class test_SymmetricCipher { QByteArray paceAlgo = toByteArray(KnownOIDs::id_PACE::ECDH::GM_AES_CBC_CMAC_256); KeyDerivationFunction kdf(paceAlgo); - QByteArray key = kdf.pi("123456"); + QByteArray key = kdf.pi(PIN); SymmetricCipher sc(paceAlgo, key); QByteArray encryptedData = sc.encrypt(DATA); @@ -120,7 +119,7 @@ class test_SymmetricCipher { QByteArray paceAlgo = toByteArray(KnownOIDs::id_PACE::ECDH::GM_AES_CBC_CMAC_256); KeyDerivationFunction kdf(paceAlgo); - QByteArray key = kdf.pi("123456"); + QByteArray key = kdf.pi(PIN); SymmetricCipher sc(paceAlgo, key); QCOMPARE(sc.decrypt(sc.encrypt(DATA)), DATA); @@ -135,7 +134,7 @@ class test_SymmetricCipher { QByteArray paceAlgo = toByteArray(KnownOIDs::id_PACE::ECDH::GM_AES_CBC_CMAC_256); KeyDerivationFunction kdf(paceAlgo); - QByteArray key = kdf.pi("123456"); + QByteArray key = kdf.pi(PIN); SymmetricCipher sc(paceAlgo, key); QByteArray encryptedData = sc.encrypt(DATA); diff --git a/test/qt/card/pcsc/test_pcscReaderFeature.cpp b/test/qt/card/pcsc/test_pcscReaderFeature.cpp index 2b45c52..bd05ef8 100644 --- a/test/qt/card/pcsc/test_pcscReaderFeature.cpp +++ b/test/qt/card/pcsc/test_pcscReaderFeature.cpp @@ -39,13 +39,13 @@ class test_pcscReaderFeature private Q_SLOTS: void initTestCase() { - LogHandler::getInstance().init(); + Env::getSingleton()->init(); } void cleanup() { - LogHandler::getInstance().resetBacklog(); + Env::getSingleton()->resetBacklog(); } @@ -53,7 +53,7 @@ class test_pcscReaderFeature { PcscReaderFeature readerFeature(nullptr); - QSignalSpy spyLog(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy spyLog(Env::getSingleton(), &LogHandler::fireLog); qDebug() << readerFeature; QVERIFY(TestFileHelper::containsLog(spyLog, QLatin1String("()"))); @@ -66,7 +66,7 @@ class test_pcscReaderFeature QByteArray featuresTLV = QByteArray::fromHex("120442330012"); PcscReaderFeature readerFeature(featuresTLV.constData(), static_cast(featuresTLV.length())); - QSignalSpy spyLog(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy spyLog(Env::getSingleton(), &LogHandler::fireLog); qDebug() << readerFeature; QVERIFY(TestFileHelper::containsLog(spyLog, QLatin1String("(TLV_PROPERTIES)"))); @@ -81,7 +81,7 @@ class test_pcscReaderFeature QByteArray featuresTLV = QByteArray::fromHex("060442000db2070442000db3080442000db4090442000db5200442000dcc"); PcscReaderFeature readerFeature(featuresTLV.constData(), static_cast(featuresTLV.length())); - QSignalSpy spyLog(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy spyLog(Env::getSingleton(), &LogHandler::fireLog); qDebug() << readerFeature; QVERIFY(TestFileHelper::containsLog(spyLog, QLatin1String("(VERIFY_PIN_DIRECT, MODIFY_PIN_DIRECT, MCT_READERDIRECT, MCT_UNIVERSAL, EXECUTE_PACE)"))); diff --git a/test/qt/card/pcsc/test_pcscReaderPaceCapability.cpp b/test/qt/card/pcsc/test_pcscReaderPaceCapability.cpp index 7e3f79f..8625d5d 100644 --- a/test/qt/card/pcsc/test_pcscReaderPaceCapability.cpp +++ b/test/qt/card/pcsc/test_pcscReaderPaceCapability.cpp @@ -39,13 +39,13 @@ class test_pcscReaderPaceCapability private Q_SLOTS: void initTestCase() { - LogHandler::getInstance().init(); + Env::getSingleton()->init(); } void cleanup() { - LogHandler::getInstance().resetBacklog(); + Env::getSingleton()->resetBacklog(); } @@ -53,7 +53,7 @@ class test_pcscReaderPaceCapability { PcscReaderPaceCapability paceCapa(nullptr); - QSignalSpy spyLog(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy spyLog(Env::getSingleton(), &LogHandler::fireLog); qDebug() << paceCapa; QVERIFY(TestFileHelper::containsLog(spyLog, QLatin1String("()"))); @@ -66,7 +66,7 @@ class test_pcscReaderPaceCapability QByteArray capabilitiesTLV = QByteArray::fromHex("00000000010060"); PcscReaderPaceCapability paceCapa(capabilitiesTLV.constData(), static_cast(capabilitiesTLV.length())); - QSignalSpy spyLog(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy spyLog(Env::getSingleton(), &LogHandler::fireLog); qDebug() << paceCapa; QVERIFY(TestFileHelper::containsLog(spyLog, QLatin1String("(EID, GENERIC)"))); @@ -81,7 +81,7 @@ class test_pcscReaderPaceCapability QByteArray capabilitiesTLV = QByteArray::fromHex("00000000010070"); PcscReaderPaceCapability paceCapa(capabilitiesTLV.constData(), static_cast(capabilitiesTLV.length())); - QSignalSpy spyLog(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy spyLog(Env::getSingleton(), &LogHandler::fireLog); qDebug() << paceCapa; QVERIFY(TestFileHelper::containsLog(spyLog, QLatin1String("(ESIGN, EID, GENERIC)"))); diff --git a/test/qt/card/remote/test_RemoteReaderManagerPlugin.cpp b/test/qt/card/remote/test_RemoteReaderManagerPlugin.cpp deleted file mode 100644 index 09e57ca..0000000 --- a/test/qt/card/remote/test_RemoteReaderManagerPlugin.cpp +++ /dev/null @@ -1,536 +0,0 @@ -/*! - * \brief Unit tests for \ref RemoteReaderManagerPlugIn - * - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "RemoteReaderManagerPlugIn.h" - -#include "messages/IfdConnect.h" -#include "messages/IfdDisconnect.h" -#include "messages/IfdError.h" -#include "messages/IfdEstablishContext.h" -#include "messages/IfdStatus.h" -#include "messages/IfdTransmit.h" -#include "messages/RemoteMessageParser.h" -#include "MockDataChannel.h" -#include "MockIfdStatus.h" -#include "MockRemoteDispatcher.h" -#include "RemoteClient.h" - -#include -#include -#include -#include - - -using namespace governikus; - - -Q_DECLARE_METATYPE(QSharedPointer ) - - -class MockRemoteClient - : public RemoteClient -{ - Q_OBJECT - - public: - MockRemoteClient() = default; - virtual ~MockRemoteClient() override = default; - - virtual void startDetection() override; - virtual void stopDetection() override; - virtual bool isDetecting() override; - virtual void establishConnection(const QSharedPointer& pEntry, const QString& pPsk) override; - virtual QVector getConnectedDeviceInfos() override; - -}; - - -void MockRemoteClient::startDetection() -{ -} - - -void MockRemoteClient::stopDetection() -{ -} - - -bool MockRemoteClient::isDetecting() -{ - return false; -} - - -void MockRemoteClient::establishConnection(const QSharedPointer& pEntry, const QString& pPsk) -{ - Q_UNUSED(pEntry); - Q_UNUSED(pPsk); -} - - -QVector MockRemoteClient::getConnectedDeviceInfos() -{ - return QVector(); -} - - -class test_RemoteReaderManagerPlugIn - : public QObject -{ - Q_OBJECT - - private: - QThread mNetworkThread; - QSharedPointer mPlugin; - QSharedPointer mRemoteClient; - QSharedPointer mDispatcher1; - QSharedPointer mDispatcher2; - QVector > mClientMessages; - - void waitForSignals(int identifier, QSignalSpy* const pSpy, const int pExpectedCount, const int pTimeoutMs) - { - qDebug() << "waitForSignals:" << identifier; - for (int tryCount = 0; pSpy->count() < pExpectedCount && tryCount < pExpectedCount; ++tryCount) - { - pSpy->wait(pTimeoutMs); - } - QCOMPARE(pSpy->count(), pExpectedCount); - } - - - private Q_SLOTS: - void initTestCase() - { - const RemoteMessageParser parser; - - mNetworkThread.setObjectName(QStringLiteral("NetworkThread")); - - mRemoteClient.reset(new MockRemoteClient); - - mDispatcher1.reset(new MockRemoteDispatcher()); - mDispatcher1->moveToThread(&mNetworkThread); - - mDispatcher2.reset(new MockRemoteDispatcher()); - mDispatcher2->moveToThread(&mNetworkThread); - - mNetworkThread.start(); - - auto m = [&](const char* pJsonByteData) -> QSharedPointer - { - const QByteArray jsonData(pJsonByteData); - return parser.parse(jsonData); - }; - - mClientMessages - << m("{\n" - " \"msg\": \"IFDEstablishContext\",\n" - " \"Protocol\": \"IFDInterface_WebSocket_v0\",\n" - " \"UDName\": \"MAC-MINI\"\n" - "}") - << m("{\n" - " \"ContextHandle\": \"contextHandle\",\n" - " \"msg\": \"IFDGetStatus\",\n" - " \"SlotName\": \"Remote Reader\"\n" - "}") - << m("{\n" - " \"ContextHandle\": \"contextHandle\",\n" - " \"exclusive\": true,\n" - " \"msg\": \"IFDConnect\",\n" - " \"SlotName\": \"NFC Reader\"\n" - "}") - << m("{\n" - " \"ContextHandle\": \"contextHandle\",\n" - " \"SlotHandle\": \"NFC Reader\",\n" - " \"msg\": \"IFDDisconnect\",\n" - " \"readerName\": \"NFC Reader\"\n" - "}") - << m("{\n" - " \"ContextHandle\": \"contextHandle\",\n" - " \"msg\": \"IFDTransmit\",\n" - " \"SlotHandle\": \"NFC Reader\",\n" - " \"CommandAPDUs\": [\n" - " {\n" - " \"InputAPDU\": \"00A402022F00\",\n" - " \"AcceptableStatusCodes\": null\n" - " }\n" - " ]\n" - "}") - << m("{\n" - " \"ContextHandle\": \"TestContext\",\n" - " \"InputData\": \"abcd1234\",\n" - " \"SlotHandle\": \"My little Reader\",\n" - " \"msg\": \"IFDEstablishPACEChannel\"\n" - "}\n"); - } - - - void cleanupTestCase() - { - mNetworkThread.quit(); - mNetworkThread.wait(); - } - - - void init() - { - // local - mPlugin.reset(new RemoteReaderManagerPlugIn()); - mPlugin->setRemoteClient(mRemoteClient); - } - - - void cleanup() - { - // local - } - - - void testStandardValues() - { - QSignalSpy spySend(mDispatcher1.data(), &MockRemoteDispatcher::fireSend); - - QVERIFY(mPlugin->getReaders().isEmpty()); - - Q_EMIT mRemoteClient->fireNewRemoteDispatcher(mDispatcher1); - waitForSignals(1, &spySend, 1, 1000); - - QVERIFY(mPlugin->getReaders().isEmpty()); - QCOMPARE(spySend.size(), 1); - - QSharedPointer result = qvariant_cast >(spySend.takeFirst().at(0)); - QCOMPARE(result->getType(), RemoteCardMessageType::IFDEstablishContext); - } - - - void testSingleDispatcherSingleReaderAddRemoveWithoutCard() - { - QSignalSpy spySend(mDispatcher1.data(), &MockRemoteDispatcher::fireSend); - - Q_EMIT mRemoteClient->fireNewRemoteDispatcher(mDispatcher1); - waitForSignals(2, &spySend, 1, 1000); - spySend.clear(); - - QSharedPointer message; - QSignalSpy spyAdded(mPlugin.data(), &ReaderManagerPlugIn::fireReaderAdded); - QSignalSpy spyRemoved(mPlugin.data(), &ReaderManagerPlugIn::fireReaderRemoved); - - message.reset(new MockIfdStatus(QStringLiteral("NFC Reader"), PaceCapabilities(), 500, true)); - mDispatcher1->onReceived(message); - QCOMPARE(mPlugin->getReaders().size(), 1); - QCOMPARE(spySend.size(), 0); - QCOMPARE(spyAdded.size(), 1); - QCOMPARE(spyAdded.takeFirst()[0].toString(), QStringLiteral("NFC Reader")); - QCOMPARE(mPlugin->getReaders().size(), 1); - QCOMPARE(mPlugin->getReaders()[0]->getName(), QStringLiteral("NFC Reader")); - QCOMPARE(mPlugin->getReaders()[0]->getReaderInfo().isBasicReader(), true); - QCOMPARE(mPlugin->getReaders()[0]->getReaderInfo().getMaxApduLength(), 500); - - message.reset(new MockIfdStatus(QStringLiteral("NFC Reader"), PaceCapabilities(), 500, false)); - mDispatcher1->onReceived(message); - QCOMPARE(mPlugin->getReaders().size(), 0); - QCOMPARE(spySend.size(), 0); - QCOMPARE(spyAdded.size(), 0); - QCOMPARE(spyRemoved.size(), 1); - QCOMPARE(spyRemoved.takeFirst()[0].toString(), QStringLiteral("NFC Reader")); - - message.reset(new MockIfdStatus(QStringLiteral("NFC Reader"), PaceCapabilities(), 500, true)); - mDispatcher1->onReceived(message); - QCOMPARE(mPlugin->getReaders().size(), 1); - QCOMPARE(mPlugin->getReaders()[0]->getName(), QStringLiteral("NFC Reader")); - spyAdded.clear(); - - mDispatcher1->onClosed(); - QCOMPARE(spySend.size(), 0); - QCOMPARE(spyAdded.size(), 0); - QCOMPARE(spyRemoved.size(), 1); - QCOMPARE(spyRemoved.takeFirst()[0].toString(), QStringLiteral("NFC Reader")); - QCOMPARE(mPlugin->getReaders().size(), 0); - } - - - void testSingleDispatcherSingleReaderPaceCapabilities() - { - QSignalSpy spySend(mDispatcher1.data(), &MockRemoteDispatcher::fireSend); - - Q_EMIT mRemoteClient->fireNewRemoteDispatcher(mDispatcher1); - waitForSignals(3, &spySend, 1, 1000); - spySend.clear(); - - QSharedPointer message; - QSignalSpy spyAdded(mPlugin.data(), &ReaderManagerPlugIn::fireReaderAdded); - QSignalSpy spyRemoved(mPlugin.data(), &ReaderManagerPlugIn::fireReaderRemoved); - - message.reset(new MockIfdStatus(QStringLiteral("NFC Reader"), PaceCapabilities(true), 500, true)); - mDispatcher1->onReceived(message); - QCOMPARE(spySend.size(), 0); - QCOMPARE(spyAdded.size(), 1); - QCOMPARE(spyAdded.takeFirst()[0].toString(), QStringLiteral("NFC Reader")); - QCOMPARE(mPlugin->getReaders().size(), 1); - QCOMPARE(mPlugin->getReaders()[0]->getName(), QStringLiteral("NFC Reader")); - QCOMPARE(mPlugin->getReaders()[0]->getReaderInfo().isBasicReader(), false); - QCOMPARE(mPlugin->getReaders()[0]->getReaderInfo().getMaxApduLength(), 500); - } - - - void testMultipleDispatcherSingleReaderAddRemoveWithoutCard() - { - QSignalSpy spySend1(mDispatcher1.data(), &MockRemoteDispatcher::fireSend); - QSignalSpy spySend2(mDispatcher2.data(), &MockRemoteDispatcher::fireSend); - - Q_EMIT mRemoteClient->fireNewRemoteDispatcher(mDispatcher1); - Q_EMIT mRemoteClient->fireNewRemoteDispatcher(mDispatcher2); - waitForSignals(4, &spySend1, 1, 1000); - waitForSignals(5, &spySend2, 1, 1000); - spySend1.clear(); - spySend2.clear(); - - QSharedPointer message; - QSignalSpy spyAdded(mPlugin.data(), &ReaderManagerPlugIn::fireReaderAdded); - QSignalSpy spyRemoved(mPlugin.data(), &ReaderManagerPlugIn::fireReaderRemoved); - - message.reset(new MockIfdStatus(QStringLiteral("NFC Reader 1"), PaceCapabilities(), 500, true)); - mDispatcher1->onReceived(message); - message.reset(new MockIfdStatus(QStringLiteral("NFC Reader 2"), PaceCapabilities(), 500, true)); - mDispatcher2->onReceived(message); - QCOMPARE(spySend1.size(), 0); - QCOMPARE(spySend2.size(), 0); - QCOMPARE(spyAdded.size(), 2); - QCOMPARE(spyAdded.takeFirst()[0].toString(), QStringLiteral("NFC Reader 1")); - QCOMPARE(spyAdded.takeFirst()[0].toString(), QStringLiteral("NFC Reader 2")); - QCOMPARE(spyRemoved.size(), 0); - QCOMPARE(mPlugin->getReaders().size(), 2); - QCOMPARE(mPlugin->getReaders()[0]->getName(), QStringLiteral("NFC Reader 1")); - QCOMPARE(mPlugin->getReaders()[1]->getName(), QStringLiteral("NFC Reader 2")); - - message.reset(new MockIfdStatus(QStringLiteral("NFC Reader 1"), PaceCapabilities(), 500, false)); - mDispatcher1->onReceived(message); - QCOMPARE(spySend1.size(), 0); - QCOMPARE(spySend2.size(), 0); - QCOMPARE(spyAdded.size(), 0); - QCOMPARE(spyRemoved.size(), 1); - QCOMPARE(spyRemoved.takeFirst()[0].toString(), QStringLiteral("NFC Reader 1")); - QCOMPARE(mPlugin->getReaders().size(), 1); - QCOMPARE(mPlugin->getReaders()[0]->getName(), QStringLiteral("NFC Reader 2")); - - message.reset(new MockIfdStatus(QStringLiteral("NFC Reader 1"), PaceCapabilities(), 500, true)); - mDispatcher1->onReceived(message); - QCOMPARE(spySend1.size(), 0); - QCOMPARE(spySend2.size(), 0); - QCOMPARE(spyAdded.size(), 1); - QCOMPARE(spyAdded.takeFirst()[0].toString(), QStringLiteral("NFC Reader 1")); - QCOMPARE(spyRemoved.size(), 0); - QCOMPARE(mPlugin->getReaders().size(), 2); - QCOMPARE(mPlugin->getReaders()[0]->getName(), QStringLiteral("NFC Reader 1")); - QCOMPARE(mPlugin->getReaders()[1]->getName(), QStringLiteral("NFC Reader 2")); - - mDispatcher1->onClosed(); - QCOMPARE(spySend1.size(), 0); - QCOMPARE(spySend2.size(), 0); - QCOMPARE(spyAdded.size(), 0); - QCOMPARE(spyRemoved.size(), 1); - QCOMPARE(spyRemoved.takeFirst()[0].toString(), QStringLiteral("NFC Reader 1")); - QCOMPARE(mPlugin->getReaders().size(), 1); - QCOMPARE(mPlugin->getReaders()[0]->getName(), QStringLiteral("NFC Reader 2")); - - mDispatcher2->onClosed(); - QCOMPARE(spySend1.size(), 0); - QCOMPARE(spySend2.size(), 0); - QCOMPARE(spyAdded.size(), 0); - QCOMPARE(spyRemoved.size(), 1); - QCOMPARE(spyRemoved.takeFirst()[0].toString(), QStringLiteral("NFC Reader 2")); - QCOMPARE(mPlugin->getReaders().size(), 0); - } - - - void testSingleDispatcherSingleReaderChangeCard() - { - QSharedPointer result; - QSignalSpy spySend(mDispatcher1.data(), &MockRemoteDispatcher::fireSend); - - mDispatcher1->setState(MockRemoteDispatcher::DispatcherState::ReaderWithCardError); - Q_EMIT mRemoteClient->fireNewRemoteDispatcher(mDispatcher1); - - waitForSignals(6, &spySend, 2, 1000); - spySend.takeFirst(); - result = qvariant_cast >(spySend.takeFirst().at(0)); - QCOMPARE(result->getType(), RemoteCardMessageType::IFDConnect); - - QSharedPointer message; - message.reset(new MockIfdStatus(QStringLiteral("NFC Reader"), PaceCapabilities(), 500, true, false)); - mDispatcher1->onReceived(message); - QCOMPARE(mPlugin->getReaders()[0]->getCard(), nullptr); - - QSignalSpy spyInserted(mPlugin->getReaders()[0], &Reader::fireCardInserted); - QSignalSpy spyRemoved(mPlugin->getReaders()[0], &Reader::fireCardRemoved); - QSignalSpy spyChanged(mPlugin->getReaders()[0], &Reader::fireCardRetryCounterChanged); - QSignalSpy spyUpdated(mPlugin->getReaders()[0], &Reader::fireReaderPropertiesUpdated); - - message.reset(new MockIfdStatus(QStringLiteral("NFC Reader"), PaceCapabilities(), 500, true, true)); - mDispatcher1->onReceived(message); - - waitForSignals(7, &spySend, 1, 1000); - result = qvariant_cast >(spySend.takeFirst().at(0)); - QCOMPARE(result->getType(), RemoteCardMessageType::IFDConnect); - QVERIFY(mPlugin->getReaders()[0]->getCard() != nullptr); - QCOMPARE(mPlugin->getReaders()[0]->getReaderInfo().getMaxApduLength(), 500); - QVERIFY(mPlugin->getReaders()[0]->getReaderInfo().hasCard()); - QCOMPARE(spyInserted.size(), 1); - QCOMPARE(spyInserted.takeFirst()[0].toString(), QStringLiteral("NFC Reader")); - QCOMPARE(spyRemoved.size(), 0); - QCOMPARE(spyChanged.size(), 0); - QCOMPARE(spyUpdated.size(), 0); - - message.reset(new MockIfdStatus(QStringLiteral("NFC Reader"), PaceCapabilities(), 1, true, true)); - mDispatcher1->onReceived(message); - QVERIFY(mPlugin->getReaders()[0]->getCard() != nullptr); - QCOMPARE(mPlugin->getReaders()[0]->getReaderInfo().getMaxApduLength(), 1); - QVERIFY(mPlugin->getReaders()[0]->getReaderInfo().hasCard()); - QCOMPARE(spyInserted.size(), 0); - QCOMPARE(spyRemoved.size(), 0); - QCOMPARE(spyChanged.size(), 0); - QCOMPARE(spyUpdated.size(), 1); - QCOMPARE(spyUpdated.takeFirst()[0].toString(), QStringLiteral("NFC Reader")); - - message.reset(new MockIfdStatus(QStringLiteral("NFC Reader"), PaceCapabilities(), 1, true, true)); - mDispatcher1->onReceived(message); - QVERIFY(mPlugin->getReaders()[0]->getCard() != nullptr); - QCOMPARE(mPlugin->getReaders()[0]->getReaderInfo().getMaxApduLength(), 1); - QVERIFY(mPlugin->getReaders()[0]->getReaderInfo().hasCard()); - QCOMPARE(spyInserted.size(), 0); - QCOMPARE(spyRemoved.size(), 0); - QCOMPARE(spyChanged.size(), 0); - QCOMPARE(spyUpdated.size(), 0); - - message.reset(new MockIfdStatus(QStringLiteral("NFC Reader"), PaceCapabilities(), 1, true, false)); - mDispatcher1->onReceived(message); - QCOMPARE(mPlugin->getReaders()[0]->getCard(), nullptr); - QVERIFY(!mPlugin->getReaders()[0]->getReaderInfo().hasCard()); - QCOMPARE(spyInserted.size(), 0); - QCOMPARE(spyRemoved.size(), 1); - QCOMPARE(spyRemoved.takeFirst()[0].toString(), QStringLiteral("NFC Reader")); - QCOMPARE(spyChanged.size(), 0); - QCOMPARE(spyUpdated.size(), 0); - - message.reset(new MockIfdStatus(QStringLiteral("NFC Reader"), PaceCapabilities(), 1, true, true)); - mDispatcher1->onReceived(message); - waitForSignals(8, &spySend, 1, 1000); - result = qvariant_cast >(spySend.takeFirst().at(0)); - QCOMPARE(result->getType(), RemoteCardMessageType::IFDConnect); - QVERIFY(mPlugin->getReaders()[0]->getCard() != nullptr); - QCOMPARE(mPlugin->getReaders()[0]->getReaderInfo().getMaxApduLength(), 1); - QVERIFY(mPlugin->getReaders()[0]->getReaderInfo().hasCard()); - QCOMPARE(spyInserted.size(), 1); - QCOMPARE(spyInserted.takeFirst()[0].toString(), QStringLiteral("NFC Reader")); - QCOMPARE(spyRemoved.size(), 0); - QCOMPARE(spyChanged.size(), 0); - QCOMPARE(spyUpdated.size(), 0); - } - - - void testSingleDispatcherSingleReaderCardCommunication() - { - QSharedPointer result; - QSignalSpy spySend(mDispatcher1.data(), &MockRemoteDispatcher::fireSend); - - mDispatcher1->setState(MockRemoteDispatcher::DispatcherState::ReaderWithCard); - Q_EMIT mRemoteClient->fireNewRemoteDispatcher(mDispatcher1); - - waitForSignals(9, &spySend, 4, 1000); - spySend.takeFirst(); - result = qvariant_cast >(spySend.takeFirst().at(0)); - QCOMPARE(result->getType(), RemoteCardMessageType::IFDConnect); - result = qvariant_cast >(spySend.takeFirst().at(0)); - QCOMPARE(result->getType(), RemoteCardMessageType::IFDTransmit); - result = qvariant_cast >(spySend.takeFirst().at(0)); - QCOMPARE(result->getType(), RemoteCardMessageType::IFDDisconnect); - - QCOMPARE(mPlugin->getReaders().size(), 1); - Card* card = mPlugin->getReaders().at(0)->getCard(); - QVERIFY(card != nullptr); - - QCOMPARE(card->connect(), CardReturnCode::OK); - waitForSignals(10, &spySend, 1, 1000); - result = qvariant_cast >(spySend.takeFirst().at(0)); - QCOMPARE(result->getType(), RemoteCardMessageType::IFDConnect); - QCOMPARE(static_cast(result.data())->getSlotName(), QStringLiteral("NFC Reader")); - - QCOMPARE(card->disconnect(), CardReturnCode::OK); - waitForSignals(11, &spySend, 1, 1000); - result = qvariant_cast >(spySend.takeFirst().at(0)); - QCOMPARE(result->getType(), RemoteCardMessageType::IFDDisconnect); - QCOMPARE(static_cast(result.data())->getSlotHandle(), QStringLiteral("NFC Reader")); - - CommandApdu cmd(QByteArray("ping")); - ResponseApdu res; - QCOMPARE(card->transmit(cmd, res), CardReturnCode::OK); - waitForSignals(12, &spySend, 1, 1000); - result = qvariant_cast >(spySend.takeFirst().at(0)); - QCOMPARE(result->getType(), RemoteCardMessageType::IFDTransmit); - QCOMPARE(static_cast(result.data())->getSlotHandle(), QStringLiteral("NFC Reader")); - QCOMPARE(static_cast(result.data())->getInputApdu(), QByteArray("ping")); - QCOMPARE(res.getBuffer(), QByteArray("pong")); - - mDispatcher1->setState(MockRemoteDispatcher::DispatcherState::ReaderWithCardError); - - QCOMPARE(card->connect(), CardReturnCode::COMMAND_FAILED); - waitForSignals(13, &spySend, 1, 1000); - result = qvariant_cast >(spySend.takeFirst().at(0)); - QCOMPARE(result->getType(), RemoteCardMessageType::IFDConnect); - QCOMPARE(static_cast(result.data())->getSlotName(), QStringLiteral("NFC Reader")); - - QCOMPARE(card->disconnect(), CardReturnCode::COMMAND_FAILED); - waitForSignals(14, &spySend, 1, 1000); - result = qvariant_cast >(spySend.takeFirst().at(0)); - QCOMPARE(result->getType(), RemoteCardMessageType::IFDDisconnect); - QCOMPARE(static_cast(result.data())->getSlotHandle(), QStringLiteral("NFC Reader")); - - res.setBuffer(QByteArray()); - QCOMPARE(card->transmit(cmd, res), CardReturnCode::COMMAND_FAILED); - waitForSignals(15, &spySend, 1, 1000); - result = qvariant_cast >(spySend.takeFirst().at(0)); - QCOMPARE(result->getType(), RemoteCardMessageType::IFDTransmit); - QCOMPARE(static_cast(result.data())->getSlotHandle(), QStringLiteral("NFC Reader")); - QCOMPARE(static_cast(result.data())->getInputApdu(), QByteArray("ping")); - QCOMPARE(res.getBuffer(), QByteArray()); - } - - - void testUnexpectedMessagesCauseAnIfdErrorMessage() - { - QSignalSpy spySend(mDispatcher1.data(), &MockRemoteDispatcher::fireSend); - - mDispatcher1->setState(MockRemoteDispatcher::DispatcherState::WithoutReader); - Q_EMIT mRemoteClient->fireNewRemoteDispatcher(mDispatcher1); - waitForSignals(1, &spySend, 1, 1000); - spySend.clear(); - - for (const QSharedPointer& clientMessage : qAsConst(mClientMessages)) - { - QMetaObject::invokeMethod(mDispatcher1.data(), "onReceived", Qt::QueuedConnection, Q_ARG(QSharedPointer, clientMessage)); - waitForSignals(2, &spySend, 1, 1000); - const QList& arguments = spySend.last(); - - const QVariant remoteMessageVariant = arguments.at(0); - QVERIFY(remoteMessageVariant.canConvert >()); - const QSharedPointer message = remoteMessageVariant.value >(); - const QSharedPointer errorMessage = message.dynamicCast(); - - QVERIFY(!errorMessage.isNull()); - QCOMPARE(errorMessage->getType(), RemoteCardMessageType::IFDError); - QCOMPARE(errorMessage->getContextHandle(), QString()); - QCOMPARE(errorMessage->getSlotHandle(), QString()); - QVERIFY(errorMessage->resultHasError()); - QCOMPARE(errorMessage->getResultMinor(), QStringLiteral("http://www.bsi.bund.de/ecard/api/1.1/resultminor/al/common#unknownAPIFunction")); - - spySend.clear(); - } - } - - -}; - -QTEST_GUILESS_MAIN(test_RemoteReaderManagerPlugIn) -#include "test_RemoteReaderManagerPlugin.moc" diff --git a/test/qt/card/test_CommandApdu.cpp b/test/qt/card/test_CommandApdu.cpp deleted file mode 100644 index a137cfa..0000000 --- a/test/qt/card/test_CommandApdu.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/*! - * \brief Tests for the class CommandApdu. - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include -#include - -#include "Apdu.h" - -#include "TestFileHelper.h" - -using namespace governikus; - -class test_CommandApdu - : public QObject -{ - Q_OBJECT - - private Q_SLOTS: - void testGetLc() - { - QByteArray data = QByteArray::fromHex( - "7f4e82016e5f290100420e44454356434165494430303130327f4982011d060a04007f000702020202038120a9fb57d" - "ba1eea9bc3e660a909d838d726e3bf623d52620282013481d1f6e537782207d5a0975fc2c3057eef67530417affe7fb" - "8055c126dc5c6ce94a4b44f330b5d9832026dc5c6ce94a4b44f330b5d9bbd77cbf958416295cf7e1ce6bccdc18ff8c0" - "7b68441048bd2aeb9cb7e57cb2c4b482ffc81b7afb9de27e1e3bd23c23a4453bd9ace3262547ef835c3dac4fd97f846" - "1a14611dc9c27745132ded8e545c1d54c72f0469978520a9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f79" - "01e0e82974856a78641048925419fc7f194922cfc6b8dd25ae6a19c1b59216e6cf06270e5d75cfd64205f55cf867bbf" - "efeefd6e680e1fd197f18ab684484901362568efc9adb5c6018d728701015f200e44454356434165494430303130337" - "f4c12060904007f0007030102025305fc0f13ffff5f25060102010200035f24060105010200035f37404d6f08a86a4f" - "18409f6685387dd3c6a7ff5d68ea4f7714a861bbb3bb721d05d3014adf1763c9292f715d8e94ee9b3e1b73ab1382414" - "ebf39dfb3b0fb6c09dbeb"); - CommandApdu apdu(CommandApdu::CLA, 0x2a, 0, char(190), data); - QCOMPARE(apdu.getLc(), 438); - QCOMPARE(apdu.getLe(), 0); - } - - - void testISecureMessaging() - { - CommandApdu secureApdu(QByteArray::fromHex("0CB0840000000E970200008E087ACC9D93FCEAF4660000")); - CommandApdu insecureApdu(QByteArray::fromHex("0022c1a427800a04007f0007020204020283010384010d7f4c12060904007f0007030102025305000111db05")); - - QVERIFY(CommandApdu::isSecureMessaging(secureApdu.getBuffer())); - QVERIFY(!CommandApdu::isSecureMessaging(insecureApdu.getBuffer())); - } - - -}; - -QTEST_GUILESS_MAIN(test_CommandApdu) -#include "test_CommandApdu.moc" diff --git a/test/qt/card/test_EstablishPACEChannelBuilder.cpp b/test/qt/card/test_EstablishPACEChannelBuilder.cpp deleted file mode 100644 index 3efe21e..0000000 --- a/test/qt/card/test_EstablishPACEChannelBuilder.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/*! - * \brief Tests for card EstablishPACEChannelBuilder - * - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#include -#include - -#include "EstablishPACEChannel.h" - - -using namespace governikus; - - -class test_EstablishPACEChannelBuilder - : public QObject -{ - Q_OBJECT - - private Q_SLOTS: - void setPasswordId() - { - QByteArray hexBytes("30 05" - " A1 03 02 01 03" - ); - EstablishPACEChannelBuilder builder; - builder.setPasswordId(PACE_PASSWORD_ID::PACE_PIN); - - QCOMPARE(builder.createCommandDataCcid().getData(), QByteArray::fromHex(hexBytes)); - } - - - void setChat() - { - QByteArray hexBytes("30 1E" - " A1 03 02 01 03" - " A3 17 04 15 7F4C 12 060904007F00070301020253050000000F0F" - ); - EstablishPACEChannelBuilder builder; - builder.setPasswordId(PACE_PASSWORD_ID::PACE_PIN); - builder.setChat(QByteArray::fromHex(" 7F4C12060904007F00070301020253050000000F0F")); - - QCOMPARE(builder.createCommandDataCcid().getData(), QByteArray::fromHex(hexBytes)); - } - - - void setCertificateDescription() - { - QByteArray certDescriptionHex("30 8202A4" - " 06 0A 04007F00070301030103" - " A1 0E 0C0C442D547275737420476D6248" - " A3 3A 0C38476573616D7476657262616E64206465722064657574736368656E20566572736963686572756E67737769727473636861667420652E562E" - " A5 820248" - " 04 820244 4E616D652C20416E7363687269667420756E6420452D4D61696C2D4164726573736520646573204469656E737465616E626965746572733A0D0A476573616D7476657262616E64206465722064657574736368656E20566572736963686572756E67737769727473636861667420652E562E0D0A57696C68656C6D73747261C39F652034332F3433670D0A3130313137204265726C696E0D0A6265726C696E406764762E64650D0A0D0A4765736368C3A46674737A7765636B3A0D0A2D52656769737472696572756E6720756E64204C6F67696E20616D204744562D4D616B6C6572706F7274616C2D0D0A0D0A48696E7765697320617566206469652066C3BC722064656E204469656E737465616E626965746572207A757374C3A46E646967656E205374656C6C656E2C20646965206469652045696E68616C74756E672064657220566F7273636872696674656E207A756D20446174656E73636875747A206B6F6E74726F6C6C696572656E3A0D0A4265726C696E6572204265617566747261677465722066C3BC7220446174656E73636875747A20756E6420496E666F726D6174696F6E7366726569686569740D0A416E20646572205572616E696120342D31300D0A3130373837204265726C696E0D0A3033302F3133382038392D300D0A6D61696C626F7840646174656E73636875747A2D6265726C696E2E64650D0A687474703A2F2F7777772E646174656E73636875747A2D6265726C696E2E64650D0A416E737072656368706172746E65723A2044722E20416C6578616E64657220446978" - ); - - QByteArray hexBytes("30 8202CA" - " A1 03 02 01 03" - " A3 17 04 15 7F4C 12 060904007F00070301020253050000000F0F" - " A4 8202A8 CERTDESCR" - ); - hexBytes = hexBytes.replace("CERTDESCR", certDescriptionHex); - - EstablishPACEChannelBuilder builder; - builder.setPasswordId(PACE_PASSWORD_ID::PACE_PIN); - builder.setChat(QByteArray::fromHex(" 7F4C12060904007F00070301020253050000000F0F")); - builder.setCertificateDescription(QByteArray::fromHex(certDescriptionHex)); - - QCOMPARE(builder.createCommandDataCcid().getData().toHex(), QByteArray::fromHex(hexBytes).toHex()); - } - - -}; - -QTEST_GUILESS_MAIN(test_EstablishPACEChannelBuilder) -#include "test_EstablishPACEChannelBuilder.moc" diff --git a/test/qt/card/test_EstablishPACEChannelOutput.cpp b/test/qt/card/test_EstablishPACEChannelOutput.cpp deleted file mode 100644 index 80ff0b8..0000000 --- a/test/qt/card/test_EstablishPACEChannelOutput.cpp +++ /dev/null @@ -1,239 +0,0 @@ -/*! - * \brief Tests for card EstablishPACEChannelOutput - * - * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany - */ - -#include -#include - -#include "EstablishPACEChannel.h" -#include "TestFileHelper.h" - - -using namespace governikus; - - -class test_EstablishPACEChannelOutput - : public QObject -{ - Q_OBJECT - - private Q_SLOTS: - void parseEstablishPACEChannelOutput() - { - QByteArray bytes = QByteArray::fromHex(TestFileHelper::readFile(":/card/EstablishPACEChannelOutput.hex")); - - EstablishPACEChannelOutput channelOutput; - channelOutput.parse(bytes, PACE_PASSWORD_ID::PACE_PIN); - QCOMPARE(channelOutput.getCARcurr(), QByteArray("DETESTeID00004")); - QVERIFY(channelOutput.getCARprev().isEmpty()); - QVERIFY(channelOutput.getEfCardAccess().toHex().startsWith(QByteArray("3181c1300d0608040"))); - } - - - void parseEstablishPACEChannelOutput_wrongPIN() - { - QByteArray bytes = QByteArray::fromHex(TestFileHelper::readFile(":/card/EstablishPACEChannelOutput_wrongPIN.hex")); - - EstablishPACEChannelOutput channelOutput; - channelOutput.parse(bytes, PACE_PASSWORD_ID::PACE_PIN); - QVERIFY(channelOutput.getCARcurr().isEmpty()); - QVERIFY(channelOutput.getCARprev().isEmpty()); - QVERIFY(channelOutput.getEfCardAccess().isEmpty()); - } - - - void parseEstablishPACEChannelOutput_fromCcid() - { - QByteArray bytes = QByteArray::fromHex(TestFileHelper::readFile(":/card/EstablishPACEChannelOutput_fromCcid.hex")); - - EstablishPACEChannelOutput channelOutput; - channelOutput.parseFromCcid(bytes, PACE_PASSWORD_ID::PACE_PIN); - QCOMPARE(CardReturnCode::OK, channelOutput.getPaceReturnCode()); - QCOMPARE(channelOutput.getCARcurr(), QByteArray("DECVCAeID00103")); - QCOMPARE(channelOutput.getCARprev(), QByteArray("DECVCAeID00102")); - QVERIFY(!channelOutput.getEfCardAccess().isEmpty()); - QVERIFY(!channelOutput.getIDicc().isEmpty()); - } - - - void parseEstablishPACEChannelOutput_fromCcid2() - { - QByteArray bytes = QByteArray::fromHex(TestFileHelper::readFile(":/card/EstablishPACEChannelOutput_fromCcid2.hex")); - - EstablishPACEChannelOutput channelOutput; - channelOutput.parseFromCcid(bytes, PACE_PASSWORD_ID::PACE_PIN); - QCOMPARE(CardReturnCode::OK, channelOutput.getPaceReturnCode()); - QVERIFY(channelOutput.getCARcurr().isEmpty()); - QVERIFY(channelOutput.getCARprev().isEmpty()); - QVERIFY(!channelOutput.getEfCardAccess().isEmpty()); - QVERIFY(!channelOutput.getIDicc().isEmpty()); - } - - - void parseEstablishPACEChannelOutput_fromCcid_parseCrap() - { - QByteArray hexBytes = QByteArray("30 0C" - " 04 04 F0200001" - " 04 02 9000" - " 31 00" - // " A4 22 04 20 24e41d62b8c848226b86fcc6c7657577dca47ad2bf21573617bae84807f85c6b" - // " A5 10 040e4445435643416549443030313033" - // " A6 10 040e4445435643416549443030313032" - " 9000" // ReturnCode - ); - - EstablishPACEChannelOutput channelOutput; - channelOutput.parseFromCcid(QByteArray::fromHex(hexBytes), PACE_PASSWORD_ID::PACE_PIN); - QCOMPARE(CardReturnCode::UNKNOWN, channelOutput.getPaceReturnCode()); - QCOMPARE(channelOutput.getMseStatusSetAt(), QByteArray()); - QCOMPARE(channelOutput.getCARcurr(), QByteArray()); - QCOMPARE(channelOutput.getCARprev(), QByteArray()); - QCOMPARE(channelOutput.getEfCardAccess(), QByteArray()); - QCOMPARE(channelOutput.getIDicc(), QByteArray()); - } - - - void parseEstablishPACEChannelOutput_fromCcid_onlyMandatoryElements() - { - QByteArray hexBytes = QByteArray("30 12" - " A1 06 04 04 F0200001" - " A2 04 04 02 9000" - " A3 02 31 00" - // " A4 22 04 20 24e41d62b8c848226b86fcc6c7657577dca47ad2bf21573617bae84807f85c6b" - // " A5 10 040e4445435643416549443030313033" - // " A6 10 040e4445435643416549443030313032" - " 9000" // ReturnCode - ); - - EstablishPACEChannelOutput channelOutput; - channelOutput.parseFromCcid(QByteArray::fromHex(hexBytes), PACE_PASSWORD_ID::PACE_PIN); - QCOMPARE(CardReturnCode::CANCELLATION_BY_USER, channelOutput.getPaceReturnCode()); - QCOMPARE(channelOutput.getMseStatusSetAt(), QByteArray::fromHex("9000")); - QCOMPARE(channelOutput.getCARcurr(), QByteArray()); - QCOMPARE(channelOutput.getCARprev(), QByteArray()); - QCOMPARE(channelOutput.getEfCardAccess(), QByteArray::fromHex("3100")); - QCOMPARE(channelOutput.getIDicc(), QByteArray()); - } - - - void parseEstablishPACEChannelOutput_fromCcid_allElements() - { - QByteArray hexBytes = QByteArray("30 5A" - " A1 06 04 04 F0200001" - " A2 04 04 02 9000" - " A3 02 31 00" - " A4 22 04 20 24e41d62b8c848226b86fcc6c7657577dca47ad2bf21573617bae84807f85c6b" - " A5 10 04 0e 4445435643416549443030313033" - " A6 10 04 0e 4445435643416549443030313032" - " 9000" // ReturnCode - ); - - EstablishPACEChannelOutput channelOutput; - channelOutput.parseFromCcid(QByteArray::fromHex(hexBytes), PACE_PASSWORD_ID::PACE_PIN); - QCOMPARE(CardReturnCode::CANCELLATION_BY_USER, channelOutput.getPaceReturnCode()); - QCOMPARE(channelOutput.getMseStatusSetAt(), QByteArray::fromHex("9000")); - QCOMPARE(channelOutput.getCARcurr(), QByteArray("DECVCAeID00103")); - QCOMPARE(channelOutput.getCARprev(), QByteArray("DECVCAeID00102")); - QCOMPARE(channelOutput.getEfCardAccess(), QByteArray::fromHex("3100")); - QCOMPARE(channelOutput.getIDicc(), QByteArray::fromHex("24e41d62b8c848226b86fcc6c7657577dca47ad2bf21573617bae84807f85c6b")); - } - - - /*! - * Workaround for erroneous firmware implementation of reader cyberJack wave: - * When entering a wrong PIN, the reader returns an invalid ASN.1 structure, where the element - * - * ESTABLISHPACECHANNELOUTPUT.efCardAccess - * - * is treated as IMPLICIT TAGGED. That's wrong. - */ - void parseEstablishPACEChannelOutput_fromCcid_cyberJackWave_workaround() - { - QByteArray hexBytes = QByteArray("30 10" - " a1 06 04 04 f00663c2" - " a2 04 04 02 0000" - " a3 00" - "9000" // ReturnCode - ); - EstablishPACEChannelOutput channelOutput; - channelOutput.parseFromCcid(QByteArray::fromHex(hexBytes), PACE_PASSWORD_ID::PACE_PIN); - QCOMPARE(CardReturnCode::INVALID_PIN, channelOutput.getPaceReturnCode()); - } - - - void toCcid() - { - QByteArray hexBytes = QByteArray("30 5A" - " A1 06 04 04 F0200001" - " A2 04 04 02 9000" - " A3 02 31 00" - " A4 22 04 20 24e41d62b8c848226b86fcc6c7657577dca47ad2bf21573617bae84807f85c6b" - " A5 10 04 0e 4445435643416549443030313033" - " A6 10 04 0e 4445435643416549443030313032" - " 9000" // ReturnCode - ); - - EstablishPACEChannelOutput channelOutput; - channelOutput.parseFromCcid(QByteArray::fromHex(hexBytes), PACE_PASSWORD_ID::PACE_PIN); - - EstablishPACEChannelOutput channelOutput2; - channelOutput2.parseFromCcid(channelOutput.toCcid(), PACE_PASSWORD_ID::PACE_PIN); - - QCOMPARE(channelOutput2.getPaceReturnCode(), CardReturnCode::CANCELLATION_BY_USER); - QCOMPARE(channelOutput2.getPaceReturnCode(), channelOutput.getPaceReturnCode()); - - QCOMPARE(channelOutput2.getMseStatusSetAt(), QByteArray::fromHex("9000")); - QCOMPARE(channelOutput2.getMseStatusSetAt(), channelOutput.getMseStatusSetAt()); - - QCOMPARE(channelOutput2.getCARcurr(), QByteArray("DECVCAeID00103")); - QCOMPARE(channelOutput2.getCARcurr(), channelOutput.getCARcurr()); - - QCOMPARE(channelOutput2.getCARprev(), QByteArray("DECVCAeID00102")); - QCOMPARE(channelOutput2.getCARprev(), channelOutput.getCARprev()); - - QCOMPARE(channelOutput2.getEfCardAccess(), QByteArray::fromHex("3100")); - QCOMPARE(channelOutput2.getEfCardAccess(), channelOutput.getEfCardAccess()); - - QCOMPARE(channelOutput2.getIDicc(), QByteArray::fromHex("24e41d62b8c848226b86fcc6c7657577dca47ad2bf21573617bae84807f85c6b")); - QCOMPARE(channelOutput2.getIDicc(), channelOutput.getIDicc()); - } - - - void toCcid_ReturnCode_ErrorCode_data() - { - QTest::addColumn("cardReturnCode"); - QTest::addColumn("pacePasswordId"); - - QTest::newRow("INVALID_CAN") << CardReturnCode::INVALID_CAN << PACE_PASSWORD_ID::PACE_CAN; - QTest::newRow("INVALID_PIN") << CardReturnCode::INVALID_PIN << PACE_PASSWORD_ID::PACE_PIN; - QTest::newRow("INVALID_PUK") << CardReturnCode::INVALID_PUK << PACE_PASSWORD_ID::PACE_PUK; - - QTest::newRow("OK") << CardReturnCode::OK << PACE_PASSWORD_ID::PACE_PIN; - QTest::newRow("CANCELLATION_BY_USER") << CardReturnCode::CANCELLATION_BY_USER << PACE_PASSWORD_ID::PACE_PIN; - QTest::newRow("INPUT_TIME_OUT") << CardReturnCode::INPUT_TIME_OUT << PACE_PASSWORD_ID::PACE_PIN; - QTest::newRow("COMMAND_FAILED") << CardReturnCode::COMMAND_FAILED << PACE_PASSWORD_ID::PACE_PIN; - } - - - void toCcid_ReturnCode_ErrorCode() - { - QFETCH(CardReturnCode, cardReturnCode); - QFETCH(PACE_PASSWORD_ID, pacePasswordId); - - EstablishPACEChannelOutput channelOutput; - channelOutput.setPaceReturnCode(cardReturnCode); - - EstablishPACEChannelOutput channelOutput2; - channelOutput2.parseFromCcid(channelOutput.toCcid(), pacePasswordId); - - QCOMPARE(channelOutput2.getPaceReturnCode(), cardReturnCode); - QCOMPARE(channelOutput2.getPaceReturnCode(), channelOutput.getPaceReturnCode()); - } - - -}; - -QTEST_GUILESS_MAIN(test_EstablishPACEChannelOutput) -#include "test_EstablishPACEChannelOutput.moc" diff --git a/test/qt/card/test_EstablishPACEChannelParser.cpp b/test/qt/card/test_EstablishPACEChannelParser.cpp deleted file mode 100644 index 657852e..0000000 --- a/test/qt/card/test_EstablishPACEChannelParser.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/*! - * \brief Tests for \ref EstablishPACEChannelParser - * - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "EstablishPACEChannelParser.h" - -#include - -using namespace governikus; - - -class test_EstablishPACEChannelParser - : public QObject -{ - Q_OBJECT - - private Q_SLOTS: - void buildAndParse() - { - const PACE_PASSWORD_ID pinId(PACE_PASSWORD_ID::PACE_PIN); - const QByteArray chat = QByteArray::fromHex("7F4C12060904007F00070301020253050000000F0F"); - const QByteArray certDescription = QByteArray::fromHex("30 8202A4" - " 06 0A 04007F00070301030103" - " A1 0E 0C0C442D547275737420476D6248" - " A3 3A 0C38476573616D7476657262616E64206465722064657574736368656E20566572736963686572756E67737769727473636861667420652E562E" - " A5 820248" - " 04 820244 4E616D652C20416E7363687269667420756E6420452D4D61696C2D4164726573736520646573204469656E737465616E626965746572733A0D0A476573616D7476657262616E64206465722064657574736368656E20566572736963686572756E67737769727473636861667420652E562E0D0A57696C68656C6D73747261C39F652034332F3433670D0A3130313137204265726C696E0D0A6265726C696E406764762E64650D0A0D0A4765736368C3A46674737A7765636B3A0D0A2D52656769737472696572756E6720756E64204C6F67696E20616D204744562D4D616B6C6572706F7274616C2D0D0A0D0A48696E7765697320617566206469652066C3BC722064656E204469656E737465616E626965746572207A757374C3A46E646967656E205374656C6C656E2C20646965206469652045696E68616C74756E672064657220566F7273636872696674656E207A756D20446174656E73636875747A206B6F6E74726F6C6C696572656E3A0D0A4265726C696E6572204265617566747261677465722066C3BC7220446174656E73636875747A20756E6420496E666F726D6174696F6E7366726569686569740D0A416E20646572205572616E696120342D31300D0A3130373837204265726C696E0D0A3033302F3133382038392D300D0A6D61696C626F7840646174656E73636875747A2D6265726C696E2E64650D0A687474703A2F2F7777772E646174656E73636875747A2D6265726C696E2E64650D0A416E737072656368706172746E65723A2044722E20416C6578616E64657220446978" - ); - - - EstablishPACEChannelBuilder builder; - builder.setPasswordId(pinId); - builder.setChat(chat); - builder.setCertificateDescription(certDescription); - const auto command = builder.createCommandDataCcid(); - const auto buffer = command.getBuffer(); - - EstablishPACEChannelParser parser = EstablishPACEChannelParser::fromCcid(buffer); - - QCOMPARE(pinId, parser.getPasswordId()); - QCOMPARE(chat, parser.getChat()); - QCOMPARE(certDescription, parser.getCertificateDescription()); - QCOMPARE(command.getData(), parser.getCommandData()); - } - - -}; - -QTEST_GUILESS_MAIN(test_EstablishPACEChannelParser) -#include "test_EstablishPACEChannelParser.moc" diff --git a/test/qt/card/test_EstablishPaceChannel.cpp b/test/qt/card/test_EstablishPaceChannel.cpp new file mode 100644 index 0000000..938e54a --- /dev/null +++ b/test/qt/card/test_EstablishPaceChannel.cpp @@ -0,0 +1,76 @@ +/*! + * \brief Tests for card EstablishPaceChannelBuilder + * + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#include +#include + +#include "EstablishPaceChannel.h" + + +using namespace governikus; + + +class test_EstablishPaceChannel + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void setPasswordId() + { + QByteArray hexBytes("30 05" + " A1 03 02 01 03"); + + EstablishPaceChannel builder; + builder.setPasswordId(PacePasswordId::PACE_PIN); + + QCOMPARE(builder.createCommandDataCcid().getData(), QByteArray::fromHex(hexBytes)); + } + + + void setChat() + { + QByteArray hexBytes("30 1E" + " A1 03 02 01 03" + " A3 17 04 15 7F4C 12 060904007F00070301020253050000000F0F"); + + EstablishPaceChannel builder; + builder.setPasswordId(PacePasswordId::PACE_PIN); + builder.setChat(QByteArray::fromHex(" 7F4C12060904007F00070301020253050000000F0F")); + + QCOMPARE(builder.createCommandDataCcid().getData(), QByteArray::fromHex(hexBytes)); + } + + + void setCertificateDescription() + { + QByteArray certDescriptionHex("30 8202A4" + " 06 0A 04007F00070301030103" + " A1 0E 0C0C442D547275737420476D6248" + " A3 3A 0C38476573616D7476657262616E64206465722064657574736368656E20566572736963686572756E67737769727473636861667420652E562E" + " A5 820248" + " 04 820244 4E616D652C20416E7363687269667420756E6420452D4D61696C2D4164726573736520646573204469656E737465616E626965746572733A0D0A476573616D7476657262616E64206465722064657574736368656E20566572736963686572756E67737769727473636861667420652E562E0D0A57696C68656C6D73747261C39F652034332F3433670D0A3130313137204265726C696E0D0A6265726C696E406764762E64650D0A0D0A4765736368C3A46674737A7765636B3A0D0A2D52656769737472696572756E6720756E64204C6F67696E20616D204744562D4D616B6C6572706F7274616C2D0D0A0D0A48696E7765697320617566206469652066C3BC722064656E204469656E737465616E626965746572207A757374C3A46E646967656E205374656C6C656E2C20646965206469652045696E68616C74756E672064657220566F7273636872696674656E207A756D20446174656E73636875747A206B6F6E74726F6C6C696572656E3A0D0A4265726C696E6572204265617566747261677465722066C3BC7220446174656E73636875747A20756E6420496E666F726D6174696F6E7366726569686569740D0A416E20646572205572616E696120342D31300D0A3130373837204265726C696E0D0A3033302F3133382038392D300D0A6D61696C626F7840646174656E73636875747A2D6265726C696E2E64650D0A687474703A2F2F7777772E646174656E73636875747A2D6265726C696E2E64650D0A416E737072656368706172746E65723A2044722E20416C6578616E64657220446978"); + + QByteArray hexBytes("30 8202CA" + " A1 03 02 01 03" + " A3 17 04 15 7F4C 12 060904007F00070301020253050000000F0F" + " A4 8202A8 CERTDESCR"); + + hexBytes = hexBytes.replace("CERTDESCR", certDescriptionHex); + + EstablishPaceChannel builder; + builder.setPasswordId(PacePasswordId::PACE_PIN); + builder.setChat(QByteArray::fromHex(" 7F4C12060904007F00070301020253050000000F0F")); + builder.setCertificateDescription(QByteArray::fromHex(certDescriptionHex)); + + QCOMPARE(builder.createCommandDataCcid().getData().toHex(), QByteArray::fromHex(hexBytes).toHex()); + } + + +}; + +QTEST_GUILESS_MAIN(test_EstablishPaceChannel) +#include "test_EstablishPaceChannel.moc" diff --git a/test/qt/card/test_EstablishPaceChannelOutput.cpp b/test/qt/card/test_EstablishPaceChannelOutput.cpp new file mode 100644 index 0000000..e53fd26 --- /dev/null +++ b/test/qt/card/test_EstablishPaceChannelOutput.cpp @@ -0,0 +1,240 @@ +/*! + * \brief Tests for card EstablishPACEChannelOutput + * + * \copyright Copyright (c) 2015-2018 Governikus GmbH & Co. KG, Germany + */ + +#include +#include + +#include "EstablishPaceChannelOutput.h" +#include "TestFileHelper.h" + + +using namespace governikus; + + +class test_EstablishPaceChannelOutput + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void parseEstablishPaceChannelOutput() + { + QByteArray bytes = QByteArray::fromHex(TestFileHelper::readFile(":/card/EstablishPaceChannelOutput.hex")); + + EstablishPaceChannelOutput channelOutput; + channelOutput.parse(bytes, PacePasswordId::PACE_PIN); + QCOMPARE(channelOutput.getCARcurr(), QByteArray("DETESTeID00004")); + QVERIFY(channelOutput.getCARprev().isEmpty()); + QVERIFY(channelOutput.getEfCardAccess().toHex().startsWith(QByteArray("3181c1300d0608040"))); + } + + + void parseEstablishPaceChannelOutput_wrongPIN() + { + QByteArray bytes = QByteArray::fromHex(TestFileHelper::readFile(":/card/EstablishPaceChannelOutput_wrongPIN.hex")); + + EstablishPaceChannelOutput channelOutput; + channelOutput.parse(bytes, PacePasswordId::PACE_PIN); + QVERIFY(channelOutput.getCARcurr().isEmpty()); + QVERIFY(channelOutput.getCARprev().isEmpty()); + QVERIFY(channelOutput.getEfCardAccess().isEmpty()); + } + + + void parseEstablishPaceChannelOutput_fromCcid() + { + QByteArray bytes = QByteArray::fromHex(TestFileHelper::readFile(":/card/EstablishPaceChannelOutput_fromCcid.hex")); + + EstablishPaceChannelOutput channelOutput; + channelOutput.parseFromCcid(bytes, PacePasswordId::PACE_PIN); + QCOMPARE(CardReturnCode::OK, channelOutput.getPaceReturnCode()); + QCOMPARE(channelOutput.getCARcurr(), QByteArray("DECVCAeID00103")); + QCOMPARE(channelOutput.getCARprev(), QByteArray("DECVCAeID00102")); + QVERIFY(!channelOutput.getEfCardAccess().isEmpty()); + QVERIFY(!channelOutput.getIDicc().isEmpty()); + } + + + void parseEstablishPaceChannelOutput_fromCcid2() + { + QByteArray bytes = QByteArray::fromHex(TestFileHelper::readFile(":/card/EstablishPaceChannelOutput_fromCcid2.hex")); + + EstablishPaceChannelOutput channelOutput; + channelOutput.parseFromCcid(bytes, PacePasswordId::PACE_PIN); + QCOMPARE(CardReturnCode::OK, channelOutput.getPaceReturnCode()); + QVERIFY(channelOutput.getCARcurr().isEmpty()); + QVERIFY(channelOutput.getCARprev().isEmpty()); + QVERIFY(!channelOutput.getEfCardAccess().isEmpty()); + QVERIFY(!channelOutput.getIDicc().isEmpty()); + } + + + void parseEstablishPaceChannelOutput_fromCcid_parseCrap() + { + QByteArray hexBytes = QByteArray("30 0C" + " 04 04 F0200001" + " 04 02 9000" + " 31 00" + // " A4 22 04 20 24e41d62b8c848226b86fcc6c7657577dca47ad2bf21573617bae84807f85c6b" + // " A5 10 040e4445435643416549443030313033" + // " A6 10 040e4445435643416549443030313032" + " 9000" // ReturnCode + ); + + EstablishPaceChannelOutput channelOutput; + channelOutput.parseFromCcid(QByteArray::fromHex(hexBytes), PacePasswordId::PACE_PIN); + QCOMPARE(CardReturnCode::UNKNOWN, channelOutput.getPaceReturnCode()); + QCOMPARE(channelOutput.getMseStatusSetAt(), QByteArray()); + QCOMPARE(channelOutput.getCARcurr(), QByteArray()); + QCOMPARE(channelOutput.getCARprev(), QByteArray()); + QCOMPARE(channelOutput.getEfCardAccess(), QByteArray()); + QCOMPARE(channelOutput.getIDicc(), QByteArray()); + } + + + void parseEstablishPaceChannelOutput_fromCcid_onlyMandatoryElements() + { + QByteArray hexBytes = QByteArray("30 12" + " A1 06 04 04 F0200001" + " A2 04 04 02 9000" + " A3 02 31 00" + // " A4 22 04 20 24e41d62b8c848226b86fcc6c7657577dca47ad2bf21573617bae84807f85c6b" + // " A5 10 040e4445435643416549443030313033" + // " A6 10 040e4445435643416549443030313032" + " 9000" // ReturnCode + ); + + EstablishPaceChannelOutput channelOutput; + channelOutput.parseFromCcid(QByteArray::fromHex(hexBytes), PacePasswordId::PACE_PIN); + QCOMPARE(CardReturnCode::CANCELLATION_BY_USER, channelOutput.getPaceReturnCode()); + QCOMPARE(channelOutput.getMseStatusSetAt(), QByteArray::fromHex("9000")); + QCOMPARE(channelOutput.getCARcurr(), QByteArray()); + QCOMPARE(channelOutput.getCARprev(), QByteArray()); + QCOMPARE(channelOutput.getEfCardAccess(), QByteArray::fromHex("3100")); + QCOMPARE(channelOutput.getIDicc(), QByteArray()); + } + + + void parseEstablishPaceChannelOutput_fromCcid_allElements() + { + QByteArray hexBytes = QByteArray("30 5A" + " A1 06 04 04 F0200001" + " A2 04 04 02 9000" + " A3 02 31 00" + " A4 22 04 20 24e41d62b8c848226b86fcc6c7657577dca47ad2bf21573617bae84807f85c6b" + " A5 10 04 0e 4445435643416549443030313033" + " A6 10 04 0e 4445435643416549443030313032" + " 9000" // ReturnCode + ); + + EstablishPaceChannelOutput channelOutput; + channelOutput.parseFromCcid(QByteArray::fromHex(hexBytes), PacePasswordId::PACE_PIN); + QCOMPARE(CardReturnCode::CANCELLATION_BY_USER, channelOutput.getPaceReturnCode()); + QCOMPARE(channelOutput.getMseStatusSetAt(), QByteArray::fromHex("9000")); + QCOMPARE(channelOutput.getCARcurr(), QByteArray("DECVCAeID00103")); + QCOMPARE(channelOutput.getCARprev(), QByteArray("DECVCAeID00102")); + QCOMPARE(channelOutput.getEfCardAccess(), QByteArray::fromHex("3100")); + QCOMPARE(channelOutput.getIDicc(), QByteArray::fromHex("24e41d62b8c848226b86fcc6c7657577dca47ad2bf21573617bae84807f85c6b")); + } + + + /*! + * Workaround for erroneous firmware implementation of reader cyberJack wave: + * When entering a wrong PIN, the reader returns an invalid ASN.1 structure, where the element + * + * ESTABLISHPACECHANNELOUTPUT.efCardAccess + * + * is treated as IMPLICIT TAGGED. That's wrong. + */ + void parseEstablishPaceChannelOutput_fromCcid_cyberJackWave_workaround() + { + QByteArray hexBytes = QByteArray("30 10" + " a1 06 04 04 f00663c2" + " a2 04 04 02 0000" + " a3 00" + "9000" // ReturnCode + ); + EstablishPaceChannelOutput channelOutput; + channelOutput.parseFromCcid(QByteArray::fromHex(hexBytes), PacePasswordId::PACE_PIN); + QCOMPARE(CardReturnCode::INVALID_PIN, channelOutput.getPaceReturnCode()); + } + + + void toCcid() + { + QByteArray hexBytes = QByteArray("30 5A" + " A1 06 04 04 F0200001" + " A2 04 04 02 9000" + " A3 02 31 00" + " A4 22 04 20 24e41d62b8c848226b86fcc6c7657577dca47ad2bf21573617bae84807f85c6b" + " A5 10 04 0e 4445435643416549443030313033" + " A6 10 04 0e 4445435643416549443030313032" + " 9000" // ReturnCode + ); + + EstablishPaceChannelOutput channelOutput; + channelOutput.parseFromCcid(QByteArray::fromHex(hexBytes), PacePasswordId::PACE_PIN); + + EstablishPaceChannelOutput channelOutput2; + channelOutput2.parseFromCcid(channelOutput.toCcid(), PacePasswordId::PACE_PIN); + + QCOMPARE(channelOutput2.getPaceReturnCode(), CardReturnCode::CANCELLATION_BY_USER); + QCOMPARE(channelOutput2.getPaceReturnCode(), channelOutput.getPaceReturnCode()); + + QCOMPARE(channelOutput2.getMseStatusSetAt(), QByteArray::fromHex("9000")); + QCOMPARE(channelOutput2.getMseStatusSetAt(), channelOutput.getMseStatusSetAt()); + + QCOMPARE(channelOutput2.getCARcurr(), QByteArray("DECVCAeID00103")); + QCOMPARE(channelOutput2.getCARcurr(), channelOutput.getCARcurr()); + + QCOMPARE(channelOutput2.getCARprev(), QByteArray("DECVCAeID00102")); + QCOMPARE(channelOutput2.getCARprev(), channelOutput.getCARprev()); + + QCOMPARE(channelOutput2.getEfCardAccess(), QByteArray::fromHex("3100")); + QCOMPARE(channelOutput2.getEfCardAccess(), channelOutput.getEfCardAccess()); + + QCOMPARE(channelOutput2.getIDicc(), QByteArray::fromHex("24e41d62b8c848226b86fcc6c7657577dca47ad2bf21573617bae84807f85c6b")); + QCOMPARE(channelOutput2.getIDicc(), channelOutput.getIDicc()); + } + + + void toCcid_ReturnCode_ErrorCode_data() + { + QTest::addColumn("cardReturnCode"); + QTest::addColumn("pacePasswordId"); + + QTest::newRow("INVALID_CAN") << CardReturnCode::INVALID_CAN << PacePasswordId::PACE_CAN; + QTest::newRow("INVALID_PIN") << CardReturnCode::INVALID_PIN << PacePasswordId::PACE_PIN; + QTest::newRow("INVALID_PUK") << CardReturnCode::INVALID_PUK << PacePasswordId::PACE_PUK; + + QTest::newRow("OK") << CardReturnCode::OK << PacePasswordId::PACE_PIN; + QTest::newRow("CANCELLATION_BY_USER") << CardReturnCode::CANCELLATION_BY_USER << PacePasswordId::PACE_PIN; + QTest::newRow("INPUT_TIME_OUT") << CardReturnCode::INPUT_TIME_OUT << PacePasswordId::PACE_PIN; + QTest::newRow("COMMAND_FAILED") << CardReturnCode::COMMAND_FAILED << PacePasswordId::PACE_PIN; + QTest::newRow("CARD_NOT_FOUND") << CardReturnCode::CARD_NOT_FOUND << PacePasswordId::PACE_PIN; + } + + + void toCcid_ReturnCode_ErrorCode() + { + QFETCH(CardReturnCode, cardReturnCode); + QFETCH(PacePasswordId, pacePasswordId); + + EstablishPaceChannelOutput channelOutput; + channelOutput.setPaceReturnCode(cardReturnCode); + + EstablishPaceChannelOutput channelOutput2; + channelOutput2.parseFromCcid(channelOutput.toCcid(), pacePasswordId); + + QCOMPARE(channelOutput2.getPaceReturnCode(), cardReturnCode); + QCOMPARE(channelOutput2.getPaceReturnCode(), channelOutput.getPaceReturnCode()); + } + + +}; + +QTEST_GUILESS_MAIN(test_EstablishPaceChannelOutput) +#include "test_EstablishPaceChannelOutput.moc" diff --git a/test/qt/card/test_EstablishPaceChannelParser.cpp b/test/qt/card/test_EstablishPaceChannelParser.cpp new file mode 100644 index 0000000..18daefb --- /dev/null +++ b/test/qt/card/test_EstablishPaceChannelParser.cpp @@ -0,0 +1,51 @@ +/*! + * \brief Tests for \ref EstablishPaceChannelParser + * + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "EstablishPaceChannelParser.h" + +#include + +using namespace governikus; + + +class test_EstablishPaceChannelParser + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void buildAndParse() + { + const PacePasswordId pinId(PacePasswordId::PACE_PIN); + const QByteArray chat = QByteArray::fromHex("7F4C12060904007F00070301020253050000000F0F"); + const QByteArray certDescription = QByteArray::fromHex("30 8202A4" + " 06 0A 04007F00070301030103" + " A1 0E 0C0C442D547275737420476D6248" + " A3 3A 0C38476573616D7476657262616E64206465722064657574736368656E20566572736963686572756E67737769727473636861667420652E562E" + " A5 820248" + " 04 820244 4E616D652C20416E7363687269667420756E6420452D4D61696C2D4164726573736520646573204469656E737465616E626965746572733A0D0A476573616D7476657262616E64206465722064657574736368656E20566572736963686572756E67737769727473636861667420652E562E0D0A57696C68656C6D73747261C39F652034332F3433670D0A3130313137204265726C696E0D0A6265726C696E406764762E64650D0A0D0A4765736368C3A46674737A7765636B3A0D0A2D52656769737472696572756E6720756E64204C6F67696E20616D204744562D4D616B6C6572706F7274616C2D0D0A0D0A48696E7765697320617566206469652066C3BC722064656E204469656E737465616E626965746572207A757374C3A46E646967656E205374656C6C656E2C20646965206469652045696E68616C74756E672064657220566F7273636872696674656E207A756D20446174656E73636875747A206B6F6E74726F6C6C696572656E3A0D0A4265726C696E6572204265617566747261677465722066C3BC7220446174656E73636875747A20756E6420496E666F726D6174696F6E7366726569686569740D0A416E20646572205572616E696120342D31300D0A3130373837204265726C696E0D0A3033302F3133382038392D300D0A6D61696C626F7840646174656E73636875747A2D6265726C696E2E64650D0A687474703A2F2F7777772E646174656E73636875747A2D6265726C696E2E64650D0A416E737072656368706172746E65723A2044722E20416C6578616E64657220446978" + ); + + EstablishPaceChannel builder; + builder.setPasswordId(pinId); + builder.setChat(chat); + builder.setCertificateDescription(certDescription); + const auto command = builder.createCommandDataCcid(); + const auto buffer = command.getBuffer(); + + EstablishPaceChannelParser parser = EstablishPaceChannelParser::fromCcid(buffer); + + QCOMPARE(pinId, parser.getPasswordId()); + QCOMPARE(chat, parser.getChat()); + QCOMPARE(certDescription, parser.getCertificateDescription()); + QCOMPARE(command.getData(), parser.getCommandData()); + } + + +}; + +QTEST_GUILESS_MAIN(test_EstablishPaceChannelParser) +#include "test_EstablishPaceChannelParser.moc" diff --git a/test/qt/card/test_GeneralAuthenticateResponse.cpp b/test/qt/card/test_GeneralAuthenticateResponse.cpp index e297673..8657288 100644 --- a/test/qt/card/test_GeneralAuthenticateResponse.cpp +++ b/test/qt/card/test_GeneralAuthenticateResponse.cpp @@ -8,7 +8,6 @@ #include #include "GeneralAuthenticateResponse.h" -#include "TestFileHelper.h" using namespace governikus; diff --git a/test/qt/card/test_MSEBuilder.cpp b/test/qt/card/test_MSEBuilder.cpp index 216d988..69540b0 100644 --- a/test/qt/card/test_MSEBuilder.cpp +++ b/test/qt/card/test_MSEBuilder.cpp @@ -3,7 +3,6 @@ */ #include "Commands.h" -#include "TestFileHelper.h" #include #include @@ -56,7 +55,7 @@ class test_MSEBuilder void setPublicKeyId() { - PACE_PASSWORD_ID pinId = PACE_PASSWORD_ID::PACE_PIN; + PacePasswordId pinId = PacePasswordId::PACE_PIN; mMseBuilder->setPublicKey(pinId); assertCommandContent(QByteArray::fromHex("8301").append(static_cast(pinId))); } diff --git a/test/qt/card/test_ReaderManager.cpp b/test/qt/card/test_ReaderManager.cpp index 4278a5c..a16a9ca 100644 --- a/test/qt/card/test_ReaderManager.cpp +++ b/test/qt/card/test_ReaderManager.cpp @@ -5,14 +5,12 @@ #include "ReaderManager.h" #include "MockReaderManagerPlugIn.h" -#include "ReaderManagerWorker.h" #include #include #include #include - Q_IMPORT_PLUGIN(MockReaderManagerPlugIn) using namespace governikus; @@ -26,22 +24,10 @@ class CreateCardConnectionCommandSlot public: QSharedPointer mCardConnection; bool mSlotCalled = false; - QTime mDieTime; - - void wait(int pMillis = 1000) - { - mDieTime = QTime::currentTime().addMSecs(pMillis); - while (QTime::currentTime() < mDieTime) - { - QCoreApplication::processEvents(QEventLoop::AllEvents, 100); - } - } - public Q_SLOTS: void onCardCommandDone(QSharedPointer pCommand) { - mDieTime = QTime::currentTime(); mSlotCalled = true; mCardConnection = pCommand->getCardConnection(); } @@ -58,38 +44,37 @@ class test_ReaderManager private Q_SLOTS: void initTestCase() { - ReaderManager::getInstance().init(); - ReaderManager::getInstance().getPlugInInfos(); // just to wait until initialization finished + const auto readerManager = Env::getSingleton(); + readerManager->init(); + readerManager->getPlugInInfos(); // just to wait until initialization finished } void cleanupTestCase() { - ReaderManager::getInstance().shutdown(); + Env::getSingleton()->shutdown(); } void fireReaderAdded() { - QSignalSpy spy(&ReaderManager::getInstance(), &ReaderManager::fireReaderAdded); + QSignalSpy spy(Env::getSingleton(), &ReaderManager::fireReaderAdded); MockReaderManagerPlugIn::getInstance().addReader("MockReader 4711"); - spy.wait(); - QCOMPARE(spy.count(), 1); + QTRY_COMPARE(spy.count(), 1); QCOMPARE(spy.takeFirst().at(0).toString(), QString("MockReader 4711")); } void fireReaderRemoved() { - QSignalSpy spy(&ReaderManager::getInstance(), &ReaderManager::fireReaderRemoved); + QSignalSpy spy(Env::getSingleton(), &ReaderManager::fireReaderRemoved); MockReaderManagerPlugIn::getInstance().addReader("MockReader 4711"); MockReaderManagerPlugIn::getInstance().removeReader("MockReader 4711"); - spy.wait(); - QCOMPARE(spy.count(), 1); + QTRY_COMPARE(spy.count(), 1); QCOMPARE(spy.takeFirst().at(0).toString(), QString("MockReader 4711")); } @@ -98,10 +83,9 @@ class test_ReaderManager { CreateCardConnectionCommandSlot commandSlot; - ReaderManager::getInstance().callCreateCardConnectionCommand("UnknownReader", &commandSlot, &CreateCardConnectionCommandSlot::onCardCommandDone); + Env::getSingleton()->callCreateCardConnectionCommand("UnknownReader", &commandSlot, &CreateCardConnectionCommandSlot::onCardCommandDone); - commandSlot.wait(); - QVERIFY(commandSlot.mSlotCalled); + QTRY_COMPARE(commandSlot.mSlotCalled, true); QVERIFY(commandSlot.mCardConnection.isNull()); } @@ -111,10 +95,9 @@ class test_ReaderManager CreateCardConnectionCommandSlot commandSlot; MockReaderManagerPlugIn::getInstance().addReader("MockReader 4711"); - ReaderManager::getInstance().callCreateCardConnectionCommand("UnknownReader", &commandSlot, &CreateCardConnectionCommandSlot::onCardCommandDone); + Env::getSingleton()->callCreateCardConnectionCommand("UnknownReader", &commandSlot, &CreateCardConnectionCommandSlot::onCardCommandDone); - commandSlot.wait(); - QVERIFY(commandSlot.mSlotCalled); + QTRY_COMPARE(commandSlot.mSlotCalled, true); QVERIFY(commandSlot.mCardConnection.isNull()); } @@ -127,10 +110,9 @@ class test_ReaderManager cardConfig.mConnect = CardReturnCode::COMMAND_FAILED; reader->setCard(cardConfig); - ReaderManager::getInstance().callCreateCardConnectionCommand("MockReader 4711", &commandSlot, &CreateCardConnectionCommandSlot::onCardCommandDone); + Env::getSingleton()->callCreateCardConnectionCommand("MockReader 4711", &commandSlot, &CreateCardConnectionCommandSlot::onCardCommandDone); - commandSlot.wait(); - QVERIFY(commandSlot.mSlotCalled); + QTRY_COMPARE(commandSlot.mSlotCalled, true); QVERIFY(commandSlot.mCardConnection.isNull()); } @@ -143,10 +125,9 @@ class test_ReaderManager cardConfig.mConnect = CardReturnCode::OK; reader->setCard(cardConfig); - ReaderManager::getInstance().callCreateCardConnectionCommand("MockReader 4711", &commandSlot, &CreateCardConnectionCommandSlot::onCardCommandDone); + Env::getSingleton()->callCreateCardConnectionCommand("MockReader 4711", &commandSlot, &CreateCardConnectionCommandSlot::onCardCommandDone); - commandSlot.wait(); - QVERIFY(commandSlot.mSlotCalled); + QTRY_COMPARE(commandSlot.mSlotCalled, true); QVERIFY(!commandSlot.mCardConnection.isNull()); } @@ -154,14 +135,14 @@ class test_ReaderManager void getInvalidReaderInfoWithAndWithoutInitializedReaderManager() { { - const auto& readerInfo = ReaderManager::getInstance().getReaderInfo("test dummy"); + const auto& readerInfo = Env::getSingleton()->getReaderInfo("test dummy"); QCOMPARE(readerInfo.getPlugInType(), ReaderManagerPlugInType::UNKNOWN); QCOMPARE(readerInfo.getName(), QStringLiteral("test dummy")); } cleanupTestCase(); { - const auto& readerInfo = ReaderManager::getInstance().getReaderInfo("test dummy"); + const auto& readerInfo = Env::getSingleton()->getReaderInfo("test dummy"); QCOMPARE(readerInfo.getPlugInType(), ReaderManagerPlugInType::UNKNOWN); QCOMPARE(readerInfo.getName(), QStringLiteral("test dummy")); } diff --git a/test/qt/card/test_SecureMessaging.cpp b/test/qt/card/test_SecureMessaging.cpp index 6ffda81..4fcb17c 100644 --- a/test/qt/card/test_SecureMessaging.cpp +++ b/test/qt/card/test_SecureMessaging.cpp @@ -95,7 +95,7 @@ class test_SecureMessaging void testCommandChainingCLA() { QByteArray data; - CommandApdu command(static_cast(0x10), static_cast(0x10), static_cast(0x20), static_cast(0x30), data, Apdu::NO_LE); + CommandApdu command(static_cast(0x10), static_cast(0x10), static_cast(0x20), static_cast(0x30), data, CommandApdu::NO_LE); QCOMPARE(command.getBuffer(), QByteArray::fromHex("10102030")); CommandApdu securedCommand = mSecureMessaging->encrypt(command); QCOMPARE(securedCommand.getBuffer(), QByteArray::fromHex("1c1020300a8e0852d8f5136eddaab800")); @@ -105,7 +105,7 @@ class test_SecureMessaging void testNoDataNoLe() { QByteArray data; - CommandApdu command(static_cast(0x00), static_cast(0x10), static_cast(0x20), static_cast(0x30), data, Apdu::NO_LE); + CommandApdu command(static_cast(0x00), static_cast(0x10), static_cast(0x20), static_cast(0x30), data, CommandApdu::NO_LE); QCOMPARE(command.getBuffer(), QByteArray::fromHex("00102030")); CommandApdu securedCommand = mSecureMessaging->encrypt(command); QCOMPARE(securedCommand.getBuffer(), QByteArray::fromHex("0c1020300a8e08fd824409ae18c2f500")); @@ -125,7 +125,7 @@ class test_SecureMessaging void testNoDataShortMaxLe() { QByteArray data; - CommandApdu command(static_cast(0x00), static_cast(0x10), static_cast(0x20), static_cast(0x30), data, Apdu::SHORT_MAX_LE); + CommandApdu command(static_cast(0x00), static_cast(0x10), static_cast(0x20), static_cast(0x30), data, CommandApdu::SHORT_MAX_LE); QCOMPARE(command.getBuffer(), QByteArray::fromHex("0010203000")); CommandApdu securedCommand = mSecureMessaging->encrypt(command); QCOMPARE(securedCommand.getBuffer(), QByteArray::fromHex("0c1020300d9701008e08a361948e0c90464b00")); @@ -135,7 +135,7 @@ class test_SecureMessaging void testNoDataExtendedMaxLe() { QByteArray data; - CommandApdu command(static_cast(0x00), static_cast(0x10), static_cast(0x20), static_cast(0x30), data, Apdu::EXTENDED_MAX_LE); + CommandApdu command(static_cast(0x00), static_cast(0x10), static_cast(0x20), static_cast(0x30), data, CommandApdu::EXTENDED_MAX_LE); QCOMPARE(command.getBuffer(), QByteArray::fromHex("00102030000000")); CommandApdu securedCommand = mSecureMessaging->encrypt(command); QCOMPARE(securedCommand.getBuffer(), QByteArray::fromHex("0c10203000000e970200008e088b5e9fdb6cc8d9c70000")); @@ -145,7 +145,7 @@ class test_SecureMessaging void testDataShortMaxLe() { QByteArray data = QByteArray::fromHex("D0D1D2D3"); - CommandApdu command(static_cast(0x00), static_cast(0x10), static_cast(0x20), static_cast(0x30), data, Apdu::SHORT_MAX_LE); + CommandApdu command(static_cast(0x00), static_cast(0x10), static_cast(0x20), static_cast(0x30), data, CommandApdu::SHORT_MAX_LE); QCOMPARE(command.getBuffer(), QByteArray::fromHex("0010203004d0d1d2d300")); CommandApdu securedCommand = mSecureMessaging->encrypt(command); QCOMPARE(securedCommand.getBuffer(), @@ -156,7 +156,7 @@ class test_SecureMessaging void testDataExtendedMaxLe() { QByteArray data = QByteArray::fromHex("D0D1D2D3"); - CommandApdu command(static_cast(0x00), static_cast(0x10), static_cast(0x20), static_cast(0x30), data, Apdu::EXTENDED_MAX_LE); + CommandApdu command(static_cast(0x00), static_cast(0x10), static_cast(0x20), static_cast(0x30), data, CommandApdu::EXTENDED_MAX_LE); QCOMPARE(command.getBuffer(), QByteArray::fromHex("00102030000004d0d1d2d30000")); CommandApdu securedCommand = mSecureMessaging->encrypt(command); QCOMPARE(securedCommand.getBuffer(), @@ -167,7 +167,7 @@ class test_SecureMessaging void testSendSequenceCounter() { QByteArray data = QByteArray::fromHex("D0D1D2D3"); - CommandApdu command(static_cast(0x00), static_cast(0x10), static_cast(0x20), static_cast(0x30), data, Apdu::EXTENDED_MAX_LE); + CommandApdu command(static_cast(0x00), static_cast(0x10), static_cast(0x20), static_cast(0x30), data, CommandApdu::EXTENDED_MAX_LE); QCOMPARE(command.getBuffer(), QByteArray::fromHex("00102030000004d0d1d2d30000")); CommandApdu securedCommand1 = mSecureMessaging->encrypt(command); diff --git a/test/qt/configuration/test_ProviderConfiguration.cpp b/test/qt/configuration/test_ProviderConfiguration.cpp index ad9f2c3..b999d3e 100644 --- a/test/qt/configuration/test_ProviderConfiguration.cpp +++ b/test/qt/configuration/test_ProviderConfiguration.cpp @@ -6,7 +6,6 @@ #include "ProviderConfiguration.h" -#include "Env.h" #include "ResourceLoader.h" #include @@ -114,7 +113,6 @@ class test_ProviderConfiguration { QVERIFY(provider.getPhone().isEmpty() || provider.getPhone().startsWith("+49 ")); } - } diff --git a/test/qt/configuration/test_ProviderConfigurationParser.cpp b/test/qt/configuration/test_ProviderConfigurationParser.cpp index 9035d09..07ebe1c 100644 --- a/test/qt/configuration/test_ProviderConfigurationParser.cpp +++ b/test/qt/configuration/test_ProviderConfigurationParser.cpp @@ -250,12 +250,12 @@ class test_ProviderConfigurationParser { QTest::addColumn("count"); - const int desktop = 58; + const int desktop = 59; QTest::newRow("win") << desktop; QTest::newRow("mac") << desktop; QTest::newRow("linux") << desktop; - QTest::newRow("android") << 53; - QTest::newRow("ios") << 13; + QTest::newRow("android") << 54; + QTest::newRow("ios") << 14; } diff --git a/test/qt/configuration/test_ReaderConfiguration.cpp b/test/qt/configuration/test_ReaderConfiguration.cpp index be3def8..f54cef3 100644 --- a/test/qt/configuration/test_ReaderConfiguration.cpp +++ b/test/qt/configuration/test_ReaderConfiguration.cpp @@ -7,7 +7,6 @@ #include "ReaderConfiguration.h" #include "Env.h" -#include "FuncUtils.h" #include "MockReaderDetector.h" #include "ResourceLoader.h" @@ -85,14 +84,14 @@ class test_ReaderConfiguration QTest::newRow("SCM SDI011") << UsbId(0x04E6, 0x512B) << "SDI011 Contactless Reader" << "SDI011 Contactless Reader" << "img_Identive_SDI011" << "SDI011 (USB )?(Smart Card|Contactless) Reader"; QTest::newRow("SCM SCL011") << UsbId(0x04E6, 0x5292) << "SCM Microsystems Inc. SCL011 Contactless Reader" << "SCL01x Contactless Reader" << "img_Identive_SCL011" << "^(SCM Microsystems Inc. )?SCL011 Contactless Reader( 0)?$"; - QTest::newRow("ACS-ACR122U") << UsbId(0x072F, 0x2200) << "ACS ACR122U PICC Interface" << "ACS ACR122U" << "img_ACS_ACR122U" << "ACS ACR122U PICC Interface( 0)?"; QTest::newRow("ACS-ACR1281U") << UsbId(0x072F, 0x0901) << "ACS ACR1281 PICC Reader" << "ACS ACR1281U" << "img_ACS_ACR1281U" << "ACS ACR1281 PICC Reader( 0)?"; QTest::newRow("ACS-ACR1252U") << UsbId(0x072F, 0x223B) << "ACS ACR1252 1S CL Reader PICC 0" << "ACS ACR1252U" << "img_ACS_ACR1252U" << R"(ACS ACR1252 1S CL Reader PICC 0|ACS ACR1252 Dual Reader(\(1\)|\(2\)))"; QTest::newRow("OMNIKEY 5021") << UsbId(0x076B, 0x5340) << "OMNIKEY CardMan 5x21-CL 0" << "OMNIKEY 5021-CL" << "img_HID_Omnikey_Mobile_Reader_502X_CL" << "OMNIKEY CardMan 5x21-CL 0|OMNIKEY CardMan \\(076B:5340\\) 5021 CL"; QTest::newRow("OMNIKEY 5022") << UsbId(0x076B, 0x5022) << "HID Global OMNIKEY 5022 Smart Card Reader 0" << "OMNIKEY 5022-CL" << "img_HID_Omnikey_Mobile_Reader_502X_CL" << "HID Global OMNIKEY 5022 Smart Card Reader 0|HID Global OMNIKEY 5022 Smart Card Reader"; QTest::newRow("OMNIKEY 5321 v2") << UsbId(0x076B, 0x5321) << "OOMNIKEY CardMan 5x21-CL 0" << "OMNIKEY 5321 v2" << "img_HID_Global_OMNIKEY_5321_V2" << R"(OMNIKEY CardMan 5x21-CL 0|OMNIKEY CardMan \(076B:5321\) 5321(\(1\)|\(2\)))"; - QTest::newRow("OMNIKEY 5421") << UsbId(0x076B, 0x5421) << "OMNIKEY CardMan 5x21-CL 0" << "OMNIKEY 5421" << "img_HID_Omnikey_5421" << R"(OMNIKEY CardMan 5x21-CL 0|OMNIKEY CardMan \(076B:5421\) 5421(\(1\)|\(2\)))"; + QTest::newRow("OMNIKEY 5421") << UsbId(0x076B, 0x5421) << "OMNIKEY CardMan 5x21-CL 0" << "OMNIKEY 5421" << "img_HID_Omnikey_542x" << R"(OMNIKEY CardMan 5x21-CL 0|OMNIKEY CardMan \(076B:5421\) 5421(\(1\)|\(2\)))"; + QTest::newRow("OMNIKEY 5422") << UsbId(0x076B, 0x5422) << "HID Global OMNIKEY 5422CL Smartcard Reader 0" << "OMNIKEY 5422" << "img_HID_Omnikey_542x" << R"(HID Global OMNIKEY 5422(CL)? Smartcard Reader 0|HID Global OMNIKEY Smartcard Reader (\(1\)|\(2\)))"; QTest::newRow("FEIG OBID myAXXESS RFID-Reader") << UsbId(0x0AB1, 0x0003) << "FEIG ELECTRONIC GmbH OBID myAXXESS basic Slot:CL 358334430" << "OBID RFID-Reader" << "img_FEIG_myAXXES_basic" << "FEIG ELECTRONIC GmbH OBID myAXXESS basic Slot:CL 358334430"; @@ -145,28 +144,28 @@ class test_ReaderConfiguration QTest::newRow("REINER SCT cyberJack RFID komfort-windows-7-64") << UsbId(0x0C4B, 0x0501) << "REINER SCT cyberJack RFID komfort USB 1" << "REINER SCT cyberJack RFID komfort"; QTest::newRow("REINER SCT cyberJack RFID komfort-windows-8.1") << UsbId(0x0C4B, 0x0501) << "REINER SCT cyberJack RFID komfort USB 1" << "REINER SCT cyberJack RFID komfort"; QTest::newRow("REINER SCT cyberJack RFID komfort-windows-10.0") << UsbId(0x0C4B, 0x0501) << "REINER SCT cyberJack RFID komfort USB 1" << "REINER SCT cyberJack RFID komfort"; - QTest::newRow("REINER SCT cyberJack RFID komfort-macosx-10.10") << UsbId(0x0C4B, 0x0501) << "REINER SCT cyberJack RFID komfort" << "REINER SCT cyberJack RFID komfort"; QTest::newRow("REINER SCT cyberJack RFID komfort-macosx-10.11") << UsbId(0x0C4B, 0x0501) << "REINER SCT cyberJack RFID komfort" << "REINER SCT cyberJack RFID komfort"; QTest::newRow("REINER SCT cyberJack RFID komfort-macosx-10.12") << UsbId(0x0C4B, 0x0501) << "REINER SCT cyberJack RFID komfort" << "REINER SCT cyberJack RFID komfort"; QTest::newRow("REINER SCT cyberJack RFID komfort-macosx-10.13") << UsbId(0x0C4B, 0x0501) << "REINER SCT cyberJack RFID komfort" << "REINER SCT cyberJack RFID komfort"; + QTest::newRow("REINER SCT cyberJack RFID komfort-macosx-10.14") << UsbId(0x0C4B, 0x0501) << "REINER SCT cyberJack RFID komfort" << "REINER SCT cyberJack RFID komfort"; QTest::newRow("REINER SCT cyberJack RFID standard-windows-7.32") << UsbId(0x0C4B, 0x0500) << "REINER SCT cyberJack RFID standard USB 1" << "REINER SCT cyberJack RFID standard"; QTest::newRow("REINER SCT cyberJack RFID standard-windows-7.64") << UsbId(0x0C4B, 0x0500) << "REINER SCT cyberJack RFID standard USB 1" << "REINER SCT cyberJack RFID standard"; QTest::newRow("REINER SCT cyberJack RFID standard-windows-8.1") << UsbId(0x0C4B, 0x0500) << "REINER SCT cyberJack RFID standard USB 1" << "REINER SCT cyberJack RFID standard"; QTest::newRow("REINER SCT cyberJack RFID standard-windows-10.0") << UsbId(0x0C4B, 0x0500) << "REINER SCT cyberJack RFID standard USB 1" << "REINER SCT cyberJack RFID standard"; - QTest::newRow("REINER SCT cyberJack RFID standard-macosx-10.10") << UsbId(0x0C4B, 0x0500) << "REINER SCT cyberJack RFID standard" << "REINER SCT cyberJack RFID standard"; QTest::newRow("REINER SCT cyberJack RFID standard-macosx-10.11") << UsbId(0x0C4B, 0x0500) << "REINER SCT cyberJack RFID standard" << "REINER SCT cyberJack RFID standard"; QTest::newRow("REINER SCT cyberJack RFID standard-macosx-10.12") << UsbId(0x0C4B, 0x0500) << "REINER SCT cyberJack RFID standard" << "REINER SCT cyberJack RFID standard"; QTest::newRow("REINER SCT cyberJack RFID standard-macosx-10.13") << UsbId(0x0C4B, 0x0500) << "REINER SCT cyberJack RFID standard" << "REINER SCT cyberJack RFID standard"; + QTest::newRow("REINER SCT cyberJack RFID standard-macosx-10.14") << UsbId(0x0C4B, 0x0500) << "REINER SCT cyberJack RFID standard" << "REINER SCT cyberJack RFID standard"; QTest::newRow("REINER SCT cyberJack RFID basis-windows-7.32") << UsbId(0x0C4B, 0x9102) << "REINER SCT cyberJack RFID basis 0" << "REINER SCT cyberJack RFID basis"; QTest::newRow("REINER SCT cyberJack RFID basis-windows-7.64") << UsbId(0x0C4B, 0x9102) << "REINER SCT cyberJack RFID basis 0" << "REINER SCT cyberJack RFID basis"; QTest::newRow("REINER SCT cyberJack RFID basis-windows-8.1") << UsbId(0x0C4B, 0x9102) << "REINER SCT cyberJack RFID basis 0" << "REINER SCT cyberJack RFID basis"; QTest::newRow("REINER SCT cyberJack RFID basis-windows-10.0") << UsbId(0x0C4B, 0x9102) << "REINER SCT cyberJack RFID basis 0" << "REINER SCT cyberJack RFID basis"; - QTest::newRow("REINER SCT cyberJack RFID basis-macosx-10.10") << UsbId(0x0C4B, 0x9102) << "REINER SCT cyberJack RFID basis" << "REINER SCT cyberJack RFID basis"; QTest::newRow("REINER SCT cyberJack RFID basis-macosx-10.11") << UsbId(0x0C4B, 0x9102) << "REINER SCT cyberJack RFID basis" << "REINER SCT cyberJack RFID basis"; QTest::newRow("REINER SCT cyberJack RFID basis-macosx-10.12") << UsbId(0x0C4B, 0x9102) << "REINER SCT cyberJack RFID basis" << "REINER SCT cyberJack RFID basis"; QTest::newRow("REINER SCT cyberJack RFID basis-macosx-10.13") << UsbId(0x0C4B, 0x9102) << "REINER SCT cyberJack RFID basis" << "REINER SCT cyberJack RFID basis"; + QTest::newRow("REINER SCT cyberJack RFID basis-macosx-10.14") << UsbId(0x0C4B, 0x9102) << "REINER SCT cyberJack RFID basis" << "REINER SCT cyberJack RFID basis"; QTest::newRow("REINER SCT cyberJack wave-windows-7.32") << UsbId(0x0C4B, 0x0505) << "REINER SCT cyberJack wave 0" << "REINER SCT cyberJack wave"; QTest::newRow("REINER SCT cyberJack wave-windows-7.64") << UsbId(0x0C4B, 0x0505) << "REINER SCT cyberJack wave 0" << "REINER SCT cyberJack wave"; @@ -175,15 +174,16 @@ class test_ReaderConfiguration QTest::newRow("REINER SCT cyberJack wave-macosx-10.11") << UsbId(0x0C4B, 0x0505) << "REINER SCT cyberJack wave" << "REINER SCT cyberJack wave"; QTest::newRow("REINER SCT cyberJack wave-macosx-10.12") << UsbId(0x0C4B, 0x0505) << "REINER SCT cyberJack wave" << "REINER SCT cyberJack wave"; QTest::newRow("REINER SCT cyberJack wave-macosx-10.13") << UsbId(0x0C4B, 0x0505) << "REINER SCT cyberJack wave" << "REINER SCT cyberJack wave"; + QTest::newRow("REINER SCT cyberJack wave-macosx-10.14") << UsbId(0x0C4B, 0x0505) << "REINER SCT cyberJack wave" << "REINER SCT cyberJack wave"; QTest::newRow("KOBIL IDToken-windows-7.32") << UsbId(0x0D46, 0x301D) << "KOBIL IDToken 0" << "KOBIL IDToken"; QTest::newRow("KOBIL IDToken-windows-7.64") << UsbId(0x0D46, 0x301D) << "KOBIL IDToken 0" << "KOBIL IDToken"; QTest::newRow("KOBIL IDToken-windows-8.1") << UsbId(0x0D46, 0x301D) << "KOBIL IDToken 0" << "KOBIL IDToken"; QTest::newRow("KOBIL IDToken-windows-10") << UsbId(0x0D46, 0x301D) << "KOBIL IDToken 0" << "KOBIL IDToken"; - QTest::newRow("KOBIL IDToken-macosx-10.10") << UsbId(0x0D46, 0x301D) << "KOBIL Systems IDToken" << "KOBIL IDToken"; QTest::newRow("KOBIL IDToken-macosx-10.11") << UsbId(0x0D46, 0x301D) << "KOBIL Systems IDToken" << "KOBIL IDToken"; QTest::newRow("KOBIL IDToken-macosx-10.12") << UsbId(0x0D46, 0x301D) << "KOBIL Systems IDToken" << "KOBIL IDToken"; QTest::newRow("KOBIL IDToken-macosx-10.13") << UsbId(0x0D46, 0x301D) << "KOBIL Systems IDToken" << "KOBIL IDToken"; + QTest::newRow("KOBIL IDToken-macosx-10.14") << UsbId(0x0D46, 0x301D) << "KOBIL Systems IDToken" << "KOBIL IDToken"; QTest::newRow("SCM SDI011-windows-7.32-1") << UsbId(0x04E6, 0x512B) << "SCM Microsystems Inc. SDI011 Contactless Reader 0" << "SDI011 Contactless Reader"; QTest::newRow("SCM SDI011-windows-7.32-2") << UsbId(0x04E6, 0x512B) << "SCM Microsystems Inc. SDI011 Smart Card Reader 0" << "SDI011 Contactless Reader"; @@ -193,41 +193,32 @@ class test_ReaderConfiguration QTest::newRow("SCM SDI011-windows-8.1-2") << UsbId(0x04E6, 0x512B) << "SCM Microsystems Inc. SDI011 Smart Card Reader 0" << "SDI011 Contactless Reader"; QTest::newRow("SCM SDI011-windows-10-1") << UsbId(0x04E6, 0x512B) << "SCM Microsystems Inc. SDI011 Contactless Reader 0" << "SDI011 Contactless Reader"; QTest::newRow("SCM SDI011-windows-10-2") << UsbId(0x04E6, 0x512B) << "SCM Microsystems Inc. SDI011 Smart Card Reader 0" << "SDI011 Contactless Reader"; - QTest::newRow("SCM SDI011-macosx-10.10-1") << UsbId(0x04E6, 0x512B) << "SDI011 USB Smart Card Reader(1)" << "SDI011 Contactless Reader"; - QTest::newRow("SCM SDI011-macosx-10.10-2") << UsbId(0x04E6, 0x512B) << "SDI011 USB Smart Card Reader(2)" << "SDI011 Contactless Reader"; QTest::newRow("SCM SDI011-macosx-10.11-1") << UsbId(0x04E6, 0x512B) << "SDI011 USB Smart Card Reader(1)" << "SDI011 Contactless Reader"; QTest::newRow("SCM SDI011-macosx-10.11-2") << UsbId(0x04E6, 0x512B) << "SDI011 USB Smart Card Reader(2)" << "SDI011 Contactless Reader"; QTest::newRow("SCM SDI011-macosx-10.12-1") << UsbId(0x04E6, 0x512B) << "SDI011 USB Smart Card Reader(1)" << "SDI011 Contactless Reader"; QTest::newRow("SCM SDI011-macosx-10.12-1") << UsbId(0x04E6, 0x512B) << "SDI011 USB Smart Card Reader(2)" << "SDI011 Contactless Reader"; QTest::newRow("SCM SDI011-macosx-10.13-1") << UsbId(0x04E6, 0x512B) << "SDI011 USB Smart Card Reader(1)" << "SDI011 Contactless Reader"; QTest::newRow("SCM SDI011-macosx-10.13-2") << UsbId(0x04E6, 0x512B) << "SDI011 USB Smart Card Reader(2)" << "SDI011 Contactless Reader"; + QTest::newRow("SCM SDI011-macosx-10.14-1") << UsbId(0x04E6, 0x512B) << "SDI011 USB Smart Card Reader(1)" << "SDI011 Contactless Reader"; + QTest::newRow("SCM SDI011-macosx-10.14-2") << UsbId(0x04E6, 0x512B) << "SDI011 USB Smart Card Reader(2)" << "SDI011 Contactless Reader"; QTest::newRow("SCM SCL011-windows-7.32") << UsbId(0x04E6, 0x5292) << "SCM Microsystems Inc. SCL011 Contactless Reader 0" << "SCL01x Contactless Reader"; QTest::newRow("SCM SCL011-windows-7.64") << UsbId(0x04E6, 0x5292) << "SCM Microsystems Inc. SCL011 Contactless Reader 0" << "SCL01x Contactless Reader"; QTest::newRow("SCM SCL011-windows-8.1") << UsbId(0x04E6, 0x5292) << "SCM Microsystems Inc. SCL011 Contactless Reader 0" << "SCL01x Contactless Reader"; QTest::newRow("SCM SCL011-windows-10") << UsbId(0x04E6, 0x5292) << "SCM Microsystems Inc. SCL011 Contactless Reader 0" << "SCL01x Contactless Reader"; - QTest::newRow("SCM SCL011-macosx-10.10") << UsbId(0x04E6, 0x5292) << "SCL011 Contactless Reader" << "SCL01x Contactless Reader"; QTest::newRow("SCM SCL011-macosx-10.11") << UsbId(0x04E6, 0x5292) << "SCL011 Contactless Reader" << "SCL01x Contactless Reader"; QTest::newRow("SCM SCL011-macosx-10.12") << UsbId(0x04E6, 0x5292) << "SCL011 Contactless Reader" << "SCL01x Contactless Reader"; QTest::newRow("SCM SCL011-macosx-10.13") << UsbId(0x04E6, 0x5292) << "SCL011 Contactless Reader" << "SCL01x Contactless Reader"; - - QTest::newRow("ACS-ACR122U-windows-7.32") << UsbId(0x072F, 0x2200) << "ACS ACR122U PICC Interface 0" << "ACS ACR122U"; - QTest::newRow("ACS-ACR122U-windows-7.64") << UsbId(0x072F, 0x2200) << "ACS ACR122U PICC Interface 0" << "ACS ACR122U"; - QTest::newRow("ACS-ACR122U-windows-8.1") << UsbId(0x072F, 0x2200) << "ACS ACR122U PICC Interface 0" << "ACS ACR122U"; - QTest::newRow("ACS-ACR122U-windows-10") << UsbId(0x072F, 0x2200) << "ACS ACR122U PICC Interface 0" << "ACS ACR122U"; - QTest::newRow("ACS-ACR122U-macosx-10.10") << UsbId(0x072F, 0x2200) << "ACS ACR122U PICC Interface" << "ACS ACR122U"; - QTest::newRow("ACS-ACR122U-macosx-10.11") << UsbId(0x072F, 0x2200) << "ACS ACR122U PICC Interface" << "ACS ACR122U"; - QTest::newRow("ACS-ACR122U-macosx-10.12") << UsbId(0x072F, 0x2200) << "ACS ACR122U PICC Interface" << "ACS ACR122U"; - QTest::newRow("ACS-ACR122U-macosx-10.13") << UsbId(0x072F, 0x2200) << "ACS ACR122U PICC Interface" << "ACS ACR122U"; + QTest::newRow("SCM SCL011-macosx-10.14") << UsbId(0x04E6, 0x5292) << "SCL011 Contactless Reader" << "SCL01x Contactless Reader"; QTest::newRow("ACS-ACR1281U-windows-7.32") << UsbId(0x072F, 0x0901) << "ACS ACR1281 PICC Reader 0" << "ACS ACR1281U"; QTest::newRow("ACS-ACR1281U-windows-7.64") << UsbId(0x072F, 0x0901) << "ACS ACR1281 PICC Reader 0" << "ACS ACR1281U"; QTest::newRow("ACS-ACR1281U-windows-8.1") << UsbId(0x072F, 0x0901) << "ACS ACR1281 PICC Reader 0" << "ACS ACR1281U"; QTest::newRow("ACS-ACR1281U-windows-10") << UsbId(0x072F, 0x0901) << "ACS ACR1281 PICC Reader 0" << "ACS ACR1281U"; - QTest::newRow("ACS-ACR1281U-macosx-10.10") << UsbId(0x072F, 0x0901) << "ACS ACR1281 PICC Reader" << "ACS ACR1281U"; QTest::newRow("ACS-ACR1281U-macosx-10.11") << UsbId(0x072F, 0x0901) << "ACS ACR1281 PICC Reader" << "ACS ACR1281U"; QTest::newRow("ACS-ACR1281U-macosx-10.12") << UsbId(0x072F, 0x0901) << "ACS ACR1281 PICC Reader" << "ACS ACR1281U"; QTest::newRow("ACS-ACR1281U-macosx-10.13") << UsbId(0x072F, 0x0901) << "ACS ACR1281 PICC Reader" << "ACS ACR1281U"; + QTest::newRow("ACS-ACR1281U-macosx-10.14") << UsbId(0x072F, 0x0901) << "ACS ACR1281 PICC Reader" << "ACS ACR1281U"; QTest::newRow("ACS-ACR1252U-windows-7.32-1") << UsbId(0x072F, 0x223B) << "ACS ACR1252 1S CL Reader PICC 0" << "ACS ACR1252U"; QTest::newRow("ACS-ACR1252U-windows-7.32-2") << UsbId(0x072F, 0x223B) << "ACS ACR1252 1S CL Reader SAM 0" << "ACS ACR1252 1S CL Reader SAM 0"; @@ -237,32 +228,32 @@ class test_ReaderConfiguration QTest::newRow("ACS-ACR1252U-windows-8.1-2") << UsbId(0x072F, 0x223B) << "ACS ACR1252 1S CL Reader SAM 0" << "ACS ACR1252 1S CL Reader SAM 0"; QTest::newRow("ACS-ACR1252U-windows-10-1") << UsbId(0x072F, 0x223B) << "ACS ACR1252 1S CL Reader PICC 0" << "ACS ACR1252U"; QTest::newRow("ACS-ACR1252U-windows-10-2") << UsbId(0x072F, 0x223B) << "ACS ACR1252 1S CL Reader SAM 0" << "ACS ACR1252 1S CL Reader SAM 0"; - QTest::newRow("ACS-ACR1252U-macosx-10.10-1") << UsbId(0x072F, 0x223B) << "ACS ACR1252 Dual Reader(1)" << "ACS ACR1252U"; - QTest::newRow("ACS-ACR1252U-macosx-10.10-2") << UsbId(0x072F, 0x223B) << "ACS ACR1252 Dual Reader(2)" << "ACS ACR1252U"; QTest::newRow("ACS-ACR1252U-macosx-10.11-1") << UsbId(0x072F, 0x223B) << "ACS ACR1252 Dual Reader(1)" << "ACS ACR1252U"; QTest::newRow("ACS-ACR1252U-macosx-10.11-2") << UsbId(0x072F, 0x223B) << "ACS ACR1252 Dual Reader(2)" << "ACS ACR1252U"; QTest::newRow("ACS-ACR1252U-macosx-10.12-1") << UsbId(0x072F, 0x223B) << "ACS ACR1252 Dual Reader(1)" << "ACS ACR1252U"; QTest::newRow("ACS-ACR1252U-macosx-10.12-2") << UsbId(0x072F, 0x223B) << "ACS ACR1252 Dual Reader(2)" << "ACS ACR1252U"; QTest::newRow("ACS-ACR1252U-macosx-10.13-1") << UsbId(0x072F, 0x223B) << "ACS ACR1252 Dual Reader(1)" << "ACS ACR1252U"; QTest::newRow("ACS-ACR1252U-macosx-10.13-2") << UsbId(0x072F, 0x223B) << "ACS ACR1252 Dual Reader(2)" << "ACS ACR1252U"; + QTest::newRow("ACS-ACR1252U-macosx-10.14-1") << UsbId(0x072F, 0x223B) << "ACS ACR1252 Dual Reader(1)" << "ACS ACR1252U"; + QTest::newRow("ACS-ACR1252U-macosx-10.14-2") << UsbId(0x072F, 0x223B) << "ACS ACR1252 Dual Reader(2)" << "ACS ACR1252U"; QTest::newRow("OMNIKEY 5021-windows-7.32") << UsbId(0x076B, 0x5340) << "OMNIKEY CardMan 5x21-CL 0" << "OMNIKEY 5021-CL"; QTest::newRow("OMNIKEY 5021-windows-7.64") << UsbId(0x076B, 0x5340) << "OMNIKEY CardMan 5x21-CL 0" << "OMNIKEY 5021-CL"; QTest::newRow("OMNIKEY 5021-windows-8.1") << UsbId(0x076B, 0x5340) << "OMNIKEY CardMan 5x21-CL 0" << "OMNIKEY 5021-CL"; QTest::newRow("OMNIKEY 5021-windows-10") << UsbId(0x076B, 0x5340) << "OMNIKEY CardMan 5x21-CL 0" << "OMNIKEY 5021-CL"; - QTest::newRow("OMNIKEY 5021-macosx-10.10") << UsbId(0x076B, 0x5340) << "OMNIKEY CardMan (076B:5340) 5021 CL" << "OMNIKEY 5021-CL"; QTest::newRow("OMNIKEY 5021-macosx-10.11") << UsbId(0x076B, 0x5340) << "OMNIKEY CardMan (076B:5340) 5021 CL" << "OMNIKEY 5021-CL"; QTest::newRow("OMNIKEY 5021-macosx-10.12") << UsbId(0x076B, 0x5340) << "OMNIKEY CardMan (076B:5340) 5021 CL" << "OMNIKEY 5021-CL"; QTest::newRow("OMNIKEY 5021-macosx-10.13") << UsbId(0x076B, 0x5340) << "OMNIKEY CardMan (076B:5340) 5021 CL" << "OMNIKEY 5021-CL"; + QTest::newRow("OMNIKEY 5021-macosx-10.14") << UsbId(0x076B, 0x5340) << "OMNIKEY CardMan (076B:5340) 5021 CL" << "OMNIKEY 5021-CL"; QTest::newRow("OMNIKEY 5022-windows-7.32") << UsbId(0x076B, 0x5022) << "HID Global OMNIKEY 5022 Smart Card Reader 0" << "OMNIKEY 5022-CL"; QTest::newRow("OMNIKEY 5022-windows-7.64") << UsbId(0x076B, 0x5022) << "HID Global OMNIKEY 5022 Smart Card Reader 0" << "OMNIKEY 5022-CL"; QTest::newRow("OMNIKEY 5022-windows-8.1") << UsbId(0x076B, 0x5022) << "HID Global OMNIKEY 5022 Smart Card Reader 0" << "OMNIKEY 5022-CL"; QTest::newRow("OMNIKEY 5022-windows-10") << UsbId(0x076B, 0x5022) << "HID Global OMNIKEY 5022 Smart Card Reader 0" << "OMNIKEY 5022-CL"; - QTest::newRow("OMNIKEY 5022-macosx-10.10") << UsbId(0x076B, 0x5022) << "HID Global OMNIKEY 5022 Smart Card Reader" << "OMNIKEY 5022-CL"; QTest::newRow("OMNIKEY 5022-macosx-10.11") << UsbId(0x076B, 0x5022) << "HID Global OMNIKEY 5022 Smart Card Reader" << "OMNIKEY 5022-CL"; QTest::newRow("OMNIKEY 5022-macosx-10.12") << UsbId(0x076B, 0x5022) << "HID Global OMNIKEY 5022 Smart Card Reader" << "OMNIKEY 5022-CL"; QTest::newRow("OMNIKEY 5022-macosx-10.13") << UsbId(0x076B, 0x5022) << "HID Global OMNIKEY 5022 Smart Card Reader" << "OMNIKEY 5022-CL"; + QTest::newRow("OMNIKEY 5022-macosx-10.14") << UsbId(0x076B, 0x5022) << "HID Global OMNIKEY 5022 Smart Card Reader" << "OMNIKEY 5022-CL"; QTest::newRow("OMNIKEY 5321 v2-windows-7.32-1") << UsbId(0x076B, 0x5321) << "OMNIKEY CardMan 5x21 0" << "OMNIKEY CardMan 5x21 0"; QTest::newRow("OMNIKEY 5321 v2-windows-7.32-2") << UsbId(0x076B, 0x5321) << "OMNIKEY CardMan 5x21-CL 0" << "OMNIKEY 5321 v2"; @@ -272,14 +263,14 @@ class test_ReaderConfiguration QTest::newRow("OMNIKEY 5321 v2-windows-8.1-2") << UsbId(0x076B, 0x5321) << "OMNIKEY CardMan 5x21-CL 0" << "OMNIKEY 5321 v2"; QTest::newRow("OMNIKEY 5321 v2-windows-10.1-1") << UsbId(0x076B, 0x5321) << "OMNIKEY CardMan 5x21 0" << "OMNIKEY CardMan 5x21 0"; QTest::newRow("OMNIKEY 5321 v2-windows-10.1-2") << UsbId(0x076B, 0x5321) << "OMNIKEY CardMan 5x21-CL 0" << "OMNIKEY 5321 v2"; - QTest::newRow("OMNIKEY 5321 v2-macosx-10.10-1") << UsbId(0x076B, 0x5321) << "OMNIKEY CardMan (076B:5321) 5321(1)" << "OMNIKEY 5321 v2"; - QTest::newRow("OMNIKEY 5321 v2-macosx-10.10-2") << UsbId(0x076B, 0x5321) << "OMNIKEY CardMan (076B:5321) 5321(2)" << "OMNIKEY 5321 v2"; QTest::newRow("OMNIKEY 5321 v2-macosx-10.11-1") << UsbId(0x076B, 0x5321) << "OMNIKEY CardMan (076B:5321) 5321(1)" << "OMNIKEY 5321 v2"; QTest::newRow("OMNIKEY 5321 v2-macosx-10.11-2") << UsbId(0x076B, 0x5321) << "OMNIKEY CardMan (076B:5321) 5321(2)" << "OMNIKEY 5321 v2"; QTest::newRow("OMNIKEY 5321 v2-macosx-10.12-1") << UsbId(0x076B, 0x5321) << "OMNIKEY CardMan (076B:5321) 5321(1)" << "OMNIKEY 5321 v2"; QTest::newRow("OMNIKEY 5321 v2-macosx-10.12-2") << UsbId(0x076B, 0x5321) << "OMNIKEY CardMan (076B:5321) 5321(2)" << "OMNIKEY 5321 v2"; QTest::newRow("OMNIKEY 5321 v2-macosx-10.13-1") << UsbId(0x076B, 0x5321) << "OMNIKEY CardMan (076B:5321) 5321(1)" << "OMNIKEY 5321 v2"; QTest::newRow("OMNIKEY 5321 v2-macosx-10.13-2") << UsbId(0x076B, 0x5321) << "OMNIKEY CardMan (076B:5321) 5321(2)" << "OMNIKEY 5321 v2"; + QTest::newRow("OMNIKEY 5321 v2-macosx-10.14-1") << UsbId(0x076B, 0x5321) << "OMNIKEY CardMan (076B:5321) 5321(1)" << "OMNIKEY 5321 v2"; + QTest::newRow("OMNIKEY 5321 v2-macosx-10.14-2") << UsbId(0x076B, 0x5321) << "OMNIKEY CardMan (076B:5321) 5321(2)" << "OMNIKEY 5321 v2"; QTest::newRow("OMNIKEY 5421-windows-7.1-1") << UsbId(0x076B, 0x5421) << "OMNIKEY CardMan 5x21 0" << "OMNIKEY CardMan 5x21 0"; QTest::newRow("OMNIKEY 5421-windows-7.1-2") << UsbId(0x076B, 0x5421) << "OMNIKEY CardMan 5x21-CL 0" << "OMNIKEY 5421"; @@ -287,14 +278,27 @@ class test_ReaderConfiguration QTest::newRow("OMNIKEY 5421-windows-8.1-2") << UsbId(0x076B, 0x5421) << "OMNIKEY CardMan 5x21-CL 0" << "OMNIKEY 5421"; QTest::newRow("OMNIKEY 5421-windows-10.1-1") << UsbId(0x076B, 0x5421) << "OMNIKEY CardMan 5x21 0" << "OMNIKEY CardMan 5x21 0"; QTest::newRow("OMNIKEY 5421-windows-10.1-2") << UsbId(0x076B, 0x5421) << "OMNIKEY CardMan 5x21-CL 0" << "OMNIKEY 5421"; - QTest::newRow("OMNIKEY 5421-macosx-10.10-1") << UsbId(0x076B, 0x5421) << "OMNIKEY CardMan (076B:5421) 5421(1)" << "OMNIKEY 5421"; - QTest::newRow("OMNIKEY 5421-macosx-10.10-2") << UsbId(0x076B, 0x5421) << "OMNIKEY CardMan (076B:5421) 5421(2)" << "OMNIKEY 5421"; QTest::newRow("OMNIKEY 5421-macosx-10.11-1") << UsbId(0x076B, 0x5421) << "OMNIKEY CardMan (076B:5421) 5421(1)" << "OMNIKEY 5421"; QTest::newRow("OMNIKEY 5421-macosx-10.11-2") << UsbId(0x076B, 0x5421) << "OMNIKEY CardMan (076B:5421) 5421(2)" << "OMNIKEY 5421"; QTest::newRow("OMNIKEY 5421-macosx-10.12-1") << UsbId(0x076B, 0x5421) << "OMNIKEY CardMan (076B:5421) 5421(1)" << "OMNIKEY 5421"; QTest::newRow("OMNIKEY 5421-macosx-10.12-2") << UsbId(0x076B, 0x5421) << "OMNIKEY CardMan (076B:5421) 5421(2)" << "OMNIKEY 5421"; QTest::newRow("OMNIKEY 5421-macosx-10.13-1") << UsbId(0x076B, 0x5421) << "OMNIKEY CardMan (076B:5421) 5421(1)" << "OMNIKEY 5421"; QTest::newRow("OMNIKEY 5421-macosx-10.13-2") << UsbId(0x076B, 0x5421) << "OMNIKEY CardMan (076B:5421) 5421(2)" << "OMNIKEY 5421"; + QTest::newRow("OMNIKEY 5421-macosx-10.14-1") << UsbId(0x076B, 0x5421) << "OMNIKEY CardMan (076B:5421) 5421(1)" << "OMNIKEY 5421"; + QTest::newRow("OMNIKEY 5421-macosx-10.14-2") << UsbId(0x076B, 0x5421) << "OMNIKEY CardMan (076B:5421) 5421(2)" << "OMNIKEY 5421"; + + QTest::newRow("OMNIKEY 5422-windows-7.32-1") << UsbId(0x076B, 0x5422) << "HID Global OMNIKEY 5422 Smartcard Reader 0" << "OMNIKEY 5422"; + QTest::newRow("OMNIKEY 5422-windows-7.32-2") << UsbId(0x076B, 0x5422) << "HID Global OMNIKEY 5422CL Smartcard Reader 0" << "OMNIKEY 5422"; + QTest::newRow("OMNIKEY 5422-windows-7.64-1") << UsbId(0x076B, 0x5422) << "HID Global OMNIKEY 5422 Smartcard Reader 0" << "OMNIKEY 5422"; + QTest::newRow("OMNIKEY 5422-windows-7.64-2") << UsbId(0x076B, 0x5422) << "HID Global OMNIKEY 5422CL Smartcard Reader 0" << "OMNIKEY 5422"; + QTest::newRow("OMNIKEY 5422-windows-8.1-1") << UsbId(0x076B, 0x5422) << "HID Global OMNIKEY 5422 Smartcard Reader 0" << "OMNIKEY 5422"; + QTest::newRow("OMNIKEY 5422-windows-8.1-2") << UsbId(0x076B, 0x5422) << "HID Global OMNIKEY 5422CL Smartcard Reader 0" << "OMNIKEY 5422"; + QTest::newRow("OMNIKEY 5422-windows-10.1-1") << UsbId(0x076B, 0x5422) << "HID Global OMNIKEY 5422 Smartcard Reader 0" << "OMNIKEY 5422"; + QTest::newRow("OMNIKEY 5422-windows-10.1-2") << UsbId(0x076B, 0x5422) << "HID Global OMNIKEY 5422CL Smartcard Reader 0" << "OMNIKEY 5422"; + QTest::newRow("OMNIKEY 5421-macosx-10.13-1") << UsbId(0x076B, 0x5422) << "HID Global OMNIKEY Smartcard Reader (1)" << "OMNIKEY 5422"; + QTest::newRow("OMNIKEY 5421-macosx-10.13-2") << UsbId(0x076B, 0x5422) << "HID Global OMNIKEY Smartcard Reader (2)" << "OMNIKEY 5422"; + QTest::newRow("OMNIKEY 5421-macosx-10.14-1") << UsbId(0x076B, 0x5422) << "HID Global OMNIKEY Smartcard Reader (1)" << "OMNIKEY 5422"; + QTest::newRow("OMNIKEY 5421-macosx-10.14-2") << UsbId(0x076B, 0x5422) << "HID Global OMNIKEY Smartcard Reader (2)" << "OMNIKEY 5422"; QTest::newRow("FEIG OBID myAXXESS RFID-Reader-windows-7.32") << UsbId(0x0AB1, 0x0003) << "FEIG ELECTRONIC GmbH OBID myAXXESS basic Slot:CL 358334430" << "OBID RFID-Reader"; QTest::newRow("FEIG OBID myAXXESS RFID-Reader-windows-7.64") << UsbId(0x0AB1, 0x0003) << "FEIG ELECTRONIC GmbH OBID myAXXESS basic Slot:CL 358334430" << "OBID RFID-Reader"; @@ -309,14 +313,14 @@ class test_ReaderConfiguration QTest::newRow("Gemalto-Prox-DU-windows-8.1-2") << UsbId(0x08E6, 0x5503) << "Gemalto Prox-DU Contactless_10900383 0" << "Prox-DU HID"; QTest::newRow("Gemalto-Prox-DU-windows-10-1") << UsbId(0x08E6, 0x5503) << "Gemalto Prox-DU Contact_10900383 0" << "Gemalto Prox-DU Contact_10900383 0"; QTest::newRow("Gemalto-Prox-DU-windows-10-2") << UsbId(0x08E6, 0x5503) << "Gemalto Prox-DU Contactless_10900383 0l" << "Prox-DU HID"; - QTest::newRow("Gemalto-Prox-DU-windows-macosx-10.10-1") << UsbId(0x08E6, 0x5503) << "Gemalto Prox Dual USB PC LinkReader(1)" << "Prox-DU HID"; - QTest::newRow("Gemalto-Prox-DU-windows-macosx-10.10-2") << UsbId(0x08E6, 0x5503) << "Gemalto Prox Dual USB PC LinkReader(2)" << "Prox-DU HID"; QTest::newRow("Gemalto-Prox-DU-windows-macosx-10.11-1") << UsbId(0x08E6, 0x5503) << "Gemalto Prox Dual USB PC Link Reader(1)" << "Prox-DU HID"; QTest::newRow("Gemalto-Prox-DU-windows-macosx-10.11-2") << UsbId(0x08E6, 0x5503) << "Gemalto Prox Dual USB PC Link Reader(2)" << "Prox-DU HID"; QTest::newRow("Gemalto-Prox-DU-windows-macosx-10.12-1") << UsbId(0x08E6, 0x5503) << "Gemalto Prox Dual USB PC Link Reader(1)" << "Prox-DU HID"; QTest::newRow("Gemalto-Prox-DU-windows-macosx-10.12-2") << UsbId(0x08E6, 0x5503) << "Gemalto Prox Dual USB PC Link Reader(2)" << "Prox-DU HID"; QTest::newRow("Gemalto-Prox-DU-windows-macosx-10.13-1") << UsbId(0x08E6, 0x5503) << "Gemalto Prox Dual USB PC Link Reader(1)" << "Prox-DU HID"; QTest::newRow("Gemalto-Prox-DU-windows-macosx-10.13-2") << UsbId(0x08E6, 0x5503) << "Gemalto Prox Dual USB PC Link Reader(2)" << "Prox-DU HID"; + QTest::newRow("Gemalto-Prox-DU-windows-macosx-10.14-1") << UsbId(0x08E6, 0x5503) << "Gemalto Prox Dual USB PC Link Reader(1)" << "Prox-DU HID"; + QTest::newRow("Gemalto-Prox-DU-windows-macosx-10.14-2") << UsbId(0x08E6, 0x5503) << "Gemalto Prox Dual USB PC Link Reader(2)" << "Prox-DU HID"; QTest::newRow("Gemalto-Prox-SU-windows-7.32-1") << UsbId(0x08E6, 0x5504) << "Gemalto Prox-SU Contact_10800004 0" << "Gemalto Prox-SU Contact_10800004 0"; QTest::newRow("Gemalto-Prox-SU-windows-7.32-2") << UsbId(0x08E6, 0x5504) << "Gemalto Prox-SU Contactless_10800004 0" << "Gemalto Prox-SU Contactless"; @@ -326,29 +330,28 @@ class test_ReaderConfiguration QTest::newRow("Gemalto-Prox-SU-windows-8.1-2") << UsbId(0x08E6, 0x5504) << "Gemalto Prox-SU Contactless_10800004 0" << "Gemalto Prox-SU Contactless"; QTest::newRow("Gemalto-Prox-SU-windows-10-1") << UsbId(0x08E6, 0x5504) << "Gemalto Prox-SU Contact_10800004 0" << "Gemalto Prox-SU Contact_10800004 0"; QTest::newRow("Gemalto-Prox-SU-windows-10-2") << UsbId(0x08E6, 0x5504) << "Gemalto Prox-SU Contactless_10800004 0" << "Gemalto Prox-SU Contactless"; - QTest::newRow("Gemalto-Prox-SU-windows-macosx-10.10-1") << UsbId(0x08E6, 0x5504) << "Gemalto Prox SU USB PC LinkReader(1)" << "Gemalto Prox-SU Contactless"; - QTest::newRow("Gemalto-Prox-SU-windows-macosx-10.10-2") << UsbId(0x08E6, 0x5504) << "Gemalto Prox SU USB PC LinkReader(2)" << "Gemalto Prox-SU Contactless"; QTest::newRow("Gemalto-Prox-SU-windows-macosx-10.11-1") << UsbId(0x08E6, 0x5504) << "Gemalto Prox SU USB PC LinkReader(1)" << "Gemalto Prox-SU Contactless"; QTest::newRow("Gemalto-Prox-SU-windows-macosx-10.11-2") << UsbId(0x08E6, 0x5504) << "Gemalto Prox SU USB PC LinkReader(2)" << "Gemalto Prox-SU Contactless"; QTest::newRow("Gemalto-Prox-SU-windows-macosx-10.12-1") << UsbId(0x08E6, 0x5504) << "Gemalto Prox SU USB PC LinkReader(1)" << "Gemalto Prox-SU Contactless"; QTest::newRow("Gemalto-Prox-SU-windows-macosx-10.12-2") << UsbId(0x08E6, 0x5504) << "Gemalto Prox SU USB PC LinkReader(2)" << "Gemalto Prox-SU Contactless"; QTest::newRow("Gemalto-Prox-SU-windows-macosx-10.13-1") << UsbId(0x08E6, 0x5504) << "Gemalto Prox SU USB PC LinkReader(1)" << "Gemalto Prox-SU Contactless"; QTest::newRow("Gemalto-Prox-SU-windows-macosx-10.13-2") << UsbId(0x08E6, 0x5504) << "Gemalto Prox SU USB PC LinkReader(2)" << "Gemalto Prox-SU Contactless"; + QTest::newRow("Gemalto-Prox-SU-windows-macosx-10.14-1") << UsbId(0x08E6, 0x5504) << "Gemalto Prox SU USB PC LinkReader(1)" << "Gemalto Prox-SU Contactless"; + QTest::newRow("Gemalto-Prox-SU-windows-macosx-10.14-2") << UsbId(0x08E6, 0x5504) << "Gemalto Prox SU USB PC LinkReader(2)" << "Gemalto Prox-SU Contactless"; QTest::newRow("Identiv-SCL-3711-windows-7.32") << UsbId(0x04E6, 0x5591) << "SCM Microsystems SCL3711 reader & NFC device 0" << "Identiv SCL3711"; QTest::newRow("Identiv-SCL-3711-windows-7.64") << UsbId(0x04E6, 0x5591) << "SCM Microsystems SCL3711 reader & NFC device 0" << "Identiv SCL3711"; QTest::newRow("Identiv-SCL-3711-windows-8.1") << UsbId(0x04E6, 0x5591) << "SCM Microsystems SCL3711 reader & NFC device 0" << "Identiv SCL3711"; QTest::newRow("Identiv-SCL-3711-windows-10") << UsbId(0x04E6, 0x5591) << "SCM Microsystems SCL3711 reader & NFC device 0" << "Identiv SCL3711"; - QTest::newRow("Identiv-SCL-3711-macosx-10.10") << UsbId(0x04E6, 0x5591) << "SCL3711 reader and NFC device" << "Identiv SCL3711"; QTest::newRow("Identiv-Cloud-3700-F-windows-7.32") << UsbId(0x04E6, 0x5790) << "Identiv CLOUD 3700 F Contactless Reader 0" << "Identiv Cloud 3700 F"; QTest::newRow("Identiv-Cloud-3700-F-windows-7.64") << UsbId(0x04E6, 0x5790) << "Identiv CLOUD 3700 F Contactless Reader 0" << "Identiv Cloud 3700 F"; QTest::newRow("Identiv-Cloud-3700-F-windows-8.1") << UsbId(0x04E6, 0x5790) << "Identiv CLOUD 3700 F Contactless Reader 0" << "Identiv Cloud 3700 F"; QTest::newRow("Identiv-Cloud-3700-F-windows-10") << UsbId(0x04E6, 0x5790) << "Identiv CLOUD 3700 F Contactless Reader 0" << "Identiv Cloud 3700 F"; - QTest::newRow("Identiv-Cloud-3700-F-macosx-10.10") << UsbId(0x04E6, 0x5790) << "Identiv uTrust 3700 F CL Reader" << "Identiv Cloud 3700 F"; QTest::newRow("Identiv-Cloud-3700-F-macosx-10.11") << UsbId(0x04E6, 0x5790) << "Identiv uTrust 3700 F CL Reader" << "Identiv Cloud 3700 F"; QTest::newRow("Identiv-Cloud-3700-F-macosx-10.12") << UsbId(0x04E6, 0x5790) << "Identiv uTrust 3700 F CL Reader" << "Identiv Cloud 3700 F"; QTest::newRow("Identiv-Cloud-3700-F-macosx-10.13") << UsbId(0x04E6, 0x5790) << "Identiv uTrust 3700 F CL Reader" << "Identiv Cloud 3700 F"; + QTest::newRow("Identiv-Cloud-3700-F-macosx-10.14") << UsbId(0x04E6, 0x5790) << "Identiv uTrust 3700 F CL Reader" << "Identiv Cloud 3700 F"; QTest::newRow("Identiv-Cloud-4700-F-windows-7.32-1") << UsbId(0x04E6, 0x5720) << "Identiv CLOUD 4700 F Contact Reader 0" << "Identiv Cloud 4700 F"; QTest::newRow("Identiv-Cloud-4700-F-windows-7.32-2") << UsbId(0x04E6, 0x5720) << "Identiv CLOUD 4700 F Contactless Reader 1" << "Identiv Cloud 4700 F"; @@ -358,14 +361,14 @@ class test_ReaderConfiguration QTest::newRow("Identiv-Cloud-4700-F-windows-8.1-2") << UsbId(0x04E6, 0x5720) << "Identiv CLOUD 4700 F Contactless Reader 1" << "Identiv Cloud 4700 F"; QTest::newRow("Identiv-Cloud-4700-F-windows-10-1") << UsbId(0x04E6, 0x5720) << "Identiv CLOUD 4700 F Contact Reader 0" << "Identiv Cloud 4700 F"; QTest::newRow("Identiv-Cloud-4700-F-windows-10-2") << UsbId(0x04E6, 0x5720) << "Identiv CLOUD 4700 F Contactless Reader 1" << "Identiv Cloud 4700 F"; - QTest::newRow("Identiv-Cloud-4700-F-macosx-10.10-1") << UsbId(0x04E6, 0x5720) << "Identiv uTrust 4700 F Dual Interface Reader(2)" << "Identiv Cloud 4700 F"; - QTest::newRow("Identiv-Cloud-4700-F-macosx-10.10-2") << UsbId(0x04E6, 0x5720) << "Identiv uTrust 4700 F Dual Interface Reader(1)" << "Identiv Cloud 4700 F"; QTest::newRow("Identiv-Cloud-4700-F-macosx-10.11-1") << UsbId(0x04E6, 0x5720) << "Identiv uTrust 4700 F Dual Interface Reader(2)" << "Identiv Cloud 4700 F"; QTest::newRow("Identiv-Cloud-4700-F-macosx-10.11-2") << UsbId(0x04E6, 0x5720) << "Identiv uTrust 4700 F Dual Interface Reader(1)" << "Identiv Cloud 4700 F"; QTest::newRow("Identiv-Cloud-4700-F-macosx-10.12-1") << UsbId(0x04E6, 0x5720) << "Identiv uTrust 4700 F Dual Interface Reader(2)" << "Identiv Cloud 4700 F"; QTest::newRow("Identiv-Cloud-4700-F-macosx-10.12-2") << UsbId(0x04E6, 0x5720) << "Identiv uTrust 4700 F Dual Interface Reader(1)" << "Identiv Cloud 4700 F"; QTest::newRow("Identiv-Cloud-4700-F-macosx-10.13-1") << UsbId(0x04E6, 0x5720) << "Identiv uTrust 4700 F Dual Interface Reader(2)" << "Identiv Cloud 4700 F"; QTest::newRow("Identiv-Cloud-4700-F-macosx-10.13-2") << UsbId(0x04E6, 0x5720) << "Identiv uTrust 4700 F Dual Interface Reader(1)" << "Identiv Cloud 4700 F"; + QTest::newRow("Identiv-Cloud-4700-F-macosx-10.14-1") << UsbId(0x04E6, 0x5720) << "Identiv uTrust 4700 F Dual Interface Reader(2)" << "Identiv Cloud 4700 F"; + QTest::newRow("Identiv-Cloud-4700-F-macosx-10.14-2") << UsbId(0x04E6, 0x5720) << "Identiv uTrust 4700 F Dual Interface Reader(1)" << "Identiv Cloud 4700 F"; QTest::newRow("Identiv-Cloud-4700-F-windows-7.32-1") << UsbId(0x04E6, 0x5724) << "Identiv CLOUD 4701 F Contact Reader 0" << "Identiv Cloud 4701 F"; QTest::newRow("Identiv-Cloud-4701-F-windows-7.32-2") << UsbId(0x04E6, 0x5724) << "Identiv CLOUD 4701 F Contactless Reader 1" << "Identiv Cloud 4701 F"; @@ -375,22 +378,21 @@ class test_ReaderConfiguration QTest::newRow("Identiv-Cloud-4701-F-windows-8.1-2") << UsbId(0x04E6, 0x5724) << "Identiv CLOUD 4701 F Contactless Reader 1" << "Identiv Cloud 4701 F"; QTest::newRow("Identiv-Cloud-4701-F-windows-10-1") << UsbId(0x04E6, 0x5724) << "Identiv CLOUD 4701 F Contact Reader 0" << "Identiv Cloud 4701 F"; QTest::newRow("Identiv-Cloud-4701-F-windows-10-2") << UsbId(0x04E6, 0x5724) << "Identiv CLOUD 4701 F Contactless Reader 1" << "Identiv Cloud 4701 F"; - QTest::newRow("Identiv-Cloud-4701-F-macosx-10.10-1") << UsbId(0x04E6, 0x5724) << "Identiv uTrust 4701 F Dual Interface Reader(1)" << "Identiv Cloud 4701 F"; - QTest::newRow("Identiv-Cloud-4701-F-macosx-10.10-2") << UsbId(0x04E6, 0x5724) << "Identiv uTrust 4701 F Dual Interface Reader(2)" << "Identiv Cloud 4701 F"; QTest::newRow("Identiv-Cloud-4701-F-macosx-10.11-1") << UsbId(0x04E6, 0x5724) << "Identiv uTrust 4701 F Dual Interface Reader(1)" << "Identiv Cloud 4701 F"; QTest::newRow("Identiv-Cloud-4701-F-macosx-10.11-2") << UsbId(0x04E6, 0x5724) << "Identiv uTrust 4701 F Dual Interface Reader(2)" << "Identiv Cloud 4701 F"; QTest::newRow("Identiv-Cloud-4701-F-macosx-10.12-1") << UsbId(0x04E6, 0x5724) << "Identiv uTrust 4701 F Dual Interface Reader(1)" << "Identiv Cloud 4701 F"; QTest::newRow("Identiv-Cloud-4701-F-macosx-10.12-2") << UsbId(0x04E6, 0x5724) << "Identiv uTrust 4701 F Dual Interface Reader(2)" << "Identiv Cloud 4701 F"; QTest::newRow("Identiv-Cloud-4701-F-macosx-10.13-1") << UsbId(0x04E6, 0x5724) << "Identiv uTrust 4701 F Dual Interface Reader(1)" << "Identiv Cloud 4701 F"; QTest::newRow("Identiv-Cloud-4701-F-macosx-10.13-2") << UsbId(0x04E6, 0x5724) << "Identiv uTrust 4701 F Dual Interface Reader(2)" << "Identiv Cloud 4701 F"; + QTest::newRow("Identiv-Cloud-4701-F-macosx-10.14-1") << UsbId(0x04E6, 0x5724) << "Identiv uTrust 4701 F Dual Interface Reader(1)" << "Identiv Cloud 4701 F"; + QTest::newRow("Identiv-Cloud-4701-F-macosx-10.14-2") << UsbId(0x04E6, 0x5724) << "Identiv uTrust 4701 F Dual Interface Reader(2)" << "Identiv Cloud 4701 F"; QTest::newRow("Cherry-TC-1200-windows-7-64") << UsbId(0x046A, 0x0091) << "Cherry Smartcard Terminal TC 12xx-CL 0" << "Cherry TC-1200"; QTest::newRow("Cherry-TC-1200-windows-8.1-1") << UsbId(0x046A, 0x0091) << "Cherry Smartcard Terminal TC 12xx-CL 0" << "Cherry TC-1200"; QTest::newRow("Cherry-TC-1200-windows-10-1") << UsbId(0x046A, 0x0091) << "Cherry Smartcard Terminal TC 12xx 0" << "Cherry Smartcard Terminal TC 12xx 0"; QTest::newRow("Cherry-TC-1200-windows-10-2") << UsbId(0x046A, 0x0091) << "Cherry Smartcard Terminal TC 12xx-CL 0" << "Cherry TC-1200"; - QTest::newRow("Cherry-TC-1200-macosx-10.11") << UsbId(0x046A, 0x0091) << "Cherry SC Reader (046A:0091)" << "Cherry TC-1200"; - QTest::newRow("Cherry-TC-1200-macosx-10.12") << UsbId(0x046A, 0x0091) << "Cherry SC Reader (046A:0091)" << "Cherry TC-1200"; QTest::newRow("Cherry-TC-1200-macosx-10.13") << UsbId(0x046A, 0x0091) << "Cherry SC Reader (046A:0091)" << "Cherry TC-1200"; + QTest::newRow("Cherry-TC-1200-macosx-10.14") << UsbId(0x046A, 0x0091) << "Cherry SC Reader (046A:0091)" << "Cherry TC-1200"; QTest::newRow("Cherry-TC-1200-linux") << UsbId(0x046A, 0x0091) << "Cherry TC 1200" << "Cherry TC-1200"; QTest::newRow("Cherry-TC-1300-windows-7-64") << UsbId(0x046A, 0x0092) << "Cherry Smartcard Terminal TC 13xx 0" << "Cherry Smartcard Terminal TC 13xx 0"; @@ -405,6 +407,8 @@ class test_ReaderConfiguration QTest::newRow("Cherry-TC-1300-macosx-10.12-2") << UsbId(0x046A, 0x0092) << "Cherry SC Reader (046A:0092)(2)" << "Cherry TC-1300"; QTest::newRow("Cherry-TC-1300-macosx-10.13-1") << UsbId(0x046A, 0x0092) << "Cherry SC Reader (046A:0092)(1)" << "Cherry TC-1300"; QTest::newRow("Cherry-TC-1300-macosx-10.13-2") << UsbId(0x046A, 0x0092) << "Cherry SC Reader (046A:0092)(2)" << "Cherry TC-1300"; + QTest::newRow("Cherry-TC-1300-macosx-10.14-1") << UsbId(0x046A, 0x0092) << "Cherry SC Reader (046A:0092)(1)" << "Cherry TC-1300"; + QTest::newRow("Cherry-TC-1300-macosx-10.14-2") << UsbId(0x046A, 0x0092) << "Cherry SC Reader (046A:0092)(2)" << "Cherry TC-1300"; QTest::newRow("Cherry-TC-1300-linux") << UsbId(0x046A, 0x0092) << "Cherry TC 1300" << "Cherry TC-1300"; QTest::newRow("Cherry-ST-1275-windows-7.32-1") << UsbId(0x046A, 0x0072) << "Cherry SmartTerminal XX7X 0" << "Cherry SmartTerminal XX7X 0"; @@ -420,10 +424,10 @@ class test_ReaderConfiguration QTest::newRow("Signotec Omega Pad-windows-7.64") << UsbId(0x2133, 0x010B) << "NXP PR533 0" << "Signotec Omega Pad"; QTest::newRow("Signotec Omega Pad-windows-8.1") << UsbId(0x2133, 0x010B) << "NXP PR533 0" << "Signotec Omega Pad"; QTest::newRow("Signotec Omega Pad-windows-10") << UsbId(0x2133, 0x010B) << "NXP PR533 0" << "Signotec Omega Pad"; - QTest::newRow("Signotec Omega Pad-macosx-10.10") << UsbId(0x2133, 0x010B) << "NXP PR533" << "Signotec Omega Pad"; QTest::newRow("Signotec Omega Pad-macosx-10.11") << UsbId(0x2133, 0x010B) << "NXP PR533" << "Signotec Omega Pad"; QTest::newRow("Signotec Omega Pad-macosx-10.12") << UsbId(0x2133, 0x010B) << "NXP PR533" << "Signotec Omega Pad"; QTest::newRow("Signotec Omega Pad-macosx-10.13") << UsbId(0x2133, 0x010B) << "NXP PR533" << "Signotec Omega Pad"; + QTest::newRow("Signotec Omega Pad-macosx-10.14") << UsbId(0x2133, 0x010B) << "NXP PR533" << "Signotec Omega Pad"; } diff --git a/test/qt/configuration/test_ReaderConfigurationEntryParser.cpp b/test/qt/configuration/test_ReaderConfigurationEntryParser.cpp index f05623a..e7a9e08 100644 --- a/test/qt/configuration/test_ReaderConfigurationEntryParser.cpp +++ b/test/qt/configuration/test_ReaderConfigurationEntryParser.cpp @@ -40,9 +40,7 @@ class test_ReaderConfigurationEntryParser QVERIFY(parser.matchPlatform(data, QOperatingSystemVersion::OSXYosemite)); QVERIFY(parser.matchPlatform(data, QOperatingSystemVersion::OSXElCapitan)); QVERIFY(parser.matchPlatform(data, QOperatingSystemVersion::MacOSSierra)); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 1)) QVERIFY(parser.matchPlatform(data, QOperatingSystemVersion::MacOSHighSierra)); -#endif } diff --git a/test/qt/configuration/test_ReaderConfigurationParser.cpp b/test/qt/configuration/test_ReaderConfigurationParser.cpp index 82b5d00..b5b1c21 100644 --- a/test/qt/configuration/test_ReaderConfigurationParser.cpp +++ b/test/qt/configuration/test_ReaderConfigurationParser.cpp @@ -20,7 +20,7 @@ const QLatin1String KOMFORT_DRIVER_URL("https://www.reiner-sct.com/support/suppo #else const QLatin1String KOMFORT_DRIVER_URL("https://www.reiner-sct.com/support/support-anfrage/?os=Linux&productGroup=77304735&product=77304822&q=driver#choice5"); #endif -} +} // namespace class test_ReaderConfigurationParser diff --git a/test/qt/core/context/test_AuthContext.cpp b/test/qt/core/context/test_AuthContext.cpp index 72c2793..6a4b3db 100644 --- a/test/qt/core/context/test_AuthContext.cpp +++ b/test/qt/core/context/test_AuthContext.cpp @@ -5,7 +5,6 @@ */ #include "TestAuthContext.h" -#include "TestFileHelper.h" #include #include @@ -199,6 +198,165 @@ class test_AuthContext } + void test_ErrorReportedToServer() + { + AuthContext context(nullptr); + + QVERIFY(!context.isErrorReportedToServer()); + + context.setErrorReportedToServer(true); + QVERIFY(context.isErrorReportedToServer()); + + context.setErrorReportedToServer(false); + QVERIFY(!context.isErrorReportedToServer()); + } + + + void test_DidAuthenticateEacResponse1() + { + AuthContext context(nullptr); + const QSharedPointer eac(new DIDAuthenticateResponseEAC1()); + + QCOMPARE(context.getDidAuthenticateResponseEac1(), nullptr); + + context.setDidAuthenticateResponseEac1(eac); + QCOMPARE(context.getDidAuthenticateResponseEac1(), eac); + } + + + void test_DidAuthenticateResponseEacAdditionalInputType() + { + AuthContext context(nullptr); + const QSharedPointer eac(new DIDAuthenticateResponseEAC2()); + + QCOMPARE(context.getDidAuthenticateResponseEacAdditionalInputType(), nullptr); + + context.setDidAuthenticateResponseEacAdditionalInputType(eac); + QCOMPARE(context.getDidAuthenticateResponseEacAdditionalInputType(), eac); + } + + + void test_DidAuthenticateEacAdditional() + { + AuthContext context(nullptr); + const QSharedPointer eac(new DIDAuthenticateEACAdditional()); + + QCOMPARE(context.getDidAuthenticateEacAdditional(), nullptr); + + context.setDidAuthenticateEacAdditional(eac); + QCOMPARE(context.getDidAuthenticateEacAdditional(), eac); + } + + + void test_DidAuthenticateResponseEac2() + { + AuthContext context(nullptr); + const QSharedPointer eac(new DIDAuthenticateResponseEAC2()); + + QCOMPARE(context.getDidAuthenticateResponseEac2(), nullptr); + + context.setDidAuthenticateResponseEac2(eac); + QCOMPARE(context.getDidAuthenticateResponseEac2(), eac); + } + + + void test_DidList() + { + AuthContext context(nullptr); + const QByteArray data("data"); + const QSharedPointer list(new DIDList(data)); + + QCOMPARE(context.getDidList(), nullptr); + + context.setDidList(list); + QCOMPARE(context.getDidList(), list); + } + + + void test_DidListResponse() + { + AuthContext context(nullptr); + const QSharedPointer response(new DIDListResponse()); + + QCOMPARE(context.getDidListResponse(), nullptr); + + context.setDidListResponse(response); + QCOMPARE(context.getDidListResponse(), response); + } + + + void test_Disconnect() + { + AuthContext context(nullptr); + const QByteArray data("disconnect"); + const QSharedPointer discnnct(new Disconnect(data)); + + QCOMPARE(context.getDisconnect(), nullptr); + + context.setDisconnect(discnnct); + QCOMPARE(context.getDisconnect(), discnnct); + } + + + void test_DisconnectResponse() + { + AuthContext context(nullptr); + const QSharedPointer response(new DisconnectResponse()); + + QCOMPARE(context.getDisconnectResponse(), nullptr); + + context.setDisconnectResponse(response); + QCOMPARE(context.getDisconnectResponse(), response); + } + + + void test_TransmitResponses() + { + AuthContext context(nullptr); + QSharedPointer response(new TransmitResponse()); + + QVERIFY(context.getTransmitResponses().isEmpty()); + + context.addTransmitResponse(response); + QVERIFY(context.getTransmitResponses().contains(response)); + } + + + void test_Transmits() + { + AuthContext context(nullptr); + const QSharedPointer transmit(new Transmit()); + + QVERIFY(context.getTransmits().isEmpty()); + + context.addTransmit(transmit); + QVERIFY(context.getTransmits().contains(transmit)); + } + + + void test_StartPaos() + { + AuthContext context(nullptr); + const QByteArray data("paos"); + const QSharedPointer paos(new StartPaos(data)); + + QCOMPARE(context.getStartPaos(), nullptr); + + context.setStartPaos(paos); + QCOMPARE(context.getStartPaos(), paos); + } + + + void test_SslSession() + { + AuthContext context(nullptr); + const QByteArray session("session"); + + context.setSslSession(session); + QCOMPARE(context.getSslSession(), session); + } + + }; QTEST_GUILESS_MAIN(test_AuthContext) diff --git a/test/qt/core/context/test_ChangePinContext.cpp b/test/qt/core/context/test_ChangePinContext.cpp new file mode 100644 index 0000000..29c533b --- /dev/null +++ b/test/qt/core/context/test_ChangePinContext.cpp @@ -0,0 +1,85 @@ +/*! + * \brief Test for \ref ChangePinContext. + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "context/ChangePinContext.h" + +#include + +#include "TestFileHelper.h" + +using namespace governikus; + +class test_ChangePinContext + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void test_NewPin() + { + ChangePinContext context; + const QString pin1 = QStringLiteral("456789"); + const QString pin2 = QStringLiteral("111111"); + const QString pin3 = QStringLiteral("111111"); + QSignalSpy spy(&context, &ChangePinContext::fireNewPinChanged); + + context.setNewPin(pin1); + QCOMPARE(context.getNewPin(), pin1); + QCOMPARE(spy.count(), 1); + + context.setNewPin(pin2); + QCOMPARE(context.getNewPin(), pin2); + QCOMPARE(spy.count(), 2); + + context.setNewPin(pin3); + QCOMPARE(context.getNewPin(), pin2); + QCOMPARE(spy.count(), 2); + } + + + void test_SuccessMessage() + { + ChangePinContext context; + const QString message1 = QStringLiteral("message1"); + const QString message2 = QStringLiteral("message2"); + const QString message3 = QStringLiteral("message2"); + QSignalSpy spy(&context, &ChangePinContext::fireSuccessMessageChanged); + + context.setSuccessMessage(message1); + QCOMPARE(spy.count(), 1); + QCOMPARE(context.getSuccessMessage(), message1); + + context.setSuccessMessage(message2); + QCOMPARE(spy.count(), 2); + QCOMPARE(context.getSuccessMessage(), message2); + + context.setSuccessMessage(message3); + QCOMPARE(spy.count(), 2); + QCOMPARE(context.getSuccessMessage(), message2); + } + + + void test_RequestTransportPin() + { + { + ChangePinContext context; + QVERIFY(!context.requestTransportPin()); + } + { + ChangePinContext context(false); + QVERIFY(!context.requestTransportPin()); + } + { + ChangePinContext context(true); + QVERIFY(context.requestTransportPin()); + } + } + + +}; + +QTEST_GUILESS_MAIN(test_ChangePinContext) +#include "test_ChangePinContext.moc" diff --git a/test/qt/core/context/test_DiagnosisContext.cpp b/test/qt/core/context/test_DiagnosisContext.cpp new file mode 100644 index 0000000..202a597 --- /dev/null +++ b/test/qt/core/context/test_DiagnosisContext.cpp @@ -0,0 +1,99 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "context/DiagnosisContext.h" + +#include + + +using namespace governikus; + + +class test_DiagnosisContext + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void test_setTimestamp() + { + DiagnosisContext context; + const QDateTime emptyTime = QDateTime(); + const QDateTime currentTime = QDateTime::currentDateTime(); + QSignalSpy spy(&context, &DiagnosisContext::timestampChanged); + + context.setTimestamp(emptyTime); + QCOMPARE(context.getTimestamp(), emptyTime); + QCOMPARE(spy.count(), 1); + + context.setTimestamp(currentTime); + QCOMPARE(context.getTimestamp(), currentTime); + QCOMPARE(spy.count(), 2); + } + + + void test_setReaderInfos() + { + DiagnosisContext context; + const QString name1 = QStringLiteral("name"); + const QString name2 = QStringLiteral("name2"); + ReaderInfo info1(name1); + ReaderInfo info2(name2); + QVector infoVector({info1, info2}); + QSignalSpy spy(&context, &DiagnosisContext::readerInfosChanged); + + context.setReaderInfos(infoVector); + QCOMPARE(spy.count(), 1); + context.setReaderInfos(infoVector); + QCOMPARE(spy.count(), 2); + QCOMPARE(context.getReaderInfos().size(), 2); + QCOMPARE(context.getReaderInfos().value(0).getName(), name1); + QCOMPARE(context.getReaderInfos().value(1).getName(), name2); + } + + + void test_setPscInfo() + { + DiagnosisContext context; + const QString version = QStringLiteral("version"); + QVector components({DiagnosisContext::ComponentInfo()}); + QVector drivers({DiagnosisContext::ComponentInfo()}); + QSignalSpy spy(&context, &DiagnosisContext::pcscInfoChanged); + + context.setPcscInfo(version, components, drivers); + QCOMPARE(spy.count(), 1); + context.setPcscInfo(version, components, drivers); + QCOMPARE(spy.count(), 2); + + QCOMPARE(context.getPcscComponents().size(), 1); + QCOMPARE(context.getPcscDrivers().size(), 1); + QCOMPARE(context.getPcscVersion(), version); + } + + + void test_ComponentInfo() + { + const QString path = QStringLiteral("path"); + const QString description = QStringLiteral("description"); + const QString version = QStringLiteral("version"); + const QString manufacturer = QStringLiteral("manufacturer"); + + DiagnosisContext::ComponentInfo infoEmpty; + DiagnosisContext::ComponentInfo info(path, description, version, manufacturer); + + QCOMPARE(infoEmpty.getPath(), QString()); + QCOMPARE(info.getPath(), path); + + QCOMPARE(infoEmpty.getDescription(), QString()); + QCOMPARE(info.getDescription(), description); + + QCOMPARE(infoEmpty.getVersion(), QString()); + QCOMPARE(info.getVersion(), version); + } + + +}; + +QTEST_GUILESS_MAIN(test_DiagnosisContext) +#include "test_DiagnosisContext.moc" diff --git a/test/qt/core/context/test_RemoteServiceContext.cpp b/test/qt/core/context/test_RemoteServiceContext.cpp new file mode 100644 index 0000000..2ef11d1 --- /dev/null +++ b/test/qt/core/context/test_RemoteServiceContext.cpp @@ -0,0 +1,83 @@ +/*! + * \brief Unit tests for the class \ref RemoteServiceContext. + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "context/RemoteServiceContext.h" + +#include +#include +#include + + +using namespace governikus; + + +class test_RemoteServiceContext + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void test_NewPin() + { + RemoteServiceContext context; + const QString pin1 = QStringLiteral("567890"); + const QString pin2 = QStringLiteral("654321"); + + context.setNewPin(pin1); + QCOMPARE(context.getNewPin(), pin1); + + context.setNewPin(pin2); + QCOMPARE(context.getNewPin(), pin2); + } + + + void test_ModifyPinMessage() + { + RemoteServiceContext context; + const QSharedPointer message(new IfdModifyPin(QString("message"), QByteArray("message"))); + const QSharedPointer emptyMessage(new IfdModifyPin(QString(), QByteArray())); + + context.setModifyPinMessage(message); + QCOMPARE(context.getModifyPinMessage(), message); + + context.setModifyPinMessage(emptyMessage); + QCOMPARE(context.getModifyPinMessage(), emptyMessage); + } + + + void test_OnResetMessageHandler() + { + RemoteServiceContext context; + context.onResetMessageHandler(); + QCOMPARE(context.getCardConnection(), QSharedPointer()); + QCOMPARE(context.getCan(), QString()); + QCOMPARE(context.getPin(), QString()); + QCOMPARE(context.getPuk(), QString()); + QCOMPARE(context.getNewPin(), QString()); + QCOMPARE(context.getLastPaceResult(), CardReturnCode::OK); + QCOMPARE(context.getEstablishPaceChannelMessage(), QSharedPointer()); + QCOMPARE(context.getModifyPinMessage(), QSharedPointer()); + } + + + void test_EstablishPaceChannelMessage() + { + RemoteServiceContext context; + const QSharedPointer message(new IfdEstablishPaceChannel(QString("message"), QByteArray("message"))); + const QSharedPointer emptyMessage(new IfdEstablishPaceChannel(QString(), QByteArray())); + + context.setEstablishPaceChannelMessage(message); + QCOMPARE(context.getEstablishPaceChannelMessage(), message); + + context.setEstablishPaceChannelMessage(emptyMessage); + QCOMPARE(context.getEstablishPaceChannelMessage(), emptyMessage); + } + + +}; + +QTEST_GUILESS_MAIN(test_RemoteServiceContext) +#include "test_RemoteServiceContext.moc" diff --git a/test/qt/core/context/test_WorkflowContext.cpp b/test/qt/core/context/test_WorkflowContext.cpp new file mode 100644 index 0000000..80a77b8 --- /dev/null +++ b/test/qt/core/context/test_WorkflowContext.cpp @@ -0,0 +1,278 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "context/WorkflowContext.h" + +#include "MockCardConnectionWorker.h" +#include "MockReader.h" + +#include + + +using namespace governikus; + + +class test_WorkflowContext + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void test_WorkflowFinished() + { + WorkflowContext workflowContext; + + workflowContext.setWorkflowFinished(true); + QVERIFY(workflowContext.isWorkflowFinished()); + + workflowContext.setWorkflowFinished(false); + QVERIFY(!workflowContext.isWorkflowFinished()); + } + + + void test_CanAllowed() + { + WorkflowContext workflowContext; + QSignalSpy spy(&workflowContext, &WorkflowContext::fireCanAllowedModeChanged); + + workflowContext.setCanAllowedMode(true); + QVERIFY(workflowContext.isCanAllowedMode()); + QCOMPARE(spy.count(), 1); + + workflowContext.setCanAllowedMode(false); + QVERIFY(!workflowContext.isCanAllowedMode()); + QCOMPARE(spy.count(), 2); + } + + + void test_Can() + { + WorkflowContext workflowContext; + const QString can1 = QStringLiteral("123256"); + const QString can2 = QStringLiteral("222222"); + const QString can3 = QStringLiteral("222222"); + + QSignalSpy spy(&workflowContext, &WorkflowContext::fireCanChanged); + + workflowContext.setCan(can1); + QCOMPARE(workflowContext.getCan(), can1); + QCOMPARE(spy.count(), 1); + + workflowContext.setCan(can2); + QCOMPARE(workflowContext.getCan(), can2); + QCOMPARE(spy.count(), 2); + + workflowContext.setCan(can3); + QCOMPARE(workflowContext.getCan(), can2); + QCOMPARE(spy.count(), 2); + } + + + void test_Pin() + { + WorkflowContext workflowContext; + const QString pin1 = QStringLiteral("123256"); + const QString pin2 = QStringLiteral("222222"); + const QString pin3 = QStringLiteral("222222"); + QSignalSpy spy(&workflowContext, &WorkflowContext::firePinChanged); + + workflowContext.setPin(pin1); + QCOMPARE(workflowContext.getPin(), pin1); + QCOMPARE(spy.count(), 1); + + workflowContext.setPin(pin2); + QCOMPARE(workflowContext.getPin(), pin2); + QCOMPARE(spy.count(), 2); + + workflowContext.setPin(pin3); + QCOMPARE(workflowContext.getPin(), pin2); + QCOMPARE(spy.count(), 2); + } + + + void test_Puk() + { + WorkflowContext workflowContext; + const QString puk1 = QStringLiteral("123256789"); + const QString puk2 = QStringLiteral("222222222"); + const QString puk3 = QStringLiteral("222222222"); + QSignalSpy spy(&workflowContext, &WorkflowContext::firePukChanged); + + workflowContext.setPuk(puk1); + QCOMPARE(workflowContext.getPuk(), puk1); + QCOMPARE(spy.count(), 1); + + workflowContext.setPuk(puk2); + QCOMPARE(workflowContext.getPuk(), puk2); + QCOMPARE(spy.count(), 2); + + workflowContext.setPuk(puk3); + QCOMPARE(workflowContext.getPuk(), puk2); + QCOMPARE(spy.count(), 2); + } + + + void test_ErrorReportToUser() + { + WorkflowContext workflowContext; + + workflowContext.setErrorReportedToUser(true); + QVERIFY(workflowContext.isErrorReportedToUser()); + + workflowContext.setErrorReportedToUser(false); + QVERIFY(!workflowContext.isErrorReportedToUser()); + } + + + void test_CurrentState() + { + WorkflowContext workflowContext; + const QString state1 = QStringLiteral("state1"); + const QString state2 = QStringLiteral("state2"); + QSignalSpy spy(&workflowContext, &WorkflowContext::fireStateChanged); + + workflowContext.setCurrentState(state1); + QCOMPARE(workflowContext.getCurrentState(), state1); + QCOMPARE(spy.count(), 1); + QVERIFY(!workflowContext.isStateApproved()); + + workflowContext.setCurrentState(state2); + QCOMPARE(workflowContext.getCurrentState(), state2); + QCOMPARE(spy.count(), 2); + QVERIFY(!workflowContext.isStateApproved()); + + workflowContext.killWorkflow(); + QCOMPARE(workflowContext.getCurrentState(), state2); + QCOMPARE(spy.count(), 2); + QVERIFY(workflowContext.isStateApproved()); + } + + + void test_ReaderPlugInTypes() + { + WorkflowContext workflowContext; + QVector vector1({ReaderManagerPlugInType::PCSC}); + QVector vector2({ReaderManagerPlugInType::BLUETOOTH}); + QSignalSpy spy(&workflowContext, &WorkflowContext::fireReaderPlugInTypesChanged); + + workflowContext.setReaderPlugInTypes(vector1); + QCOMPARE(workflowContext.getReaderPlugInTypes(), vector1); + QCOMPARE(spy.count(), 1); + spy.clear(); + + workflowContext.setReaderPlugInTypes(vector2); + QCOMPARE(workflowContext.getReaderPlugInTypes(), vector2); + QCOMPARE(spy.count(), 1); + } + + + void test_LastPaceAndResult() + { + WorkflowContext workflowContext; + QSignalSpy spy(&workflowContext, &WorkflowContext::fireLastPaceResultChanged); + + workflowContext.setLastPaceResult(CardReturnCode::COMMAND_FAILED); + QCOMPARE(workflowContext.getLastPaceResult(), CardReturnCode::COMMAND_FAILED); + QCOMPARE(spy.count(), 1); + + workflowContext.setLastPaceResult(CardReturnCode::OK); + QCOMPARE(workflowContext.getLastPaceResult(), CardReturnCode::OK); + QCOMPARE(spy.count(), 2); + } + + + void test_CardConnection() + { + QThread cardThread; + cardThread.start(); + WorkflowContext workflowContext; + QSignalSpy spy(&workflowContext, &WorkflowContext::fireCardConnectionChanged); + + MockReader reader1(QStringLiteral("reader")); + reader1.moveToThread(&cardThread); + QSharedPointer worker1 = CardConnectionWorker::create(&reader1); + worker1->moveToThread(&cardThread); + QSharedPointer cardConnection1(new CardConnection(worker1)); + + MockReader reader2(QStringLiteral("reader2")); + reader2.moveToThread(&cardThread); + QSharedPointer worker2 = CardConnectionWorker::create(&reader2); + worker2->moveToThread(&cardThread); + QSharedPointer cardConnection2(new CardConnection(worker2)); + + workflowContext.setCardConnection(cardConnection1); + QCOMPARE(workflowContext.getCardConnection(), cardConnection1); + QCOMPARE(spy.count(), 1); + + workflowContext.setCardConnection(cardConnection2); + QCOMPARE(workflowContext.getCardConnection(), cardConnection2); + QCOMPARE(spy.count(), 2); + + cardThread.quit(); + cardThread.wait(); + } + + + void test_IsPinBlocked() + { + QThread workerThread; + workerThread.start(); + + const QSharedPointer worker(new MockCardConnectionWorker()); + worker->moveToThread(&workerThread); + const QSharedPointer connection1(new CardConnection(worker)); + const QSharedPointer connection2(new CardConnection(worker)); + const CardInfo cardInfo1(CardType::EID_CARD, QSharedPointer(), 3, false, false); + const CardInfo cardInfo2(CardType::EID_CARD, QSharedPointer(), 0, false, false); + const ReaderInfo readerInfo1(QString(), ReaderManagerPlugInType::UNKNOWN, cardInfo1); + const ReaderInfo readerInfo2(QString(), ReaderManagerPlugInType::UNKNOWN, cardInfo2); + + connection1->onReaderInfoChanged(readerInfo1); + connection2->onReaderInfoChanged(readerInfo2); + + WorkflowContext context; + QVERIFY(!context.isPinBlocked()); + + context.setCardConnection(connection1); + QVERIFY(!context.isPinBlocked()); + + context.setCardConnection(connection2); + QVERIFY(context.isPinBlocked()); + + workerThread.quit(); + workerThread.wait(); + } + + + void test_WorkflowKilled() + { + WorkflowContext context; + QSignalSpy spy(&context, &WorkflowContext::fireCancelWorkflow); + + QVERIFY(!context.isWorkflowKilled()); + + QTest::ignoreMessage(QtWarningMsg, "Killing the current workflow."); + context.killWorkflow(); + QVERIFY(context.isWorkflowKilled()); + QCOMPARE(context.getStatus().getStatusCode(), GlobalStatus::Code::Card_Cancellation_By_User); + QVERIFY(context.isStateApproved()); + QCOMPARE(spy.count(), 1); + } + + + void test_IsWorkflowCancelled() + { + WorkflowContext context; + + QVERIFY(!context.isWorkflowCancelled()); + + context.mWorkflowCancelled = true; + QVERIFY(context.isWorkflowCancelled()); + } + + +}; + +QTEST_GUILESS_MAIN(test_WorkflowContext) +#include "test_WorkflowContext.moc" diff --git a/test/qt/core/controller/test_ChangePinController.cpp b/test/qt/core/controller/test_ChangePinController.cpp index dc7a66b..bf13912 100644 --- a/test/qt/core/controller/test_ChangePinController.cpp +++ b/test/qt/core/controller/test_ChangePinController.cpp @@ -38,7 +38,7 @@ class test_ChangePinController void onCardInserted(const QString& pReaderName) { - if (ReaderManager::getInstance().getReaderInfo(pReaderName).hasEidCard()) + if (Env::getSingleton()->getReaderInfo(pReaderName).hasEidCard()) { Q_EMIT fireEidCardInserted(); } @@ -80,8 +80,9 @@ class test_ChangePinController QSignalSpy eidCardDetected(this, &test_ChangePinController::fireEidCardInserted); - connect(&ReaderManager::getInstance(), &ReaderManager::fireCardInserted, this, &test_ChangePinController::onCardInserted); - ReaderManager::getInstance().init(); + const auto readerManager = Env::getSingleton(); + connect(readerManager, &ReaderManager::fireCardInserted, this, &test_ChangePinController::onCardInserted); + readerManager->init(); QVERIFY(eidCardDetected.wait(20000)); } @@ -89,8 +90,9 @@ class test_ChangePinController void cleanupTestCase() { - ReaderManager::getInstance().shutdown(); - disconnect(&ReaderManager::getInstance(), &ReaderManager::fireCardInserted, this, &test_ChangePinController::onCardInserted); + const auto readerManager = Env::getSingleton(); + readerManager->shutdown(); + disconnect(readerManager, &ReaderManager::fireCardInserted, this, &test_ChangePinController::onCardInserted); QVERIFY(mPersoSim->shutdown()); mPersoSim.reset(); @@ -152,7 +154,6 @@ class test_ChangePinController Q_SIGNALS: void fireEidCardInserted(); - }; QTEST_GUILESS_MAIN(test_ChangePinController) diff --git a/test/qt/core/paos/invoke/test_DidAuthenticateResponseEAC1.cpp b/test/qt/core/paos/invoke/test_DidAuthenticateResponseEAC1.cpp index 8b9a2d9..6321ffe 100644 --- a/test/qt/core/paos/invoke/test_DidAuthenticateResponseEAC1.cpp +++ b/test/qt/core/paos/invoke/test_DidAuthenticateResponseEAC1.cpp @@ -7,7 +7,7 @@ #include "paos/invoke/DidAuthenticateResponseEac1.h" #include "asn1/CVCertificate.h" -#include "EstablishPACEChannel.h" +#include "EstablishPaceChannelOutput.h" #include "TestFileHelper.h" #include @@ -29,7 +29,6 @@ class test_DidAuthenticateResponseEAC1 void type() { DIDAuthenticateResponseEAC1 elem; - elem.setMessageId("dummy"); QCOMPARE(elem.mType, PaosType::DID_AUTHENTICATE_RESPONSE_EAC1); } @@ -37,7 +36,6 @@ class test_DidAuthenticateResponseEAC1 void getCertificateHolderAuthorizationTemplate() { DIDAuthenticateResponseEAC1 msg; - msg.setMessageId("dummy"); msg.setCertificateHolderAuthorizationTemplate("dummy cert holder"); QCOMPARE(msg.getCertificateHolderAuthorizationTemplate(), QByteArray("dummy cert holder")); QVERIFY(msg.marshall().contains("dummy cert holder")); @@ -47,7 +45,6 @@ class test_DidAuthenticateResponseEAC1 void getEFCardAccess() { DIDAuthenticateResponseEAC1 msg; - msg.setMessageId("dummy"); msg.setEFCardAccess("dummy card access"); QCOMPARE(msg.getEFCardAccess(), QByteArray("dummy card access")); QVERIFY(msg.marshall().contains("dummy card access")); @@ -57,7 +54,6 @@ class test_DidAuthenticateResponseEAC1 void getIDPICC() { DIDAuthenticateResponseEAC1 msg; - msg.setMessageId("dummy"); msg.setIDPICC("bla bla bla"); QCOMPARE(msg.getIDPICC(), QByteArray("bla bla bla")); QVERIFY(msg.marshall().contains("bla bla bla")); @@ -67,7 +63,6 @@ class test_DidAuthenticateResponseEAC1 void getChallenge() { DIDAuthenticateResponseEAC1 msg; - msg.setMessageId("dummy"); msg.setChallenge("1234567890"); QVERIFY(msg.marshall().contains("1234567890")); } @@ -78,14 +73,31 @@ class test_DidAuthenticateResponseEAC1 const auto& cvca_DETESTeID00002 = CVCertificate::fromHex(readFile("cvca-DETESTeID00002.hex")); const auto& cvca_DETESTeID00001 = CVCertificate::fromHex(readFile("cvca-DETESTeID00001.hex")); - EstablishPACEChannelOutput channel; + EstablishPaceChannelOutput channel; channel.setCarCurr(cvca_DETESTeID00002->getBody().getCertificateHolderReference()); channel.setCarPrev(cvca_DETESTeID00001->getBody().getCertificateHolderReference()); DIDAuthenticateResponseEAC1 msg; - msg.setMessageId("dummy"); msg.setCertificationAuthorityReference(channel); - QVERIFY(msg.marshall().contains("DETESTeID00002\n DETESTeID00001")); + QVERIFY(msg.marshall().contains("DETESTeID00002\n DETESTeID00001")); + } + + + void checkTemplate() + { + DIDAuthenticateResponseEAC1 msg; + msg.setRelatedMessageId("urn:uuid:A9CF4F0B8BFE483B8A5C7E6738C178FE"); + msg.setCertificateHolderAuthorizationTemplate("a"); + EstablishPaceChannelOutput output; + output.setCarCurr("b"); + msg.setCertificationAuthorityReference(output); + msg.setEFCardAccess("c"); + msg.setIDPICC("d"); + msg.setChallenge("e"); + + auto data = QString::fromLatin1(msg.marshall()); + data.replace(QRegularExpression(".*"), "STRIP ME"); + QCOMPARE(data, QString::fromLatin1(TestFileHelper::readFile(":/paos/DIDAuthenticateResponse2.xml"))); } diff --git a/test/qt/core/paos/invoke/test_DidAuthenticateResponseEAC2.cpp b/test/qt/core/paos/invoke/test_DidAuthenticateResponseEAC2.cpp index 31f7f53..ac1ada1 100644 --- a/test/qt/core/paos/invoke/test_DidAuthenticateResponseEAC2.cpp +++ b/test/qt/core/paos/invoke/test_DidAuthenticateResponseEAC2.cpp @@ -6,6 +6,8 @@ #include "paos/invoke/DidAuthenticateResponseEac2.h" +#include "TestFileHelper.h" + #include #include @@ -21,7 +23,6 @@ class test_DidAuthenticateResponseEAC2 void type() { DIDAuthenticateResponseEAC2 elem; - elem.setMessageId("dummy"); QCOMPARE(elem.mType, PaosType::DID_AUTHENTICATE_RESPONSE_EAC2); } @@ -29,7 +30,6 @@ class test_DidAuthenticateResponseEAC2 void emptyAuthenticationProtocolData() { DIDAuthenticateResponseEAC2 msg; - msg.setMessageId("dummy"); QVERIFY(!msg.marshall().contains("EFCardSecurity")); QVERIFY(!msg.marshall().contains("AuthenticationToken")); @@ -41,7 +41,6 @@ class test_DidAuthenticateResponseEAC2 void efCardSecurity() { DIDAuthenticateResponseEAC2 msg; - msg.setMessageId("dummy"); msg.setEfCardSecurity(QByteArray::fromHex(QByteArray("1234567890"))); QVERIFY(msg.marshall().contains("EFCardSecurity")); @@ -51,7 +50,6 @@ class test_DidAuthenticateResponseEAC2 void authenticationToken() { DIDAuthenticateResponseEAC2 msg; - msg.setMessageId("dummy"); msg.setAuthenticationToken(QByteArray::fromHex(QByteArray("1234567890"))); QVERIFY(msg.marshall().contains("AuthenticationToken")); @@ -61,7 +59,6 @@ class test_DidAuthenticateResponseEAC2 void nonce() { DIDAuthenticateResponseEAC2 msg; - msg.setMessageId("dummy"); msg.setNonce(QByteArray::fromHex(QByteArray("1234567890"))); QVERIFY(msg.marshall().contains("Nonce")); @@ -71,13 +68,24 @@ class test_DidAuthenticateResponseEAC2 void challenge() { DIDAuthenticateResponseEAC2 msg; - msg.setMessageId("dummy"); msg.setChallenge("1234567890"); QVERIFY(msg.marshall().contains("Challenge")); } + void checkTemplate() + { + DIDAuthenticateResponseEAC2 msg; + msg.setEfCardSecurity("a"); + msg.setAuthenticationToken("b"); + msg.setNonce("c"); + auto data = QString::fromLatin1(msg.marshall()); + data.replace(QRegularExpression(".*"), "STRIP ME"); + QCOMPARE(data, QString::fromLatin1(TestFileHelper::readFile(":/paos/DIDAuthenticateResponse.xml"))); + } + + }; QTEST_GUILESS_MAIN(test_DidAuthenticateResponseEAC2) diff --git a/test/qt/core/paos/invoke/test_DisconnectResponse.cpp b/test/qt/core/paos/invoke/test_DisconnectResponse.cpp index edb6816..478aa16 100644 --- a/test/qt/core/paos/invoke/test_DisconnectResponse.cpp +++ b/test/qt/core/paos/invoke/test_DisconnectResponse.cpp @@ -17,17 +17,10 @@ class test_DisconnectResponse { Q_OBJECT - QString getValue(const QDomElement& pElement, const QString& pName) - { - return pElement.elementsByTagName(pName).at(0).firstChild().nodeValue(); - } - - private Q_SLOTS: void type() { DisconnectResponse elem; - elem.setMessageId("dummy"); QCOMPARE(elem.mType, PaosType::DISCONNECT_RESPONSE); } @@ -35,7 +28,6 @@ class test_DisconnectResponse void marshall() { DisconnectResponse response; - response.setMessageId("dummy"); QByteArray elem = response.marshall(); QVERIFY(elem.contains("http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok")); - DisconnectResponse responseWithSlot; - responseWithSlot.setMessageId("dummy"); - responseWithSlot.setResult(Result(CardReturnCodeUtil::toGlobalStatus(CardReturnCode::CARD_NOT_FOUND))); + responseWithSlot.setResult(ECardApiResult(CardReturnCodeUtil::toGlobalStatus(CardReturnCode::CARD_NOT_FOUND))); responseWithSlot.setSlotHandle("huhu"); elem = responseWithSlot.marshall(); @@ -60,18 +50,6 @@ class test_DisconnectResponse } - void elements() - { - DisconnectResponse elem; - elem.setMessageId("dummy"); - QCOMPARE(elem.createDisconnectResponse().nodeName(), QString("DisconnectResponse")); - QVERIFY(elem.createDisconnectResponse().elementsByTagName("SlotHandle").isEmpty()); - - elem.setSlotHandle("huhu"); - QVERIFY(!elem.createDisconnectResponse().elementsByTagName("SlotHandle").isEmpty()); - } - - }; QTEST_GUILESS_MAIN(test_DisconnectResponse) diff --git a/test/qt/core/paos/invoke/test_PaosCreator.cpp b/test/qt/core/paos/invoke/test_PaosCreator.cpp index a432c23..4141a02 100644 --- a/test/qt/core/paos/invoke/test_PaosCreator.cpp +++ b/test/qt/core/paos/invoke/test_PaosCreator.cpp @@ -20,29 +20,23 @@ struct test_PaosCreatorDummy { QString mText; bool mNamespace = false; + const QString mTag = QStringLiteral("content"); - virtual QDomElement getDocumentStructure() override + virtual void createBodyElement() override { if (mNamespace) { - return createTextElement(Namespace::SOAP, "content", mText); + mWriter.writeTextElement(getNamespaceType(Namespace::SOAP, mTag), mText); } else { - return createTextElement("content", mText); + mWriter.writeTextElement(mTag, mText); } } - QByteArray getData(QDomElement& pElement) - { - mDoc.appendChild(pElement); - return mDoc.toByteArray(); - } - - }; -} +} // namespace class test_PaosCreator @@ -53,13 +47,13 @@ class test_PaosCreator private Q_SLOTS: void initTestCase() { - LogHandler::getInstance().init(); + Env::getSingleton()->init(); } void cleanup() { - LogHandler::getInstance().resetBacklog(); + Env::getSingleton()->resetBacklog(); } @@ -82,19 +76,25 @@ class test_PaosCreator } - void createHeaderElement() + void checkRelatesTo_data() { - test_PaosCreatorDummy creator; - auto elem = creator.createHeaderElement(QString(), "something"); - QByteArray data = creator.getData(elem); - QVERIFY(!data.contains("RelatesTo>")); - QVERIFY(data.contains("something")); + QTest::addColumn("relatesTo"); - test_PaosCreatorDummy creator2; - elem = creator2.createHeaderElement("first one", "second one"); - data = creator.getData(elem); - QVERIFY(data.contains("first one")); - QVERIFY(data.contains("second one")); + QTest::newRow("empty") << QString(); + QTest::newRow("related") << QStringLiteral("first one"); + } + + + void checkRelatesTo() + { + QFETCH(QString, relatesTo); + + test_PaosCreatorDummy creator; + creator.setRelatedMessageId(relatesTo); + QByteArray data = creator.marshall(); + + QCOMPARE(data.contains("RelatesTo>"), !relatesTo.isNull()); + QVERIFY(data.contains("urn:uuid:")); } diff --git a/test/qt/core/paos/invoke/test_StartPaos.cpp b/test/qt/core/paos/invoke/test_StartPaos.cpp index 765e2c7..8954899 100644 --- a/test/qt/core/paos/invoke/test_StartPaos.cpp +++ b/test/qt/core/paos/invoke/test_StartPaos.cpp @@ -6,6 +6,8 @@ #include "paos/invoke/StartPaos.h" +#include "TestFileHelper.h" + #include #include @@ -16,12 +18,6 @@ class test_StartPaos { Q_OBJECT - QString getValue(const QDomElement& pElement, const QString& pName) - { - return pElement.elementsByTagName(pName).at(0).firstChild().nodeValue(); - } - - private Q_SLOTS: void initTestCase() { @@ -32,7 +28,6 @@ class test_StartPaos void type() { StartPaos elem("session"); - elem.setMessageId("dummy"); QCOMPARE(elem.mType, PaosType::STARTPAOS); } @@ -40,7 +35,6 @@ class test_StartPaos void marshall() { StartPaos startPaos("session"); - startPaos.setMessageId("dummy"); QByteArray elem = startPaos.marshall(); QVERIFY(elem.contains("")); @@ -64,35 +58,13 @@ class test_StartPaos } - void elements() + void checkTemplate() { - StartPaos ctor("session123"); - ctor.setMessageId("dummy"); - - QCOMPARE(ctor.createSessionIdentifierElement().nodeName(), QString("SessionIdentifier")); - QCOMPARE(ctor.createSessionIdentifierElement().firstChild().nodeValue(), QString("session123")); - - auto elem = ctor.createConnectionHandleElement(); - QCOMPARE(getValue(elem, "CardApplication"), QString("e80704007f00070302")); - QCOMPARE(getValue(elem, "SlotHandle"), QString("00")); - } - - - void userAgentElement() - { - StartPaos elem("session123"); - elem.setMessageId("dummy"); - QCOMPARE(getValue(elem.createUserAgentElement(), "Name"), QString("Test_core_paos_invoke_StartPaos")); - } - - - void supportedAPIVersionsElement() - { - StartPaos elem("session123"); - elem.setMessageId("dummy"); - QCOMPARE(getValue(elem.createSupportedAPIVersionsElement(), "Major"), QString("1")); - QCOMPARE(getValue(elem.createSupportedAPIVersionsElement(), "Minor"), QString("1")); - QCOMPARE(getValue(elem.createSupportedAPIVersionsElement(), "Subminor"), QString("5")); + StartPaos startPaos("abcd"); + startPaos.setRelatedMessageId("urn:uuid:dummy"); + auto data = QString::fromLatin1(startPaos.marshall()); + data.replace(QRegularExpression(".*"), "STRIP ME"); + QCOMPARE(data, QString::fromLatin1(TestFileHelper::readFile(":/paos/StartPAOS.xml"))); } diff --git a/test/qt/core/paos/invoke/test_TransmitResponse.cpp b/test/qt/core/paos/invoke/test_TransmitResponse.cpp index a8581b6..0c0f07f 100644 --- a/test/qt/core/paos/invoke/test_TransmitResponse.cpp +++ b/test/qt/core/paos/invoke/test_TransmitResponse.cpp @@ -6,6 +6,8 @@ #include "paos/invoke/TransmitResponse.h" +#include "TestFileHelper.h" + #include #include @@ -20,7 +22,6 @@ class test_TransmitResponse void type() { TransmitResponse elem; - elem.setMessageId("dummy"); QCOMPARE(elem.mType, PaosType::TRANSMIT_RESPONSE); } @@ -28,7 +29,6 @@ class test_TransmitResponse void marshall() { TransmitResponse response; - response.setMessageId("dummy"); QByteArray elem = response.marshall(); QVERIFY(elem.contains("bla")); } - void elements() + void checkTemplate() { - TransmitResponse elem; - elem.setMessageId("dummy"); - - QCOMPARE(elem.createTransmitResponse().nodeName(), QString("TransmitResponse")); - QVERIFY(elem.createTransmitResponse().elementsByTagName("OutputAPDU").isEmpty()); - - elem.setOutputApdus(QByteArrayList() << "bla"); - QVERIFY(!elem.createTransmitResponse().elementsByTagName("OutputAPDU").isEmpty()); + TransmitResponse response; + response.setResult(ECardApiResult(GlobalStatus::Code::Workflow_Wrong_Parameter_Invocation)); + response.setOutputApdus(QByteArrayList{"a", "b", "c"}); + auto data = QString::fromLatin1(response.marshall()); + data.replace(QRegularExpression(".*"), "STRIP ME"); + QCOMPARE(data, QString::fromLatin1(TestFileHelper::readFile(":/paos/TransmitResponse.xml"))); } diff --git a/test/qt/core/paos/retrieve/test_StartPAOSResponse.cpp b/test/qt/core/paos/retrieve/test_StartPAOSResponse.cpp index 806f07f..593669b 100644 --- a/test/qt/core/paos/retrieve/test_StartPAOSResponse.cpp +++ b/test/qt/core/paos/retrieve/test_StartPAOSResponse.cpp @@ -30,8 +30,8 @@ class test_StartPAOSResponse StartPaosResponse message(content); - QCOMPARE(message.getResult().getMajor(), Result::Major::Ok); - QCOMPARE(message.getResult().getMinor(), GlobalStatus::Code::No_Error); + QCOMPARE(message.getResult().getMajor(), ECardApiResult::Major::Ok); + QCOMPARE(message.getResult().getMinor(), ECardApiResult::Minor::null); QVERIFY(message.getResult().getMessage().isNull()); } @@ -42,8 +42,8 @@ class test_StartPAOSResponse StartPaosResponse message(content); - QCOMPARE(message.getResult().getMajor(), Result::Major::Error); - QCOMPARE(message.getResult().getMinor(), GlobalStatus::Code::Paos_Error_DP_Timeout_Error); + QCOMPARE(message.getResult().getMajor(), ECardApiResult::Major::Error); + QCOMPARE(message.getResult().getMinor(), ECardApiResult::Minor::DP_Timeout_Error); QVERIFY(message.getResult().getMessage().isNull()); } @@ -54,8 +54,8 @@ class test_StartPAOSResponse StartPaosResponse message(content); - QCOMPARE(message.getResult().getMajor(), Result::Major::Error); - QCOMPARE(message.getResult().getMinor(), GlobalStatus::Code::Paos_Error_DP_Timeout_Error); + QCOMPARE(message.getResult().getMajor(), ECardApiResult::Major::Error); + QCOMPARE(message.getResult().getMinor(), ECardApiResult::Minor::DP_Timeout_Error); QCOMPARE(message.getResult().getMessage(), QString("Detail message")); } diff --git a/test/qt/core/paos/retrieve/test_transmit.cpp b/test/qt/core/paos/retrieve/test_transmit.cpp index ca68969..323f82f 100644 --- a/test/qt/core/paos/retrieve/test_transmit.cpp +++ b/test/qt/core/paos/retrieve/test_transmit.cpp @@ -49,14 +49,13 @@ class test_transmit QCOMPARE(inputApdusInfos[6].getInputApdu().getBuffer(), QByteArray::fromHex("0C86000000014287820131014B102237A6D2AC0A7873562D14A36A98CEF053674C3CAB4F09FAFB09ED12941F0DDC27655679D4BD86A12FFD6A3F490B73F2DF03EF19C106D4519929A3116B7BD3AF9FE960BD88F301275EAD3FAA9E832BF93991728E378A2848D60596B1C643DA3E5ADBC119EB3EB444A3789367815B600D218C407A4016F8B3A7923EE8DC3CBE0BD8AA91763859E819B325479F605AA50FC8FF6066055678CE6C1A3FD1DB536E55C3A3D131367AD84B78213667F899D059D313CA7F1EC0785F20F4FEF14D1E3D077B620223E75F101B66262642B1D6416C44AA4ED2EFAC88D7E38EA4EE9EFAA3DAD71E70E96C696A137960532807B52C74070EDA3F573939B39725B86AD0A255E62D26A33C54154ADDF871ED06FD5038B38E3E5E42FF680807734385B900833F54350A447DF71F012B0BE59AB4C6F09701248E0826C663CDA8343CBD0000")); - const QByteArrayList emptyList; - QCOMPARE(inputApdusInfos[0].getAcceptableStatusCodes(), emptyList); - QCOMPARE(inputApdusInfos[1].getAcceptableStatusCodes(), emptyList); - QCOMPARE(inputApdusInfos[2].getAcceptableStatusCodes(), QByteArrayList(emptyList) << QByteArray("9000")); - QCOMPARE(inputApdusInfos[3].getAcceptableStatusCodes(), QByteArrayList(emptyList) << QByteArray("9001")); - QCOMPARE(inputApdusInfos[4].getAcceptableStatusCodes(), emptyList); - QCOMPARE(inputApdusInfos[5].getAcceptableStatusCodes(), QByteArrayList(emptyList) << QByteArray("9002")); - QCOMPARE(inputApdusInfos[6].getAcceptableStatusCodes(), QByteArrayList(emptyList) << QByteArray("9003")); + QCOMPARE(inputApdusInfos[0].getAcceptableStatusCodes(), QByteArrayList()); + QCOMPARE(inputApdusInfos[1].getAcceptableStatusCodes(), QByteArrayList()); + QCOMPARE(inputApdusInfos[2].getAcceptableStatusCodes(), QByteArrayList {"9000"}); + QCOMPARE(inputApdusInfos[3].getAcceptableStatusCodes(), QByteArrayList {"9001"}); + QCOMPARE(inputApdusInfos[4].getAcceptableStatusCodes(), QByteArrayList()); + QCOMPARE(inputApdusInfos[5].getAcceptableStatusCodes(), QByteArrayList {"9002"}); + QCOMPARE(inputApdusInfos[6].getAcceptableStatusCodes(), QByteArrayList {"9003"}); } @@ -87,14 +86,13 @@ class test_transmit QCOMPARE(inputApdusInfos[6].getInputApdu().getBuffer(), QByteArray::fromHex("0C86000000014287820131014B102237A6D2AC0A7873562D14A36A98CEF053674C3CAB4F09FAFB09ED12941F0DDC27655679D4BD86A12FFD6A3F490B73F2DF03EF19C106D4519929A3116B7BD3AF9FE960BD88F301275EAD3FAA9E832BF93991728E378A2848D60596B1C643DA3E5ADBC119EB3EB444A3789367815B600D218C407A4016F8B3A7923EE8DC3CBE0BD8AA91763859E819B325479F605AA50FC8FF6066055678CE6C1A3FD1DB536E55C3A3D131367AD84B78213667F899D059D313CA7F1EC0785F20F4FEF14D1E3D077B620223E75F101B66262642B1D6416C44AA4ED2EFAC88D7E38EA4EE9EFAA3DAD71E70E96C696A137960532807B52C74070EDA3F573939B39725B86AD0A255E62D26A33C54154ADDF871ED06FD5038B38E3E5E42FF680807734385B900833F54350A447DF71F012B0BE59AB4C6F09701248E0826C663CDA8343CBD0000")); - const QByteArrayList emptyList; - QCOMPARE(inputApdusInfos[0].getAcceptableStatusCodes(), emptyList); - QCOMPARE(inputApdusInfos[1].getAcceptableStatusCodes(), emptyList); - QCOMPARE(inputApdusInfos[2].getAcceptableStatusCodes(), QByteArrayList(emptyList) << QByteArray("9000")); - QCOMPARE(inputApdusInfos[3].getAcceptableStatusCodes(), QByteArrayList(emptyList) << QByteArray("9001")); - QCOMPARE(inputApdusInfos[4].getAcceptableStatusCodes(), emptyList); - QCOMPARE(inputApdusInfos[5].getAcceptableStatusCodes(), QByteArrayList(emptyList) << QByteArray("9002")); - QCOMPARE(inputApdusInfos[6].getAcceptableStatusCodes(), QByteArrayList(emptyList) << QByteArray("9003")); + QCOMPARE(inputApdusInfos[0].getAcceptableStatusCodes(), QByteArrayList()); + QCOMPARE(inputApdusInfos[1].getAcceptableStatusCodes(), QByteArrayList()); + QCOMPARE(inputApdusInfos[2].getAcceptableStatusCodes(), QByteArrayList {"9000"}); + QCOMPARE(inputApdusInfos[3].getAcceptableStatusCodes(), QByteArrayList {"9001"}); + QCOMPARE(inputApdusInfos[4].getAcceptableStatusCodes(), QByteArrayList()); + QCOMPARE(inputApdusInfos[5].getAcceptableStatusCodes(), QByteArrayList {"9002"}); + QCOMPARE(inputApdusInfos[6].getAcceptableStatusCodes(), QByteArrayList {"9003"}); } diff --git a/test/qt/core/paos/test_MessageIdHandler.cpp b/test/qt/core/paos/test_MessageIdHandler.cpp deleted file mode 100644 index 623ffe2..0000000 --- a/test/qt/core/paos/test_MessageIdHandler.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include -#include - -#include "paos/MessageIdHandler.h" - -using namespace governikus; - -class test_MessageIdHandler - : public QObject -{ - Q_OBJECT - - private Q_SLOTS: - void testCreateNewMsgId() - { - MessageIdHandler handler; - - QString myMsgId = handler.createNewMsgId(); - - QVERIFY(myMsgId.startsWith("urn:uuid:")); - QVERIFY(!QUuid(myMsgId.mid(QString("urn:uuid:").size())).isNull()); - QCOMPARE(myMsgId.size(), 9 + 36); - } - - - void testRemoteMsgId() - { - MessageIdHandler handler; - QString remoteMsgId("123456789"); - handler.setRemoteMsgId(remoteMsgId); - - QCOMPARE(handler.getRemoteMsgId(), remoteMsgId); - QVERIFY(handler.createNewMsgId().startsWith(QLatin1String("urn:uuid:"))); - } - - -}; - -QTEST_GUILESS_MAIN(test_MessageIdHandler) -#include "test_MessageIdHandler.moc" diff --git a/test/qt/core/paos/test_PaosMessage.cpp b/test/qt/core/paos/test_PaosMessage.cpp index 9f51a7f..bceccf4 100644 --- a/test/qt/core/paos/test_PaosMessage.cpp +++ b/test/qt/core/paos/test_PaosMessage.cpp @@ -38,17 +38,17 @@ class test_PaosMessage void handleWSAddressingHeaders() { PaosMessage msg(PaosType::UNKNOWN); - QCOMPARE(msg.handleWSAddressingHeaders("elem name", "some value", QXmlStreamAttributes()), false); + QCOMPARE(msg.handleWSAddressingHeaders(QStringLiteral("elem name"), QStringLiteral("some value"), QXmlStreamAttributes()), false); QCOMPARE(msg.getMessageId(), QString()); QCOMPARE(msg.getRelatesTo(), QString()); - QCOMPARE(msg.handleWSAddressingHeaders("MessageID", "msg value", QXmlStreamAttributes()), true); - QCOMPARE(msg.getMessageId(), QString("msg value")); + QCOMPARE(msg.handleWSAddressingHeaders(QStringLiteral("MessageID"), QStringLiteral("msg value"), QXmlStreamAttributes()), true); + QCOMPARE(msg.getMessageId(), QStringLiteral("msg value")); QCOMPARE(msg.getRelatesTo(), QString()); - QCOMPARE(msg.handleWSAddressingHeaders("RelatesTo", "relates value", QXmlStreamAttributes()), true); - QCOMPARE(msg.getMessageId(), QString("msg value")); - QCOMPARE(msg.getRelatesTo(), QString("relates value")); + QCOMPARE(msg.handleWSAddressingHeaders(QStringLiteral("RelatesTo"), QStringLiteral("relates value"), QXmlStreamAttributes()), true); + QCOMPARE(msg.getMessageId(), QStringLiteral("msg value")); + QCOMPARE(msg.getRelatesTo(), QStringLiteral("relates value")); } diff --git a/test/qt/core/states/test_StateCertificateDescriptionCheck.cpp b/test/qt/core/states/test_StateCertificateDescriptionCheck.cpp index 94217b0..aae38a3 100644 --- a/test/qt/core/states/test_StateCertificateDescriptionCheck.cpp +++ b/test/qt/core/states/test_StateCertificateDescriptionCheck.cpp @@ -7,8 +7,6 @@ #include "asn1/ASN1Util.h" #include "asn1/CVCertificate.h" #include "context/AuthContext.h" -#include "Result.h" -#include "TestFileHelper.h" #include "TestAuthContext.h" #include diff --git a/test/qt/core/states/test_StateChangePin.cpp b/test/qt/core/states/test_StateChangePin.cpp new file mode 100644 index 0000000..624b76c --- /dev/null +++ b/test/qt/core/states/test_StateChangePin.cpp @@ -0,0 +1,109 @@ +/*! + * \brief Unit tests for \ref StateChangePin + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "states/StateChangePin.h" + +#include "MockCardConnectionWorker.h" + +#include + +using namespace governikus; + +class MockEstablishPaceChannelCommand + : public BaseCardCommand +{ + Q_OBJECT + + public: + explicit MockEstablishPaceChannelCommand(const QSharedPointer& pWorker) + : BaseCardCommand(pWorker) + { + } + + + ~MockEstablishPaceChannelCommand() override = default; + + void internalExecute() override + { + } + + + void setReturnCode(CardReturnCode pCode) + { + mReturnCode = pCode; + } + + +}; + +class test_StateChangePin + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void test_Run() + { + QThread workerThread; + workerThread.start(); + + const QSharedPointer worker(new MockCardConnectionWorker()); + worker->moveToThread(&workerThread); + const QSharedPointer connection(new CardConnection(worker)); + const QSharedPointer context(new ChangePinContext()); + context->setCardConnection(connection); + StateChangePin state(context); + + QTest::ignoreMessage(QtDebugMsg, "Invoke set Eid PIN command"); + state.run(); + QCOMPARE(state.mConnections.size(), 1); + + workerThread.quit(); + workerThread.wait(); + } + + + void test_OnSetEidPinDone() + { + const QSharedPointer context(new ChangePinContext()); + StateChangePin state(context); + const QSharedPointer worker(new MockCardConnectionWorker()); + const QSharedPointer command(new MockEstablishPaceChannelCommand(worker)); + + QSignalSpy spyContinue(&state, &StateChangePin::fireContinue); + QSignalSpy spyAbort(&state, &StateChangePin::fireAbort); + QSignalSpy spyInvalidPin(&state, &StateChangePin::fireInvalidPin); + + command->setReturnCode(CardReturnCode::OK); + state.onSetEidPinDone(command); + QCOMPARE(context->getSuccessMessage(), tr("You have successfully changed your PIN.")); + QCOMPARE(spyContinue.count(), 1); + + command->setReturnCode(CardReturnCode::CANCELLATION_BY_USER); + state.onSetEidPinDone(command); + QCOMPARE(context->getStatus().getStatusCode(), GlobalStatus::Code::Card_Cancellation_By_User); + QCOMPARE(spyAbort.count(), 1); + + context->setStatus(GlobalStatus::Code::No_Error); + + command->setReturnCode(CardReturnCode::NEW_PIN_MISMATCH); + state.onSetEidPinDone(command); + QCOMPARE(context->getStatus().getStatusCode(), GlobalStatus::Code::Card_NewPin_Mismatch); + QCOMPARE(spyInvalidPin.count(), 1); + + context->setStatus(GlobalStatus::Code::No_Error); + + command->setReturnCode(CardReturnCode::PIN_BLOCKED); + state.onSetEidPinDone(command); + QCOMPARE(context->getStatus().getStatusCode(), GlobalStatus::Code::No_Error); + QCOMPARE(spyAbort.count(), 2); + } + + +}; + +QTEST_GUILESS_MAIN(test_StateChangePin) +#include "test_StateChangePin.moc" diff --git a/test/qt/core/states/test_StateChangePinRemote.cpp b/test/qt/core/states/test_StateChangePinRemote.cpp new file mode 100644 index 0000000..3bb0d40 --- /dev/null +++ b/test/qt/core/states/test_StateChangePinRemote.cpp @@ -0,0 +1,61 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "states/remote_service/StateChangePinRemote.h" + +#include "context/RemoteServiceContext.h" + +#include "MockCardConnectionWorker.h" +#include "MockRemoteServer.h" + +#include + + +using namespace governikus; + +class test_StateChangePinRemote + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void initTestCase() + { + Env::setCreator(std::function([&] { + return new MockRemoteServer(); + })); + } + + + void test_Run() + { + QThread workerThread; + workerThread.start(); + + const QSharedPointer context(new RemoteServiceContext()); + const QString pin = QStringLiteral("103050"); + context->setPin(pin); + const QString slotHandle = QStringLiteral("slot"); + const QByteArray input("input"); + const QSharedPointer message(new IfdModifyPin(slotHandle, input)); + const QSharedPointer worker(new MockCardConnectionWorker()); + worker->moveToThread(&workerThread); + const QSharedPointer connection(new CardConnection(worker)); + context->setCardConnection(connection); + StateChangePinRemote state(context); + context->setModifyPinMessage(message); + + state.run(); + QCOMPARE(state.mConnections.size(), 1); + QCOMPARE(context->getPin(), QString("103050")); + + workerThread.quit(); + workerThread.wait(); + } + + +}; + +QTEST_GUILESS_MAIN(test_StateChangePinRemote) +#include "test_StateChangePinRemote.moc" diff --git a/test/qt/core/states/test_StateCheckRefreshAddress.cpp b/test/qt/core/states/test_StateCheckRefreshAddress.cpp index ac20b92..aadd879 100644 --- a/test/qt/core/states/test_StateCheckRefreshAddress.cpp +++ b/test/qt/core/states/test_StateCheckRefreshAddress.cpp @@ -4,6 +4,7 @@ #include "states/StateCheckRefreshAddress.h" +#include "AppSettings.h" #include "context/AuthContext.h" #include "Env.h" #include "MockNetworkManager.h" @@ -66,8 +67,8 @@ class test_StateCheckRefreshAddress for (const GlobalStatus::Code state : states) { - const Result& result = GlobalStatus(state); - QCOMPARE(result.getMinor(), GlobalStatus::Code::Paos_Error_AL_Communication_Error); + const ECardApiResult& result = GlobalStatus(state); + QCOMPARE(result.getMinor(), ECardApiResult::Minor::AL_Communication_Error); } } @@ -118,6 +119,154 @@ class test_StateCheckRefreshAddress } + void isMatchingSameOriginPolicyInDevMode() + { + Env::getSingleton()->getGeneralSettings().setDeveloperMode(false); + QVERIFY(!mState->isMatchingSameOriginPolicyInDevMode()); + + Env::getSingleton()->getGeneralSettings().setDeveloperMode(true); + const QUrl pUrlHttps("https://test/"); + const QUrl pUrlHttp("http://localhost:12345/test_StateCheckRefreshAddress/"); + const QUrl pSubjectUrl("http://localhost:12345/test_StateCheckRefreshAddress/"); + + mState->mUrl = pUrlHttps; + mState->mSubjectUrl = pSubjectUrl; + QVERIFY(!mState->isMatchingSameOriginPolicyInDevMode()); + + mState->mUrl = pUrlHttp; + QTest::ignoreMessage(QtCriticalMsg, "SOP-Check: Ignoring scheme and port in developer mode."); + QTest::ignoreMessage(QtCriticalMsg, " Origin URL: http://localhost:12345/test_StateCheckRefreshAddress/"); + QTest::ignoreMessage(QtCriticalMsg, " Refresh URL: http://localhost:12345/test_StateCheckRefreshAddress/"); + QVERIFY(mState->isMatchingSameOriginPolicyInDevMode()); + } + + + void determinateSubjectUrl() + { + const QUrl tcTokenUrl("http://test/"); + mAuthContext->setTcTokenUrl(tcTokenUrl); + + Env::getSingleton()->getGeneralSettings().setDeveloperMode(false); + QTest::ignoreMessage(QtWarningMsg, "No subjectURL/certificate description available, take the TcToken-URL instead"); + QCOMPARE(mState->determineSubjectUrl(), tcTokenUrl); + + Env::getSingleton()->getGeneralSettings().setDeveloperMode(true); + QCOMPARE(mState->determineSubjectUrl(), tcTokenUrl); + } + + + void reportCommunicationError() + { + QSignalSpy spy(mState.data(), &StateCheckRefreshAddress::fireAbort); + + QTest::ignoreMessage(QtCriticalMsg, "Card_Communication_Error | \"An error occurred while communicating with the ID card. Please make sure that your ID card is placed correctly on the card reader and try again.\""); + mState->reportCommunicationError(GlobalStatus::Code::Card_Communication_Error); + QCOMPARE(mAuthContext->getStatus().getStatusCode(), GlobalStatus::Code::Card_Communication_Error); + QCOMPARE(spy.count(), 1); + + mAuthContext->setStatus(GlobalStatus::Code::No_Error); + + QTest::ignoreMessage(QtCriticalMsg, "Network_Other_Error | \"An unknown network error occurred.\""); + mState->reportCommunicationError(GlobalStatus::Code::Network_Other_Error); + QCOMPARE(mAuthContext->getStatus().getStatusCode(), GlobalStatus::Code::Network_Other_Error); + QCOMPARE(spy.count(), 2); + } + + + void onNetworkReply_data() + { + QTest::addColumn("networkError"); + QTest::addColumn("status"); + QTest::addColumn("statusCode"); + QTest::addColumn("redirectUrl"); + QTest::addColumn("developerMode"); + + QTest::newRow("service unavailable") << QNetworkReply::NetworkError::ServiceUnavailableError << GlobalStatus::Code::Network_ServiceUnavailable << 1 << QUrl("http://test.com/") << false; + QTest::newRow("timeout") << QNetworkReply::NetworkError::TimeoutError << GlobalStatus::Code::Network_TimeOut << 2 << QUrl() << false; + QTest::newRow("proxy error") << QNetworkReply::NetworkError::ProxyNotFoundError << GlobalStatus::Code::Network_Proxy_Error << 0 << QUrl("test") << false; + QTest::newRow("ssl error") << QNetworkReply::NetworkError::SslHandshakeFailedError << GlobalStatus::Code::Network_Ssl_Establishment_Error << 1 << QUrl("https://test.com/") << false; + QTest::newRow("other error") << QNetworkReply::NetworkError::OperationCanceledError << GlobalStatus::Code::Network_Other_Error << 2 << QUrl("https://test.com/") << false; + QTest::newRow("no error unexpected status") << QNetworkReply::NetworkError::NoError << GlobalStatus::Code::Workflow_Network_Expected_Redirect << 2 << QUrl("https://test.com/") << false; + QTest::newRow("no error empty url") << QNetworkReply::NetworkError::NoError << GlobalStatus::Code::Workflow_Network_Empty_Redirect_Url << 302 << QUrl() << false; + QTest::newRow("no error invalid url") << QNetworkReply::NetworkError::NoError << GlobalStatus::Code::Workflow_Network_Malformed_Redirect_Url << 302 << QUrl("://://") << false; + QTest::newRow("no error http") << QNetworkReply::NetworkError::NoError << GlobalStatus::Code::Workflow_Network_Invalid_Scheme << 302 << QUrl("http://test.com/") << false; + QTest::newRow("no error http developer mode") << QNetworkReply::NetworkError::NoError << GlobalStatus::Code::No_Error << 302 << QUrl("http://test.com/") << true; + } + + + void onNetworkReply() + { + QFETCH(QNetworkReply::NetworkError, networkError); + QFETCH(GlobalStatus::Code, status); + QFETCH(int, statusCode); + QFETCH(QUrl, redirectUrl); + QFETCH(bool, developerMode); + + Env::getSingleton()->getGeneralSettings().setDeveloperMode(developerMode); + + QPointer reply(new MockNetworkReply()); + mState->mReply = reply; + + const QByteArray headerName("name"); + const QByteArray value("value"); + reply->setRawHeader(headerName, value); + reply->setAttribute(QNetworkRequest::HttpStatusCodeAttribute, QVariant(statusCode)); + reply->setAttribute(QNetworkRequest::RedirectionTargetAttribute, QVariant(redirectUrl)); + reply->setError(networkError, QString()); + + mAuthContext->setStatus(GlobalStatus::Code::No_Error); + QTest::ignoreMessage(QtDebugMsg, R"(Header | name: value)"); + mState->onNetworkReply(); + if (!developerMode) + { + QCOMPARE(mAuthContext->getStatus().getStatusCode(), status); + } + } + + + void fetchServerCertificate_DeveloperMode() + { + Env::getSingleton()->getGeneralSettings().setDeveloperMode(true); + const QUrl url("http://test.de/"); + mState->mUrl = url; + QTest::ignoreMessage(QtWarningMsg, "Refresh URL is http only. Certificate check skipped."); + mState->fetchServerCertificate(); + } + + + void fetchServerCertificate_AlreadyVerified() + { + Env::getSingleton()->getGeneralSettings().setDeveloperMode(false); + const QUrl url("https://test.de/"); + mState->mUrl = url; + mState->mVerifiedRefreshUrlHosts.insert(0, url); + QTest::ignoreMessage(QtDebugMsg, "SSL certificate already collected for QUrl(\"https://test.de/\")"); + mState->fetchServerCertificate(); + } + + + void fetchServerCertificate() + { + Env::getSingleton()->getGeneralSettings().setDeveloperMode(false); + const QUrl url("https://test.de/"); + mState->mUrl = url; + mState->fetchServerCertificate(); + QCOMPARE(mState->mConnections.size(), 3); + } + + + void doneSuccess() + { + const QUrl url("https://test.de/"); + mState->mUrl = url; + QSignalSpy spy(mState.data(), &StateCheckRefreshAddress::fireContinue); + + QTest::ignoreMessage(QtDebugMsg, "Determined RefreshUrl: QUrl(\"https://test.de/\")"); + mState->doneSuccess(); + QCOMPARE(mAuthContext->getRefreshUrl(), url); + } + + }; QTEST_GUILESS_MAIN(test_StateCheckRefreshAddress) diff --git a/test/qt/core/states/test_StateConnectCard.cpp b/test/qt/core/states/test_StateConnectCard.cpp new file mode 100644 index 0000000..1951de0 --- /dev/null +++ b/test/qt/core/states/test_StateConnectCard.cpp @@ -0,0 +1,77 @@ +/*! + * \brief unit tests for \ref StateConnectCard + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "states/StateConnectCard.h" + +#include "MockCardConnectionWorker.h" + +#include + +using namespace governikus; + +class test_StateConnectCard + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void test_OnCommandDone() + { + QThread workerThread; + workerThread.start(); + + const QSharedPointer context(new WorkflowContext()); + StateConnectCard connectCard(context); + const QString rName("reader name"); + const QSharedPointer command(new CreateCardConnectionCommand(rName, QPointer())); + + QSignalSpy spyContinue(&connectCard, &StateConnectCard::fireContinue); + + QSharedPointer connectionWorker(new MockCardConnectionWorker()); + connectionWorker->moveToThread(&workerThread); + QSharedPointer cardConnection(new CardConnection(connectionWorker)); + command->mCardConnection = cardConnection; + QTest::ignoreMessage(QtDebugMsg, "Card connection was successful"); + connectCard.onCommandDone(command); + QCOMPARE(context->getCardConnection(), cardConnection); + QCOMPARE(spyContinue.count(), 1); + + workerThread.quit(); + workerThread.wait(); + } + + + void test_OnReaderRemoved() + { + const QString readerName = QStringLiteral("name"); + const QSharedPointer context(new WorkflowContext()); + StateConnectCard connectCard(context); + + QSignalSpy spy(&connectCard, &StateConnectCard::fireReaderRemoved); + + connectCard.onReaderRemoved(readerName); + QCOMPARE(spy.count(), 0); + + context->setReaderName(readerName); + connectCard.onReaderRemoved(readerName); + QCOMPARE(spy.count(), 1); + } + + + void test_OnAbort() + { + const QSharedPointer context(new WorkflowContext()); + StateConnectCard connectCard(context); + QSignalSpy spyRetry(&connectCard, &StateConnectCard::fireRetry); + connectCard.onAbort(); + QCOMPARE(spyRetry.count(), 1); + } + + +}; + +QTEST_GUILESS_MAIN(test_StateConnectCard) +#include "test_StateConnectCard.moc" diff --git a/test/qt/core/states/test_StateDestroyPace.cpp b/test/qt/core/states/test_StateDestroyPace.cpp new file mode 100644 index 0000000..bf82a44 --- /dev/null +++ b/test/qt/core/states/test_StateDestroyPace.cpp @@ -0,0 +1,77 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "states/StateDestroyPace.h" + +#include "MockCardConnectionWorker.h" + +#include + + +using namespace governikus; + +class MockCardCommand + : public BaseCardCommand +{ + Q_OBJECT + + public: + explicit MockCardCommand(const QSharedPointer& pCardConnectionWorker) + : BaseCardCommand(pCardConnectionWorker) + { + } + + + ~MockCardCommand() override = default; + + void internalExecute() override + { + } + + +}; + +class test_StateDestroyPace + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void test_Run() + { + QThread workerThread; + workerThread.start(); + + const QSharedPointer context(new ChangePinContext()); + const QSharedPointer worker(new MockCardConnectionWorker()); + worker->moveToThread(&workerThread); + const QSharedPointer connection(new CardConnection(worker)); + context->setCardConnection(connection); + StateDestroyPace state(context); + + state.run(); + QCOMPARE(state.mConnections.size(), 1); + + workerThread.quit(); + workerThread.wait(); + } + + + void test_OnDestroyPace() + { + const QSharedPointer worker(new MockCardConnectionWorker()); + const QSharedPointer command(new MockCardCommand(worker)); + const QSharedPointer context(new ChangePinContext()); + StateDestroyPace state(context); + QSignalSpy spy(&state, &StateDestroyPace::fireContinue); + + state.onDestroyPaceDone(command); + QCOMPARE(spy.count(), 1); + } + + +}; + +QTEST_GUILESS_MAIN(test_StateDestroyPace) +#include "test_StateDestroyPace.moc" diff --git a/test/qt/core/states/test_StateDidAuthenticateEac1.cpp b/test/qt/core/states/test_StateDidAuthenticateEac1.cpp new file mode 100644 index 0000000..5cf18aa --- /dev/null +++ b/test/qt/core/states/test_StateDidAuthenticateEac1.cpp @@ -0,0 +1,111 @@ +/*! + * \brief Unit test for \ref StateDidAuthenticateEac1 + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "states/StateDidAuthenticateEac1.h" + +#include "MockCardConnectionWorker.h" +#include "TestAuthContext.h" +#include "TestFileHelper.h" + +#include + +using namespace governikus; + +class MockEstablishPaceChannelCommand + : public DidAuthenticateEAC1Command +{ + Q_OBJECT + + public: + explicit MockEstablishPaceChannelCommand(const QSharedPointer& pCardConnectionWorker) + : DidAuthenticateEAC1Command(pCardConnectionWorker) + { + } + + + void setReturnCode(CardReturnCode pCode) + { + mReturnCode = pCode; + } + + +}; + + +class test_StateDidAuthenticateEac1 + : public QObject +{ + Q_OBJECT + QSharedPointer mAuthContext; + + private Q_SLOTS: + void init() + { + mAuthContext.reset(new TestAuthContext(nullptr, ":/paos/DIDAuthenticateEAC1.xml")); + } + + + void cleanup() + { + mAuthContext.clear(); + } + + + void test_RunStateDidAuthenticateEac1() + { + QThread workerThread; + workerThread.start(); + + const QSharedPointer worker(new MockCardConnectionWorker()); + worker->moveToThread(&workerThread); + const QSharedPointer connection(new CardConnection(worker)); + const QSharedPointer eac1(new DIDAuthenticateEAC1()); + const EstablishPaceChannelOutput output; + mAuthContext->setCardConnection(connection); + mAuthContext->setDidAuthenticateEac1(eac1); + mAuthContext->setPaceOutputData(output); + + StateDidAuthenticateEac1 state(mAuthContext); + state.run(); + QCOMPARE(state.mConnections.size(), 1); + + workerThread.quit(); + workerThread.wait(); + } + + + void test_OnCardCommandDone() + { + const QSharedPointer worker(new MockCardConnectionWorker()); + QSharedPointer command(new MockEstablishPaceChannelCommand(worker)); + StateDidAuthenticateEac1 state(mAuthContext); + + QSignalSpy spyContinue(&state, &StateDidAuthenticateEac1::fireContinue); + QSignalSpy spyAbort(&state, &StateDidAuthenticateEac1::fireAbort); + + command->setReturnCode(CardReturnCode::UNKNOWN); + mAuthContext->setStatus(GlobalStatus::Code::No_Error); + state.onCardCommandDone(command); + QCOMPARE(mAuthContext->getStatus().getStatusCode(), GlobalStatus::Code::Unknown_Error); + QCOMPARE(spyAbort.count(), 1); + + command->setReturnCode(CardReturnCode::OK); + const QByteArray array(QByteArray::fromHex("9000")); + command->mChallenge = array; + QSharedPointer response(new DIDAuthenticateResponseEAC1()); + mAuthContext->setDidAuthenticateResponseEac1(response); + EstablishPaceChannelOutput output; + mAuthContext->setPaceOutputData(output); + QTest::ignoreMessage(QtDebugMsg, "No cvc chain determined, request new cvc list"); + state.onCardCommandDone(command); + QCOMPARE(spyContinue.count(), 1); + } + + +}; + +QTEST_GUILESS_MAIN(test_StateDidAuthenticateEac1) +#include "test_StateDidAuthenticateEac1.moc" diff --git a/test/qt/core/states/test_StateEstablishPaceChannel.cpp b/test/qt/core/states/test_StateEstablishPaceChannel.cpp new file mode 100644 index 0000000..29de8d4 --- /dev/null +++ b/test/qt/core/states/test_StateEstablishPaceChannel.cpp @@ -0,0 +1,232 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "states/StateEstablishPaceChannel.h" + +#include "context/ChangePinContext.h" +#include "MockCardConnectionWorker.h" +#include "TestAuthContext.h" +#include "TestFileHelper.h" + +#include + +using namespace governikus; + +class MockEstablishPaceChannelCommand + : public EstablishPaceChannelCommand +{ + Q_OBJECT + + public: + explicit MockEstablishPaceChannelCommand(const QSharedPointer& pCardConnectionWorker, PacePasswordId pPacePasswordId) + : EstablishPaceChannelCommand(pCardConnectionWorker, pPacePasswordId, QString(), QByteArray(), QByteArray()) + { + } + + + void setReturnCode(CardReturnCode pCode) + { + mReturnCode = pCode; + } + + +}; + +class MockUnblockPinCommand + : public UnblockPinCommand +{ + Q_OBJECT + + public: + explicit MockUnblockPinCommand(const QSharedPointer& pCardConnectionWorker) + : UnblockPinCommand(pCardConnectionWorker, QString()) + { + } + + + void setReturnCode(CardReturnCode pCode) + { + mReturnCode = pCode; + } + + +}; + + +class test_StateEstablishPaceChannel + : public QObject +{ + Q_OBJECT + QThread mWorkerThread; + QSharedPointer mAuthContext; + + private Q_SLOTS: + void init() + { + mWorkerThread.start(); + mAuthContext.reset(new TestAuthContext(nullptr, ":/paos/DIDAuthenticateEAC1.xml")); + } + + + void cleanup() + { + mAuthContext.clear(); + mWorkerThread.quit(); + mWorkerThread.wait(); + } + + + void test_OnUserCancelled() + { + StateEstablishPaceChannel state(mAuthContext); + const QSharedPointer worker(new MockCardConnectionWorker()); + worker->moveToThread(&mWorkerThread); + const QSharedPointer connection(new CardConnection(worker)); + const CardInfo cInfo(CardType::NONE, QSharedPointer(), 3, false, false); + ReaderInfo rInfo; + rInfo.setCardInfo(cInfo); + connection->mReaderInfo = rInfo; + mAuthContext->setCardConnection(connection); + + QSignalSpy spyAbort(&state, &AbstractState::fireAbort); + + QTest::ignoreMessage(QtInfoMsg, "Cancellation by user"); + state.onUserCancelled(); + QCOMPARE(mAuthContext->getStatus().getStatusCode(), GlobalStatus::Code::Workflow_Cancellation_By_User); + QCOMPARE(mAuthContext->getLastPaceResult(), CardReturnCode::CANCELLATION_BY_USER); + } + + + void test_OnEstablishConnectionDone_Pin() + { + StateEstablishPaceChannel state(mAuthContext); + state.mPasswordId = PacePasswordId::PACE_PIN; + const QSharedPointer worker(new MockCardConnectionWorker()); + const QSharedPointer command(new MockEstablishPaceChannelCommand(worker, PacePasswordId::PACE_PIN)); + worker->moveToThread(&mWorkerThread); + const QSharedPointer connection(new CardConnection(worker)); + const CardInfo cInfo(CardType::NONE, QSharedPointer(), 3, false, false); + ReaderInfo rInfo; + rInfo.setCardInfo(cInfo); + connection->mReaderInfo = rInfo; + mAuthContext->setCardConnection(connection); + + QSignalSpy spyContinue(&state, &StateEstablishPaceChannel::fireContinue); + QSignalSpy spyPaceChannelEstablished(&state, &StateEstablishPaceChannel::firePaceChannelEstablished); + QSignalSpy spyAbort(&state, &StateEstablishPaceChannel::fireAbort); + + command->setReturnCode(CardReturnCode::OK); + state.onEstablishConnectionDone(command); + QCOMPARE(mAuthContext->getLastPaceResult(), CardReturnCode::OK); + QCOMPARE(spyPaceChannelEstablished.count(), 1); + + mAuthContext->setStatus(GlobalStatus::Code::No_Error); + command->setReturnCode(CardReturnCode::CANCELLATION_BY_USER); + state.onEstablishConnectionDone(command); + QVERIFY(mAuthContext->getCardConnection()); + QCOMPARE(mAuthContext->getLastPaceResult(), CardReturnCode::CANCELLATION_BY_USER); + QCOMPARE(mAuthContext->getStatus().getStatusCode(), GlobalStatus::Code::Card_Cancellation_By_User); + QCOMPARE(spyAbort.count(), 1); + + command->setReturnCode(CardReturnCode::INVALID_PIN); + mAuthContext->setCardConnection(connection); + state.onEstablishConnectionDone(command); + QCOMPARE(mAuthContext->getLastPaceResult(), CardReturnCode::INVALID_PIN); + QCOMPARE(spyAbort.count(), 2); + + QCOMPARE(spyContinue.count(), 0); + } + + + void test_OnEstablishConnectionDone_Can() + { + const QSharedPointer worker(new MockCardConnectionWorker()); + worker->moveToThread(&mWorkerThread); + const QSharedPointer connection(new CardConnection(worker)); + const CardInfo cardInfo(CardType::NONE, QSharedPointer(), 2, false, false); + const ReaderInfo readerInfo(QString(), ReaderManagerPlugInType::UNKNOWN, cardInfo); + connection->onReaderInfoChanged(readerInfo); + const QSharedPointer context(new AuthContext(nullptr)); + context->setCardConnection(connection); + StateEstablishPaceChannel state(context); + state.mPasswordId = PacePasswordId::PACE_CAN; + QSharedPointer command(new MockEstablishPaceChannelCommand(worker, PacePasswordId::PACE_CAN)); + + QSignalSpy spyContinue(&state, &StateEstablishPaceChannel::fireContinue); + QSignalSpy spyAbort(&state, &StateEstablishPaceChannel::fireAbort); + + command->setReturnCode(CardReturnCode::OK); + state.onEstablishConnectionDone(command); + QCOMPARE(context->getLastPaceResult(), CardReturnCode::OK); + QCOMPARE(spyContinue.count(), 1); + + command->setReturnCode(CardReturnCode::CANCELLATION_BY_USER); + state.onEstablishConnectionDone(command); + QCOMPARE(context->getStatus().getStatusCode(), GlobalStatus::Code::Card_Cancellation_By_User); + QCOMPARE(context->getLastPaceResult(), CardReturnCode::CANCELLATION_BY_USER); + QCOMPARE(spyAbort.count(), 1); + + command->setReturnCode(CardReturnCode::INVALID_CAN); + state.onEstablishConnectionDone(command); + QCOMPARE(context->getLastPaceResult(), CardReturnCode::INVALID_CAN); + QCOMPARE(spyAbort.count(), 2); + } + + + void test_OnEstablishConnectionDone_Puk() + { + const QSharedPointer worker(new MockCardConnectionWorker()); + worker->moveToThread(&mWorkerThread); + const QSharedPointer connection(new CardConnection(worker)); + const CardInfo cInfo(CardType::UNKNOWN, QSharedPointer(), 3, false, false); + const ReaderInfo rInfo(QString(), ReaderManagerPlugInType::UNKNOWN, cInfo); + connection->onReaderInfoChanged(rInfo); + const QSharedPointer context(new ChangePinContext()); + context->setCardConnection(connection); + const QSharedPointer command(new MockUnblockPinCommand(worker)); + + StateEstablishPaceChannel state(context); + state.mPasswordId = PacePasswordId::PACE_PUK; + QSignalSpy spyContinue(&state, &StateEstablishPaceChannel::fireContinue); + QSignalSpy spyPacePukEstablished(&state, &StateEstablishPaceChannel::firePacePukEstablished); + QSignalSpy spyAbort(&state, &StateEstablishPaceChannel::fireAbort); + + command->setReturnCode(CardReturnCode::OK); + state.onEstablishConnectionDone(command); + QCOMPARE(context->getLastPaceResult(), CardReturnCode::OK_PUK); + QCOMPARE(context->getSuccessMessage(), tr("PIN successfully unblocked")); + QCOMPARE(spyPacePukEstablished.count(), 1); + + command->setReturnCode(CardReturnCode::CANCELLATION_BY_USER); + state.onEstablishConnectionDone(command); + QCOMPARE(context->getLastPaceResult(), CardReturnCode::CANCELLATION_BY_USER); + QCOMPARE(context->getStatus().getStatusCode(), GlobalStatus::Code::Card_Cancellation_By_User); + QCOMPARE(spyAbort.count(), 1); + + command->setReturnCode(CardReturnCode::INVALID_PUK); + state.onEstablishConnectionDone(command); + QCOMPARE(context->getLastPaceResult(), CardReturnCode::INVALID_PUK); + QCOMPARE(spyAbort.count(), 2); + + context->setStatus(GlobalStatus::Code::No_Error); + command->setReturnCode(CardReturnCode::PUK_INOPERATIVE); + state.onEstablishConnectionDone(command); + QCOMPARE(context->getLastPaceResult(), CardReturnCode::PUK_INOPERATIVE); + QCOMPARE(context->getStatus().getStatusCode(), GlobalStatus::Code::Card_Puk_Blocked); + QCOMPARE(spyAbort.count(), 3); + + context->setStatus(GlobalStatus::Code::No_Error); + command->setReturnCode(CardReturnCode::UNKNOWN); + state.onEstablishConnectionDone(command); + QCOMPARE(context->getStatus().getStatusCode(), GlobalStatus::Code::No_Error); + QCOMPARE(spyAbort.count(), 4); + + QCOMPARE(spyContinue.count(), 0); + } + + +}; + +QTEST_GUILESS_MAIN(test_StateEstablishPaceChannel) +#include "test_StateEstablishPaceChannel.moc" diff --git a/test/qt/core/states/test_StateEstablishPaceChannelRemote.cpp b/test/qt/core/states/test_StateEstablishPaceChannelRemote.cpp new file mode 100644 index 0000000..9411a9a --- /dev/null +++ b/test/qt/core/states/test_StateEstablishPaceChannelRemote.cpp @@ -0,0 +1,61 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "states/remote_service/StateEstablishPaceChannelRemote.h" + +#include "context/AuthContext.h" +#include "EstablishPaceChannelParser.h" + +#include "MockCardConnectionWorker.h" +#include "MockRemoteServer.h" + +#include + + +using namespace governikus; + +class test_StateEstablishPaceChannelRemote + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void initTestCase() + { + Env::setCreator(std::function([&] { + return new MockRemoteServer(); + })); + } + + + void test_Run() + { + QThread connectionThread; + connectionThread.start(); + + const QSharedPointer context(new RemoteServiceContext()); + StateEstablishPaceChannelRemote state(context); + const QString slotHandle = QStringLiteral("slothandle"); + const QByteArray input("abc"); + const QSharedPointer message(new IfdEstablishPaceChannel(slotHandle, input)); + context->setEstablishPaceChannelMessage(message); + const QSharedPointer worker(new MockCardConnectionWorker()); + worker->moveToThread(&connectionThread); + const QSharedPointer connection(new CardConnection(worker)); + context->setCardConnection(connection); + context->setPin(QString("1234")); + + state.run(); + QCOMPARE(context->getPin(), QString("1234")); + QCOMPARE(state.mConnections.size(), 1); + + connectionThread.quit(); + connectionThread.wait(); + } + + +}; + +QTEST_GUILESS_MAIN(test_StateEstablishPaceChannelRemote) +#include "test_StateEstablishPaceChannelRemote.moc" diff --git a/test/qt/core/states/test_StateGenericSendReceive.cpp b/test/qt/core/states/test_StateGenericSendReceive.cpp index 965d44d..461d5b4 100644 --- a/test/qt/core/states/test_StateGenericSendReceive.cpp +++ b/test/qt/core/states/test_StateGenericSendReceive.cpp @@ -2,10 +2,7 @@ * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany */ -#include "controller/AuthController.h" #include "Env.h" -#include "paos/invoke/InitializeFrameworkResponse.h" -#include "paos/retrieve/InitializeFramework.h" #include "states/StateBuilder.h" #include "states/StateGenericSendReceive.h" @@ -46,6 +43,102 @@ class test_StateGenericSendReceive } + void setReceivedMessage_data() + { + QTest::addColumn("type"); + QTest::addColumn("messageId"); + QTest::addColumn("testCase"); + + QTest::newRow("startpaosResponse") << PaosType::STARTPAOS_RESPONSE << QString("startpaosResponse") << 0; + QTest::newRow("initializeFramework") << PaosType::INITIALIZE_FRAMEWORK << QString("initializeFramework") << 1; + QTest::newRow("didList") << PaosType::DID_LIST << QString("didList") << 2; + QTest::newRow("didAuthenticateEac1") << PaosType::DID_AUTHENTICATE_EAC1 << QString("didAuthenticateEac1") << 3; + QTest::newRow("didAuthenticateEac2") << PaosType::DID_AUTHENTICATE_EAC2 << QString("didAuthenticateEac2") << 4; + QTest::newRow("didAuthenticateEacAdditionalInputType") << PaosType::DID_AUTHENTICATE_EAC_ADDITIONAL_INPUT_TYPE << QString("didAuthenticateEacAdditionalInputType") << 5; + QTest::newRow("transmit") << PaosType::TRANSMIT << QString("transmit") << 6; + QTest::newRow("disconnect") << PaosType::DISCONNECT << QString("disconnect") << 7; + QTest::newRow("default") << PaosType::UNKNOWN << QString("default") << 8; + } + + + void setReceivedMessage() + { + QFETCH(PaosType, type); + QFETCH(QString, messageId); + QFETCH(int, testCase); + + const QSharedPointer message(new PaosMessage(type)); + message->setMessageId(messageId); + + if (testCase == 8) + { + QTest::ignoreMessage(QtWarningMsg, "Unknown received message type: 0"); + } + + mState->setReceivedMessage(message); + QCOMPARE(mAuthContext->getReceivedMessageId(), messageId); + + switch (testCase) + { + case 0: + QCOMPARE(mAuthContext->getStartPaosResponse(), message.staticCast()); + break; + + case 1: + QCOMPARE(mAuthContext->getInitializeFramework(), message.staticCast()); + QVERIFY(mAuthContext->getInitializeFrameworkResponse()); + break; + + case 2: + QCOMPARE(mAuthContext->getDidList(), message.staticCast()); + QVERIFY(mAuthContext->getDidListResponse()); + break; + + case 3: + QCOMPARE(mAuthContext->getDidAuthenticateEac1(), message.staticCast()); + QVERIFY(mAuthContext->getDidAuthenticateResponseEac1()); + break; + + case 4: + QCOMPARE(mAuthContext->getDidAuthenticateEac2(), message.staticCast()); + QVERIFY(mAuthContext->getDidAuthenticateResponseEac2()); + break; + + case 5: + QCOMPARE(mAuthContext->getDidAuthenticateEacAdditional(), message.staticCast()); + break; + + case 6: + QVERIFY(mAuthContext->getTransmits().contains(message.staticCast())); + QVERIFY(!mAuthContext->getTransmitResponses().isEmpty()); + break; + + case 7: + QCOMPARE(mAuthContext->getDisconnect(), message.staticCast()); + QVERIFY(mAuthContext->getDisconnectResponse()); + break; + } + } + + + void onPreSharedKeyAuthenticationRequired() + { + const QByteArray data("data"); + const QByteArray psk("psk"); + const QByteArray sessionIdentifier("session"); + const QSharedPointer token(new TcToken(data)); + token->mPsk = psk; + token->mSessionIdentifier = sessionIdentifier; + mAuthContext->setTcToken(token); + auto* authenticator = new QSslPreSharedKeyAuthenticator(); + + QTest::ignoreMessage(QtDebugMsg, "pre-shared key authentication requested: \"\""); + mState->onPreSharedKeyAuthenticationRequired(authenticator); + QCOMPARE(authenticator->identity(), sessionIdentifier); + QCOMPARE(authenticator->preSharedKey(), QByteArray::fromHex(psk)); + } + + void connectionError() { MockNetworkReply reply; @@ -128,8 +221,8 @@ class test_StateGenericSendReceive for (const GlobalStatus::Code state : states) { - const Result& result = Result(GlobalStatus(state)); - QCOMPARE(result.getMinor(), GlobalStatus::Code::Paos_Error_DP_Trusted_Channel_Establishment_Failed); + const ECardApiResult& result = ECardApiResult(GlobalStatus(state)); + QCOMPARE(result.getMinor(), ECardApiResult::Minor::DP_Trusted_Channel_Establishment_Failed); } } diff --git a/test/qt/core/states/test_StateGetTcToken.cpp b/test/qt/core/states/test_StateGetTcToken.cpp new file mode 100644 index 0000000..b010125 --- /dev/null +++ b/test/qt/core/states/test_StateGetTcToken.cpp @@ -0,0 +1,132 @@ +/*! + * \brief Unit tests for \ref StateGetTcToken + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "states/StateGetTcToken.h" + +#include "MockNetworkReply.h" + +#include +#include + + +using namespace governikus; + + +class test_StateGetTcToken + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void test_Run() + { + const QSharedPointer context(new AuthContext(nullptr)); + StateGetTcToken state(context); + const QUrl validUrl(QString("https://test.com")); + const QUrl invalidUrl(QString("test")); + QSignalSpy spyAbort(&state, &StateGetTcToken::fireAbort); + + context->setTcTokenUrl(validUrl); + QTest::ignoreMessage(QtDebugMsg, "Got TC Token URL: QUrl(\"https://test.com\")"); + state.run(); + QCOMPARE(spyAbort.count(), 0); + + context->setTcTokenUrl(invalidUrl); + QTest::ignoreMessage(QtDebugMsg, "Got TC Token URL: QUrl(\"test\")"); + state.run(); + QCOMPARE(spyAbort.count(), 1); + } + + + void test_IsValidRedirectUrl() + { + const QSharedPointer context(new AuthContext(nullptr)); + StateGetTcToken state(context); + + QTest::ignoreMessage(QtCriticalMsg, "Error while connecting to the service provider. The server returns an invalid or empty redirect URL."); + const QUrl emptyUrl; + QVERIFY(!state.isValidRedirectUrl(emptyUrl)); + + const QUrl invalidUrl(QString("test")); + QVERIFY(!state.isValidRedirectUrl(invalidUrl)); + + const QUrl validUrl(QString("https://test.com")); + QVERIFY(state.isValidRedirectUrl(validUrl)); + } + + + void test_SendRequest() + { + const QSharedPointer context(new AuthContext(nullptr)); + StateGetTcToken state(context); + const QUrl url(QString("https://test.com")); + state.sendRequest(url); + QCOMPARE(state.mConnections.size(), 3); + } + + + void test_ParseTcTokenNoData() + { + MockNetworkReply reply; + const QSharedPointer context(new AuthContext(nullptr)); + StateGetTcToken state(context); + state.mReply = QPointer(&reply); + QSignalSpy spyAbort(&state, &StateGetTcToken::fireAbort); + + QTest::ignoreMessage(QtDebugMsg, "Received no data."); + state.parseTcToken(); + QCOMPARE(context->getStatus().getStatusCode(), GlobalStatus::Code::Workflow_TrustedChannel_No_Data_Received); + QCOMPARE(spyAbort.count(), 1); + } + + + void test_ParseTcTokenWithDataUsePsk() + { + const QByteArray data("" + "" + " https://eid-server.example.de/entrypoint" + " 1A2BB129" + " https://service.example.de/loggedin?7eb39f62" + " urn:liberty:paos:2006-08 " + " urn:ietf:rfc:4279 " + " " + " 4BC1A0B5 " + " " + ""); + MockNetworkReply reply(data); + const QSharedPointer context(new AuthContext(nullptr)); + StateGetTcToken state(context); + state.mReply = QPointer(&reply); + QSignalSpy spyContinue(&state, &StateGetTcToken::fireContinue); + + QVERIFY(!context->getTcToken()); + state.parseTcToken(); + QVERIFY(context->getTcToken()); + QVERIFY(!context->isTcTokenNotFound()); + QCOMPARE(spyContinue.count(), 1); + } + + + void test_ParseTcTokenWithDataNoPsk() + { + const QByteArray data("invalid data"); + MockNetworkReply reply(data); + const QSharedPointer context(new AuthContext(nullptr)); + StateGetTcToken state(context); + state.mReply = QPointer(&reply); + QSignalSpy spyAbort(&state, &StateGetTcToken::fireAbort); + + QTest::ignoreMessage(QtCriticalMsg, "TCToken invalid"); + state.parseTcToken(); + QCOMPARE(context->getStatus().getStatusCode(), GlobalStatus::Code::Workflow_TrustedChannel_Server_Format_Error); + QCOMPARE(spyAbort.count(), 1); + } + + +}; + +QTEST_GUILESS_MAIN(test_StateGetTcToken) +#include "test_StateGetTcToken.moc" diff --git a/test/qt/core/states/test_StateInitializeFramework.cpp b/test/qt/core/states/test_StateInitializeFramework.cpp index ab0d3ac..08ceb31 100644 --- a/test/qt/core/states/test_StateInitializeFramework.cpp +++ b/test/qt/core/states/test_StateInitializeFramework.cpp @@ -2,7 +2,6 @@ * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany */ -#include "controller/AuthController.h" #include "states/StateBuilder.h" #include "states/StateInitializeFramework.h" #include "TestFileHelper.h" diff --git a/test/qt/core/states/test_StatePreVerification.cpp b/test/qt/core/states/test_StatePreVerification.cpp index 9ef358e..1547841 100644 --- a/test/qt/core/states/test_StatePreVerification.cpp +++ b/test/qt/core/states/test_StatePreVerification.cpp @@ -7,13 +7,10 @@ #include "states/StatePreVerification.h" #include "AppSettings.h" -#include "Env.h" -#include "paos/retrieve/DidAuthenticateEac1.h" -#include "SecureStorage.h" -#include "TestFileHelper.h" #include "TestAuthContext.h" +#include #include #include #include @@ -79,7 +76,7 @@ class test_StatePreVerification { const_cast(&mState->mValidationDateTime)->setDate(QDate(2013, 12, 1)); auto signature = mAuthContext->mDIDAuthenticateEAC1->mEac1InputType.mCvCertificates.at(0)->getEcdsaSignature(); -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) BIGNUM* signaturePart = signature->r; #else const BIGNUM* signaturePart = nullptr; @@ -142,7 +139,7 @@ class test_StatePreVerification } settings.save(); - const int expectedCvcaSize = 10; + const int expectedCvcaSize = 12; QCOMPARE(mState->mTrustedCvcas.size(), expectedCvcaSize); const_cast(&mState->mValidationDateTime)->setDate(QDate(2013, 12, 1)); auto& trustedCvcas = const_cast >&>(mState->mTrustedCvcas); diff --git a/test/qt/core/states/test_StateProcessCertificatesFromEac2.cpp b/test/qt/core/states/test_StateProcessCertificatesFromEac2.cpp index 35c4f86..56757e6 100644 --- a/test/qt/core/states/test_StateProcessCertificatesFromEac2.cpp +++ b/test/qt/core/states/test_StateProcessCertificatesFromEac2.cpp @@ -36,8 +36,8 @@ class test_StateProcessCertificatesFromEac2 mAuthContext.reset(new TestAuthContext(nullptr, ":/paos/DIDAuthenticateEAC1.xml")); QSharedPointer didAuthEac2(static_cast(DidAuthenticateEac2Parser().parse(TestFileHelper::readFile(":/paos/DIDAuthenticateEAC2.xml")))); mAuthContext->setDidAuthenticateEac2(didAuthEac2); - EstablishPACEChannelOutput paceOutput; - paceOutput.parse(QByteArray::fromHex(TestFileHelper::readFile(":/card/EstablishPACEChannelOutput.hex")), PACE_PASSWORD_ID::PACE_PIN); + EstablishPaceChannelOutput paceOutput; + paceOutput.parse(QByteArray::fromHex(TestFileHelper::readFile(":/card/EstablishPaceChannelOutput.hex")), PacePasswordId::PACE_PIN); mAuthContext->setPaceOutputData(paceOutput); mState.reset(new StateProcessCertificatesFromEac2(mAuthContext)); diff --git a/test/qt/core/states/test_StateProcessRemoteMessages.cpp b/test/qt/core/states/test_StateProcessRemoteMessages.cpp new file mode 100644 index 0000000..82f9d0f --- /dev/null +++ b/test/qt/core/states/test_StateProcessRemoteMessages.cpp @@ -0,0 +1,104 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "states/remote_service/StateProcessRemoteMessages.h" + +#include "context/RemoteServiceContext.h" + +#include "MockCardConnectionWorker.h" +#include "MockRemoteServer.h" + +#include + + +using namespace governikus; + +class test_StateProcessRemoteMessages + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void initTestCase() + { + Env::setCreator(std::function([&] { + return new MockRemoteServer(); + })); + } + + + void test_Run() + { + const QSharedPointer context(new RemoteServiceContext()); + StateProcessRemoteMessages state(context); + state.run(); + QCOMPARE(state.mConnections.size(), 1); + QCOMPARE(state.mMessageConnections.size(), 4); + } + + + void test_OnMessageHandlerAdded() + { + const QSharedPointer context(new RemoteServiceContext()); + StateProcessRemoteMessages state(context); + const QSharedPointer messageHandler(new ServerMessageHandlerImpl(nullptr)); + + state.onMessageHandlerAdded(nullptr); + QCOMPARE(state.mMessageConnections.size(), 0); + + state.onMessageHandlerAdded(messageHandler); + QCOMPARE(state.mMessageConnections.size(), 4); + } + + + void test_OnEstablishPaceChannel() + { + QThread workerThread; + workerThread.start(); + + const QSharedPointer context(new RemoteServiceContext()); + StateProcessRemoteMessages state(context); + const QSharedPointer message(new IfdEstablishPaceChannel()); + const QSharedPointer worker(new MockCardConnectionWorker()); + worker->moveToThread(&workerThread); + const QSharedPointer connection(new CardConnection(worker)); + QSignalSpy spy(&state, &StateProcessRemoteMessages::fireEstablishPaceChannel); + + state.onEstablishPaceChannel(message, connection); + QCOMPARE(context->getEstablishPaceChannelMessage(), message); + QCOMPARE(context->getCardConnection(), connection); + QCOMPARE(spy.count(), 1); + + workerThread.quit(); + workerThread.wait(); + } + + + void test_OnModifyPin() + { + QThread workerThread; + workerThread.start(); + + const QSharedPointer context(new RemoteServiceContext()); + StateProcessRemoteMessages state(context); + const QSharedPointer message(new IfdModifyPin()); + const QSharedPointer worker(new MockCardConnectionWorker()); + worker->moveToThread(&workerThread); + const QSharedPointer connection(new CardConnection(worker)); + QSignalSpy spy(&state, &StateProcessRemoteMessages::fireModifyPin); + + state.onModifyPin(message, connection); + QCOMPARE(context->getModifyPinMessage(), message); + QCOMPARE(context->getCardConnection(), connection); + QCOMPARE(spy.count(), 1); + + workerThread.quit(); + workerThread.wait(); + } + + +}; + +QTEST_GUILESS_MAIN(test_StateProcessRemoteMessages) +#include "test_StateProcessRemoteMessages.moc" diff --git a/test/qt/core/states/test_StateSelectPasswordId.cpp b/test/qt/core/states/test_StateSelectPasswordId.cpp new file mode 100644 index 0000000..e5f45d7 --- /dev/null +++ b/test/qt/core/states/test_StateSelectPasswordId.cpp @@ -0,0 +1,42 @@ +/*! + * \brief Tests the StateSelectPasswordId + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "states/StateSelectPasswordId.h" + +#include + +using namespace governikus; + + +class test_StateSelectPasswordId + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void test_Run() + { + QSharedPointer context(new WorkflowContext()); + StateSelectPasswordId id(context); + QSignalSpy spyPasswordIdCAN(&id, &StateSelectPasswordId::firePasswordIdCAN); + QSignalSpy spyContinue(&id, &StateSelectPasswordId::fireContinue); + + context->setCanAllowedMode(true); + QTest::ignoreMessage(QtDebugMsg, "CAN allowed: true"); + id.run(); + QCOMPARE(spyPasswordIdCAN.count(), 1); + + context->setCanAllowedMode(false); + QTest::ignoreMessage(QtDebugMsg, "CAN allowed: false"); + id.run(); + QCOMPARE(spyContinue.count(), 1); + } + + +}; + +QTEST_GUILESS_MAIN(test_StateSelectPasswordId) +#include "test_StateSelectPasswordId.moc" diff --git a/test/qt/core/states/test_StateSelectReader.cpp b/test/qt/core/states/test_StateSelectReader.cpp new file mode 100644 index 0000000..1b7197a --- /dev/null +++ b/test/qt/core/states/test_StateSelectReader.cpp @@ -0,0 +1,83 @@ +/*! + * \brief Tests the StateSelectReader + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "states/StateSelectReader.h" + +#include + +using namespace governikus; + + +class test_StateSelectReader + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void test_RequiresCard() + { + const QSharedPointer context(new WorkflowContext()); + StateSelectReader state(context); + + QVERIFY(state.requiresCard(ReaderManagerPlugInType::PCSC)); + QVERIFY(state.requiresCard(ReaderManagerPlugInType::REMOTE)); + QVERIFY(!state.requiresCard(ReaderManagerPlugInType::UNKNOWN)); + QVERIFY(!state.requiresCard(ReaderManagerPlugInType::BLUETOOTH)); + } + + + void test_OnReaderInfoChangedNoSelectableReaders() + { + const QSharedPointer context(new WorkflowContext()); + context->setReaderPlugInTypes({ReaderManagerPlugInType::PCSC, ReaderManagerPlugInType::UNKNOWN, ReaderManagerPlugInType::REMOTE}); + StateSelectReader state(context); + + QTest::ignoreMessage(QtDebugMsg, "No selectable reader detected"); + state.onReaderInfoChanged(); + } + + + void test_OnReaderDeviceError() + { + const QSharedPointer context(new WorkflowContext()); + StateSelectReader state(context); + QSignalSpy spyAbort(&state, &StateSelectReader::fireAbort); + + state.onReaderDeviceError(GlobalStatus::Code::No_Error); + QCOMPARE(spyAbort.count(), 0); + + state.onReaderDeviceError(GlobalStatus::Code::Workflow_Reader_Device_Scan_Error); + QCOMPARE(spyAbort.count(), 0); + + state.onReaderDeviceError(GlobalStatus::Code::Card_Communication_Error); + QCOMPARE(spyAbort.count(), 1); + QCOMPARE(context->getStatus().getStatusCode(), GlobalStatus::Code::Card_Communication_Error); + } + + + void test_OnEntry() + { + const QSharedPointer context(new WorkflowContext()); + StateSelectReader state(context); + const QString stateName("name"); + state.setStateName(stateName); + + QSignalSpy spyRetry(&state, &StateSelectReader::fireRetry); + + state.onEntry(nullptr); + + Q_EMIT context->fireAbortCardSelection(); + QCOMPARE(spyRetry.count(), 1); + + Q_EMIT context->fireReaderPlugInTypesChanged(); + QCOMPARE(spyRetry.count(), 2); + } + + +}; + +QTEST_GUILESS_MAIN(test_StateSelectReader) +#include "test_StateSelectReader.moc" diff --git a/test/qt/core/states/test_StateStartPaosResponse.cpp b/test/qt/core/states/test_StateStartPaosResponse.cpp index 8639a08..f492395 100644 --- a/test/qt/core/states/test_StateStartPaosResponse.cpp +++ b/test/qt/core/states/test_StateStartPaosResponse.cpp @@ -6,8 +6,6 @@ #include #include -#include "controller/AuthController.h" -#include "paos/retrieve/StartPaosResponse.h" #include "states/StateBuilder.h" #include "states/StateStartPaosResponse.h" #include "TestFileHelper.h" @@ -52,9 +50,12 @@ class test_StateStartPaosResponse Q_EMIT fireStateStart(nullptr); mAuthContext->setStateApproved(); - const Result& result = mState->getContext()->getStatus(); - QCOMPARE(result.getMajor(), Result::Major::Error); - QCOMPARE(result.getMinor(), GlobalStatus::Code::Paos_Error_DP_Timeout_Error); + const GlobalStatus& status = mState->getContext()->getStatus(); + QCOMPARE(status.getStatusCode(), GlobalStatus::Code::Card_Cancellation_By_User); + + const ECardApiResult& result = mState->getContext()->getStartPaosResult(); + QCOMPARE(result.getMajor(), ECardApiResult::Major::Error); + QCOMPARE(result.getMinor(), ECardApiResult::Minor::DP_Timeout_Error); } diff --git a/test/qt/core/states/test_StateStartRemoteService.cpp b/test/qt/core/states/test_StateStartRemoteService.cpp new file mode 100644 index 0000000..a5cd4af --- /dev/null +++ b/test/qt/core/states/test_StateStartRemoteService.cpp @@ -0,0 +1,44 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "states/remote_service/StateStartRemoteService.h" + +#include "MockCardConnectionWorker.h" +#include "MockRemoteServer.h" + +#include + + +using namespace governikus; + +class test_StateStartRemoteService + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void initTestCase() + { + Env::setCreator(std::function([&] { + return new MockRemoteServer(); + })); + } + + + void test_Run() + { + const QSharedPointer context(new RemoteServiceContext()); + StateStartRemoteService state(context); + QSignalSpy spyContinue(&state, &StateStartRemoteService::fireContinue); + + state.run(); + QCOMPARE(spyContinue.count(), 1); + state.onMessageHandlerAdded(nullptr); + } + + +}; + +QTEST_GUILESS_MAIN(test_StateStartRemoteService) +#include "test_StateStartRemoteService.moc" diff --git a/test/qt/core/states/test_StateStopRemoteService.cpp b/test/qt/core/states/test_StateStopRemoteService.cpp new file mode 100644 index 0000000..57cb611 --- /dev/null +++ b/test/qt/core/states/test_StateStopRemoteService.cpp @@ -0,0 +1,56 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "states/remote_service/StateStopRemoteService.h" + +#include "context/RemoteServiceContext.h" + +#include "MockCardConnectionWorker.h" +#include "MockRemoteServer.h" + +#include + + +using namespace governikus; + +class test_StateStopRemoteService + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void initTestCase() + { + Env::setCreator(std::function([&] { + return new MockRemoteServer(); + })); + } + + + void test_Run() + { + const QSharedPointer context(new RemoteServiceContext()); + StateStopRemoteService state(context); + QSignalSpy spyContinue(&state, &StateStopRemoteService::fireContinue); + + state.run(); + QCOMPARE(spyContinue.count(), 1); + } + + + void test_OnExit() + { + const QSharedPointer context(new RemoteServiceContext()); + StateStopRemoteService state(context); + const QString name("name"); + state.setStateName(name); + state.onExit(nullptr); + QVERIFY(!context->getRemoteServer()->isRunning()); + } + + +}; + +QTEST_GUILESS_MAIN(test_StateStopRemoteService) +#include "test_StateStopRemoteService.moc" diff --git a/test/qt/core/states/test_StateTransmit.cpp b/test/qt/core/states/test_StateTransmit.cpp new file mode 100644 index 0000000..6c1d15b --- /dev/null +++ b/test/qt/core/states/test_StateTransmit.cpp @@ -0,0 +1,120 @@ +/*! + * \brief Unit tests for \ref StateTransmit + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "states/StateTransmit.h" + +#include "MockCardConnectionWorker.h" + +#include +#include + + +using namespace governikus; + +class MockTransmitCommand + : public TransmitCommand +{ + Q_OBJECT + + public: + MockTransmitCommand(const QSharedPointer& pCardConnectionWorker, const QVector& info, const QString& slot) + : TransmitCommand(pCardConnectionWorker, info, slot) + { + } + + + virtual void internalExecute() override; + virtual ~MockTransmitCommand() override = default; + + void setReturnCode(CardReturnCode code) + { + mReturnCode = code; + } + + +}; + +void MockTransmitCommand::internalExecute() +{ +} + + +class test_StateTransmit + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void test_Run() + { + QThread workerThread; + workerThread.start(); + + const QSharedPointer worker(new MockCardConnectionWorker()); + worker->moveToThread(&workerThread); + const QSharedPointer connection(new CardConnection(worker)); + const QSharedPointer context(new AuthContext(nullptr)); + context->setCardConnection(connection); + StateTransmit stateTransmit(context); + + const QString slotHandle("slot"); + const InputAPDUInfo info; + const QSharedPointer transmit(new Transmit()); + transmit->setSlotHandle(slotHandle); + transmit->appendInputApduInfo(info); + QByteArrayList outputApdus; + const QByteArray output("output"); + outputApdus.insert(0, output); + const QSharedPointer response(new TransmitResponse()); + response->setOutputApdus(outputApdus); + context->addTransmit(transmit); + context->addTransmitResponse(response); + + stateTransmit.run(); + QCOMPARE(stateTransmit.mConnections.size(), 1); + + workerThread.quit(); + workerThread.wait(); + } + + + void test_OnCardCommandDone() + { + const QSharedPointer context(new AuthContext(nullptr)); + const QSharedPointer response1(new TransmitResponse()); + const QSharedPointer response2(new TransmitResponse()); + const QSharedPointer response3(new TransmitResponse()); + context->addTransmitResponse(response1); + context->addTransmitResponse(response2); + context->addTransmitResponse(response3); + StateTransmit stateTransmit(context); + const QSharedPointer worker(new MockCardConnectionWorker()); + QVector vector(5); + const InputAPDUInfo info(QByteArray("info")); + vector.insert(0, info); + const QSharedPointer command(new MockTransmitCommand(worker, vector, QString())); + QSignalSpy spyContinue(&stateTransmit, &StateTransmit::fireContinue); + QSignalSpy spyAbort(&stateTransmit, &StateTransmit::fireAbort); + + command->setReturnCode(CardReturnCode::OK); + stateTransmit.onCardCommandDone(command); + QCOMPARE(spyContinue.count(), 1); + + command->setReturnCode(CardReturnCode::UNEXPECTED_TRANSMIT_STATUS); + stateTransmit.onCardCommandDone(command); + QCOMPARE(context->getStatus().getStatusCode(), GlobalStatus::Code::Card_Unexpected_Transmit_Status); + QCOMPARE(spyContinue.count(), 2); + + command->setReturnCode(CardReturnCode::UNKNOWN); + stateTransmit.onCardCommandDone(command); + QCOMPARE(spyAbort.count(), 1); + } + + +}; + +QTEST_GUILESS_MAIN(test_StateTransmit) +#include "test_StateTransmit.moc" diff --git a/test/qt/core/states/test_StateUpdRetryCounter.cpp b/test/qt/core/states/test_StateUpdRetryCounter.cpp new file mode 100644 index 0000000..c906a18 --- /dev/null +++ b/test/qt/core/states/test_StateUpdRetryCounter.cpp @@ -0,0 +1,110 @@ +/*! + * \brief Unit tests for \ref StateUpdateRetryCounter + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "states/StateUpdateRetryCounter.h" + +#include "MockCardConnectionWorker.h" + +#include + +using namespace governikus; + +class MockCardCommand + : public BaseCardCommand +{ + Q_OBJECT + + public: + explicit MockCardCommand(const QSharedPointer& pCardConnectionWorker) + : BaseCardCommand(pCardConnectionWorker) + { + } + + + void setCode(CardReturnCode pCode) + { + mReturnCode = pCode; + } + + + void internalExecute() override + { + mReturnCode = mCardConnectionWorker->updateRetryCounter(); + } + + +}; + +class MockCardConnection + : public CardConnection +{ + Q_OBJECT + + private: + const ReaderInfo mReaderInfo; + + public: + MockCardConnection() + : CardConnection() + , mReaderInfo(QString(), ReaderManagerPlugInType::UNKNOWN, CardInfo(CardType::NONE, QSharedPointer(), 3)) + { + } + + + virtual const ReaderInfo& getReaderInfo() override + { + return mReaderInfo; + } + + +}; + +class test_StateUpdateRetryCounter + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void test_RunWithoutCardConnection() + { + const QSharedPointer context(new WorkflowContext()); + StateUpdateRetryCounter counter(context); + QSignalSpy spyContinue(&counter, &StateUpdateRetryCounter::fireContinue); + QSignalSpy spyAbort(&counter, &StateUpdateRetryCounter::fireAbort); + + QTest::ignoreMessage(QtDebugMsg, "No card connection available."); + counter.run(); + QCOMPARE(spyContinue.count(), 0); + QCOMPARE(spyAbort.count(), 1); + } + + + void test_OnUpdateRetryCounterDone() + { + const QSharedPointer context(new WorkflowContext()); + StateUpdateRetryCounter counter(context); + const QSharedPointer worker(new MockCardConnectionWorker()); + const QSharedPointer command(new MockCardCommand(worker)); + QSignalSpy spyContinue(&counter, &StateUpdateRetryCounter::fireContinue); + QSignalSpy spyAbort(&counter, &StateUpdateRetryCounter::fireAbort); + + QTest::ignoreMessage(QtDebugMsg, "StateUpdateRetryCounter::onUpdateRetryCounterDone()"); + QTest::ignoreMessage(QtCriticalMsg, "An error occurred while communicating with the card reader, cannot determine retry counter, abort state"); + counter.onUpdateRetryCounterDone(command); + QCOMPARE(spyAbort.count(), 1); + + command->setCode(CardReturnCode::OK); + context->setCardConnection(QSharedPointer::create()); + QTest::ignoreMessage(QtDebugMsg, "StateUpdateRetryCounter::onUpdateRetryCounterDone()"); + counter.onUpdateRetryCounterDone(command); + QCOMPARE(spyContinue.count(), 1); + } + + +}; + +QTEST_GUILESS_MAIN(test_StateUpdateRetryCounter) +#include "test_StateUpdRetryCounter.moc" diff --git a/test/qt/core/test_CertificateChecker.cpp b/test/qt/core/test_CertificateChecker.cpp index b292a5c..34d8c5f 100644 --- a/test/qt/core/test_CertificateChecker.cpp +++ b/test/qt/core/test_CertificateChecker.cpp @@ -6,12 +6,10 @@ #include "CertificateChecker.h" -#include "AppSettings.h" #include "context/AuthContext.h" #include "SecureStorage.h" #include "MockActivationContext.h" -#include "TestFileHelper.h" #include diff --git a/test/qt/core/test_DiagnosisConnectionTest.cpp b/test/qt/core/test_DiagnosisConnectionTest.cpp new file mode 100644 index 0000000..3524809 --- /dev/null +++ b/test/qt/core/test_DiagnosisConnectionTest.cpp @@ -0,0 +1,124 @@ +/*! + * \brief Unit tests for \ref DiagnosisConnectionTest + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "DiagnosisConnectionTest.h" + +#include + +using namespace governikus; + +class test_DiagnosisConnectionTest + : public QObject +{ + Q_OBJECT + + QSharedPointer mTest; + + private Q_SLOTS: + void init() + { + mTest.reset(new DiagnosisConnectionTest()); + } + + + void cleanup() + { + mTest.clear(); + } + + + void test_OnProxyPingTestDone() + { + mTest->onProxyPingTestDone(); + QVERIFY(mTest->mPingTestOnProxySuccessful); + QVERIFY(mTest->mProxyPingDone); + QCOMPARE(mTest->mPingSocketToProxy.state(), QAbstractSocket::UnconnectedState); + } + + + void test_OnProxyPingTestError() + { + QTest::ignoreMessage(QtDebugMsg, "Error occurred while trying to ping proxy: QAbstractSocket::ConnectionRefusedError"); + mTest->onProxyPingTestError(QAbstractSocket::ConnectionRefusedError); + QVERIFY(!mTest->mPingTestOnProxySuccessful); + QVERIFY(mTest->mProxyPingDone); + } + + + void test_OnSocketConnectionTestWithProxyDone() + { + mTest->onSocketConnectionTestWithProxyDone(); + QVERIFY(mTest->mConnectionTestWithProxySuccessful); + QVERIFY(mTest->mConnectionTestWithProxyDone); + QCOMPARE(mTest->mTcpSocketWithProxy.state(), QAbstractSocket::UnconnectedState); + } + + + void test_OnSocketConnectionTestWithProxyError() + { + QTest::ignoreMessage(QtDebugMsg, "Could not connect to test server with proxy: QAbstractSocket::NetworkError"); + mTest->onSocketConnectionTestWithProxyError(QAbstractSocket::NetworkError); + QVERIFY(!mTest->mConnectionTestWithProxySuccessful); + QVERIFY(mTest->mConnectionTestWithProxyDone); + } + + + void test_OnSocketConnectionTestWithoutProxyDone() + { + mTest->onSocketConnectionTestWithoutProxyDone(); + QVERIFY(mTest->mConnectionTestWithoutProxySuccessful); + QVERIFY(mTest->mConnectionTestWithoutProxyDone); + QCOMPARE(mTest->mTcpSocketWithProxy.state(), QAbstractSocket::UnconnectedState); + } + + + void test_OnSocketConnectionTestWithoutProxyError() + { + QTest::ignoreMessage(QtDebugMsg, "Could not connect to test server without proxy: QAbstractSocket::ProxyProtocolError"); + mTest->onSocketConnectionTestWithoutProxyError(QAbstractSocket::ProxyProtocolError); + QVERIFY(!mTest->mConnectionTestWithoutProxySuccessful); + QVERIFY(mTest->mConnectionTestWithoutProxyDone); + } + + + void test_CheckIfAllProcessesDone() + { + QSignalSpy spy(mTest.data(), &DiagnosisConnectionTest::fireConnectionTestDone); + + mTest->checkIfAllProcessesDone(); + QCOMPARE(spy.count(), 0); + + mTest->onProxyPingTestDone(); + mTest->onSocketConnectionTestWithoutProxyDone(); + mTest->onSocketConnectionTestWithProxyDone(); + QCOMPARE(spy.count(), 1); + } + + + void test_GetProxyTypeAsString() + { + QCOMPARE(mTest->getProxyTypeAsQString(QNetworkProxy::NoProxy), QStringLiteral("NoProxy")); + QCOMPARE(mTest->getProxyTypeAsQString(QNetworkProxy::DefaultProxy), QStringLiteral("DefaultProxy")); + QCOMPARE(mTest->getProxyTypeAsQString(QNetworkProxy::Socks5Proxy), QStringLiteral("Socks5Proxy")); + QCOMPARE(mTest->getProxyTypeAsQString(QNetworkProxy::HttpProxy), QStringLiteral("HttpProxy")); + QCOMPARE(mTest->getProxyTypeAsQString(QNetworkProxy::HttpCachingProxy), QStringLiteral("HttpCachingProxy")); + QCOMPARE(mTest->getProxyTypeAsQString(QNetworkProxy::FtpCachingProxy), QStringLiteral("FtpCachingProxy")); + } + + + void test_GetProxyCapabilitiesAsString() + { + QNetworkProxy::Capabilities caps = {QNetworkProxy::TunnelingCapability, QNetworkProxy::ListeningCapability, QNetworkProxy::UdpTunnelingCapability, + QNetworkProxy::CachingCapability, QNetworkProxy::HostNameLookupCapability, QNetworkProxy::SctpTunnelingCapability, + QNetworkProxy::SctpListeningCapability}; + QCOMPARE(mTest->getProxyCapabilitiesAsQString(caps), QString("Tunnel, Listen, UDP, Caching, NameLookup, SctpTunnel, SctpListen")); + } + + +}; + +QTEST_GUILESS_MAIN(test_DiagnosisConnectionTest) +#include "test_DiagnosisConnectionTest.moc" diff --git a/test/qt/core/test_DiagnosisModel.cpp b/test/qt/core/test_DiagnosisModel.cpp new file mode 100644 index 0000000..82409d6 --- /dev/null +++ b/test/qt/core/test_DiagnosisModel.cpp @@ -0,0 +1,185 @@ +/*! + * \brief Unit tests for \ref DiagnosisModel + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "DiagnosisModel.h" + +#include "TestFileHelper.h" + +#include + +using namespace governikus; + +class test_DiagnosisModel + : public QObject +{ + Q_OBJECT + QSharedPointer mContext; + QSharedPointer mModel; + + private Q_SLOTS: + void init() + { + mContext.reset(new DiagnosisContext()); + mModel.reset(new DiagnosisModel(mContext)); + } + + + void cleanup() + { + mModel.clear(); + mContext.clear(); + } + + + void test_newDiagnosisModel() + { + QCOMPARE(mModel->mContext, mContext); + QCOMPARE(mModel->mRootItem->getText(), QString("Diagnosis data")); + QCOMPARE(mModel->mOperatingSystemItem->getText(), QString("Operating system")); + QCOMPARE(mModel->mReaderItem->getText(), QString("Card reader")); + QCOMPARE(mModel->mPcScItem->getText(), QString("PC/SC")); + QCOMPARE(mModel->mPairedDevices->getText(), QString("Paired devices")); + QCOMPARE(mModel->mNetworkInterfaces->getText(), QString("Network interfaces")); + QCOMPARE(mModel->mNetworkConnectionTest->getText(), QString("Network connection test")); + QCOMPARE(mModel->mInstalledAntivirus->getText(), QString("Installed antivirus software")); + QCOMPARE(mModel->mWindowsFirewall->getText(), QString("Firewall")); + QCOMPARE(mModel->mTimestampItem->getText(), QString("Time of diagnosis")); + + QCOMPARE(mModel->mRootItem->childCount(), 10); + QCOMPARE(mModel->mRootItem->getChild(0), mModel->mAppVersionItem); + QCOMPARE(mModel->mRootItem->getChild(1), mModel->mOperatingSystemItem); + QCOMPARE(mModel->mRootItem->getChild(2), mModel->mReaderItem); + QCOMPARE(mModel->mRootItem->getChild(3), mModel->mPcScItem); + QCOMPARE(mModel->mRootItem->getChild(4), mModel->mPairedDevices); + QCOMPARE(mModel->mRootItem->getChild(5), mModel->mNetworkInterfaces); + QCOMPARE(mModel->mRootItem->getChild(6), mModel->mNetworkConnectionTest); + QCOMPARE(mModel->mRootItem->getChild(7), mModel->mInstalledAntivirus); + QCOMPARE(mModel->mRootItem->getChild(8), mModel->mWindowsFirewall); + QCOMPARE(mModel->mRootItem->getChild(9), mModel->mTimestampItem); + + QCOMPARE(mModel->mOperatingSystemItem->childCount(), 3); + + QCOMPARE(mModel->mReaderItem->childCount(), 1); + QCOMPARE(mModel->mReaderItem->getChild(0)->getText(), QString("Diagnosis is running...")); + + QCOMPARE(mModel->mNetworkConnectionTest->childCount(), 1); + QCOMPARE(mModel->mNetworkConnectionTest->getChild(0)->getText(), QString("Diagnosis is running...")); + } + + + void test_OnReaderInfosChanged() + { + mModel->onReaderInfosChanged(); + QCOMPARE(mModel->mReaderItem->childCount(), 1); + QCOMPARE(mModel->mReaderItem->getChild(0)->getText(), QString("Not recognised")); + + ReaderInfo defaultInfo; + ReaderInfo infoEidCard(QString("testInfo"), ReaderManagerPlugInType::PCSC, CardInfo(CardType::EID_CARD)); + ReaderInfo comfortReaderInfo(QString("name"), ReaderManagerPlugInType::BLUETOOTH, CardInfo(CardType::UNKNOWN)); + comfortReaderInfo.setBasicReader(false); + const QVector readerInfos = {defaultInfo, infoEidCard, comfortReaderInfo}; + mContext->setReaderInfos(readerInfos); + mModel->onReaderInfosChanged(); + + QCOMPARE(mModel->mReaderItem->childCount(), 3); + QCOMPARE(mModel->mReaderItem->getChild(0)->getText(), QString()); + QCOMPARE(mModel->mReaderItem->getChild(1)->getText(), QString("testInfo")); + QCOMPARE(mModel->mReaderItem->getChild(2)->getText(), QString("name")); + + auto child1 = mModel->mReaderItem->getChild(0); + QCOMPARE(child1->childCount(), 2); + QCOMPARE(child1->getChild(0)->getText(), QString("Type: Basic card reader")); + QCOMPARE(child1->getChild(1)->getText(), QString("Card: not inserted")); + + auto child2 = mModel->mReaderItem->getChild(1); + QCOMPARE(child2->childCount(), 3); + QCOMPARE(child2->getChild(0)->getText(), QString("Type: Basic card reader")); + QCOMPARE(child2->getChild(1)->getText(), QString("Card: ID card (PA/eAT)")); + QCOMPARE(child2->getChild(2)->getText(), QString("Retry counter: 4")); + + auto child3 = mModel->mReaderItem->getChild(2); + QCOMPARE(child3->childCount(), 2); + QCOMPARE(child3->getChild(0)->getText(), QString("Type: Standard / comfort card reader")); + QCOMPARE(child3->getChild(1)->getText(), QString("Card: unknown type")); + } + + + void test_onPcscInfoChanged() + { + const QString version("version"); + const DiagnosisContext::ComponentInfo component1; + const DiagnosisContext::ComponentInfo component2; + const DiagnosisContext::ComponentInfo driver1; + const DiagnosisContext::ComponentInfo driver2; + QVector components = {component1, component2}; + QVector drivers = {driver1, driver2}; + mContext->setPcscInfo(version, components, drivers); + + mModel->onPcscInfoChanged(); + QCOMPARE(mModel->mPcScItem->childCount(), 3); + QCOMPARE(mModel->mPcScItem->getChild(0)->getText(), QString("Version: version")); + QCOMPARE(mModel->mPcScItem->getChild(1)->getText(), QString("Components")); + QCOMPARE(mModel->mPcScItem->getChild(2)->getText(), QString("Driver")); + } + + + void test_OnTimestampChanged() + { + const QDateTime invalid; + const QDate date(2018, 10, 12); + const QTime time(12, 0); + const QDateTime valid(date, time); + + mContext->setTimestamp(invalid); + mModel->onTimestampChanged(); + QCOMPARE(mModel->mTimestampItem->childCount(), 1); + QCOMPARE(mModel->mTimestampItem->getChild(0)->getText(), QString("Initial diagnosis running, please wait.")); + + mContext->setTimestamp(valid); + mModel->onTimestampChanged(); + QCOMPARE(mModel->mTimestampItem->childCount(), 1); + QCOMPARE(mModel->mTimestampItem->getChild(0)->getText(), QString("12. October 2018, 12:00:00 PM")); + } + + + void test_OnNetworkInfoChanged() + { + const QNetworkInterface interface1; + const QNetworkInterface interface2; + const QNetworkInterface interface3; + QList interfaces = {interface1, interface2, interface3}; + + mContext->setNetworkInterfaces(interfaces); + mModel->onNetworkInfoChanged(); + QCOMPARE(mModel->mNetworkInterfaces->childCount(), 3); + for (int i = 0; i < mModel->mNetworkInterfaces->childCount(); i++) + { + const auto& interface = mModel->mNetworkInterfaces->getChild(i); + QCOMPARE(interface->childCount(), 2); + } + } + + + void test_OnAntivirusDetectionFailed() + { + mModel->onAntivirusDetectionFailed(); + QCOMPARE(mModel->mInstalledAntivirus->childCount(), 1); + QCOMPARE(mModel->mInstalledAntivirus->getChild(0)->getText(), QString("Antivirus detection failed.")); + } + + + void test_OnFirewallInformationFailed() + { + mModel->onFirewallInformationFailed(); + QCOMPARE(mModel->mWindowsFirewall->childCount(), 1); + QCOMPARE(mModel->mWindowsFirewall->getChild(0)->getText(), QString("An error occurred while trying to gather firewall information. Please check the log for more information.")); + } + + +}; + +QTEST_GUILESS_MAIN(test_DiagnosisModel) +#include "test_DiagnosisModel.moc" diff --git a/test/qt/core/test_SelfAuthenticationData.cpp b/test/qt/core/test_SelfAuthenticationData.cpp index 2118521..87741e9 100644 --- a/test/qt/core/test_SelfAuthenticationData.cpp +++ b/test/qt/core/test_SelfAuthenticationData.cpp @@ -5,6 +5,9 @@ */ #include "SelfAuthenticationData.h" + +#include "TestFileHelper.h" + #include using namespace governikus; @@ -14,77 +17,48 @@ class test_SelfAuthenticationData { Q_OBJECT - private: - QFile selfAuthenticationDataXmlFile; - QFile selfAuthenticationDataNoStreetXmlFile; - QFile selfAuthenticationDataNoAddressXmlFile; - - void checkAndOpenFile(QFile& file) - { - QVERIFY(file.exists()); - QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text)); - } - - - public: - test_SelfAuthenticationData() - : selfAuthenticationDataXmlFile(":/self/SelfAuthenticationData.xml") - , selfAuthenticationDataNoStreetXmlFile(":/self/SelfAuthenticationDataNoStreet.xml") - , selfAuthenticationDataNoAddressXmlFile(":/self/SelfAuthenticationDataNoAddress.xml") - { - checkAndOpenFile(selfAuthenticationDataXmlFile); - checkAndOpenFile(selfAuthenticationDataNoStreetXmlFile); - checkAndOpenFile(selfAuthenticationDataNoAddressXmlFile); - } - - private Q_SLOTS: + void parsedCrap() + { + QTest::ignoreMessage(QtDebugMsg, "JSON parsing failed: \"illegal value\""); + SelfAuthenticationData selfAuthenticationData("abc"); + QVERIFY(!selfAuthenticationData.isValid()); + + QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::DateOfExpiry), QString()); + QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::GivenNames), QString()); + QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::Nationality), QString()); + QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::IssuingState), QString()); + } + + void parsedValues() { - SelfAuthenticationData selfAuthenticationData(selfAuthenticationDataXmlFile.readAll()); + const auto& data = TestFileHelper::readFile(":/self/SelfAuthenticationData.json"); + SelfAuthenticationData selfAuthenticationData(data); QVERIFY(selfAuthenticationData.isValid()); - - QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::DocumentType), QString("TP")); + QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::DateOfExpiry), QString("2020-10-31+01:00")); + QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::DocumentType), QString("TA")); QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::IssuingState), QString("D")); - QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::GivenNames), QString("ERIKA")); + QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::Nationality), QString("AZE")); + QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::GivenNames), QStringLiteral("ANDR\u00c9")); QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::FamilyNames), QString("MUSTERMANN")); QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::ArtisticName), QString()); QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::AcademicTitle), QString()); - QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::BirthName), QString("GABLER")); - QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::DateOfBirth), QString("1964-08-12+01:00")); - QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::PlaceOfBirth), QString("BERLIN")); - QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::PlaceOfResidenceStreet), QString("HEIDESTRASSE 17")); - QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::PlaceOfResidenceCity), QStringLiteral("K\u00D6LN")); + QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::BirthName), QString()); + QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::DateOfBirth), QString("1981-06-17+02:00")); + QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::PlaceOfBirth), QString("FRANKFURT (ODER)")); + QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::PlaceOfResidenceStreet), QStringLiteral("EHM-WELK-STRA\u00dfE 33")); + QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::PlaceOfResidenceCity), QStringLiteral("L\u00dcBBENAU/SPREEWALD")); QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::PlaceOfResidenceCountry), QString("D")); - QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::PlaceOfResidenceZipCode), QString("51147")); - } - - - void parsedValuesNoStreet() - { - SelfAuthenticationData selfAuthenticationData(selfAuthenticationDataNoStreetXmlFile.readAll()); - QVERIFY(selfAuthenticationData.isValid()); - - QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::DocumentType), QString("TP")); - QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::IssuingState), QString("D")); - QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::GivenNames), QString("ANNEKATHRIN")); - QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::FamilyNames), QString("LERCH")); - QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::ArtisticName), QString()); - QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::AcademicTitle), QString()); - QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::DateOfBirth), QString("1976-07-05+01:00")); - QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::PlaceOfResidenceCity), QString("HALLE (SAALE)")); - - QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::PlaceOfResidenceStreet), QString()); - - QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::PlaceOfResidenceCountry), QString("D")); - QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::PlaceOfResidenceZipCode), QString("06108")); + QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::PlaceOfResidenceZipCode), QString("03222")); } void parsedValuesNoAddress() { - SelfAuthenticationData selfAuthenticationData(selfAuthenticationDataNoAddressXmlFile.readAll()); + const auto& data = TestFileHelper::readFile(":/self/SelfAuthenticationDataNoAddress.json"); + SelfAuthenticationData selfAuthenticationData(data); QVERIFY(selfAuthenticationData.isValid()); QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::DocumentType), QString("TP")); @@ -93,6 +67,7 @@ class test_SelfAuthenticationData QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::FamilyNames), QString("HILLEBRANDT")); QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::ArtisticName), QStringLiteral("GRAF V. L\u00DDSKY")); QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::AcademicTitle), QString("DR.HC.")); + QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::BirthName), QString("This data has not been stored in this chip generation.")); QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::DateOfBirth), QString("1952-06-17+01:00")); QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::PlaceOfBirth), QString("TRIER")); @@ -103,19 +78,27 @@ class test_SelfAuthenticationData } - void tryToParseClosedFile() + void parsedValuesNoStreet() { - selfAuthenticationDataXmlFile.close(); - SelfAuthenticationData selfAuthenticationData(selfAuthenticationDataXmlFile.readAll()); - QVERIFY(!selfAuthenticationData.isValid()); + const auto& data = TestFileHelper::readFile(":/self/SelfAuthenticationDataNoStreet.json"); + SelfAuthenticationData selfAuthenticationData(data); + QVERIFY(selfAuthenticationData.isValid()); - selfAuthenticationDataNoStreetXmlFile.close(); - SelfAuthenticationData selfAuthenticationDataNoStreet(selfAuthenticationDataNoStreetXmlFile.readAll()); - QVERIFY(!selfAuthenticationDataNoStreet.isValid()); + QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::DocumentType), QString("TP")); + QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::IssuingState), QString("D")); + QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::GivenNames), QString("ANNEKATHRIN")); + QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::FamilyNames), QString("LERCH")); + QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::ArtisticName), QString()); + QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::AcademicTitle), QString()); + QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::BirthName), QString()); + QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::DateOfBirth), QString("1976-07-05+01:00")); + QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::PlaceOfBirth), QStringLiteral("BAD K\u00D6NIGSHOFEN I. GRABFELD")); + QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::PlaceOfResidenceCity), QStringLiteral("L\u00DCBBENAU/SPREEWALD")); - selfAuthenticationDataNoAddressXmlFile.close(); - SelfAuthenticationData selfAuthenticationDataNoAddress(selfAuthenticationDataNoAddressXmlFile.readAll()); - QVERIFY(!selfAuthenticationDataNoAddress.isValid()); + QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::PlaceOfResidenceStreet), QString()); + + QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::PlaceOfResidenceCountry), QString("D")); + QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::PlaceOfResidenceZipCode), QString("06108")); } diff --git a/test/qt/core/test_TcToken.cpp b/test/qt/core/test_TcToken.cpp index c5e417f..6db7375 100644 --- a/test/qt/core/test_TcToken.cpp +++ b/test/qt/core/test_TcToken.cpp @@ -5,6 +5,7 @@ */ #include "TcToken.h" + #include using namespace governikus; @@ -46,9 +47,6 @@ class test_TcToken QCOMPARE(token.getRefreshAddress().toString(), QString("https://service.example.de/loggedin?7eb39f62")); QCOMPARE(token.getCommunicationErrorAddress().toString(), QString("https://service.example.de/ComError?7eb39f62")); QCOMPARE(token.getPsk(), QByteArray("4BC1A0B5")); - - token.clearPsk(); - QVERIFY(!token.usePsk()); } @@ -207,9 +205,6 @@ class test_TcToken QVERIFY(token.getRefreshAddress().isEmpty()); QVERIFY(token.getCommunicationErrorAddress().isEmpty()); QVERIFY(!token.usePsk()); - - token.clearPsk(); - QVERIFY(!token.usePsk()); } @@ -221,6 +216,175 @@ class test_TcToken } + void test_IsValid_data() + { + QTest::addColumn("data"); + QTest::addColumn("valid"); + + QTest::newRow("noSchemaConform") << QByteArray("" + "" + " https://eid-server.example.de/entrypoint" + " 1A2BB129" + " https://service.example.de/loggedin?7eb39f62" + " https://service.example.de/ComError?7eb39f62" + " " + " urn:ietf:rfc:4279 " + " " + " 4BC1A0B5 " + " " + "") << false; + + QTest::newRow("invalidBinding") << QByteArray("" + "" + " https://eid-server.example.de/entrypoint" + " 1A2BB129" + " https://service.example.de/loggedin?7eb39f62" + " https://service.example.de/ComError?7eb39f62" + " binding" + " urn:ietf:rfc:4279 " + " " + " 4BC1A0B5 " + " " + "") << false; + + QTest::newRow("invalidSecurityProtocol") << QByteArray("" + "" + " https://eid-server.example.de/entrypoint" + " 1A2BB129" + " https://service.example.de/loggedin?7eb39f62" + " https://service.example.de/ComError?7eb39f62" + " urn:liberty:paos:2006-08 " + " securityProtocol" + " " + " 4BC1A0B5 " + " " + "") << false; + + QTest::newRow("invalidServerAdress") << QByteArray("" + "" + " eid-server.example.de/entrypoint" + " 1A2BB129" + " https://service.example.de/loggedin?7eb39f62" + " https://service.example.de/ComError?7eb39f62" + " urn:liberty:paos:2006-08 " + " urn:ietf:rfc:4279 " + " " + " 4BC1A0B5 " + " " + "") << false; + + QTest::newRow("invalidRefreshAdress") << QByteArray("" + "" + " https://eid-server.example.de/entrypoint" + " 1A2BB129" + " service.example.de/loggedin?7eb39f62" + " https://service.example.de/ComError?7eb39f62" + " urn:liberty:paos:2006-08 " + " urn:ietf:rfc:4279 " + " " + " 4BC1A0B5 " + " " + "") << false; + + QTest::newRow("invalidRefreshAdress") << QByteArray("" + "" + " https://eid-server.example.de/entrypoint" + " 1A2BB129" + " https://service.example.de/loggedin?7eb39f62" + " https://service.example.de/ComError?7eb39f62" + " urn:liberty:paos:2006-08 " + " urn:ietf:rfc:4279 " + " " + " 4BC1A0B56 " + " " + "") << false; + + QTest::newRow("valid") << QByteArray("" + "" + " https://eid-server.example.de/entrypoint" + " 1A2BB129" + " https://service.example.de/loggedin?7eb39f62" + " https://service.example.de/ComError?7eb39f62" + " urn:liberty:paos:2006-08 " + " urn:ietf:rfc:4279 " + " " + " 4BC1A0B5 " + " " + "") << true; + } + + + void test_IsValid() + { + QFETCH(QByteArray, data); + QFETCH(bool, valid); + + TcToken token(data); + QCOMPARE(token.isValid(), valid); + } + + + void test_ValuesAreSchemaConform() + { + TcToken token(QByteArray("" + "" + " https://eid-server.example.de/entrypoint" + " 1A2BB129" + " https://service.example.de/loggedin?7eb39f62" + " https://service.example.de/ComError?7eb39f62" + " urn:liberty:paos:2006-08 " + " urn:ietf:rfc:4279 " + " " + " 4BC1A0B5 " + " " + "")); + const QString binding = QStringLiteral("urn:liberty:paos:2006-08"); + const QString pathProtocol = QStringLiteral("urn:ietf:rfc:4279"); + const QByteArray psk("4BC1A0B5"); + const QByteArray identifier("1A2BB129"); + const QString serverAdress = QStringLiteral("https://eid-server.example.de/entrypoint"); + const QString errorAdress = QStringLiteral("https://service.example.de/ComError?7eb39f62"); + const QString refreshAdress = QStringLiteral("https://service.example.de/loggedin?7eb39f62"); + + QTest::ignoreMessage(QtCriticalMsg, "Binding is no valid anyUri: \"\""); + QVERIFY(!token.valuesAreSchemaConform(QString(), pathProtocol, psk, + identifier, serverAdress, errorAdress, refreshAdress)); + QTest::ignoreMessage(QtCriticalMsg, "Binding is no valid anyUri: \"://://\""); + QVERIFY(!token.valuesAreSchemaConform(QString("://://"), pathProtocol, psk, + identifier, serverAdress, errorAdress, refreshAdress)); + + QTest::ignoreMessage(QtCriticalMsg, "PathSecurity-Protocol is no valid URI: \"\""); + QVERIFY(token.valuesAreSchemaConform(binding, QString(""), psk, + identifier, serverAdress, errorAdress, refreshAdress)); + + QTest::ignoreMessage(QtWarningMsg, "PSK is null"); + QVERIFY(token.valuesAreSchemaConform(binding, pathProtocol, QByteArray(), + identifier, serverAdress, errorAdress, refreshAdress)); + + QTest::ignoreMessage(QtWarningMsg, "SessionIdentifier is null"); + QVERIFY(token.valuesAreSchemaConform(binding, pathProtocol, psk, + QByteArray(), serverAdress, errorAdress, refreshAdress)); + + QTest::ignoreMessage(QtCriticalMsg, "ServerAddress no valid anyUri: \"\""); + QVERIFY(!token.valuesAreSchemaConform(binding, pathProtocol, psk, + identifier, QString(), errorAdress, refreshAdress)); + QTest::ignoreMessage(QtCriticalMsg, "ServerAddress no valid anyUri: \"://://\""); + QVERIFY(!token.valuesAreSchemaConform(binding, pathProtocol, psk, + identifier, QString("://://"), errorAdress, refreshAdress)); + + QTest::ignoreMessage(QtCriticalMsg, "RefreshAddress no valid anyUri: \"\""); + QVERIFY(!token.valuesAreSchemaConform(binding, pathProtocol, psk, + identifier, serverAdress, errorAdress, QString())); + QTest::ignoreMessage(QtCriticalMsg, "RefreshAddress no valid anyUri: \"://://\""); + QVERIFY(!token.valuesAreSchemaConform(binding, pathProtocol, psk, + identifier, serverAdress, errorAdress, QString("://://"))); + + QTest::ignoreMessage(QtCriticalMsg, "CommunicationErrorAddress no valid anyUri: \"://://\""); + QVERIFY(!token.valuesAreSchemaConform(binding, pathProtocol, psk, + identifier, serverAdress, QString("://://"), refreshAdress)); + } + + }; QTEST_GUILESS_MAIN(test_TcToken) diff --git a/test/qt/drivers/test_ReaderDetector.cpp b/test/qt/drivers/test_ReaderDetector.cpp index 1bac6ac..2d1a539 100644 --- a/test/qt/drivers/test_ReaderDetector.cpp +++ b/test/qt/drivers/test_ReaderDetector.cpp @@ -6,7 +6,6 @@ #include "ReaderDetector.h" -#include "Env.h" #include "MockReaderDetector.h" #include "ResourceLoader.h" @@ -24,7 +23,7 @@ const QLatin1String KOMFORT_DRIVER_URL("https://www.reiner-sct.com/support/suppo #else const QLatin1String KOMFORT_DRIVER_URL("https://www.reiner-sct.com/support/support-anfrage/?os=Linux&productGroup=77304735&product=77304822&q=driver#choice5"); #endif -} +} // namespace class test_ReaderDetector diff --git a/test/qt/export/test_PdfExporter.cpp b/test/qt/export/test_PdfExporter.cpp index dc96ef5..1ee4c58 100644 --- a/test/qt/export/test_PdfExporter.cpp +++ b/test/qt/export/test_PdfExporter.cpp @@ -42,7 +42,7 @@ class test_PdfExporter for (int i = 0; i < 100; ++i) { - entries << HistoryInfo("SubjectName", "SubjectUrl", "Usage", QDateTime::currentDateTime(), "TermOfUsage", "RequestedData"); + entries << HistoryInfo("SubjectName", "SubjectUrl", "Usage", QDateTime::currentDateTime(), "TermOfUsage", {"RequestedData"}); } QTest::newRow("multiple") << 50000 << 350000 << entries; } @@ -54,8 +54,8 @@ class test_PdfExporter QFETCH(int, max); QFETCH(QVector, infos); - AppSettings::getInstance().getHistorySettings().setHistoryInfos(infos); - AppSettings::getInstance().getHistorySettings().save(); + Env::getSingleton()->getHistorySettings().setHistoryInfos(infos); + Env::getSingleton()->getHistorySettings().save(); QTemporaryFile file; QVERIFY(file.open()); diff --git a/test/qt/file_provider/test_Downloader.cpp b/test/qt/file_provider/test_Downloader.cpp index 7e36f9c..d87fd10 100644 --- a/test/qt/file_provider/test_Downloader.cpp +++ b/test/qt/file_provider/test_Downloader.cpp @@ -20,6 +20,7 @@ class test_Downloader private: QTemporaryDir mCacheDir; + MockNetworkManager mMockNetworkManager; void verifySuccessReply(const QSignalSpy& pSpy, const QUrl& pUrl, const QDateTime& pTimestamp, const QByteArray& pData) { @@ -78,7 +79,7 @@ class test_Downloader void init() { - Env::set(NetworkManager::staticMetaObject, std::make_shared()); + Env::set(NetworkManager::staticMetaObject, &mMockNetworkManager); } @@ -92,9 +93,9 @@ class test_Downloader { const QByteArray fileContent("Some icon data"); const QDateTime timestampOnServer(QDate(2017, 6, 1), QTime(12, 00, 0, 0)); - MockNetworkReply* const reply = new MockNetworkReply(fileContent, HttpStatusCode::OK); + MockNetworkReply* const reply = new MockNetworkReply(fileContent, HTTP_STATUS_OK); reply->setFileModificationTimestamp(timestampOnServer); - Env::getSingleton()->setNextReply(reply); + mMockNetworkManager.setNextReply(reply); Downloader* const downloader = Env::getSingleton(); QSignalSpy spy(downloader, &Downloader::fireDownloadSuccess); @@ -102,7 +103,7 @@ class test_Downloader const QUrl url("http://server/reader/icons/icon.png"); downloader->download(url); - Env::getSingleton()->fireFinished(); + mMockNetworkManager.fireFinished(); verifySuccessReply(spy, url, timestampOnServer, fileContent); } @@ -110,8 +111,8 @@ class test_Downloader void downloadNonExistingFile() { - MockNetworkReply* const reply = new MockNetworkReply(QByteArray(), HttpStatusCode::NOT_FOUND); - Env::getSingleton()->setNextReply(reply); + MockNetworkReply* const reply = new MockNetworkReply(QByteArray(), HTTP_STATUS_NOT_FOUND); + mMockNetworkManager.setNextReply(reply); Downloader* const downloader = Env::getSingleton(); QSignalSpy spy(downloader, &Downloader::fireDownloadFailed); @@ -119,7 +120,7 @@ class test_Downloader const QUrl url("http://server/reader/icons/icon.png"); downloader->download(url); - Env::getSingleton()->fireFinished(); + mMockNetworkManager.fireFinished(); verifyFailedReply(spy, url, GlobalStatus::Code::Downloader_File_Not_Found); } @@ -128,10 +129,10 @@ class test_Downloader void conditionalDownloadOfNewerFile() { const QByteArray fileContent("Some icon data"); - MockNetworkReply* const reply = new MockNetworkReply(fileContent, HttpStatusCode::OK); + MockNetworkReply* const reply = new MockNetworkReply(fileContent, HTTP_STATUS_OK); const QDateTime timestampOnServer(QDate(2017, 7, 1), QTime(12, 00, 0, 0)); reply->setFileModificationTimestamp(timestampOnServer); - Env::getSingleton()->setNextReply(reply); + mMockNetworkManager.setNextReply(reply); Downloader* const downloader = Env::getSingleton(); QSignalSpy spy(downloader, &Downloader::fireDownloadSuccess); @@ -140,11 +141,11 @@ class test_Downloader const QDateTime timestampInCache(QDate(2017, 6, 1), QTime(12, 00, 0, 0)); downloader->downloadIfNew(url, timestampInCache); - Env::getSingleton()->fireFinished(); + mMockNetworkManager.fireFinished(); verifySuccessReply(spy, url, timestampOnServer, fileContent); - QNetworkRequest* const lastRequest = Env::getSingleton()->getLastRequest(); + QNetworkRequest* const lastRequest = mMockNetworkManager.getLastRequest(); QVERIFY(lastRequest); QCOMPARE(lastRequest->rawHeader(QByteArray("If-Modified-Since")), QLocale::c().toString(timestampInCache, QStringLiteral("ddd, dd MMM yyyy hh:mm:ss 'GMT'")).toLatin1()); QCOMPARE(lastRequest->rawHeader(QByteArray("If-Modified-Since")), QByteArray("Thu, 01 Jun 2017 12:00:00 GMT")); @@ -153,10 +154,10 @@ class test_Downloader void conditionalDownloadOfOlderFile() { - MockNetworkReply* const reply = new MockNetworkReply(QByteArray(), HttpStatusCode::NOT_MODIFIED); + MockNetworkReply* const reply = new MockNetworkReply(QByteArray(), HTTP_STATUS_NOT_MODIFIED); const QDateTime timestampOnServer(QDate(2017, 6, 1), QTime(12, 00, 0, 0)); reply->setFileModificationTimestamp(timestampOnServer); - Env::getSingleton()->setNextReply(reply); + mMockNetworkManager.setNextReply(reply); Downloader* const downloader = Env::getSingleton(); QSignalSpy spy(downloader, &Downloader::fireDownloadUnnecessary); @@ -165,11 +166,11 @@ class test_Downloader const QDateTime timestampInCache(QDate(2017, 7, 1), QTime(12, 00, 0, 0)); downloader->downloadIfNew(url, timestampInCache); - Env::getSingleton()->fireFinished(); + mMockNetworkManager.fireFinished(); verifyUnnecessaryDownloadReply(spy, url); - QNetworkRequest* const lastRequest = Env::getSingleton()->getLastRequest(); + QNetworkRequest* const lastRequest = mMockNetworkManager.getLastRequest(); QVERIFY(lastRequest); QCOMPARE(lastRequest->rawHeader(QByteArray("If-Modified-Since")), QLocale::c().toString(timestampInCache, QStringLiteral("ddd, dd MMM yyyy hh:mm:ss 'GMT'")).toLatin1()); QCOMPARE(lastRequest->rawHeader(QByteArray("If-Modified-Since")), QByteArray("Sat, 01 Jul 2017 12:00:00 GMT")); diff --git a/test/qt/file_provider/test_FileProvider.cpp b/test/qt/file_provider/test_FileProvider.cpp index a717d52..63e470e 100644 --- a/test/qt/file_provider/test_FileProvider.cpp +++ b/test/qt/file_provider/test_FileProvider.cpp @@ -22,12 +22,12 @@ class test_FileProvider private Q_SLOTS: void testProviderReturnsMeaningfulUpdatableFiles() { - FileProvider& provider = FileProvider::getInstance(); - const QSharedPointer updatableFile1 = provider.getFile(mSection, mName1, mDefaultPath); + const auto fileProvider = Env::getSingleton(); + const QSharedPointer updatableFile1 = fileProvider->getFile(mSection, mName1, mDefaultPath); QVERIFY(updatableFile1); QCOMPARE(updatableFile1->lookupPath(), QStringLiteral(":/updatable-files/reader/img_ACS_ACR1252U.png")); - const QSharedPointer updatableFile2 = provider.getFile(mSection, mName2, mDefaultPath); + const QSharedPointer updatableFile2 = fileProvider->getFile(mSection, mName2, mDefaultPath); QVERIFY(updatableFile2); QCOMPARE(updatableFile2->lookupPath(), mDefaultPath); } @@ -35,10 +35,10 @@ class test_FileProvider void testProviderReturnsUniqueUpdatableFiles() { - FileProvider& provider = FileProvider::getInstance(); - const QSharedPointer updatableFile11 = provider.getFile(mSection, mName1, mDefaultPath); - const QSharedPointer updatableFile12 = provider.getFile(mSection, mName1, mDefaultPath); - const QSharedPointer updatableFile2 = provider.getFile(mSection, mName2, mDefaultPath); + const auto fileProvider = Env::getSingleton(); + const QSharedPointer updatableFile11 = fileProvider->getFile(mSection, mName1, mDefaultPath); + const QSharedPointer updatableFile12 = fileProvider->getFile(mSection, mName1, mDefaultPath); + const QSharedPointer updatableFile2 = fileProvider->getFile(mSection, mName2, mDefaultPath); QVERIFY(updatableFile11); QVERIFY(updatableFile12); diff --git a/test/qt/global/test_BuildHelper.cpp b/test/qt/global/test_BuildHelper.cpp deleted file mode 100644 index a7f1e43..0000000 --- a/test/qt/global/test_BuildHelper.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/*! - * \brief Unit tests for \ref BuildHelper - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "BuildHelper.h" - -#include - -using namespace governikus; - -class test_BuildHelper - : public QObject -{ - Q_OBJECT - - private Q_SLOTS: - void dateTime() - { - QVERIFY(BuildHelper::getDateTime()); - - QString buildDateTime = QLatin1String(BuildHelper::getDateTime()); - QCOMPARE(buildDateTime.size(), 22); - QVERIFY(buildDateTime.contains(__DATE__)); - } - - -}; - -QTEST_GUILESS_MAIN(test_BuildHelper) -#include "test_BuildHelper.moc" diff --git a/test/qt/global/test_CardReturnCode.cpp b/test/qt/global/test_CardReturnCode.cpp index d13a946..08cde51 100644 --- a/test/qt/global/test_CardReturnCode.cpp +++ b/test/qt/global/test_CardReturnCode.cpp @@ -5,7 +5,7 @@ */ #include "CardReturnCode.h" -#include "Result.h" +#include "ECardApiResult.h" #include #include @@ -29,14 +29,14 @@ class test_CardReturnCode void check_errorCodeToResult() { - QCOMPARE(Result(CardReturnCodeUtil::toGlobalStatus(CardReturnCode::CANCELLATION_BY_USER)).getMinor(), GlobalStatus::Code::Paos_Error_SAL_Cancellation_by_User); + QCOMPARE(ECardApiResult(CardReturnCodeUtil::toGlobalStatus(CardReturnCode::CANCELLATION_BY_USER)).getMinor(), ECardApiResult::Minor::SAL_Cancellation_by_User); } void check_errorCodeToError() { - const Result& result = Result(CardReturnCodeUtil::toGlobalStatus(CardReturnCode::CANCELLATION_BY_USER)); - QCOMPARE(result.getMinor(), GlobalStatus::Code::Paos_Error_SAL_Cancellation_by_User); + const ECardApiResult& result = ECardApiResult(CardReturnCodeUtil::toGlobalStatus(CardReturnCode::CANCELLATION_BY_USER)); + QCOMPARE(result.getMinor(), ECardApiResult::Minor::SAL_Cancellation_by_User); QCOMPARE(result.getMessage(), QString("The process was cancelled by the user.")); } diff --git a/test/qt/global/test_ECardApiResult.cpp b/test/qt/global/test_ECardApiResult.cpp new file mode 100644 index 0000000..dddc5a0 --- /dev/null +++ b/test/qt/global/test_ECardApiResult.cpp @@ -0,0 +1,332 @@ +/*! + * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "CardReturnCode.h" +#include "ECardApiResult.h" +#include "LogHandler.h" + +#include +#include + +using namespace governikus; + +class test_ECardApiResult + : public QObject +{ + Q_OBJECT + + static QByteArray bytes(const QJsonObject& pObj) + { + return QJsonDocument(pObj).toJson(QJsonDocument::Compact); + } + + + private Q_SLOTS: + void initTestCase() + { + Env::getSingleton()->init(); + } + + + void cleanup() + { + Env::getSingleton()->resetBacklog(); + } + + + void parse() + { + const auto& crap = QStringLiteral("crap"); + QCOMPARE(ECardApiResult::parseMajor(crap), ECardApiResult::Major::Unknown); + QCOMPARE(ECardApiResult::parseMinor(crap), ECardApiResult::Minor::null); + + QVERIFY(!ECardApiResult::isMajor(crap)); + QVERIFY(!ECardApiResult::isMinor(crap)); + + const auto& resultMajorOk = QStringLiteral("http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok"); + const auto& resultMinorNoPermission = QStringLiteral("http://www.bsi.bund.de/ecard/api/1.1/resultminor/al/common#noPermission"); + QCOMPARE(ECardApiResult::parseMajor(resultMajorOk), ECardApiResult::Major::Ok); + QCOMPARE(ECardApiResult::parseMinor(resultMinorNoPermission), ECardApiResult::Minor::AL_No_Permission); + + QVERIFY(ECardApiResult::isMajor(resultMajorOk)); + QVERIFY(ECardApiResult::isMinor(resultMinorNoPermission)); + + QCOMPARE(ECardApiResult::parseMinor(QString()), ECardApiResult::Minor::null); + } + + + void createInternalError() + { + ECardApiResult result = ECardApiResult(GlobalStatus::Code::Workflow_Cannot_Confirm_IdCard_Authenticity); + QCOMPARE(result.getMajor(), ECardApiResult::Major::Error); + QCOMPARE(result.getMinor(), ECardApiResult::Minor::AL_Internal_Error); + QCOMPARE(result.getMessage(), QString("The authenticity of your ID card could not be confirmed.")); + QCOMPARE(result.getMessageLang(), QString("en")); + } + + + void createOk() + { + ECardApiResult result = ECardApiResult::createOk(); + QCOMPARE(result.getMajor(), ECardApiResult::Major::Ok); + QCOMPARE(result.getMinor(), ECardApiResult::Minor::null); + QCOMPARE(result.getMessage(), QString()); + QCOMPARE(result.getMessageLang(), QString("en")); + } + + + void logStream() + { + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); + qDebug() << ECardApiResult::createOk(); + + QCOMPARE(spy.count(), 1); + auto param = spy.takeFirst(); + QVERIFY(param.at(0).toString().contains("Result: \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok | | \"")); + + spy.clear(); + + qDebug() << ECardApiResult(GlobalStatus::Code::Workflow_Cannot_Confirm_IdCard_Authenticity); + QCOMPARE(spy.count(), 1); + param = spy.takeFirst(); + QVERIFY(param.at(0).toString().contains("Result: \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#error | http://www.bsi.bund.de/ecard/api/1.1/resultminor/al/common#internalError | The authenticity of your ID card could not be confirmed.\"")); + + spy.clear(); + + qDebug() << ECardApiResult(CardReturnCodeUtil::toGlobalStatus(CardReturnCode::CANCELLATION_BY_USER)); + QCOMPARE(spy.count(), 1); + param = spy.takeFirst(); + QVERIFY(param.at(0).toString().contains("Result: \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#error | http://www.bsi.bund.de/ecard/api/1.1/resultminor/sal#cancellationByUser | The process was cancelled by the user.\"")); + + spy.clear(); + + qDebug() << ECardApiResult(CardReturnCodeUtil::toGlobalStatus(CardReturnCode::UNDEFINED)); + QCOMPARE(spy.count(), 1); + param = spy.takeFirst(); + QVERIFY(param.at(0).toString().contains("Result: \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#error | http://www.bsi.bund.de/ecard/api/1.1/resultminor/al/common#unknownError | An unexpected error has occurred during processing.\"")); + + spy.clear(); + + qDebug() << ECardApiResult(GlobalStatus::Code::Workflow_Preverification_Error); + QCOMPARE(spy.count(), 1); + param = spy.takeFirst(); + QVERIFY(param.at(0).toString().contains("Result: \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#error | http://www.bsi.bund.de/ecard/api/1.1/resultminor/il/signature#invalidCertificatePath | Pre-verification failed.\"")); + } + + + void json() + { + QByteArray expected; + + expected = "{\"major\":\"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\"}"; + QCOMPARE(bytes(ECardApiResult::createOk().toJson()), expected); + + expected = "{\"description\":\"The process was cancelled by the user.\"," + "\"language\":\"en\",\"major\":\"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#error\"," + "\"message\":\"The process was cancelled by the user.\"," + "\"minor\":\"http://www.bsi.bund.de/ecard/api/1.1/resultminor/sal#cancellationByUser\"}"; + QCOMPARE(bytes(ECardApiResult(CardReturnCodeUtil::toGlobalStatus(CardReturnCode::CANCELLATION_BY_USER)).toJson()), expected); + + expected = "{\"description\":\"A Communication error occurred during processing.\"," + "\"language\":\"en\",\"major\":\"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#error\"," + "\"message\":\"The selected card reader cannot be accessed anymore.\"," + "\"minor\":\"http://www.bsi.bund.de/ecard/api/1.1/resultminor/al/common#communicationError\"}"; + QCOMPARE(bytes(ECardApiResult(GlobalStatus::Code::Workflow_Reader_Became_Inaccessible).toJson()), expected); + } + + + void comparison() + { + QVERIFY(!(ECardApiResult::createOk() == ECardApiResult(CardReturnCodeUtil::toGlobalStatus(CardReturnCode::CANCELLATION_BY_USER)))); + + const ECardApiResult& result = ECardApiResult::createOk(); + QVERIFY(result == ECardApiResult(result.toStatus())); + } + + + void convertToStatus_data() + { + QTest::addColumn("minor"); + + const QMetaEnum& metaEnum = QMetaEnum::fromType(); + for (int i = 0; i < metaEnum.keyCount(); i++) + { + const ECardApiResult::Minor minor = static_cast(i); + const char* name = metaEnum.valueToKey(i); + + if (minor == ECardApiResult::Minor::null) + { + continue; + } + + QTest::newRow(name) << minor; + } + } + + + void convertToStatus() + { + QFETCH(ECardApiResult::Minor, minor); + const ECardApiResult result(ECardApiResult::Major::Error, minor, "Game Over :(", ECardApiResult::Origin::Client); + QVERIFY(result.toStatus().isError()); + } + + + void convertToStatusAndBackToResult_data() + { + QTest::addColumn("minor"); + + const QMetaEnum& metaEnum = QMetaEnum::fromType(); + for (int i = 0; i < metaEnum.keyCount(); i++) + { + const ECardApiResult::Minor minor = static_cast(i); + const char* name = metaEnum.valueToKey(i); + + if (minor == ECardApiResult::Minor::null) + { + continue; + } + + QTest::newRow(name) << minor; + } + } + + + void convertToStatusAndBackToResult() + { + QFETCH(ECardApiResult::Minor, minor); + switch (minor) + { + case ECardApiResult::Minor::AL_No_Permission: + case ECardApiResult::Minor::AL_Parameter_Error: + case ECardApiResult::Minor::AL_Unkown_API_Function: + case ECardApiResult::Minor::AL_Not_Initialized: + case ECardApiResult::Minor::AL_Warning_Connection_Disconnected: + case ECardApiResult::Minor::AL_Session_Terminated_Warning: + case ECardApiResult::Minor::DP_Timeout_Error: + case ECardApiResult::Minor::DP_Unknown_Channel_Handle: + case ECardApiResult::Minor::DP_Communication_Error: + case ECardApiResult::Minor::DP_Unknown_Protocol: + case ECardApiResult::Minor::DP_Unknown_Cipher_Suite: + case ECardApiResult::Minor::DP_Unknown_Webservice_Binding: + case ECardApiResult::Minor::DP_Node_Not_Reachable: + case ECardApiResult::Minor::IFDL_Timeout_Error: + case ECardApiResult::Minor::IFDL_UnknownSlot: + case ECardApiResult::Minor::IFDL_CancellationByUser: + case ECardApiResult::Minor::IFDL_InvalidSlotHandle: + case ECardApiResult::Minor::IFDL_IFD_SharingViolation: + case ECardApiResult::Minor::IFDL_Terminal_NoCard: + case ECardApiResult::Minor::IFDL_IO_RepeatedDataMismatch: + case ECardApiResult::Minor::IFDL_IO_UnknownPINFormat: + case ECardApiResult::Minor::IL_Signature_InvalidCertificatePath: + case ECardApiResult::Minor::KEY_KeyGenerationNotPossible: + case ECardApiResult::Minor::SAL_SecurityConditionNotSatisfied: + case ECardApiResult::Minor::SAL_MEAC_AgeVerificationFailedWarning: + case ECardApiResult::Minor::SAL_MEAC_CommunityVerificationFailedWarning: + case ECardApiResult::Minor::SAL_MEAC_DocumentValidityVerificationFailed: + QSKIP("No bidirection conversion for this error code intended."); + break; + + default: + break; + } + + const ECardApiResult result_1(ECardApiResult::Major::Error, minor, "Game Over :(", ECardApiResult::Origin::Client); + QVERIFY(result_1 == ECardApiResult(result_1.toStatus())); + + const ECardApiResult result_2(ECardApiResult::Major::Error, minor, "Game Over :(", ECardApiResult::Origin::Server); + QVERIFY(!(result_2 == ECardApiResult(result_2.toStatus()))); + + const ECardApiResult result_3(ECardApiResult::Major::Error, minor, ECardApiResult::getMessage(minor), ECardApiResult::Origin::Server); + QVERIFY(result_3 == ECardApiResult(result_3.toStatus())); + + QVERIFY(!(result_1 == result_2)); + } + + + void illegalInitialization() + { + ECardApiResult internalError = ECardApiResult(ECardApiResult::Major::Error, ECardApiResult::Minor::AL_Internal_Error, "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.", ECardApiResult::Origin::Client); + + QCOMPARE(ECardApiResult(ECardApiResult::Major::Error, ECardApiResult::Minor::null, "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.", ECardApiResult::Origin::Client), internalError); + QCOMPARE(ECardApiResult(ECardApiResult::Major::Ok, ECardApiResult::Minor::AL_Parameter_Error, "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.", ECardApiResult::Origin::Client), internalError); + } + + + void successfulConversionFromStatus_data() + { + QTest::addColumn("statusCode"); + + const QMetaEnum& metaEnum = QMetaEnum::fromType(); + for (int i = 0; i < metaEnum.keyCount(); i++) + { + const GlobalStatus::Code statusCode = static_cast(i); + const char* name = metaEnum.valueToKey(i); + QTest::newRow(name) << statusCode; + } + } + + + void successfulConversionFromStatus() + { + QFETCH(GlobalStatus::Code, statusCode); + + ECardApiResult::fromStatus(statusCode); + } + + + void getMessage_data() + { + QTest::addColumn("minor"); + QTest::addColumn("message"); + + QTest::newRow("unknownError") << ECardApiResult::Minor::AL_Unknown_Error << tr("An unexpected error has occurred during processing."); + QTest::newRow("noPermission") << ECardApiResult::Minor::AL_No_Permission << tr("Use of the function by the client application is not permitted."); + QTest::newRow("internalError") << ECardApiResult::Minor::AL_Internal_Error << tr("An internal error has occurred during processing."); + QTest::newRow("parameterError") << ECardApiResult::Minor::AL_Parameter_Error << tr("There was some problem with a provided or omitted parameter."); + QTest::newRow("unknownApiFunction") << ECardApiResult::Minor::AL_Unkown_API_Function << tr("The API function is unknown."); + QTest::newRow("notInitialized") << ECardApiResult::Minor::AL_Not_Initialized << tr("The framework or layer has not been initialized."); + QTest::newRow("warningDisconnected") << ECardApiResult::Minor::AL_Warning_Connection_Disconnected << tr("The active session has been terminated."); + QTest::newRow("sessionTerminated") << ECardApiResult::Minor::AL_Session_Terminated_Warning << tr("The active session has been terminated."); + QTest::newRow("communicationError") << ECardApiResult::Minor::AL_Communication_Error << tr("A Communication error occurred during processing."); + QTest::newRow("timeoutError") << ECardApiResult::Minor::DP_Timeout_Error << tr("The operation was terminated as the set time was exceeded."); + QTest::newRow("unknownChannelHandle") << ECardApiResult::Minor::DP_Unknown_Channel_Handle << tr("The operation was aborted as an invalid channel handle was used."); + QTest::newRow("dpCommunicationError") << ECardApiResult::Minor::DP_Communication_Error << tr("A Communication error occurred during processing."); + QTest::newRow("channelEstablishmentFailed") << ECardApiResult::Minor::DP_Trusted_Channel_Establishment_Failed << tr("A trusted channel could not be opened."); + QTest::newRow("unknownProtocol") << ECardApiResult::Minor::DP_Unknown_Protocol << tr("The operation was aborted as an unknown protocol was used."); + QTest::newRow("unknownCipherSuite") << ECardApiResult::Minor::DP_Unknown_Cipher_Suite << tr("The operation was aborted as an unknown cipher suite was used."); + QTest::newRow("unknownWebserviceBinding") << ECardApiResult::Minor::DP_Unknown_Webservice_Binding << tr("The operation was aborted as an unknown web service binding was used."); + QTest::newRow("NodeNotReachable") << ECardApiResult::Minor::DP_Node_Not_Reachable << tr("A Communication error occurred during processing."); + QTest::newRow("IfdlTimeoutError") << ECardApiResult::Minor::IFDL_Timeout_Error << tr("The operation was terminated as the set time was exceeded."); + QTest::newRow("terminalNoCard") << ECardApiResult::Minor::IFDL_Terminal_NoCard << tr("The card is missing or was removed."); + QTest::newRow("repeatedDataMismatch") << ECardApiResult::Minor::IFDL_IO_RepeatedDataMismatch << tr("The new PIN and the confirmation do not match."); + QTest::newRow("unknownPinFormat") << ECardApiResult::Minor::IFDL_IO_UnknownPINFormat << tr("The format of the PIN is wrong."); + QTest::newRow("keyGenerationNotPossible") << ECardApiResult::Minor::KEY_KeyGenerationNotPossible << tr("Signature certificate key generation is not possible."); + QTest::newRow("cancellationByUser") << ECardApiResult::Minor::SAL_Cancellation_by_User << tr("The process was cancelled by the user."); + QTest::newRow("invalidCertificatePath") << ECardApiResult::Minor::IL_Signature_InvalidCertificatePath << tr("One or more certificate checks failed. The operation will be aborted due to security reasons."); + QTest::newRow("invalidKey") << ECardApiResult::Minor::SAL_Invalid_Key << tr("This action cannot be performed. The online identification function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the online identification function."); + QTest::newRow("securityConditionNotSatisfied") << ECardApiResult::Minor::SAL_SecurityConditionNotSatisfied << tr("The authenticity of your ID card could not be verified. Please make sure that you are using a genuine ID card. Please note that test applications require the use of a test ID card."); + QTest::newRow("ageVerificationFailed") << ECardApiResult::Minor::SAL_MEAC_AgeVerificationFailedWarning << tr("The age verification failed."); + QTest::newRow("comunityVerificationFailed") << ECardApiResult::Minor::SAL_MEAC_CommunityVerificationFailedWarning << tr("The community verification failed."); + QTest::newRow("documentValidityVerificationFailed") << ECardApiResult::Minor::SAL_MEAC_DocumentValidityVerificationFailed << tr("The ID card is invalid or disabled."); + QTest::newRow("null") << ECardApiResult::Minor::null << QString(); + QTest::newRow("default") << ECardApiResult::Minor::IFDL_IFD_SharingViolation << QString(); + } + + + void getMessage() + { + QFETCH(ECardApiResult::Minor, minor); + QFETCH(QString, message); + + ECardApiResult result(GlobalStatus::Code::Unknown_Error); + + QCOMPARE(result.getMessage(minor), message); + } + + +}; + +QTEST_GUILESS_MAIN(test_ECardApiResult) +#include "test_ECardApiResult.moc" diff --git a/test/qt/global/test_EnumHelper.cpp b/test/qt/global/test_EnumHelper.cpp index eb8307f..06a3e20 100644 --- a/test/qt/global/test_EnumHelper.cpp +++ b/test/qt/global/test_EnumHelper.cpp @@ -19,14 +19,14 @@ const QLatin1String lineBreak("\r\n"); #else const QLatin1Char lineBreak('\n'); #endif -} +} // namespace namespace governikus { defineEnumType(TestEnum1, FIRST, SECOND, THIRD) defineEnumType(TestEnum2, FIRST = 0xff, SECOND = 0x01, THIRD = 0xaa) -} +} // namespace governikus class test_EnumHelper : public QObject @@ -36,7 +36,7 @@ class test_EnumHelper private: void testBadConverion(const int pValue, const QString& pExpectedOutput) { - QSignalSpy spy(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); TestEnum1 badEnumValue = static_cast(pValue); QCOMPARE(Enum::getName(badEnumValue), QLatin1String()); @@ -50,13 +50,13 @@ class test_EnumHelper private Q_SLOTS: void initTestCase() { - LogHandler::getInstance().init(); + Env::getSingleton()->init(); } void cleanup() { - LogHandler::getInstance().resetBacklog(); + Env::getSingleton()->resetBacklog(); } @@ -84,7 +84,7 @@ class test_EnumHelper void operatorDebug() { - QSignalSpy spy(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); qDebug() << TestEnum1::FIRST; QCOMPARE(spy.count(), 1); diff --git a/test/qt/global/test_Env.cpp b/test/qt/global/test_Env.cpp index adeb89c..7e2cac1 100644 --- a/test/qt/global/test_Env.cpp +++ b/test/qt/global/test_Env.cpp @@ -6,13 +6,20 @@ #include "Env.h" +#include "LogHandler.h" + #include +#include #include using namespace governikus; namespace { +struct TestEmptyTmp +{ +}; + class AbstractTestPodInstance { public: @@ -192,22 +199,45 @@ class TestMoveCtorAssign }; -} +class TestSingleInCtor +{ + Q_GADGET + + public: + TestSingleInCtor() + { + Env::create(); + Env::getSingleton(); + } + + + static TestSingleInCtor& getInstance() + { + static TestSingleInCtor Instance; + return Instance; + } + + +}; + +} // namespace + +Q_DECLARE_METATYPE(std::function ) namespace governikus { -template<> TestAbstractUnmanagedInstance* singleton(bool& pTakeOwnership) +template<> TestAbstractUnmanagedInstance* singleton() { - pTakeOwnership = false; static TestUnmanagedInstance instance; return &instance; } -template<> AbstractTestInstance* singleton(bool&) +template<> AbstractTestInstance* singleton() { - return new AbstractTestInstanceImpl; + static AbstractTestInstanceImpl instance; + return &instance; } @@ -230,13 +260,11 @@ template<> AbstractTestInstance* createNewObject { } - virtual QString dummy() override { return mDummy; } - }; return new tmpCtor(pStr); @@ -255,7 +283,7 @@ template<> AbstractTestPodInstance* createNewObject()->init(); + } + + void cleanup() { + Env::getSingleton()->resetBacklog(); Env::clear(); } @@ -365,43 +400,6 @@ class test_Env } - void getMockSingleton() - { - MockedAbstractTestInstance m; - Env::set(AbstractTestInstance::staticMetaObject, &m); - MockedAbstractTestInstance* mocked = Env::getSingleton(); - QVERIFY(mocked); - QCOMPARE(mocked->dummy(), QLatin1String("mocked")); - - Env::set(AbstractTestInstance::staticMetaObject); - mocked = Env::getSingleton(); - QVERIFY(!mocked); - - - Env::set(AbstractTestInstance::staticMetaObject, &m); - Env::set(AbstractTestInstance::staticMetaObject, std::make_shared()); - MockedAbstractTestInstance* mocked2 = Env::getSingleton(); - QVERIFY(mocked2); - QCOMPARE(mocked2->dummy(), QLatin1String("mocked")); - - Env::set(AbstractTestInstance::staticMetaObject); - AbstractTestInstance* orig = Env::getSingleton(); - QVERIFY(orig); - QCOMPARE(orig->dummy(), QLatin1String("impl")); - QVERIFY((!Env::getSingleton())); - - Env::set(AbstractTestInstance::staticMetaObject, std::make_shared()); - AbstractTestInstance* mocked3 = Env::getSingleton(); - QVERIFY(mocked3); - QCOMPARE(mocked3->dummy(), QLatin1String("mocked")); - - Env::set(AbstractTestInstance::staticMetaObject, std::shared_ptr()); - mocked3 = Env::getSingleton(); - QVERIFY(mocked3); - QCOMPARE(mocked3->dummy(), QLatin1String("impl")); - } - - void mockCreateNewInstance() { QScopedPointer implOrig(Env::create()); @@ -418,7 +416,6 @@ class test_Env return QStringLiteral("lambda"); } - }; return new tmp; @@ -456,13 +453,11 @@ class test_Env { } - virtual QString dummy() override { return mDummy; } - }; return new tmp(pStr); @@ -478,7 +473,6 @@ class test_Env return QStringLiteral("default"); } - }; return new tmp(); @@ -510,13 +504,11 @@ class test_Env { } - int data() { return mData; } - }; auto obj = Env::create(666); @@ -568,7 +560,6 @@ class test_Env { } - }; auto obj = Env::create(); @@ -621,6 +612,137 @@ class test_Env } + void threadSafeCheckSetCreator() + { + QVERIFY(QThreadPool::globalInstance()->maxThreadCount() > 1); + + const std::function func = [](){ + return TestEmptyTmp(); + }; + + TestMockedInstance mock; + QVector > threads; + + for (int i = 0; i < 100; ++i) + { + threads << QtConcurrent::run([func, &mock] { + for (int j = 0; j < 100; ++j) + { + Env::setCreator(func); + Env::create(); + Env::getSingleton(); + Env::getShared(); + Env::set(TestInstance::staticMetaObject, &mock); + } + }); + } + + for (auto future : qAsConst(threads)) + { + future.waitForFinished(); + } + } + + + void threadSafeDeadlockDuringCreate_data() + { + QTest::addColumn >("creator"); + + QTest::newRow("with mock") << std::function([](){ + return TestEmptyTmp(); + }); + + QTest::newRow("without mock") << std::function(); + } + + + void threadSafeDeadlockDuringCreate() + { + QFETCH(std::function, creator); + + if (creator) + { + Env::setCreator(creator); + } + + class CreateTmp + { + public: + CreateTmp() + { + Env::create(); + Env::getSingleton(); + } + + }; + + Env::create(); + + if (creator) + { + QVERIFY(Env::getCounter() > 0); + } + } + + + void checkLogForSameThreadsGadget() + { + QThread::currentThread()->setObjectName("Main"); + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); + + Env::getSingleton(); + QCOMPARE(spy.count(), 0); + + Env::getSingleton(); + QCOMPARE(spy.count(), 0); + } + + + void checkLogForDifferentThreadsGadget() + { + QThread::currentThread()->setObjectName("Main"); + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); + + Env::getSingleton(); + QCOMPARE(spy.count(), 0); + + QThreadPool pool; // do not use global one, otherwise the main thread is allowed, too + QtConcurrent::run(&pool, [] { + Env::getSingleton(); + }).waitForFinished(); + + QCOMPARE(spy.count(), 0); + } + + + void checkLogForSameThreadsQObject() + { + QThread::currentThread()->setObjectName("Main"); + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); + + Env::getSingleton(); + QCOMPARE(spy.count(), 0); + } + + + void checkLogForDifferentThreadsQObject() + { + QThread::currentThread()->setObjectName("Main"); + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); + + Env::getSingleton(); + QCOMPARE(spy.count(), 0); + + QThreadPool pool; // do not use global one, otherwise the main thead is allowed, too + QtConcurrent::run(&pool, [] { + Env::getSingleton(); + }).waitForFinished(); + + QCOMPARE(spy.count(), 1); + QVERIFY(spy.takeLast().at(0).toString().contains(QLatin1String("governikus::LogHandler was created in \"Main\" but is requested by \"Thread (pooled)\""))); + } + + }; QTEST_GUILESS_MAIN(test_Env) diff --git a/test/qt/global/test_GlobalStatus.cpp b/test/qt/global/test_GlobalStatus.cpp new file mode 100644 index 0000000..9ec3c99 --- /dev/null +++ b/test/qt/global/test_GlobalStatus.cpp @@ -0,0 +1,94 @@ +/*! + * \brief Unit tests for \ref GlobalStatus. + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "GlobalStatus.h" + +#include + +using namespace governikus; + + +class test_GlobalStatus + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void test_Is() + { + GlobalStatus status; + + QVERIFY(status.is(GlobalStatus::Code::Unknown_Error)); + QVERIFY(!status.is(GlobalStatus::Code::Downloader_Cannot_Save_File)); + } + + + void test_IsCancellationByUser() + { + GlobalStatus status1; + GlobalStatus status2(GlobalStatus::Code::Workflow_Cancellation_By_User, QStringList(), Origin::Client); + GlobalStatus status3(GlobalStatus::Code::Card_Cancellation_By_User, QStringList(), Origin::Client); + GlobalStatus status4(GlobalStatus::Code::Paos_Error_SAL_Cancellation_by_User, QStringList(), Origin::Client); + + QVERIFY(!status1.isCancellationByUser()); + QVERIFY(status2.isCancellationByUser()); + QVERIFY(status3.isCancellationByUser()); + QVERIFY(status4.isCancellationByUser()); + } + + + void test_ToDescriptionError_data() + { + QTest::addColumn("code"); + QTest::addColumn("message"); + + QTest::newRow("inProgress") << GlobalStatus::Code::Workflow_AlreadyInProgress_Error << tr("Cannot start authentication. An operation is already in progress."); + QTest::newRow("cardRemoved") << GlobalStatus::Code::Workflow_Card_Removed << tr("The ID card has been removed. The process is aborted."); + QTest::newRow("unknownPaos") << GlobalStatus::Code::Workflow_Unknown_Paos_Form_EidServer << tr("The program received an unknown message from the server."); + QTest::newRow("unexpectedMessage") << GlobalStatus::Code::Workflow_Unexpected_Message_From_EidServer << tr("The program received an unexpected message from the server."); + QTest::newRow("pinBlocked") << GlobalStatus::Code::Workflow_Pin_Blocked_And_Puk_Objectionable << tr("After three wrong entries your PIN is blocked. Please use the PIN management in this app to unblock it with the help of your PUK."); + QTest::newRow("preverificationDevelopermode") << GlobalStatus::Code::Workflow_Preverification_Developermode_Error << tr("Using the developer mode is only allowed in a test environment."); + QTest::newRow("noUniqueAtCvc") << GlobalStatus::Code::Workflow_No_Unique_AtCvc << tr("No unique AT CVC"); + QTest::newRow("noUniqueDvCvc") << GlobalStatus::Code::Workflow_No_Unique_DvCvc << tr("No unique DV CVC"); + QTest::newRow("noPermission") << GlobalStatus::Code::Workflow_No_Permission_Error << tr("Authentication failed."); + QTest::newRow("certificateNoDescription") << GlobalStatus::Code::Workflow_Certificate_No_Description << tr("No certificate description available."); + QTest::newRow("certificateNoUrl") << GlobalStatus::Code::Workflow_Certificate_No_Url_In_Description << tr("No subject url available in certificate description."); + QTest::newRow("hashError") << GlobalStatus::Code::Workflow_Certificate_Hash_Error << tr("The certificate description does not match the certificate."); + QTest::newRow("certificateSop") << GlobalStatus::Code::Workflow_Certificate_Sop_Error << tr("The subject URL in the certificate description and the TCToken URL don't satisfy the same origin policy."); + QTest::newRow("wrongParameter") << GlobalStatus::Code::Workflow_Wrong_Parameter_Invocation << tr("Application was invoked with wrong parameters."); + QTest::newRow("readerCommunication") << GlobalStatus::Code::Workflow_Reader_Communication_Error << tr("An error occurred while communicating with the card reader."); + QTest::newRow("incompleteInformation") << GlobalStatus::Code::Workflow_Server_Incomplete_Information_Provided << tr("The server provided no or incomplete information. Your personal data could not be read out."); + QTest::newRow("deviceConnection") << GlobalStatus::Code::Workflow_Reader_Device_Connection_Error << tr("An error occurred while connecting to a reader device."); + QTest::newRow("deviceScan") << GlobalStatus::Code::Workflow_Reader_Device_Scan_Error << tr("An error occurred while scanning for reader devices."); + QTest::newRow("normalClose") << GlobalStatus::Code::RemoteReader_CloseCode_NormalClose << tr("The remote reader connection was closed properly."); + QTest::newRow("abnormalClose") << GlobalStatus::Code::RemoteReader_CloseCode_AbnormalClose << tr("The remote card reader connection was not closed properly."); + QTest::newRow("closeUndefined") << GlobalStatus::Code::RemoteReader_CloseCode_Undefined << tr("Undefined error code occured when the remote card reader connection was closed."); + QTest::newRow("invalidRequest") << GlobalStatus::Code::RemoteConnector_InvalidRequest << tr("Remote reader connection request contains invalid parameters."); + QTest::newRow("emptyPassword") << GlobalStatus::Code::RemoteConnector_EmptyPassword << tr("Empty password in extended encryption of remote reader connection request."); + QTest::newRow("noSupportedApiLevel") << GlobalStatus::Code::RemoteConnector_NoSupportedApiLevel << tr("Your remote reader version is incompatible with the local version. Please install the latest AusweisApp2 version on both your smartphone and your computer."); + QTest::newRow("connectionTimeout") << GlobalStatus::Code::RemoteConnector_ConnectionTimeout << tr("A timeout occurred while trying to establish a connection to a remote reader."); + QTest::newRow("connectionError") << GlobalStatus::Code::RemoteConnector_ConnectionError << tr("An error occurred while trying to establish a connection to a remote reader."); + QTest::newRow("hostRefused") << GlobalStatus::Code::RemoteConnector_RemoteHostRefusedConnection << tr("Remote device has rejected the connection. Please check the pairing code."); + QTest::newRow("cannotSaveFile") << GlobalStatus::Code::Downloader_Cannot_Save_File << tr("Cannot save file."); + QTest::newRow("dataCorrupted") << GlobalStatus::Code::Downloader_Data_Corrupted << tr("Received data were corrupted."); + } + + + void test_ToDescriptionError() + { + QFETCH(GlobalStatus::Code, code); + QFETCH(QString, message); + + GlobalStatus status(code, QStringList(), Origin::Client); + + QCOMPARE(status.toErrorDescription(false), message); + } + + +}; + +QTEST_GUILESS_MAIN(test_GlobalStatus) +#include "test_GlobalStatus.moc" diff --git a/test/qt/global/test_LanguageLoader.cpp b/test/qt/global/test_LanguageLoader.cpp index 96f11a7..6abd8a1 100644 --- a/test/qt/global/test_LanguageLoader.cpp +++ b/test/qt/global/test_LanguageLoader.cpp @@ -6,7 +6,6 @@ #include "LanguageLoader.h" -#include "FileDestination.h" #include "LogHandler.h" #include "TestFileHelper.h" @@ -24,7 +23,7 @@ class test_LanguageLoader private Q_SLOTS: void initTestCase() { - LogHandler::getInstance().init(); + Env::getSingleton()->init(); TestFileHelper::createTranslations(mTranslationDir.path()); LanguageLoader::getInstance().setPath(mTranslationDir.path()); } @@ -36,7 +35,7 @@ class test_LanguageLoader { LanguageLoader::getInstance().unload(); } - LogHandler::getInstance().resetBacklog(); + Env::getSingleton()->resetBacklog(); } @@ -50,7 +49,7 @@ class test_LanguageLoader void loadTwice() { - QSignalSpy spy(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); QVERIFY(!LanguageLoader::getInstance().isLoaded()); LanguageLoader::getInstance().load(QLocale::German); @@ -133,8 +132,7 @@ class test_LanguageLoader QLocale::Language lang = LanguageLoader::getInstance().getFallbackLanguage(); QCOMPARE(lang, QLocale::English); - - QSignalSpy spy(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); LanguageLoader::getInstance().load(QLocale::English); QVERIFY(spy.count() > 0); diff --git a/test/qt/global/test_LogHandler.cpp b/test/qt/global/test_LogHandler.cpp index 98108df..b58ba54 100644 --- a/test/qt/global/test_LogHandler.cpp +++ b/test/qt/global/test_LogHandler.cpp @@ -8,6 +8,12 @@ #include +#ifndef Q_OS_WIN +#include +#include +#include +#endif + using namespace governikus; class test_LogHandler @@ -15,16 +21,36 @@ class test_LogHandler { Q_OBJECT + void fakeLastModifiedAndLastAccessTime(const QString& pPath) + { + #ifdef Q_OS_WIN + Q_UNUSED(pPath); + #else + struct timeval tv[2]; + + struct timeval& accessTime = tv[0]; + gettimeofday(&accessTime, nullptr); + + struct timeval& modifyTime = tv[1]; + gettimeofday(&modifyTime, nullptr); + + time_t fiveteenDays = 60 * 60 * 24 * 15; + modifyTime.tv_sec -= fiveteenDays; + utimes(pPath.toLatin1().constData(), tv); + #endif + } + + private Q_SLOTS: void initTestCase() { - LogHandler::getInstance().init(); + Env::getSingleton()->init(); } void cleanup() { - LogHandler::getInstance().resetBacklog(); + Env::getSingleton()->resetBacklog(); } @@ -36,33 +62,32 @@ class test_LogHandler } - void backlog() + void checkBacklog() { - LogHandler::getInstance().resetBacklog(); - QByteArray blog = LogHandler::getInstance().getBacklog(); + Env::getSingleton()->resetBacklog(); + QByteArray blog = Env::getSingleton()->getBacklog(); QCOMPARE(blog.size(), 0); QByteArray msg("dummy message with some useless information ... 123456"); qDebug() << msg; - blog = LogHandler::getInstance().getBacklog(); + blog = Env::getSingleton()->getBacklog(); QVERIFY(blog.size() > 0); QVERIFY(blog.contains(msg)); - LogHandler::getInstance().resetBacklog(); - blog = LogHandler::getInstance().getBacklog(); + Env::getSingleton()->resetBacklog(); + blog = Env::getSingleton()->getBacklog(); QCOMPARE(blog.size(), 0); } void fireLog() { - QSignalSpy spy(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); qDebug() << "hallo"; qDebug() << "test nachricht"; - QCOMPARE(spy.count(), 2); auto param1 = spy.takeFirst(); auto param2 = spy.takeLast(); @@ -80,27 +105,118 @@ class test_LogHandler void otherLogFilesWithoutCurrent() { - auto list = LogHandler::getInstance().getOtherLogfiles(); - QVERIFY(!list.contains(LogHandler::getInstance().mLogFile)); + auto list = Env::getSingleton()->getOtherLogfiles(); + QVERIFY(!list.contains(Env::getSingleton()->mLogFile)); } void debugStream() { - QSignalSpy spy(&LogHandler::getInstance(), &LogHandler::fireLog); - qDebug() << LogHandler::getInstance(); + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); + qDebug() << *Env::getSingleton(); QCOMPARE(spy.count(), 1); auto param = spy.takeFirst(); - QVERIFY(param.at(0).toString().contains(LogHandler::getInstance().mLogFile.fileName())); + QVERIFY(param.at(0).toString().contains(Env::getSingleton()->mLogFile.fileName())); } void copyFile() { qDebug() << "dummy"; - QVERIFY(!LogHandler::getInstance().copy(QString())); - QVERIFY(!LogHandler::getInstance().copy(QStringLiteral(" "))); + QVERIFY(!Env::getSingleton()->copy(QString())); + QVERIFY(!Env::getSingleton()->copy(QStringLiteral(" "))); + } + + + void initReset() + { + QVERIFY(Env::getSingleton()->isInitialized()); + + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); + qDebug() << "dummy"; + QCOMPARE(spy.count(), 1); + const auto& backlog = Env::getSingleton()->getBacklog(); + QVERIFY(!backlog.isEmpty()); + spy.clear(); + + Env::getSingleton()->reset(); + QVERIFY(!Env::getSingleton()->isInitialized()); + qDebug() << "dummy"; + QCOMPARE(spy.count(), 0); + QCOMPARE(Env::getSingleton()->getBacklog(), backlog); + + Env::getSingleton()->init(); + QVERIFY(Env::getSingleton()->isInitialized()); + qDebug() << "dummy"; + QCOMPARE(spy.count(), 1); + QVERIFY(Env::getSingleton()->getBacklog().size() > backlog.size()); + } + + + void useLogfile() + { + const auto& logger = Env::getSingleton(); + logger->resetBacklog(); + QVERIFY(logger->useLogfile()); + + // will backlog + qDebug() << "dummy"; + QVERIFY(logger->getBacklog().contains(QByteArrayLiteral("dummy"))); + + // enable already enabled one + logger->setLogfile(true); + QVERIFY(logger->useLogfile()); + QVERIFY(logger->getBacklog().contains(QByteArrayLiteral("dummy"))); + + // disable it + logger->setLogfile(false); + qDebug() << "another dummy"; + QVERIFY(!logger->useLogfile()); + QVERIFY(logger->getBacklog().isNull()); + QVERIFY(!logger->getCurrentLogfileDate().isValid()); + logger->resetBacklog(); + QVERIFY(logger->getBacklog().isNull()); + + // disable already disabled one + logger->setLogfile(false); + QVERIFY(!logger->useLogfile()); + QVERIFY(logger->getBacklog().isNull()); + QVERIFY(!logger->getCurrentLogfileDate().isValid()); + + // enable again + logger->setLogfile(true); + QVERIFY(logger->getBacklog().isEmpty()); + qDebug() << "another yummy"; + QVERIFY(logger->useLogfile()); + QVERIFY(logger->getBacklog().contains(QByteArrayLiteral("another yummy"))); + QVERIFY(logger->getCurrentLogfileDate().isValid()); + } + + + void removeUpOldLogfiles() + { + #ifdef Q_OS_WIN + QSKIP("File time stamp mocking unimplemented on windows"); + #endif + + const auto& logger = Env::getSingleton(); + + const auto& initialFiles = logger->getOtherLogfiles(); + QTemporaryFile tmp(LogHandler::getLogFileTemplate()); + QVERIFY(tmp.open()); + tmp.fileName(); // touch it + const auto& filesWithMock = logger->getOtherLogfiles(); + QVERIFY(filesWithMock.size() > initialFiles.size()); + + logger->removeOldLogfiles(); + QVERIFY(tmp.exists()); + QCOMPARE(filesWithMock.size(), logger->getOtherLogfiles().size()); + + fakeLastModifiedAndLastAccessTime(tmp.fileName()); + logger->removeOldLogfiles(); + QCOMPARE(initialFiles.size(), logger->getOtherLogfiles().size()); + QVERIFY(!tmp.exists()); } diff --git a/test/qt/global/test_ResourceLoader.cpp b/test/qt/global/test_ResourceLoader.cpp index d81e563..73997b1 100644 --- a/test/qt/global/test_ResourceLoader.cpp +++ b/test/qt/global/test_ResourceLoader.cpp @@ -7,7 +7,6 @@ #include "ResourceLoader.h" #include "LogHandler.h" -#include "TestFileHelper.h" #include @@ -21,7 +20,7 @@ class test_ResourceLoader private Q_SLOTS: void initTestCase() { - LogHandler::getInstance().init(); + Env::getSingleton()->init(); } @@ -31,13 +30,13 @@ class test_ResourceLoader { ResourceLoader::getInstance().shutdown(); } - LogHandler::getInstance().resetBacklog(); + Env::getSingleton()->resetBacklog(); } void initAndLog() { - QSignalSpy spy(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); QVERIFY(!ResourceLoader::getInstance().isLoaded()); ResourceLoader::getInstance().init(); @@ -52,7 +51,7 @@ class test_ResourceLoader void initAndShutdown() { - QSignalSpy spy(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); QVERIFY(!ResourceLoader::getInstance().isLoaded()); ResourceLoader::getInstance().init(); diff --git a/test/qt/global/test_ScopeGuard.cpp b/test/qt/global/test_ScopeGuard.cpp index e03d7dc..4ada370 100644 --- a/test/qt/global/test_ScopeGuard.cpp +++ b/test/qt/global/test_ScopeGuard.cpp @@ -20,13 +20,13 @@ class test_ScopeGuard private Q_SLOTS: void initTestCase() { - LogHandler::getInstance().init(); + Env::getSingleton()->init(); } void cleanup() { - LogHandler::getInstance().resetBacklog(); + Env::getSingleton()->resetBacklog(); } @@ -100,7 +100,7 @@ class test_ScopeGuard void emptyFunc() { - QSignalSpy spy(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); const std::function func; diff --git a/test/qt/global/test_VersionInfo.cpp b/test/qt/global/test_VersionInfo.cpp index 59f9884..67e8568 100644 --- a/test/qt/global/test_VersionInfo.cpp +++ b/test/qt/global/test_VersionInfo.cpp @@ -22,13 +22,13 @@ class test_VersionInfo { QCoreApplication::setOrganizationName(QStringLiteral("Governikus GmbH & Co. KG")); QCoreApplication::setApplicationVersion(QStringLiteral("x.y.z")); - LogHandler::getInstance().init(); + Env::getSingleton()->init(); } void cleanup() { - LogHandler::getInstance().resetBacklog(); + Env::getSingleton()->resetBacklog(); } @@ -111,7 +111,7 @@ class test_VersionInfo void logging() { auto versionInfo = VersionInfo::getInstance(); - QSignalSpy spy(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); qDebug() << versionInfo; diff --git a/test/qt/global/test_VersionNumber.cpp b/test/qt/global/test_VersionNumber.cpp index bb8d793..c871b6b 100644 --- a/test/qt/global/test_VersionNumber.cpp +++ b/test/qt/global/test_VersionNumber.cpp @@ -35,7 +35,6 @@ class test_VersionNumber VersionNumber number6(""); QVERIFY(number1 != number6); - VersionNumber number7("1.12.0+123-default-abc123"); VersionNumber number8("1.12.0+123-default-abc123"); QVERIFY(number7 == number8); diff --git a/test/qt/global/test_result.cpp b/test/qt/global/test_result.cpp deleted file mode 100644 index 4f3a58e..0000000 --- a/test/qt/global/test_result.cpp +++ /dev/null @@ -1,190 +0,0 @@ -/*! - * \brief Unit tests for \ref result - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "CardReturnCode.h" -#include "LogHandler.h" -#include "Result.h" - -#include -#include - -using namespace governikus; - -class test_result - : public QObject -{ - Q_OBJECT - - static QByteArray bytes(const QJsonObject& pObj) - { - return QJsonDocument(pObj).toJson(QJsonDocument::Compact); - } - - - private Q_SLOTS: - void initTestCase() - { - LogHandler::getInstance().init(); - } - - - void cleanup() - { - LogHandler::getInstance().resetBacklog(); - } - - - void parse() - { - QCOMPARE(Result::parseMajor("crap"), Result::Major::Unknown); - QCOMPARE(Result::parseMinor("crap"), GlobalStatus::Code::Unknown_Error); - - QVERIFY(!Result::isMajor("crap")); - QVERIFY(!Result::isMinor("crap")); - - QCOMPARE(Result::parseMajor("http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok"), Result::Major::Ok); - QCOMPARE(Result::parseMinor("http://www.bsi.bund.de/ecard/api/1.1/resultminor/al/common#noPermission"), - GlobalStatus::Code::Paos_Error_AL_No_Permission); - - QVERIFY(Result::isMajor("http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok")); - QVERIFY(Result::isMinor("http://www.bsi.bund.de/ecard/api/1.1/resultminor/al/common#noPermission")); - - QCOMPARE(Result::parseMinor(QString()), GlobalStatus::Code::No_Error); - QVERIFY(Result::isMinor(QString())); - } - - - void createInternalError() - { - Result result = Result(GlobalStatus::Code::Workflow_Cannot_Confirm_IdCard_Authenticity); - QCOMPARE(result.getMajor(), Result::Major::Error); - QCOMPARE(result.getMinor(), GlobalStatus::Code::Paos_Error_AL_Internal_Error); - QCOMPARE(result.getMessage(), QString("The authenticity of your ID card could not be confirmed.")); - QCOMPARE(result.getMessageLang(), QString("en")); - } - - - void createOk() - { - Result result = Result::createOk(); - QCOMPARE(result.getMajor(), Result::Major::Ok); - QCOMPARE(result.getMinor(), GlobalStatus::Code::No_Error); - QCOMPARE(result.getMessage(), QString()); - QCOMPARE(result.getMessageLang(), QString("en")); - } - - - void logStream() - { - QSignalSpy spy(&LogHandler::getInstance(), &LogHandler::fireLog); - qDebug() << Result::createOk(); - - QCOMPARE(spy.count(), 1); - auto param = spy.takeFirst(); - QVERIFY(param.at(0).toString().contains("Result: \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok | | \"")); - - spy.clear(); - - qDebug() << Result(GlobalStatus::Code::Workflow_Cannot_Confirm_IdCard_Authenticity); - QCOMPARE(spy.count(), 1); - param = spy.takeFirst(); - QVERIFY(param.at(0).toString().contains("Result: \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#error | http://www.bsi.bund.de/ecard/api/1.1/resultminor/al/common#internalError | The authenticity of your ID card could not be confirmed.\"")); - - spy.clear(); - - qDebug() << Result(CardReturnCodeUtil::toGlobalStatus(CardReturnCode::CANCELLATION_BY_USER)); - QCOMPARE(spy.count(), 1); - param = spy.takeFirst(); - QVERIFY(param.at(0).toString().contains("Result: \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#error | http://www.bsi.bund.de/ecard/api/1.1/resultminor/sal#cancellationByUser | The process was cancelled by the user.\"")); - - spy.clear(); - - qDebug() << Result(CardReturnCodeUtil::toGlobalStatus(CardReturnCode::UNDEFINED)); - QCOMPARE(spy.count(), 1); - param = spy.takeFirst(); - QVERIFY(param.at(0).toString().contains("Result: \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#error | http://www.bsi.bund.de/ecard/api/1.1/resultminor/al/common#unknownError | An unexpected error has occurred during processing.\"")); - - spy.clear(); - - qDebug() << Result(GlobalStatus::Code::Workflow_Preverification_Error); - QCOMPARE(spy.count(), 1); - param = spy.takeFirst(); - QVERIFY(param.at(0).toString().contains("Result: \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#error | http://www.bsi.bund.de/ecard/api/1.1/resultminor/sal#certificateChainInterrupted | Pre-verification failed.\"")); - } - - - void json() - { - QByteArray expected; - - expected = "{\"major\":\"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\"}"; - QCOMPARE(bytes(Result::createOk().toJson()), expected); - - expected = "{\"description\":\"The process was cancelled by the user.\"," - "\"language\":\"en\",\"major\":\"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#error\"," - "\"message\":\"The process was cancelled by the user.\"," - "\"minor\":\"http://www.bsi.bund.de/ecard/api/1.1/resultminor/sal#cancellationByUser\"}"; - QCOMPARE(bytes(Result(CardReturnCodeUtil::toGlobalStatus(CardReturnCode::CANCELLATION_BY_USER)).toJson()), expected); - - expected = "{\"description\":\"A Communication error occurred during processing.\"," - "\"language\":\"en\",\"major\":\"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#error\"," - "\"message\":\"The selected card reader cannot be accessed anymore.\"," - "\"minor\":\"http://www.bsi.bund.de/ecard/api/1.1/resultminor/al/common#communicationError\"}"; - QCOMPARE(bytes(Result(GlobalStatus::Code::Workflow_Reader_Became_Inaccessible).toJson()), expected); - } - - - void comparison() - { - QVERIFY(!(Result::createOk() == Result(CardReturnCodeUtil::toGlobalStatus(CardReturnCode::CANCELLATION_BY_USER)))); - - const Result& result = Result::createOk(); - QVERIFY(result == Result(result.toStatus())); - } - - - void conversion_data() - { - QTest::addColumn("minor"); - - const QMetaEnum& metaEnum = QMetaEnum::fromType(); - for (int i = 0; i < metaEnum.keyCount(); i++) - { - const GlobalStatus::Code minor = static_cast(i); - const char* name = metaEnum.valueToKey(i); - const QString check = QString::fromStdString(name); - - if (minor == GlobalStatus::Code::Paos_Unexpected_Warning || !check.startsWith(QLatin1String("Paos_"))) - { - continue; - } - - QTest::newRow(name) << minor; - } - } - - - void conversion() - { - QFETCH(GlobalStatus::Code, minor); - - const Result result_1(Result::Major::Error, minor, "Game Over :(", Origin::Client); - QVERIFY(result_1 == Result(result_1.toStatus())); - - const Result result_2(Result::Major::Error, minor, "Game Over :(", Origin::Server); - QVERIFY(!(result_2 == Result(result_2.toStatus()))); - - const Result result_3(Result::Major::Error, minor, Result::getMessage(minor), Origin::Server); - QVERIFY(result_3 == Result(result_3.toStatus())); - - QVERIFY(!(result_1 == result_2)); - } - - -}; - -QTEST_GUILESS_MAIN(test_result) -#include "test_result.moc" diff --git a/test/qt/jsonapi/test_Message.cpp b/test/qt/jsonapi/test_Message.cpp index 1f59635..7de4016 100644 --- a/test/qt/jsonapi/test_Message.cpp +++ b/test/qt/jsonapi/test_Message.cpp @@ -97,12 +97,13 @@ class test_Message } - void processStateEstablishPacePin() + void processStateEnterPacePassword() { MessageDispatcher dispatcher; dispatcher.init(QSharedPointer(new WorkflowContext())); - const auto& msg = dispatcher.processStateChange(QStringLiteral("StateEstablishPacePin")); + dispatcher.mContext.getWorkflowContext()->setEstablishPaceChannelType(PacePasswordId::PACE_PIN); + const auto& msg = dispatcher.processStateChange(QStringLiteral("StateEnterPacePassword")); QCOMPARE(msg, QByteArray("{\"msg\":\"ENTER_PIN\"}")); } @@ -125,6 +126,7 @@ class test_Message MessageDispatcher dispatcher; dispatcher.init(context); + context->setReaderName("dummy"); QVERIFY(!context->isStateApproved()); QCOMPARE(dispatcher.processStateChange(QStringLiteral("SomeUnknownState")), QByteArray()); QVERIFY(context->isStateApproved()); @@ -134,7 +136,8 @@ class test_Message auto expectedBadState = QByteArray("{\"error\":\"SET_CAN\",\"msg\":\"BAD_STATE\"}"); QCOMPARE(dispatcher.processCommand(msg), expectedBadState); - QVERIFY(!dispatcher.processStateChange(QStringLiteral("StateEstablishPaceCan")).isEmpty()); + dispatcher.mContext.getWorkflowContext()->setEstablishPaceChannelType(PacePasswordId::PACE_CAN); + QVERIFY(!dispatcher.processStateChange(QStringLiteral("StateEnterPacePassword")).isEmpty()); QVERIFY(!context->isStateApproved()); auto expectedEnterCan = QByteArray("{\"error\":\"You must provide 6 digits\",\"msg\":\"ENTER_CAN\"}"); diff --git a/test/qt/jsonapi/test_MsgHandler.cpp b/test/qt/jsonapi/test_MsgHandler.cpp index 1e57994..83ef284 100644 --- a/test/qt/jsonapi/test_MsgHandler.cpp +++ b/test/qt/jsonapi/test_MsgHandler.cpp @@ -29,7 +29,7 @@ class MsgTest }; -} +} // namespace class test_MsgHandler diff --git a/test/qt/jsonapi/test_MsgHandlerAuth.cpp b/test/qt/jsonapi/test_MsgHandlerAuth.cpp index 2a01a98..14b3a5a 100644 --- a/test/qt/jsonapi/test_MsgHandlerAuth.cpp +++ b/test/qt/jsonapi/test_MsgHandlerAuth.cpp @@ -27,7 +27,6 @@ class test_MsgHandlerAuth MessageDispatcher dispatcher; QByteArray msg("{\"cmd\": \"RUN_AUTH\"}"); QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"error\":\"tcTokenURL cannot be undefined\",\"msg\":\"AUTH\"}")); - } diff --git a/test/qt/jsonapi/test_MsgHandlerCertificate.cpp b/test/qt/jsonapi/test_MsgHandlerCertificate.cpp index 8c55fce..9e9aca4 100644 --- a/test/qt/jsonapi/test_MsgHandlerCertificate.cpp +++ b/test/qt/jsonapi/test_MsgHandlerCertificate.cpp @@ -8,7 +8,6 @@ #include "InternalActivationContext.h" #include "MessageDispatcher.h" -#include "paos/retrieve/DidAuthenticateEac1Parser.h" #include "TestAuthContext.h" #include diff --git a/test/qt/jsonapi/test_MsgHandlerEnterCan.cpp b/test/qt/jsonapi/test_MsgHandlerEnterCan.cpp index 0223f0a..a6903f5 100644 --- a/test/qt/jsonapi/test_MsgHandlerEnterCan.cpp +++ b/test/qt/jsonapi/test_MsgHandlerEnterCan.cpp @@ -6,12 +6,15 @@ #include "messages/MsgHandlerEnterCan.h" +#include "MsgHandlerEnterPassword.h" + #include "MessageDispatcher.h" -#include "MockReaderManagerPlugIn.h" #include "ReaderManager.h" +#include "MockReaderManagerPlugIn.h" + #include -#include +#include Q_IMPORT_PLUGIN(MockReaderManagerPlugIn) @@ -22,32 +25,33 @@ class test_MsgHandlerEnterCan { Q_OBJECT - static void setValidCanState(MessageDispatcher& pDispatcher, const QString& pState = QStringLiteral("StateEstablishPaceCan")) + static void setValidCanState(MessageDispatcher& pDispatcher, + bool pSelectReader = true, + bool pBasicReader = true, + const PacePasswordId pPasswordID = PacePasswordId::PACE_CAN) { - QSharedPointer context(new WorkflowContext()); - pDispatcher.init(context); - - QByteArray expected; - if (pState == QLatin1String("StateEstablishPaceCan")) - { - expected = "{\"msg\":\"ENTER_CAN\"}"; - } - - QCOMPARE(pDispatcher.processStateChange(pState), expected); + setValidState(pDispatcher, pSelectReader, pBasicReader, pPasswordID); } private Q_SLOTS: void initTestCase() { - ReaderManager::getInstance().init(); - ReaderManager::getInstance().getPlugInInfos(); // just to wait until initialization finished + const auto readerManager = Env::getSingleton(); + readerManager->init(); + readerManager->getPlugInInfos(); // just to wait until initialization finished } void cleanupTestCase() { - ReaderManager::getInstance().shutdown(); + Env::getSingleton()->shutdown(); + } + + + void cleanup() + { + MockReaderManagerPlugIn::getInstance().removeAllReader(); } @@ -56,8 +60,8 @@ class test_MsgHandlerEnterCan MessageDispatcher dispatcher; setValidCanState(dispatcher); - QByteArray msg("{\"cmd\": \"SET_CAN\"}"); - QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"error\":\"Value cannot be undefined\",\"msg\":\"ENTER_CAN\"}")); + const QByteArray msg(R"({"cmd": "SET_CAN"})"); + QCOMPARE(dispatcher.processCommand(msg), addReaderData(R"({"error":"Value cannot be undefined","msg":"ENTER_CAN"})")); } @@ -66,8 +70,8 @@ class test_MsgHandlerEnterCan MessageDispatcher dispatcher; setValidCanState(dispatcher); - QByteArray msg("{\"cmd\": \"SET_CAN\", \"value\": 123456}"); - QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"error\":\"Invalid value\",\"msg\":\"ENTER_CAN\"}")); + const QByteArray msg(R"({"cmd": "SET_CAN", "value": 123456})"); + QCOMPARE(dispatcher.processCommand(msg), addReaderData(R"({"error":"Invalid value","msg":"ENTER_CAN"})")); } @@ -76,23 +80,23 @@ class test_MsgHandlerEnterCan MessageDispatcher dispatcher; setValidCanState(dispatcher); - QByteArray msg("{\"cmd\": \"SET_CAN\", \"value\": \"12345\"}"); - QByteArray expected("{\"error\":\"You must provide 6 digits\",\"msg\":\"ENTER_CAN\"}"); + QByteArray msg(R"({"cmd": "SET_CAN", "value": "12345"})"); + const QByteArray expected(addReaderData(R"({"error":"You must provide 6 digits","msg":"ENTER_CAN"})")); QCOMPARE(dispatcher.processCommand(msg), expected); - msg = "{\"cmd\": \"SET_CAN\", \"value\": \"1234567\"}"; + msg = R"({"cmd": "SET_CAN", "value": "1234567"})"; QCOMPARE(dispatcher.processCommand(msg), expected); - msg = "{\"cmd\": \"SET_CAN\", \"value\": \"abcdef\"}"; + msg = R"({"cmd": "SET_CAN", "value": "abcdef"})"; QCOMPARE(dispatcher.processCommand(msg), expected); - msg = "{\"cmd\": \"SET_CAN\", \"value\": \"\"}"; + msg = R"({"cmd": "SET_CAN", "value": ""})"; QCOMPARE(dispatcher.processCommand(msg), expected); - msg = "{\"cmd\": \"SET_CAN\", \"value\": \"123456a\"}"; + msg = R"({"cmd": "SET_CAN", "value": "123456a"})"; QCOMPARE(dispatcher.processCommand(msg), expected); - msg = "{\"cmd\": \"SET_CAN\", \"value\": \"12x456\"}"; + msg = R"({"cmd": "SET_CAN", "value": "12x456"})"; QCOMPARE(dispatcher.processCommand(msg), expected); } @@ -100,13 +104,13 @@ class test_MsgHandlerEnterCan void badState() { MessageDispatcher dispatcher; - setValidCanState(dispatcher, QStringLiteral("invalid")); + setValidCanState(dispatcher, true, true, PacePasswordId::UNKNOWN); - QByteArray msg("{\"cmd\": \"SET_CAN\", \"value\": \"12345\"}"); - QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"error\":\"SET_CAN\",\"msg\":\"BAD_STATE\"}")); + QByteArray msg(R"({"cmd": "SET_CAN", "value": "12345"})"); + QCOMPARE(dispatcher.processCommand(msg), QByteArray(R"({"error":"SET_CAN","msg":"BAD_STATE"})")); - msg = "{\"cmd\": \"SET_CAN\", \"value\": \"123456\"}"; - QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"error\":\"SET_CAN\",\"msg\":\"BAD_STATE\"}")); + msg = R"({"cmd": "SET_CAN", "value": "123456"})"; + QCOMPARE(dispatcher.processCommand(msg), QByteArray(R"({"error":"SET_CAN","msg":"BAD_STATE"})")); } @@ -115,30 +119,26 @@ class test_MsgHandlerEnterCan MessageDispatcher dispatcher; setValidCanState(dispatcher); - QByteArray msg("{\"cmd\": \"SET_CAN\", \"value\": \"123456\"}"); + const QByteArray msg(R"({"cmd": "SET_CAN", "value": "123456"})"); QCOMPARE(dispatcher.processCommand(msg), QByteArray()); } - void readerInfo() + void pinPadReader() { - MockReader* reader = MockReaderManagerPlugIn::getInstance().addReader("MockReader CARD"); - reader->setCard(MockCardConfig()); - - QSharedPointer context(new WorkflowContext()); MessageDispatcher dispatcher; - dispatcher.init(context); + setValidCanState(dispatcher, true, false); - context->setReaderName("MockReader"); - QCOMPARE(dispatcher.processStateChange("StateEstablishPaceCan"), QByteArray("{\"msg\":\"ENTER_CAN\"}")); - QByteArray msg = "{\"cmd\": \"SET_CAN\", \"value\": \"54321\"}"; - QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"error\":\"You must provide 6 digits\",\"msg\":\"ENTER_CAN\"}")); + QByteArray msg(R"({"cmd": "SET_CAN", "value": "111111"})"); + QByteArray expected(addReaderData(R"({"error":"Value cannot be defined","msg":"ENTER_CAN"})", true)); + QCOMPARE(dispatcher.processCommand(msg), expected); + msg = QByteArray(R"({"cmd": "SET_CAN", "value": ""})"); + expected = QByteArray(addReaderData(R"({"error":"Value cannot be defined","msg":"ENTER_CAN"})", true)); + QCOMPARE(dispatcher.processCommand(msg), expected); - context->setReaderName("MockReader CARD"); - QCOMPARE(dispatcher.processStateChange("StateEstablishPaceCan"), QByteArray("{\"msg\":\"ENTER_CAN\",\"reader\":{\"attached\":true,\"card\":{\"deactivated\":false,\"inoperative\":false,\"retryCounter\":-1},\"name\":\"MockReader CARD\"}}")); - msg = "{\"cmd\": \"SET_CAN\", \"value\": \"54321\"}"; - QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"error\":\"You must provide 6 digits\",\"msg\":\"ENTER_CAN\",\"reader\":{\"attached\":true,\"card\":{\"deactivated\":false,\"inoperative\":false,\"retryCounter\":-1},\"name\":\"MockReader CARD\"}}")); + msg = QByteArray(R"({"cmd": "SET_CAN"})"); + QCOMPARE(dispatcher.processCommand(msg), QByteArray()); } diff --git a/test/qt/jsonapi/test_MsgHandlerEnterPin.cpp b/test/qt/jsonapi/test_MsgHandlerEnterPin.cpp index dcf56b9..7abdd67 100644 --- a/test/qt/jsonapi/test_MsgHandlerEnterPin.cpp +++ b/test/qt/jsonapi/test_MsgHandlerEnterPin.cpp @@ -6,12 +6,15 @@ #include "messages/MsgHandlerEnterPin.h" +#include "MsgHandlerEnterPassword.h" + #include "MessageDispatcher.h" -#include "MockReaderManagerPlugIn.h" #include "ReaderManager.h" +#include "MockReaderManagerPlugIn.h" + #include -#include +#include Q_IMPORT_PLUGIN(MockReaderManagerPlugIn) @@ -22,32 +25,43 @@ class test_MsgHandlerEnterPin { Q_OBJECT - static void setValidPinState(MessageDispatcher& pDispatcher, const QString& pState = QStringLiteral("StateEstablishPacePin")) + static void setValidPinState(MessageDispatcher& pDispatcher, + bool pSelectReader = true, + bool pBasicReader = true, + const PacePasswordId pPasswordID = PacePasswordId::PACE_PIN) { - QSharedPointer context(new WorkflowContext()); - pDispatcher.init(context); - - QByteArray expected; - if (pState == QLatin1String("StateEstablishPacePin")) - { - expected = "{\"msg\":\"ENTER_PIN\"}"; - } - - QCOMPARE(pDispatcher.processStateChange(pState), expected); + setValidState(pDispatcher, pSelectReader, pBasicReader, pPasswordID); } private Q_SLOTS: void initTestCase() { - ReaderManager::getInstance().init(); - ReaderManager::getInstance().getPlugInInfos(); // just to wait until initialization finished + const auto readerManager = Env::getSingleton(); + readerManager->init(); + readerManager->getPlugInInfos(); // just to wait until initialization finished } void cleanupTestCase() { - ReaderManager::getInstance().shutdown(); + Env::getSingleton()->shutdown(); + } + + + void cleanup() + { + MockReaderManagerPlugIn::getInstance().removeAllReader(); + } + + + void notInserted() + { + MessageDispatcher dispatcher; + setValidPinState(dispatcher, false); + + const QByteArray msg(R"({"cmd": "SET_PIN"})"); + QCOMPARE(dispatcher.processCommand(msg), QByteArray(R"({"error":"No card inserted","msg":"ENTER_PIN"})")); } @@ -56,8 +70,8 @@ class test_MsgHandlerEnterPin MessageDispatcher dispatcher; setValidPinState(dispatcher); - QByteArray msg("{\"cmd\": \"SET_PIN\"}"); - QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"error\":\"Value cannot be undefined\",\"msg\":\"ENTER_PIN\"}")); + const QByteArray msg(R"({"cmd": "SET_PIN"})"); + QCOMPARE(dispatcher.processCommand(msg), addReaderData(R"({"error":"Value cannot be undefined","msg":"ENTER_PIN"})")); } @@ -66,8 +80,8 @@ class test_MsgHandlerEnterPin MessageDispatcher dispatcher; setValidPinState(dispatcher); - QByteArray msg("{\"cmd\": \"SET_PIN\", \"value\": 123456}"); - QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"error\":\"Invalid value\",\"msg\":\"ENTER_PIN\"}")); + const QByteArray msg(R"({"cmd": "SET_PIN", "value": 123456})"); + QCOMPARE(dispatcher.processCommand(msg), addReaderData(R"({"error":"Invalid value","msg":"ENTER_PIN"})")); } @@ -76,23 +90,23 @@ class test_MsgHandlerEnterPin MessageDispatcher dispatcher; setValidPinState(dispatcher); - QByteArray msg("{\"cmd\": \"SET_PIN\", \"value\": \"12345\"}"); - QByteArray expected("{\"error\":\"You must provide 6 digits\",\"msg\":\"ENTER_PIN\"}"); + QByteArray msg(R"({"cmd": "SET_PIN", "value": "12345"})"); + const QByteArray expected(addReaderData(R"({"error":"You must provide 6 digits","msg":"ENTER_PIN"})")); QCOMPARE(dispatcher.processCommand(msg), expected); - msg = "{\"cmd\": \"SET_PIN\", \"value\": \"1234567\"}"; + msg = R"({"cmd": "SET_PIN", "value": "1234567"})"; QCOMPARE(dispatcher.processCommand(msg), expected); - msg = "{\"cmd\": \"SET_PIN\", \"value\": \"abcdef\"}"; + msg = R"({"cmd": "SET_PIN", "value": "abcdef"})"; QCOMPARE(dispatcher.processCommand(msg), expected); - msg = "{\"cmd\": \"SET_PIN\", \"value\": \"\"}"; + msg = R"({"cmd": "SET_PIN", "value": ""})"; QCOMPARE(dispatcher.processCommand(msg), expected); - msg = "{\"cmd\": \"SET_PIN\", \"value\": \"123456a\"}"; + msg = R"({"cmd": "SET_PIN", "value": "123456a"})"; QCOMPARE(dispatcher.processCommand(msg), expected); - msg = "{\"cmd\": \"SET_PIN\", \"value\": \"12x456\"}"; + msg = R"({"cmd": "SET_PIN", "value": "12x456"})"; QCOMPARE(dispatcher.processCommand(msg), expected); } @@ -100,13 +114,13 @@ class test_MsgHandlerEnterPin void badState() { MessageDispatcher dispatcher; - setValidPinState(dispatcher, QStringLiteral("invalid")); + setValidPinState(dispatcher, true, true, PacePasswordId::UNKNOWN); - QByteArray msg("{\"cmd\": \"SET_PIN\", \"value\": \"12345\"}"); - QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"error\":\"SET_PIN\",\"msg\":\"BAD_STATE\"}")); + QByteArray msg(R"({"cmd": "SET_PIN", "value": "12345"})"); + QCOMPARE(dispatcher.processCommand(msg), QByteArray(R"({"error":"SET_PIN","msg":"BAD_STATE"})")); - msg = "{\"cmd\": \"SET_PIN\", \"value\": \"123456\"}"; - QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"error\":\"SET_PIN\",\"msg\":\"BAD_STATE\"}")); + msg = R"({"cmd": "SET_PIN", "value": "123456"})"; + QCOMPARE(dispatcher.processCommand(msg), QByteArray(R"({"error":"SET_PIN","msg":"BAD_STATE"})")); } @@ -115,30 +129,26 @@ class test_MsgHandlerEnterPin MessageDispatcher dispatcher; setValidPinState(dispatcher); - QByteArray msg("{\"cmd\": \"SET_PIN\", \"value\": \"123456\"}"); + const QByteArray msg(R"({"cmd": "SET_PIN", "value": "123456"})"); QCOMPARE(dispatcher.processCommand(msg), QByteArray()); } - void readerInfo() + void pinPadReader() { - MockReader* reader = MockReaderManagerPlugIn::getInstance().addReader("MockReader CARD"); - reader->setCard(MockCardConfig()); - - QSharedPointer context(new WorkflowContext()); MessageDispatcher dispatcher; - dispatcher.init(context); + setValidPinState(dispatcher, true, false); - context->setReaderName("MockReader"); - QCOMPARE(dispatcher.processStateChange("StateEstablishPacePin"), QByteArray("{\"msg\":\"ENTER_PIN\"}")); - QByteArray msg = "{\"cmd\": \"SET_PIN\", \"value\": \"54321\"}"; - QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"error\":\"You must provide 6 digits\",\"msg\":\"ENTER_PIN\"}")); + QByteArray msg(R"({"cmd": "SET_PIN", "value": "111111"})"); + QByteArray expected(addReaderData(R"({"error":"Value cannot be defined","msg":"ENTER_PIN"})", true)); + QCOMPARE(dispatcher.processCommand(msg), expected); + msg = QByteArray(R"({"cmd": "SET_PIN", "value": ""})"); + expected = QByteArray(addReaderData(R"({"error":"Value cannot be defined","msg":"ENTER_PIN"})", true)); + QCOMPARE(dispatcher.processCommand(msg), expected); - context->setReaderName("MockReader CARD"); - QCOMPARE(dispatcher.processStateChange("StateEstablishPacePin"), QByteArray("{\"msg\":\"ENTER_PIN\",\"reader\":{\"attached\":true,\"card\":{\"deactivated\":false,\"inoperative\":false,\"retryCounter\":-1},\"name\":\"MockReader CARD\"}}")); - msg = "{\"cmd\": \"SET_PIN\", \"value\": \"54321\"}"; - QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"error\":\"You must provide 6 digits\",\"msg\":\"ENTER_PIN\",\"reader\":{\"attached\":true,\"card\":{\"deactivated\":false,\"inoperative\":false,\"retryCounter\":-1},\"name\":\"MockReader CARD\"}}")); + msg = QByteArray(R"({"cmd": "SET_PIN"})"); + QCOMPARE(dispatcher.processCommand(msg), QByteArray()); } diff --git a/test/qt/jsonapi/test_MsgHandlerEnterPuk.cpp b/test/qt/jsonapi/test_MsgHandlerEnterPuk.cpp index 2e518c8..42f69ad 100644 --- a/test/qt/jsonapi/test_MsgHandlerEnterPuk.cpp +++ b/test/qt/jsonapi/test_MsgHandlerEnterPuk.cpp @@ -6,12 +6,15 @@ #include "messages/MsgHandlerEnterPuk.h" +#include "MsgHandlerEnterPassword.h" + #include "MessageDispatcher.h" -#include "MockReaderManagerPlugIn.h" #include "ReaderManager.h" +#include "MockReaderManagerPlugIn.h" + #include -#include +#include Q_IMPORT_PLUGIN(MockReaderManagerPlugIn) @@ -22,72 +25,73 @@ class test_MsgHandlerEnterPuk { Q_OBJECT - static void setValidState(MessageDispatcher& pDispatcher, const QString& pState = QStringLiteral("StateEstablishPacePuk")) + static void setValidPukState(MessageDispatcher& pDispatcher, + bool pSelectReader = true, + bool pBasicReader = true, + const PacePasswordId pPasswordID = PacePasswordId::PACE_PUK) { - QSharedPointer context(new WorkflowContext()); - pDispatcher.init(context); - - QByteArray expected; - if (pState == QLatin1String("StateEstablishPacePuk")) - { - expected = "{\"msg\":\"ENTER_PUK\"}"; - } - - QCOMPARE(pDispatcher.processStateChange(pState), expected); + setValidState(pDispatcher, pSelectReader, pBasicReader, pPasswordID); } private Q_SLOTS: void initTestCase() { - ReaderManager::getInstance().init(); - ReaderManager::getInstance().getPlugInInfos(); // just to wait until initialization finished + const auto readerManager = Env::getSingleton(); + readerManager->init(); + readerManager->getPlugInInfos(); // just to wait until initialization finished } void cleanupTestCase() { - ReaderManager::getInstance().shutdown(); + Env::getSingleton()->shutdown(); + } + + + void cleanup() + { + MockReaderManagerPlugIn::getInstance().removeAllReader(); } void stateMsg() { MessageDispatcher dispatcher; - setValidState(dispatcher); + setValidPukState(dispatcher); } void undefined() { MessageDispatcher dispatcher; - setValidState(dispatcher); + setValidPukState(dispatcher); - QByteArray msg("{\"cmd\": \"SET_PUK\"}"); - QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"error\":\"Value cannot be undefined\",\"msg\":\"ENTER_PUK\"}")); + const QByteArray msg(R"({"cmd": "SET_PUK"})"); + QCOMPARE(dispatcher.processCommand(msg), addReaderData(R"({"error":"Value cannot be undefined","msg":"ENTER_PUK"})")); } void invalid() { MessageDispatcher dispatcher; - setValidState(dispatcher); + setValidPukState(dispatcher); - QByteArray msg("{\"cmd\": \"SET_PUK\", \"value\": 12345667890}"); - QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"error\":\"Invalid value\",\"msg\":\"ENTER_PUK\"}")); + const QByteArray msg(R"({"cmd": "SET_PUK", "value": 12345667890})"); + QCOMPARE(dispatcher.processCommand(msg), addReaderData(R"({"error":"Invalid value","msg":"ENTER_PUK"})")); } void badInput() { MessageDispatcher dispatcher; - setValidState(dispatcher); + setValidPukState(dispatcher); - QByteArray msg("{\"cmd\": \"SET_PUK\", \"value\": \"123456\"}"); - QByteArray expected("{\"error\":\"You must provide 10 digits\",\"msg\":\"ENTER_PUK\"}"); + QByteArray msg(R"({"cmd": "SET_PUK", "value": "123456"})"); + const QByteArray expected(addReaderData(R"({"error":"You must provide 10 digits","msg":"ENTER_PUK"})")); QCOMPARE(dispatcher.processCommand(msg), expected); - msg = "{\"cmd\": \"SET_PUK\", \"value\": \"12345\"}"; + msg = (R"({"cmd": "SET_PUK", "value": "12345"})"); QCOMPARE(dispatcher.processCommand(msg), expected); } @@ -95,12 +99,12 @@ class test_MsgHandlerEnterPuk void badState() { MessageDispatcher dispatcher; - setValidState(dispatcher, QStringLiteral("invalid")); + setValidPukState(dispatcher, true, true, PacePasswordId::UNKNOWN); - QByteArray msg("{\"cmd\": \"SET_PUK\", \"value\": \"12345\"}"); + QByteArray msg(R"({"cmd": "SET_PUK", "value": "123456789"})"); QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"error\":\"SET_PUK\",\"msg\":\"BAD_STATE\"}")); - msg = "{\"cmd\": \"SET_PUK\", \"value\": \"123456\"}"; + msg = (R"({"cmd": "SET_PUK", "value": "1234567890"})"); QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"error\":\"SET_PUK\",\"msg\":\"BAD_STATE\"}")); } @@ -108,32 +112,28 @@ class test_MsgHandlerEnterPuk void noDirectResponseIfPukLooksValid() { MessageDispatcher dispatcher; - setValidState(dispatcher); + setValidPukState(dispatcher); - QByteArray msg("{\"cmd\": \"SET_PUK\", \"value\": \"1234567890\"}"); + const QByteArray msg(R"({"cmd": "SET_PUK", "value": "1234567890"})"); QCOMPARE(dispatcher.processCommand(msg), QByteArray()); } - void readerInfo() + void pinPadReader() { - MockReader* reader = MockReaderManagerPlugIn::getInstance().addReader("MockReader CARD"); - reader->setCard(MockCardConfig()); - - QSharedPointer context(new WorkflowContext()); MessageDispatcher dispatcher; - dispatcher.init(context); + setValidPukState(dispatcher, true, false); - context->setReaderName("MockReader"); - QCOMPARE(dispatcher.processStateChange("StateEstablishPacePuk"), QByteArray("{\"msg\":\"ENTER_PUK\"}")); - QByteArray msg = "{\"cmd\": \"SET_PUK\", \"value\": \"654321\"}"; - QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"error\":\"You must provide 10 digits\",\"msg\":\"ENTER_PUK\"}")); + QByteArray msg(R"({"cmd": "SET_PUK", "value": "1234567890"})"); + QByteArray expected(addReaderData(R"({"error":"Value cannot be defined","msg":"ENTER_PUK"})", true)); + QCOMPARE(dispatcher.processCommand(msg), expected); + msg = QByteArray(R"({"cmd": "SET_PUK", "value": ""})"); + expected = QByteArray(addReaderData(R"({"error":"Value cannot be defined","msg":"ENTER_PUK"})", true)); + QCOMPARE(dispatcher.processCommand(msg), expected); - context->setReaderName("MockReader CARD"); - QCOMPARE(dispatcher.processStateChange("StateEstablishPacePuk"), QByteArray("{\"msg\":\"ENTER_PUK\",\"reader\":{\"attached\":true,\"card\":{\"deactivated\":false,\"inoperative\":false,\"retryCounter\":-1},\"name\":\"MockReader CARD\"}}")); - msg = "{\"cmd\": \"SET_PUK\", \"value\": \"654321\"}"; - QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"error\":\"You must provide 10 digits\",\"msg\":\"ENTER_PUK\",\"reader\":{\"attached\":true,\"card\":{\"deactivated\":false,\"inoperative\":false,\"retryCounter\":-1},\"name\":\"MockReader CARD\"}}")); + msg = QByteArray(R"({"cmd": "SET_PUK"})"); + QCOMPARE(dispatcher.processCommand(msg), QByteArray()); } diff --git a/test/qt/jsonapi/test_MsgHandlerInsertCard.cpp b/test/qt/jsonapi/test_MsgHandlerInsertCard.cpp index 6cfc347..a131dd5 100644 --- a/test/qt/jsonapi/test_MsgHandlerInsertCard.cpp +++ b/test/qt/jsonapi/test_MsgHandlerInsertCard.cpp @@ -32,14 +32,15 @@ class test_MsgHandlerInsertCard private Q_SLOTS: void initTestCase() { - ReaderManager::getInstance().init(); - ReaderManager::getInstance().getPlugInInfos(); // just to wait until initialization finished + const auto readerManager = Env::getSingleton(); + readerManager->init(); + readerManager->getPlugInInfos(); // just to wait until initialization finished } void cleanupTestCase() { - ReaderManager::getInstance().shutdown(); + Env::getSingleton()->shutdown(); } diff --git a/test/qt/jsonapi/test_MsgHandlerReader.cpp b/test/qt/jsonapi/test_MsgHandlerReader.cpp index 94d1bcd..9ede58d 100644 --- a/test/qt/jsonapi/test_MsgHandlerReader.cpp +++ b/test/qt/jsonapi/test_MsgHandlerReader.cpp @@ -24,14 +24,15 @@ class test_MsgHandlerReader private Q_SLOTS: void initTestCase() { - ReaderManager::getInstance().init(); - ReaderManager::getInstance().getPlugInInfos(); // just to wait until initialization finished + const auto readerManager = Env::getSingleton(); + readerManager->init(); + readerManager->getPlugInInfos(); // just to wait until initialization finished } void cleanupTestCase() { - ReaderManager::getInstance().shutdown(); + Env::getSingleton()->shutdown(); } @@ -43,7 +44,7 @@ class test_MsgHandlerReader QCOMPARE(noReader.toJson(), QByteArray("{\"attached\":false,\"msg\":\"READER\",\"name\":\"MockReader\"}")); MsgHandlerReader reader("MockReader 0815"); - QCOMPARE(reader.toJson(), QByteArray("{\"attached\":true,\"card\":null,\"msg\":\"READER\",\"name\":\"MockReader 0815\"}")); + QCOMPARE(reader.toJson(), QByteArray("{\"attached\":true,\"card\":null,\"keypad\":false,\"msg\":\"READER\",\"name\":\"MockReader 0815\"}")); } @@ -67,7 +68,7 @@ class test_MsgHandlerReader QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"attached\":false,\"msg\":\"READER\",\"name\":\"MockReader 081\"}")); msg = "{\"cmd\": \"GET_READER\", \"name\": \"MockReader 0815\"}"; - QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"attached\":true,\"card\":null,\"msg\":\"READER\",\"name\":\"MockReader 0815\"}")); + QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"attached\":true,\"card\":null,\"keypad\":false,\"msg\":\"READER\",\"name\":\"MockReader 0815\"}")); MockReaderManagerPlugIn::getInstance().removeReader("MockReader 0815"); msg = "{\"cmd\": \"GET_READER\", \"name\": \"MockReader 0815\"}"; @@ -82,7 +83,7 @@ class test_MsgHandlerReader MessageDispatcher dispatcher; QByteArray msg("{\"cmd\": \"GET_READER\", \"name\": \"MockReader 0815\"}"); - QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"attached\":true,\"card\":{\"deactivated\":false,\"inoperative\":false,\"retryCounter\":-1},\"msg\":\"READER\",\"name\":\"MockReader 0815\"}")); + QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"attached\":true,\"card\":{\"deactivated\":false,\"inoperative\":false,\"retryCounter\":-1},\"keypad\":false,\"msg\":\"READER\",\"name\":\"MockReader 0815\"}")); } @@ -98,28 +99,26 @@ class test_MsgHandlerReader reader->setCard(MockCardConfig()); reader->getReaderInfo().setCardInfo(CardInfo(CardType::UNKNOWN)); - reader = MockReaderManagerPlugIn::getInstance().addReader("SpecialMockWithGermanCard"); reader->setCard(MockCardConfig()); auto cardInfo = CardInfo(CardType::EID_CARD, QSharedPointer(), 3, true); reader->getReaderInfo().setCardInfo(cardInfo); - MessageDispatcher dispatcher; QByteArray msg("{\"cmd\": \"GET_READER\", \"name\": \"MockReader 0815\"}"); - QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"attached\":true,\"card\":{\"deactivated\":false,\"inoperative\":false,\"retryCounter\":-1},\"msg\":\"READER\",\"name\":\"MockReader 0815\"}")); + QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"attached\":true,\"card\":{\"deactivated\":false,\"inoperative\":false,\"retryCounter\":-1},\"keypad\":false,\"msg\":\"READER\",\"name\":\"MockReader 0815\"}")); msg = "{\"cmd\": \"GET_READER\", \"name\": \"ReaderMock\"}"; - QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"attached\":true,\"card\":{\"deactivated\":false,\"inoperative\":false,\"retryCounter\":-1},\"msg\":\"READER\",\"name\":\"ReaderMock\"}")); + QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"attached\":true,\"card\":{\"deactivated\":false,\"inoperative\":false,\"retryCounter\":-1},\"keypad\":false,\"msg\":\"READER\",\"name\":\"ReaderMock\"}")); msg = "{\"cmd\": \"GET_READER\", \"name\": \"ReaderMockXYZ\"}"; - QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"attached\":true,\"card\":null,\"msg\":\"READER\",\"name\":\"ReaderMockXYZ\"}")); + QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"attached\":true,\"card\":null,\"keypad\":false,\"msg\":\"READER\",\"name\":\"ReaderMockXYZ\"}")); msg = "{\"cmd\": \"GET_READER\", \"name\": \"SpecialMock\"}"; - QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"attached\":true,\"card\":null,\"msg\":\"READER\",\"name\":\"SpecialMock\"}")); + QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"attached\":true,\"card\":null,\"keypad\":false,\"msg\":\"READER\",\"name\":\"SpecialMock\"}")); msg = "{\"cmd\": \"GET_READER\", \"name\": \"SpecialMockWithGermanCard\"}"; - QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"attached\":true,\"card\":{\"deactivated\":true,\"inoperative\":false,\"retryCounter\":3},\"msg\":\"READER\",\"name\":\"SpecialMockWithGermanCard\"}")); + QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"attached\":true,\"card\":{\"deactivated\":true,\"inoperative\":false,\"retryCounter\":3},\"keypad\":false,\"msg\":\"READER\",\"name\":\"SpecialMockWithGermanCard\"}")); } diff --git a/test/qt/jsonapi/test_MsgHandlerReaderList.cpp b/test/qt/jsonapi/test_MsgHandlerReaderList.cpp index 26dfedb..23767e0 100644 --- a/test/qt/jsonapi/test_MsgHandlerReaderList.cpp +++ b/test/qt/jsonapi/test_MsgHandlerReaderList.cpp @@ -23,14 +23,15 @@ class test_MsgHandlerReaderList private Q_SLOTS: void initTestCase() { - ReaderManager::getInstance().init(); - ReaderManager::getInstance().getPlugInInfos(); // just to wait until initialization finished + const auto readerManager = Env::getSingleton(); + readerManager->init(); + readerManager->getPlugInInfos(); // just to wait until initialization finished } void cleanupTestCase() { - ReaderManager::getInstance().shutdown(); + Env::getSingleton()->shutdown(); } @@ -48,7 +49,7 @@ class test_MsgHandlerReaderList MessageDispatcher dispatcher; QByteArray msg("{\"cmd\": \"GET_READER_LIST\"}"); - QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"msg\":\"READER_LIST\",\"reader\":[{\"attached\":true,\"card\":null,\"name\":\"MockReader 0815\"}]}")); + QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"msg\":\"READER_LIST\",\"reader\":[{\"attached\":true,\"card\":null,\"keypad\":false,\"name\":\"MockReader 0815\"}]}")); } @@ -59,7 +60,7 @@ class test_MsgHandlerReaderList MessageDispatcher dispatcher; QByteArray msg("{\"cmd\": \"GET_READER_LIST\"}"); - QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"msg\":\"READER_LIST\",\"reader\":[{\"attached\":true,\"card\":{\"deactivated\":false,\"inoperative\":false,\"retryCounter\":-1},\"name\":\"MockReader 0815\"}]}")); + QCOMPARE(dispatcher.processCommand(msg), QByteArray("{\"msg\":\"READER_LIST\",\"reader\":[{\"attached\":true,\"card\":{\"deactivated\":false,\"inoperative\":false,\"retryCounter\":-1},\"keypad\":false,\"name\":\"MockReader 0815\"}]}")); } @@ -75,26 +76,23 @@ class test_MsgHandlerReaderList reader->setCard(MockCardConfig()); reader->getReaderInfo().setCardInfo(CardInfo(CardType::UNKNOWN)); - reader = MockReaderManagerPlugIn::getInstance().addReader("SpecialMockWithGermanCard"); reader->setCard(MockCardConfig()); auto cardInfo = CardInfo(CardType::EID_CARD, QSharedPointer(), 3, true); reader->getReaderInfo().setCardInfo(cardInfo); - MessageDispatcher dispatcher; QByteArray msg("{\"cmd\": \"GET_READER_LIST\"}"); QByteArray expected("{\"msg\":\"READER_LIST\",\"reader\":[" - "{\"attached\":true,\"card\":{\"deactivated\":false,\"inoperative\":false,\"retryCounter\":-1},\"name\":\"MockReader 0815\"}," - "{\"attached\":true,\"card\":{\"deactivated\":false,\"inoperative\":false,\"retryCounter\":-1},\"name\":\"ReaderMock\"}," - "{\"attached\":true,\"card\":null,\"name\":\"ReaderMockXYZ\"}," - "{\"attached\":true,\"card\":null,\"name\":\"SpecialMock\"}," - "{\"attached\":true,\"card\":{\"deactivated\":true,\"inoperative\":false,\"retryCounter\":3},\"name\":\"SpecialMockWithGermanCard\"}" + "{\"attached\":true,\"card\":{\"deactivated\":false,\"inoperative\":false,\"retryCounter\":-1},\"keypad\":false,\"name\":\"MockReader 0815\"}," + "{\"attached\":true,\"card\":{\"deactivated\":false,\"inoperative\":false,\"retryCounter\":-1},\"keypad\":false,\"name\":\"ReaderMock\"}," + "{\"attached\":true,\"card\":null,\"keypad\":false,\"name\":\"ReaderMockXYZ\"}," + "{\"attached\":true,\"card\":null,\"keypad\":false,\"name\":\"SpecialMock\"}," + "{\"attached\":true,\"card\":{\"deactivated\":true,\"inoperative\":false,\"retryCounter\":3},\"keypad\":false,\"name\":\"SpecialMockWithGermanCard\"}" "]}"); QCOMPARE(dispatcher.processCommand(msg), expected); - } diff --git a/test/qt/network/test_DatagramHandlerImpl.cpp b/test/qt/network/test_DatagramHandlerImpl.cpp index 54e40b9..6fe279d 100644 --- a/test/qt/network/test_DatagramHandlerImpl.cpp +++ b/test/qt/network/test_DatagramHandlerImpl.cpp @@ -24,7 +24,7 @@ class test_DatagramHandlerImpl private Q_SLOTS: void initTestCase() { - LogHandler::getInstance().init(); + Env::getSingleton()->init(); } @@ -36,13 +36,13 @@ class test_DatagramHandlerImpl void cleanup() { - LogHandler::getInstance().resetBacklog(); + Env::getSingleton()->resetBacklog(); } void startUpShutDown() { - QSignalSpy spy(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); QSharedPointer socket(Env::create()); QVERIFY(socket->isBound()); @@ -65,7 +65,7 @@ class test_DatagramHandlerImpl DatagramHandlerImpl::cPort = 80; - QSignalSpy spy(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); QSharedPointer socket(Env::create()); QVERIFY(!socket->isBound()); @@ -81,20 +81,18 @@ class test_DatagramHandlerImpl QVERIFY(socket->isBound()); QSignalSpy spySocket(socket.data(), &DatagramHandler::fireNewMessage); - QSignalSpy spy(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); QUdpSocket clientSocket; #ifndef QT_NO_NETWORKPROXY clientSocket.setProxy(QNetworkProxy::NoProxy); #endif auto written = clientSocket.writeDatagram("dummy", QHostAddress::LocalHost, socket.staticCast()->mSocket->localPort()); - spy.wait(); + QTRY_COMPARE(spySocket.count(), 1); QCOMPARE(written, 5); - - QCOMPARE(spySocket.count(), 0); - QCOMPARE(spy.count(), 1); - auto param = spy.takeFirst(); - QVERIFY(param.at(0).toString().contains("Datagram does not contain valid JSON: \"dummy\"")); + QCOMPARE(spy.count(), 0); + auto param = spySocket.takeFirst(); + QCOMPARE(param.at(0).toByteArray(), QByteArray("dummy")); } @@ -129,13 +127,11 @@ class test_DatagramHandlerImpl QByteArray data("{\"key\":\"value\"}"); auto written = clientSocket.writeDatagram(data, broadcast ? QHostAddress::Broadcast : QHostAddress::LocalHost, socket.staticCast()->mSocket->localPort()); - spySocket.wait(); + QTRY_COMPARE(spySocket.count(), 1); QCOMPARE(written, data.size()); - - QCOMPARE(spySocket.count(), 1); const auto& msg = spySocket.takeFirst(); QCOMPARE(msg.size(), 2); - QCOMPARE(msg.at(0).toJsonDocument().toJson(QJsonDocument::Compact), data); + QCOMPARE(msg.at(0).toByteArray(), data); } @@ -160,11 +156,8 @@ class test_DatagramHandlerImpl QVERIFY(receiver.bind()); QSignalSpy spyReceiver(&receiver, &QUdpSocket::readyRead); - QSharedPointer datagramHandlerImpl = QSharedPointer(Env::create()).dynamicCast(); - QVERIFY(datagramHandlerImpl); - - DatagramHandlerImpl::cPort = receiver.localPort(); - QSignalSpy spy(&LogHandler::getInstance(), &LogHandler::fireLog); + QSharedPointer datagramHandlerImpl = QSharedPointer::create(false); + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); QJsonObject obj; obj["test"] = "dummy"; @@ -175,16 +168,14 @@ class test_DatagramHandlerImpl #ifdef Q_OS_FREEBSD QSKIP("FreeBSD does not like that"); #endif - QVERIFY(datagramHandlerImpl->send(doc)); + QVERIFY(datagramHandlerImpl->send(doc.toJson(QJsonDocument::Compact), receiver.localPort())); } else { - QVERIFY(datagramHandlerImpl->send(doc, QHostAddress::LocalHost)); + QVERIFY(datagramHandlerImpl->send(doc.toJson(QJsonDocument::Compact), QHostAddress::LocalHost, receiver.localPort())); } - spyReceiver.wait(); - - QCOMPARE(spyReceiver.count(), 1); + QTRY_COMPARE(spyReceiver.count(), 1); QCOMPARE(spy.count(), 0); QVERIFY(receiver.hasPendingDatagrams()); diff --git a/test/qt/network/test_HttpResponse.cpp b/test/qt/network/test_HttpResponse.cpp index 08cfdd2..00a704d 100644 --- a/test/qt/network/test_HttpResponse.cpp +++ b/test/qt/network/test_HttpResponse.cpp @@ -6,8 +6,6 @@ #include "HttpResponse.h" -#include "MockSocket.h" - #include using namespace governikus; @@ -28,10 +26,10 @@ class test_HttpResponse void someHeader() { HttpResponse response; - response.setStatus(HttpStatusCode::NON_AUTHORITATIVE_INFORMATION); + response.setStatus(HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION); const auto& msg = response.getMessage(); - QVERIFY(msg.contains("HTTP/1.0 203 NON AUTHORITATIVE INFORMATION")); + QVERIFY(msg.contains("HTTP/1.0 203 Non-Authoritative Information")); QVERIFY(msg.contains("Content-Length: 0")); QVERIFY(msg.contains("Date: ")); QVERIFY(msg.contains("Server: Test_network_HttpResponse/1.2 (TR-03124-1/1.3)")); @@ -42,7 +40,7 @@ class test_HttpResponse void body() { HttpResponse response; - response.setStatus(HttpStatusCode::OK); + response.setStatus(HTTP_STATUS_OK); response.setBody(QByteArray("this is dummy content"), QByteArray("text/plain")); const auto& msg = response.getMessage(); @@ -54,22 +52,10 @@ class test_HttpResponse } - void valid() - { - HttpResponse response; - QVERIFY(!response.isValid()); - response.setBody("dummy"); - QVERIFY(!response.isValid()); - response.setStatus(HttpStatusCode::OK); - QVERIFY(response.isValid()); - } - - void ctorNoBody() { - HttpResponse response(HttpStatusCode::OK); - QVERIFY(response.isValid()); - QCOMPARE(response.getStatus(), HttpStatusCode::OK); + HttpResponse response(HTTP_STATUS_OK); + QCOMPARE(response.getStatus(), HTTP_STATUS_OK); QVERIFY(response.getBody().isEmpty()); QCOMPARE(response.getHeader("Content-Length"), QByteArray("0")); QVERIFY(response.getHeader("Content-Type").isEmpty()); @@ -78,9 +64,8 @@ class test_HttpResponse void ctorNoContentType() { - HttpResponse response(HttpStatusCode::OK, "hello"); - QVERIFY(response.isValid()); - QCOMPARE(response.getStatus(), HttpStatusCode::OK); + HttpResponse response(HTTP_STATUS_OK, "hello"); + QCOMPARE(response.getStatus(), HTTP_STATUS_OK); QCOMPARE(response.getBody(), QByteArray("hello")); QCOMPARE(response.getHeader("Content-Length"), QByteArray("5")); QVERIFY(response.getHeader("Content-Type").isEmpty()); @@ -89,9 +74,8 @@ class test_HttpResponse void ctor() { - HttpResponse response(HttpStatusCode::OK, "hello", "text/plain"); - QVERIFY(response.isValid()); - QCOMPARE(response.getStatus(), HttpStatusCode::OK); + HttpResponse response(HTTP_STATUS_OK, "hello", "text/plain"); + QCOMPARE(response.getStatus(), HTTP_STATUS_OK); QCOMPARE(response.getBody(), QByteArray("hello")); QCOMPARE(response.getHeader("Content-Length"), QByteArray("5")); QCOMPARE(response.getHeader("Content-Type"), QByteArray("text/plain")); diff --git a/test/qt/network/test_HttpServer.cpp b/test/qt/network/test_HttpServer.cpp index 5ed8a17..424a7f6 100644 --- a/test/qt/network/test_HttpServer.cpp +++ b/test/qt/network/test_HttpServer.cpp @@ -8,7 +8,6 @@ #include "Env.h" #include "LogHandler.h" -#include "MockSocket.h" #include #include @@ -30,7 +29,7 @@ class test_HttpServer private Q_SLOTS: void initTestCase() { - LogHandler::getInstance().init(); + Env::getSingleton()->init(); } @@ -42,18 +41,21 @@ class test_HttpServer void cleanup() { - LogHandler::getInstance().resetBacklog(); + Env::getSingleton()->resetBacklog(); } void startUpShutDown() { - QSignalSpy spy(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); auto server = Env::getShared(); QVERIFY(server->isListening()); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.count(), 2); + auto param = spy.takeFirst(); + QVERIFY(param.at(0).toString().contains("Spawn shared instance: governikus::HttpServer")); + param = spy.takeFirst(); QVERIFY(param.at(0).toString().contains("Listening on port:")); server.reset(); @@ -71,7 +73,7 @@ class test_HttpServer HttpServer::cPort = 80; - QSignalSpy spy(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); HttpServer server; QVERIFY(!server.isListening()); @@ -87,21 +89,19 @@ class test_HttpServer QVERIFY(server.isListening()); QSignalSpy spyServer(&server, &HttpServer::fireNewHttpRequest); - auto url = QUrl("http://localhost:" + QString::number(server.getServerPort()) + "/eID-Client?tcTokenURL=https%3A%2F%2Fdummy.de"); + auto url = QUrl("http://127.0.0.1:" + QString::number(server.getServerPort()) + "/eID-Client?tcTokenURL=https%3A%2F%2Fdummy.de"); auto reply = mAccessManager.get(QNetworkRequest(url)); QSignalSpy spyClient(reply, &QNetworkReply::finished); - spyServer.wait(); - QCOMPARE(spyServer.count(), 1); + QTRY_COMPARE(spyServer.count(), 1); auto param = spyServer.takeFirst(); auto httpRequest = qvariant_cast >(param.at(0)); QCOMPARE(httpRequest->getMethod(), QByteArray("GET")); QCOMPARE(httpRequest->getUrl(), QUrl("/eID-Client?tcTokenURL=https%3A%2F%2Fdummy.de")); - QVERIFY(httpRequest->send(HttpStatusCode::NOT_FOUND)); + QVERIFY(httpRequest->send(HTTP_STATUS_NOT_FOUND)); - spyClient.wait(); - QCOMPARE(spyClient.count(), 1); + QTRY_COMPARE(spyClient.count(), 1); QCOMPARE(reply->error(), QNetworkReply::ContentNotFoundError); QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 404); } @@ -113,16 +113,15 @@ class test_HttpServer QVERIFY(server.isListening()); QSignalSpy spyServer(&server, &HttpServer::fireNewWebSocketRequest); - QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.getServerPort()))); + QNetworkRequest request(QUrl("http://127.0.0.1:" + QString::number(server.getServerPort()))); request.setRawHeader("upgrade", "websocket"); request.setRawHeader("connection", "upgrade"); mAccessManager.get(request); - QSignalSpy spy(&LogHandler::getInstance(), &LogHandler::fireLog); - spyServer.wait(); - QCOMPARE(spyServer.count(), 1); + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); + QTRY_COMPARE(spyServer.count(), 1); auto param = spyServer.takeFirst(); - auto socket = qvariant_cast >(param.at(0)); + auto socket = qvariant_cast >(param.at(0))->take(); QVERIFY(socket->bytesAvailable() > 0); // check rollbackTransaction const auto& requestData = socket->readAll(); QVERIFY(requestData.contains("GET / HTTP/1.1")); @@ -140,15 +139,14 @@ class test_HttpServer HttpServer server; QVERIFY(server.isListening()); - QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.getServerPort()))); + QNetworkRequest request(QUrl("http://127.0.0.1:" + QString::number(server.getServerPort()))); request.setRawHeader("upgrade", "unknown"); request.setRawHeader("connection", "upgrade"); auto reply = mAccessManager.get(request); QSignalSpy spyClient(reply, &QNetworkReply::finished); - QSignalSpy spy(&LogHandler::getInstance(), &LogHandler::fireLog); - spyClient.wait(); - QCOMPARE(spyClient.count(), 1); + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); + QTRY_COMPARE(spyClient.count(), 1); QCOMPARE(reply->error(), QNetworkReply::ContentNotFoundError); QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 404); @@ -157,6 +155,43 @@ class test_HttpServer } + void methodWithoutReceiver_data() + { + QTest::addColumn("request"); + QTest::addColumn("signal"); + + QNetworkRequest request; + + QTest::newRow("http") << request << QStringLiteral("fireNewHttpRequest"); + + request.setRawHeader("upgrade", "websocket"); + request.setRawHeader("connection", "upgrade"); + QTest::newRow("ws") << request << QStringLiteral("fireNewWebSocketRequest"); + } + + + void methodWithoutReceiver() + { + QFETCH(QNetworkRequest, request); + QFETCH(QString, signal); + + HttpServer server; + QVERIFY(server.isListening()); + request.setUrl(QUrl("http://127.0.0.1:" + QString::number(server.getServerPort()))); + + auto reply = mAccessManager.get(request); + + QSignalSpy spyClient(reply, &QNetworkReply::finished); + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); + + QTRY_COMPARE(spyClient.count(), 1); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 503); + + auto noSignalFound = QStringLiteral("No registration found: \"%1\"").arg(signal); + QVERIFY(spy.takeLast().at(0).toString().contains(noSignalFound)); + } + + }; QTEST_GUILESS_MAIN(test_HttpServer) diff --git a/test/qt/network/test_NetworkManager.cpp b/test/qt/network/test_NetworkManager.cpp index 56c38cd..e20fe04 100644 --- a/test/qt/network/test_NetworkManager.cpp +++ b/test/qt/network/test_NetworkManager.cpp @@ -4,11 +4,12 @@ * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany */ +#include "NetworkManager.h" + #include "context/SelfAuthContext.h" #include "controller/SelfAuthController.h" #include "Env.h" #include "LogHandler.h" -#include "NetworkManager.h" #include "SecureStorage.h" #include "MockNetworkManager.h" @@ -30,13 +31,13 @@ class test_NetworkManager private Q_SLOTS: void initTestCase() { - LogHandler::getInstance().init(); + Env::getSingleton()->init(); } void cleanup() { - LogHandler::getInstance().resetBacklog(); + Env::getSingleton()->resetBacklog(); } @@ -49,11 +50,8 @@ class test_NetworkManager QCOMPARE(reply->request(), request); QCOMPARE(request.sslConfiguration().ellipticCurves().size(), 6); QVERIFY(request.sslConfiguration().ellipticCurves().contains(QSslEllipticCurve::fromLongName("prime256v1"))); -#if OPENSSL_VERSION_NUMBER < 0x10100000L - QCOMPARE(request.sslConfiguration().ciphers().size(), 24); -#else - QCOMPARE(request.sslConfiguration().ciphers().size(), 18); -#endif + const int cipherCount = SecureStorage::getInstance().getTlsConfig().getCiphers().size(); + QCOMPARE(request.sslConfiguration().ciphers().size(), cipherCount); QVERIFY(request.sslConfiguration().ciphers().contains(QSslCipher("ECDHE-RSA-AES256-GCM-SHA384"))); } @@ -66,8 +64,8 @@ class test_NetworkManager QCOMPARE(request.rawHeader("PAOS"), QByteArray("ver=\"paosNamespace\"")); QCOMPARE(reply->request(), request); QCOMPARE(request.sslConfiguration().ellipticCurves().size(), 0); - QCOMPARE(request.sslConfiguration().ciphers().size(), 5); - QVERIFY(request.sslConfiguration().ciphers().contains(QSslCipher("RSA-PSK-AES256-CBC-SHA"))); + const int cipherCount = SecureStorage::getInstance().getTlsConfig(SecureStorage::TlsSuite::PSK).getCiphers().size(); + QCOMPARE(request.sslConfiguration().ciphers().size(), cipherCount); QVERIFY(request.sslConfiguration().ciphers().contains(QSslCipher("RSA-PSK-AES128-CBC-SHA256"))); QVERIFY(request.sslConfiguration().ciphers().contains(QSslCipher("RSA-PSK-AES128-GCM-SHA256"))); QVERIFY(request.sslConfiguration().ciphers().contains(QSslCipher("RSA-PSK-AES256-CBC-SHA384"))); @@ -126,7 +124,6 @@ class test_NetworkManager reply->setNetworkError(QNetworkReply::ServiceUnavailableError, "dummy"); networkManager.setNextReply(reply); - auto context = QSharedPointer::create(); connect(context.data(), &AuthContext::fireStateChanged, this, [&] { context->setStateApproved(); @@ -137,11 +134,7 @@ class test_NetworkManager controller.run(); - if (spy.count() == 0) - { - spy.wait(); - } - QCOMPARE(spy.count(), 1); + QTRY_COMPARE(spy.count(), 1); QCOMPARE(context->getStatus(), GlobalStatus(GlobalStatus::Code::Workflow_TrustedChannel_ServiceUnavailable)); } diff --git a/test/qt/network/test_PortFile.cpp b/test/qt/network/test_PortFile.cpp new file mode 100644 index 0000000..9721d93 --- /dev/null +++ b/test/qt/network/test_PortFile.cpp @@ -0,0 +1,63 @@ +/*! + * \brief Unit tests for \ref PortFile + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "PortFile.h" + +#include +#include + +using namespace governikus; + +class test_PortFile + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void testHandlePort_data() + { + QTest::addColumn("usage"); + QTest::addColumn("port"); + QTest::addColumn("fileCreated"); + + QTest::newRow("default") << QString() << PortFile::cDefaultPort << false; + QTest::newRow("default_udp") << QStringLiteral("udp") << PortFile::cDefaultPort << false; + QTest::newRow("other") << QString() << quint16(1234) << true; + QTest::newRow("other_udp") << QStringLiteral("udp") << quint16(1234) << true; + } + + + void testHandlePort() + { + QFETCH(QString, usage); + QFETCH(quint16, port); + QFETCH(bool, fileCreated); + + const auto& filename = PortFile::getPortFilename(usage); + QVERIFY(filename.contains(QString::number(QCoreApplication::applicationPid()))); + QVERIFY(filename.contains(QCoreApplication::applicationName())); + QVERIFY(filename.endsWith(QStringLiteral(".port"))); + + { + PortFile portFile(usage); + QVERIFY(!QFile::exists(filename)); + portFile.handlePort(port); + QCOMPARE(QFile::exists(filename), fileCreated); + + if (!usage.isEmpty()) + { + QVERIFY(filename.contains(QLatin1Char('.') + usage + QLatin1Char('.'))); + } + } + + QVERIFY(!QFile::exists(filename)); + } + + +}; + +QTEST_GUILESS_MAIN(test_PortFile) +#include "test_PortFile.moc" diff --git a/test/qt/network/test_TlsChecker.cpp b/test/qt/network/test_TlsChecker.cpp index 017b667..c4a5506 100644 --- a/test/qt/network/test_TlsChecker.cpp +++ b/test/qt/network/test_TlsChecker.cpp @@ -41,7 +41,7 @@ class test_TlsChecker private Q_SLOTS: void initTestCase() { - LogHandler::getInstance().init(); + Env::getSingleton()->init(); certs = SecureStorage::getInstance().getUpdateCertificates(); QVERIFY(certs.size() > 0); } @@ -49,7 +49,7 @@ class test_TlsChecker void cleanup() { - LogHandler::getInstance().resetBacklog(); + Env::getSingleton()->resetBacklog(); } @@ -81,7 +81,7 @@ class test_TlsChecker const auto& content = TestFileHelper::readFile(filename); QVERIFY(!content.isEmpty()); QSslCertificate invalidCert(content); - QSignalSpy spy(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); QVERIFY(!TlsChecker::hasValidCertificateKeyLength(invalidCert)); QVERIFY(spy.count() > 0); @@ -295,9 +295,9 @@ class test_TlsChecker void sslConfigLog() { - QSignalSpy spy(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); QSslConfiguration cfg; - TlsChecker::logSslConfig(cfg, qInfo(network)); + TlsChecker::logSslConfig(cfg, spawnMessageLogger(network)); QCOMPARE(spy.count(), 6); QVERIFY(spy.at(0).at(0).toString().contains("Used session cipher QSslCipher(name=, bits=0, proto=)")); diff --git a/test/qt/network/test_UrlUtil.cpp b/test/qt/network/test_UrlUtil.cpp index d2bae2d..b914e66 100644 --- a/test/qt/network/test_UrlUtil.cpp +++ b/test/qt/network/test_UrlUtil.cpp @@ -1,10 +1,9 @@ /*! - * \brief Unit tests for \ref test_UrlUtil + * \brief Unit tests for \ref UrlUtil * * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany */ -#include "TestFileHelper.h" #include "UrlUtil.h" #include #include @@ -18,16 +17,63 @@ class test_UrlUtil Q_OBJECT private Q_SLOTS: + void isMatchingSameOriginPolicy_data() + { + QTest::addColumn("url1"); + QTest::addColumn("url2"); + QTest::addColumn("valid"); + + QTest::newRow("none_80") << QUrl("http://www.web.de/index.html") << QUrl("http://www.web.de:80") << true; + QTest::newRow("8080_8080") << QUrl("http://www.web.de:8080/index.html") << QUrl("http://www.web.de:8080") << true; + QTest::newRow("none_443") << QUrl("https://www.web.de/index.html") << QUrl("https://www.web.de:443") << true; + QTest::newRow("8443_8443") << QUrl("https://www.web.de:8443/index.html") << QUrl("https://www.web.de:8443") << true; + + QTest::newRow("false_8443_8444") << QUrl("https://www.web.de:8443/index.html") << QUrl("https://www.web.de:8444") << false; + QTest::newRow("false_999_999") << QUrl("https://www.web.de:999/index.html") << QUrl("http://www.web.de:999") << false; + QTest::newRow("false_different_domain") << QUrl("http://www.google.de:999/index.html") << QUrl("http://www.web.de:999") << false; + + QTest::newRow("no_scheme_https_with_port") << QUrl("de.dummy.cz") << QUrl("https://de.dummy.cz:443") << false; + QTest::newRow("no_scheme_https_without_port") << QUrl("de.dummy.cz") << QUrl("https://de.dummy.cz") << false; + + QTest::newRow("no_scheme_http_with_port") << QUrl("de.dummy.cz") << QUrl("http://de.dummy.cz:80") << false; + QTest::newRow("no_scheme_http_without_port") << QUrl("de.dummy.cz") << QUrl("http://de.dummy.cz") << false; + } + + void isMatchingSameOriginPolicy() { - QVERIFY(UrlUtil::isMatchingSameOriginPolicy(QUrl("http://www.web.de/index.html"), QUrl("http://www.web.de:80"))); - QVERIFY(UrlUtil::isMatchingSameOriginPolicy(QUrl("http://www.web.de:8080/index.html"), QUrl("http://www.web.de:8080"))); - QVERIFY(UrlUtil::isMatchingSameOriginPolicy(QUrl("https://www.web.de/index.html"), QUrl("https://www.web.de:443"))); - QVERIFY(UrlUtil::isMatchingSameOriginPolicy(QUrl("https://www.web.de:8443/index.html"), QUrl("https://www.web.de:8443"))); + QFETCH(QUrl, url1); + QFETCH(QUrl, url2); + QFETCH(bool, valid); - QVERIFY(!UrlUtil::isMatchingSameOriginPolicy(QUrl("https://www.web.de:8443/index.html"), QUrl("https://www.web.de:8444"))); - QVERIFY(!UrlUtil::isMatchingSameOriginPolicy(QUrl("https://www.web.de:999/index.html"), QUrl("http://www.web.de:999"))); - QVERIFY(!UrlUtil::isMatchingSameOriginPolicy(QUrl("http://www.google.de:999/index.html"), QUrl("http://www.web.de:999"))); + QCOMPARE(UrlUtil::isMatchingSameOriginPolicy(url1, url2), valid); + } + + + void majorMinor() + { + const QString URL_PREFIX("https://www.der-pott-kocht.net:8443/index.html"); + const QUrl url(URL_PREFIX); + + // Ok + QCOMPARE(UrlUtil::addMajorMinor(url, GlobalStatus(ECardApiResult(ECardApiResult::Major::Ok, ECardApiResult::Minor::null))).toString(), + URL_PREFIX + "?ResultMajor=ok"); + + // General server error + QCOMPARE(UrlUtil::addMajorMinor(url, GlobalStatus(ECardApiResult(ECardApiResult::Major::Error, ECardApiResult::Minor::AL_Communication_Error, QString(), ECardApiResult::Origin::Server))).toString(), + URL_PREFIX + "?ResultMajor=error&ResultMinor=serverError"); + + // Minors defined in TR-03112-1 + QCOMPARE(UrlUtil::addMajorMinor(url, GlobalStatus(ECardApiResult(ECardApiResult::Major::Error, ECardApiResult::Minor::AL_Communication_Error))).toString(), + URL_PREFIX + "?ResultMajor=error&ResultMinor=communicationError"); + QCOMPARE(UrlUtil::addMajorMinor(url, GlobalStatus(ECardApiResult(ECardApiResult::Major::Error, ECardApiResult::Minor::DP_Trusted_Channel_Establishment_Failed))).toString(), + URL_PREFIX + "?ResultMajor=error&ResultMinor=trustedChannelEstablishmentFailed"); + QCOMPARE(UrlUtil::addMajorMinor(url, GlobalStatus(ECardApiResult(ECardApiResult::Major::Error, ECardApiResult::Minor::SAL_Cancellation_by_User))).toString(), + URL_PREFIX + "?ResultMajor=error&ResultMinor=cancellationByUser"); + + // General client error + QCOMPARE(UrlUtil::addMajorMinor(url, GlobalStatus(ECardApiResult(ECardApiResult::Major::Error, ECardApiResult::Minor::AL_Not_Initialized))).toString(), + URL_PREFIX + "?ResultMajor=error&ResultMinor=clientError"); } diff --git a/test/qt/qml/test_AuthModel.cpp b/test/qt/qml/test_AuthModel.cpp new file mode 100644 index 0000000..ac23801 --- /dev/null +++ b/test/qt/qml/test_AuthModel.cpp @@ -0,0 +1,75 @@ +/*! + * \brief Unit tests for \ref AuthModel + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "AuthModel.h" + +#include "context/AuthContext.h" + +#include "MockActivationContext.h" + +#include +#include + + +using namespace governikus; + + +class test_AuthModel + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void test_ResetContext() + { + AuthModel model; + const QSharedPointer context(new AuthContext(nullptr)); + const QString transactionInfo = QStringLiteral("info"); + + QSignalSpy spyCurrentStateChanged(&model, &WorkflowModel::fireCurrentStateChanged); + QSignalSpy spyTransactionInfoChanged(&model, &AuthModel::fireTransactionInfoChanged); + + model.resetContext(nullptr); + QCOMPARE(spyCurrentStateChanged.count(), 1); + QCOMPARE(spyTransactionInfoChanged.count(), 0); + + model.mTransactionInfo = transactionInfo; + model.resetContext(context); + QVERIFY(model.mTransactionInfo.isEmpty()); + QCOMPARE(spyCurrentStateChanged.count(), 2); + QCOMPARE(spyTransactionInfoChanged.count(), 1); + } + + + void test_OnDidAuthenticateEac1Changed() + { + AuthModel model; + const QSharedPointer context(new AuthContext(nullptr)); + const QString transactionInfo = QStringLiteral("info"); + const QSharedPointer eac1(new DIDAuthenticateEAC1()); + Eac1InputType type; + const QString info = QStringLiteral("new info"); + QSignalSpy spy(&model, &AuthModel::fireTransactionInfoChanged); + + model.mContext = context; + model.mTransactionInfo = transactionInfo; + model.onDidAuthenticateEac1Changed(); + QCOMPARE(model.mTransactionInfo, QString()); + QCOMPARE(spy.count(), 1); + + type.setTransactionInfo(info); + eac1->setEac1InputType(type); + context->setDidAuthenticateEac1(eac1); + model.onDidAuthenticateEac1Changed(); + QCOMPARE(model.mTransactionInfo, info); + QCOMPARE(spy.count(), 2); + } + + +}; + +QTEST_GUILESS_MAIN(test_AuthModel) +#include "test_AuthModel.moc" diff --git a/test/qt/qml/test_ChangePinModel.cpp b/test/qt/qml/test_ChangePinModel.cpp new file mode 100644 index 0000000..1088de3 --- /dev/null +++ b/test/qt/qml/test_ChangePinModel.cpp @@ -0,0 +1,62 @@ +/*! + * \brief Unit tests for \ref ChangePinModel + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "ChangePinModel.h" + +#include +#include + +using namespace governikus; + + +class test_ChangePinModel + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void test_ResetContext() + { + ChangePinModel model; + QSharedPointer context(new ChangePinContext()); + + QSignalSpy resultChanged(&model, &ChangePinModel::fireResultChanged); + QSignalSpy newContextSet(&model, &ChangePinModel::fireNewContextSet); + + model.resetContext(); + QCOMPARE(resultChanged.count(), 1); + QCOMPARE(newContextSet.count(), 0); + + Q_EMIT context->fireSuccessMessageChanged(); + QCOMPARE(resultChanged.count(), 1); + + model.resetContext(context); + QCOMPARE(resultChanged.count(), 2); + QCOMPARE(newContextSet.count(), 1); + + Q_EMIT context->fireSuccessMessageChanged(); + QCOMPARE(resultChanged.count(), 3); + } + + + void test_GetResultString() + { + ChangePinModel model; + QSharedPointer context(new ChangePinContext()); + + QCOMPARE(model.getResultString(), QString()); + + context->setStatus(GlobalStatus::Code::No_Error); + context->setSuccessMessage(QStringLiteral("success")); + model.mContext = context; + QCOMPARE(model.getResultString(), QStringLiteral("success")); + } + + +}; + +QTEST_GUILESS_MAIN(test_ChangePinModel) +#include "test_ChangePinModel.moc" diff --git a/test/qt/qml/test_ChatModel.cpp b/test/qt/qml/test_ChatModel.cpp new file mode 100644 index 0000000..22eb80d --- /dev/null +++ b/test/qt/qml/test_ChatModel.cpp @@ -0,0 +1,146 @@ +/*! + * \brief Unit tests for \ref ChatModel + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "ChatModel.h" + +#include "context/SelfAuthContext.h" + +#include "MockActivationContext.h" + +#include +#include + + +using namespace governikus; + + +class test_ChatModel + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void test_ResetContext() + { + ChatModel model; + QSharedPointer activationContext(new MockActivationContext()); + QSharedPointer authContext(new AuthContext(activationContext)); + QSharedPointer selfAuthContext(new SelfAuthContext()); + + model.resetContext(nullptr); + QVERIFY(model.mAllRights.contains(AccessRight::READ_DG05)); + QVERIFY(model.mAllRights.contains(AccessRight::READ_DG13)); + QVERIFY(model.mAllRights.contains(AccessRight::READ_DG04)); + QVERIFY(model.mAllRights.contains(AccessRight::READ_DG07)); + QVERIFY(model.mAllRights.contains(AccessRight::READ_DG08)); + QVERIFY(model.mAllRights.contains(AccessRight::READ_DG09)); + QVERIFY(model.mAllRights.contains(AccessRight::READ_DG17)); + QVERIFY(model.mAllRights.contains(AccessRight::READ_DG01)); + QVERIFY(model.mAllRights.contains(AccessRight::READ_DG10)); + QVERIFY(model.mAllRights.contains(AccessRight::READ_DG06)); + QVERIFY(model.mAllRights.contains(AccessRight::READ_DG02)); + QVERIFY(model.mAllRights.contains(AccessRight::READ_DG19)); + QCOMPARE(model.mSelectedRights, model.mAllRights.toSet()); + + model.resetContext(selfAuthContext); + QVERIFY(model.mAllRights.contains(AccessRight::READ_DG05)); + QVERIFY(model.mAllRights.contains(AccessRight::READ_DG13)); + QVERIFY(model.mAllRights.contains(AccessRight::READ_DG04)); + QVERIFY(model.mAllRights.contains(AccessRight::READ_DG07)); + QVERIFY(model.mAllRights.contains(AccessRight::READ_DG08)); + QVERIFY(model.mAllRights.contains(AccessRight::READ_DG09)); + QVERIFY(model.mAllRights.contains(AccessRight::READ_DG17)); + QVERIFY(model.mAllRights.contains(AccessRight::READ_DG01)); + QVERIFY(model.mAllRights.contains(AccessRight::READ_DG10)); + QVERIFY(model.mAllRights.contains(AccessRight::READ_DG06)); + QVERIFY(model.mAllRights.contains(AccessRight::READ_DG02)); + QVERIFY(model.mAllRights.contains(AccessRight::READ_DG19)); + QCOMPARE(model.mSelectedRights, model.mAllRights.toSet()); + + model.resetContext(authContext); + QVERIFY(model.mAllRights.isEmpty()); + QVERIFY(model.mOptionalRights.isEmpty()); + QVERIFY(model.mSelectedRights.isEmpty()); + } + + + void test_OnAuthenticationDataChanged() + { + ChatModel model; + QSharedPointer activationContext(new MockActivationContext()); + QSharedPointer authContext(new AuthContext(activationContext)); + model.mAuthContext = authContext; + + model.onAuthenticationDataChanged(); + QVERIFY(model.mAllRights.isEmpty()); + QVERIFY(model.mOptionalRights.isEmpty()); + QVERIFY(model.mSelectedRights.isEmpty()); + + authContext->mRequiredAccessRights += AccessRight::READ_DG01; + authContext->mRequiredAccessRights += AccessRight::READ_DG04; + + model.onAuthenticationDataChanged(); + QVERIFY(model.mAllRights.contains(AccessRight::READ_DG01)); + QVERIFY(model.mAllRights.contains(AccessRight::READ_DG04)); + QVERIFY(model.mSelectedRights.contains(AccessRight::READ_DG01)); + QVERIFY(model.mSelectedRights.contains(AccessRight::READ_DG04)); + QVERIFY(model.mOptionalRights.isEmpty()); + + authContext->mOptionalAccessRights += AccessRight::READ_DG10; + authContext->mOptionalAccessRights += AccessRight::READ_DG17; + + model.onAuthenticationDataChanged(); + QVERIFY(model.mAllRights.contains(AccessRight::READ_DG01)); + QVERIFY(model.mAllRights.contains(AccessRight::READ_DG04)); + QVERIFY(model.mAllRights.contains(AccessRight::READ_DG10)); + QVERIFY(model.mAllRights.contains(AccessRight::READ_DG17)); + QVERIFY(model.mSelectedRights.contains(AccessRight::READ_DG01)); + QVERIFY(model.mSelectedRights.contains(AccessRight::READ_DG04)); + QVERIFY(model.mSelectedRights.contains(AccessRight::READ_DG10)); + QVERIFY(model.mSelectedRights.contains(AccessRight::READ_DG17)); + QVERIFY(model.mSelectedRights.contains(AccessRight::READ_DG10)); + QVERIFY(model.mSelectedRights.contains(AccessRight::READ_DG17)); + } + + + void test_SetOrderedAllRights() + { + ChatModel model; + QSet rights; + rights.insert(AccessRight::READ_DG01); + rights.insert(AccessRight::INSTALL_QUAL_CERT); + rights.insert(AccessRight::CAN_ALLOWED); + model.setOrderedAllRights(rights); + QVERIFY(model.mAllRights.contains(AccessRight::READ_DG01)); + QVERIFY(model.mAllRights.contains(AccessRight::INSTALL_QUAL_CERT)); + QVERIFY(!model.mAllRights.contains(AccessRight::CAN_ALLOWED)); + } + + + void test_TransferAccessRights() + { + ChatModel model; + QSharedPointer activationContext(new MockActivationContext()); + QSharedPointer authContext(new AuthContext(activationContext)); + authContext->mOptionalAccessRights += model.mSelectedRights += AccessRight::INSTALL_QUAL_CERT; + authContext->mOptionalAccessRights += model.mSelectedRights += AccessRight::READ_DG01; + QVERIFY(!authContext->getEffectiveAccessRights().contains(AccessRight::INSTALL_QUAL_CERT)); + QVERIFY(!authContext->getEffectiveAccessRights().contains(AccessRight::READ_DG01)); + + model.mAuthContext = authContext; + model.mSelectedRights += AccessRight::INSTALL_QUAL_CERT; + model.mSelectedRights += AccessRight::READ_DG01; + + model.transferAccessRights(); + QVERIFY(authContext->getEffectiveAccessRights().contains(AccessRight::INSTALL_QUAL_CERT)); + QVERIFY(authContext->getEffectiveAccessRights().contains(AccessRight::READ_DG01)); + } + + +}; + +QTEST_GUILESS_MAIN(test_ChatModel) +#include "test_ChatModel.moc" diff --git a/test/qt/qml/test_ConnectivityManager.cpp b/test/qt/qml/test_ConnectivityManager.cpp new file mode 100644 index 0000000..85938ba --- /dev/null +++ b/test/qt/qml/test_ConnectivityManager.cpp @@ -0,0 +1,78 @@ +/*! + * \brief Unit tests for \ref ConnectivityManager + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "ConnectivityManager.h" + +#include "LogHandler.h" + +#include +#include + + +using namespace governikus; + + +class test_ConnectivityManager + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void initTestCase() + { + Env::getSingleton()->init(); + } + + + void test_Active() + { + ConnectivityManager manager; + const QString name = QStringLiteral("name"); + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + QSignalSpy signalSpy(&manager, &ConnectivityManager::fireNetworkInterfaceActiveChanged); + + QVERIFY(!manager.isNetworkInterfaceActive()); + + manager.setActive(true, name); + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.takeFirst().at(0).toString().contains("Found active network interface")); + QCOMPARE(signalSpy.count(), 1); + QVERIFY(manager.isNetworkInterfaceActive()); + + manager.setActive(false, name); + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.takeFirst().at(0).toString().contains("Found no active network interface")); + QCOMPARE(signalSpy.count(), 2); + QVERIFY(!manager.isNetworkInterfaceActive()); + } + + + void test_Watching() + { + ConnectivityManager manager; + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + manager.startWatching(); + QVERIFY(manager.mTimerId != 0); + QCOMPARE(logSpy.count(), 1); + + manager.startWatching(); + QCOMPARE(logSpy.count(), 2); + QVERIFY(logSpy.at(1).at(0).toString().contains("Already started, skip")); + + manager.stopWatching(); + QCOMPARE(manager.mTimerId, 0); + + manager.stopWatching(); + QCOMPARE(logSpy.count(), 3); + QVERIFY(logSpy.at(2).at(0).toString().contains("Already stopped, skip")); + } + + +}; + +QTEST_GUILESS_MAIN(test_ConnectivityManager) +#include "test_ConnectivityManager.moc" diff --git a/test/qt/qml/test_LogModel.cpp b/test/qt/qml/test_LogModel.cpp new file mode 100644 index 0000000..91a481f --- /dev/null +++ b/test/qt/qml/test_LogModel.cpp @@ -0,0 +1,265 @@ +/*! + * \brief Unit tests for \ref LogModel + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "LogModel.h" + +#include "LogHandler.h" + +#include +#include + + +using namespace governikus; + +class test_LogModel + : public QObject +{ + Q_OBJECT + QSharedPointer mModel; + + private Q_SLOTS: + void init() + { + mModel.reset(new LogModel()); + } + + + void cleanup() + { + mModel.clear(); + } + + + void test_AddLogEntry_data() + { + QTest::addColumn("input"); + QTest::addColumn("entry1"); + QTest::addColumn("entry2"); + + QTest::newRow("validEntry") << QString("input : test") << QString("input") << QString("test"); + QTest::newRow("empty") << QString(" : ") << QString() << QString(); + QTest::newRow("leftEmpty") << QString(" : test") << QString() << QString("test"); + QTest::newRow("rightEmpty") << QString("input : ") << QString("input") << QString(); + QTest::newRow("NoSpaceRight") << QString("input :test") << QString("input :test") << QString(); + QTest::newRow("NoSpaceLeft") << QString("input: test") << QString("input: test") << QString(); + QTest::newRow("invalidEntry") << QString("inputTest") << QString("inputTest") << QString(); + QTest::newRow("NoSpaces") << QString("input:test") << QString("input:test") << QString(); + QTest::newRow("emptyString") << QString() << QString() << QString(); + QTest::newRow("::") << QString("::") << QString("::") << QString(); + QTest::newRow("a : b : c") << QString("a : b : c") << QString("a") << QString("b : c"); + } + + + void test_AddLogEntry() + { + QFETCH(QString, input); + QFETCH(QString, entry1); + QFETCH(QString, entry2); + + mModel->addLogEntry(input); + QCOMPARE(mModel->mLogEntries.at(0), entry1); + QCOMPARE(mModel->mLogEntries.at(1), entry2); + } + + + void test_MoveViewNoChange_data() + { + QTest::addColumn("fileName"); + + QTest::newRow("Empty") << QString(":/logfiles/empty.txt"); + QTest::newRow("Size78") << QString(":/logfiles/size78.txt"); + QTest::newRow("Size80") << QString(":/logfiles/size80.txt"); + } + + + void test_MoveViewNoChange() + { + QFETCH(QString, fileName); + QFile file(fileName); + file.open(QIODevice::ReadOnly); + QTextStream stream(&file); + mModel->setLogEntries(stream); + + QSignalSpy spyRemove(mModel.data(), &LogModel::rowsRemoved); + QSignalSpy spyInsert(mModel.data(), &LogModel::rowsInserted); + QSignalSpy spyVisibleAreaChanged(mModel.data(), &LogModel::fireVisibleAreaChanged); + + for (int i = -5; i <= 5; i++) + { + mModel->moveView(i); + QCOMPARE(spyRemove.count(), 0); + QCOMPARE(spyInsert.count(), 0); + QCOMPARE(spyVisibleAreaChanged.count(), 0); + } + } + + + void test_MoveView_data() + { + QTest::addColumn("fileName"); + QTest::addColumn("input"); + QTest::addColumn("index"); + QTest::addColumn("rowsRemovedStart"); + QTest::addColumn("rowsRemovedEnd"); + QTest::addColumn("rowsInsertedStart"); + QTest::addColumn("rowsInsertedEnd"); + QTest::addColumn("newIndex"); + QTest::addColumn("dataNewIndex"); + + QTest::newRow("ScrollUpSuccessful_Size82") << QString(":/logfiles/size82.txt") << -2 << 2 << 78 << 79 << 0 << 1 << 0 << QString("1 input"); + QTest::newRow("ScrollDownSuccessful_Size82") << QString(":/logfiles/size82.txt") << 1 << 0 << 0 << 0 << 79 << 79 << 1 << QString("1 test"); + QTest::newRow("ScrollUp_DistanceOutOfRange_Size82") << QString(":/logfiles/size82.txt") << -50 << 20 << 60 << 79 << 0 << 19 << 0 << QString("1 input"); + QTest::newRow("ScrollDown_DistanceOutOfRange_Size82") << QString(":/logfiles/size82.txt") << 50 << 70 << 12 << 79 << 0 << 67 << 2 << QString("2 input"); + + QTest::newRow("ScrollUpSuccessful_Size160") << QString(":/logfiles/size160.txt") << -20 << 50 << 60 << 79 << 0 << 19 << 30 << QString("16 input"); + QTest::newRow("ScrollDownSuccessful_Size160") << QString(":/logfiles/size160.txt") << 25 << 0 << 0 << 24 << 55 << 79 << 25 << QString("13 test"); + QTest::newRow("ScrollUp_DistanceOutOfRange_Size160") << QString(":/logfiles/size160.txt") << -41 << 40 << 40 << 79 << 0 << 39 << 0 << QString("1 input"); + QTest::newRow("ScrollDown_DistanceOutOfRange_Size160") << QString(":/logfiles/size160.txt") << 82 << 79 << 0 << 0 << 79 << 79 << 80 << QString("41 input"); + } + + + void test_MoveView() + { + QFETCH(QString, fileName); + QFETCH(int, index); + QFETCH(int, input); + QFETCH(int, rowsRemovedStart); + QFETCH(int, rowsRemovedEnd); + QFETCH(int, rowsInsertedStart); + QFETCH(int, rowsInsertedEnd); + QFETCH(int, newIndex); + QFETCH(QString, dataNewIndex); + + QFile file(fileName); + file.open(QIODevice::ReadOnly); + QTextStream stream(&file); + mModel->setLogEntries(stream); + mModel->mIndex = index; + QSignalSpy spyRemove(mModel.data(), &LogModel::rowsRemoved); + QSignalSpy spyInsert(mModel.data(), &LogModel::rowsInserted); + QSignalSpy spyVisibleAreaChanged(mModel.data(), &LogModel::fireVisibleAreaChanged); + + mModel->moveView(input); + QCOMPARE(spyRemove.count(), 1); + QCOMPARE(spyInsert.count(), 1); + QCOMPARE(spyVisibleAreaChanged.count(), 1); + QList argumentsRemove = spyRemove.takeFirst(); + QCOMPARE(argumentsRemove.at(1).toInt(), rowsRemovedStart); + QCOMPARE(argumentsRemove.at(2).toInt(), rowsRemovedEnd); + QList argumentsInsert = spyInsert.takeFirst(); + QCOMPARE(argumentsInsert.at(1).toInt(), rowsInsertedStart); + QCOMPARE(argumentsInsert.at(2).toInt(), rowsInsertedEnd); + QCOMPARE(mModel->mIndex, newIndex); + + if (!mModel->mLogEntries.isEmpty() && !mModel->mLogEntries.at(newIndex).isNull()) + { + QModelIndex indexBegin = mModel->createIndex(0, 0); + QCOMPARE(mModel->data(indexBegin, 0), QVariant(dataNewIndex)); + } + } + + + void test_SetLogEntries_data() + { + QTest::addColumn("fileName"); + QTest::addColumn("logEntriesSize"); + QTest::addColumn("count"); + + QTest::newRow("empty") << QString(":/logfiles/empty.txt") << 0 << 0; + QTest::newRow("size78") << QString(":/logfiles/size78.txt") << 78 << 78; + QTest::newRow("size80") << QString(":/logfiles/size80.txt") << 80 << 80; + QTest::newRow("size82") << QString(":/logfiles/size82.txt") << 82 << 80; + QTest::newRow("size160") << QString(":/logfiles/size160.txt") << 160 << 80; + } + + + void test_SetLogEntries() + { + QFETCH(QString, fileName); + QFETCH(int, logEntriesSize); + QFETCH(int, count); + + QFile file(fileName); + file.open(QIODevice::ReadOnly); + QTextStream stream(&file); + QSignalSpy spy(mModel.data(), &LogModel::fireVisibleAreaChanged); + + mModel->setLogEntries(stream); + QCOMPARE(mModel->mLogEntries.size(), logEntriesSize); + QCOMPARE(mModel->mCount, count); + QCOMPARE(mModel->mIndex, 0); + QCOMPARE(spy.count(), 1); + } + + + void test_OnNewLogMsg_data() + { + QTest::addColumn("msg"); + QTest::addColumn("fileName"); + QTest::addColumn("autoFlick"); + QTest::addColumn("index"); + QTest::addColumn("selectedFile"); + QTest::addColumn("visibleAreaChangedCounter"); + QTest::addColumn("newLogMsgCounter"); + + QTest::newRow("emptyFile_MsgAdded") << QString(" : ") << QString(":/logfiles/empty.txt") << true << 1 << 0 << 1 << 0; + QTest::newRow("emptyFile_MsgNotAdded") << QString(" : ") << QString(":/logfiles/empty.txt") << false << 0 << 1 << 0 << 0; + QTest::newRow("emptyFile_MsgAdded_ViewChanged") << QString("test : input") << QString(":/logfiles/empty.txt") << true << 0 << 0 << 2 << 1; + + QTest::newRow("MsgAdded_Size78") << QString() << QString(":/logfiles/size78.txt") << false << 5 << 0 << 1 << 0; + QTest::newRow("MsgAdded_ViewChanged_Size78") << QString("test : input") << QString(":/logfiles/size78.txt") << true << 0 << 0 << 2 << 1; + QTest::newRow("MsgNotAdded_Size78") << QString("test : input") << QString(":/logfiles/size78.txt") << true << 5 << 2 << 0 << 0; + + QTest::newRow("MsgAdded_Size80") << QString() << QString(":/logfiles/size80.txt") << false << 5 << 0 << 1 << 0; + QTest::newRow("MsgNotAdded_Size80") << QString("test : input") << QString(":/logfiles/size80.txt") << false << 0 << 1 << 0 << 0; + QTest::newRow("MsgAdded_ViewChanged_Size80") << QString("test : input") << QString(":/logfiles/size80.txt") << true << 0 << 0 << 2 << 1; + + QTest::newRow("MsgAdded_Size82") << QString("test : input") << QString(":/logfiles/size82.txt") << true << 0 << 0 << 1 << 0; + QTest::newRow("MsgNotAdded_Size82") << QString(" : ") << QString(":/logfiles/size82.txt") << true << 0 << 3 << 0 << 0; + QTest::newRow("MsgAdded_ViewChanged_Size82") << QString("test : input") << QString(":/logfiles/size82.txt") << true << 2 << 0 << 2 << 1; + } + + + void test_OnNewLogMsg() + { + QFETCH(QString, msg); + QFETCH(QString, fileName); + QFETCH(bool, autoFlick); + QFETCH(int, index); + QFETCH(int, selectedFile); + QFETCH(int, visibleAreaChangedCounter); + QFETCH(int, newLogMsgCounter); + + QFile file(fileName); + file.open(QIODevice::ReadOnly); + QTextStream stream(&file); + mModel->setLogEntries(stream); + int oldSize = mModel->mLogEntries.size(); + mModel->mSelectedLogFile = selectedFile; + mModel->mIndex = index; + mModel->mAutoFlick = autoFlick; + QSignalSpy spyVisibleAreaChanged(mModel.data(), &LogModel::fireVisibleAreaChanged); + QSignalSpy spyNewLogMsg(mModel.data(), &LogModel::fireNewLogMsg); + + mModel->onNewLogMsg(msg); + QCOMPARE(spyVisibleAreaChanged.count(), visibleAreaChangedCounter); + QCOMPARE(spyNewLogMsg.count(), newLogMsgCounter); + + if (visibleAreaChangedCounter == 0) + { + QCOMPARE(mModel->mLogEntries.size(), oldSize); + } + else + { + QCOMPARE(mModel->mLogEntries.size(), oldSize + 2); + } + } + + +}; + +QTEST_GUILESS_MAIN(test_LogModel) +#include "test_LogModel.moc" diff --git a/test/qt/qml/test_NumberModel.cpp b/test/qt/qml/test_NumberModel.cpp new file mode 100644 index 0000000..78eb706 --- /dev/null +++ b/test/qt/qml/test_NumberModel.cpp @@ -0,0 +1,356 @@ +/*! + * \brief Unit tests for \ref NumberModel + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "NumberModel.h" + +#include "context/ChangePinContext.h" +#include "context/RemoteServiceContext.h" + +#include "MockCardConnectionWorker.h" + +#include +#include + +using namespace governikus; + + +class test_NumberModel + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void test_ResetContext() + { + NumberModel model; + QSharedPointer context(new WorkflowContext()); + QSharedPointer pinContext(new ChangePinContext()); + + QSignalSpy spyCanChanged(&model, &NumberModel::fireCanChanged); + QSignalSpy spyPinChanged(&model, &NumberModel::firePinChanged); + QSignalSpy spyNewPinChanged(&model, &NumberModel::fireNewPinChanged); + QSignalSpy spyCanAllowed(&model, &NumberModel::fireCanAllowedModeChanged); + QSignalSpy spyPukChanged(&model, &NumberModel::firePukChanged); + QSignalSpy spyReaderNameChanged(&model, &NumberModel::fireReaderInfoChanged); + QSignalSpy spyLastPaceResultChanged(&model, &NumberModel::fireInputErrorChanged); + + model.resetContext(); + QCOMPARE(spyCanChanged.count(), 1); + QCOMPARE(spyPinChanged.count(), 1); + QCOMPARE(spyNewPinChanged.count(), 1); + QCOMPARE(spyCanAllowed.count(), 1); + QCOMPARE(spyPukChanged.count(), 1); + QCOMPARE(spyReaderNameChanged.count(), 1); + QCOMPARE(spyLastPaceResultChanged.count(), 1); + + model.resetContext(context); + QCOMPARE(model.mContext, context); + QCOMPARE(spyCanChanged.count(), 2); + QCOMPARE(spyPinChanged.count(), 2); + QCOMPARE(spyNewPinChanged.count(), 2); + QCOMPARE(spyCanAllowed.count(), 2); + QCOMPARE(spyPukChanged.count(), 2); + QCOMPARE(spyReaderNameChanged.count(), 2); + QCOMPARE(spyLastPaceResultChanged.count(), 2); + Q_EMIT context->fireCanChanged(); + QCOMPARE(spyCanChanged.count(), 3); + Q_EMIT context->firePinChanged(); + QCOMPARE(spyPinChanged.count(), 3); + Q_EMIT context->fireCanAllowedModeChanged(); + QCOMPARE(spyCanAllowed.count(), 3); + Q_EMIT context->fireReaderNameChanged(); + QCOMPARE(spyReaderNameChanged.count(), 3); + Q_EMIT context->fireLastPaceResultChanged(); + QCOMPARE(spyLastPaceResultChanged.count(), 3); + + model.resetContext(pinContext); + QCOMPARE(model.mContext, pinContext); + QCOMPARE(spyCanChanged.count(), 4); + QCOMPARE(spyPinChanged.count(), 4); + QCOMPARE(spyNewPinChanged.count(), 3); + QCOMPARE(spyCanAllowed.count(), 4); + QCOMPARE(spyPukChanged.count(), 3); + QCOMPARE(spyReaderNameChanged.count(), 4); + QCOMPARE(spyLastPaceResultChanged.count(), 4); + + Q_EMIT pinContext->fireCanChanged(); + QCOMPARE(spyCanChanged.count(), 5); + Q_EMIT pinContext->firePinChanged(); + QCOMPARE(spyPinChanged.count(), 5); + Q_EMIT pinContext->fireCanAllowedModeChanged(); + QCOMPARE(spyCanAllowed.count(), 5); + Q_EMIT pinContext->fireReaderNameChanged(); + QCOMPARE(spyReaderNameChanged.count(), 5); + Q_EMIT pinContext->fireLastPaceResultChanged(); + QCOMPARE(spyLastPaceResultChanged.count(), 5); + Q_EMIT pinContext->fireNewPinChanged(); + QCOMPARE(spyNewPinChanged.count(), 4); + Q_EMIT pinContext->firePukChanged(); + QCOMPARE(spyPukChanged.count(), 4); + } + + + void test_Can() + { + NumberModel model; + QSharedPointer context(new WorkflowContext()); + + const QString can = QStringLiteral("111111"); + model.setCan(can); + QCOMPARE(model.getCan(), QString()); + + model.mContext = context; + model.setCan(can); + QCOMPARE(model.getCan(), can); + QCOMPARE(context->getCan(), can); + } + + + void test_Pin() + { + NumberModel model; + QSharedPointer context(new WorkflowContext()); + + const QString pin = QStringLiteral("111111"); + model.setPin(pin); + QCOMPARE(model.getPin(), QString()); + + model.mContext = context; + model.setPin(pin); + QCOMPARE(model.getPin(), pin); + QCOMPARE(context->getPin(), pin); + } + + + void test_NewPin() + { + NumberModel model; + QSharedPointer workflowContext(new WorkflowContext()); + QSharedPointer changePinContext(new ChangePinContext()); + QSharedPointer remoteServiceContext(new RemoteServiceContext()); + const QString pin = QStringLiteral("111111"); + + model.mContext = workflowContext; + model.setNewPin(pin); + QCOMPARE(model.getNewPin(), QString()); + + model.mContext = changePinContext; + model.setNewPin(pin); + QCOMPARE(changePinContext->getNewPin(), pin); + QCOMPARE(model.getNewPin(), pin); + + model.mContext = remoteServiceContext; + model.setNewPin(pin); + QCOMPARE(remoteServiceContext->getNewPin(), pin); + QCOMPARE(model.getNewPin(), QString()); + } + + + void test_Puk() + { + NumberModel model; + QSharedPointer workflowContext(new WorkflowContext()); + const QString puk = QStringLiteral("111111"); + + model.mContext = workflowContext; + model.setPuk(puk); + QCOMPARE(workflowContext->getPuk(), puk); + QCOMPARE(model.getPuk(), puk); + } + + + void test_OnReaderInfoChanged() + { + NumberModel model; + QSharedPointer context(new WorkflowContext()); + QSignalSpy spyReaderNameChanged(&model, &NumberModel::fireReaderInfoChanged); + + const QString readerName = QStringLiteral("name"); + const QString test = QStringLiteral("test"); + + model.onReaderInfoChanged(readerName); + QCOMPARE(spyReaderNameChanged.count(), 0); + + context->setReaderName(test); + model.mContext = context; + model.onReaderInfoChanged(readerName); + QCOMPARE(spyReaderNameChanged.count(), 0); + + context->setReaderName(readerName); + model.mContext = context; + model.onReaderInfoChanged(readerName); + QCOMPARE(spyReaderNameChanged.count(), 1); + } + + + void test_CanAllowed() + { + NumberModel model; + QSharedPointer context(new WorkflowContext()); + + QVERIFY(!model.isCanAllowedMode()); + + model.mContext = context; + QVERIFY(!model.isCanAllowedMode()); + + context->setCanAllowedMode(true); + QVERIFY(model.isCanAllowedMode()); + } + + + void test_PinDeactivatedFalse() + { + NumberModel model; + QSharedPointer context(new WorkflowContext()); + + QVERIFY(!model.isPinDeactivated()); + + model.mContext = context; + QVERIFY(!model.isPinDeactivated()); + } + + + void test_Error() + { + QThread connectionThread; + connectionThread.start(); + + NumberModel model; + QSharedPointer context(new WorkflowContext()); + + QCOMPARE(model.getInputError(), QString()); + QVERIFY(!model.hasError()); + + context->setLastPaceResult(CardReturnCode::OK); + model.mContext = context; + QCOMPARE(model.getInputError(), QString()); + QVERIFY(!model.hasError()); + + context->setLastPaceResult(CardReturnCode::CANCELLATION_BY_USER); + QCOMPARE(model.getInputError(), QString()); + QVERIFY(!model.hasError()); + + QSharedPointer worker(new MockCardConnectionWorker()); + worker->moveToThread(&connectionThread); + QSharedPointer connection(new CardConnection(worker)); + context->setCardConnection(connection); + + context->setLastPaceResult(CardReturnCode::INVALID_PIN); + QCOMPARE(model.getInputError(), tr("The given PIN is not correct. You have 2 tries to enter the correct PIN.")); + QVERIFY(model.hasError()); + + context->setLastPaceResult(CardReturnCode::INVALID_PIN_2); + QCOMPARE(model.getInputError(), tr("You have entered the wrong PIN twice. " + "Prior to a third attempt, you have to enter your six-digit card access number first. " + "You can find your card access number on the front of your ID card.")); + QVERIFY(model.hasError()); + + context->setLastPaceResult(CardReturnCode::INVALID_PIN_3); + QCOMPARE(model.getInputError(), tr("You have entered a wrong PIN three times. " + "Your PIN is now blocked. " + "You have to enter the PUK now for unblocking.")); + QVERIFY(model.hasError()); + + context->setLastPaceResult(CardReturnCode::INVALID_CAN); + model.mContext = context; + QCOMPARE(model.getInputError(), tr("You have entered a wrong CAN, please try again.")); + + context->setLastPaceResult(CardReturnCode::INVALID_PUK); + QCOMPARE(model.getInputError(), tr("You have entered a wrong PUK. " + "Please try again.")); + QVERIFY(model.hasError()); + + context->setLastPaceResult(CardReturnCode::UNKNOWN); + QCOMPARE(model.getInputError(), tr("An unexpected error has occurred during processing.")); + + context->setLastPaceResult(CardReturnCode::UNEXPECTED_TRANSMIT_STATUS); + model.mContext = context; + QCOMPARE(model.getInputError(), QStringLiteral("%1 %3.").arg( + tr("A protocol error occurred. Please make sure that your ID card is placed correctly on the card reader and try again. If the problem occurs again, please contact our support at"), + tr("https://www.ausweisapp.bund.de/en/service/support/"), + tr("AusweisApp2 Support"))); + QVERIFY(model.hasError()); + + connectionThread.quit(); + connectionThread.wait(); + } + + + void test_GetRetryCounter() + { + QThread connectionThread; + connectionThread.start(); + + NumberModel model; + QSharedPointer context(new WorkflowContext()); + + QCOMPARE(model.getRetryCounter(), -1); + + model.mContext = context; + QCOMPARE(model.getRetryCounter(), -1); + + const QString name = QStringLiteral("name"); + CardInfo cardInfo(CardType::EID_CARD, QSharedPointer(), + 3, true, false); + MockReader reader(name); + reader.getReaderInfo().setCardInfo(cardInfo); + QSharedPointer worker(new MockCardConnectionWorker(&reader)); + worker->moveToThread(&connectionThread); + QSharedPointer connection(new CardConnection(worker)); + context->setCardConnection(connection); + QCOMPARE(model.getRetryCounter(), 3); + + connectionThread.quit(); + connectionThread.wait(); + } + + + void test_RequestTransportPin_data() + { + QTest::addColumn >("context"); + QTest::addColumn("contextRequestedTransportPin"); + + QTest::newRow("WorkflowContext") << QSharedPointer(new WorkflowContext()) << false; + QTest::newRow("ChangePinContext") << QSharedPointer(new ChangePinContext()) << false; + QTest::newRow("ChangePinContext-false") << QSharedPointer(new ChangePinContext(false)) << false; + QTest::newRow("ChangePinContext-true") << QSharedPointer(new ChangePinContext(true)) << true; + } + + + void test_RequestTransportPin() + { + QFETCH(QSharedPointer, context); + QFETCH(bool, contextRequestedTransportPin); + + NumberModel model; + QVERIFY(!model.isRequestTransportPin()); + + model.setRequestTransportPin(true); + QVERIFY(model.isRequestTransportPin()); + model.setRequestTransportPin(false); + QVERIFY(!model.isRequestTransportPin()); + + model.resetContext(context); + QCOMPARE(model.isRequestTransportPin(), contextRequestedTransportPin); + + model.setRequestTransportPin(true); + model.resetContext(context); + QVERIFY(model.isRequestTransportPin()); + + model.setRequestTransportPin(false); + QVERIFY(!model.isRequestTransportPin()); + model.setRequestTransportPin(true); + QVERIFY(model.isRequestTransportPin()); + + model.resetContext(); + QVERIFY(!model.isRequestTransportPin()); + } + + +}; + +QTEST_GUILESS_MAIN(test_NumberModel) +#include "test_NumberModel.moc" diff --git a/test/qt/qml/test_ProviderCategoryFilterModel.cpp b/test/qt/qml/test_ProviderCategoryFilterModel.cpp new file mode 100644 index 0000000..b388e85 --- /dev/null +++ b/test/qt/qml/test_ProviderCategoryFilterModel.cpp @@ -0,0 +1,78 @@ +/*! + * \brief Unit tests for \ref ProviderCategoryFilterModel + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "ProviderCategoryFilterModel.h" + +#include + + +using namespace governikus; + + +class test_ProviderCategoryFilterModel + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void test_UpdateSearchString() + { + ProviderCategoryFilterModel model; + QSignalSpy spy(&model, &ProviderCategoryFilterModel::fireCriteriaChanged); + const QString search = QStringLiteral("search"); + + model.updateSearchString(search); + QCOMPARE(model.getSearchString(), search); + QCOMPARE(spy.count(), 1); + } + + + void test_CategorySelection() + { + ProviderCategoryFilterModel model; + QSignalSpy spy(&model, &ProviderCategoryFilterModel::fireCriteriaChanged); + const QString category1 = QStringLiteral("CATEGORY"); + const QString category2 = QStringLiteral("testCATEGORY"); + + model.setCategorySelection(QString()); + QVERIFY(model.mSelectedCategories.isEmpty()); + QCOMPARE(spy.count(), 1); + + model.setCategorySelection(category1); + QVERIFY(model.mSelectedCategories.contains("category")); + QCOMPARE(spy.count(), 2); + + model.updateCategorySelection(category2, true); + QVERIFY(model.mSelectedCategories.contains("testcategory")); + QCOMPARE(spy.count(), 3); + + model.updateCategorySelection(category2, true); + QVERIFY(model.mSelectedCategories.contains("testcategory")); + QCOMPARE(spy.count(), 3); + + model.updateCategorySelection(category1, false); + QVERIFY(model.mSelectedCategories.contains("testcategory")); + QVERIFY(!model.mSelectedCategories.contains("category")); + QCOMPARE(spy.count(), 4); + } + + + void test_SortByCategoryFirst() + { + ProviderCategoryFilterModel model; + + model.sortByCategoryFirst(true); + QCOMPARE(model.sortRole(), ProviderModel::SORT_ROLE); + + model.sortByCategoryFirst(false); + QCOMPARE(model.sortRole(), ProviderModel::SHORTNAME); + } + + +}; + +QTEST_GUILESS_MAIN(test_ProviderCategoryFilterModel) +#include "test_ProviderCategoryFilterModel.moc" diff --git a/test/qt/qml/test_QmlFileStructure.cpp b/test/qt/qml/test_QmlFileStructure.cpp new file mode 100644 index 0000000..e16ed26 --- /dev/null +++ b/test/qt/qml/test_QmlFileStructure.cpp @@ -0,0 +1,106 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "ResourceLoader.h" + +#include +#include +#include + +using namespace governikus; + +class test_QmlFileStructure + : public QObject +{ + Q_OBJECT + + private: + QStringList mQmlFiles; + + private Q_SLOTS: + void initTestCase() + { + ResourceLoader::getInstance().init(); + + QDirIterator iterator(QStringLiteral(":"), {QStringLiteral("*.qml")}, QDir::Files | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); + while (iterator.hasNext()) + { + const auto& file = iterator.next(); + if (!file.endsWith(QStringLiteral("main.qml"))) + { + mQmlFiles += file; + } + } + } + + + void qmlFileStructureIsSane_data() + { + QTest::addColumn("qmlFile"); + + for (const QString& file : qAsConst(mQmlFiles)) + { + const QFileInfo info(file); + QTest::newRow(info.fileName().toLatin1().data()) << info; + } + } + + + void qmlFileStructureIsSane() + { + QFETCH(QFileInfo, qmlFile); + + QDir dir = qmlFile.dir(); + while (dir.dirName().startsWith(QChar('+'))) + { + dir.cdUp(); + const QString fileInParentFolder = dir.path() + QDir::separator() + qmlFile.fileName(); + QVERIFY(!mQmlFiles.contains(fileInParentFolder)); + } + } + + + void avoidMultipleDeviceFiles_data() + { + QTest::addColumn("qmlFile"); + + for (const QString& file : qAsConst(mQmlFiles)) + { + if (file.contains(QStringLiteral("+phone")) || file.contains(QStringLiteral("+tablet"))) + { + const QFileInfo info(file); + QTest::newRow(info.fileName().toLatin1().data()) << info; + } + } + } + + + void avoidMultipleDeviceFiles() + { + QFETCH(QFileInfo, qmlFile); + + QDir dir = qmlFile.dir(); + const auto& device = dir.dirName(); + dir.cdUp(); + + const auto& parentFolder = dir.dirName(); + if (parentFolder == QStringLiteral("+android") || parentFolder == QStringLiteral("+ios")) + { + dir.cdUp(); + dir.cd(device); + const QString fileInParentFolder = dir.path() + QDir::separator() + qmlFile.fileName(); + QVERIFY(!mQmlFiles.contains(fileInParentFolder)); + return; + } + + const QString file = QDir::separator() + device + QDir::separator() + qmlFile.fileName(); + QVERIFY(!mQmlFiles.contains(dir.path() + QDir::separator() + QStringLiteral("+android") + file)); + QVERIFY(!mQmlFiles.contains(dir.path() + QDir::separator() + QStringLiteral("+ios") + file)); + } + + +}; + +QTEST_GUILESS_MAIN(test_QmlFileStructure) +#include "test_QmlFileStructure.moc" diff --git a/test/qt/qml/test_RemoteServiceModel.cpp b/test/qt/qml/test_RemoteServiceModel.cpp new file mode 100644 index 0000000..0933b6b --- /dev/null +++ b/test/qt/qml/test_RemoteServiceModel.cpp @@ -0,0 +1,149 @@ +/*! + * \brief Unit tests for \ref ProviderModel + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "RemoteServiceModel.h" + +#include "context/RemoteServiceContext.h" +#include "MockRemoteServer.h" + +#include +#include + + +using namespace governikus; + +class test_RemoteServiceModel + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void initTestCase() + { + Env::setCreator(std::function([&] { + return new MockRemoteServer(); + })); + } + + + void test_ReaderPlugInType() + { + const QSharedPointer context(new RemoteServiceContext()); + RemoteServiceModel model; + const ReaderManagerPlugInType input1 = ReaderManagerPlugInType::NFC; + const ReaderManagerPlugInType input2 = ReaderManagerPlugInType::UNKNOWN; + + model.setReaderPlugInType(input1); + QCOMPARE(model.getReaderPlugInType(), ReaderManagerPlugInType::UNKNOWN); + + model.resetContext(context); + model.setReaderPlugInType(input1); + QCOMPARE(model.getReaderPlugInType(), input1); + + model.setReaderPlugInType(input2); + QCOMPARE(model.getReaderPlugInType(), ReaderManagerPlugInType::UNKNOWN); + } + + + void test_ErrorMessage() + { + RemoteServiceModel model; + + QCOMPARE(model.getErrorMessage(true, true, true), QString()); + QCOMPARE(model.getErrorMessage(false, true, true), tr("NFC is not available on your device.")); + QCOMPARE(model.getErrorMessage(true, false, true), tr("Please enable NFC to use the remote service.")); + QCOMPARE(model.getErrorMessage(true, true, false), tr("Please connect your WiFi to use the remote service.")); + } + + + void test_CancelPasswordRequest() + { + const QSharedPointer context(new RemoteServiceContext()); + RemoteServiceModel model; + QSignalSpy spy(context.data(), &RemoteServiceContext::fireCancelPasswordRequest); + + model.cancelPasswordRequest(); + QCOMPARE(spy.count(), 0); + + model.mContext = context; + model.cancelPasswordRequest(); + QCOMPARE(spy.count(), 1); + } + + + void test_Running() + { + const QSharedPointer context(new RemoteServiceContext()); + RemoteServiceModel model; + QSignalSpy spyStartWorkflow(&model, &RemoteServiceModel::fireStartWorkflow); + QSignalSpy spyCancelWorkflow(context.data(), &RemoteServiceContext::fireCancelWorkflow); + QSignalSpy spyIsRunningChanged(&model, &RemoteServiceModel::fireIsRunningChanged); + + QVERIFY(!model.isRunning()); + + model.mContext = context; + QVERIFY(!model.isRunning()); + + context->getRemoteServer()->start(QString()); + QVERIFY(model.isRunning()); + model.setRunning(false); + QCOMPARE(spyIsRunningChanged.count(), 1); + QCOMPARE(spyCancelWorkflow.count(), 1); + + context->getRemoteServer()->stop(); + model.setRunning(true); + QCOMPARE(spyIsRunningChanged.count(), 2); + QCOMPARE(spyStartWorkflow.count(), 1); + } + + + void test_ResetContext() + { + const QSharedPointer context(new RemoteServiceContext()); + RemoteServiceModel model; + + QSignalSpy spyConnectedChanged(&model, &RemoteServiceModel::fireConnectedChanged); + QSignalSpy spyCurrentStateChanged(&model, &WorkflowModel::fireCurrentStateChanged); + QSignalSpy spyIsRunningChanged(&model, &RemoteServiceModel::fireIsRunningChanged); + QSignalSpy spyPskChanged(&model, &RemoteServiceModel::firePskChanged); + QSignalSpy spyConnectedClientDeviceNameChanged(&model, &RemoteServiceModel::fireConnectedClientDeviceNameChanged); + + model.resetContext(context); + QCOMPARE(model.mContext, context); + QCOMPARE(spyCurrentStateChanged.count(), 1); + QCOMPARE(spyConnectedChanged.count(), 1); + + Q_EMIT context->fireStateChanged(QString()); + QCOMPARE(spyIsRunningChanged.count(), 1); + + Q_EMIT context->getRemoteServer()->firePskChanged(QByteArray()); + QCOMPARE(spyPskChanged.count(), 1); + + Q_EMIT context->getRemoteServer()->fireConnectedChanged(true); + QCOMPARE(spyConnectedChanged.count(), 2); + QCOMPARE(spyConnectedClientDeviceNameChanged.count(), 1); + } + + + void test_SetPairing() + { + const QSharedPointer context(new RemoteServiceContext()); + RemoteServiceModel model; + model.mContext = context; + const QSharedPointer server = qSharedPointerCast(context->getRemoteServer()); + + model.setPairing(true); + QVERIFY(server->getPairing()); + + model.setPairing(false); + QVERIFY(!server->getPairing()); + } + + +}; + +QTEST_GUILESS_MAIN(test_RemoteServiceModel) +#include "test_RemoteServiceModel.moc" diff --git a/test/qt/qml/test_SelfAuthModel.cpp b/test/qt/qml/test_SelfAuthModel.cpp new file mode 100644 index 0000000..749cd4c --- /dev/null +++ b/test/qt/qml/test_SelfAuthModel.cpp @@ -0,0 +1,106 @@ +/*! + * \brief Unit tests for \ref SelfAuthModel + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "SelfAuthModel.h" + +#include "context/SelfAuthContext.h" + +#include "MockCardConnectionWorker.h" + +#include + + +using namespace governikus; + +class test_SelfAuthModel + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void test_ResetContext() + { + SelfAuthModel model; + const QSharedPointer context(new SelfAuthContext()); + + model.resetContext(nullptr); + QVERIFY(!model.mContext); + + model.resetContext(context); + QCOMPARE(model.mContext, context); + } + + + void test_StartWorkflow() + { + SelfAuthModel model; + QSignalSpy spy(&model, &SelfAuthModel::fireStartWorkflow); + + model.startWorkflow(); + QCOMPARE(spy.count(), 1); + } + + + void test_CancelWorkflow() + { + SelfAuthModel model; + const QSharedPointer context(new SelfAuthContext()); + QSignalSpy spy(context.data(), &SelfAuthContext::fireCancelWorkflow); + + model.cancelWorkflow(); + QCOMPARE(spy.count(), 0); + + model.mContext = context; + model.cancelWorkflow(); + QCOMPARE(spy.count(), 1); + } + + + void test_IsBasicReader() + { + QThread workerThread; + workerThread.start(); + + SelfAuthModel model; + + QVERIFY(model.isBasicReader()); + + const QSharedPointer worker(new MockCardConnectionWorker()); + worker->moveToThread(&workerThread); + const QSharedPointer context(new SelfAuthContext()); + const QSharedPointer connection(new CardConnection(worker)); + context->setCardConnection(connection); + ReaderInfo info; + + info.setBasicReader(true); + connection->mReaderInfo = info; + model.mContext = context; + QVERIFY(model.isBasicReader()); + + info.setBasicReader(false); + connection->mReaderInfo = info; + QVERIFY(!model.isBasicReader()); + + workerThread.quit(); + workerThread.wait(); + } + + + void test_RoleNames() + { + SelfAuthModel model; + + QVERIFY(model.roleNames().keys().contains(SelfAuthModel::DataRoles::NAME)); + QVERIFY(model.roleNames().keys().contains(SelfAuthModel::DataRoles::VALUE)); + QVERIFY(model.roleNames().values().contains("name")); + QVERIFY(model.roleNames().values().contains("value")); + } + + +}; + +QTEST_GUILESS_MAIN(test_SelfAuthModel) +#include "test_SelfAuthModel.moc" diff --git a/test/qt/qml/test_WorkflowModel.cpp b/test/qt/qml/test_WorkflowModel.cpp new file mode 100644 index 0000000..83ef44e --- /dev/null +++ b/test/qt/qml/test_WorkflowModel.cpp @@ -0,0 +1,138 @@ +/*! + * \brief Unit tests for \ref WorkflowModel + * + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "WorkflowModel.h" + +#include "MockCardConnectionWorker.h" + +#include +#include + + +using namespace governikus; + + +class test_WorkflowModel + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void test_ResetContext() + { + WorkflowModel model; + QSharedPointer context(new WorkflowContext()); + + QSignalSpy spyCurrentStateChanged(&model, &WorkflowModel::fireCurrentStateChanged); + QSignalSpy spyResultChanged(&model, &WorkflowModel::fireResultChanged); + QSignalSpy spyReaderPlugInTypeChanged(&model, &WorkflowModel::fireReaderPlugInTypeChanged); + QSignalSpy spyIsBasicReaderChanged(&model, &WorkflowModel::fireIsBasicReaderChanged); + + model.resetContext(); + QCOMPARE(spyCurrentStateChanged.count(), 1); + QCOMPARE(spyResultChanged.count(), 1); + + model.resetContext(context); + QCOMPARE(spyCurrentStateChanged.count(), 2); + QCOMPARE(spyResultChanged.count(), 2); + + Q_EMIT context->fireStateChanged(QString("state")); + QCOMPARE(spyCurrentStateChanged.count(), 3); + + Q_EMIT context->fireResultChanged(); + QCOMPARE(spyResultChanged.count(), 3); + + Q_EMIT context->fireReaderPlugInTypesChanged(); + QCOMPARE(spyReaderPlugInTypeChanged.count(), 1); + + Q_EMIT context->fireCardConnectionChanged(); + QCOMPARE(spyIsBasicReaderChanged.count(), 1); + } + + + void test_StartWorkflow() + { + WorkflowModel model; + QSignalSpy spy(&model, &WorkflowModel::fireStartWorkflow); + model.startWorkflow(); + QCOMPARE(spy.count(), 1); + } + + + void test_CancelWorkflowOnPinBlocked() + { + WorkflowModel model; + QSharedPointer context(new WorkflowContext()); + model.mContext = context; + + model.cancelWorkflowOnPinBlocked(); + QCOMPARE(context->getStatus().getStatusCode(), GlobalStatus::Code::Workflow_Pin_Blocked_And_Puk_Objectionable); + } + + + void test_IsBasicReader() + { + QThread connectionThread; + connectionThread.start(); + + WorkflowModel model; + QSharedPointer context(new WorkflowContext()); + + QVERIFY(model.isBasicReader()); + + model.mContext = context; + QVERIFY(model.isBasicReader()); + + MockReader reader; + reader.getReaderInfo().setBasicReader(false); + QSharedPointer worker(new MockCardConnectionWorker(&reader)); + worker->moveToThread(&connectionThread); + QSharedPointer connection(new CardConnection(worker)); + context->setCardConnection(connection); + model.mContext = context; + QVERIFY(!model.isBasicReader()); + + connectionThread.quit(); + connectionThread.wait(); + } + + + void test_ReaderPlugInType() + { + WorkflowModel model; + QSharedPointer context(new WorkflowContext()); + + QCOMPARE(model.getReaderPlugInType(), ReaderManagerPlugInType::UNKNOWN); + + model.mContext = context; + QCOMPARE(model.getReaderPlugInType(), ReaderManagerPlugInType::UNKNOWN); + + model.setReaderPlugInType(ReaderManagerPlugInType::UNKNOWN); + QVERIFY(context->getReaderPlugInTypes().contains(ReaderManagerPlugInType::UNKNOWN)); + QCOMPARE(model.getReaderPlugInType(), ReaderManagerPlugInType::UNKNOWN); + + model.setReaderPlugInType(ReaderManagerPlugInType::NFC); + QVERIFY(context->getReaderPlugInTypes().contains(ReaderManagerPlugInType::NFC)); + QCOMPARE(model.getReaderPlugInType(), ReaderManagerPlugInType::NFC); + } + + + void test_SelectedReaderHasCardNoConnection() + { + WorkflowModel model; + QSharedPointer context(new WorkflowContext()); + + QVERIFY(!model.selectedReaderHasCard()); + + model.mContext = context; + QVERIFY(!model.selectedReaderHasCard()); + } + + +}; + +QTEST_GUILESS_MAIN(test_WorkflowModel) +#include "test_WorkflowModel.moc" diff --git a/test/qt/remote_device/messages/test_Discovery.cpp b/test/qt/remote_device/messages/test_Discovery.cpp new file mode 100644 index 0000000..c9e6adf --- /dev/null +++ b/test/qt/remote_device/messages/test_Discovery.cpp @@ -0,0 +1,246 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "messages/Discovery.h" + +#include "LogHandler.h" + +#include + + +using namespace governikus; + + +class test_Discovery + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void initTestCase() + { + Env::getSingleton()->init(); + } + + + void invalidJson() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray message("FooBar"); + const auto& obj = QJsonDocument::fromJson(message).object(); + QVERIFY(obj.isEmpty()); + + Discovery msg(obj); + QVERIFY(msg.isIncomplete()); + + QCOMPARE(logSpy.count(), 6); + QVERIFY(logSpy.at(0).at(0).toString().contains("Missing value \"msg\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("The value of msg should be REMOTE_IFD")); + QVERIFY(logSpy.at(2).at(0).toString().contains("Missing value \"IFDName\"")); + QVERIFY(logSpy.at(3).at(0).toString().contains("Missing value \"IFDID\"")); + QVERIFY(logSpy.at(4).at(0).toString().contains("Missing value \"port\"")); + QVERIFY(logSpy.at(5).at(0).toString().contains("Missing value \"SupportedAPI\"")); + } + + + void values() + { + const Discovery discovery( + QStringLiteral("Sony Xperia Z5 compact"), + QStringLiteral("0123456789ABCDEF"), + 24728, + {IfdVersion::Version::v0, IfdVersion::Version::v_test} + ); + + QVERIFY(!discovery.isIncomplete()); + QCOMPARE(discovery.getType(), RemoteCardMessageType::UNDEFINED); + QCOMPARE(discovery.getContextHandle(), QString()); + QCOMPARE(discovery.getIfdName(), QStringLiteral("Sony Xperia Z5 compact")); + QCOMPARE(discovery.getIfdId(), QStringLiteral("0123456789ABCDEF")); + QVERIFY(discovery.getPort() == static_cast(24728)); + QCOMPARE(discovery.getSupportedApis(), QVector({IfdVersion::Version::v0, IfdVersion::Version::v_test})); + } + + + void toJson() + { + const Discovery discovery( + QStringLiteral("Sony Xperia Z5 compact"), + QStringLiteral("0123456789ABCDEF"), + 24728, + {IfdVersion::Version::v0, IfdVersion::Version::v_test} + ); + + const QByteArray& byteArray = discovery.toByteArray(); + QCOMPARE(byteArray, + QByteArray("{\n" + " \"IFDID\": \"0123456789ABCDEF\",\n" + " \"IFDName\": \"Sony Xperia Z5 compact\",\n" + " \"SupportedAPI\": [\n" + " \"IFDInterface_WebSocket_v0\",\n" + " \"IFDInterface_WebSocket_v_test\"\n" + " ],\n" + " \"msg\": \"REMOTE_IFD\",\n" + " \"port\": 24728\n" + "}\n")); + + const QJsonObject obj = QJsonDocument::fromJson(byteArray).object(); + QCOMPARE(obj.size(), 5); + QCOMPARE(obj.value(QLatin1String("IFDName")).toString(), QStringLiteral("Sony Xperia Z5 compact")); + QCOMPARE(obj.value(QLatin1String("IFDID")).toString(), QStringLiteral("0123456789ABCDEF")); + QCOMPARE(obj.value(QLatin1String("msg")).toString(), QStringLiteral("REMOTE_IFD")); + QCOMPARE(obj.value(QLatin1String("port")).toInt(), 24728); + const QJsonValue apiLevels = obj.value(QLatin1String("SupportedAPI")); + QVERIFY(apiLevels.isArray()); + QCOMPARE(apiLevels.toArray().size(), 2); + QCOMPARE(apiLevels.toArray().at(0).toString(), QStringLiteral("IFDInterface_WebSocket_v0")); + QCOMPARE(apiLevels.toArray().at(1).toString(), QStringLiteral("IFDInterface_WebSocket_v_test")); + } + + + void fromJson() + { + const QByteArray message("{\n" + " \"IFDID\": \"0123456789ABCDEF\",\n" + " \"IFDName\": \"Sony Xperia Z5 compact\",\n" + " \"SupportedAPI\": [\n" + " \"IFDInterface_WebSocket_v0\",\n" + " \"IFDInterface_WebSocket_v_test\"\n" + " ],\n" + " \"msg\": \"REMOTE_IFD\",\n" + " \"port\": 24728\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const Discovery discovery(obj); + QVERIFY(!discovery.isIncomplete()); + QCOMPARE(discovery.getType(), RemoteCardMessageType::UNDEFINED); + QCOMPARE(discovery.getContextHandle(), QString()); + QCOMPARE(discovery.getIfdName(), QStringLiteral("Sony Xperia Z5 compact")); + QCOMPARE(discovery.getIfdId(), QStringLiteral("0123456789ABCDEF")); + QVERIFY(discovery.getPort() == static_cast(24728)); + QCOMPARE(discovery.getSupportedApis(), QVector({IfdVersion::Version::v0, IfdVersion::Version::v_test})); + } + + + void msgField_data() + { + QTest::addColumn("type"); + + const auto& msgTypes = Enum::getList(); + for (const auto& type : msgTypes) + { + QTest::newRow(getEnumName(type).data()) << QString(getEnumName(type)).toUtf8(); + } + } + + + void msgField() + { + QFETCH(QByteArray, type); + + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray message("{\n" + " \"IFDName\": \"Sony Xperia Z5 compact\",\n" + " \"IFDID\": \"0123456789ABCDEF\",\n" + " \"port\": 24728,\n" + " \"SupportedAPI\": [\"IFDInterface_WebSocket_v0\", \"IFDInterface_WebSocket_v_test\"],\n" + " \"msg\": \"%1\"\n" + "}"); + const QJsonObject& obj = QJsonDocument::fromJson(message.replace("%1", type)).object(); + const Discovery discovery(obj); + + QVERIFY(discovery.isIncomplete()); + QCOMPARE(discovery.getType(), RemoteCardMessageType::UNDEFINED); + + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of msg should be REMOTE_IFD")); + } + + + void ignoreContext() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"IFDID\": \"0123456789ABCDEF\",\n" + " \"IFDName\": \"Sony Xperia Z5 compact\",\n" + " \"SupportedAPI\": [\n" + " \"IFDInterface_WebSocket_v0\",\n" + " \"IFDInterface_WebSocket_v_test\"\n" + " ],\n" + " \"msg\": \"REMOTE_IFD\",\n" + " \"port\": 24728\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const Discovery discovery(obj); + QVERIFY(!discovery.isIncomplete()); + QCOMPARE(discovery.getContextHandle(), QString()); + + QCOMPARE(logSpy.count(), 0); + } + + + void wrongTypes() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"IFDID\": 1,\n" + " \"IFDName\": 2,\n" + " \"SupportedAPI\": \"IFDInterface_WebSocket_v0\",\n" + " \"msg\": \"REMOTE_IFD\",\n" + " \"port\": \"3\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const Discovery discovery(obj); + QVERIFY(discovery.isIncomplete()); + QCOMPARE(discovery.getIfdName(), QString()); + QCOMPARE(discovery.getIfdId(), QString()); + QVERIFY(discovery.getPort() == 0); + QCOMPARE(discovery.getSupportedApis(), QVector()); + + QCOMPARE(logSpy.count(), 4); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of \"IFDName\" should be of type \"string\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("The value of \"IFDID\" should be of type \"string\"")); + QVERIFY(logSpy.at(2).at(0).toString().contains("The value of \"port\" should be of type \"number\"")); + QVERIFY(logSpy.at(3).at(0).toString().contains("The value of \"SupportedAPI\" should be of type \"array\"")); + } + + + void wrongApiType() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"IFDID\": \"0123456789ABCDEF\",\n" + " \"IFDName\": \"Sony Xperia Z5 compact\",\n" + " \"SupportedAPI\": [\n" + " 0,\n" + " \"IFDInterface_WebSocket_v_test\"\n" + " ],\n" + " \"msg\": \"REMOTE_IFD\",\n" + " \"port\": 24728\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const Discovery discovery(obj); + QVERIFY(discovery.isIncomplete()); + QCOMPARE(discovery.getSupportedApis(), QVector({IfdVersion::Version::v_test})); + + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of \"SupportedAPI\" should be of type \"string array\"")); + } + + +}; + +QTEST_GUILESS_MAIN(test_Discovery) +#include "test_Discovery.moc" diff --git a/test/qt/remote_device/messages/test_IfdConnect.cpp b/test/qt/remote_device/messages/test_IfdConnect.cpp new file mode 100644 index 0000000..67009cf --- /dev/null +++ b/test/qt/remote_device/messages/test_IfdConnect.cpp @@ -0,0 +1,191 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "messages/IfdConnect.h" + +#include "LogHandler.h" + +#include + + +using namespace governikus; + + +class test_IfdConnect + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void initTestCase() + { + Env::getSingleton()->init(); + } + + + void invalidJson() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray message("FooBar"); + const auto& obj = QJsonDocument::fromJson(message).object(); + QVERIFY(obj.isEmpty()); + + IfdConnect msg(obj); + QVERIFY(msg.isIncomplete()); + + QCOMPARE(logSpy.count(), 6); + QVERIFY(logSpy.at(0).at(0).toString().contains("Missing value \"msg\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("Invalid messageType received: \"\"")); + QVERIFY(logSpy.at(2).at(0).toString().contains("Missing value \"ContextHandle\"")); + QVERIFY(logSpy.at(3).at(0).toString().contains("Missing value \"SlotName\"")); + QVERIFY(logSpy.at(4).at(0).toString().contains("Missing value \"exclusive\"")); + QVERIFY(logSpy.at(5).at(0).toString().contains("The value of msg should be IFDConnect")); + } + + + void values() + { + const IfdConnect ifdConnect( + QStringLiteral("SlotName") + ); + + QVERIFY(!ifdConnect.isIncomplete()); + QCOMPARE(ifdConnect.getType(), RemoteCardMessageType::IFDConnect); + QCOMPARE(ifdConnect.getContextHandle(), QString()); + QCOMPARE(ifdConnect.getSlotName(), QStringLiteral("SlotName")); + QVERIFY(ifdConnect.isExclusive()); + } + + + void toJson() + { + const IfdConnect ifdConnect( + QStringLiteral("SlotName") + ); + + const QByteArray& byteArray = ifdConnect.toByteArray(QStringLiteral("TestContext")); + QCOMPARE(byteArray, + QByteArray("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"SlotName\": \"SlotName\",\n" + " \"exclusive\": true,\n" + " \"msg\": \"IFDConnect\"\n" + "}\n")); + + const QJsonObject obj = QJsonDocument::fromJson(byteArray).object(); + QCOMPARE(obj.size(), 4); + QCOMPARE(obj.value(QLatin1String("msg")).toString(), QStringLiteral("IFDConnect")); + QCOMPARE(obj.value(QLatin1String("ContextHandle")).toString(), QStringLiteral("TestContext")); + QCOMPARE(obj.value(QLatin1String("SlotName")).toString(), QStringLiteral("SlotName")); + QCOMPARE(obj.value(QLatin1String("exclusive")).toBool(), true); + } + + + void fromJson() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"SlotName\": \"SlotName\",\n" + " \"exclusive\": true,\n" + " \"msg\": \"IFDConnect\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const IfdConnect ifdConnect(obj); + QVERIFY(!ifdConnect.isIncomplete()); + QCOMPARE(ifdConnect.getType(), RemoteCardMessageType::IFDConnect); + QCOMPARE(ifdConnect.getContextHandle(), QStringLiteral("TestContext")); + QCOMPARE(ifdConnect.getSlotName(), QStringLiteral("SlotName")); + QVERIFY(ifdConnect.isExclusive()); + + QCOMPARE(logSpy.count(), 0); + } + + + void msgField_data() + { + QTest::addColumn("type"); + + const auto& msgTypes = Enum::getList(); + for (const auto& type : msgTypes) + { + QTest::newRow(getEnumName(type).data()) << type; + } + } + + + void msgField() + { + QFETCH(RemoteCardMessageType, type); + + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"SlotName\": \"SlotName\",\n" + " \"exclusive\": true,\n" + " \"msg\": \"%1\"\n" + "}\n"); + const QJsonObject& obj = QJsonDocument::fromJson(message.replace("%1", QTest::currentDataTag())).object(); + const IfdConnect ifdConnect(obj); + + if (type == RemoteCardMessageType::IFDConnect) + { + QVERIFY(!ifdConnect.isIncomplete()); + QCOMPARE(ifdConnect.getType(), RemoteCardMessageType::IFDConnect); + + QCOMPARE(logSpy.count(), 0); + + return; + } + + QVERIFY(ifdConnect.isIncomplete()); + QCOMPARE(ifdConnect.getType(), type); + + if (type == RemoteCardMessageType::UNDEFINED) + { + QCOMPARE(logSpy.count(), 2); + QVERIFY(logSpy.at(0).at(0).toString().contains("Invalid messageType received: \"UNDEFINED\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("The value of msg should be IFDConnect")); + + return; + } + + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of msg should be IFDConnect")); + } + + + void wrongTypes() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"SlotName\": 1,\n" + " \"exclusive\": 2,\n" + " \"msg\": \"IFDConnect\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const IfdConnect ifdConnect(obj); + QVERIFY(ifdConnect.isIncomplete()); + QCOMPARE(ifdConnect.getType(), RemoteCardMessageType::IFDConnect); + QCOMPARE(ifdConnect.getContextHandle(), QStringLiteral("TestContext")); + QCOMPARE(ifdConnect.getSlotName(), QString()); + QVERIFY(!ifdConnect.isExclusive()); + + QCOMPARE(logSpy.count(), 2); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of \"SlotName\" should be of type \"string\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("The value of \"exclusive\" should be of type \"boolean\"")); + } + + +}; + +QTEST_GUILESS_MAIN(test_IfdConnect) +#include "test_IfdConnect.moc" diff --git a/test/qt/remote_device/messages/test_IfdConnectResponse.cpp b/test/qt/remote_device/messages/test_IfdConnectResponse.cpp new file mode 100644 index 0000000..b414518 --- /dev/null +++ b/test/qt/remote_device/messages/test_IfdConnectResponse.cpp @@ -0,0 +1,199 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "messages/IfdConnectResponse.h" + +#include "LogHandler.h" + +#include + + +using namespace governikus; + + +class test_IfdConnectResponse + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void initTestCase() + { + Env::getSingleton()->init(); + } + + + void invalidJson() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray message("FooBar"); + const auto& obj = QJsonDocument::fromJson(message).object(); + QVERIFY(obj.isEmpty()); + + IfdConnectResponse msg(obj); + QVERIFY(msg.isIncomplete()); + + QCOMPARE(logSpy.count(), 7); + QVERIFY(logSpy.at(0).at(0).toString().contains("Missing value \"msg\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("Invalid messageType received: \"\"")); + QVERIFY(logSpy.at(2).at(0).toString().contains("Missing value \"ContextHandle\"")); + QVERIFY(logSpy.at(3).at(0).toString().contains("Missing value \"ResultMajor\"")); + QVERIFY(logSpy.at(4).at(0).toString().contains("Missing value \"ResultMinor\"")); + QVERIFY(logSpy.at(5).at(0).toString().contains("Missing value \"SlotHandle\"")); + QVERIFY(logSpy.at(6).at(0).toString().contains("The value of msg should be IFDConnectResponse")); + } + + + void values() + { + const IfdConnectResponse ifdConnectResponse( + QStringLiteral("SlotHandle") + ); + + QVERIFY(!ifdConnectResponse.isIncomplete()); + QCOMPARE(ifdConnectResponse.getType(), RemoteCardMessageType::IFDConnectResponse); + QCOMPARE(ifdConnectResponse.getContextHandle(), QString()); + QCOMPARE(ifdConnectResponse.getSlotHandle(), QStringLiteral("SlotHandle")); + QVERIFY(!ifdConnectResponse.resultHasError()); + QCOMPARE(ifdConnectResponse.getResultMinor(), ECardApiResult::Minor::null); + } + + + void toJson() + { + const IfdConnectResponse ifdConnectResponse( + QStringLiteral("SlotHandle") + ); + + const QByteArray& byteArray = ifdConnectResponse.toByteArray(QStringLiteral("TestContext")); + QCOMPARE(byteArray, + QByteArray("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\",\n" + " \"ResultMinor\": null,\n" + " \"SlotHandle\": \"SlotHandle\",\n" + " \"msg\": \"IFDConnectResponse\"\n" + "}\n")); + + const QJsonObject obj = QJsonDocument::fromJson(byteArray).object(); + QCOMPARE(obj.size(), 5); + QCOMPARE(obj.value(QLatin1String("msg")).toString(), QStringLiteral("IFDConnectResponse")); + QCOMPARE(obj.value(QLatin1String("ContextHandle")).toString(), QStringLiteral("TestContext")); + QCOMPARE(obj.value(QLatin1String("SlotHandle")).toString(), QStringLiteral("SlotHandle")); + QCOMPARE(obj.value(QLatin1String("ResultMajor")).toString(), QStringLiteral("http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok")); + QCOMPARE(obj.value(QLatin1String("ResultMinor")).toString(), QString()); + } + + + void fromJson() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\",\n" + " \"ResultMinor\": null,\n" + " \"SlotHandle\": \"SlotHandle\",\n" + " \"msg\": \"IFDConnectResponse\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const IfdConnectResponse ifdConnectResponse(obj); + QVERIFY(!ifdConnectResponse.isIncomplete()); + QCOMPARE(ifdConnectResponse.getType(), RemoteCardMessageType::IFDConnectResponse); + QCOMPARE(ifdConnectResponse.getContextHandle(), QStringLiteral("TestContext")); + QCOMPARE(ifdConnectResponse.getSlotHandle(), QStringLiteral("SlotHandle")); + QVERIFY(!ifdConnectResponse.resultHasError()); + QCOMPARE(ifdConnectResponse.getResultMinor(), ECardApiResult::Minor::null); + + QCOMPARE(logSpy.count(), 0); + } + + + void msgField_data() + { + QTest::addColumn("type"); + + const auto& msgTypes = Enum::getList(); + for (const auto& type : msgTypes) + { + QTest::newRow(getEnumName(type).data()) << type; + } + } + + + void msgField() + { + QFETCH(RemoteCardMessageType, type); + + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\",\n" + " \"ResultMinor\": null,\n" + " \"SlotHandle\": \"SlotHandle\",\n" + " \"msg\": \"%1\"\n" + "}\n"); + const QJsonObject& obj = QJsonDocument::fromJson(message.replace("%1", QTest::currentDataTag())).object(); + const IfdConnectResponse ifdConnectResponse(obj); + + if (type == RemoteCardMessageType::IFDConnectResponse) + { + QVERIFY(!ifdConnectResponse.isIncomplete()); + QCOMPARE(ifdConnectResponse.getType(), RemoteCardMessageType::IFDConnectResponse); + + QCOMPARE(logSpy.count(), 0); + + return; + } + + QVERIFY(ifdConnectResponse.isIncomplete()); + QCOMPARE(ifdConnectResponse.getType(), type); + + if (type == RemoteCardMessageType::UNDEFINED) + { + QCOMPARE(logSpy.count(), 2); + QVERIFY(logSpy.at(0).at(0).toString().contains("Invalid messageType received: \"UNDEFINED\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("The value of msg should be IFDConnectResponse")); + + return; + } + + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of msg should be IFDConnectResponse")); + } + + + void wrongTypes() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\",\n" + " \"ResultMinor\": null,\n" + " \"SlotHandle\": 1,\n" + " \"msg\": \"IFDConnectResponse\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const IfdConnectResponse ifdConnectResponse(obj); + QVERIFY(ifdConnectResponse.isIncomplete()); + QCOMPARE(ifdConnectResponse.getType(), RemoteCardMessageType::IFDConnectResponse); + QCOMPARE(ifdConnectResponse.getContextHandle(), QStringLiteral("TestContext")); + QCOMPARE(ifdConnectResponse.getSlotHandle(), QString()); + QVERIFY(!ifdConnectResponse.resultHasError()); + QCOMPARE(ifdConnectResponse.getResultMinor(), ECardApiResult::Minor::null); + + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of \"SlotHandle\" should be of type \"string\"")); + } + + +}; + +QTEST_GUILESS_MAIN(test_IfdConnectResponse) +#include "test_IfdConnectResponse.moc" diff --git a/test/qt/remote_device/messages/test_IfdDisconnect.cpp b/test/qt/remote_device/messages/test_IfdDisconnect.cpp new file mode 100644 index 0000000..dbe5dc7 --- /dev/null +++ b/test/qt/remote_device/messages/test_IfdDisconnect.cpp @@ -0,0 +1,181 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "messages/IfdDisconnect.h" + +#include "LogHandler.h" + +#include + + +using namespace governikus; + + +class test_IfdDisconnect + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void initTestCase() + { + Env::getSingleton()->init(); + } + + + void invalidJson() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray message("FooBar"); + const auto& obj = QJsonDocument::fromJson(message).object(); + QVERIFY(obj.isEmpty()); + + IfdDisconnect msg(obj); + QVERIFY(msg.isIncomplete()); + + QCOMPARE(logSpy.count(), 5); + QVERIFY(logSpy.at(0).at(0).toString().contains("Missing value \"msg\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("Invalid messageType received: \"\"")); + QVERIFY(logSpy.at(2).at(0).toString().contains("Missing value \"ContextHandle\"")); + QVERIFY(logSpy.at(3).at(0).toString().contains("Missing value \"SlotHandle\"")); + QVERIFY(logSpy.at(4).at(0).toString().contains("The value of msg should be IFDDisconnect")); + } + + + void values() + { + const IfdDisconnect ifdDisconnect( + QStringLiteral("SlotHandle") + ); + + QVERIFY(!ifdDisconnect.isIncomplete()); + QCOMPARE(ifdDisconnect.getType(), RemoteCardMessageType::IFDDisconnect); + QCOMPARE(ifdDisconnect.getContextHandle(), QString()); + QCOMPARE(ifdDisconnect.getSlotHandle(), QStringLiteral("SlotHandle")); + } + + + void toJson() + { + const IfdDisconnect ifdDisconnect( + QStringLiteral("SlotHandle") + ); + + const QByteArray& byteArray = ifdDisconnect.toByteArray(QStringLiteral("TestContext")); + QCOMPARE(byteArray, + QByteArray("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"SlotHandle\": \"SlotHandle\",\n" + " \"msg\": \"IFDDisconnect\"\n" + "}\n")); + + const QJsonObject obj = QJsonDocument::fromJson(byteArray).object(); + QCOMPARE(obj.size(), 3); + QCOMPARE(obj.value(QLatin1String("msg")).toString(), QStringLiteral("IFDDisconnect")); + QCOMPARE(obj.value(QLatin1String("ContextHandle")).toString(), QStringLiteral("TestContext")); + QCOMPARE(obj.value(QLatin1String("SlotHandle")).toString(), QStringLiteral("SlotHandle")); + } + + + void fromJson() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"SlotHandle\": \"SlotHandle\",\n" + " \"msg\": \"IFDDisconnect\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const IfdDisconnect ifdDisconnect(obj); + QVERIFY(!ifdDisconnect.isIncomplete()); + QCOMPARE(ifdDisconnect.getType(), RemoteCardMessageType::IFDDisconnect); + QCOMPARE(ifdDisconnect.getContextHandle(), QStringLiteral("TestContext")); + QCOMPARE(ifdDisconnect.getSlotHandle(), QStringLiteral("SlotHandle")); + + QCOMPARE(logSpy.count(), 0); + } + + + void msgField_data() + { + QTest::addColumn("type"); + + const auto& msgTypes = Enum::getList(); + for (const auto& type : msgTypes) + { + QTest::newRow(getEnumName(type).data()) << type; + } + } + + + void msgField() + { + QFETCH(RemoteCardMessageType, type); + + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"SlotHandle\": \"SlotHandle\",\n" + " \"msg\": \"%1\"\n" + "}\n"); + const QJsonObject& obj = QJsonDocument::fromJson(message.replace("%1", QTest::currentDataTag())).object(); + const IfdDisconnect ifdDisconnect(obj); + + if (type == RemoteCardMessageType::IFDDisconnect) + { + QVERIFY(!ifdDisconnect.isIncomplete()); + QCOMPARE(ifdDisconnect.getType(), RemoteCardMessageType::IFDDisconnect); + + QCOMPARE(logSpy.count(), 0); + + return; + } + + QVERIFY(ifdDisconnect.isIncomplete()); + QCOMPARE(ifdDisconnect.getType(), type); + + if (type == RemoteCardMessageType::UNDEFINED) + { + QCOMPARE(logSpy.count(), 2); + QVERIFY(logSpy.at(0).at(0).toString().contains("Invalid messageType received: \"UNDEFINED\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("The value of msg should be IFDDisconnect")); + + return; + } + + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of msg should be IFDDisconnect")); + } + + + void wrongTypes() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"SlotHandle\": 1,\n" + " \"msg\": \"IFDDisconnect\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const IfdDisconnect ifdDisconnect(obj); + QVERIFY(ifdDisconnect.isIncomplete()); + QCOMPARE(ifdDisconnect.getType(), RemoteCardMessageType::IFDDisconnect); + QCOMPARE(ifdDisconnect.getContextHandle(), QStringLiteral("TestContext")); + QCOMPARE(ifdDisconnect.getSlotHandle(), QString()); + + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of \"SlotHandle\" should be of type \"string\"")); + } + + +}; + +QTEST_GUILESS_MAIN(test_IfdDisconnect) +#include "test_IfdDisconnect.moc" diff --git a/test/qt/remote_device/messages/test_IfdDisconnectResponse.cpp b/test/qt/remote_device/messages/test_IfdDisconnectResponse.cpp new file mode 100644 index 0000000..1fa61d9 --- /dev/null +++ b/test/qt/remote_device/messages/test_IfdDisconnectResponse.cpp @@ -0,0 +1,199 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "messages/IfdDisconnectResponse.h" + +#include "LogHandler.h" + +#include + + +using namespace governikus; + + +class test_IfdDisconnectResponse + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void initTestCase() + { + Env::getSingleton()->init(); + } + + + void invalidJson() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray message("FooBar"); + const auto& obj = QJsonDocument::fromJson(message).object(); + QVERIFY(obj.isEmpty()); + + IfdDisconnectResponse msg(obj); + QVERIFY(msg.isIncomplete()); + + QCOMPARE(logSpy.count(), 7); + QVERIFY(logSpy.at(0).at(0).toString().contains("Missing value \"msg\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("Invalid messageType received: \"\"")); + QVERIFY(logSpy.at(2).at(0).toString().contains("Missing value \"ContextHandle\"")); + QVERIFY(logSpy.at(3).at(0).toString().contains("Missing value \"ResultMajor\"")); + QVERIFY(logSpy.at(4).at(0).toString().contains("Missing value \"ResultMinor\"")); + QVERIFY(logSpy.at(5).at(0).toString().contains("Missing value \"SlotHandle\"")); + QVERIFY(logSpy.at(6).at(0).toString().contains("The value of msg should be IFDDisconnectResponse")); + } + + + void values() + { + const IfdDisconnectResponse ifdDisconnectResponse( + QStringLiteral("SlotHandle") + ); + + QVERIFY(!ifdDisconnectResponse.isIncomplete()); + QCOMPARE(ifdDisconnectResponse.getType(), RemoteCardMessageType::IFDDisconnectResponse); + QCOMPARE(ifdDisconnectResponse.getContextHandle(), QString()); + QCOMPARE(ifdDisconnectResponse.getSlotHandle(), QStringLiteral("SlotHandle")); + QVERIFY(!ifdDisconnectResponse.resultHasError()); + QCOMPARE(ifdDisconnectResponse.getResultMinor(), ECardApiResult::Minor::null); + } + + + void toJson() + { + const IfdDisconnectResponse ifdDisconnectResponse( + QStringLiteral("SlotHandle") + ); + + const QByteArray& byteArray = ifdDisconnectResponse.toByteArray(QStringLiteral("TestContext")); + QCOMPARE(byteArray, + QByteArray("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\",\n" + " \"ResultMinor\": null,\n" + " \"SlotHandle\": \"SlotHandle\",\n" + " \"msg\": \"IFDDisconnectResponse\"\n" + "}\n")); + + const QJsonObject obj = QJsonDocument::fromJson(byteArray).object(); + QCOMPARE(obj.size(), 5); + QCOMPARE(obj.value(QLatin1String("msg")).toString(), QStringLiteral("IFDDisconnectResponse")); + QCOMPARE(obj.value(QLatin1String("ContextHandle")).toString(), QStringLiteral("TestContext")); + QCOMPARE(obj.value(QLatin1String("SlotHandle")).toString(), QStringLiteral("SlotHandle")); + QCOMPARE(obj.value(QLatin1String("ResultMajor")).toString(), QStringLiteral("http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok")); + QCOMPARE(obj.value(QLatin1String("ResultMinor")).toString(), QString()); + } + + + void fromJson() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\",\n" + " \"ResultMinor\": null,\n" + " \"SlotHandle\": \"SlotHandle\",\n" + " \"msg\": \"IFDDisconnectResponse\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const IfdDisconnectResponse ifdDisconnectResponse(obj); + QVERIFY(!ifdDisconnectResponse.isIncomplete()); + QCOMPARE(ifdDisconnectResponse.getType(), RemoteCardMessageType::IFDDisconnectResponse); + QCOMPARE(ifdDisconnectResponse.getContextHandle(), QStringLiteral("TestContext")); + QCOMPARE(ifdDisconnectResponse.getSlotHandle(), QStringLiteral("SlotHandle")); + QVERIFY(!ifdDisconnectResponse.resultHasError()); + QCOMPARE(ifdDisconnectResponse.getResultMinor(), ECardApiResult::Minor::null); + + QCOMPARE(logSpy.count(), 0); + } + + + void msgField_data() + { + QTest::addColumn("type"); + + const auto& msgTypes = Enum::getList(); + for (const auto& type : msgTypes) + { + QTest::newRow(getEnumName(type).data()) << type; + } + } + + + void msgField() + { + QFETCH(RemoteCardMessageType, type); + + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\",\n" + " \"ResultMinor\": null,\n" + " \"SlotHandle\": \"SlotHandle\",\n" + " \"msg\": \"%1\"\n" + "}\n"); + const QJsonObject& obj = QJsonDocument::fromJson(message.replace("%1", QTest::currentDataTag())).object(); + const IfdDisconnectResponse ifdDisconnectResponse(obj); + + if (type == RemoteCardMessageType::IFDDisconnectResponse) + { + QVERIFY(!ifdDisconnectResponse.isIncomplete()); + QCOMPARE(ifdDisconnectResponse.getType(), RemoteCardMessageType::IFDDisconnectResponse); + + QCOMPARE(logSpy.count(), 0); + + return; + } + + QVERIFY(ifdDisconnectResponse.isIncomplete()); + QCOMPARE(ifdDisconnectResponse.getType(), type); + + if (type == RemoteCardMessageType::UNDEFINED) + { + QCOMPARE(logSpy.count(), 2); + QVERIFY(logSpy.at(0).at(0).toString().contains("Invalid messageType received: \"UNDEFINED\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("The value of msg should be IFDDisconnectResponse")); + + return; + } + + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of msg should be IFDDisconnectResponse")); + } + + + void wrongTypes() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\",\n" + " \"ResultMinor\": null,\n" + " \"SlotHandle\": 1,\n" + " \"msg\": \"IFDDisconnectResponse\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const IfdDisconnectResponse ifdDisconnectResponse(obj); + QVERIFY(ifdDisconnectResponse.isIncomplete()); + QCOMPARE(ifdDisconnectResponse.getType(), RemoteCardMessageType::IFDDisconnectResponse); + QCOMPARE(ifdDisconnectResponse.getContextHandle(), QStringLiteral("TestContext")); + QCOMPARE(ifdDisconnectResponse.getSlotHandle(), QString()); + QVERIFY(!ifdDisconnectResponse.resultHasError()); + QCOMPARE(ifdDisconnectResponse.getResultMinor(), ECardApiResult::Minor::null); + + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of \"SlotHandle\" should be of type \"string\"")); + } + + +}; + +QTEST_GUILESS_MAIN(test_IfdDisconnectResponse) +#include "test_IfdDisconnectResponse.moc" diff --git a/test/qt/remote_device/messages/test_IfdError.cpp b/test/qt/remote_device/messages/test_IfdError.cpp new file mode 100644 index 0000000..474a084 --- /dev/null +++ b/test/qt/remote_device/messages/test_IfdError.cpp @@ -0,0 +1,199 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "messages/IfdError.h" + +#include "LogHandler.h" + +#include + + +using namespace governikus; + + +class test_IfdError + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void initTestCase() + { + Env::getSingleton()->init(); + } + + + void invalidJson() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray message("FooBar"); + const auto& obj = QJsonDocument::fromJson(message).object(); + QVERIFY(obj.isEmpty()); + + IfdError msg(obj); + QVERIFY(msg.isIncomplete()); + + QCOMPARE(logSpy.count(), 7); + QVERIFY(logSpy.at(0).at(0).toString().contains("Missing value \"msg\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("Invalid messageType received: \"\"")); + QVERIFY(logSpy.at(2).at(0).toString().contains("Missing value \"ContextHandle\"")); + QVERIFY(logSpy.at(3).at(0).toString().contains("Missing value \"ResultMajor\"")); + QVERIFY(logSpy.at(4).at(0).toString().contains("Missing value \"ResultMinor\"")); + QVERIFY(logSpy.at(5).at(0).toString().contains("Missing value \"SlotHandle\"")); + QVERIFY(logSpy.at(6).at(0).toString().contains("The value of msg should be IFDError")); + } + + + void values() + { + const IfdError ifdError( + QStringLiteral("SlotHandle") + ); + + QVERIFY(!ifdError.isIncomplete()); + QCOMPARE(ifdError.getType(), RemoteCardMessageType::IFDError); + QCOMPARE(ifdError.getContextHandle(), QString()); + QCOMPARE(ifdError.getSlotHandle(), QStringLiteral("SlotHandle")); + QVERIFY(!ifdError.resultHasError()); + QCOMPARE(ifdError.getResultMinor(), ECardApiResult::Minor::null); + } + + + void toJson() + { + const IfdError ifdError( + QStringLiteral("SlotHandle") + ); + + const QByteArray& byteArray = ifdError.toByteArray(QStringLiteral("TestContext")); + QCOMPARE(byteArray, + QByteArray("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\",\n" + " \"ResultMinor\": null,\n" + " \"SlotHandle\": \"SlotHandle\",\n" + " \"msg\": \"IFDError\"\n" + "}\n")); + + const QJsonObject obj = QJsonDocument::fromJson(byteArray).object(); + QCOMPARE(obj.size(), 5); + QCOMPARE(obj.value(QLatin1String("msg")).toString(), QStringLiteral("IFDError")); + QCOMPARE(obj.value(QLatin1String("ContextHandle")).toString(), QStringLiteral("TestContext")); + QCOMPARE(obj.value(QLatin1String("SlotHandle")).toString(), QStringLiteral("SlotHandle")); + QCOMPARE(obj.value(QLatin1String("ResultMajor")).toString(), QStringLiteral("http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok")); + QCOMPARE(obj.value(QLatin1String("ResultMinor")).toString(), QString()); + } + + + void fromJson() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\",\n" + " \"ResultMinor\": null,\n" + " \"SlotHandle\": \"SlotHandle\",\n" + " \"msg\": \"IFDError\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const IfdError ifdError(obj); + QVERIFY(!ifdError.isIncomplete()); + QCOMPARE(ifdError.getType(), RemoteCardMessageType::IFDError); + QCOMPARE(ifdError.getContextHandle(), QStringLiteral("TestContext")); + QCOMPARE(ifdError.getSlotHandle(), QStringLiteral("SlotHandle")); + QVERIFY(!ifdError.resultHasError()); + QCOMPARE(ifdError.getResultMinor(), ECardApiResult::Minor::null); + + QCOMPARE(logSpy.count(), 0); + } + + + void msgField_data() + { + QTest::addColumn("type"); + + const auto& msgTypes = Enum::getList(); + for (const auto& type : msgTypes) + { + QTest::newRow(getEnumName(type).data()) << type; + } + } + + + void msgField() + { + QFETCH(RemoteCardMessageType, type); + + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\",\n" + " \"ResultMinor\": null,\n" + " \"SlotHandle\": \"SlotHandle\",\n" + " \"msg\": \"%1\"\n" + "}\n"); + const QJsonObject& obj = QJsonDocument::fromJson(message.replace("%1", QTest::currentDataTag())).object(); + const IfdError ifdError(obj); + + if (type == RemoteCardMessageType::IFDError) + { + QVERIFY(!ifdError.isIncomplete()); + QCOMPARE(ifdError.getType(), RemoteCardMessageType::IFDError); + + QCOMPARE(logSpy.count(), 0); + + return; + } + + QVERIFY(ifdError.isIncomplete()); + QCOMPARE(ifdError.getType(), type); + + if (type == RemoteCardMessageType::UNDEFINED) + { + QCOMPARE(logSpy.count(), 2); + QVERIFY(logSpy.at(0).at(0).toString().contains("Invalid messageType received: \"UNDEFINED\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("The value of msg should be IFDError")); + + return; + } + + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of msg should be IFDError")); + } + + + void wrongTypes() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\",\n" + " \"ResultMinor\": null,\n" + " \"SlotHandle\": 1,\n" + " \"msg\": \"IFDError\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const IfdError ifdError(obj); + QVERIFY(ifdError.isIncomplete()); + QCOMPARE(ifdError.getType(), RemoteCardMessageType::IFDError); + QCOMPARE(ifdError.getContextHandle(), QStringLiteral("TestContext")); + QCOMPARE(ifdError.getSlotHandle(), QString()); + QVERIFY(!ifdError.resultHasError()); + QCOMPARE(ifdError.getResultMinor(), ECardApiResult::Minor::null); + + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of \"SlotHandle\" should be of type \"string\"")); + } + + +}; + +QTEST_GUILESS_MAIN(test_IfdError) +#include "test_IfdError.moc" diff --git a/test/qt/remote_device/messages/test_IfdEstablishContext.cpp b/test/qt/remote_device/messages/test_IfdEstablishContext.cpp new file mode 100644 index 0000000..01d80d6 --- /dev/null +++ b/test/qt/remote_device/messages/test_IfdEstablishContext.cpp @@ -0,0 +1,205 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "messages/IfdEstablishContext.h" + +#include "LogHandler.h" + +#include + + +using namespace governikus; + + +class test_IfdEstablishContext + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void initTestCase() + { + Env::getSingleton()->init(); + } + + + void invalidJson() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray message("FooBar"); + const auto& obj = QJsonDocument::fromJson(message).object(); + QVERIFY(obj.isEmpty()); + + IfdEstablishContext msg(obj); + QVERIFY(msg.isIncomplete()); + + QCOMPARE(logSpy.count(), 6); + QVERIFY(logSpy.at(0).at(0).toString().contains("Missing value \"msg\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("Invalid messageType received: \"\"")); + QVERIFY(logSpy.at(2).at(0).toString().contains("Missing value \"ContextHandle\"")); + QVERIFY(logSpy.at(3).at(0).toString().contains("Missing value \"Protocol\"")); + QVERIFY(logSpy.at(4).at(0).toString().contains("Missing value \"UDName\"")); + QVERIFY(logSpy.at(5).at(0).toString().contains("The value of msg should be IFDEstablishContext")); + } + + + void values() + { + const IfdEstablishContext ifdEstablishContext( + QStringLiteral("IFDInterface_WebSocket_v0"), + QStringLiteral("MAC-MINI") + ); + + QVERIFY(!ifdEstablishContext.isIncomplete()); + QCOMPARE(ifdEstablishContext.getType(), RemoteCardMessageType::IFDEstablishContext); + QCOMPARE(ifdEstablishContext.getContextHandle(), QString()); + QCOMPARE(ifdEstablishContext.getProtocol(), QStringLiteral("IFDInterface_WebSocket_v0")); + QCOMPARE(ifdEstablishContext.getUdName(), QStringLiteral("MAC-MINI")); + } + + + void toJson() + { + const IfdEstablishContext ifdEstablishContext( + QStringLiteral("IFDInterface_WebSocket_v0"), + QStringLiteral("MAC-MINI") + ); + + const QByteArray& byteArray = ifdEstablishContext.toByteArray(QStringLiteral("TestContext")); + QCOMPARE(byteArray, + QByteArray("{\n" + " \"Protocol\": \"IFDInterface_WebSocket_v0\",\n" + " \"UDName\": \"MAC-MINI\",\n" + " \"msg\": \"IFDEstablishContext\"\n" + "}\n")); + + const QJsonObject obj = QJsonDocument::fromJson(byteArray).object(); + QCOMPARE(obj.size(), 3); + QCOMPARE(obj.value(QLatin1String("Protocol")).toString(), QStringLiteral("IFDInterface_WebSocket_v0")); + QCOMPARE(obj.value(QLatin1String("UDName")).toString(), QStringLiteral("MAC-MINI")); + QCOMPARE(obj.value(QLatin1String("msg")).toString(), QStringLiteral("IFDEstablishContext")); + } + + + void fromJson() + { + const QByteArray message("{\n" + " \"Protocol\": \"IFDInterface_WebSocket_v0\",\n" + " \"UDName\": \"MAC-MINI\",\n" + " \"msg\": \"IFDEstablishContext\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const IfdEstablishContext ifdEstablishContext(obj); + QVERIFY(!ifdEstablishContext.isIncomplete()); + QCOMPARE(ifdEstablishContext.getType(), RemoteCardMessageType::IFDEstablishContext); + QCOMPARE(ifdEstablishContext.getContextHandle(), QString()); + QCOMPARE(ifdEstablishContext.getProtocol(), QStringLiteral("IFDInterface_WebSocket_v0")); + QCOMPARE(ifdEstablishContext.getUdName(), QStringLiteral("MAC-MINI")); + } + + + void msgField_data() + { + QTest::addColumn("type"); + + const auto& msgTypes = Enum::getList(); + for (const auto& type : msgTypes) + { + QTest::newRow(getEnumName(type).data()) << type; + } + } + + + void msgField() + { + QFETCH(RemoteCardMessageType, type); + + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray message("{\n" + " \"Protocol\": \"IFDInterface_WebSocket_v0\",\n" + " \"UDName\": \"MAC-MINI\",\n" + " \"msg\": \"%1\"\n" + "}\n"); + const QJsonObject& obj = QJsonDocument::fromJson(message.replace("%1", QTest::currentDataTag())).object(); + const IfdEstablishContext ifdEstablishContext(obj); + + if (type == RemoteCardMessageType::IFDEstablishContext) + { + QVERIFY(!ifdEstablishContext.isIncomplete()); + QCOMPARE(ifdEstablishContext.getType(), RemoteCardMessageType::IFDEstablishContext); + + QCOMPARE(logSpy.count(), 0); + + return; + } + + QVERIFY(ifdEstablishContext.isIncomplete()); + QCOMPARE(ifdEstablishContext.getType(), type); + + if (type == RemoteCardMessageType::UNDEFINED) + { + QCOMPARE(logSpy.count(), 3); + QVERIFY(logSpy.at(0).at(0).toString().contains("Invalid messageType received: \"UNDEFINED\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("Missing value \"ContextHandle\"")); + QVERIFY(logSpy.at(2).at(0).toString().contains("The value of msg should be IFDEstablishContext")); + + return; + } + + QCOMPARE(logSpy.count(), 2); + QVERIFY(logSpy.at(0).at(0).toString().contains("Missing value \"ContextHandle\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("The value of msg should be IFDEstablishContext")); + } + + + void ignoreContext() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"Protocol\": \"IFDInterface_WebSocket_v0\",\n" + " \"UDName\": \"MAC-MINI\",\n" + " \"msg\": \"IFDEstablishContext\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const IfdEstablishContext ifdEstablishContext(obj); + QVERIFY(!ifdEstablishContext.isIncomplete()); + QCOMPARE(ifdEstablishContext.getContextHandle(), QString()); + + QCOMPARE(logSpy.count(), 0); + } + + + void wrongTypes() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"Protocol\": 1,\n" + " \"UDName\": 2,\n" + " \"msg\": \"IFDEstablishContext\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const IfdEstablishContext ifdEstablishContext(obj); + QVERIFY(ifdEstablishContext.isIncomplete()); + + QCOMPARE(ifdEstablishContext.getProtocol(), QString()); + QCOMPARE(ifdEstablishContext.getUdName(), QString()); + + QCOMPARE(logSpy.count(), 2); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of \"Protocol\" should be of type \"string\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("The value of \"UDName\" should be of type \"string\"")); + } + + +}; + +QTEST_GUILESS_MAIN(test_IfdEstablishContext) +#include "test_IfdEstablishContext.moc" diff --git a/test/qt/remote_device/messages/test_IfdEstablishContextResponse.cpp b/test/qt/remote_device/messages/test_IfdEstablishContextResponse.cpp new file mode 100644 index 0000000..c5d759d --- /dev/null +++ b/test/qt/remote_device/messages/test_IfdEstablishContextResponse.cpp @@ -0,0 +1,199 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "messages/IfdEstablishContextResponse.h" + +#include "LogHandler.h" + +#include + + +using namespace governikus; + + +class test_IfdEstablishContextResponse + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void initTestCase() + { + Env::getSingleton()->init(); + } + + + void invalidJson() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray message("FooBar"); + const auto& obj = QJsonDocument::fromJson(message).object(); + QVERIFY(obj.isEmpty()); + + IfdEstablishContextResponse msg(obj); + QVERIFY(msg.isIncomplete()); + + QCOMPARE(logSpy.count(), 7); + QVERIFY(logSpy.at(0).at(0).toString().contains("Missing value \"msg\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("Invalid messageType received: \"\"")); + QVERIFY(logSpy.at(2).at(0).toString().contains("Missing value \"ContextHandle\"")); + QVERIFY(logSpy.at(3).at(0).toString().contains("Missing value \"ResultMajor\"")); + QVERIFY(logSpy.at(4).at(0).toString().contains("Missing value \"ResultMinor\"")); + QVERIFY(logSpy.at(5).at(0).toString().contains("Missing value \"IFDName\"")); + QVERIFY(logSpy.at(6).at(0).toString().contains("The value of msg should be IFDEstablishContextResponse")); + } + + + void values() + { + const IfdEstablishContextResponse ifdEstablishContextResponse( + QStringLiteral("IFD Remote Server") + ); + + QVERIFY(!ifdEstablishContextResponse.isIncomplete()); + QCOMPARE(ifdEstablishContextResponse.getType(), RemoteCardMessageType::IFDEstablishContextResponse); + QCOMPARE(ifdEstablishContextResponse.getContextHandle(), QString()); + QCOMPARE(ifdEstablishContextResponse.getIfdName(), QStringLiteral("IFD Remote Server")); + QVERIFY(!ifdEstablishContextResponse.resultHasError()); + QCOMPARE(ifdEstablishContextResponse.getResultMinor(), ECardApiResult::Minor::null); + } + + + void toJson() + { + const IfdEstablishContextResponse ifdEstablishContextResponse( + QStringLiteral("IFD Remote Server") + ); + + const QByteArray& byteArray = ifdEstablishContextResponse.toByteArray(QStringLiteral("TestContext")); + QCOMPARE(byteArray, + QByteArray("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"IFDName\": \"IFD Remote Server\",\n" + " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\",\n" + " \"ResultMinor\": null,\n" + " \"msg\": \"IFDEstablishContextResponse\"\n" + "}\n")); + + const QJsonObject obj = QJsonDocument::fromJson(byteArray).object(); + QCOMPARE(obj.size(), 5); + QCOMPARE(obj.value(QLatin1String("msg")).toString(), QStringLiteral("IFDEstablishContextResponse")); + QCOMPARE(obj.value(QLatin1String("ContextHandle")).toString(), QStringLiteral("TestContext")); + QCOMPARE(obj.value(QLatin1String("IFDName")).toString(), QStringLiteral("IFD Remote Server")); + QCOMPARE(obj.value(QLatin1String("ResultMajor")).toString(), QStringLiteral("http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok")); + QCOMPARE(obj.value(QLatin1String("ResultMinor")).toString(), QString()); + } + + + void fromJson() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"IFDName\": \"IFD Remote Server\",\n" + " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\",\n" + " \"ResultMinor\": null,\n" + " \"msg\": \"IFDEstablishContextResponse\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const IfdEstablishContextResponse ifdEstablishContextResponse(obj); + QVERIFY(!ifdEstablishContextResponse.isIncomplete()); + QCOMPARE(ifdEstablishContextResponse.getType(), RemoteCardMessageType::IFDEstablishContextResponse); + QCOMPARE(ifdEstablishContextResponse.getContextHandle(), QStringLiteral("TestContext")); + QCOMPARE(ifdEstablishContextResponse.getIfdName(), QStringLiteral("IFD Remote Server")); + QVERIFY(!ifdEstablishContextResponse.resultHasError()); + QCOMPARE(ifdEstablishContextResponse.getResultMinor(), ECardApiResult::Minor::null); + + QCOMPARE(logSpy.count(), 0); + } + + + void msgField_data() + { + QTest::addColumn("type"); + + const auto& msgTypes = Enum::getList(); + for (const auto& type : msgTypes) + { + QTest::newRow(getEnumName(type).data()) << type; + } + } + + + void msgField() + { + QFETCH(RemoteCardMessageType, type); + + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"IFDName\": \"IFD Remote Server\",\n" + " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\",\n" + " \"ResultMinor\": null,\n" + " \"msg\": \"%1\"\n" + "}\n"); + const QJsonObject& obj = QJsonDocument::fromJson(message.replace("%1", QTest::currentDataTag())).object(); + const IfdEstablishContextResponse ifdEstablishContextResponse(obj); + + if (type == RemoteCardMessageType::IFDEstablishContextResponse) + { + QVERIFY(!ifdEstablishContextResponse.isIncomplete()); + QCOMPARE(ifdEstablishContextResponse.getType(), RemoteCardMessageType::IFDEstablishContextResponse); + + QCOMPARE(logSpy.count(), 0); + + return; + } + + QVERIFY(ifdEstablishContextResponse.isIncomplete()); + QCOMPARE(ifdEstablishContextResponse.getType(), type); + + if (type == RemoteCardMessageType::UNDEFINED) + { + QCOMPARE(logSpy.count(), 2); + QVERIFY(logSpy.at(0).at(0).toString().contains("Invalid messageType received: \"UNDEFINED\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("The value of msg should be IFDEstablishContextResponse")); + + return; + } + + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of msg should be IFDEstablishContextResponse")); + } + + + void wrongTypes() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"IFDName\": 1,\n" + " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\",\n" + " \"ResultMinor\": null,\n" + " \"msg\": \"IFDEstablishContextResponse\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const IfdEstablishContextResponse ifdEstablishContextResponse(obj); + QVERIFY(ifdEstablishContextResponse.isIncomplete()); + QCOMPARE(ifdEstablishContextResponse.getType(), RemoteCardMessageType::IFDEstablishContextResponse); + QCOMPARE(ifdEstablishContextResponse.getContextHandle(), QStringLiteral("TestContext")); + QCOMPARE(ifdEstablishContextResponse.getIfdName(), QString()); + QVERIFY(!ifdEstablishContextResponse.resultHasError()); + QCOMPARE(ifdEstablishContextResponse.getResultMinor(), ECardApiResult::Minor::null); + + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of \"IFDName\" should be of type \"string\"")); + } + + +}; + +QTEST_GUILESS_MAIN(test_IfdEstablishContextResponse) +#include "test_IfdEstablishContextResponse.moc" diff --git a/test/qt/remote_device/messages/test_IfdEstablishPaceChannel.cpp b/test/qt/remote_device/messages/test_IfdEstablishPaceChannel.cpp new file mode 100644 index 0000000..2989a09 --- /dev/null +++ b/test/qt/remote_device/messages/test_IfdEstablishPaceChannel.cpp @@ -0,0 +1,193 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "messages/IfdEstablishPaceChannel.h" + +#include "LogHandler.h" + +#include + + +using namespace governikus; + + +class test_IfdEstablishPaceChannel + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void initTestCase() + { + Env::getSingleton()->init(); + } + + + void invalidJson() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray message("FooBar"); + const auto& obj = QJsonDocument::fromJson(message).object(); + QVERIFY(obj.isEmpty()); + + IfdEstablishPaceChannel msg(obj); + QVERIFY(msg.isIncomplete()); + + QCOMPARE(logSpy.count(), 6); + QVERIFY(logSpy.at(0).at(0).toString().contains("Missing value \"msg\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("Invalid messageType received: \"\"")); + QVERIFY(logSpy.at(2).at(0).toString().contains("Missing value \"ContextHandle\"")); + QVERIFY(logSpy.at(3).at(0).toString().contains("Missing value \"SlotHandle\"")); + QVERIFY(logSpy.at(4).at(0).toString().contains("Missing value \"InputData\"")); + QVERIFY(logSpy.at(5).at(0).toString().contains("The value of msg should be IFDEstablishPACEChannel")); + } + + + void values() + { + const IfdEstablishPaceChannel ifdEstablishPaceChannel( + QStringLiteral("SlotHandle"), + QByteArray::fromHex("abcd1234") + ); + + QVERIFY(!ifdEstablishPaceChannel.isIncomplete()); + QCOMPARE(ifdEstablishPaceChannel.getType(), RemoteCardMessageType::IFDEstablishPACEChannel); + QCOMPARE(ifdEstablishPaceChannel.getContextHandle(), QString()); + QCOMPARE(ifdEstablishPaceChannel.getSlotHandle(), QStringLiteral("SlotHandle")); + QCOMPARE(ifdEstablishPaceChannel.getInputData(), QByteArray::fromHex("abcd1234")); + } + + + void toJson() + { + const IfdEstablishPaceChannel ifdEstablishPaceChannel( + QStringLiteral("SlotHandle"), + QByteArray::fromHex("abcd1234") + ); + + const QByteArray& byteArray = ifdEstablishPaceChannel.toByteArray(QStringLiteral("TestContext")); + QCOMPARE(byteArray, + QByteArray("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"InputData\": \"abcd1234\",\n" + " \"SlotHandle\": \"SlotHandle\",\n" + " \"msg\": \"IFDEstablishPACEChannel\"\n" + "}\n")); + + const QJsonObject obj = QJsonDocument::fromJson(byteArray).object(); + QCOMPARE(obj.size(), 4); + QCOMPARE(obj.value(QLatin1String("msg")).toString(), QStringLiteral("IFDEstablishPACEChannel")); + QCOMPARE(obj.value(QLatin1String("ContextHandle")).toString(), QStringLiteral("TestContext")); + QCOMPARE(obj.value(QLatin1String("SlotHandle")).toString(), QStringLiteral("SlotHandle")); + QCOMPARE(obj.value(QLatin1String("InputData")).toString(), QStringLiteral("abcd1234")); + } + + + void fromJson() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"InputData\": \"abcd1234\",\n" + " \"SlotHandle\": \"SlotHandle\",\n" + " \"msg\": \"IFDEstablishPACEChannel\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const IfdEstablishPaceChannel ifdEstablishPaceChannel(obj); + QVERIFY(!ifdEstablishPaceChannel.isIncomplete()); + QCOMPARE(ifdEstablishPaceChannel.getType(), RemoteCardMessageType::IFDEstablishPACEChannel); + QCOMPARE(ifdEstablishPaceChannel.getContextHandle(), QStringLiteral("TestContext")); + QCOMPARE(ifdEstablishPaceChannel.getSlotHandle(), QStringLiteral("SlotHandle")); + QCOMPARE(ifdEstablishPaceChannel.getInputData(), QByteArray::fromHex("abcd1234")); + + QCOMPARE(logSpy.count(), 0); + } + + + void msgField_data() + { + QTest::addColumn("type"); + + const auto& msgTypes = Enum::getList(); + for (const auto& type : msgTypes) + { + QTest::newRow(getEnumName(type).data()) << type; + } + } + + + void msgField() + { + QFETCH(RemoteCardMessageType, type); + + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"InputData\": \"abcd1234\",\n" + " \"SlotHandle\": \"SlotHandle\",\n" + " \"msg\": \"%1\"\n" + "}\n"); + const QJsonObject& obj = QJsonDocument::fromJson(message.replace("%1", QTest::currentDataTag())).object(); + const IfdEstablishPaceChannel ifdEstablishPaceChannel(obj); + + if (type == RemoteCardMessageType::IFDEstablishPACEChannel) + { + QVERIFY(!ifdEstablishPaceChannel.isIncomplete()); + QCOMPARE(ifdEstablishPaceChannel.getType(), RemoteCardMessageType::IFDEstablishPACEChannel); + + QCOMPARE(logSpy.count(), 0); + + return; + } + + QVERIFY(ifdEstablishPaceChannel.isIncomplete()); + QCOMPARE(ifdEstablishPaceChannel.getType(), type); + + if (type == RemoteCardMessageType::UNDEFINED) + { + QCOMPARE(logSpy.count(), 2); + QVERIFY(logSpy.at(0).at(0).toString().contains("Invalid messageType received: \"UNDEFINED\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("The value of msg should be IFDEstablishPACEChannel")); + + return; + } + + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of msg should be IFDEstablishPACEChannel")); + } + + + void wrongTypes() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"InputData\": 1,\n" + " \"SlotHandle\": 2,\n" + " \"msg\": \"IFDEstablishPACEChannel\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const IfdEstablishPaceChannel ifdEstablishPaceChannel(obj); + QVERIFY(ifdEstablishPaceChannel.isIncomplete()); + QCOMPARE(ifdEstablishPaceChannel.getType(), RemoteCardMessageType::IFDEstablishPACEChannel); + QCOMPARE(ifdEstablishPaceChannel.getContextHandle(), QStringLiteral("TestContext")); + QCOMPARE(ifdEstablishPaceChannel.getSlotHandle(), QString()); + QCOMPARE(ifdEstablishPaceChannel.getInputData(), QByteArray()); + + QCOMPARE(logSpy.count(), 2); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of \"SlotHandle\" should be of type \"string\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("The value of \"InputData\" should be of type \"string\"")); + } + + +}; + +QTEST_GUILESS_MAIN(test_IfdEstablishPaceChannel) +#include "test_IfdEstablishPaceChannel.moc" diff --git a/test/qt/remote_device/messages/test_IfdEstablishPaceChannelResponse.cpp b/test/qt/remote_device/messages/test_IfdEstablishPaceChannelResponse.cpp new file mode 100644 index 0000000..e5d776f --- /dev/null +++ b/test/qt/remote_device/messages/test_IfdEstablishPaceChannelResponse.cpp @@ -0,0 +1,209 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "messages/IfdEstablishPaceChannelResponse.h" + +#include "LogHandler.h" + +#include + + +using namespace governikus; + + +class test_IfdEstablishPaceChannelResponse + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void initTestCase() + { + Env::getSingleton()->init(); + } + + + void invalidJson() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray message("FooBar"); + const auto& obj = QJsonDocument::fromJson(message).object(); + QVERIFY(obj.isEmpty()); + + IfdEstablishPaceChannelResponse msg(obj); + QVERIFY(msg.isIncomplete()); + + QCOMPARE(logSpy.count(), 8); + QVERIFY(logSpy.at(0).at(0).toString().contains("Missing value \"msg\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("Invalid messageType received: \"\"")); + QVERIFY(logSpy.at(2).at(0).toString().contains("Missing value \"ContextHandle\"")); + QVERIFY(logSpy.at(3).at(0).toString().contains("Missing value \"ResultMajor\"")); + QVERIFY(logSpy.at(4).at(0).toString().contains("Missing value \"ResultMinor\"")); + QVERIFY(logSpy.at(5).at(0).toString().contains("Missing value \"SlotHandle\"")); + QVERIFY(logSpy.at(6).at(0).toString().contains("Missing value \"OutputData\"")); + QVERIFY(logSpy.at(7).at(0).toString().contains("The value of msg should be IFDEstablishPACEChannelResponse")); + } + + + void values() + { + const IfdEstablishPaceChannelResponse ifdEstablishPaceChannelResponse( + QStringLiteral("SlotHandle"), + QByteArray::fromHex("abcd1234") + ); + + QVERIFY(!ifdEstablishPaceChannelResponse.isIncomplete()); + QCOMPARE(ifdEstablishPaceChannelResponse.getType(), RemoteCardMessageType::IFDEstablishPACEChannelResponse); + QCOMPARE(ifdEstablishPaceChannelResponse.getContextHandle(), QString()); + QCOMPARE(ifdEstablishPaceChannelResponse.getSlotHandle(), QStringLiteral("SlotHandle")); + QCOMPARE(ifdEstablishPaceChannelResponse.getOutputData(), QByteArray::fromHex("abcd1234")); + QVERIFY(!ifdEstablishPaceChannelResponse.resultHasError()); + QCOMPARE(ifdEstablishPaceChannelResponse.getResultMinor(), ECardApiResult::Minor::null); + } + + + void toJson() + { + const IfdEstablishPaceChannelResponse ifdEstablishPaceChannelResponse( + QStringLiteral("SlotHandle"), + QByteArray::fromHex("abcd1234") + ); + + const QByteArray& byteArray = ifdEstablishPaceChannelResponse.toByteArray(QStringLiteral("TestContext")); + QCOMPARE(byteArray, + QByteArray("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"OutputData\": \"abcd1234\",\n" + " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\",\n" + " \"ResultMinor\": null,\n" + " \"SlotHandle\": \"SlotHandle\",\n" + " \"msg\": \"IFDEstablishPACEChannelResponse\"\n" + "}\n")); + + const QJsonObject obj = QJsonDocument::fromJson(byteArray).object(); + QCOMPARE(obj.size(), 6); + QCOMPARE(obj.value(QLatin1String("msg")).toString(), QStringLiteral("IFDEstablishPACEChannelResponse")); + QCOMPARE(obj.value(QLatin1String("ContextHandle")).toString(), QStringLiteral("TestContext")); + QCOMPARE(obj.value(QLatin1String("SlotHandle")).toString(), QStringLiteral("SlotHandle")); + QCOMPARE(obj.value(QLatin1String("OutputData")).toString(), QStringLiteral("abcd1234")); + } + + + void fromJson() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"OutputData\": \"abcd1234\",\n" + " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\",\n" + " \"ResultMinor\": null,\n" + " \"SlotHandle\": \"SlotHandle\",\n" + " \"msg\": \"IFDEstablishPACEChannelResponse\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const IfdEstablishPaceChannelResponse ifdEstablishPaceChannelResponse(obj); + QVERIFY(!ifdEstablishPaceChannelResponse.isIncomplete()); + QCOMPARE(ifdEstablishPaceChannelResponse.getType(), RemoteCardMessageType::IFDEstablishPACEChannelResponse); + QCOMPARE(ifdEstablishPaceChannelResponse.getContextHandle(), QString("TestContext")); + QCOMPARE(ifdEstablishPaceChannelResponse.getSlotHandle(), QStringLiteral("SlotHandle")); + QCOMPARE(ifdEstablishPaceChannelResponse.getOutputData(), QByteArray::fromHex("abcd1234")); + QVERIFY(!ifdEstablishPaceChannelResponse.resultHasError()); + QCOMPARE(ifdEstablishPaceChannelResponse.getResultMinor(), ECardApiResult::Minor::null); + + QCOMPARE(logSpy.count(), 0); + } + + + void msgField_data() + { + QTest::addColumn("type"); + + const auto& msgTypes = Enum::getList(); + for (const auto& type : msgTypes) + { + QTest::newRow(getEnumName(type).data()) << type; + } + } + + + void msgField() + { + QFETCH(RemoteCardMessageType, type); + + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"OutputData\": \"abcd1234\",\n" + " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\",\n" + " \"ResultMinor\": null,\n" + " \"SlotHandle\": \"SlotHandle\",\n" + " \"msg\": \"%1\"\n" + "}\n"); + const QJsonObject& obj = QJsonDocument::fromJson(message.replace("%1", QTest::currentDataTag())).object(); + const IfdEstablishPaceChannelResponse ifdEstablishPaceChannelResponse(obj); + + if (type == RemoteCardMessageType::IFDEstablishPACEChannelResponse) + { + QVERIFY(!ifdEstablishPaceChannelResponse.isIncomplete()); + QCOMPARE(ifdEstablishPaceChannelResponse.getType(), RemoteCardMessageType::IFDEstablishPACEChannelResponse); + + QCOMPARE(logSpy.count(), 0); + + return; + } + + QVERIFY(ifdEstablishPaceChannelResponse.isIncomplete()); + QCOMPARE(ifdEstablishPaceChannelResponse.getType(), type); + + if (type == RemoteCardMessageType::UNDEFINED) + { + QCOMPARE(logSpy.count(), 2); + QVERIFY(logSpy.at(0).at(0).toString().contains("Invalid messageType received: \"UNDEFINED\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("The value of msg should be IFDEstablishPACEChannelResponse")); + + return; + } + + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of msg should be IFDEstablishPACEChannelResponse")); + } + + + void wrongTypes() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"OutputData\": 1,\n" + " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\",\n" + " \"ResultMinor\": null,\n" + " \"SlotHandle\": 2,\n" + " \"msg\": \"IFDEstablishPACEChannelResponse\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const IfdEstablishPaceChannelResponse ifdEstablishPaceChannelResponse(obj); + QVERIFY(ifdEstablishPaceChannelResponse.isIncomplete()); + QCOMPARE(ifdEstablishPaceChannelResponse.getType(), RemoteCardMessageType::IFDEstablishPACEChannelResponse); + QCOMPARE(ifdEstablishPaceChannelResponse.getContextHandle(), QStringLiteral("TestContext")); + QCOMPARE(ifdEstablishPaceChannelResponse.getSlotHandle(), QString()); + QCOMPARE(ifdEstablishPaceChannelResponse.getOutputData(), QByteArray()); + QVERIFY(!ifdEstablishPaceChannelResponse.resultHasError()); + QCOMPARE(ifdEstablishPaceChannelResponse.getResultMinor(), ECardApiResult::Minor::null); + + QCOMPARE(logSpy.count(), 2); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of \"SlotHandle\" should be of type \"string\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("The value of \"OutputData\" should be of type \"string\"")); + } + + +}; + +QTEST_GUILESS_MAIN(test_IfdEstablishPaceChannelResponse) +#include "test_IfdEstablishPaceChannelResponse.moc" diff --git a/test/qt/remote_device/messages/test_IfdGetStatus.cpp b/test/qt/remote_device/messages/test_IfdGetStatus.cpp new file mode 100644 index 0000000..bc1cb1d --- /dev/null +++ b/test/qt/remote_device/messages/test_IfdGetStatus.cpp @@ -0,0 +1,181 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "messages/IfdGetStatus.h" + +#include "LogHandler.h" + +#include + + +using namespace governikus; + + +class test_IfdGetStatus + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void initTestCase() + { + Env::getSingleton()->init(); + } + + + void invalidJson() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray message("FooBar"); + const auto& obj = QJsonDocument::fromJson(message).object(); + QVERIFY(obj.isEmpty()); + + IfdGetStatus msg(obj); + QVERIFY(msg.isIncomplete()); + + QCOMPARE(logSpy.count(), 5); + QVERIFY(logSpy.at(0).at(0).toString().contains("Missing value \"msg\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("Invalid messageType received: \"\"")); + QVERIFY(logSpy.at(2).at(0).toString().contains("Missing value \"ContextHandle\"")); + QVERIFY(logSpy.at(3).at(0).toString().contains("Missing value \"SlotName\"")); + QVERIFY(logSpy.at(4).at(0).toString().contains("The value of msg should be IFDGetStatus")); + } + + + void values() + { + const IfdGetStatus ifdGetStatus( + QStringLiteral("SlotName") + ); + + QVERIFY(!ifdGetStatus.isIncomplete()); + QCOMPARE(ifdGetStatus.getType(), RemoteCardMessageType::IFDGetStatus); + QCOMPARE(ifdGetStatus.getContextHandle(), QString()); + QCOMPARE(ifdGetStatus.getSlotName(), QStringLiteral("SlotName")); + } + + + void toJson() + { + const IfdGetStatus ifdGetStatus( + QStringLiteral("SlotName") + ); + + const QByteArray& byteArray = ifdGetStatus.toByteArray(QStringLiteral("TestContext")); + QCOMPARE(byteArray, + QByteArray("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"SlotName\": \"SlotName\",\n" + " \"msg\": \"IFDGetStatus\"\n" + "}\n")); + + const QJsonObject obj = QJsonDocument::fromJson(byteArray).object(); + QCOMPARE(obj.size(), 3); + QCOMPARE(obj.value(QLatin1String("msg")).toString(), QStringLiteral("IFDGetStatus")); + QCOMPARE(obj.value(QLatin1String("ContextHandle")).toString(), QStringLiteral("TestContext")); + QCOMPARE(obj.value(QLatin1String("SlotName")).toString(), QStringLiteral("SlotName")); + } + + + void fromJson() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"SlotName\": \"SlotName\",\n" + " \"msg\": \"IFDGetStatus\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const IfdGetStatus ifdGetStatus(obj); + QVERIFY(!ifdGetStatus.isIncomplete()); + QCOMPARE(ifdGetStatus.getType(), RemoteCardMessageType::IFDGetStatus); + QCOMPARE(ifdGetStatus.getContextHandle(), QStringLiteral("TestContext")); + QCOMPARE(ifdGetStatus.getSlotName(), QStringLiteral("SlotName")); + + QCOMPARE(logSpy.count(), 0); + } + + + void msgField_data() + { + QTest::addColumn("type"); + + const auto& msgTypes = Enum::getList(); + for (const auto& type : msgTypes) + { + QTest::newRow(getEnumName(type).data()) << type; + } + } + + + void msgField() + { + QFETCH(RemoteCardMessageType, type); + + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"SlotName\": \"SlotName\",\n" + " \"msg\": \"%1\"\n" + "}\n"); + const QJsonObject& obj = QJsonDocument::fromJson(message.replace("%1", QTest::currentDataTag())).object(); + const IfdGetStatus ifdGetStatus(obj); + + if (type == RemoteCardMessageType::IFDGetStatus) + { + QVERIFY(!ifdGetStatus.isIncomplete()); + QCOMPARE(ifdGetStatus.getType(), RemoteCardMessageType::IFDGetStatus); + + QCOMPARE(logSpy.count(), 0); + + return; + } + + QVERIFY(ifdGetStatus.isIncomplete()); + QCOMPARE(ifdGetStatus.getType(), type); + + if (type == RemoteCardMessageType::UNDEFINED) + { + QCOMPARE(logSpy.count(), 2); + QVERIFY(logSpy.at(0).at(0).toString().contains("Invalid messageType received: \"UNDEFINED\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("The value of msg should be IFDGetStatus")); + + return; + } + + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of msg should be IFDGetStatus")); + } + + + void wrongTypes() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"SlotName\": 1,\n" + " \"msg\": \"IFDGetStatus\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const IfdGetStatus ifdGetStatus(obj); + QVERIFY(ifdGetStatus.isIncomplete()); + QCOMPARE(ifdGetStatus.getType(), RemoteCardMessageType::IFDGetStatus); + QCOMPARE(ifdGetStatus.getContextHandle(), QStringLiteral("TestContext")); + QCOMPARE(ifdGetStatus.getSlotName(), QString()); + + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of \"SlotName\" should be of type \"string\"")); + } + + +}; + +QTEST_GUILESS_MAIN(test_IfdGetStatus) +#include "test_IfdGetStatus.moc" diff --git a/test/qt/remote_device/messages/test_IfdModifyPin.cpp b/test/qt/remote_device/messages/test_IfdModifyPin.cpp index 28e44f4..fe1351f 100644 --- a/test/qt/remote_device/messages/test_IfdModifyPin.cpp +++ b/test/qt/remote_device/messages/test_IfdModifyPin.cpp @@ -20,25 +20,28 @@ class test_IfdModifyPin private Q_SLOTS: void initTestCase() { - LogHandler::getInstance().init(); + Env::getSingleton()->init(); } void invalidJson() { - QSignalSpy logSpy(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); QByteArray message("FooBar"); const auto& obj = QJsonDocument::fromJson(message).object(); QVERIFY(obj.isEmpty()); IfdModifyPin msg(obj); + QVERIFY(msg.isIncomplete()); - QCOMPARE(logSpy.count(), 4); + QCOMPARE(logSpy.count(), 6); QVERIFY(logSpy.at(0).at(0).toString().contains("Missing value \"msg\"")); - QVERIFY(logSpy.at(1).at(0).toString().contains("Missing value \"ContextHandle\"")); - QVERIFY(logSpy.at(2).at(0).toString().contains("Missing value \"SlotHandle\"")); - QVERIFY(logSpy.at(3).at(0).toString().contains("Missing value \"InputData\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("Invalid messageType received: \"\"")); + QVERIFY(logSpy.at(2).at(0).toString().contains("Missing value \"ContextHandle\"")); + QVERIFY(logSpy.at(3).at(0).toString().contains("Missing value \"SlotHandle\"")); + QVERIFY(logSpy.at(4).at(0).toString().contains("Missing value \"InputData\"")); + QVERIFY(logSpy.at(5).at(0).toString().contains("The value of msg should be IFDModifyPIN")); } @@ -49,6 +52,7 @@ class test_IfdModifyPin QByteArray::fromHex("abcd1234") ); + QVERIFY(!ifdModifyPin.isIncomplete()); QCOMPARE(ifdModifyPin.getType(), RemoteCardMessageType::IFDModifyPIN); QCOMPARE(ifdModifyPin.getContextHandle(), QString()); QCOMPARE(ifdModifyPin.getSlotHandle(), QStringLiteral("SlotHandle")); @@ -63,9 +67,8 @@ class test_IfdModifyPin QByteArray::fromHex("abcd1234") ); - const QJsonDocument& doc = ifdModifyPin.toJson(QStringLiteral("TestContext")); - QVERIFY(doc.isObject()); - QCOMPARE(doc.toJson(), + const QByteArray& byteArray = ifdModifyPin.toByteArray(QStringLiteral("TestContext")); + QCOMPARE(byteArray, QByteArray("{\n" " \"ContextHandle\": \"TestContext\",\n" " \"InputData\": \"abcd1234\",\n" @@ -73,7 +76,7 @@ class test_IfdModifyPin " \"msg\": \"IFDModifyPIN\"\n" "}\n")); - const QJsonObject obj = doc.object(); + const QJsonObject obj = QJsonDocument::fromJson(byteArray).object(); QCOMPARE(obj.size(), 4); QCOMPARE(obj.value(QLatin1String("msg")).toString(), QStringLiteral("IFDModifyPIN")); QCOMPARE(obj.value(QLatin1String("ContextHandle")).toString(), QStringLiteral("TestContext")); @@ -84,7 +87,7 @@ class test_IfdModifyPin void fromJson() { - QSignalSpy logSpy(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); const QByteArray message("{\n" " \"ContextHandle\": \"TestContext\",\n" @@ -95,6 +98,7 @@ class test_IfdModifyPin const QJsonObject& obj = QJsonDocument::fromJson(message).object(); const IfdModifyPin ifdModifyPin(obj); + QVERIFY(!ifdModifyPin.isIncomplete()); QCOMPARE(ifdModifyPin.getType(), RemoteCardMessageType::IFDModifyPIN); QCOMPARE(ifdModifyPin.getContextHandle(), QStringLiteral("TestContext")); QCOMPARE(ifdModifyPin.getSlotHandle(), QStringLiteral("SlotHandle")); @@ -120,7 +124,7 @@ class test_IfdModifyPin { QFETCH(RemoteCardMessageType, type); - QSignalSpy logSpy(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); QByteArray message("{\n" " \"ContextHandle\": \"TestContext\",\n" @@ -133,6 +137,7 @@ class test_IfdModifyPin if (type == RemoteCardMessageType::IFDModifyPIN) { + QVERIFY(!ifdModifyPin.isIncomplete()); QCOMPARE(ifdModifyPin.getType(), RemoteCardMessageType::IFDModifyPIN); QCOMPARE(logSpy.count(), 0); @@ -140,16 +145,26 @@ class test_IfdModifyPin return; } - QVERIFY(ifdModifyPin.isValid()); + QVERIFY(ifdModifyPin.isIncomplete()); QCOMPARE(ifdModifyPin.getType(), type); - QCOMPARE(logSpy.count(), 0); + if (type == RemoteCardMessageType::UNDEFINED) + { + QCOMPARE(logSpy.count(), 2); + QVERIFY(logSpy.at(0).at(0).toString().contains("Invalid messageType received: \"UNDEFINED\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("The value of msg should be IFDModifyPIN")); + + return; + } + + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of msg should be IFDModifyPIN")); } void wrongTypes() { - QSignalSpy logSpy(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); const QByteArray message("{\n" " \"ContextHandle\": \"TestContext\",\n" @@ -160,6 +175,7 @@ class test_IfdModifyPin const QJsonObject& obj = QJsonDocument::fromJson(message).object(); const IfdModifyPin ifdModifyPin(obj); + QVERIFY(ifdModifyPin.isIncomplete()); QCOMPARE(ifdModifyPin.getType(), RemoteCardMessageType::IFDModifyPIN); QCOMPARE(ifdModifyPin.getContextHandle(), QStringLiteral("TestContext")); QCOMPARE(ifdModifyPin.getSlotHandle(), QString()); diff --git a/test/qt/remote_device/messages/test_IfdModifyPinResponse.cpp b/test/qt/remote_device/messages/test_IfdModifyPinResponse.cpp index 4dc252f..8ce451e 100644 --- a/test/qt/remote_device/messages/test_IfdModifyPinResponse.cpp +++ b/test/qt/remote_device/messages/test_IfdModifyPinResponse.cpp @@ -20,27 +20,30 @@ class test_IfdModifyPinResponse private Q_SLOTS: void initTestCase() { - LogHandler::getInstance().init(); + Env::getSingleton()->init(); } void invalidJson() { - QSignalSpy logSpy(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); QByteArray message("FooBar"); const auto& obj = QJsonDocument::fromJson(message).object(); QVERIFY(obj.isEmpty()); IfdModifyPinResponse msg(obj); + QVERIFY(msg.isIncomplete()); - QCOMPARE(logSpy.count(), 6); + QCOMPARE(logSpy.count(), 8); QVERIFY(logSpy.at(0).at(0).toString().contains("Missing value \"msg\"")); - QVERIFY(logSpy.at(1).at(0).toString().contains("Missing value \"ContextHandle\"")); - QVERIFY(logSpy.at(2).at(0).toString().contains("Missing value \"ResultMajor\"")); - QVERIFY(logSpy.at(3).at(0).toString().contains("Missing value \"ResultMinor\"")); - QVERIFY(logSpy.at(4).at(0).toString().contains("Missing value \"SlotHandle\"")); - QVERIFY(logSpy.at(5).at(0).toString().contains("Missing value \"OutputData\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("Invalid messageType received: \"\"")); + QVERIFY(logSpy.at(2).at(0).toString().contains("Missing value \"ContextHandle\"")); + QVERIFY(logSpy.at(3).at(0).toString().contains("Missing value \"ResultMajor\"")); + QVERIFY(logSpy.at(4).at(0).toString().contains("Missing value \"ResultMinor\"")); + QVERIFY(logSpy.at(5).at(0).toString().contains("Missing value \"SlotHandle\"")); + QVERIFY(logSpy.at(6).at(0).toString().contains("Missing value \"OutputData\"")); + QVERIFY(logSpy.at(7).at(0).toString().contains("The value of msg should be IFDModifyPINResponse")); } @@ -51,12 +54,13 @@ class test_IfdModifyPinResponse QByteArray::fromHex("abcd1234") ); + QVERIFY(!ifdModifyPinResponse.isIncomplete()); QCOMPARE(ifdModifyPinResponse.getType(), RemoteCardMessageType::IFDModifyPINResponse); QCOMPARE(ifdModifyPinResponse.getContextHandle(), QString()); QCOMPARE(ifdModifyPinResponse.getSlotHandle(), QStringLiteral("SlotHandle")); QCOMPARE(ifdModifyPinResponse.getOutputData(), QByteArray::fromHex("abcd1234")); QVERIFY(!ifdModifyPinResponse.resultHasError()); - QCOMPARE(ifdModifyPinResponse.getResultMinor(), QString()); + QCOMPARE(ifdModifyPinResponse.getResultMinor(), ECardApiResult::Minor::null); } @@ -67,9 +71,8 @@ class test_IfdModifyPinResponse QByteArray::fromHex("abcd1234") ); - const QJsonDocument& doc = ifdModifyPinResponse.toJson(QStringLiteral("TestContext")); - QVERIFY(doc.isObject()); - QCOMPARE(doc.toJson(), + const QByteArray& byteArray = ifdModifyPinResponse.toByteArray(QStringLiteral("TestContext")); + QCOMPARE(byteArray, QByteArray("{\n" " \"ContextHandle\": \"TestContext\",\n" " \"OutputData\": \"abcd1234\",\n" @@ -79,7 +82,7 @@ class test_IfdModifyPinResponse " \"msg\": \"IFDModifyPINResponse\"\n" "}\n")); - const QJsonObject obj = doc.object(); + const QJsonObject obj = QJsonDocument::fromJson(byteArray).object(); QCOMPARE(obj.size(), 6); QCOMPARE(obj.value(QLatin1String("msg")).toString(), QStringLiteral("IFDModifyPINResponse")); QCOMPARE(obj.value(QLatin1String("ContextHandle")).toString(), QStringLiteral("TestContext")); @@ -90,7 +93,7 @@ class test_IfdModifyPinResponse void fromJson() { - QSignalSpy logSpy(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); const QByteArray message("{\n" " \"ContextHandle\": \"TestContext\",\n" @@ -103,12 +106,13 @@ class test_IfdModifyPinResponse const QJsonObject& obj = QJsonDocument::fromJson(message).object(); const IfdModifyPinResponse ifdModifyPinResponse(obj); + QVERIFY(!ifdModifyPinResponse.isIncomplete()); QCOMPARE(ifdModifyPinResponse.getType(), RemoteCardMessageType::IFDModifyPINResponse); QCOMPARE(ifdModifyPinResponse.getContextHandle(), QString("TestContext")); QCOMPARE(ifdModifyPinResponse.getSlotHandle(), QStringLiteral("SlotHandle")); QCOMPARE(ifdModifyPinResponse.getOutputData(), QByteArray::fromHex("abcd1234")); QVERIFY(!ifdModifyPinResponse.resultHasError()); - QCOMPARE(ifdModifyPinResponse.getResultMinor(), QString()); + QCOMPARE(ifdModifyPinResponse.getResultMinor(), ECardApiResult::Minor::null); QCOMPARE(logSpy.count(), 0); } @@ -130,7 +134,7 @@ class test_IfdModifyPinResponse { QFETCH(RemoteCardMessageType, type); - QSignalSpy logSpy(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); QByteArray message("{\n" " \"ContextHandle\": \"TestContext\",\n" @@ -145,6 +149,7 @@ class test_IfdModifyPinResponse if (type == RemoteCardMessageType::IFDModifyPINResponse) { + QVERIFY(!ifdModifyPinResponse.isIncomplete()); QCOMPARE(ifdModifyPinResponse.getType(), RemoteCardMessageType::IFDModifyPINResponse); QCOMPARE(logSpy.count(), 0); @@ -152,16 +157,26 @@ class test_IfdModifyPinResponse return; } - QVERIFY(ifdModifyPinResponse.isValid()); + QVERIFY(ifdModifyPinResponse.isIncomplete()); QCOMPARE(ifdModifyPinResponse.getType(), type); - QCOMPARE(logSpy.count(), 0); + if (type == RemoteCardMessageType::UNDEFINED) + { + QCOMPARE(logSpy.count(), 2); + QVERIFY(logSpy.at(0).at(0).toString().contains("Invalid messageType received: \"UNDEFINED\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("The value of msg should be IFDModifyPINResponse")); + + return; + } + + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of msg should be IFDModifyPINResponse")); } void wrongTypes() { - QSignalSpy logSpy(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); const QByteArray message("{\n" " \"ContextHandle\": \"TestContext\",\n" @@ -174,12 +189,13 @@ class test_IfdModifyPinResponse const QJsonObject& obj = QJsonDocument::fromJson(message).object(); const IfdModifyPinResponse ifdModifyPinResponse(obj); + QVERIFY(ifdModifyPinResponse.isIncomplete()); QCOMPARE(ifdModifyPinResponse.getType(), RemoteCardMessageType::IFDModifyPINResponse); QCOMPARE(ifdModifyPinResponse.getContextHandle(), QStringLiteral("TestContext")); QCOMPARE(ifdModifyPinResponse.getSlotHandle(), QString()); QCOMPARE(ifdModifyPinResponse.getOutputData(), QByteArray()); QVERIFY(!ifdModifyPinResponse.resultHasError()); - QCOMPARE(ifdModifyPinResponse.getResultMinor(), QString()); + QCOMPARE(ifdModifyPinResponse.getResultMinor(), ECardApiResult::Minor::null); QCOMPARE(logSpy.count(), 2); QVERIFY(logSpy.at(0).at(0).toString().contains("The value of \"SlotHandle\" should be of type \"string\"")); @@ -187,6 +203,36 @@ class test_IfdModifyPinResponse } + void test_GetReturnCode_data() + { + QTest::addColumn("minor"); + QTest::addColumn("code"); + + QTest::newRow("timeout") << ECardApiResult::Minor::IFDL_Timeout_Error << CardReturnCode::INPUT_TIME_OUT; + QTest::newRow("cancellation") << ECardApiResult::Minor::IFDL_CancellationByUser << CardReturnCode::CANCELLATION_BY_USER; + QTest::newRow("mismatch") << ECardApiResult::Minor::IFDL_IO_RepeatedDataMismatch << CardReturnCode::NEW_PIN_MISMATCH; + QTest::newRow("unknownPinFormat") << ECardApiResult::Minor::IFDL_IO_UnknownPINFormat << CardReturnCode::NEW_PIN_INVALID_LENGTH; + QTest::newRow("unknownApiFunction") << ECardApiResult::Minor::AL_Unkown_API_Function << CardReturnCode::PROTOCOL_ERROR; + QTest::newRow("unknownError") << ECardApiResult::Minor::AL_Unknown_Error << CardReturnCode::UNKNOWN; + QTest::newRow("default") << ECardApiResult::Minor::SAL_Invalid_Key << CardReturnCode::COMMAND_FAILED; + } + + + void test_GetReturnCode() + { + QFETCH(ECardApiResult::Minor, minor); + QFETCH(CardReturnCode, code); + const QString slotHandle = QStringLiteral("slothandle"); + const QByteArray output("output"); + + const IfdModifyPinResponse response1(slotHandle, output, ECardApiResult::Minor::null); + const IfdModifyPinResponse response2(slotHandle, output, minor); + + QCOMPARE(response1.getReturnCode(), CardReturnCode::OK); + QCOMPARE(response2.getReturnCode(), code); + } + + }; QTEST_GUILESS_MAIN(test_IfdModifyPinResponse) diff --git a/test/qt/remote_device/messages/test_IfdStatus.cpp b/test/qt/remote_device/messages/test_IfdStatus.cpp new file mode 100644 index 0000000..6987088 --- /dev/null +++ b/test/qt/remote_device/messages/test_IfdStatus.cpp @@ -0,0 +1,390 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "messages/IfdStatus.h" + +#include "AppSettings.h" +#include "LogHandler.h" + +#include + + +using namespace governikus; + + +class test_IfdStatus + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void initTestCase() + { + Env::getSingleton()->init(); + } + + + void paceCapabilities_data() + { + QTest::addColumn("pace"); + QTest::addColumn("eid"); + QTest::addColumn("esign"); + QTest::addColumn("destroy"); + + QTest::newRow("false") << false << false << false << false; + QTest::newRow("true") << true << true << true << true; + } + + + void paceCapabilities() + { + QFETCH(bool, pace); + QFETCH(bool, eid); + QFETCH(bool, esign); + QFETCH(bool, destroy); + + PaceCapabilities capabilities(pace, eid, esign, destroy); + + QCOMPARE(capabilities.getPace(), pace); + QCOMPARE(capabilities.getEId(), eid); + QCOMPARE(capabilities.getESign(), esign); + QCOMPARE(capabilities.getDestroy(), destroy); + } + + + void invalidJson() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray message("FooBar"); + const auto& obj = QJsonDocument::fromJson(message).object(); + QVERIFY(obj.isEmpty()); + + IfdStatus msg(obj); + QVERIFY(msg.isIncomplete()); + + QCOMPARE(logSpy.count(), 9); + QVERIFY(logSpy.at(0).at(0).toString().contains("Missing value \"msg\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("Invalid messageType received: \"\"")); + QVERIFY(logSpy.at(2).at(0).toString().contains("Missing value \"ContextHandle\"")); + QVERIFY(logSpy.at(3).at(0).toString().contains("Missing value \"SlotName\"")); + QVERIFY(logSpy.at(4).at(0).toString().contains("Missing value \"PINCapabilities\"")); + QVERIFY(logSpy.at(5).at(0).toString().contains("Missing value \"MaxAPDULength\"")); + QVERIFY(logSpy.at(6).at(0).toString().contains("Missing value \"ConnectedReader\"")); + QVERIFY(logSpy.at(7).at(0).toString().contains("Missing value \"CardAvailable\"")); + QVERIFY(logSpy.at(8).at(0).toString().contains("The value of msg should be IFDStatus")); + } + + + void values() + { + const IfdStatus ifdStatus( + QStringLiteral("SlotName"), + PaceCapabilities(true, false, true, false), + 500, + false, + false + ); + + QVERIFY(!ifdStatus.isIncomplete()); + QCOMPARE(ifdStatus.getType(), RemoteCardMessageType::IFDStatus); + QCOMPARE(ifdStatus.getContextHandle(), QString()); + QCOMPARE(ifdStatus.getSlotName(), QStringLiteral("SlotName")); + QVERIFY(ifdStatus.getPaceCapabilities().getPace()); + QVERIFY(!ifdStatus.getPaceCapabilities().getEId()); + QVERIFY(ifdStatus.getPaceCapabilities().getESign()); + QVERIFY(!ifdStatus.getPaceCapabilities().getDestroy()); + QCOMPARE(ifdStatus.getMaxApduLength(), 500); + QVERIFY(!ifdStatus.getConnectedReader()); + QVERIFY(!ifdStatus.getCardAvailable()); + } + + + void toJson() + { + const IfdStatus ifdStatus( + QStringLiteral("SlotName"), + PaceCapabilities(true, false, true, false), + 500, + false, + false + ); + + const QByteArray& byteArray = ifdStatus.toByteArray(QStringLiteral("TestContext")); + QCOMPARE(byteArray, + QByteArray("{\n" + " \"CardAvailable\": false,\n" + " \"ConnectedReader\": false,\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"EFATR\": null,\n" + " \"EFDIR\": null,\n" + " \"MaxAPDULength\": 500,\n" + " \"PINCapabilities\": {\n" + " \"Destroy\": false,\n" + " \"PACE\": true,\n" + " \"eID\": false,\n" + " \"eSign\": true\n" + " },\n" + " \"SlotName\": \"SlotName\",\n" + " \"msg\": \"IFDStatus\"\n" + "}\n")); + + const QJsonObject obj = QJsonDocument::fromJson(byteArray).object(); + QCOMPARE(obj.size(), 9); + QCOMPARE(obj.value(QLatin1String("msg")).toString(), QStringLiteral("IFDStatus")); + QCOMPARE(obj.value(QLatin1String("ContextHandle")).toString(), QStringLiteral("TestContext")); + QCOMPARE(obj.value(QLatin1String("SlotName")).toString(), QStringLiteral("SlotName")); + const QJsonObject cap = obj.value(QLatin1String("PINCapabilities")).toObject(); + QCOMPARE(cap.size(), 4); + QVERIFY(cap.value(QLatin1String("PACE")).toBool()); + QVERIFY(!cap.value(QLatin1String("eID")).toBool()); + QVERIFY(cap.value(QLatin1String("eSign")).toBool()); + QVERIFY(!cap.value(QLatin1String("Destroy")).toBool()); + QCOMPARE(obj.value(QLatin1String("MaxAPDULength")).toInt(), 500); + QVERIFY(!obj.value(QLatin1String("ConnectedReader")).toBool()); + QVERIFY(!obj.value(QLatin1String("CardAvailable")).toBool()); + } + + + void fromJson() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"CardAvailable\": false,\n" + " \"ConnectedReader\": false,\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"EFATR\": null,\n" + " \"EFDIR\": null,\n" + " \"MaxAPDULength\": 500,\n" + " \"PINCapabilities\": {\n" + " \"Destroy\": false,\n" + " \"PACE\": true,\n" + " \"eID\": false,\n" + " \"eSign\": true\n" + " },\n" + " \"SlotName\": \"SlotName\",\n" + " \"msg\": \"IFDStatus\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const IfdStatus ifdStatus(obj); + QVERIFY(!ifdStatus.isIncomplete()); + QCOMPARE(ifdStatus.getType(), RemoteCardMessageType::IFDStatus); + QCOMPARE(ifdStatus.getContextHandle(), QStringLiteral("TestContext")); + QCOMPARE(ifdStatus.getSlotName(), QStringLiteral("SlotName")); + QVERIFY(ifdStatus.getPaceCapabilities().getPace()); + QVERIFY(!ifdStatus.getPaceCapabilities().getEId()); + QVERIFY(ifdStatus.getPaceCapabilities().getESign()); + QVERIFY(!ifdStatus.getPaceCapabilities().getDestroy()); + QCOMPARE(ifdStatus.getMaxApduLength(), 500); + QVERIFY(!ifdStatus.getConnectedReader()); + QVERIFY(!ifdStatus.getCardAvailable()); + + QCOMPARE(logSpy.count(), 0); + } + + + void msgField_data() + { + QTest::addColumn("type"); + + const auto& msgTypes = Enum::getList(); + for (const auto& type : msgTypes) + { + QTest::newRow(getEnumName(type).data()) << type; + } + } + + + void msgField() + { + QFETCH(RemoteCardMessageType, type); + + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray message("{\n" + " \"CardAvailable\": false,\n" + " \"ConnectedReader\": false,\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"EFATR\": null,\n" + " \"EFDIR\": null,\n" + " \"MaxAPDULength\": 500,\n" + " \"PINCapabilities\": {\n" + " \"Destroy\": false,\n" + " \"PACE\": true,\n" + " \"eID\": false,\n" + " \"eSign\": true\n" + " },\n" + " \"SlotName\": \"SlotName\",\n" + " \"msg\": \"%1\"\n" + "}\n"); + const QJsonObject& obj = QJsonDocument::fromJson(message.replace("%1", QTest::currentDataTag())).object(); + const IfdStatus ifdStatus(obj); + + if (type == RemoteCardMessageType::IFDStatus) + { + QVERIFY(!ifdStatus.isIncomplete()); + QCOMPARE(ifdStatus.getType(), RemoteCardMessageType::IFDStatus); + + QCOMPARE(logSpy.count(), 0); + + return; + } + + QVERIFY(ifdStatus.isIncomplete()); + QCOMPARE(ifdStatus.getType(), type); + + if (type == RemoteCardMessageType::UNDEFINED) + { + QCOMPARE(logSpy.count(), 2); + QVERIFY(logSpy.at(0).at(0).toString().contains("Invalid messageType received: \"UNDEFINED\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("The value of msg should be IFDStatus")); + + return; + } + + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of msg should be IFDStatus")); + } + + + void wrongTypes() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"CardAvailable\": 1,\n" + " \"ConnectedReader\": 2,\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"EFATR\": null,\n" + " \"EFDIR\": null,\n" + " \"MaxAPDULength\": \"3\",\n" + " \"PINCapabilities\": 4,\n" + " \"SlotName\": 5,\n" + " \"msg\": \"IFDStatus\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const IfdStatus ifdStatus(obj); + QVERIFY(ifdStatus.isIncomplete()); + QCOMPARE(ifdStatus.getType(), RemoteCardMessageType::IFDStatus); + QCOMPARE(ifdStatus.getContextHandle(), QStringLiteral("TestContext")); + QCOMPARE(ifdStatus.getSlotName(), QString()); + QVERIFY(!ifdStatus.getPaceCapabilities().getPace()); + QVERIFY(!ifdStatus.getPaceCapabilities().getEId()); + QVERIFY(!ifdStatus.getPaceCapabilities().getESign()); + QVERIFY(!ifdStatus.getPaceCapabilities().getDestroy()); + QCOMPARE(ifdStatus.getMaxApduLength(), 0); + QVERIFY(!ifdStatus.getConnectedReader()); + QVERIFY(!ifdStatus.getCardAvailable()); + + QCOMPARE(logSpy.count(), 5); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of \"SlotName\" should be of type \"string\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("The value of \"PINCapabilities\" should be of type \"object\"")); + QVERIFY(logSpy.at(2).at(0).toString().contains("The value of \"MaxAPDULength\" should be of type \"number\"")); + QVERIFY(logSpy.at(3).at(0).toString().contains("The value of \"ConnectedReader\" should be of type \"boolean\"")); + QVERIFY(logSpy.at(4).at(0).toString().contains("The value of \"CardAvailable\" should be of type \"boolean\"")); + } + + + void wrongPINCapabilityTypes() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"CardAvailable\": false,\n" + " \"ConnectedReader\": false,\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"EFATR\": null,\n" + " \"EFDIR\": null,\n" + " \"MaxAPDULength\": 500,\n" + " \"PINCapabilities\": {\n" + " \"Destroy\": \"1\",\n" + " \"PACE\": \"2\",\n" + " \"eID\": \"3\",\n" + " \"eSign\": \"4\"\n" + " },\n" + " \"SlotName\": \"SlotName\",\n" + " \"msg\": \"IFDStatus\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const IfdStatus ifdStatus(obj); + QVERIFY(ifdStatus.isIncomplete()); + QCOMPARE(ifdStatus.getType(), RemoteCardMessageType::IFDStatus); + QCOMPARE(ifdStatus.getContextHandle(), QStringLiteral("TestContext")); + QCOMPARE(ifdStatus.getSlotName(), QStringLiteral("SlotName")); + QVERIFY(!ifdStatus.getPaceCapabilities().getPace()); + QVERIFY(!ifdStatus.getPaceCapabilities().getEId()); + QVERIFY(!ifdStatus.getPaceCapabilities().getESign()); + QVERIFY(!ifdStatus.getPaceCapabilities().getDestroy()); + QCOMPARE(ifdStatus.getMaxApduLength(), 500); + QVERIFY(!ifdStatus.getConnectedReader()); + QVERIFY(!ifdStatus.getCardAvailable()); + + QCOMPARE(logSpy.count(), 4); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of \"PACE\" should be of type \"boolean\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("The value of \"eID\" should be of type \"boolean\"")); + QVERIFY(logSpy.at(2).at(0).toString().contains("The value of \"eSign\" should be of type \"boolean\"")); + QVERIFY(logSpy.at(3).at(0).toString().contains("The value of \"Destroy\" should be of type \"boolean\"")); + } + + + void constructionWithReaderInfo_data() + { + QTest::addColumn("slotName"); + QTest::addColumn("isBasicReader"); + QTest::addColumn("maxApduLength"); + QTest::addColumn("connected"); + QTest::addColumn("cardAvailable"); + QTest::addColumn("pinPadMode"); + + QTest::newRow("Unconnected basic reader") << "BasicReader" << true << 500 << false << false << false; + QTest::newRow("Connected basic reader without card") << "SlotName" << true << 500 << true << false << false; + QTest::newRow("Connected basic reader with card") << "SlotName" << true << 500 << true << true << false; + QTest::newRow("Connected basic reader with card without extended length") << "SlotName" << true << 200 << true << true << false; + QTest::newRow("Connected basic reader with card and pin pad mode") << "SlotName" << true << 500 << true << true << true; + QTest::newRow("Connected comfort reader with card and enabled pin pad mode") << "SlotName" << false << 500 << true << true << true; + QTest::newRow("Connected comfort reader with card and disabled pin pad mode") << "SlotName" << false << 500 << true << true << false; + } + + + void constructionWithReaderInfo() + { + QFETCH(QString, slotName); + QFETCH(bool, isBasicReader); + QFETCH(int, maxApduLength); + QFETCH(bool, connected); + QFETCH(bool, cardAvailable); + QFETCH(bool, pinPadMode); + + Env::getSingleton()->getRemoteServiceSettings().setPinPadMode(pinPadMode); + + const CardInfo cardInfo(cardAvailable ? CardType::EID_CARD : CardType::NONE); + + ReaderInfo readerInfo(slotName, ReaderManagerPlugInType::UNKNOWN, cardInfo); + readerInfo.setBasicReader(isBasicReader); + readerInfo.setMaxApduLength(maxApduLength); + readerInfo.setConnected(connected); + + const IfdStatus ifdStatus(readerInfo); + QVERIFY(!ifdStatus.isIncomplete()); + QCOMPARE(ifdStatus.getType(), RemoteCardMessageType::IFDStatus); + QCOMPARE(ifdStatus.getContextHandle(), QString()); + QCOMPARE(ifdStatus.getSlotName(), slotName); + QCOMPARE(ifdStatus.getPaceCapabilities().getPace(), !isBasicReader || pinPadMode); + QVERIFY(!ifdStatus.getPaceCapabilities().getEId()); + QVERIFY(!ifdStatus.getPaceCapabilities().getESign()); + QVERIFY(!ifdStatus.getPaceCapabilities().getDestroy()); + QCOMPARE(ifdStatus.getMaxApduLength(), maxApduLength); + QCOMPARE(ifdStatus.getConnectedReader(), connected); + QCOMPARE(ifdStatus.getCardAvailable(), cardAvailable); + } + + +}; + +QTEST_GUILESS_MAIN(test_IfdStatus) +#include "test_IfdStatus.moc" diff --git a/test/qt/remote_device/messages/test_IfdTransmit.cpp b/test/qt/remote_device/messages/test_IfdTransmit.cpp new file mode 100644 index 0000000..850d786 --- /dev/null +++ b/test/qt/remote_device/messages/test_IfdTransmit.cpp @@ -0,0 +1,301 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "messages/IfdTransmit.h" + +#include "LogHandler.h" + +#include + + +using namespace governikus; + + +class test_IfdTransmit + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void initTestCase() + { + Env::getSingleton()->init(); + } + + + void invalidJson() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray message("FooBar"); + const auto& obj = QJsonDocument::fromJson(message).object(); + QVERIFY(obj.isEmpty()); + + IfdTransmit msg(obj); + QVERIFY(msg.isIncomplete()); + + QCOMPARE(logSpy.count(), 6); + QVERIFY(logSpy.at(0).at(0).toString().contains("Missing value \"msg\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("Invalid messageType received: \"\"")); + QVERIFY(logSpy.at(2).at(0).toString().contains("Missing value \"ContextHandle\"")); + QVERIFY(logSpy.at(3).at(0).toString().contains("Missing value \"SlotHandle\"")); + QVERIFY(logSpy.at(4).at(0).toString().contains("Missing value \"CommandAPDUs\"")); + QVERIFY(logSpy.at(5).at(0).toString().contains("The value of msg should be IFDTransmit")); + } + + + void values() + { + const IfdTransmit ifdTransmit( + QStringLiteral("SlotHandle"), + QByteArray::fromHex("00a402022f00") + ); + + QVERIFY(!ifdTransmit.isIncomplete()); + QCOMPARE(ifdTransmit.getType(), RemoteCardMessageType::IFDTransmit); + QCOMPARE(ifdTransmit.getContextHandle(), QString()); + QCOMPARE(ifdTransmit.getSlotHandle(), QStringLiteral("SlotHandle")); + QCOMPARE(ifdTransmit.getInputApdu(), QByteArray::fromHex("00a402022f00")); + } + + + void toJson() + { + const IfdTransmit ifdTransmit( + QStringLiteral("SlotHandle"), + QByteArray::fromHex("00a402022f00") + ); + + const QByteArray& byteArray = ifdTransmit.toByteArray(QStringLiteral("TestContext")); + QCOMPARE(byteArray, + QByteArray("{\n" + " \"CommandAPDUs\": [\n" + " {\n" + " \"AcceptableStatusCodes\": null,\n" + " \"InputAPDU\": \"00a402022f00\"\n" + " }\n" + " ],\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"SlotHandle\": \"SlotHandle\",\n" + " \"msg\": \"IFDTransmit\"\n" + "}\n")); + + const QJsonObject obj = QJsonDocument::fromJson(byteArray).object(); + QCOMPARE(obj.size(), 4); + QCOMPARE(obj.value(QLatin1String("msg")).toString(), QStringLiteral("IFDTransmit")); + QCOMPARE(obj.value(QLatin1String("ContextHandle")).toString(), QStringLiteral("TestContext")); + QCOMPARE(obj.value(QLatin1String("SlotHandle")).toString(), QStringLiteral("SlotHandle")); + const QJsonArray array = obj.value(QLatin1String("CommandAPDUs")).toArray(); + QCOMPARE(array.size(), 1); + const QJsonObject com = array.at(0).toObject(); + QCOMPARE(com.size(), 2); + QVERIFY(com.value(QLatin1String("AcceptableStatusCodes")).isNull()); + QCOMPARE(com.value(QLatin1String("InputAPDU")).toString(), QStringLiteral("00a402022f00")); + } + + + void fromJson() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"CommandAPDUs\": [\n" + " {\n" + " \"AcceptableStatusCodes\": null,\n" + " \"InputAPDU\": \"00a402022f00\"\n" + " }\n" + " ],\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"SlotHandle\": \"SlotHandle\",\n" + " \"msg\": \"IFDTransmit\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const IfdTransmit ifdTransmit(obj); + QVERIFY(!ifdTransmit.isIncomplete()); + QCOMPARE(ifdTransmit.getType(), RemoteCardMessageType::IFDTransmit); + QCOMPARE(ifdTransmit.getContextHandle(), QStringLiteral("TestContext")); + QCOMPARE(ifdTransmit.getSlotHandle(), QStringLiteral("SlotHandle")); + QCOMPARE(ifdTransmit.getInputApdu(), QByteArray::fromHex("00a402022f00")); + + QCOMPARE(logSpy.count(), 0); + } + + + void msgField_data() + { + QTest::addColumn("type"); + + const auto& msgTypes = Enum::getList(); + for (const auto& type : msgTypes) + { + QTest::newRow(getEnumName(type).data()) << type; + } + } + + + void msgField() + { + QFETCH(RemoteCardMessageType, type); + + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray message("{\n" + " \"CommandAPDUs\": [\n" + " {\n" + " \"AcceptableStatusCodes\": null,\n" + " \"InputAPDU\": \"00a402022f00\"\n" + " }\n" + " ],\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"SlotHandle\": \"SlotHandle\",\n" + " \"msg\": \"%1\"\n" + "}\n"); + const QJsonObject& obj = QJsonDocument::fromJson(message.replace("%1", QTest::currentDataTag())).object(); + const IfdTransmit ifdTransmit(obj); + + if (type == RemoteCardMessageType::IFDTransmit) + { + QVERIFY(!ifdTransmit.isIncomplete()); + QCOMPARE(ifdTransmit.getType(), RemoteCardMessageType::IFDTransmit); + + QCOMPARE(logSpy.count(), 0); + + return; + } + + QVERIFY(ifdTransmit.isIncomplete()); + QCOMPARE(ifdTransmit.getType(), type); + + if (type == RemoteCardMessageType::UNDEFINED) + { + QCOMPARE(logSpy.count(), 2); + QVERIFY(logSpy.at(0).at(0).toString().contains("Invalid messageType received: \"UNDEFINED\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("The value of msg should be IFDTransmit")); + + return; + } + + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of msg should be IFDTransmit")); + } + + + void wrongTypes() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"CommandAPDUs\": 1,\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"SlotHandle\": 2,\n" + " \"msg\": \"IFDTransmit\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const IfdTransmit ifdTransmit(obj); + QVERIFY(ifdTransmit.isIncomplete()); + QCOMPARE(ifdTransmit.getType(), RemoteCardMessageType::IFDTransmit); + QCOMPARE(ifdTransmit.getContextHandle(), QStringLiteral("TestContext")); + QCOMPARE(ifdTransmit.getSlotHandle(), QString()); + QCOMPARE(ifdTransmit.getInputApdu(), QByteArray()); + + QCOMPARE(logSpy.count(), 2); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of \"SlotHandle\" should be of type \"string\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("The value of \"CommandAPDUs\" should be of type \"array\"")); + } + + + void wrongCommandApdusType() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"CommandAPDUs\": [\n" + " 1\n" + " ],\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"SlotHandle\": \"SlotHandle\",\n" + " \"msg\": \"IFDTransmit\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const IfdTransmit ifdTransmit(obj); + QVERIFY(ifdTransmit.isIncomplete()); + QCOMPARE(ifdTransmit.getType(), RemoteCardMessageType::IFDTransmit); + QCOMPARE(ifdTransmit.getContextHandle(), QStringLiteral("TestContext")); + QCOMPARE(ifdTransmit.getSlotHandle(), QString("SlotHandle")); + QCOMPARE(ifdTransmit.getInputApdu(), QByteArray()); + + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of \"CommandAPDUs\" should be of type \"object array\"")); + } + + + void wrongCommandApdusSubTypes() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"CommandAPDUs\": [\n" + " {\n" + " \"AcceptableStatusCodes\": 1,\n" + " \"InputAPDU\": 2\n" + " }\n" + " ],\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"SlotHandle\": \"SlotHandle\",\n" + " \"msg\": \"IFDTransmit\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const IfdTransmit ifdTransmit(obj); + QVERIFY(ifdTransmit.isIncomplete()); + QCOMPARE(ifdTransmit.getType(), RemoteCardMessageType::IFDTransmit); + QCOMPARE(ifdTransmit.getContextHandle(), QStringLiteral("TestContext")); + QCOMPARE(ifdTransmit.getSlotHandle(), QString("SlotHandle")); + QCOMPARE(ifdTransmit.getInputApdu(), QByteArray()); + + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of \"InputAPDU\" should be of type \"string\"")); + } + + + void multipleApdus() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"CommandAPDUs\": [\n" + " {\n" + " \"AcceptableStatusCodes\": \"9000\",\n" + " \"InputAPDU\": \"00a402022f00\"\n" + " },\n" + " {\n" + " \"AcceptableStatusCodes\": \"6300\",\n" + " \"InputAPDU\": \"00a402022f01\"\n" + " }\n" + " ],\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"SlotHandle\": \"SlotHandle\",\n" + " \"msg\": \"IFDTransmit\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const IfdTransmit ifdTransmit(obj); + QVERIFY(!ifdTransmit.isIncomplete()); + QCOMPARE(ifdTransmit.getType(), RemoteCardMessageType::IFDTransmit); + QCOMPARE(ifdTransmit.getContextHandle(), QStringLiteral("TestContext")); + QCOMPARE(ifdTransmit.getSlotHandle(), QStringLiteral("SlotHandle")); + QCOMPARE(ifdTransmit.getInputApdu(), QByteArray::fromHex("00a402022f00")); + + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.at(0).at(0).toString().contains("Only using the first CommandAPDU. Command chaining ist not supported yet")); + } + + +}; + +QTEST_GUILESS_MAIN(test_IfdTransmit) +#include "test_IfdTransmit.moc" diff --git a/test/qt/remote_device/messages/test_IfdTransmitResponse.cpp b/test/qt/remote_device/messages/test_IfdTransmitResponse.cpp new file mode 100644 index 0000000..48d9259 --- /dev/null +++ b/test/qt/remote_device/messages/test_IfdTransmitResponse.cpp @@ -0,0 +1,280 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "messages/IfdTransmitResponse.h" + +#include "LogHandler.h" + +#include + + +using namespace governikus; + + +class test_IfdTransmitResponse + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void initTestCase() + { + Env::getSingleton()->init(); + } + + + void invalidJson() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray message("FooBar"); + const auto& obj = QJsonDocument::fromJson(message).object(); + QVERIFY(obj.isEmpty()); + + IfdTransmitResponse msg(obj); + QVERIFY(msg.isIncomplete()); + + QCOMPARE(logSpy.count(), 8); + QVERIFY(logSpy.at(0).at(0).toString().contains("Missing value \"msg\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("Invalid messageType received: \"\"")); + QVERIFY(logSpy.at(2).at(0).toString().contains("Missing value \"ContextHandle\"")); + QVERIFY(logSpy.at(3).at(0).toString().contains("Missing value \"ResultMajor\"")); + QVERIFY(logSpy.at(4).at(0).toString().contains("Missing value \"ResultMinor\"")); + QVERIFY(logSpy.at(5).at(0).toString().contains("Missing value \"SlotHandle\"")); + QVERIFY(logSpy.at(6).at(0).toString().contains("Missing value \"ResponseAPDUs\"")); + QVERIFY(logSpy.at(7).at(0).toString().contains("The value of msg should be IFDTransmitResponse")); + } + + + void values() + { + const IfdTransmitResponse ifdTransmitResponse( + QStringLiteral("SlotHandle"), + QByteArray::fromHex("9000") + ); + + QVERIFY(!ifdTransmitResponse.isIncomplete()); + QCOMPARE(ifdTransmitResponse.getType(), RemoteCardMessageType::IFDTransmitResponse); + QCOMPARE(ifdTransmitResponse.getContextHandle(), QString()); + QCOMPARE(ifdTransmitResponse.getSlotHandle(), QStringLiteral("SlotHandle")); + QCOMPARE(ifdTransmitResponse.getResponseApdu(), QByteArray::fromHex("9000")); + QVERIFY(!ifdTransmitResponse.resultHasError()); + QCOMPARE(ifdTransmitResponse.getResultMinor(), ECardApiResult::Minor::null); + } + + + void toJson() + { + const IfdTransmitResponse ifdTransmitResponse( + QStringLiteral("SlotHandle"), + QByteArray::fromHex("9000") + ); + + const QByteArray& byteArray = ifdTransmitResponse.toByteArray(QStringLiteral("TestContext")); + QCOMPARE(byteArray, + QByteArray("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"ResponseAPDUs\": [\n" + " \"9000\"\n" + " ],\n" + " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\",\n" + " \"ResultMinor\": null,\n" + " \"SlotHandle\": \"SlotHandle\",\n" + " \"msg\": \"IFDTransmitResponse\"\n" + "}\n")); + + const QJsonObject obj = QJsonDocument::fromJson(byteArray).object(); + QCOMPARE(obj.size(), 6); + QCOMPARE(obj.value(QLatin1String("msg")).toString(), QStringLiteral("IFDTransmitResponse")); + QCOMPARE(obj.value(QLatin1String("ContextHandle")).toString(), QStringLiteral("TestContext")); + QCOMPARE(obj.value(QLatin1String("SlotHandle")).toString(), QStringLiteral("SlotHandle")); + const QJsonArray array = obj.value(QLatin1String("ResponseAPDUs")).toArray(); + QCOMPARE(array.size(), 1); + QCOMPARE(array.at(0).toString(), QStringLiteral("9000")); + QCOMPARE(obj.value(QLatin1String("ResultMajor")).toString(), QStringLiteral("http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok")); + QCOMPARE(obj.value(QLatin1String("ResultMinor")).toString(), QString()); + } + + + void fromJson() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"ResponseAPDUs\": [\n" + " \"9000\"\n" + " ],\n" + " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\",\n" + " \"ResultMinor\": null,\n" + " \"SlotHandle\": \"SlotHandle\",\n" + " \"msg\": \"IFDTransmitResponse\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const IfdTransmitResponse ifdTransmitResponse(obj); + QVERIFY(!ifdTransmitResponse.isIncomplete()); + QCOMPARE(ifdTransmitResponse.getType(), RemoteCardMessageType::IFDTransmitResponse); + QCOMPARE(ifdTransmitResponse.getContextHandle(), QString("TestContext")); + QCOMPARE(ifdTransmitResponse.getSlotHandle(), QStringLiteral("SlotHandle")); + QCOMPARE(ifdTransmitResponse.getResponseApdu(), QByteArray::fromHex("9000")); + QVERIFY(!ifdTransmitResponse.resultHasError()); + QCOMPARE(ifdTransmitResponse.getResultMinor(), ECardApiResult::Minor::null); + + QCOMPARE(logSpy.count(), 0); + } + + + void msgField_data() + { + QTest::addColumn("type"); + + const auto& msgTypes = Enum::getList(); + for (const auto& type : msgTypes) + { + QTest::newRow(getEnumName(type).data()) << type; + } + } + + + void msgField() + { + QFETCH(RemoteCardMessageType, type); + + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"ResponseAPDUs\": [\n" + " \"9000\"\n" + " ],\n" + " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\",\n" + " \"ResultMinor\": null,\n" + " \"SlotHandle\": \"SlotHandle\",\n" + " \"msg\": \"%1\"\n" + "}\n"); + const QJsonObject& obj = QJsonDocument::fromJson(message.replace("%1", QTest::currentDataTag())).object(); + const IfdTransmitResponse ifdTransmitResponse(obj); + + if (type == RemoteCardMessageType::IFDTransmitResponse) + { + QVERIFY(!ifdTransmitResponse.isIncomplete()); + QCOMPARE(ifdTransmitResponse.getType(), RemoteCardMessageType::IFDTransmitResponse); + + QCOMPARE(logSpy.count(), 0); + + return; + } + + QVERIFY(ifdTransmitResponse.isIncomplete()); + QCOMPARE(ifdTransmitResponse.getType(), type); + + if (type == RemoteCardMessageType::UNDEFINED) + { + QCOMPARE(logSpy.count(), 2); + QVERIFY(logSpy.at(0).at(0).toString().contains("Invalid messageType received: \"UNDEFINED\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("The value of msg should be IFDTransmitResponse")); + + return; + } + + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of msg should be IFDTransmitResponse")); + } + + + void wrongTypes() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"ResponseAPDUs\": 1,\n" + " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\",\n" + " \"ResultMinor\": null,\n" + " \"SlotHandle\": 2,\n" + " \"msg\": \"IFDTransmitResponse\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const IfdTransmitResponse ifdTransmitResponse(obj); + QVERIFY(ifdTransmitResponse.isIncomplete()); + QCOMPARE(ifdTransmitResponse.getType(), RemoteCardMessageType::IFDTransmitResponse); + QCOMPARE(ifdTransmitResponse.getContextHandle(), QStringLiteral("TestContext")); + QCOMPARE(ifdTransmitResponse.getSlotHandle(), QString()); + QCOMPARE(ifdTransmitResponse.getResponseApdu(), QByteArray()); + QVERIFY(!ifdTransmitResponse.resultHasError()); + QCOMPARE(ifdTransmitResponse.getResultMinor(), ECardApiResult::Minor::null); + + QCOMPARE(logSpy.count(), 2); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of \"SlotHandle\" should be of type \"string\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("The value of \"ResponseAPDUs\" should be of type \"array\"")); + } + + + void wrongResponseApdusType() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"ResponseAPDUs\": [\n" + " 1\n" + " ],\n" + " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\",\n" + " \"ResultMinor\": null,\n" + " \"SlotHandle\": \"SlotHandle\",\n" + " \"msg\": \"IFDTransmitResponse\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const IfdTransmitResponse ifdTransmitResponse(obj); + QVERIFY(ifdTransmitResponse.isIncomplete()); + QCOMPARE(ifdTransmitResponse.getType(), RemoteCardMessageType::IFDTransmitResponse); + QCOMPARE(ifdTransmitResponse.getContextHandle(), QStringLiteral("TestContext")); + QCOMPARE(ifdTransmitResponse.getSlotHandle(), QString("SlotHandle")); + QCOMPARE(ifdTransmitResponse.getResponseApdu(), QByteArray()); + QVERIFY(!ifdTransmitResponse.resultHasError()); + QCOMPARE(ifdTransmitResponse.getResultMinor(), ECardApiResult::Minor::null); + + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of \"ResponseAPDUs\" should be of type \"string array\"")); + } + + + void multipleApdus() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"ResponseAPDUs\": [\n" + " \"9000\",\n" + " \"6300\"\n" + " ],\n" + " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\",\n" + " \"ResultMinor\": null,\n" + " \"SlotHandle\": \"SlotHandle\",\n" + " \"msg\": \"IFDTransmitResponse\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const IfdTransmitResponse ifdTransmitResponse(obj); + QVERIFY(!ifdTransmitResponse.isIncomplete()); + QCOMPARE(ifdTransmitResponse.getType(), RemoteCardMessageType::IFDTransmitResponse); + QCOMPARE(ifdTransmitResponse.getContextHandle(), QStringLiteral("TestContext")); + QCOMPARE(ifdTransmitResponse.getSlotHandle(), QString("SlotHandle")); + QCOMPARE(ifdTransmitResponse.getResponseApdu(), QByteArray::fromHex("9000")); + QVERIFY(!ifdTransmitResponse.resultHasError()); + QCOMPARE(ifdTransmitResponse.getResultMinor(), ECardApiResult::Minor::null); + + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.at(0).at(0).toString().contains("Only using the first ResponseAPDU. Command chaining ist not supported yet")); + } + + +}; + +QTEST_GUILESS_MAIN(test_IfdTransmitResponse) +#include "test_IfdTransmitResponse.moc" diff --git a/test/qt/remote_device/messages/test_IfdVersion.cpp b/test/qt/remote_device/messages/test_IfdVersion.cpp index 58d4a90..42bdcb3 100644 --- a/test/qt/remote_device/messages/test_IfdVersion.cpp +++ b/test/qt/remote_device/messages/test_IfdVersion.cpp @@ -17,9 +17,9 @@ class test_IfdVersion private Q_SLOTS: void stringParsing() { - QCOMPARE(IfdVersion::fromString("IFDInterface_WebSocket_v0"), IfdVersion(IfdVersion::Version::v0)); + QCOMPARE(IfdVersion::fromString("IFDInterface_WebSocket_v0"), IfdVersion::Version::v0); - QCOMPARE(IfdVersion::fromString("IFDInterface_WebSocket_v9001"), IfdVersion(IfdVersion::Version::Unknown)); + QCOMPARE(IfdVersion::fromString("IFDInterface_WebSocket_v9001"), IfdVersion::Version::Unknown); } @@ -45,9 +45,10 @@ class test_IfdVersion void selectSupportedVersions() { - QCOMPARE(IfdVersion::selectLatestSupported({}), IfdVersion(IfdVersion::Version::Unknown)); - QCOMPARE(IfdVersion::selectLatestSupported({IfdVersion::Version::Unknown}), IfdVersion(IfdVersion::Version::Unknown)); - QCOMPARE(IfdVersion::selectLatestSupported({IfdVersion::Version::v0}), IfdVersion(IfdVersion::Version::v0)); + QCOMPARE(IfdVersion::selectLatestSupported({}), IfdVersion::Version::Unknown); + QCOMPARE(IfdVersion::selectLatestSupported({IfdVersion::Version::v_test}), IfdVersion::Version::Unknown); + QCOMPARE(IfdVersion::selectLatestSupported({IfdVersion::Version::v0}), IfdVersion::Version::v0); + QCOMPARE(IfdVersion::selectLatestSupported({IfdVersion::Version::v0, IfdVersion::Version::v_test}), IfdVersion::Version::v0); } diff --git a/test/qt/remote_device/messages/test_RemoteMessage.cpp b/test/qt/remote_device/messages/test_RemoteMessage.cpp new file mode 100644 index 0000000..9ad8cd6 --- /dev/null +++ b/test/qt/remote_device/messages/test_RemoteMessage.cpp @@ -0,0 +1,276 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "messages/RemoteMessage.h" + +#include "LogHandler.h" + +#include + + +using namespace governikus; + + +class test_RemoteMessage + : public QObject +{ + Q_OBJECT + + private: + void checkMissingContextHandle(RemoteCardMessageType type) + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray input("{\n" + " \"msg\": \"%1\"\n" + "}\n"); + const auto& obj = RemoteMessage::parseByteArray(input.replace("%1", QTest::currentDataTag())); + QVERIFY(!obj.isEmpty()); + + RemoteMessage msg(obj); + QCOMPARE(msg.getType(), type); + QCOMPARE(msg.getContextHandle(), QString()); + + if (type == RemoteCardMessageType::IFDEstablishContext) + { + QVERIFY(!msg.isIncomplete()); + QCOMPARE(logSpy.count(), 0); + return; + } + + QVERIFY(msg.isIncomplete()); + + if (type != RemoteCardMessageType::UNDEFINED) + { + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.at(0).at(0).toString().contains(QString("Missing value \"ContextHandle\""))); + return; + } + + QCOMPARE(logSpy.count(), 2); + QVERIFY(logSpy.at(0).at(0).toString().contains(QString("Invalid messageType received: \""))); + QVERIFY(logSpy.at(1).at(0).toString().contains(QString("Missing value \"ContextHandle\""))); + } + + + void checkEmptyContextHandle(RemoteCardMessageType type) + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray input("{\n" + " \"ContextHandle\": \"\",\n" + " \"msg\": \"%1\"\n" + "}\n"); + const auto& obj = RemoteMessage::parseByteArray(input.replace("%1", QTest::currentDataTag())); + QVERIFY(!obj.isEmpty()); + + RemoteMessage msg(obj); + QCOMPARE(msg.getType(), type); + QCOMPARE(msg.getContextHandle(), QString()); + + if (type != RemoteCardMessageType::UNDEFINED) + { + QVERIFY(!msg.isIncomplete()); + QCOMPARE(logSpy.count(), 0); + return; + } + + QVERIFY(msg.isIncomplete()); + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.at(0).at(0).toString().contains(QString("Invalid messageType received: \""))); + } + + + void checkValidContextHandle(RemoteCardMessageType type) + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray input("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"msg\": \"%1\"\n" + "}\n"); + const auto& obj = RemoteMessage::parseByteArray(input.replace("%1", QTest::currentDataTag())); + QVERIFY(!obj.isEmpty()); + + RemoteMessage msg(obj); + QCOMPARE(msg.getType(), type); + + if (type == RemoteCardMessageType::IFDEstablishContext) + { + QCOMPARE(msg.getContextHandle(), QString()); + QVERIFY(!msg.isIncomplete()); + QCOMPARE(logSpy.count(), 0); + return; + } + + QCOMPARE(msg.getContextHandle(), QString("TestContext")); + + if (type != RemoteCardMessageType::UNDEFINED) + { + QVERIFY(!msg.isIncomplete()); + QCOMPARE(logSpy.count(), 0); + return; + } + + QVERIFY(msg.isIncomplete()); + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.at(0).at(0).toString().contains(QString("Invalid messageType received: \""))); + } + + + private Q_SLOTS: + void initTestCase() + { + Env::getSingleton()->init(); + } + + + void invalidJson() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray input("FooBar"); + const auto& obj = RemoteMessage::parseByteArray(input); + QVERIFY(obj.isEmpty()); + + RemoteMessage msg(obj); + QVERIFY(msg.isIncomplete()); + + QCOMPARE(logSpy.count(), 5); + QVERIFY(logSpy.at(0).at(0).toString().contains("Json parsing failed. 1 : \"illegal value\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("Expected object at top level")); + QVERIFY(logSpy.at(2).at(0).toString().contains("Missing value \"msg\"")); + QVERIFY(logSpy.at(3).at(0).toString().contains("Invalid messageType received: \"\"")); + QVERIFY(logSpy.at(4).at(0).toString().contains("Missing value \"ContextHandle\"")); + } + + + void missingObject() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray input("[]"); + const auto& obj = RemoteMessage::parseByteArray(input); + QVERIFY(obj.isEmpty()); + + RemoteMessage msg(obj); + QVERIFY(msg.isIncomplete()); + + QCOMPARE(logSpy.count(), 4); + QVERIFY(logSpy.at(0).at(0).toString().contains("Expected object at top level")); + QVERIFY(logSpy.at(1).at(0).toString().contains("Missing value \"msg\"")); + QVERIFY(logSpy.at(2).at(0).toString().contains("Invalid messageType received: \"\"")); + QVERIFY(logSpy.at(3).at(0).toString().contains("Missing value \"ContextHandle\"")); + } + + + void emptyObject() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray input("{}"); + const auto& obj = RemoteMessage::parseByteArray(input); + QVERIFY(obj.isEmpty()); + + RemoteMessage msg(obj); + QVERIFY(msg.isIncomplete()); + + QCOMPARE(logSpy.count(), 4); + QVERIFY(logSpy.at(0).at(0).toString().contains("Expected object at top level")); + QVERIFY(logSpy.at(1).at(0).toString().contains("Missing value \"msg\"")); + QVERIFY(logSpy.at(2).at(0).toString().contains("Invalid messageType received: \"\"")); + QVERIFY(logSpy.at(3).at(0).toString().contains("Missing value \"ContextHandle\"")); + } + + + void missingMessageType() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray input("{\n" + " \"ContextHandle\": \"TestContext\"\n" + "}\n"); + const auto& obj = RemoteMessage::parseByteArray(input.replace(50, 2, QTest::currentDataTag())); + QVERIFY(!obj.isEmpty()); + + RemoteMessage msg(obj); + QVERIFY(msg.isIncomplete()); + + QCOMPARE(logSpy.count(), 2); + QVERIFY(logSpy.at(0).at(0).toString().contains(QString("Missing value \"msg\""))); + QVERIFY(logSpy.at(1).at(0).toString().contains(QString("Invalid messageType received: \"\""))); + } + + + void messageType_data() + { + QTest::addColumn("type"); + + QTest::newRow("") << RemoteCardMessageType::UNDEFINED; + const auto& typeList = Enum::getList(); + for (const auto& type : typeList) + { + QTest::newRow(getEnumName(type).data()) << type; + } + QTest::newRow("unknown") << RemoteCardMessageType::UNDEFINED; + } + + + void messageType() + { + QFETCH(RemoteCardMessageType, type); + + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray input("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"msg\": \"%1\"\n" + "}\n"); + const auto& obj = RemoteMessage::parseByteArray(input.replace("%1", QTest::currentDataTag())); + QVERIFY(!obj.isEmpty()); + + RemoteMessage msg(obj); + QCOMPARE(msg.getType(), type); + + if (type != RemoteCardMessageType::UNDEFINED) + { + QVERIFY(!msg.isIncomplete()); + QCOMPARE(logSpy.count(), 0); + return; + } + + QVERIFY(msg.isIncomplete()); + QCOMPARE(logSpy.count(), 1); + QVERIFY(logSpy.at(0).at(0).toString().contains(QString("Invalid messageType received: \""))); + } + + + void contextHandle_data() + { + QTest::addColumn("type"); + + QTest::newRow("") << RemoteCardMessageType::UNDEFINED; + const auto& msgTypes = Enum::getList(); + for (const auto& type : msgTypes) + { + QTest::newRow(getEnumName(type).data()) << type; + } + QTest::newRow("unknown") << RemoteCardMessageType::UNDEFINED; + } + + + void contextHandle() + { + QFETCH(RemoteCardMessageType, type); + + checkMissingContextHandle(type); + checkEmptyContextHandle(type); + checkValidContextHandle(type); + } + + +}; + +QTEST_GUILESS_MAIN(test_RemoteMessage) +#include "test_RemoteMessage.moc" diff --git a/test/qt/remote_device/messages/test_RemoteMessageResponse.cpp b/test/qt/remote_device/messages/test_RemoteMessageResponse.cpp new file mode 100644 index 0000000..fba387b --- /dev/null +++ b/test/qt/remote_device/messages/test_RemoteMessageResponse.cpp @@ -0,0 +1,143 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "messages/RemoteMessageResponse.h" + +#include "LogHandler.h" + +#include + + +using namespace governikus; + + +class test_RemoteMessageResponse + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void initTestCase() + { + Env::getSingleton()->init(); + } + + + void invalidJson() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + QByteArray message("FooBar"); + const auto& obj = QJsonDocument::fromJson(message).object(); + QVERIFY(obj.isEmpty()); + + RemoteMessageResponse msg(obj); + QVERIFY(msg.isIncomplete()); + + QCOMPARE(logSpy.count(), 5); + QVERIFY(logSpy.at(0).at(0).toString().contains("Missing value \"msg\"")); + QVERIFY(logSpy.at(1).at(0).toString().contains("Invalid messageType received: \"\"")); + QVERIFY(logSpy.at(2).at(0).toString().contains("Missing value \"ContextHandle\"")); + QVERIFY(logSpy.at(3).at(0).toString().contains("Missing value \"ResultMajor\"")); + QVERIFY(logSpy.at(4).at(0).toString().contains("Missing value \"ResultMinor\"")); + } + + + void checkResult_data() + { + QTest::addColumn("resultMinor"); + QTest::addColumn("hasError"); + + QTest::newRow("empty") << ECardApiResult::Minor::null << false; + QTest::newRow("result") << ECardApiResult::Minor::AL_Unknown_Error << true; + } + + + void checkResult() + { + QFETCH(ECardApiResult::Minor, resultMinor); + QFETCH(bool, hasError); + + const RemoteMessageResponse remoteMessageResponse( + RemoteCardMessageType::IFDEstablishContextResponse, + resultMinor + ); + + QVERIFY(!remoteMessageResponse.isIncomplete()); + QCOMPARE(remoteMessageResponse.getType(), RemoteCardMessageType::IFDEstablishContextResponse); + QCOMPARE(remoteMessageResponse.getContextHandle(), QString()); + QCOMPARE(remoteMessageResponse.resultHasError(), hasError); + const ECardApiResult::Minor minor = (hasError ? ECardApiResult::Minor::AL_Unknown_Error : ECardApiResult::Minor::null); + QCOMPARE(remoteMessageResponse.getResultMinor(), minor); + } + + + void fromJson_data() + { + QTest::addColumn("message"); + QTest::addColumn("isIncomplete"); + QTest::addColumn("hasError"); + QTest::addColumn("resultMinor"); + QTest::addColumn("spyCount"); + + QTest::newRow("withoutError") << QByteArray("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\",\n" + " \"ResultMinor\": null,\n" + " \"msg\": \"IFDEstablishContextResponse\"\n" + "}\n") << false << false << ECardApiResult::Minor::null << 0; + + QTest::newRow("withError") << QByteArray("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#error\",\n" + " \"ResultMinor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultminor/al/common#unknownError\",\n" + " \"msg\": \"IFDEstablishContextResponse\"\n" + "}\n") << false << true << ECardApiResult::Minor::AL_Unknown_Error << 0; + + QTest::newRow("WithoutErrorWithMinor") << QByteArray("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\",\n" + " \"ResultMinor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultminor/al/common#unknownError\",\n" + " \"msg\": \"IFDEstablishContextResponse\"\n" + "}\n") << false << false << ECardApiResult::Minor::null << 0; + + QTest::newRow("WithErrorWithoutMinor") << QByteArray("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#error\",\n" + " \"ResultMinor\": null,\n" + " \"msg\": \"IFDEstablishContextResponse\"\n" + "}\n") << true << true << ECardApiResult::Minor::null << 1; + } + + + void fromJson() + { + QFETCH(QByteArray, message); + QFETCH(bool, isIncomplete); + QFETCH(bool, hasError); + QFETCH(ECardApiResult::Minor, resultMinor); + QFETCH(int, spyCount); + + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const RemoteMessageResponse remoteMessageResponse(obj); + QCOMPARE(remoteMessageResponse.isIncomplete(), isIncomplete); + QCOMPARE(remoteMessageResponse.getType(), RemoteCardMessageType::IFDEstablishContextResponse); + QCOMPARE(remoteMessageResponse.getContextHandle(), QStringLiteral("TestContext")); + QCOMPARE(remoteMessageResponse.resultHasError(), hasError); + QCOMPARE(remoteMessageResponse.getResultMinor(), resultMinor); + + QCOMPARE(logSpy.count(), spyCount); + if (spyCount > 0) + { + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of \"ResultMinor\" should be of type \"string\"")); + } + } + + +}; + +QTEST_GUILESS_MAIN(test_RemoteMessageResponse) +#include "test_RemoteMessageResponse.moc" diff --git a/test/qt/remote_device/test_RemoteClientImpl.cpp b/test/qt/remote_device/test_RemoteClientImpl.cpp index eed195b..d8fce1d 100644 --- a/test/qt/remote_device/test_RemoteClientImpl.cpp +++ b/test/qt/remote_device/test_RemoteClientImpl.cpp @@ -13,7 +13,6 @@ #include #include - using namespace governikus; @@ -35,7 +34,7 @@ class DatagramHandlerMock } - virtual bool send(const QJsonDocument&) override + virtual bool send(const QByteArray&) override { return true; } @@ -57,7 +56,6 @@ class RemoteDeviceListMock RemoteDeviceListMock(int pI1, int pI2) : RemoteDeviceList(pI1, pI2) { - } @@ -71,7 +69,6 @@ class RemoteDeviceListMock virtual void clear() override { - } @@ -79,7 +76,6 @@ class RemoteDeviceListMock RemoteDeviceListMock::~RemoteDeviceListMock() { - } @@ -102,7 +98,7 @@ void RemoteConnectorMock::onConnectRequest(const RemoteDeviceDescriptor&, const } -} +} // namespace governikus class test_RemoteClient : public QObject @@ -117,7 +113,7 @@ class test_RemoteClient private Q_SLOTS: void initTestCase() { - LogHandler::getInstance().init(); + Env::getSingleton()->init(); } @@ -172,74 +168,95 @@ class test_RemoteClient } - void testReceiveIPv6() + void testReceiveIPv6LinkLocal() { - QSignalSpy logSpy(&LogHandler::getInstance(), &LogHandler::fireLog); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) || defined(GOVERNIKUS_QT) + QSKIP("QTBUG-25550 is fixed in the used Qt version."); +#endif - const auto& offerJson = QJsonDocument::fromJson("{\n" - " \"deviceName\": \"Sony Xperia Z5 compact\",\n" - " \"encrypted\": true,\n" - " \"port\": 24728,\n" - " \"availableApiLevels\": [1, 2, 3, 4]\n" - "}"); + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + const QByteArray offerJson("{\n" + " \"IFDName\": \"Sony Xperia Z5 compact\",\n" + " \"IFDID\": \"0123456789ABCDEF\",\n" + " \"encrypted\": true,\n" + " \"msg\": \"REMOTE_IFD\",\n" + " \"port\": 24728,\n" + " \"SupportedAPI\": [\"IFDInterface_WebSocket_v0\", \"IFDInterface_WebSocket_v2\"]\n" + "}"); RemoteClientImpl client; QSignalSpy spyAppeared(&client, &RemoteClient::fireDeviceAppeared); client.startDetection(); - Q_EMIT mDatagramHandlerMock->fireNewMessage(offerJson, QHostAddress("2001:0db8:85a3:08d3:1319:8a2e:0370:7344")); + Q_EMIT mDatagramHandlerMock->fireNewMessage(offerJson, QHostAddress("fe80::5a38:a519:8ff4:1f1f%enp0s31f6")); QCOMPARE(logSpy.count(), 1); - QVERIFY(logSpy.at(0).at(0).toString().contains("IPv6 not supported")); + QVERIFY(logSpy.at(0).at(0).toString().contains("Dropping message from unparsable address:")); QVERIFY(spyAppeared.isEmpty()); } void testReceiveUnparsable() { - QSignalSpy logSpy(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); - const auto& unparsableJson = QJsonDocument::fromJson("{\n" - " \"device___Name\": \"Sony Xperia Z5 compact\",\n" - " \"encrypted\": true,\n" - " \"port\": 24728,\n" - " \"availableApiLevels\": [1, 2, 3, 4]\n" - "}"); + const QByteArray unparsableJson("{\n" + " \"device___Name\": \"Sony Xperia Z5 compact\",\n" + " \"encrypted\": true,\n" + " \"port\": 24728,\n" + " \"availableApiLevels\": [1, 2, 3, 4]\n" + "}"); RemoteClientImpl client; client.startDetection(); QVERIFY(!mDatagramHandlerMock.isNull()); Q_EMIT mDatagramHandlerMock->fireNewMessage(unparsableJson, QHostAddress("192.168.1.88")); - QCOMPARE(logSpy.count(), 2); - QVERIFY(logSpy.at(1).at(0).toString().contains("Discarding unparsable message")); + QCOMPARE(logSpy.count(), 6); + QVERIFY(logSpy.at(5).at(0).toString().contains("Discarding unparsable message")); } void testReceiveUnexpected() { - QSignalSpy logSpy(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); RemoteClientImpl client; client.startDetection(); QVERIFY(!mDatagramHandlerMock.isNull()); - Q_EMIT mDatagramHandlerMock->fireNewMessage(IfdEstablishContext(QStringLiteral("IFDInterface_WebSocket_v0"), DeviceInfo::getName()).toJson(QStringLiteral("TestContext")), QHostAddress("192.168.1.88")); - QCOMPARE(logSpy.count(), 2); - QVERIFY(logSpy.at(0).at(0).toString().contains("The value of \"IFDName\" should be of type string")); - QVERIFY(logSpy.at(1).at(0).toString().contains("Discarding unparsable message")); + const auto& json = IfdEstablishContext(QStringLiteral("IFDInterface_WebSocket_v0"), DeviceInfo::getName()).toByteArray(QStringLiteral("TestContext")); + Q_EMIT mDatagramHandlerMock->fireNewMessage(json, QHostAddress("192.168.1.88")); + QCOMPARE(logSpy.count(), 6); + QVERIFY(logSpy.at(0).at(0).toString().contains("The value of msg should be REMOTE_IFD")); + QVERIFY(logSpy.at(5).at(0).toString().contains("Discarding unparsable message")); + } + + + void testReceive_data() + { + QTest::addColumn("hostAddress"); + + QTest::newRow("IPv4") << "192.168.1.88"; + QTest::newRow("IPv6 link-global") << "2001:0db8:85a3:08d3:1319:8a2e:0370:7344"; +#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) || defined(GOVERNIKUS_QT) + QTest::newRow("IPv6 link-local") << "fe80::5a38:a519:8ff4:1f1f%enp0s31f6"; +#endif } void testReceive() { - const auto& offerJson = QJsonDocument::fromJson("{\n" - " \"IFDName\": \"Sony Xperia Z5 compact\",\n" - " \"IFDID\": \"0123456789ABCDEF\",\n" - " \"encrypted\": true,\n" - " \"msg\": \"REMOTE_IFD\",\n" - " \"port\": 24728,\n" - " \"SupportedAPI\": [\"IFDInterface_WebSocket_v0\", \"IFDInterface_WebSocket_v2\"]\n" - "}"); + QFETCH(QString, hostAddress); + + const QByteArray offerJson("{\n" + " \"IFDName\": \"Sony Xperia Z5 compact\",\n" + " \"IFDID\": \"0123456789ABCDEF\",\n" + " \"encrypted\": true,\n" + " \"msg\": \"REMOTE_IFD\",\n" + " \"port\": 24728,\n" + " \"SupportedAPI\": [\"IFDInterface_WebSocket_v0\", \"IFDInterface_WebSocket_v2\"]\n" + "}"); RemoteClientImpl client; client.startDetection(); @@ -249,7 +266,7 @@ class test_RemoteClient QSignalSpy spyAppearedList(mRemoteDeviceListMock.data(), &RemoteDeviceListMock::fireDeviceAppeared); QSignalSpy spyAppeared(&client, &RemoteClient::fireDeviceAppeared); - Q_EMIT mDatagramHandlerMock->fireNewMessage(offerJson, QHostAddress("192.168.1.88")); + Q_EMIT mDatagramHandlerMock->fireNewMessage(offerJson, QHostAddress(hostAddress)); QCOMPARE(spyAppearedList.count(), 1); QCOMPARE(spyAppeared.count(), 1); @@ -274,12 +291,12 @@ class test_RemoteClient void testRemoteConnectorEstablish() { - const auto& offerJson = QJsonDocument::fromJson("{\n" - " \"deviceName\": \"Sony Xperia Z5 compact\",\n" - " \"encrypted\": true,\n" - " \"port\": 24728,\n" - " \"availableApiLevels\": [\"IFDInterface_WebSocket_v0\", \"IFDInterface_WebSocket_v2\"]\n" - "}"); + const QByteArray offerJson("{\n" + " \"deviceName\": \"Sony Xperia Z5 compact\",\n" + " \"encrypted\": true,\n" + " \"port\": 24728,\n" + " \"availableApiLevels\": [\"IFDInterface_WebSocket_v0\", \"IFDInterface_WebSocket_v2\"]\n" + "}"); RemoteClientImpl client; client.startDetection(); @@ -288,13 +305,12 @@ class test_RemoteClient QVERIFY(!mRemoteConnectorMock.isNull()); QSignalSpy spyConnectionRequest(mRemoteConnectorMock.data(), &RemoteConnectorMock::fireConnectionRequestReceived); - const QSharedPointer msg(new Discovery("", QStringLiteral("0123456789ABCDEF"), 12345, {IfdVersion::Version::v0})); - const RemoteDeviceDescriptor descr(msg, QHostAddress("192.168.1.88")); + const Discovery discovery("", QStringLiteral("0123456789ABCDEF"), 12345, {IfdVersion::Version::v0, IfdVersion::Version::v_test}); + const RemoteDeviceDescriptor descr(discovery, QHostAddress("192.168.1.88")); QSharedPointer emptyEntry(new RemoteDeviceListEntry(descr)); client.establishConnection(emptyEntry, QString("password1")); - spyConnectionRequest.wait(); - QCOMPARE(spyConnectionRequest.count(), 1); + QTRY_COMPARE(spyConnectionRequest.count(), 1); QSignalSpy spyConnectionDone(&client, &RemoteClient::fireEstablishConnectionDone); Q_EMIT mRemoteConnectorMock->fireRemoteDispatcherError(emptyEntry->getRemoteDeviceDescriptor(), RemoteErrorCode::CONNECTION_ERROR); diff --git a/test/qt/remote_device/test_RemoteConnector.cpp b/test/qt/remote_device/test_RemoteConnector.cpp index f4b557a..3f77404 100644 --- a/test/qt/remote_device/test_RemoteConnector.cpp +++ b/test/qt/remote_device/test_RemoteConnector.cpp @@ -10,8 +10,6 @@ #include "Env.h" #include "KeyPair.h" #include "messages/Discovery.h" -#include "MockDataChannel.h" -#include "RemoteHelper.h" #include "RemoteWebSocketServer.h" #include "SecureStorage.h" @@ -34,30 +32,15 @@ class test_RemoteConnector Q_OBJECT private: - static const int cConnectTimeoutMs = 500; - static const int cSignalTimeoutMs = 10 * cConnectTimeoutMs; - - void waitForSignals(QSignalSpy* const pSpy, const int pExpectedCount, const int pTimeoutMs) - { - for (int tryCount = 0; pSpy->count() < pExpectedCount && tryCount < pExpectedCount; ++tryCount) - { - pSpy->wait(pTimeoutMs); - } - QCOMPARE(pSpy->count(), pExpectedCount); - } - - void sendRequest(const QSharedPointer& pConnector, const QHostAddress& pHostAddress, - const QSharedPointer& pDiscovery, + const Discovery& pDiscovery, const QString& pPassword) { const RemoteDeviceDescriptor descr(pDiscovery, pHostAddress); - QMetaObject::invokeMethod(pConnector.data(), - "onConnectRequest", - Qt::QueuedConnection, - Q_ARG(RemoteDeviceDescriptor, descr), - Q_ARG(QString, pPassword)); + QMetaObject::invokeMethod(pConnector.data(), [ = ] { + pConnector->onConnectRequest(descr, pPassword); + }, Qt::QueuedConnection); } @@ -76,8 +59,8 @@ class test_RemoteConnector QVERIFY(!descr.isNull()); const QVariant dispatcherVariant = arguments.at(1); - QVERIFY(dispatcherVariant.canConvert >()); - const QSharedPointer dispatcher = dispatcherVariant.value >(); + QVERIFY(dispatcherVariant.canConvert >()); + const QSharedPointer dispatcher = dispatcherVariant.value >(); QVERIFY(dispatcher); const QUrl remoteUrl = descr.getUrl(); @@ -138,7 +121,6 @@ class test_RemoteConnector } } QVERIFY(signalFound); - } @@ -153,7 +135,7 @@ class test_RemoteConnector { QThread clientThread; - const QSharedPointer connector(new RemoteConnectorImpl(cConnectTimeoutMs)); + const QSharedPointer connector(new RemoteConnectorImpl()); QSignalSpy spyError(connector.data(), &RemoteConnector::fireRemoteDispatcherError); QSignalSpy spySuccess(connector.data(), &RemoteConnector::fireRemoteDispatcherCreated); @@ -162,9 +144,9 @@ class test_RemoteConnector // No device name. const QHostAddress hostAddress(QHostAddress::LocalHost); - const QSharedPointer msg(new Discovery(QString(), QStringLiteral("0123456789ABCDEF"), 2020, {IfdVersion::Version::v0})); - sendRequest(connector, hostAddress, msg, QString()); - waitForSignals(&spyError, 1, cSignalTimeoutMs); + const Discovery discoveryMsg(QString(), QStringLiteral("0123456789ABCDEF"), 2020, {IfdVersion::Version::v0}); + sendRequest(connector, hostAddress, discoveryMsg, QString()); + QTRY_COMPARE(spyError.count(), 1); clientThread.exit(); QVERIFY(clientThread.wait()); @@ -178,7 +160,7 @@ class test_RemoteConnector { QThread clientThread; - const QSharedPointer connector(new RemoteConnectorImpl(cConnectTimeoutMs)); + const QSharedPointer connector(new RemoteConnectorImpl()); QSignalSpy spyError(connector.data(), &RemoteConnector::fireRemoteDispatcherError); QSignalSpy spySuccess(connector.data(), &RemoteConnector::fireRemoteDispatcherCreated); @@ -187,8 +169,8 @@ class test_RemoteConnector // Device information is null. const QHostAddress hostAddress(QHostAddress::LocalHost); - sendRequest(connector, hostAddress, QSharedPointer(), QStringLiteral("secret")); - waitForSignals(&spyError, 1, cSignalTimeoutMs); + sendRequest(connector, hostAddress, Discovery(QJsonObject()), QStringLiteral("secret")); + QTRY_COMPARE(spyError.count(), 1); clientThread.exit(); QVERIFY(clientThread.wait()); @@ -204,7 +186,7 @@ class test_RemoteConnector server->listen("dummy"); QThread clientThread; - const QSharedPointer connector(new RemoteConnectorImpl(cConnectTimeoutMs)); + const QSharedPointer connector(new RemoteConnectorImpl()); QSignalSpy spyError(connector.data(), &RemoteConnector::fireRemoteDispatcherError); QSignalSpy spySuccess(connector.data(), &RemoteConnector::fireRemoteDispatcherCreated); @@ -213,9 +195,9 @@ class test_RemoteConnector // Password is empty. const QHostAddress hostAddress(QHostAddress::LocalHost); - const QSharedPointer msg(new Discovery(QStringLiteral("Smartphone1"), QStringLiteral("0123456789ABCDEF"), server->getServerPort(), {IfdVersion::Version::v0})); - sendRequest(connector, hostAddress, msg, QString()); - waitForSignals(&spyError, 1, cSignalTimeoutMs); + const Discovery discoveryMsg(QStringLiteral("Smartphone1"), QStringLiteral("0123456789ABCDEF"), server->getServerPort(), {IfdVersion::Version::v0}); + sendRequest(connector, hostAddress, discoveryMsg, QString()); + QTRY_COMPARE(spyError.count(), 1); clientThread.exit(); QVERIFY(clientThread.wait()); @@ -229,7 +211,7 @@ class test_RemoteConnector { QThread clientThread; - const QSharedPointer connector(new RemoteConnectorImpl(cConnectTimeoutMs)); + const QSharedPointer connector(new RemoteConnectorImpl()); QSignalSpy spyError(connector.data(), &RemoteConnector::fireRemoteDispatcherError); QSignalSpy spySuccess(connector.data(), &RemoteConnector::fireRemoteDispatcherCreated); @@ -238,9 +220,9 @@ class test_RemoteConnector // Currently, only API level 1 is supported. const QHostAddress hostAddress(QHostAddress::LocalHost); - const QSharedPointer msg(new Discovery(QStringLiteral("Smartphone1"), QStringLiteral("0123456789ABCDEF"), 2020, {IfdVersion::Version::Unknown})); - sendRequest(connector, hostAddress, msg, QStringLiteral("secret")); - waitForSignals(&spyError, 1, cSignalTimeoutMs); + const Discovery discoveryMsg(QStringLiteral("Smartphone1"), QStringLiteral("0123456789ABCDEF"), 2020, {IfdVersion::Version::v_test}); + sendRequest(connector, hostAddress, discoveryMsg, QStringLiteral("secret")); + QTRY_COMPARE(spyError.count(), 1); clientThread.exit(); QVERIFY(clientThread.wait()); @@ -254,7 +236,7 @@ class test_RemoteConnector { QThread clientThread; - const QSharedPointer connector(new RemoteConnectorImpl(cConnectTimeoutMs)); + const QSharedPointer connector(new RemoteConnectorImpl(2500)); QSignalSpy spyError(connector.data(), &RemoteConnector::fireRemoteDispatcherError); QSignalSpy spySuccess(connector.data(), &RemoteConnector::fireRemoteDispatcherCreated); @@ -263,9 +245,9 @@ class test_RemoteConnector // Correct request but no server is running. const QHostAddress hostAddress(QHostAddress::LocalHost); - const QSharedPointer msg(new Discovery(QStringLiteral("Smartphone1"), QStringLiteral("0123456789ABCDEF"), 2020, {IfdVersion::Version::v0})); - sendRequest(connector, hostAddress, msg, QString("dummy")); - waitForSignals(&spyError, 1, cSignalTimeoutMs); + const Discovery discoveryMsg(QStringLiteral("Smartphone1"), QStringLiteral("0123456789ABCDEF"), 2020, {IfdVersion::Version::v0}); + sendRequest(connector, hostAddress, discoveryMsg, QString("dummy")); + QTRY_COMPARE(spyError.count(), 1); clientThread.exit(); QVERIFY(clientThread.wait()); @@ -287,7 +269,8 @@ class test_RemoteConnector QTest::addColumn >("trustedCertificates"); auto& settings = Env::getSingleton()->getRemoteServiceSettings(); - QVERIFY(RemoteHelper::checkAndGenerateKey()); + QVERIFY(settings.checkAndGenerateKey()); + const KeyPair pair = KeyPair::generate(); QVERIFY(pair.isValid()); @@ -334,7 +317,7 @@ class test_RemoteConnector // Execute test in internal block so that all relevant smart pointers are released before stopping the client thread. QSharedPointer remoteDispatcherDestructionSpy; { - const QSharedPointer connector(new RemoteConnectorImpl(cConnectTimeoutMs)); + const QSharedPointer connector(new RemoteConnectorImpl()); QSignalSpy spyConnectorError(connector.data(), &RemoteConnector::fireRemoteDispatcherError); QSignalSpy spyConnectorSuccess(connector.data(), &RemoteConnector::fireRemoteDispatcherCreated); @@ -343,23 +326,23 @@ class test_RemoteConnector // Send valid encrypted connect request. const QHostAddress hostAddress(QHostAddress::LocalHost); - const QSharedPointer msg(new Discovery(QStringLiteral("Smartphone1"), QStringLiteral("0123456789ABCDEF"), serverPort, {IfdVersion::Version::v0})); - sendRequest(connector, hostAddress, msg, psk); + const Discovery discoveryMsg(QStringLiteral("Smartphone1"), QStringLiteral("0123456789ABCDEF"), serverPort, {IfdVersion::Version::v0}); + sendRequest(connector, hostAddress, discoveryMsg, psk); - waitForSignals(&spyConnectorSuccess, 1, cSignalTimeoutMs); + QTRY_COMPARE(spyConnectorSuccess.count(), 1); QCOMPARE(spyConnectorError.count(), 0); verifySuccessSignal(spyConnectorSuccess, serverPort); const QVariant dispatcherVariant = spyConnectorSuccess.first().at(1); - QVERIFY(dispatcherVariant.canConvert >()); - const QSharedPointer dispatcher = dispatcherVariant.value >(); + QVERIFY(dispatcherVariant.canConvert >()); + const QSharedPointer dispatcher = dispatcherVariant.value >(); remoteDispatcherDestructionSpy.reset(new QSignalSpy(dispatcher.data(), &QObject::destroyed)); } QCOMPARE(spySocketError.count(), 0); QCOMPARE(spySocketSuccess.count(), 1); - waitForSignals(remoteDispatcherDestructionSpy.data(), 1, cSignalTimeoutMs); + QTRY_COMPARE(remoteDispatcherDestructionSpy->count(), 1); QCOMPARE(remoteDispatcherDestructionSpy->count(), 1); clientThread.exit(); @@ -396,7 +379,7 @@ class test_RemoteConnector // Set up client thread. QThread clientThread; - const QSharedPointer connector(new RemoteConnectorImpl(cConnectTimeoutMs)); + const QSharedPointer connector(new RemoteConnectorImpl()); QSignalSpy spyConnectorError(connector.data(), &RemoteConnector::fireRemoteDispatcherError); QSignalSpy spyConnectorSuccess(connector.data(), &RemoteConnector::fireRemoteDispatcherCreated); @@ -405,10 +388,10 @@ class test_RemoteConnector // Send encrypted connect request with wrong psk. const QHostAddress hostAddress(QHostAddress::LocalHost); - const QSharedPointer msg(new Discovery(QStringLiteral("Smartphone1"), QStringLiteral("0123456789ABCDEF"), serverPort, {IfdVersion::Version::v0})); - sendRequest(connector, hostAddress, msg, QStringLiteral("sekret")); + const Discovery discoveryMsg(QStringLiteral("Smartphone1"), QStringLiteral("0123456789ABCDEF"), serverPort, {IfdVersion::Version::v0}); + sendRequest(connector, hostAddress, discoveryMsg, QStringLiteral("sekret")); - waitForSignals(&spyConnectorError, 1, cSignalTimeoutMs); + QTRY_COMPARE(spyConnectorError.count(), 1); QCOMPARE(spyConnectorSuccess.count(), 0); verifyErrorSignal(spyConnectorError, {RemoteErrorCode::REMOTE_HOST_REFUSED_CONNECTION}, serverPort, QStringLiteral("Smartphone1")); diff --git a/test/qt/remote_device/test_RemoteDeviceDescriptor.cpp b/test/qt/remote_device/test_RemoteDeviceDescriptor.cpp index 5a1e427..c5aed49 100644 --- a/test/qt/remote_device/test_RemoteDeviceDescriptor.cpp +++ b/test/qt/remote_device/test_RemoteDeviceDescriptor.cpp @@ -29,7 +29,7 @@ class test_RemoteDeviceDescriptor void testValidDescriptorIsEqualToItself() { - const QSharedPointer validMsg(new Discovery(QStringLiteral("Device"), QStringLiteral("0123456789ABCDEF"), mPort, {IfdVersion::Version::v0})); + const Discovery validMsg(QStringLiteral("Device"), QStringLiteral("0123456789ABCDEF"), mPort, {IfdVersion::Version::v0}); const QHostAddress address(QHostAddress::LocalHost); const RemoteDeviceDescriptor valid(validMsg, address); @@ -42,8 +42,8 @@ class test_RemoteDeviceDescriptor const QHostAddress address1(QStringLiteral("192.168.1.1")); const QHostAddress address2(QHostAddress::LocalHost); - const RemoteDeviceDescriptor invalid1(QSharedPointer(), address1); - const RemoteDeviceDescriptor invalid2(QSharedPointer(), address2); + const RemoteDeviceDescriptor invalid1(Discovery(QJsonObject()), address1); + const RemoteDeviceDescriptor invalid2(Discovery(QJsonObject()), address2); QVERIFY(invalid1 == invalid2); } @@ -51,8 +51,8 @@ class test_RemoteDeviceDescriptor void testValidDescriptorIsDifferentFromInvalid() { - const QSharedPointer validMsg(new Discovery(QStringLiteral("Device"), QStringLiteral("0123456789ABCDEF"), mPort, {IfdVersion::Version::v0})); - const QSharedPointer invalidMsg; + const Discovery validMsg(QStringLiteral("Device"), QStringLiteral("0123456789ABCDEF"), mPort, {IfdVersion::Version::v0}); + const Discovery invalidMsg("", "", 0, {}); const QHostAddress address(QHostAddress::LocalHost); const RemoteDeviceDescriptor valid(validMsg, address); @@ -64,8 +64,8 @@ class test_RemoteDeviceDescriptor void testDistinctValidDescriptorsWithDifferentDataAreDifferent() { - const QSharedPointer validMsg1(new Discovery(QStringLiteral("Device"), QStringLiteral("0123456789ABCDEF"), mPort, {IfdVersion::Version::v0})); - const QSharedPointer validMsg2(new Discovery(QStringLiteral("Device"), QStringLiteral("0123456789ABCDEF"), mPort, {IfdVersion::Version::v0, IfdVersion::Version::Unknown})); + const Discovery validMsg1(QStringLiteral("Device"), QStringLiteral("0123456789ABCDEF"), mPort, {IfdVersion::Version::v0}); + const Discovery validMsg2(QStringLiteral("Device"), QStringLiteral("0123456789ABCDEF"), mPort, {IfdVersion::Version::v0, IfdVersion::Version::v_test}); const QHostAddress address(QHostAddress::LocalHost); const RemoteDeviceDescriptor valid1(validMsg1, address); @@ -77,8 +77,8 @@ class test_RemoteDeviceDescriptor void testDistinctValidDescriptorsWithTheSameDataAreEqual() { - const QSharedPointer validMsg1(new Discovery(QStringLiteral("Device"), QStringLiteral("0123456789ABCDEF"), mPort, {IfdVersion::Version::v0})); - const QSharedPointer validMsg2(new Discovery(QStringLiteral("Device"), QStringLiteral("0123456789ABCDEF"), mPort, {IfdVersion::Version::v0})); + const Discovery validMsg1(QStringLiteral("Device"), QStringLiteral("0123456789ABCDEF"), mPort, {IfdVersion::Version::v0}); + const Discovery validMsg2(QStringLiteral("Device"), QStringLiteral("0123456789ABCDEF"), mPort, {IfdVersion::Version::v0}); const QHostAddress address(QHostAddress::LocalHost); const RemoteDeviceDescriptor valid1(validMsg1, address); diff --git a/test/qt/remote_device/test_RemoteDeviceListImpl.cpp b/test/qt/remote_device/test_RemoteDeviceListImpl.cpp index f4b7c31..710902a 100644 --- a/test/qt/remote_device/test_RemoteDeviceListImpl.cpp +++ b/test/qt/remote_device/test_RemoteDeviceListImpl.cpp @@ -8,7 +8,6 @@ #include - using namespace governikus; @@ -23,11 +22,12 @@ class test_RemoteDeviceListImpl RemoteDeviceListImpl deviceList(10, 1); QSignalSpy spyVanished(&deviceList, &RemoteDeviceListImpl::fireDeviceVanished); - QSharedPointer offerMsg1; - QHostAddress addr1; + const Discovery offerMsg1 = Discovery("Dev1", QStringLiteral("0123456789ABCDEF"), 1234, {IfdVersion::Version::v0}); + const Discovery offerMsg2 = Discovery("Dev1", QStringLiteral("0123456789ABCDFF"), 1234, {IfdVersion::Version::v0}); + const QHostAddress addr1 = QHostAddress(QString("5.6.7.8")); + const QHostAddress addr2 = QHostAddress(QString("5.6.7.9")); + { - offerMsg1 = QSharedPointer(new Discovery("Dev1", QStringLiteral("0123456789ABCDEF"), 1234, {IfdVersion::Version::v0})); - addr1 = QHostAddress(QString("5.6.7.8")); const RemoteDeviceDescriptor descr(offerMsg1, addr1); QSignalSpy spy(&deviceList, &RemoteDeviceListImpl::fireDeviceAppeared); @@ -35,11 +35,15 @@ class test_RemoteDeviceListImpl QCOMPARE(spy.count(), 1); } - QSharedPointer offerMsg2; - QHostAddress addr2; { - offerMsg2 = QSharedPointer(new Discovery("Dev1", QStringLiteral("0123456789ABCDEF"), 1234, {IfdVersion::Version::v0})); - addr2 = QHostAddress(QString("5.6.7.8")); + const RemoteDeviceDescriptor descr(offerMsg1, addr1); + + QSignalSpy spy(&deviceList, &RemoteDeviceListImpl::fireDeviceAppeared); + deviceList.update(descr); + QCOMPARE(spy.count(), 0); + } + + { const RemoteDeviceDescriptor descr(offerMsg1, addr2); QSignalSpy spy(&deviceList, &RemoteDeviceListImpl::fireDeviceAppeared); @@ -48,17 +52,22 @@ class test_RemoteDeviceListImpl } { - offerMsg2 = QSharedPointer(new Discovery("Dev1", QStringLiteral("0123456789ABCDEF"), 1234, {IfdVersion::Version::v0})); - addr2 = QHostAddress(QString("5.6.7.9")); - const RemoteDeviceDescriptor descr(offerMsg1, addr2); + const RemoteDeviceDescriptor descr(offerMsg2, addr2); QSignalSpy spy(&deviceList, &RemoteDeviceListImpl::fireDeviceAppeared); deviceList.update(descr); QCOMPARE(spy.count(), 1); } - spyVanished.wait(); - QCOMPARE(spyVanished.count(), 2); + { + const RemoteDeviceDescriptor descr(offerMsg2, addr1); + + QSignalSpy spy(&deviceList, &RemoteDeviceListImpl::fireDeviceAppeared); + deviceList.update(descr); + QCOMPARE(spy.count(), 0); + } + + QTRY_COMPARE(spyVanished.count(), 2); } diff --git a/test/qt/remote_device/test_RemoteDeviceModel.cpp b/test/qt/remote_device/test_RemoteDeviceModel.cpp new file mode 100644 index 0000000..441af21 --- /dev/null +++ b/test/qt/remote_device/test_RemoteDeviceModel.cpp @@ -0,0 +1,183 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "RemoteDeviceModel.h" + +#include + + +using namespace governikus; + + +class test_RemoteDeviceModel + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void test_Paired() + { + const QString name = QStringLiteral("name"); + RemoteDeviceModelEntry entry(name); + QVERIFY(!entry.isPaired()); + entry.setPaired(true); + QVERIFY(entry.isPaired()); + } + + + void test_DeviceName() + { + const QString name = QStringLiteral("name"); + RemoteDeviceModelEntry entry(name); + QCOMPARE(entry.getDeviceName(), name); + } + + + void test_Id() + { + const QString name = QStringLiteral("name"); + const QString id = QStringLiteral("id"); + RemoteDeviceModelEntry entry(name); + + QCOMPARE(entry.getId(), QString()); + + entry.setId(id); + QCOMPARE(entry.getId(), id); + } + + + void test_NetworkVisible() + { + const QString name = QStringLiteral("name"); + RemoteDeviceModelEntry entry(name); + + QVERIFY(!entry.isNetworkVisible()); + + entry.setNetworkVisible(true); + QVERIFY(entry.isNetworkVisible()); + } + + + void test_Supported() + { + const QString name = QStringLiteral("name"); + const QString id = QStringLiteral("id"); + const QDateTime time(QDateTime::currentDateTime()); + RemoteDeviceModelEntry entry1(name); + RemoteDeviceModelEntry entry2(name, id, true, true, true, time); + + QVERIFY(!entry1.isSupported()); + QVERIFY(entry2.isSupported()); + } + + + void test_RemoteDeviceListEntry() + { + const QString name = QStringLiteral("name"); + + RemoteDeviceModelEntry entry1(name); + QCOMPARE(entry1.getRemoteDeviceListEntry(), nullptr); + + const RemoteDeviceDescriptor descriptor = RemoteDeviceDescriptor(); + QSharedPointer pointer(new RemoteDeviceListEntry(descriptor)); + const QString id = QStringLiteral("id"); + RemoteDeviceModelEntry entry2(name, id, pointer); + QCOMPARE(entry2.getRemoteDeviceListEntry(), pointer); + } + + + void test_LastConnected() + { + const QString name = QStringLiteral("name"); + RemoteDeviceModelEntry entry(name); + QDateTime time(QDateTime::currentDateTime()); + + QCOMPARE(entry.getLastConnected(), QDateTime()); + + entry.setLastConnected(time); + QCOMPARE(entry.getLastConnected(), time); + } + + + void test_RoleNames() + { + RemoteDeviceModel model; + QVERIFY(model.roleNames().keys().contains(RemoteDeviceModel::SettingsRemoteRoles::REMOTE_DEVICE_NAME)); + QVERIFY(model.roleNames().keys().contains(RemoteDeviceModel::SettingsRemoteRoles::LAST_CONNECTED)); + QVERIFY(model.roleNames().keys().contains(RemoteDeviceModel::SettingsRemoteRoles::DEVICE_ID)); + QVERIFY(model.roleNames().keys().contains(RemoteDeviceModel::SettingsRemoteRoles::IS_NETWORK_VISIBLE)); + QVERIFY(model.roleNames().keys().contains(RemoteDeviceModel::SettingsRemoteRoles::IS_SUPPORTED)); + + QVERIFY(model.roleNames().values().contains(QByteArrayLiteral("remoteDeviceName"))); + QVERIFY(model.roleNames().values().contains(QByteArrayLiteral("lastConnected"))); + QVERIFY(model.roleNames().values().contains(QByteArrayLiteral("deviceId"))); + QVERIFY(model.roleNames().values().contains(QByteArrayLiteral("isNetworkVisible"))); + QVERIFY(model.roleNames().values().contains(QByteArrayLiteral("isSupported"))); + } + + + void test_GetStatus() + { + RemoteDeviceModelEntry entry; + + RemoteDeviceModel model; + + QCOMPARE(model.getStatus(entry), QString("Not connected")); + + model.mAllRemoteReaders.insert(0, entry); + + QCOMPARE(model.getStatus(entry), QString("Unsupported version")); + + entry.mSupported = true; + QCOMPARE(model.getStatus(entry), QString("Not paired")); + + entry.setPaired(true); + QCOMPARE(model.getStatus(entry), QString("Paired, but unavailable")); + + entry.setNetworkVisible(true); + QCOMPARE(model.getStatus(entry), QString("Paired and available")); + + entry.mSupported = false; + QCOMPARE(model.getStatus(entry), QString("Paired, but unsupported")); + } + + + void test_HeaderData() + { + RemoteDeviceModel model; + + QCOMPARE(model.headerData(3, Qt::Vertical, 3), QVariant()); + QCOMPARE(model.headerData(0, Qt::Horizontal, 3), QVariant()); + QCOMPARE(model.headerData(0, Qt::Horizontal, 0), QVariant("Device")); + QCOMPARE(model.headerData(1, Qt::Horizontal, 0), QVariant("Status")); + } + + + void test_ColumnCount() + { + RemoteDeviceModel model; + QCOMPARE(model.columnCount(), 2); + } + + + void test_GetRemoteListEntry() + { + RemoteDeviceModel model; + RemoteDeviceModelEntry entry1; + RemoteDeviceModelEntry entry2; + entry1.setId(QString("id")); + + QCOMPARE(model.getRemoteDeviceListEntry(QString("id")), QSharedPointer()); + + model.mAllRemoteReaders.insert(0, entry1); + model.mAllRemoteReaders.insert(1, entry2); + + QCOMPARE(model.getRemoteDeviceListEntry(QString("id")), nullptr); + } + + +}; + +QTEST_GUILESS_MAIN(test_RemoteDeviceModel) +#include "test_RemoteDeviceModel.moc" diff --git a/test/qt/remote_device/test_RemoteDisp.cpp b/test/qt/remote_device/test_RemoteDisp.cpp new file mode 100644 index 0000000..9a37655 --- /dev/null +++ b/test/qt/remote_device/test_RemoteDisp.cpp @@ -0,0 +1,342 @@ +/*! + * \brief Unit tests for \ref RemoteDispatcher + * + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "RemoteDispatcherClient.h" +#include "RemoteDispatcherServer.h" + +#include "messages/IfdConnect.h" +#include "messages/IfdDisconnect.h" +#include "messages/IfdEstablishContext.h" +#include "messages/IfdEstablishContextResponse.h" +#include "messages/IfdTransmit.h" +#include "MockDataChannel.h" + +#include +#include + + +using namespace governikus; + + +class RemoteDispatcherSpy + : public QObject +{ + Q_OBJECT + + private: + const QSharedPointer mRemoteDispatcher; + bool mClosed; + GlobalStatus::Code mCloseCode; + QVector mReceivedMessageTypes; + QVector mReceivedMessages; + QVector mReceivedSignalSenders; + + public: + RemoteDispatcherSpy(const QSharedPointer pRemoteDispatcher); + virtual ~RemoteDispatcherSpy(); + + bool isClosed() const; + GlobalStatus::Code getCloseCode() const; + const QVector& getReceivedMessageTypes() const; + const QVector& getReceivedMessages() const; + const QVector& getReceivedSignalSenders() const; + + private Q_SLOTS: + void onClosed(GlobalStatus::Code pCloseCode, const QString& pId); + void onReceived(RemoteCardMessageType pMessageType, const QJsonObject& pJsonObject, const QString& pId); +}; + + +RemoteDispatcherSpy::RemoteDispatcherSpy(const QSharedPointer pRemoteDispatcher) + : mRemoteDispatcher(pRemoteDispatcher) + , mClosed(false) + , mCloseCode(GlobalStatus::Code::RemoteReader_CloseCode_Undefined) +{ + const auto client = mRemoteDispatcher.objectCast(); + if (client) + { + connect(client.data(), &RemoteDispatcherClient::fireClosed, this, &RemoteDispatcherSpy::onClosed); + connect(client.data(), &RemoteDispatcherClient::fireReceived, this, &RemoteDispatcherSpy::onReceived); + return; + } + + const auto server = mRemoteDispatcher.objectCast(); + if (server) + { + connect(server.data(), &RemoteDispatcherServer::fireClosed, this, &RemoteDispatcherSpy::onClosed); + connect(server.data(), &RemoteDispatcherServer::fireReceived, this, &RemoteDispatcherSpy::onReceived); + return; + } +} + + +RemoteDispatcherSpy::~RemoteDispatcherSpy() +{ + const auto client = mRemoteDispatcher.objectCast(); + if (client) + { + disconnect(client.data(), &RemoteDispatcherClient::fireClosed, this, &RemoteDispatcherSpy::onClosed); + disconnect(client.data(), &RemoteDispatcherClient::fireReceived, this, &RemoteDispatcherSpy::onReceived); + return; + } + + const auto server = mRemoteDispatcher.objectCast(); + if (server) + { + disconnect(server.data(), &RemoteDispatcherServer::fireClosed, this, &RemoteDispatcherSpy::onClosed); + disconnect(server.data(), &RemoteDispatcherServer::fireReceived, this, &RemoteDispatcherSpy::onReceived); + return; + } +} + + +bool RemoteDispatcherSpy::isClosed() const +{ + return mClosed; +} + + +GlobalStatus::Code RemoteDispatcherSpy::getCloseCode() const +{ + return mCloseCode; +} + + +const QVector& RemoteDispatcherSpy::getReceivedMessageTypes() const +{ + return mReceivedMessageTypes; +} + + +const QVector& RemoteDispatcherSpy::getReceivedMessages() const +{ + return mReceivedMessages; +} + + +const QVector& RemoteDispatcherSpy::getReceivedSignalSenders() const +{ + return mReceivedSignalSenders; +} + + +void RemoteDispatcherSpy::onClosed(GlobalStatus::Code pCloseCode, const QString& pId) +{ + mClosed = true; + mCloseCode = pCloseCode; + mReceivedSignalSenders += pId; +} + + +void RemoteDispatcherSpy::onReceived(RemoteCardMessageType pMessageType, const QJsonObject& pJsonObject, const QString& pId) +{ + qDebug() << "RemoteDispatcherSpy::onReceived() -" << pMessageType; + mReceivedMessageTypes += pMessageType; + mReceivedMessages += pJsonObject; + mReceivedSignalSenders += pId; +} + + +class test_RemoteDisp + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void channelClosedNormallyClient() + { + const QSharedPointer channel(new MockDataChannel()); + const QSharedPointer dispatcher(new RemoteDispatcherClient(IfdVersion::Version::v_test, channel)); + RemoteDispatcherSpy spy(dispatcher); + + channel->close(); + QVERIFY(spy.isClosed()); + QCOMPARE(spy.getCloseCode(), GlobalStatus::Code::RemoteReader_CloseCode_NormalClose); + + const QVector& senders = spy.getReceivedSignalSenders(); + QCOMPARE(senders.size(), 1); + QCOMPARE(senders.first(), dispatcher->getId()); + } + + + void channelClosedNormallyServer() + { + const QSharedPointer channel(new MockDataChannel()); + const QSharedPointer dispatcher(new RemoteDispatcherServer(channel)); + RemoteDispatcherSpy spy(dispatcher); + + channel->close(); + QVERIFY(spy.isClosed()); + QCOMPARE(spy.getCloseCode(), GlobalStatus::Code::RemoteReader_CloseCode_NormalClose); + + const QVector& senders = spy.getReceivedSignalSenders(); + QCOMPARE(senders.size(), 1); + QCOMPARE(senders.first(), dispatcher->getId()); + } + + + void channelClosedAbnormallyClient() + { + const QSharedPointer channel(new MockDataChannel()); + const QSharedPointer dispatcher(new RemoteDispatcherClient(IfdVersion::Version::v_test, channel)); + RemoteDispatcherSpy spy(dispatcher); + + channel->closeAbnormal(); + QVERIFY(spy.isClosed()); + QCOMPARE(spy.getCloseCode(), GlobalStatus::Code::RemoteReader_CloseCode_AbnormalClose); + + const QVector& senders = spy.getReceivedSignalSenders(); + QCOMPARE(senders.size(), 1); + QCOMPARE(senders.first(), dispatcher->getId()); + } + + + void channelClosedAbnormallyServer() + { + const QSharedPointer channel(new MockDataChannel()); + const QSharedPointer dispatcher(new RemoteDispatcherServer(channel)); + RemoteDispatcherSpy spy(dispatcher); + + channel->closeAbnormal(); + QVERIFY(spy.isClosed()); + QCOMPARE(spy.getCloseCode(), GlobalStatus::Code::RemoteReader_CloseCode_AbnormalClose); + + const QVector& senders = spy.getReceivedSignalSenders(); + QCOMPARE(senders.size(), 1); + QCOMPARE(senders.first(), dispatcher->getId()); + } + + + void messagesAreDelivered() + { + const QSharedPointer clientChannel(new MockDataChannel()); + const QSharedPointer clientDispatcher(new RemoteDispatcherClient(IfdVersion::Version::v0, clientChannel)); + + const QSharedPointer serverChannel(new MockDataChannel()); + const QSharedPointer serverDispatcher(new RemoteDispatcherServer(serverChannel)); + + connect(clientChannel.data(), &MockDataChannel::fireSend, serverChannel.data(), &MockDataChannel::onReceived, Qt::DirectConnection); + connect(serverChannel.data(), &MockDataChannel::fireSend, clientChannel.data(), &MockDataChannel::onReceived, Qt::DirectConnection); + + RemoteDispatcherSpy spy(serverDispatcher); + + clientDispatcher->send(QSharedPointer(new IfdEstablishContext(IfdVersion::Version::v0, DeviceInfo::getName()))); + clientDispatcher->send(QSharedPointer(new IfdConnect(QStringLiteral("NFC Reader")))); + clientDispatcher->send(QSharedPointer(new IfdTransmit(QStringLiteral("NFC Reader"), QByteArray::fromHex("00A402022F00")))); + clientDispatcher->send(QSharedPointer(new IfdDisconnect(QStringLiteral("NFC Reader")))); + + const QVector receivedMessageTypes = spy.getReceivedMessageTypes(); + const QVector receivedMessages = spy.getReceivedMessages(); + QCOMPARE(receivedMessageTypes.size(), 3); + QCOMPARE(receivedMessages.size(), 3); + + QCOMPARE(receivedMessageTypes.at(0), RemoteCardMessageType::IFDConnect); + const IfdConnect ifdConnect(receivedMessages.at(0)); + QCOMPARE(ifdConnect.getType(), RemoteCardMessageType::IFDConnect); + QCOMPARE(ifdConnect.getSlotName(), QStringLiteral("NFC Reader")); + + QCOMPARE(receivedMessageTypes.at(1), RemoteCardMessageType::IFDTransmit); + const IfdTransmit ifdTransmit(receivedMessages.at(1)); + QCOMPARE(ifdTransmit.getType(), RemoteCardMessageType::IFDTransmit); + QCOMPARE(ifdTransmit.getSlotHandle(), QStringLiteral("NFC Reader")); + QCOMPARE(ifdTransmit.getInputApdu(), QByteArray::fromHex("00A402022F00")); + + QCOMPARE(receivedMessageTypes.at(2), RemoteCardMessageType::IFDDisconnect); + const IfdDisconnect ifdDisconnect(receivedMessages.at(2)); + QCOMPARE(ifdDisconnect.getType(), RemoteCardMessageType::IFDDisconnect); + QCOMPARE(ifdDisconnect.getSlotHandle(), QStringLiteral("NFC Reader")); + + const QVector& senders = spy.getReceivedSignalSenders(); + QCOMPARE(senders.size(), 3); + QCOMPARE(senders.at(0), serverDispatcher->getId()); + QCOMPARE(senders.at(1), serverDispatcher->getId()); + QCOMPARE(senders.at(2), serverDispatcher->getId()); + } + + + void channelIsClosedWhenRemoteDispatcherIsDestroyed() + { + const QSharedPointer clientChannel(new MockDataChannel()); + QSharedPointer clientDispatcher(new RemoteDispatcherClient(IfdVersion::Version::v0, clientChannel)); + + const QSharedPointer serverChannel(new MockDataChannel()); + const QSharedPointer serverDispatcher(new RemoteDispatcherServer(serverChannel)); + + connect(clientChannel.data(), &MockDataChannel::fireSend, serverChannel.data(), &MockDataChannel::onReceived, Qt::DirectConnection); + connect(clientChannel.data(), &DataChannel::fireClosed, serverChannel.data(), &DataChannel::close, Qt::DirectConnection); + + RemoteDispatcherSpy spy(serverDispatcher); + QVERIFY(!spy.isClosed()); + // Destroying a remote dispatcher should close the underlying channel. + clientDispatcher.reset(); + QVERIFY(spy.isClosed()); + QCOMPARE(spy.getCloseCode(), GlobalStatus::Code::RemoteReader_CloseCode_NormalClose); + + const QVector& senders = spy.getReceivedSignalSenders(); + QCOMPARE(senders.size(), 1); + QCOMPARE(senders.at(0), serverDispatcher->getId()); + } + + + void repeatedIfdEstablishContextGenerateCorrectErrorMessage() + { + const QSharedPointer clientChannel(new MockDataChannel()); + const QSharedPointer clientDispatcher(new RemoteDispatcherClient(IfdVersion::Version::v0, clientChannel)); + + const QSharedPointer serverChannel(new MockDataChannel()); + const QSharedPointer serverDispatcher(new RemoteDispatcherServer(serverChannel)); + + connect(clientChannel.data(), &MockDataChannel::fireSend, serverChannel.data(), &MockDataChannel::onReceived, Qt::DirectConnection); + connect(serverChannel.data(), &MockDataChannel::fireSend, clientChannel.data(), &MockDataChannel::onReceived, Qt::DirectConnection); + + clientDispatcher->send(QSharedPointer(new IfdEstablishContext(IfdVersion::Version::v0, DeviceInfo::getName()))); + clientDispatcher->send(QSharedPointer(new IfdEstablishContext(IfdVersion::Version::v0, DeviceInfo::getName()))); + + const QVector& clientReceivedDataBlocks = clientChannel->getReceivedDataBlocks(); + QCOMPARE(clientReceivedDataBlocks.size(), 2); + + const IfdEstablishContextResponse message1(RemoteMessage::parseByteArray(clientReceivedDataBlocks.at(0))); + QVERIFY(!message1.isIncomplete()); + QCOMPARE(message1.getType(), RemoteCardMessageType::IFDEstablishContextResponse); + QCOMPARE(message1.resultHasError(), false); + QCOMPARE(message1.getResultMinor(), ECardApiResult::Minor::null); + + const IfdEstablishContextResponse message2(RemoteMessage::parseByteArray(clientReceivedDataBlocks.at(1))); + QVERIFY(!message2.isIncomplete()); + QCOMPARE(message2.getType(), RemoteCardMessageType::IFDEstablishContextResponse); + QCOMPARE(message2.resultHasError(), true); + QCOMPARE(message2.getResultMinor(), ECardApiResult::Minor::AL_Unknown_Error); + } + + + void ifdEstablishContextWithWrongProtocolGeneratesCorrectErrorMessage() + { + const QSharedPointer clientChannel(new MockDataChannel()); + const QSharedPointer clientDispatcher(new RemoteDispatcherClient(IfdVersion::Version::v_test, clientChannel)); + + const QSharedPointer serverChannel(new MockDataChannel()); + const QSharedPointer serverDispatcher(new RemoteDispatcherServer(serverChannel)); + + connect(clientChannel.data(), &MockDataChannel::fireSend, serverChannel.data(), &MockDataChannel::onReceived, Qt::DirectConnection); + connect(serverChannel.data(), &MockDataChannel::fireSend, clientChannel.data(), &MockDataChannel::onReceived, Qt::DirectConnection); + + clientDispatcher->send(QSharedPointer(new IfdEstablishContext(IfdVersion::Version::v_test, DeviceInfo::getName()))); + + const QVector& clientReceivedDataBlocks = clientChannel->getReceivedDataBlocks(); + QCOMPARE(clientReceivedDataBlocks.size(), 1); + + const IfdEstablishContextResponse message(RemoteMessage::parseByteArray(clientReceivedDataBlocks.at(0))); + QVERIFY(!message.isIncomplete()); + QCOMPARE(message.getType(), RemoteCardMessageType::IFDEstablishContextResponse); + QCOMPARE(message.resultHasError(), true); + QCOMPARE(message.getResultMinor(), ECardApiResult::Minor::AL_Unknown_Error); + } + + +}; + +QTEST_GUILESS_MAIN(test_RemoteDisp) +#include "test_RemoteDisp.moc" diff --git a/test/qt/remote_device/test_RemoteDispImpl.cpp b/test/qt/remote_device/test_RemoteDispImpl.cpp deleted file mode 100644 index d8357aa..0000000 --- a/test/qt/remote_device/test_RemoteDispImpl.cpp +++ /dev/null @@ -1,275 +0,0 @@ -/*! - * \brief Unit tests for \ref RemoteDispatcher - * - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "RemoteDispatcherImpl.h" - -#include "messages/IfdConnect.h" -#include "messages/IfdDisconnect.h" -#include "messages/IfdEstablishContext.h" -#include "messages/IfdEstablishContextResponse.h" -#include "messages/IfdTransmit.h" -#include "MockDataChannel.h" -#include "RemoteMessageChecker.h" - -#include -#include - - -using namespace governikus; - - -class RemoteDispatcherSpy - : public QObject -{ - Q_OBJECT - - private: - const QSharedPointer mRemoteDispatcher; - bool mClosed; - GlobalStatus::Code mCloseCode; - QVector > mReceivedMessages; - QVector > mReceivedSignalSenders; - - public: - RemoteDispatcherSpy(const QSharedPointer pRemoteDispatcher); - virtual ~RemoteDispatcherSpy(); - - bool isClosed() const; - GlobalStatus::Code getCloseCode() const; - const QVector >& getReceivedMessages() const; - const QVector >& getReceivedSignalSenders() const; - - private Q_SLOTS: - void onClosed(GlobalStatus::Code pCloseCode, const QSharedPointer& pDispatcher); - void onReceived(const QSharedPointer& pMessage, const QSharedPointer& pDispatcher); -}; - - -RemoteDispatcherSpy::RemoteDispatcherSpy(const QSharedPointer pRemoteDispatcher) - : mRemoteDispatcher(pRemoteDispatcher) - , mClosed(false) - , mCloseCode(GlobalStatus::Code::RemoteReader_CloseCode_Undefined) -{ - connect(mRemoteDispatcher.data(), &RemoteDispatcher::fireClosed, this, &RemoteDispatcherSpy::onClosed); - connect(mRemoteDispatcher.data(), &RemoteDispatcher::fireReceived, this, &RemoteDispatcherSpy::onReceived); -} - - -RemoteDispatcherSpy::~RemoteDispatcherSpy() -{ - disconnect(mRemoteDispatcher.data(), &RemoteDispatcher::fireClosed, this, &RemoteDispatcherSpy::onClosed); - disconnect(mRemoteDispatcher.data(), &RemoteDispatcher::fireReceived, this, &RemoteDispatcherSpy::onReceived); -} - - -bool RemoteDispatcherSpy::isClosed() const -{ - return mClosed; -} - - -GlobalStatus::Code RemoteDispatcherSpy::getCloseCode() const -{ - return mCloseCode; -} - - -const QVector >& RemoteDispatcherSpy::getReceivedMessages() const -{ - return mReceivedMessages; -} - - -const QVector >& RemoteDispatcherSpy::getReceivedSignalSenders() const -{ - return mReceivedSignalSenders; -} - - -void RemoteDispatcherSpy::onClosed(GlobalStatus::Code pCloseCode, const QSharedPointer& pDispatcher) -{ - mClosed = true; - mCloseCode = pCloseCode; - mReceivedSignalSenders += pDispatcher; -} - - -void RemoteDispatcherSpy::onReceived(const QSharedPointer& pMessage, const QSharedPointer& pDispatcher) -{ - qDebug() << "RemoteDispatcherSpy::onReceived() -" << pMessage->getType(); - mReceivedMessages += pMessage; - mReceivedSignalSenders += pDispatcher; -} - - -class test_RemoteDisp - : public QObject -{ - Q_OBJECT - - private: - RemoteMessageChecker mChecker; - - private Q_SLOTS: - void channelClosedNormally() - { - const QSharedPointer channel(new MockDataChannel()); - const QSharedPointer dispatcher(new RemoteDispatcherImpl(channel)); - RemoteDispatcherSpy spy(dispatcher); - - channel->close(); - QVERIFY(spy.isClosed()); - QCOMPARE(spy.getCloseCode(), GlobalStatus::Code::RemoteReader_CloseCode_NormalClose); - - const QVector >& senders = spy.getReceivedSignalSenders(); - QCOMPARE(senders.size(), 1); - QCOMPARE(senders.first().data(), dispatcher.data()); - } - - - void channelClosedAbnormally() - { - const QSharedPointer channel(new MockDataChannel()); - const QSharedPointer dispatcher(new RemoteDispatcherImpl(channel)); - RemoteDispatcherSpy spy(dispatcher); - - channel->closeAbnormal(); - QVERIFY(spy.isClosed()); - QCOMPARE(spy.getCloseCode(), GlobalStatus::Code::RemoteReader_CloseCode_AbnormalClose); - - const QVector >& senders = spy.getReceivedSignalSenders(); - QCOMPARE(senders.size(), 1); - QCOMPARE(senders.first().data(), dispatcher.data()); - } - - - void messagesAreDelivered() - { - const QSharedPointer clientChannel(new MockDataChannel()); - const QSharedPointer clientDispatcher(new RemoteDispatcherImpl(clientChannel)); - - const QSharedPointer serverChannel(new MockDataChannel()); - const QSharedPointer serverDispatcher(new RemoteDispatcherImpl(serverChannel)); - - connect(clientChannel.data(), &MockDataChannel::fireSend, serverChannel.data(), &MockDataChannel::onReceived, Qt::DirectConnection); - connect(serverChannel.data(), &MockDataChannel::fireSend, clientChannel.data(), &MockDataChannel::onReceived, Qt::DirectConnection); - - RemoteDispatcherSpy spy(serverDispatcher); - - clientDispatcher->send(QSharedPointer(new IfdEstablishContext(QStringLiteral("IFDInterface_WebSocket_v0"), DeviceInfo::getName()))); - clientDispatcher->send(QSharedPointer(new IfdConnect(QStringLiteral("NFC Reader")))); - clientDispatcher->send(QSharedPointer(new IfdTransmit(QStringLiteral("NFC Reader"), QByteArray::fromHex("00A402022F00")))); - clientDispatcher->send(QSharedPointer(new IfdDisconnect(QStringLiteral("NFC Reader")))); - - const QVector > receivedMessages = spy.getReceivedMessages(); - QCOMPARE(receivedMessages.size(), 3); - QCOMPARE(receivedMessages.at(0)->getType(), RemoteCardMessageType::IFDConnect); - mChecker.receive(receivedMessages.at(0)); - - QCOMPARE(receivedMessages.at(1)->getType(), RemoteCardMessageType::IFDTransmit); - mChecker.receive(receivedMessages.at(1)); - - QCOMPARE(receivedMessages.at(2)->getType(), RemoteCardMessageType::IFDDisconnect); - mChecker.receive(receivedMessages.at(2)); - - const QVector >& senders = spy.getReceivedSignalSenders(); - QCOMPARE(senders.size(), 3); - QCOMPARE(senders.at(0).data(), serverDispatcher.data()); - QCOMPARE(senders.at(1).data(), serverDispatcher.data()); - QCOMPARE(senders.at(2).data(), serverDispatcher.data()); - } - - - void channelIsClosedWhenRemoteDispatcherIsDestroyed() - { - const QSharedPointer clientChannel(new MockDataChannel()); - QSharedPointer clientDispatcher(new RemoteDispatcherImpl(clientChannel)); - - const QSharedPointer serverChannel(new MockDataChannel()); - const QSharedPointer serverDispatcher(new RemoteDispatcherImpl(serverChannel)); - - connect(clientChannel.data(), &MockDataChannel::fireSend, serverChannel.data(), &MockDataChannel::onReceived, Qt::DirectConnection); - connect(clientChannel.data(), &DataChannel::fireClosed, serverChannel.data(), &DataChannel::close, Qt::DirectConnection); - - RemoteDispatcherSpy spy(serverDispatcher); - QVERIFY(!spy.isClosed()); - // Destroying a remote dispatcher should close the underlying channel. - clientDispatcher.reset(); - QVERIFY(spy.isClosed()); - QCOMPARE(spy.getCloseCode(), GlobalStatus::Code::RemoteReader_CloseCode_NormalClose); - - const QVector >& senders = spy.getReceivedSignalSenders(); - QCOMPARE(senders.size(), 1); - QCOMPARE(senders.at(0).data(), serverDispatcher.data()); - } - - - void repeatedIfdEstablishContextGenerateCorrectErrorMessage() - { - const QSharedPointer clientChannel(new MockDataChannel()); - const QSharedPointer clientDispatcher(new RemoteDispatcherImpl(clientChannel)); - - const QSharedPointer serverChannel(new MockDataChannel()); - const QSharedPointer serverDispatcher(new RemoteDispatcherImpl(serverChannel)); - - connect(clientChannel.data(), &MockDataChannel::fireSend, serverChannel.data(), &MockDataChannel::onReceived, Qt::DirectConnection); - connect(serverChannel.data(), &MockDataChannel::fireSend, clientChannel.data(), &MockDataChannel::onReceived, Qt::DirectConnection); - - clientDispatcher->send(QSharedPointer(new IfdEstablishContext(QStringLiteral("IFDInterface_WebSocket_v0"), DeviceInfo::getName()))); - clientDispatcher->send(QSharedPointer(new IfdEstablishContext(QStringLiteral("IFDInterface_WebSocket_v0"), DeviceInfo::getName()))); - - const QVector& clientReceivedDataBlocks = clientChannel->getReceivedDataBlocks(); - QCOMPARE(clientReceivedDataBlocks.size(), 2); - const QSharedPointer message1 = RemoteMessageParser().parse(clientReceivedDataBlocks.at(0)).dynamicCast(); - QVERIFY(!message1.isNull()); - QCOMPARE(message1->getType(), RemoteCardMessageType::IFDEstablishContextResponse); - QCOMPARE(message1->resultHasError(), false); - QCOMPARE(message1->getResultMinor(), QString()); - - const QSharedPointer message2 = RemoteMessageParser().parse(clientReceivedDataBlocks.at(1)).dynamicCast(); - QVERIFY(!message2.isNull()); - QCOMPARE(message2->getType(), RemoteCardMessageType::IFDEstablishContextResponse); - QCOMPARE(message2->resultHasError(), true); - QCOMPARE(message2->getResultMinor(), QStringLiteral("http://www.bsi.bund.de/ecard/api/1.1/resultminor/al/common#unknownError")); - } - - - void ifdEstablishContextWithWrongProtocolGeneratesCorrectErrorMessage() - { - const QSharedPointer clientChannel(new MockDataChannel()); - const QSharedPointer clientDispatcher(new RemoteDispatcherImpl(clientChannel)); - - const QSharedPointer serverChannel(new MockDataChannel()); - const QSharedPointer serverDispatcher(new RemoteDispatcherImpl(serverChannel)); - - connect(clientChannel.data(), &MockDataChannel::fireSend, serverChannel.data(), &MockDataChannel::onReceived, Qt::DirectConnection); - connect(serverChannel.data(), &MockDataChannel::fireSend, clientChannel.data(), &MockDataChannel::onReceived, Qt::DirectConnection); - - clientDispatcher->send(QSharedPointer(new IfdEstablishContext(QStringLiteral("IFDInterface_WebSocket_v2"), DeviceInfo::getName()))); - - const QVector& clientReceivedDataBlocks = clientChannel->getReceivedDataBlocks(); - QCOMPARE(clientReceivedDataBlocks.size(), 1); - const QSharedPointer message = RemoteMessageParser().parse(clientReceivedDataBlocks.at(0)); - QVERIFY(!message.isNull()); - QCOMPARE(message->getType(), RemoteCardMessageType::IFDEstablishContextResponse); - const QSharedPointer ifdEstablishContextResponse = message.dynamicCast(); - QVERIFY(!ifdEstablishContextResponse.isNull()); - QCOMPARE(ifdEstablishContextResponse->resultHasError(), true); - QCOMPARE(ifdEstablishContextResponse->getResultMinor(), QStringLiteral("http://www.bsi.bund.de/ecard/api/1.1/resultminor/al/common#unknownError")); - } - - - public: - test_RemoteDisp() - : mChecker() - { - } - - -}; - -QTEST_GUILESS_MAIN(test_RemoteDisp) -#include "test_RemoteDispImpl.moc" diff --git a/test/qt/remote_device/test_RemoteHelper.cpp b/test/qt/remote_device/test_RemoteHelper.cpp deleted file mode 100644 index 1488b1c..0000000 --- a/test/qt/remote_device/test_RemoteHelper.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/*! - * \brief Unit tests for \ref RemoteHelper - * - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "AppSettings.h" -#include "RemoteHelper.h" -#include "RemoteServiceSettings.h" - -#include - -using namespace governikus; - -class test_RemoteHelper - : public QObject -{ - Q_OBJECT - - private Q_SLOTS: - void testCheckAndGenerateKey() - { - auto& settings = AppSettings::getInstance().getRemoteServiceSettings(); - QCOMPARE(settings.getKey(), QSslKey()); - QCOMPARE(settings.getCertificate(), QSslCertificate()); - - QVERIFY(RemoteHelper::checkAndGenerateKey()); - const auto& key = settings.getKey(); - const auto& cert = settings.getCertificate(); - QVERIFY(!key.isNull()); - QVERIFY(!cert.isNull()); - - QVERIFY(RemoteHelper::checkAndGenerateKey()); - QCOMPARE(settings.getKey(), key); - QCOMPARE(settings.getCertificate(), cert); - - QCOMPARE(settings.getCertificate().effectiveDate(), QDateTime::fromString(QStringLiteral("1970-01-01T00:00:00Z"), Qt::ISODate)); - QCOMPARE(settings.getCertificate().expiryDate(), QDateTime::fromString(QStringLiteral("9999-12-31T23:59:59Z"), Qt::ISODate)); - QVERIFY(settings.getCertificate().effectiveDate().isValid()); - QVERIFY(settings.getCertificate().expiryDate().isValid()); - } - - -}; - - -QTEST_GUILESS_MAIN(test_RemoteHelper) -#include "test_RemoteHelper.moc" diff --git a/test/qt/remote_device/test_RemoteMessageParser.cpp b/test/qt/remote_device/test_RemoteMessageParser.cpp deleted file mode 100644 index ba02478..0000000 --- a/test/qt/remote_device/test_RemoteMessageParser.cpp +++ /dev/null @@ -1,609 +0,0 @@ -/*! - * \brief Unit tests for \ref RemoteMessageParser - * - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "messages/RemoteMessageParser.h" - -#include "messages/IfdConnectResponse.h" -#include "messages/IfdDisconnectResponse.h" -#include "messages/IfdEstablishContext.h" -#include "messages/IfdStatus.h" -#include "messages/IfdTransmitResponse.h" -#include "messages/RemoteMessage.h" -#include "RemoteMessageChecker.h" - -#include -#include - - -using namespace governikus; - - -class test_RemoteMessageParser - : public QObject -{ - Q_OBJECT - - private: - const RemoteMessageParser mParser; - - RemoteMessageChecker mChecker; - - void parseAndVerify(const char* pJsonByteData, bool isValid = true) - { - const QByteArray jsonData(pJsonByteData); - const QSharedPointer message = mParser.parse(jsonData); - - QVERIFY(!message.isNull()); - mChecker.receive(message); - QCOMPARE(message->isValid(), isValid); - } - - - private Q_SLOTS: - void invalidJsonInput() - { - const QByteArray jsonData("bla bla"); - const QSharedPointer message = mParser.parse(jsonData); - - QVERIFY(message.isNull()); - } - - - void discovery() - { - const QByteArray jsonData("{\n" - " \"IFDName\": \"Sony Xperia Z5 compact\",\n" - " \"IFDID\": \"0123456789ABCDEF\",\n" - " \"msg\": \"REMOTE_IFD\",\n" - " \"port\": 24728,\n" - " \"SupportedAPI\": [\"IFDInterface_WebSocket_v0\", \"IFDInterface_WebSocket_v2\"]\n" - "}"); - - QJsonParseError error; - const QJsonDocument& doc = QJsonDocument::fromJson(jsonData, &error); - QVERIFY(error.error == QJsonParseError::NoError); - - const QSharedPointer message = mParser.parseDiscovery(doc); - - QVERIFY(!message.isNull()); - mChecker.processDiscovery(message); - } - - - void discovery_with_missing_msg_field() - { - const QByteArray jsonData("{\n" - " \"IFDName\": \"Sony Xperia Z5 compact\",\n" - " \"IFDID\": \"0123456789ABCDEF\",\n" - " \"port\": 24728,\n" - " \"SupportedAPI\": [\"IFDInterface_WebSocket_v0\", \"IFDInterface_WebSocket_v2\"]\n" - "}"); - - QJsonParseError error; - const QJsonDocument& doc = QJsonDocument::fromJson(jsonData, &error); - QVERIFY(error.error == QJsonParseError::NoError); - - const QSharedPointer message = mParser.parseDiscovery(doc); - - QVERIFY(message.isNull()); - } - - - void discovery_with_invalid_msg_field() - { - const QByteArray jsonData("{\n" - " \"IFDName\": \"Sony Xperia Z5 compact\",\n" - " \"IFDID\": \"0123456789ABCDEF\",\n" - " \"msg\": \"REMOTE\",\n" - " \"port\": 24728,\n" - " \"SupportedAPI\": [\"IFDInterface_WebSocket_v0\", \"IFDInterface_WebSocket_v2\"]\n" - "}"); - - QJsonParseError error; - const QJsonDocument& doc = QJsonDocument::fromJson(jsonData, &error); - QVERIFY(error.error == QJsonParseError::NoError); - - const QSharedPointer message = mParser.parseDiscovery(doc); - - QVERIFY(message.isNull()); - } - - - void ifdEstablishContext() - { - parseAndVerify("{\n" - " \"msg\": \"IFDEstablishContext\",\n" - " \"Protocol\": \"IFDInterface_WebSocket_v0\",\n" - " \"UDName\": \"MAC-MINI\"\n" - "}"); - } - - - void ifdEstablishContextResponse() - { - parseAndVerify("{\n" - " \"ContextHandle\": \"contextHandle\",\n" - " \"IFDName\": \"IFD Remote Server\",\n" - " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#error\",\n" - " \"ResultMinor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultminor/minorResult\",\n" - " \"msg\": \"IFDEstablishContextResponse\"\n" - "}"); - } - - - void getIfdStatus() - { - parseAndVerify("{\n" - " \"ContextHandle\": \"contextHandle\",\n" - " \"msg\": \"IFDGetStatus\",\n" - " \"SlotName\": \"Remote Reader\"\n" - "}"); - } - - - void ifdStatus() - { - parseAndVerify("{\n" - " \"ContextHandle\": \"contextHandle\",\n" - " \"msg\": \"IFDStatus\",\n" - " \"ConnectedReader\": true,\n" - " \"IFDName\": \"Remote Reader\",\n" - " \"SlotName\": \"NFC Reader\",\n" - " \"PINCapabilities\": {\n" - " \"Destroy\": false,\n" - " \"PACE\": false,\n" - " \"eID\": false,\n" - " \"eSign\": false\n" - " },\n" - " \"MaxAPDULength\": 500,\n" - " \"CardAvailable\": false\n" - "}"); - } - - - void ifdConnect() - { - parseAndVerify("{\n" - " \"ContextHandle\": \"contextHandle\",\n" - " \"exclusive\": true,\n" - " \"msg\": \"IFDConnect\",\n" - " \"SlotName\": \"NFC Reader\"\n" - "}"); - } - - - void ifdConnectWrongContextHandleType() - { - parseAndVerify("{\n" - " \"ContextHandle\": true,\n" - " \"exclusive\": true,\n" - " \"msg\": \"IFDConnect\",\n" - " \"SlotName\": \"NFC Reader\"\n" - "}", false); - } - - - void ifdConnectMissingContextHandle() - { - parseAndVerify("{\n" - " \"exclusive\": true,\n" - " \"msg\": \"IFDConnect\",\n" - " \"SlotName\": \"NFC Reader\"\n" - "}", false); - } - - - void ifdConnectWrongExclusvieType() - { - parseAndVerify("{\n" - " \"ContextHandle\": \"contextHandle\",\n" - " \"exclusive\": \"test\",\n" - " \"msg\": \"IFDConnect\",\n" - " \"SlotName\": \"NFC Reader\"\n" - "}", false); - } - - - void ifdConnectMissingExclusvie() - { - parseAndVerify("{\n" - " \"ContextHandle\": \"contextHandle\",\n" - " \"msg\": \"IFDConnect\",\n" - " \"SlotName\": \"NFC Reader\"\n" - "}", false); - } - - - void ifdConnectResponse() - { - parseAndVerify("{\n" - " \"ContextHandle\": \"contextHandle\",\n" - " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#error\",\n" - " \"ResultMinor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultminor/minorResult\",\n" - " \"msg\": \"IFDConnectResponse\",\n" - " \"SlotHandle\": \"NFC Reader\"\n" - "}\n"); - } - - - void ifdConnectResponseWrongContextHandleType() - { - parseAndVerify("{\n" - " \"ContextHandle\": true,\n" - " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#error\",\n" - " \"ResultMinor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultminor/minorResult\",\n" - " \"msg\": \"IFDConnectResponse\",\n" - " \"SlotHandle\": \"NFC Reader\"\n" - "}\n", false); - } - - - void ifdConnectResponseMissingContextHandle() - { - parseAndVerify("{\n" - " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#error\",\n" - " \"ResultMinor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultminor/minorResult\",\n" - " \"msg\": \"IFDConnectResponse\",\n" - " \"SlotHandle\": \"NFC Reader\"\n" - "}\n", false); - } - - - void ifdConnectResponseMsg_nullError() - { - QByteArray jsonData("{\n" - " \"ContextHandle\": \"contextHandle\",\n" - " \"ResultMajor\": null,\n" - " \"ResultMinor\": \"Error message\",\n" - " \"msg\": \"IFDConnectResponse\",\n" - " \"SlotHandle\": \"NFC Reader\"\n" - "}\n"); - - const QSharedPointer message = mParser.parse(jsonData); - - QVERIFY(!message.isNull()); - QCOMPARE(message->getType(), RemoteCardMessageType::IFDConnectResponse); - const auto& msg = message.staticCast(); - QCOMPARE(msg->resultHasError(), true); - QCOMPARE(msg->getResultMinor(), QStringLiteral("Error message")); - QCOMPARE(msg->getSlotHandle(), QStringLiteral("NFC Reader")); - QVERIFY(!msg->isValid()); - } - - - void ifdConnectResponseMsg_noError() - { - QByteArray jsonData("{\n" - " \"ContextHandle\": \"contextHandle\",\n" - " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\",\n" - " \"ResultMinor\": null,\n" - " \"msg\": \"IFDConnectResponse\",\n" - " \"SlotHandle\": \"NFC Reader\"\n" - "}\n"); - - const QSharedPointer message = mParser.parse(jsonData); - - QVERIFY(!message.isNull()); - QCOMPARE(message->getType(), RemoteCardMessageType::IFDConnectResponse); - const auto& msg = message.staticCast(); - QCOMPARE(msg->resultHasError(), false); - QCOMPARE(msg->getResultMinor(), QString()); - QCOMPARE(msg->getSlotHandle(), QStringLiteral("NFC Reader")); - QVERIFY(msg->isValid()); - } - - - void ifdDisconnect() - { - parseAndVerify("{\n" - " \"ContextHandle\": \"contextHandle\",\n" - " \"SlotHandle\": \"NFC Reader\",\n" - " \"msg\": \"IFDDisconnect\",\n" - " \"readerName\": \"NFC Reader\"\n" - "}"); - } - - - void ifdDisconnectResponse() - { - parseAndVerify("{\n" - " \"ContextHandle\": \"contextHandle\",\n" - " \"SlotHandle\": \"NFC Reader\",\n" - " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#error\",\n" - " \"ResultMinor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultminor/minorResult\",\n" - " \"msg\": \"IFDDisconnectResponse\"\n" - "}\n"); - } - - - void ifdDisconnectResponse_nullError() - { - QByteArray jsonData("{\n" - " \"ContextHandle\": \"contextHandle\",\n" - " \"SlotHandle\": \"NFC Reader\",\n" - " \"ResultMajor\": null,\n" - " \"ResultMinor\": \"Error message\",\n" - " \"msg\": \"IFDDisconnectResponse\"\n" - "}\n"); - - const QSharedPointer message = mParser.parse(jsonData); - - QVERIFY(!message.isNull()); - QCOMPARE(message->getType(), RemoteCardMessageType::IFDDisconnectResponse); - const auto& msg = message.staticCast(); - QCOMPARE(msg->resultHasError(), true); - QCOMPARE(msg->getResultMinor(), QStringLiteral("Error message")); - QCOMPARE(msg->getSlotHandle(), QStringLiteral("NFC Reader")); - QVERIFY(!msg->isValid()); - } - - - void ifdDisconnectResponse_noError() - { - QByteArray jsonData("{\n" - " \"ContextHandle\": \"contextHandle\",\n" - " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\",\n" - " \"ResultMinor\": null,\n" - " \"SlotHandle\": \"NFC Reader\",\n" - " \"msg\": \"IFDDisconnectResponse\"\n" - "}\n"); - - const QSharedPointer message = mParser.parse(jsonData); - - QVERIFY(!message.isNull()); - QCOMPARE(message->getType(), RemoteCardMessageType::IFDDisconnectResponse); - const auto& msg = message.staticCast(); - QCOMPARE(msg->resultHasError(), false); - QCOMPARE(msg->getResultMinor(), QString()); - QCOMPARE(msg->getSlotHandle(), QStringLiteral("NFC Reader")); - } - - - void ifdTransmit() - { - parseAndVerify("{\n" - " \"ContextHandle\": \"contextHandle\",\n" - " \"msg\": \"IFDTransmit\",\n" - " \"SlotHandle\": \"NFC Reader\",\n" - " \"CommandAPDUs\": [\n" - " {\n" - " \"InputAPDU\": \"00A402022F00\",\n" - " \"AcceptableStatusCodes\": null\n" - " }\n" - " ]\n" - "}"); - } - - - void ifdTransmitWrongContextHandleType() - { - parseAndVerify("{\n" - " \"ContextHandle\": true,\n" - " \"msg\": \"IFDTransmit\",\n" - " \"SlotHandle\": \"NFC Reader\",\n" - " \"CommandAPDUs\": [\n" - " {\n" - " \"InputAPDU\": \"00A402022F00\",\n" - " \"AcceptableStatusCodes\": null\n" - " }\n" - " ]\n" - "}", false); - } - - - void ifdTransmitMissingContextHandle() - { - parseAndVerify("{\n" - " \"msg\": \"IFDTransmit\",\n" - " \"SlotHandle\": \"NFC Reader\",\n" - " \"CommandAPDUs\": [\n" - " {\n" - " \"InputAPDU\": \"00A402022F00\",\n" - " \"AcceptableStatusCodes\": null\n" - " }\n" - " ]\n" - "}", false); - } - - - void transmitResponseMsg() - { - parseAndVerify("{\n" - " \"ContextHandle\": \"contextHandle\",\n" - " \"SlotHandle\": \"NFC Reader\",\n" - " \"ResponseAPDUs\": [\n" - " \"9000\"\n" - " ],\n" - " \"msg\": \"IFDTransmitResponse\",\n" - " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#error\",\n" - " \"ResultMinor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultminor/minorResult\"\n" - "}"); - } - - - void transmitResponseMsg_nullError() - { - QByteArray jsonData("{\n" - " \"ContextHandle\": \"contextHandle\",\n" - " \"SlotHandle\": \"NFC Reader\",\n" - " \"ResponseAPDUs\": [\n" - " \"9000\"\n" - " ],\n" - " \"ResultMajor\": null,\n" - " \"ResultMinor\": \"Error message\",\n" - " \"msg\": \"IFDTransmitResponse\"\n" - "}\n"); - - const QSharedPointer message = mParser.parse(jsonData); - - QVERIFY(!message.isNull()); - QCOMPARE(message->getType(), RemoteCardMessageType::IFDTransmitResponse); - const auto& msg = message.staticCast(); - QCOMPARE(msg->resultHasError(), true); - QCOMPARE(msg->getResultMinor(), QStringLiteral("Error message")); - QCOMPARE(msg->getSlotHandle(), QStringLiteral("NFC Reader")); - QCOMPARE(msg->getResponseApdu(), QByteArray::fromHex("9000")); - QVERIFY(!msg->isValid()); - } - - - void transmitResponseMsg_noError() - { - QByteArray jsonData("{\n" - " \"ContextHandle\": \"contextHandle\",\n" - " \"SlotHandle\": \"NFC Reader\",\n" - " \"ResponseAPDUs\": [\n" - " \"9000\"\n" - " ],\n" - " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\",\n" - " \"ResultMinor\": null,\n" - " \"msg\": \"IFDTransmitResponse\"\n" - "}\n"); - - const QSharedPointer message = mParser.parse(jsonData); - - QVERIFY(!message.isNull()); - QVERIFY(message->isValid()); - QCOMPARE(message->getType(), RemoteCardMessageType::IFDTransmitResponse); - const auto& msg = message.staticCast(); - QCOMPARE(msg->resultHasError(), false); - QCOMPARE(msg->getResultMinor(), QString()); - QCOMPARE(msg->getSlotHandle(), QStringLiteral("NFC Reader")); - QCOMPARE(msg->getResponseApdu(), QByteArray::fromHex("9000")); - QVERIFY(msg->isValid()); - } - - - void transmitResponseMsg_noResponseApdu() - { - QByteArray jsonData("{\n" - " \"ContextHandle\": \"contextHandle\",\n" - " \"SlotHandle\": \"NFC Reader\",\n" - " \"ResultMajor\": \"/resultmajor#error\",\n" - " \"ResultMinor\": \"Error message\",\n" - " \"msg\": \"IFDTransmitResponse\"\n" - "}\n"); - - const QSharedPointer message = mParser.parse(jsonData); - - QVERIFY(!message.isNull()); - QVERIFY(!message->isValid()); - QCOMPARE(message->getType(), RemoteCardMessageType::IFDTransmitResponse); - const auto& msg = message.staticCast(); - QCOMPARE(msg->resultHasError(), true); - QCOMPARE(msg->getResultMinor(), QStringLiteral("Error message")); - QCOMPARE(msg->getSlotHandle(), QStringLiteral("NFC Reader")); - QCOMPARE(msg->getResponseApdu(), QByteArray()); - QVERIFY(!msg->isValid()); - } - - - void transmitResponseMsg_nullResponseApdu() - { - QByteArray jsonData("{\n" - " \"ContextHandle\": \"contextHandle\",\n" - " \"ResponseAPDUs\": null,\n" - " \"SlotHandle\": \"NFC Reader\",\n" - " \"ResultMajor\": \"/resultmajor#error\",\n" - " \"ResultMinor\": \"Error message\",\n" - " \"msg\": \"IFDTransmitResponse\"\n" - "}\n"); - - const QSharedPointer message = mParser.parse(jsonData); - - QVERIFY(!message.isNull()); - QCOMPARE(message->getType(), RemoteCardMessageType::IFDTransmitResponse); - const auto& msg = message.staticCast(); - QCOMPARE(msg->resultHasError(), true); - QCOMPARE(msg->getResultMinor(), QStringLiteral("Error message")); - QCOMPARE(msg->getSlotHandle(), QStringLiteral("NFC Reader")); - QCOMPARE(msg->getResponseApdu(), QByteArray()); - QVERIFY(!msg->isValid()); - } - - - void ifdEstablishPaceChannelMsg() - { - parseAndVerify("{\n" - " \"ContextHandle\": \"TestContext\",\n" - " \"InputData\": \"abcd1234\",\n" - " \"SlotHandle\": \"My little Reader\",\n" - " \"msg\": \"IFDEstablishPACEChannel\"\n" - "}\n"); - } - - - void ifdEstablishPaceChannelMessageResponse() - { - parseAndVerify("{\n" - " \"ContextHandle\": \"TestContext\",\n" - " \"OutputData\": \"abcd1234\",\n" - " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\",\n" - " \"ResultMinor\": null,\n" - " \"SlotHandle\": \"My little Reader\",\n" - " \"msg\": \"IFDEstablishPACEChannelResponse\"\n" - "}\n"); - } - - - void ifdEstablishPaceChannelMessageResponseWithError() - { - parseAndVerify("{\n" - " \"ContextHandle\": \"TestContext\",\n" - " \"OutputData\": \"abcd1234\",\n" - " \"ResultMajor\": \"/resultmajor#error\",\n" - " \"ResultMinor\": \"/resultminor/ifdl/common#timeoutError\",\n" - " \"SlotHandle\": \"My little Reader\",\n" - " \"msg\": \"IFDEstablishPACEChannelResponse\"\n" - "}"); - } - - - void ifdEstablishPaceChannelMessageResponseWithErrorMissingMinor() - { - parseAndVerify("{\n" - " \"ContextHandle\": \"TestContext\",\n" - " \"OutputData\": \"abcd1234\",\n" - " \"ResultMajor\": \"/resultmajor#error\",\n" - " \"SlotHandle\": \"My little Reader\",\n" - " \"msg\": \"IFDEstablishPACEChannelResponse\"\n" - "}", false); - - parseAndVerify("{\n" - " \"ContextHandle\": \"TestContext\",\n" - " \"OutputData\": \"abcd1234\",\n" - " \"ResultMajor\": \"/resultmajor#error\",\n" - " \"ResultMinor\": null,\n" - " \"SlotHandle\": \"My little Reader\",\n" - " \"msg\": \"IFDEstablishPACEChannelResponse\"\n" - "}\n", false); - } - - - void ifdError() - { - parseAndVerify("{\n" - " \"ContextHandle\": \"TestContext\",\n" - " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#error\",\n" - " \"ResultMinor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultminor/minorResult\",\n" - " \"SlotHandle\": \"NFC Reader\",\n" - " \"msg\": \"IFDError\"\n" - "}\n"); - } - - - public: - test_RemoteMessageParser() - : mParser() - , mChecker() - { - } - - -}; - -QTEST_GUILESS_MAIN(test_RemoteMessageParser) -#include "test_RemoteMessageParser.moc" diff --git a/test/qt/remote_device/test_RemoteMessages.cpp b/test/qt/remote_device/test_RemoteMessages.cpp deleted file mode 100644 index 44aa4fe..0000000 --- a/test/qt/remote_device/test_RemoteMessages.cpp +++ /dev/null @@ -1,504 +0,0 @@ -/*! - * \brief Unit tests for \ref RemoteMessages - * - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "messages/Discovery.h" -#include "messages/GetIfdStatus.h" -#include "messages/IfdConnect.h" -#include "messages/IfdConnectResponse.h" -#include "messages/IfdDisconnect.h" -#include "messages/IfdDisconnectResponse.h" -#include "messages/IfdError.h" -#include "messages/IfdEstablishContext.h" -#include "messages/IfdEstablishContextResponse.h" -#include "messages/IfdEstablishPaceChannel.h" -#include "messages/IfdEstablishPaceChannelResponse.h" -#include "messages/IfdStatus.h" -#include "messages/IfdTransmit.h" -#include "messages/IfdTransmitResponse.h" - -#include "MockIfdStatus.h" -#include "RemoteMessageChecker.h" -#include "TestFileHelper.h" - -#include -#include - - -using namespace governikus; - - -Q_DECLARE_METATYPE(QSharedPointer ) - - -class test_RemoteMessages - : public QObject -{ - Q_OBJECT - - private: - RemoteMessageChecker mChecker; - - private Q_SLOTS: - void discovery() - { - const QSharedPointer message( - new Discovery(QStringLiteral("Sony Xperia Z5 compact"), - QStringLiteral("0123456789ABCDEF"), - 24728, - {IfdVersion::Version::v0}) - ); - - mChecker.processDiscovery(message); - - const QJsonDocument document = message->toJson(); - QVERIFY(document.isObject()); - - const QJsonObject object = document.object(); - QCOMPARE(object.size(), 5); - QCOMPARE(object.value(QLatin1String("IFDName")).toString(), QStringLiteral("Sony Xperia Z5 compact")); - QCOMPARE(object.value(QLatin1String("IFDID")).toString(), QStringLiteral("0123456789ABCDEF")); - QCOMPARE(object.value(QLatin1String("msg")).toString(), QStringLiteral("REMOTE_IFD")); - QCOMPARE(object.value(QLatin1String("port")).toInt(), 24728); - const QJsonValue apiLevels = object.value(QLatin1String("SupportedAPI")); - QVERIFY(apiLevels.isArray()); - QCOMPARE(apiLevels.toArray().size(), 1); - QCOMPARE(apiLevels.toArray().at(0).toString(), QStringLiteral("IFDInterface_WebSocket_v0")); - - QCOMPARE(document.toJson(), - QByteArray("{\n" - " \"IFDID\": \"0123456789ABCDEF\",\n" - " \"IFDName\": \"Sony Xperia Z5 compact\",\n" - " \"SupportedAPI\": [\n" - " \"IFDInterface_WebSocket_v0\"\n" - " ],\n" - " \"msg\": \"REMOTE_IFD\",\n" - " \"port\": 24728\n" - "}\n")); - } - - - void ifdEstablishContext() - { - const QSharedPointer message(new IfdEstablishContext(IfdVersion::Version::v0, "MAC-MINI")); - - mChecker.receive(message); - - const QJsonDocument document = message->toJson(QStringLiteral("TestContext")); - QVERIFY(document.isObject()); - const QJsonObject object = document.object(); - QCOMPARE(object.size(), 3); - QCOMPARE(object.value(QLatin1String("msg")).toString(), QStringLiteral("IFDEstablishContext")); - QCOMPARE(object.value(QLatin1String("Protocol")).toString(), QStringLiteral("IFDInterface_WebSocket_v0")); - QCOMPARE(object.value(QLatin1String("UDName")).toString(), QStringLiteral("MAC-MINI")); - - QByteArray jsonToCompare = - QByteArray("{\n" - " \"Protocol\": \"IFDInterface_WebSocket_v0\",\n" - " \"UDName\": \"MAC-MINI\",\n" - " \"msg\": \"IFDEstablishContext\"\n}" - "\n"); - - QCOMPARE(document.toJson(), jsonToCompare); - } - - - void ifdEstablishContextResponse() - { - const QSharedPointer message(new IfdEstablishContextResponse(QStringLiteral("IFD Reader"), QStringLiteral("/minorResult"))); - - mChecker.receive(message); - - const QJsonDocument document = message->toJson(QStringLiteral("TestContext")); - QVERIFY(document.isObject()); - const QJsonObject object = document.object(); - QCOMPARE(object.size(), 5); - QCOMPARE(object.value(QLatin1String("msg")).toString(), QStringLiteral("IFDEstablishContextResponse")); - QCOMPARE(object.value(QLatin1String("ContextHandle")).toString(), QStringLiteral("TestContext")); - QCOMPARE(object.value(QLatin1String("IFDName")).toString(), QStringLiteral("IFD Reader")); - QCOMPARE(object.value(QLatin1String("ResultMajor")).toString(), QStringLiteral("http://www.bsi.bund.de/ecard/api/1.1/resultmajor#error")); - QCOMPARE(object.value(QLatin1String("ResultMinor")).toString(), QStringLiteral("http://www.bsi.bund.de/ecard/api/1.1/resultminor/minorResult")); - - QCOMPARE(document.toJson(), - QByteArray("{\n" - " \"ContextHandle\": \"TestContext\",\n" - " \"IFDName\": \"IFD Reader\",\n" - " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#error\",\n" - " \"ResultMinor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultminor/minorResult\",\n" - " \"msg\": \"IFDEstablishContextResponse\"\n" - "}\n")); - } - - - void paceCapabilities_data() - { - QTest::addColumn("pace"); - QTest::addColumn("eid"); - QTest::addColumn("esign"); - QTest::addColumn("destroy"); - - QTest::newRow("false") << false << false << false << false; - QTest::newRow("true") << true << true << true << true; - } - - - void paceCapabilities() - { - QFETCH(bool, pace); - QFETCH(bool, eid); - QFETCH(bool, esign); - QFETCH(bool, destroy); - - PaceCapabilities capabilities(pace, eid, esign, destroy); - - QCOMPARE(capabilities.getPace(), pace); - QCOMPARE(capabilities.getEId(), eid); - QCOMPARE(capabilities.getESign(), esign); - QCOMPARE(capabilities.getDestroy(), destroy); - } - - - void getIfdStatus() - { - const QSharedPointer message(new GetIfdStatus("Remote Reader")); - - mChecker.receive(message); - - const QJsonDocument document = message->toJson(QStringLiteral("TestContext")); - QVERIFY(document.isObject()); - const QJsonObject object = document.object(); - QCOMPARE(object.size(), 3); - QCOMPARE(object.value(QLatin1String("msg")).toString(), QStringLiteral("IFDGetStatus")); - QCOMPARE(object.value(QLatin1String("ContextHandle")).toString(), QStringLiteral("TestContext")); - QCOMPARE(object.value(QLatin1String("SlotName")).toString(), QStringLiteral("Remote Reader")); - - QCOMPARE(document.toJson(), - QByteArray("{\n" - " \"ContextHandle\": \"TestContext\",\n" - " \"SlotName\": \"Remote Reader\",\n" - " \"msg\": \"IFDGetStatus\"\n" - "}\n")); - } - - - void ifdStatus() - { - const QSharedPointer message(new MockIfdStatus(QStringLiteral("NFC Reader"), - PaceCapabilities(), - 500, - true, - false)); - - mChecker.receive(message); - - // Json - const QJsonDocument document = message->toJson(QStringLiteral("TestContext")); - QVERIFY(document.isObject()); - - const QJsonObject object = document.object(); - QCOMPARE(object.size(), 9); - QCOMPARE(object.value(QLatin1String("ContextHandle")).toString(), QStringLiteral("TestContext")); - QCOMPARE(object.value(QLatin1String("msg")).toString(), QStringLiteral("IFDStatus")); - QCOMPARE(object.value(QLatin1String("SlotName")).toString(), QStringLiteral("NFC Reader")); - QCOMPARE(object.value(QLatin1String("ConnectedReader")).toBool(), true); - QCOMPARE(object.value(QLatin1String("MaxAPDULength")).toInt(), 500); - QCOMPARE(object.value(QLatin1String("CardAvailable")).toBool(), false); - - const QJsonValue pinCapVal = object.value(QLatin1String("PINCapabilities")); - QVERIFY(pinCapVal.isObject()); - const QJsonObject pinCapObject = pinCapVal.toObject(); - QCOMPARE(pinCapObject.size(), 4); - QCOMPARE(pinCapObject.value(QLatin1String("PACE")).toBool(), false); - QCOMPARE(pinCapObject.value(QLatin1String("eID")).toBool(), false); - QCOMPARE(pinCapObject.value(QLatin1String("eSign")).toBool(), false); - QCOMPARE(pinCapObject.value(QLatin1String("Destroy")).toBool(), false); - - QCOMPARE(document.toJson(), - QByteArray("{\n" - " \"CardAvailable\": false,\n" - " \"ConnectedReader\": true,\n" - " \"ContextHandle\": \"TestContext\",\n" - " \"EFATR\": null,\n" - " \"EFDIR\": null,\n" - " \"MaxAPDULength\": 500,\n" - " \"PINCapabilities\": {\n" - " \"Destroy\": false,\n" - " \"PACE\": false,\n" - " \"eID\": false,\n" - " \"eSign\": false\n" - " },\n" - " \"SlotName\": \"NFC Reader\",\n" - " \"msg\": \"IFDStatus\"\n" - "}\n")); - } - - - void ifdConnect() - { - const QSharedPointer message(new IfdConnect(QStringLiteral("NFC Reader"))); - - mChecker.receive(message); - - // Json - const QJsonDocument document = message->toJson(QStringLiteral("TestContext")); - QVERIFY(document.isObject()); - - const QJsonObject object = document.object(); - QCOMPARE(object.size(), 4); - QCOMPARE(object.value(QLatin1String("msg")).toString(), QStringLiteral("IFDConnect")); - QCOMPARE(object.value(QLatin1String("ContextHandle")).toString(), QStringLiteral("TestContext")); - QCOMPARE(object.value(QLatin1String("SlotName")).toString(), QStringLiteral("NFC Reader")); - QCOMPARE(object.value(QLatin1String("exclusive")).toBool(), true); - - QCOMPARE(document.toJson(), - QByteArray("{\n" - " \"ContextHandle\": \"TestContext\",\n" - " \"SlotName\": \"NFC Reader\",\n" - " \"exclusive\": true,\n" - " \"msg\": \"IFDConnect\"\n" - "}\n")); - } - - - void ifdConnectResponseMsg() - { - const QSharedPointer message(new IfdConnectResponse(QStringLiteral("NFC Reader"), QStringLiteral("/minorResult"))); - - mChecker.receive(message); - - // Json - const QJsonDocument document = message->toJson(QStringLiteral("TestContext")); - QVERIFY(document.isObject()); - - const QJsonObject object = document.object(); - QCOMPARE(object.size(), 5); - QCOMPARE(object.value(QLatin1String("msg")).toString(), QStringLiteral("IFDConnectResponse")); - QCOMPARE(object.value(QLatin1String("ContextHandle")).toString(), QStringLiteral("TestContext")); - QCOMPARE(object.value(QLatin1String("SlotHandle")).toString(), QStringLiteral("NFC Reader")); - QCOMPARE(object.value(QLatin1String("ResultMajor")).toString(), QStringLiteral("http://www.bsi.bund.de/ecard/api/1.1/resultmajor#error")); - QCOMPARE(object.value(QLatin1String("ResultMinor")).toString(), QStringLiteral("http://www.bsi.bund.de/ecard/api/1.1/resultminor/minorResult")); - - QCOMPARE(document.toJson(), - QByteArray("{\n" - " \"ContextHandle\": \"TestContext\",\n" - " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#error\",\n" - " \"ResultMinor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultminor/minorResult\",\n" - " \"SlotHandle\": \"NFC Reader\",\n" - " \"msg\": \"IFDConnectResponse\"\n" - "}\n")); - } - - - void ifdDisconnectCmd() - { - const QSharedPointer message(new IfdDisconnect(QStringLiteral("NFC Reader"))); - - mChecker.receive(message); - - // Json - const QJsonDocument document = message->toJson(QStringLiteral("TestContext")); - QVERIFY(document.isObject()); - - const QJsonObject object = document.object(); - QCOMPARE(object.size(), 3); - QCOMPARE(object.value(QLatin1String("msg")).toString(), QStringLiteral("IFDDisconnect")); - QCOMPARE(object.value(QLatin1String("ContextHandle")).toString(), QStringLiteral("TestContext")); - QCOMPARE(object.value(QLatin1String("SlotHandle")).toString(), QStringLiteral("NFC Reader")); - - QCOMPARE(document.toJson(), - QByteArray("{\n" - " \"ContextHandle\": \"TestContext\",\n" - " \"SlotHandle\": \"NFC Reader\",\n" - " \"msg\": \"IFDDisconnect\"\n" - "}\n")); - } - - - void ifdDisconnectResponseMsg() - { - const QSharedPointer message(new IfdDisconnectResponse(QStringLiteral("NFC Reader"), QStringLiteral("/minorResult"))); - - mChecker.receive(message); - - // Json - const QJsonDocument document = message->toJson(QStringLiteral("TestContext")); - QVERIFY(document.isObject()); - - const QJsonObject object = document.object(); - QCOMPARE(object.size(), 5); - QCOMPARE(object.value(QLatin1String("msg")).toString(), QStringLiteral("IFDDisconnectResponse")); - QCOMPARE(object.value(QLatin1String("ContextHandle")).toString(), QStringLiteral("TestContext")); - QCOMPARE(object.value(QLatin1String("SlotHandle")).toString(), QStringLiteral("NFC Reader")); - QCOMPARE(object.value(QLatin1String("ResultMajor")).toString(), QStringLiteral("http://www.bsi.bund.de/ecard/api/1.1/resultmajor#error")); - QCOMPARE(object.value(QLatin1String("ResultMinor")).toString(), QStringLiteral("http://www.bsi.bund.de/ecard/api/1.1/resultminor/minorResult")); - - QCOMPARE(document.toJson(), - QByteArray("{\n" - " \"ContextHandle\": \"TestContext\",\n" - " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#error\",\n" - " \"ResultMinor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultminor/minorResult\",\n" - " \"SlotHandle\": \"NFC Reader\",\n" - " \"msg\": \"IFDDisconnectResponse\"\n" - "}\n")); - } - - - void ifdTransmitCmd() - { - const QSharedPointer message(new IfdTransmit(QStringLiteral("NFC Reader"), QByteArray::fromHex("00A402022F00"))); - - mChecker.receive(message); - - // Json - const QJsonDocument document = message->toJson(QStringLiteral("TestContext")); - QVERIFY(document.isObject()); - - const QJsonObject object = document.object(); - QCOMPARE(object.size(), 4); - QCOMPARE(object.value(QLatin1String("msg")).toString(), QStringLiteral("IFDTransmit")); - QCOMPARE(object.value(QLatin1String("ContextHandle")).toString(), QStringLiteral("TestContext")); - QCOMPARE(object.value(QLatin1String("SlotHandle")).toString(), QStringLiteral("NFC Reader")); - QVERIFY(object.value(QLatin1String("CommandAPDUs")).isArray()); - const auto& commandApduValue = object.value(QLatin1String("CommandAPDUs")).toArray().at(0); - QVERIFY(commandApduValue.isObject()); - const auto& commandApdu = commandApduValue.toObject(); - QCOMPARE(commandApdu.value(QLatin1String("InputAPDU")).toString().toUpper(), QStringLiteral("00A402022F00")); - QVERIFY(commandApdu.value(QLatin1String("AcceptableStatusCodes")).isNull()); - - QCOMPARE(document.toJson(), - QByteArray("{\n" - " \"CommandAPDUs\": [\n" - " {\n" - " \"AcceptableStatusCodes\": null,\n" - " \"InputAPDU\": \"00a402022f00\"\n" - " }\n" - " ],\n" - " \"ContextHandle\": \"TestContext\",\n" - " \"SlotHandle\": \"NFC Reader\",\n" - " \"msg\": \"IFDTransmit\"\n" - "}\n")); - } - - - void ifdTransmitResponseMsg() - { - const QSharedPointer message(new IfdTransmitResponse(QStringLiteral("NFC Reader"), - QByteArray::fromHex("9000"), - QStringLiteral("/minorResult"))); - - mChecker.receive(message); - - // Json - const QJsonDocument document = message->toJson(QStringLiteral("TestContext")); - QVERIFY(document.isObject()); - - const QJsonObject object = document.object(); - QCOMPARE(object.size(), 6); - QCOMPARE(object.value(QLatin1String("msg")).toString(), QStringLiteral("IFDTransmitResponse")); - QCOMPARE(object.value(QLatin1String("ContextHandle")).toString(), QStringLiteral("TestContext")); - QCOMPARE(object.value(QLatin1String("SlotHandle")).toString(), QStringLiteral("NFC Reader")); - QCOMPARE(object.value(QLatin1String("ResultMajor")).toString(), QStringLiteral("http://www.bsi.bund.de/ecard/api/1.1/resultmajor#error")); - QCOMPARE(object.value(QLatin1String("ResultMinor")).toString(), QStringLiteral("http://www.bsi.bund.de/ecard/api/1.1/resultminor/minorResult")); - QVERIFY(object.value(QLatin1String("ResponseAPDUs")).isArray()); - QCOMPARE(object.value(QLatin1String("ResponseAPDUs")).toArray().at(0).toString().toUpper(), QStringLiteral("9000")); - - QCOMPARE(document.toJson(), - QByteArray("{\n" - " \"ContextHandle\": \"TestContext\",\n" - " \"ResponseAPDUs\": [\n" - " \"9000\"\n" - " ],\n" - " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#error\",\n" - " \"ResultMinor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultminor/minorResult\",\n" - " \"SlotHandle\": \"NFC Reader\",\n" - " \"msg\": \"IFDTransmitResponse\"\n" - "}\n")); - } - - - void ifdEstablishPaceChannelMessage() - { - const QSharedPointer message(new IfdEstablishPaceChannel( - QStringLiteral("My little Reader"), - QByteArray::fromHex("ABCD1234"))); - - mChecker.receive(message); - - const QJsonDocument document = message->toJson(QStringLiteral("TestContext")); - QVERIFY(document.isObject()); - - QCOMPARE(document.toJson(), - QByteArray("{\n" - " \"ContextHandle\": \"TestContext\",\n" - " \"InputData\": \"abcd1234\",\n" - " \"SlotHandle\": \"My little Reader\",\n" - " \"msg\": \"IFDEstablishPACEChannel\"\n" - "}\n")); - } - - - void ifdEstablishPaceChannelMessageResponse() - { - const QSharedPointer message(new IfdEstablishPaceChannelResponse( - QStringLiteral("My little Reader"), - QByteArray::fromHex("ABCD1234"))); - - mChecker.receive(message); - - const QJsonDocument document = message->toJson(QStringLiteral("TestContext")); - QVERIFY(document.isObject()); - - QCOMPARE(document.toJson(), - QByteArray("{\n" - " \"ContextHandle\": \"TestContext\",\n" - " \"OutputData\": \"abcd1234\",\n" - " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#ok\",\n" - " \"ResultMinor\": null,\n" - " \"SlotHandle\": \"My little Reader\",\n" - " \"msg\": \"IFDEstablishPACEChannelResponse\"\n" - "}\n")); - } - - - void ifdErrorMsg() - { - const QSharedPointer message(new IfdError(QStringLiteral("NFC Reader"), QStringLiteral("/minorResult"))); - - mChecker.receive(message); - - // Json - const QJsonDocument document = message->toJson(QStringLiteral("TestContext")); - QVERIFY(document.isObject()); - - const QJsonObject object = document.object(); - QCOMPARE(object.size(), 5); - QCOMPARE(object.value(QLatin1String("msg")).toString(), QStringLiteral("IFDError")); - QCOMPARE(object.value(QLatin1String("ContextHandle")).toString(), QStringLiteral("TestContext")); - QCOMPARE(object.value(QLatin1String("SlotHandle")).toString(), QStringLiteral("NFC Reader")); - QCOMPARE(object.value(QLatin1String("ResultMajor")).toString(), QStringLiteral("http://www.bsi.bund.de/ecard/api/1.1/resultmajor#error")); - QCOMPARE(object.value(QLatin1String("ResultMinor")).toString(), QStringLiteral("http://www.bsi.bund.de/ecard/api/1.1/resultminor/minorResult")); - - QCOMPARE(document.toJson(), - QByteArray("{\n" - " \"ContextHandle\": \"TestContext\",\n" - " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor#error\",\n" - " \"ResultMinor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultminor/minorResult\",\n" - " \"SlotHandle\": \"NFC Reader\",\n" - " \"msg\": \"IFDError\"\n" - "}\n")); - } - - - public: - test_RemoteMessages() - : mChecker() - { - } - - -}; - -QTEST_GUILESS_MAIN(test_RemoteMessages) -#include "test_RemoteMessages.moc" diff --git a/test/qt/remote_device/test_RemoteReaderAdvertiser.cpp b/test/qt/remote_device/test_RemoteReaderAdvertiser.cpp index 0721c4f..ffd6b32 100644 --- a/test/qt/remote_device/test_RemoteReaderAdvertiser.cpp +++ b/test/qt/remote_device/test_RemoteReaderAdvertiser.cpp @@ -9,12 +9,10 @@ #include "DatagramHandler.h" #include "Env.h" #include "messages/Discovery.h" -#include "messages/RemoteMessageParser.h" #include #include - using namespace governikus; @@ -26,7 +24,7 @@ class DatagramHandlerMock public: virtual bool isBound() const override; - virtual bool send(const QJsonDocument& pData) override + virtual bool send(const QByteArray& pData) override { Q_EMIT fireSend(pData); return true; @@ -34,7 +32,7 @@ class DatagramHandlerMock Q_SIGNALS: - void fireSend(const QJsonDocument& pData); + void fireSend(const QByteArray& pData); }; @@ -77,13 +75,7 @@ class test_RemoteReaderAdvertiser QSignalSpy spy(mMock.data(), &DatagramHandlerMock::fireSend); QScopedPointer advertiser(Env::create(ifdName, ifdId, port, pTimerInterval)); - spy.wait(); - spy.wait(); - spy.wait(); - spy.wait(); - spy.wait(); - - QCOMPARE(spy.count(), 5); + QTRY_COMPARE(spy.count(), 5); } @@ -96,15 +88,16 @@ class test_RemoteReaderAdvertiser QSignalSpy spy(mMock.data(), &DatagramHandlerMock::fireSend); QScopedPointer advertiser(Env::create(ifdName, ifdId, port, pTimerInterval)); - spy.wait(); + QTRY_COMPARE(spy.count(), 1); advertiser.reset(); - const auto& offerMsg = RemoteMessageParser().parseDiscovery(spy.at(0).at(0).toJsonDocument()); + const auto& byteArray = spy.at(0).at(0).toByteArray(); + const auto& offerMsg = Discovery(QJsonDocument::fromJson(byteArray).object()); - QCOMPARE(offerMsg->getIfdName(), ifdName); - QCOMPARE(offerMsg->getIfdId(), ifdId); - QCOMPARE(offerMsg->getPort(), port); - QCOMPARE(offerMsg->getSupportedApis(), {IfdVersion::Version::v0}); + QCOMPARE(offerMsg.getIfdName(), ifdName); + QCOMPARE(offerMsg.getIfdId(), ifdId); + QCOMPARE(offerMsg.getPort(), port); + QCOMPARE(offerMsg.getSupportedApis(), {IfdVersion::Version::v0}); } diff --git a/test/qt/remote_device/test_RemoteReaderManagerPlugin.cpp b/test/qt/remote_device/test_RemoteReaderManagerPlugin.cpp new file mode 100644 index 0000000..3c99902 --- /dev/null +++ b/test/qt/remote_device/test_RemoteReaderManagerPlugin.cpp @@ -0,0 +1,492 @@ +/*! + * \brief Unit tests for \ref RemoteReaderManagerPlugIn + * + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ + +#include "plugin/RemoteReaderManagerPlugIn.h" + +#include "Env.h" +#include "messages/IfdConnect.h" +#include "messages/IfdConnect.h" +#include "messages/IfdDisconnect.h" +#include "messages/IfdDisconnect.h" +#include "messages/IfdError.h" +#include "messages/IfdEstablishContext.h" +#include "messages/IfdEstablishPaceChannel.h" +#include "messages/IfdGetStatus.h" +#include "messages/IfdStatus.h" +#include "messages/IfdTransmit.h" +#include "messages/IfdTransmit.h" +#include "MockRemoteDispatcher.h" +#include "RemoteClient.h" + +#include +#include +#include +#include + +using namespace governikus; + + +Q_DECLARE_METATYPE(QSharedPointer ) + + +class MockRemoteClient + : public RemoteClient +{ + Q_OBJECT + + public: + MockRemoteClient() = default; + virtual ~MockRemoteClient() override = default; + + virtual void startDetection() override; + virtual void stopDetection() override; + virtual bool isDetecting() override; + virtual void establishConnection(const QSharedPointer& pEntry, const QString& pPsk) override; + virtual QVector getConnectedDeviceInfos() override; +}; + + +void MockRemoteClient::startDetection() +{ +} + + +void MockRemoteClient::stopDetection() +{ +} + + +bool MockRemoteClient::isDetecting() +{ + return false; +} + + +void MockRemoteClient::establishConnection(const QSharedPointer& pEntry, const QString& pPsk) +{ + Q_UNUSED(pEntry); + Q_UNUSED(pPsk); +} + + +QVector MockRemoteClient::getConnectedDeviceInfos() +{ + return QVector(); +} + + +class test_RemoteReaderManagerPlugIn + : public QObject +{ + Q_OBJECT + + private: + QThread mMainThread; + QThread mNetworkThread; + QSharedPointer mRemoteClient; + QSharedPointer mPlugin; + QSharedPointer mDispatcher1; + QSharedPointer mDispatcher2; + QVector > mClientMessages; + + private Q_SLOTS: + void initTestCase() + { + mMainThread.setObjectName(QStringLiteral("MainThread")); + mNetworkThread.setObjectName(QStringLiteral("NetworkThread")); + + mRemoteClient.reset(new MockRemoteClient()); + mRemoteClient->moveToThread(&mMainThread); + Env::set(RemoteClient::staticMetaObject, mRemoteClient.data()); + + mDispatcher1.reset(new MockRemoteDispatcher()); + mDispatcher1->moveToThread(&mNetworkThread); + + mDispatcher2.reset(new MockRemoteDispatcher()); + mDispatcher2->moveToThread(&mNetworkThread); + + mNetworkThread.start(); + } + + + void cleanupTestCase() + { + mNetworkThread.quit(); + mNetworkThread.wait(); + } + + + void init() + { + // local + mPlugin.reset(new RemoteReaderManagerPlugIn()); + mPlugin->init(); + } + + + void cleanup() + { + // local + } + + + void testStandardValues() + { + QSignalSpy spySend(mDispatcher1.data(), &MockRemoteDispatcher::fireSend); + + QVERIFY(mPlugin->getReaders().isEmpty()); + + Q_EMIT mRemoteClient->fireNewRemoteDispatcher(mDispatcher1); + QTRY_COMPARE(spySend.count(), 1); + + QVERIFY(mPlugin->getReaders().isEmpty()); + QCOMPARE(spySend.size(), 1); + + QSharedPointer result = qvariant_cast >(spySend.takeFirst().at(0)); + QCOMPARE(result->getType(), RemoteCardMessageType::IFDEstablishContext); + } + + + void testSingleDispatcherSingleReaderAddRemoveWithoutCard() + { + QSignalSpy spySend(mDispatcher1.data(), &MockRemoteDispatcher::fireSend); + + Q_EMIT mRemoteClient->fireNewRemoteDispatcher(mDispatcher1); + QTRY_COMPARE(spySend.count(), 1); + spySend.clear(); + + QSharedPointer message; + QSignalSpy spyAdded(mPlugin.data(), &ReaderManagerPlugIn::fireReaderAdded); + QSignalSpy spyRemoved(mPlugin.data(), &ReaderManagerPlugIn::fireReaderRemoved); + + message.reset(new IfdStatus(QStringLiteral("NFC Reader"), PaceCapabilities(), 500, true)); + mDispatcher1->onReceived(message); + QCOMPARE(mPlugin->getReaders().size(), 1); + QCOMPARE(spySend.size(), 0); + QCOMPARE(spyAdded.size(), 1); + QCOMPARE(spyAdded.takeFirst().at(0).toString(), QStringLiteral("NFC Reader#TestContext")); + QCOMPARE(mPlugin->getReaders().size(), 1); + QCOMPARE(mPlugin->getReaders().at(0)->getName(), QStringLiteral("NFC Reader#TestContext")); + QCOMPARE(mPlugin->getReaders().at(0)->getReaderInfo().isBasicReader(), true); + QCOMPARE(mPlugin->getReaders().at(0)->getReaderInfo().getMaxApduLength(), 500); + + message.reset(new IfdStatus(QStringLiteral("NFC Reader"), PaceCapabilities(), 500, false)); + mDispatcher1->onReceived(message); + QCOMPARE(mPlugin->getReaders().size(), 0); + QCOMPARE(spySend.size(), 0); + QCOMPARE(spyAdded.size(), 0); + QCOMPARE(spyRemoved.size(), 1); + QCOMPARE(spyRemoved.takeFirst().at(0).toString(), QStringLiteral("NFC Reader#TestContext")); + + message.reset(new IfdStatus(QStringLiteral("NFC Reader"), PaceCapabilities(), 500, true)); + mDispatcher1->onReceived(message); + QCOMPARE(mPlugin->getReaders().size(), 1); + QCOMPARE(mPlugin->getReaders().at(0)->getName(), QStringLiteral("NFC Reader#TestContext")); + spyAdded.clear(); + + mDispatcher1->onClosed(); + QCOMPARE(spySend.size(), 0); + QCOMPARE(spyAdded.size(), 0); + QCOMPARE(spyRemoved.size(), 1); + QCOMPARE(spyRemoved.takeFirst().at(0).toString(), QStringLiteral("NFC Reader#TestContext")); + QCOMPARE(mPlugin->getReaders().size(), 0); + } + + + void testSingleDispatcherSingleReaderPaceCapabilities() + { + QSignalSpy spySend(mDispatcher1.data(), &MockRemoteDispatcher::fireSend); + + Q_EMIT mRemoteClient->fireNewRemoteDispatcher(mDispatcher1); + QTRY_COMPARE(spySend.count(), 1); + spySend.clear(); + + QSharedPointer message; + QSignalSpy spyAdded(mPlugin.data(), &ReaderManagerPlugIn::fireReaderAdded); + QSignalSpy spyRemoved(mPlugin.data(), &ReaderManagerPlugIn::fireReaderRemoved); + + message.reset(new IfdStatus(QStringLiteral("NFC Reader"), PaceCapabilities(true), 500, true)); + mDispatcher1->onReceived(message); + QCOMPARE(spySend.size(), 0); + QCOMPARE(spyAdded.size(), 1); + QCOMPARE(spyAdded.takeFirst().at(0).toString(), QStringLiteral("NFC Reader#TestContext")); + QCOMPARE(mPlugin->getReaders().size(), 1); + QCOMPARE(mPlugin->getReaders().at(0)->getName(), QStringLiteral("NFC Reader#TestContext")); + QCOMPARE(mPlugin->getReaders().at(0)->getReaderInfo().isBasicReader(), false); + QCOMPARE(mPlugin->getReaders().at(0)->getReaderInfo().getMaxApduLength(), 500); + } + + + void testMultipleDispatcherSingleReaderAddRemoveWithoutCard() + { + QSignalSpy spySend1(mDispatcher1.data(), &MockRemoteDispatcher::fireSend); + QSignalSpy spySend2(mDispatcher2.data(), &MockRemoteDispatcher::fireSend); + + Q_EMIT mRemoteClient->fireNewRemoteDispatcher(mDispatcher1); + Q_EMIT mRemoteClient->fireNewRemoteDispatcher(mDispatcher2); + QTRY_COMPARE(spySend1.count(), 1); + QTRY_COMPARE(spySend2.count(), 1); + spySend1.clear(); + spySend2.clear(); + + QSharedPointer message; + QSignalSpy spyAdded(mPlugin.data(), &ReaderManagerPlugIn::fireReaderAdded); + QSignalSpy spyRemoved(mPlugin.data(), &ReaderManagerPlugIn::fireReaderRemoved); + + message.reset(new IfdStatus(QStringLiteral("NFC Reader 1"), PaceCapabilities(), 500, true)); + mDispatcher1->onReceived(message); + message.reset(new IfdStatus(QStringLiteral("NFC Reader 2"), PaceCapabilities(), 500, true)); + mDispatcher2->onReceived(message); + QCOMPARE(spySend1.size(), 0); + QCOMPARE(spySend2.size(), 0); + QCOMPARE(spyAdded.size(), 2); + QCOMPARE(spyAdded.takeFirst().at(0).toString(), QStringLiteral("NFC Reader 1#TestContext")); + QCOMPARE(spyAdded.takeFirst().at(0).toString(), QStringLiteral("NFC Reader 2#TestContext")); + QCOMPARE(spyRemoved.size(), 0); + QCOMPARE(mPlugin->getReaders().size(), 2); + QCOMPARE(mPlugin->getReaders().at(0)->getName(), QStringLiteral("NFC Reader 1#TestContext")); + QCOMPARE(mPlugin->getReaders().at(1)->getName(), QStringLiteral("NFC Reader 2#TestContext")); + + message.reset(new IfdStatus(QStringLiteral("NFC Reader 1"), PaceCapabilities(), 500, false)); + mDispatcher1->onReceived(message); + QCOMPARE(spySend1.size(), 0); + QCOMPARE(spySend2.size(), 0); + QCOMPARE(spyAdded.size(), 0); + QCOMPARE(spyRemoved.size(), 1); + QCOMPARE(spyRemoved.takeFirst().at(0).toString(), QStringLiteral("NFC Reader 1#TestContext")); + QCOMPARE(mPlugin->getReaders().size(), 1); + QCOMPARE(mPlugin->getReaders().at(0)->getName(), QStringLiteral("NFC Reader 2#TestContext")); + + message.reset(new IfdStatus(QStringLiteral("NFC Reader 1"), PaceCapabilities(), 500, true)); + mDispatcher1->onReceived(message); + QCOMPARE(spySend1.size(), 0); + QCOMPARE(spySend2.size(), 0); + QCOMPARE(spyAdded.size(), 1); + QCOMPARE(spyAdded.takeFirst().at(0).toString(), QStringLiteral("NFC Reader 1#TestContext")); + QCOMPARE(spyRemoved.size(), 0); + QCOMPARE(mPlugin->getReaders().size(), 2); + QCOMPARE(mPlugin->getReaders().at(0)->getName(), QStringLiteral("NFC Reader 1#TestContext")); + QCOMPARE(mPlugin->getReaders().at(1)->getName(), QStringLiteral("NFC Reader 2#TestContext")); + + mDispatcher1->onClosed(); + QCOMPARE(spySend1.size(), 0); + QCOMPARE(spySend2.size(), 0); + QCOMPARE(spyAdded.size(), 0); + QCOMPARE(spyRemoved.size(), 1); + QCOMPARE(spyRemoved.takeFirst().at(0).toString(), QStringLiteral("NFC Reader 1#TestContext")); + QCOMPARE(mPlugin->getReaders().size(), 1); + QCOMPARE(mPlugin->getReaders().at(0)->getName(), QStringLiteral("NFC Reader 2#TestContext")); + + mDispatcher2->onClosed(); + QCOMPARE(spySend1.size(), 0); + QCOMPARE(spySend2.size(), 0); + QCOMPARE(spyAdded.size(), 0); + QCOMPARE(spyRemoved.size(), 1); + QCOMPARE(spyRemoved.takeFirst().at(0).toString(), QStringLiteral("NFC Reader 2#TestContext")); + QCOMPARE(mPlugin->getReaders().size(), 0); + } + + + void testSingleDispatcherSingleReaderChangeCard() + { + QSharedPointer result; + QSignalSpy spySend(mDispatcher1.data(), &MockRemoteDispatcher::fireSend); + + mDispatcher1->setState(MockRemoteDispatcher::DispatcherState::ReaderWithCardError); + Q_EMIT mRemoteClient->fireNewRemoteDispatcher(mDispatcher1); + + QTRY_COMPARE(spySend.count(), 2); + spySend.takeFirst(); + result = qvariant_cast >(spySend.takeFirst().at(0)); + QCOMPARE(result->getType(), RemoteCardMessageType::IFDConnect); + + QSharedPointer message; + message.reset(new IfdStatus(QStringLiteral("NFC Reader"), PaceCapabilities(), 500, true, false)); + mDispatcher1->onReceived(message); + QCOMPARE(mPlugin->getReaders().at(0)->getCard(), nullptr); + + QSignalSpy spyInserted(mPlugin->getReaders().at(0), &Reader::fireCardInserted); + QSignalSpy spyRemoved(mPlugin->getReaders().at(0), &Reader::fireCardRemoved); + QSignalSpy spyChanged(mPlugin->getReaders().at(0), &Reader::fireCardRetryCounterChanged); + QSignalSpy spyUpdated(mPlugin->getReaders().at(0), &Reader::fireReaderPropertiesUpdated); + + message.reset(new IfdStatus(QStringLiteral("NFC Reader"), PaceCapabilities(), 500, true, true)); + mDispatcher1->onReceived(message); + + QTRY_COMPARE(spySend.count(), 1); + result = qvariant_cast >(spySend.takeFirst().at(0)); + QCOMPARE(result->getType(), RemoteCardMessageType::IFDConnect); + QVERIFY(mPlugin->getReaders().at(0)->getCard() != nullptr); + QCOMPARE(mPlugin->getReaders().at(0)->getReaderInfo().getMaxApduLength(), 500); + QVERIFY(mPlugin->getReaders().at(0)->getReaderInfo().hasCard()); + QCOMPARE(spyInserted.size(), 1); + QCOMPARE(spyInserted.takeFirst().at(0).toString(), QStringLiteral("NFC Reader#TestContext")); + QCOMPARE(spyRemoved.size(), 0); + QCOMPARE(spyChanged.size(), 0); + QCOMPARE(spyUpdated.size(), 0); + + message.reset(new IfdStatus(QStringLiteral("NFC Reader"), PaceCapabilities(), 1, true, true)); + mDispatcher1->onReceived(message); + QVERIFY(mPlugin->getReaders().at(0)->getCard() != nullptr); + QCOMPARE(mPlugin->getReaders().at(0)->getReaderInfo().getMaxApduLength(), 1); + QVERIFY(mPlugin->getReaders().at(0)->getReaderInfo().hasCard()); + QCOMPARE(spyInserted.size(), 0); + QCOMPARE(spyRemoved.size(), 0); + QCOMPARE(spyChanged.size(), 0); + QCOMPARE(spyUpdated.size(), 1); + QCOMPARE(spyUpdated.takeFirst().at(0).toString(), QStringLiteral("NFC Reader#TestContext")); + + message.reset(new IfdStatus(QStringLiteral("NFC Reader"), PaceCapabilities(), 1, true, true)); + mDispatcher1->onReceived(message); + QVERIFY(mPlugin->getReaders().at(0)->getCard() != nullptr); + QCOMPARE(mPlugin->getReaders().at(0)->getReaderInfo().getMaxApduLength(), 1); + QVERIFY(mPlugin->getReaders().at(0)->getReaderInfo().hasCard()); + QCOMPARE(spyInserted.size(), 0); + QCOMPARE(spyRemoved.size(), 0); + QCOMPARE(spyChanged.size(), 0); + QCOMPARE(spyUpdated.size(), 0); + + message.reset(new IfdStatus(QStringLiteral("NFC Reader"), PaceCapabilities(), 1, true, false)); + mDispatcher1->onReceived(message); + QCOMPARE(mPlugin->getReaders().at(0)->getCard(), nullptr); + QVERIFY(!mPlugin->getReaders().at(0)->getReaderInfo().hasCard()); + QCOMPARE(spyInserted.size(), 0); + QCOMPARE(spyRemoved.size(), 1); + QCOMPARE(spyRemoved.takeFirst().at(0).toString(), QStringLiteral("NFC Reader#TestContext")); + QCOMPARE(spyChanged.size(), 0); + QCOMPARE(spyUpdated.size(), 0); + + message.reset(new IfdStatus(QStringLiteral("NFC Reader"), PaceCapabilities(), 1, true, true)); + mDispatcher1->onReceived(message); + QTRY_COMPARE(spySend.count(), 1); + result = qvariant_cast >(spySend.takeFirst().at(0)); + QCOMPARE(result->getType(), RemoteCardMessageType::IFDConnect); + QVERIFY(mPlugin->getReaders().at(0)->getCard() != nullptr); + QCOMPARE(mPlugin->getReaders().at(0)->getReaderInfo().getMaxApduLength(), 1); + QVERIFY(mPlugin->getReaders().at(0)->getReaderInfo().hasCard()); + QCOMPARE(spyInserted.size(), 1); + QCOMPARE(spyInserted.takeFirst().at(0).toString(), QStringLiteral("NFC Reader#TestContext")); + QCOMPARE(spyRemoved.size(), 0); + QCOMPARE(spyChanged.size(), 0); + QCOMPARE(spyUpdated.size(), 0); + } + + + void testSingleDispatcherSingleReaderCardCommunication() + { + QSharedPointer result; + QSignalSpy spySend(mDispatcher1.data(), &MockRemoteDispatcher::fireSend); + + mDispatcher1->setState(MockRemoteDispatcher::DispatcherState::ReaderWithCard); + Q_EMIT mRemoteClient->fireNewRemoteDispatcher(mDispatcher1); + + QTRY_COMPARE(spySend.count(), 4); + spySend.takeFirst(); + result = qvariant_cast >(spySend.takeFirst().at(0)); + QCOMPARE(result->getType(), RemoteCardMessageType::IFDConnect); + result = qvariant_cast >(spySend.takeFirst().at(0)); + QCOMPARE(result->getType(), RemoteCardMessageType::IFDTransmit); + result = qvariant_cast >(spySend.takeFirst().at(0)); + QCOMPARE(result->getType(), RemoteCardMessageType::IFDDisconnect); + + QCOMPARE(mPlugin->getReaders().size(), 1); + Card* card = mPlugin->getReaders().at(0)->getCard(); + QVERIFY(card != nullptr); + + QCOMPARE(card->connect(), CardReturnCode::OK); + QTRY_COMPARE(spySend.count(), 1); + result = qvariant_cast >(spySend.takeFirst().at(0)); + QCOMPARE(result->getType(), RemoteCardMessageType::IFDConnect); + QCOMPARE(static_cast(result.data())->getSlotName(), QStringLiteral("NFC Reader")); + + QCOMPARE(card->disconnect(), CardReturnCode::OK); + QTRY_COMPARE(spySend.count(), 1); + result = qvariant_cast >(spySend.takeFirst().at(0)); + QCOMPARE(result->getType(), RemoteCardMessageType::IFDDisconnect); + QCOMPARE(static_cast(result.data())->getSlotHandle(), QStringLiteral("NFC Reader")); + + CommandApdu cmd(QByteArray("ping")); + ResponseApdu res; + QCOMPARE(card->transmit(cmd, res), CardReturnCode::OK); + QTRY_COMPARE(spySend.count(), 1); + result = qvariant_cast >(spySend.takeFirst().at(0)); + QCOMPARE(result->getType(), RemoteCardMessageType::IFDTransmit); + QCOMPARE(static_cast(result.data())->getSlotHandle(), QStringLiteral("NFC Reader")); + QCOMPARE(static_cast(result.data())->getInputApdu(), QByteArray("ping")); + QCOMPARE(res.getBuffer(), QByteArray("pong")); + + mDispatcher1->setState(MockRemoteDispatcher::DispatcherState::ReaderWithCardError); + + QCOMPARE(card->connect(), CardReturnCode::COMMAND_FAILED); + QTRY_COMPARE(spySend.count(), 1); + result = qvariant_cast >(spySend.takeFirst().at(0)); + QCOMPARE(result->getType(), RemoteCardMessageType::IFDConnect); + QCOMPARE(static_cast(result.data())->getSlotName(), QStringLiteral("NFC Reader")); + + QCOMPARE(card->disconnect(), CardReturnCode::COMMAND_FAILED); + QTRY_COMPARE(spySend.count(), 1); + result = qvariant_cast >(spySend.takeFirst().at(0)); + QCOMPARE(result->getType(), RemoteCardMessageType::IFDDisconnect); + QCOMPARE(static_cast(result.data())->getSlotHandle(), QStringLiteral("NFC Reader")); + + res.setBuffer(QByteArray()); + QCOMPARE(card->transmit(cmd, res), CardReturnCode::COMMAND_FAILED); + QTRY_COMPARE(spySend.count(), 1); + result = qvariant_cast >(spySend.takeFirst().at(0)); + QCOMPARE(result->getType(), RemoteCardMessageType::IFDTransmit); + QCOMPARE(static_cast(result.data())->getSlotHandle(), QStringLiteral("NFC Reader")); + QCOMPARE(static_cast(result.data())->getInputApdu(), QByteArray("ping")); + QCOMPARE(res.getBuffer(), QByteArray()); + } + + + void testUnexpectedMessagesCauseAnIfdErrorMessage() + { + QSignalSpy spySend(mDispatcher1.data(), &MockRemoteDispatcher::fireSend); + + mDispatcher1->setState(MockRemoteDispatcher::DispatcherState::WithoutReader); + Q_EMIT mRemoteClient->fireNewRemoteDispatcher(mDispatcher1); + QTRY_COMPARE(spySend.count(), 1); + spySend.clear(); + + const QVector > clientMessages({ + QSharedPointer(new IfdEstablishContext(IfdVersion::Version::v0, "MAC-MINI")), + QSharedPointer(new IfdGetStatus("Remote Reader")), + QSharedPointer(new IfdConnect("NFC Reader")), + QSharedPointer(new IfdDisconnect("NFC Reader")), + QSharedPointer(new IfdTransmit("NFC Reader", "00A402022F00")), + QSharedPointer(new IfdEstablishPaceChannel("NFC Reader", "abcd1234")) + } + ); + for (const auto& clientMessage : clientMessages) + { + QMetaObject::invokeMethod(mDispatcher1.data(), [ = ] { + mDispatcher1->onReceived(clientMessage); + }, Qt::QueuedConnection); + QTRY_COMPARE(spySend.count(), 1); + const QList& arguments = spySend.last(); + + const QVariant remoteMessageVariant = arguments.at(0); + QVERIFY(remoteMessageVariant.canConvert >()); + const QSharedPointer message = remoteMessageVariant.value >(); + const QSharedPointer errorMessage = message.dynamicCast(); + + QVERIFY(!errorMessage.isNull()); + QCOMPARE(errorMessage->getType(), RemoteCardMessageType::IFDError); + QCOMPARE(errorMessage->getContextHandle(), QString()); + QCOMPARE(errorMessage->getSlotHandle(), QString()); + QVERIFY(errorMessage->resultHasError()); + QCOMPARE(errorMessage->getResultMinor(), ECardApiResult::Minor::AL_Unkown_API_Function); + + spySend.clear(); + } + } + + +}; + +QTEST_GUILESS_MAIN(test_RemoteReaderManagerPlugIn) +#include "test_RemoteReaderManagerPlugin.moc" diff --git a/test/qt/remote_device/test_RemoteServer.cpp b/test/qt/remote_device/test_RemoteServer.cpp deleted file mode 100644 index aef4409..0000000 --- a/test/qt/remote_device/test_RemoteServer.cpp +++ /dev/null @@ -1,212 +0,0 @@ -/*! - * \brief Unit tests for \ref RemoteServerImpl - * - * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany - */ - -#include "Env.h" -#include "RemoteReaderAdvertiser.h" -#include "RemoteServer.h" -#include "RemoteWebSocketServer.h" - -#include -#include -#include - - -using namespace governikus; - -class RemoteWebSocketServerMock - : public RemoteWebSocketServer -{ - Q_OBJECT - - public: - QString mServerName; - bool mPairing; - bool mListening = false, mConnected = false; - QSharedPointer mEmptyHandler; - - bool isListening() const override - { - return mListening; - } - - - bool isConnected() const override - { - return mConnected; - } - - - void setConnected(bool pConnected) - { - if (mConnected != pConnected) - { - mConnected = pConnected; - Q_EMIT fireConnectedChanged(mConnected); - } - } - - - bool listen(const QString& pServerName) override - { - if (mListening) - { - return false; - } - mServerName = pServerName; - mListening = true; - return true; - } - - - void close() override - { - mListening = false; - mServerName = QString(); - mPairing = false; - } - - - QString getServerName() const override - { - return mServerName; - } - - - quint16 getServerPort() const override - { - return 1; - } - - - void setPairing(bool pEnabled = true) override - { - mPairing = pEnabled; - } - - - QSslCertificate getCurrentCertificate() const override - { - return QSslCertificate(); - } - - - const QSharedPointer& getMessageHandler() const override - { - return mEmptyHandler; - } - - -}; - - -class RemoteReaderAdvertiserMock - : public RemoteReaderAdvertiser -{ - Q_OBJECT - - public: - const QString mIfdName; - const QString mIfdId; - const quint16 mPort; - - RemoteReaderAdvertiserMock(const QString& pIfdName, const QString& pIfdId, quint16 pPort) - : RemoteReaderAdvertiser() - , mIfdName(pIfdName) - , mIfdId(pIfdId) - , mPort(pPort) - { - } - - - virtual ~RemoteReaderAdvertiserMock(); -}; - - -RemoteReaderAdvertiserMock::~RemoteReaderAdvertiserMock() -{ -} - - -class test_RemoteServer - : public QObject -{ - Q_OBJECT - - QPointer mAdvertiserMock; - QPointer mWebSocketMock; - QScopedPointer mServer; - - private Q_SLOTS: - void init() - { - std::function creator = [this](const QString& pIfdName, const QString& pIfdId, quint16& pPort){ - mAdvertiserMock = new RemoteReaderAdvertiserMock(pIfdName, pIfdId, pPort); - return mAdvertiserMock; - }; - Env::setCreator(creator); - std::function creator2 = [this](){ - mWebSocketMock = new RemoteWebSocketServerMock; - return mWebSocketMock; - }; - Env::setCreator(creator2); - mServer.reset(new RemoteServerImpl()); - } - - - void startStop() - { - QVERIFY(!mAdvertiserMock); - QVERIFY(!mWebSocketMock->isListening()); - - QVERIFY(mServer->start(QStringLiteral("ServerName"))); - QVERIFY(mAdvertiserMock); - QVERIFY(mWebSocketMock->isListening()); - - mServer->stop(); - QVERIFY(!mAdvertiserMock); - QVERIFY(!mWebSocketMock->isListening()); - } - - - void serverName() - { - mServer->start(QStringLiteral("ServerName")); - QCOMPARE(mAdvertiserMock->mIfdName, QStringLiteral("ServerName")); - QCOMPARE(mWebSocketMock->mServerName, QStringLiteral("ServerName")); - } - - - void port() - { - mServer->start(QStringLiteral("ServerName")); - QCOMPARE(mAdvertiserMock->mPort, mWebSocketMock->getServerPort()); - } - - - void stopAdvertisingOnConnection() - { - mServer->start(QStringLiteral("ServerName")); - mWebSocketMock->setConnected(true); - - QVERIFY(!mAdvertiserMock); - } - - - void startAdvertisingOnConnectionClosed() - { - mServer->start(QStringLiteral("ServerName")); - mWebSocketMock->setConnected(true); - mWebSocketMock->setConnected(false); - - QVERIFY(mAdvertiserMock); - } - - -}; - - -QTEST_GUILESS_MAIN(test_RemoteServer) -#include "test_RemoteServer.moc" diff --git a/test/qt/remote_device/test_RemoteServerImpl.cpp b/test/qt/remote_device/test_RemoteServerImpl.cpp new file mode 100644 index 0000000..fcaa6b0 --- /dev/null +++ b/test/qt/remote_device/test_RemoteServerImpl.cpp @@ -0,0 +1,213 @@ +/*! + * \brief Unit tests for \ref RemoteServerImpl + * + * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany + */ +#include "RemoteServerImpl.h" + +#include "Env.h" +#include "RemoteReaderAdvertiser.h" +#include "RemoteServer.h" +#include "RemoteWebSocketServer.h" + +#include +#include +#include + + +using namespace governikus; + +class RemoteWebSocketServerMock + : public RemoteWebSocketServer +{ + Q_OBJECT + + public: + QString mServerName; + bool mPairing; + bool mListening = false, mConnected = false; + QSharedPointer mEmptyHandler; + + bool isListening() const override + { + return mListening; + } + + + bool isConnected() const override + { + return mConnected; + } + + + void setConnected(bool pConnected) + { + if (mConnected != pConnected) + { + mConnected = pConnected; + Q_EMIT fireConnectedChanged(mConnected); + } + } + + + bool listen(const QString& pServerName) override + { + if (mListening) + { + return false; + } + mServerName = pServerName; + mListening = true; + return true; + } + + + void close() override + { + mListening = false; + mServerName = QString(); + mPairing = false; + } + + + QString getServerName() const override + { + return mServerName; + } + + + quint16 getServerPort() const override + { + return 1; + } + + + void setPairing(bool pEnabled = true) override + { + mPairing = pEnabled; + } + + + QSslCertificate getCurrentCertificate() const override + { + return QSslCertificate(); + } + + + const QSharedPointer& getMessageHandler() const override + { + return mEmptyHandler; + } + + +}; + + +class RemoteReaderAdvertiserMock + : public RemoteReaderAdvertiser +{ + Q_OBJECT + + public: + const QString mIfdName; + const QString mIfdId; + const quint16 mPort; + + RemoteReaderAdvertiserMock(const QString& pIfdName, const QString& pIfdId, quint16 pPort) + : RemoteReaderAdvertiser() + , mIfdName(pIfdName) + , mIfdId(pIfdId) + , mPort(pPort) + { + } + + + virtual ~RemoteReaderAdvertiserMock(); +}; + + +RemoteReaderAdvertiserMock::~RemoteReaderAdvertiserMock() +{ +} + + +class test_RemoteServerImpl + : public QObject +{ + Q_OBJECT + + QPointer mAdvertiserMock; + QPointer mWebSocketMock; + QScopedPointer mServer; + + private Q_SLOTS: + void init() + { + std::function creator = [this](const QString& pIfdName, const QString& pIfdId, quint16& pPort){ + mAdvertiserMock = new RemoteReaderAdvertiserMock(pIfdName, pIfdId, pPort); + return mAdvertiserMock; + }; + Env::setCreator(creator); + std::function creator2 = [this](){ + mWebSocketMock = new RemoteWebSocketServerMock; + return mWebSocketMock; + }; + Env::setCreator(creator2); + mServer.reset(new RemoteServerImpl()); + } + + + void startStop() + { + QVERIFY(!mAdvertiserMock); + QVERIFY(!mWebSocketMock->isListening()); + + QVERIFY(mServer->start(QStringLiteral("ServerName"))); + QVERIFY(mAdvertiserMock); + QVERIFY(mWebSocketMock->isListening()); + + mServer->stop(); + QVERIFY(!mAdvertiserMock); + QVERIFY(!mWebSocketMock->isListening()); + } + + + void serverName() + { + mServer->start(QStringLiteral("ServerName")); + QCOMPARE(mAdvertiserMock->mIfdName, QStringLiteral("ServerName")); + QCOMPARE(mWebSocketMock->mServerName, QStringLiteral("ServerName")); + } + + + void port() + { + mServer->start(QStringLiteral("ServerName")); + QCOMPARE(mAdvertiserMock->mPort, mWebSocketMock->getServerPort()); + } + + + void stopAdvertisingOnConnection() + { + mServer->start(QStringLiteral("ServerName")); + mWebSocketMock->setConnected(true); + + QVERIFY(!mAdvertiserMock); + } + + + void startAdvertisingOnConnectionClosed() + { + mServer->start(QStringLiteral("ServerName")); + mWebSocketMock->setConnected(true); + mWebSocketMock->setConnected(false); + + QVERIFY(mAdvertiserMock); + } + + +}; + + +QTEST_GUILESS_MAIN(test_RemoteServerImpl) +#include "test_RemoteServerImpl.moc" diff --git a/test/qt/remote_device/test_RemoteTlsServer.cpp b/test/qt/remote_device/test_RemoteTlsServer.cpp index 306eab7..327cf2e 100644 --- a/test/qt/remote_device/test_RemoteTlsServer.cpp +++ b/test/qt/remote_device/test_RemoteTlsServer.cpp @@ -7,19 +7,13 @@ #include "RemoteTlsServer.h" #include "AppSettings.h" -#include "Env.h" #include "KeyPair.h" -#include "RemoteHelper.h" #include "SecureStorage.h" #include #include #include -#if defined(Q_CC_CLANG) && Q_CC_CLANG < 350 -QT_WARNING_DISABLE_CLANG("-Wshadow") // QTRY_COMPARE -#endif - using namespace governikus; Q_DECLARE_METATYPE(SecureStorage::TlsSuite) @@ -49,7 +43,7 @@ class test_RemoteTlsServer QFETCH(bool, serverPairing); QFETCH(SecureStorage::TlsSuite, clientConfig); - auto& settings = AppSettings::getInstance().getRemoteServiceSettings(); + auto& settings = Env::getSingleton()->getRemoteServiceSettings(); settings.setTrustedCertificates({pair.getCertificate()}); RemoteTlsServer server; server.setPairing(serverPairing); @@ -136,7 +130,7 @@ class test_RemoteTlsServer void tryReconnectWithoutPairedDeviceAndReconnectedPaired() { - auto& settings = AppSettings::getInstance().getRemoteServiceSettings(); + auto& settings = Env::getSingleton()->getRemoteServiceSettings(); settings.setTrustedCertificates({}); RemoteTlsServer server; server.listen(); @@ -157,7 +151,6 @@ class test_RemoteTlsServer QCOMPARE(newConnection.count(), 0); } - QSslSocket clientPaired; config.setCaCertificates({settings.getCertificate()}); clientPaired.setSslConfiguration(config); @@ -183,10 +176,10 @@ class test_RemoteTlsServer void pairDeviceAndReconnect() { - auto& settings = AppSettings::getInstance().getRemoteServiceSettings(); + auto& settings = Env::getSingleton()->getRemoteServiceSettings(); settings.setTrustedCertificates({}); requiredPskForPairing(); - QVERIFY(RemoteHelper::checkAndGenerateKey()); + QVERIFY(settings.checkAndGenerateKey()); RemoteTlsServer server; server.listen(); diff --git a/test/qt/remote_device/test_RemoteWebSocketServer.cpp b/test/qt/remote_device/test_RemoteWebSocketServer.cpp index ffd7b1c..8e98023 100644 --- a/test/qt/remote_device/test_RemoteWebSocketServer.cpp +++ b/test/qt/remote_device/test_RemoteWebSocketServer.cpp @@ -7,20 +7,14 @@ #include "RemoteWebSocketServer.h" #include "AppSettings.h" -#include "Env.h" #include "KeyPair.h" #include "NetworkManager.h" #include "SecureStorage.h" -#include "ServerMessageHandler.h" #include #include #include -#if defined(Q_CC_CLANG) && Q_CC_CLANG < 350 -QT_WARNING_DISABLE_CLANG("-Wshadow") // QTRY_COMPARE -#endif - using namespace governikus; @@ -285,8 +279,7 @@ class test_RemoteWebSocketServer mServer->setPairing(); client.open(QString("wss://127.0.0.1:").append(QString::number(mServer->getServerPort()))); - serverConnectedSpy.wait(); - QCOMPARE(serverConnectedSpy.count(), 1); + QTRY_COMPARE(serverConnectedSpy.count(), 1); QVERIFY(serverConnectedSpy[0][0].toBool()); QVERIFY(mServer->isConnected()); diff --git a/test/qt/remote_device/test_ServerMessageHandler.cpp b/test/qt/remote_device/test_ServerMessageHandler.cpp index 37d3091..b85d102 100644 --- a/test/qt/remote_device/test_ServerMessageHandler.cpp +++ b/test/qt/remote_device/test_ServerMessageHandler.cpp @@ -1,23 +1,35 @@ /*! - * \brief Unit tests for \ref test_ServerMessageHandlerImpl + * \brief Unit tests for \ref ServerMessageHandlerImpl * * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany */ #include "ServerMessageHandler.h" +#include "AppSettings.h" #include "LogHandler.h" +#include "messages/IfdConnect.h" #include "messages/IfdConnectResponse.h" +#include "messages/IfdDisconnect.h" +#include "messages/IfdDisconnectResponse.h" #include "messages/IfdError.h" #include "messages/IfdEstablishContext.h" #include "messages/IfdEstablishContextResponse.h" -#include "messages/RemoteMessageParser.h" +#include "messages/IfdEstablishPaceChannel.h" +#include "messages/IfdEstablishPaceChannelResponse.h" +#include "messages/IfdStatus.h" +#include "messages/IfdTransmit.h" +#include "messages/IfdTransmitResponse.h" + #include "MockDataChannel.h" +#include "MockReaderManagerPlugIn.h" #include "TestFileHelper.h" #include #include +Q_IMPORT_PLUGIN(MockReaderManagerPlugIn) + using namespace governikus; @@ -29,92 +41,55 @@ class test_ServerMessageHandler private: QSharedPointer mDataChannel; - const RemoteMessageParser mParser; - QVector makeServerMessages(const QByteArray& pContextHandle) + + void removeReaderAndConsumeMessages(const QString& pReaderName) { - QVector serverMessages; - - auto m = [&](const char* pJsonByteData) -> QByteArray - { - QByteArray result(pJsonByteData); - result.replace(QByteArray("contextHandleToReplace"), pContextHandle); - - return result; - }; - - serverMessages - << m("{\n" - " \"ContextHandle\": \"contextHandleToReplace\",\n" - " \"msg\": \"IFDStatus\",\n" - " \"ConnectedReader\": true,\n" - " \"IFDName\": \"Remote Reader\",\n" - " \"SlotName\": \"NFC Reader\",\n" - " \"PINCapabilities\": {\n" - " \"Destroy\": false,\n" - " \"PACE\": false,\n" - " \"eID\": false,\n" - " \"eSign\": false\n" - " },\n" - " \"MaxAPDULength\": 500,\n" - " \"CardAvailable\": false\n" - "}") - << - m("{\n" - " \"ContextHandle\": \"contextHandleToReplace\",\n" - " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor/resultmajor#error\",\n" - " \"ResultMinor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultminor/minorResult\",\n" - " \"msg\": \"IFDConnectResponse\",\n" - " \"SlotHandle\": \"NFC Reader\"\n" - "}\n") - << - m("{\n" - " \"ContextHandle\": \"contextHandleToReplace\",\n" - " \"SlotHandle\": \"NFC Reader\",\n" - " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor/resultmajor#error\",\n" - " \"ResultMinor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultminor/minorResult\",\n" - " \"msg\": \"IFDDisconnectResponse\"\n" - "}\n") - << - m("{\n" - " \"ContextHandle\": \"contextHandleToReplace\",\n" - " \"SlotHandle\": \"NFC Reader\",\n" - " \"ResponseAPDUs\": [\n" - " \"9000\"\n" - " ],\n" - " \"msg\": \"IFDTransmitResponse\",\n" - " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor/resultmajor#error\",\n" - " \"ResultMinor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultminor/minorResult\"\n" - "}") - << - m("{\n" - " \"ContextHandle\": \"contextHandleToReplace\",\n" - " \"OutputData\": \"abcd1234\",\n" - " \"ResultMajor\": \"http://www.bsi.bund.de/ecard/api/1.1/resultmajor/resultmajor#ok\",\n" - " \"ResultMinor\": null,\n" - " \"SlotHandle\": \"My little Reader\",\n" - " \"msg\": \"IFDEstablishPACEChannelResponse\"\n" - "}\n"); - - return serverMessages; + QSignalSpy sendSpy(mDataChannel.data(), &MockDataChannel::fireSend); + MockReaderManagerPlugIn::getInstance().removeReader(pReaderName); + QTRY_COMPARE(sendSpy.count(), 1); } - void waitForSignals(QSignalSpy* const pSpy, const int pExpectedCount, const int pTimeoutMs) + void ensureContext(QString& pContextHandle) { - for (int tryCount = 0; pSpy->count() < pExpectedCount && tryCount < pExpectedCount; ++tryCount) - { - pSpy->wait(pTimeoutMs); - } - QCOMPARE(pSpy->count(), pExpectedCount); + QSignalSpy sendSpy(mDataChannel.data(), &MockDataChannel::fireSend); + + const QByteArray establishContextMsg("{\n" + " \"msg\": \"IFDEstablishContext\",\n" + " \"Protocol\": \"IFDInterface_WebSocket_v0\",\n" + " \"UDName\": \"MAC-MINI\"\n" + "}"); + + mDataChannel->onReceived(establishContextMsg); + + QTRY_COMPARE(sendSpy.count(), 1); + const QList& establishContextResponseArguments = sendSpy.last(); + + const QVariant establishContextResponseVariant = establishContextResponseArguments.at(0); + QVERIFY(establishContextResponseVariant.canConvert()); + const IfdEstablishContextResponse establishContextResponse(RemoteMessage::parseByteArray(establishContextResponseVariant.toByteArray())); + QVERIFY(!establishContextResponse.isIncomplete()); + QCOMPARE(establishContextResponse.getType(), RemoteCardMessageType::IFDEstablishContextResponse); + + pContextHandle = establishContextResponse.getContextHandle(); + QVERIFY(!pContextHandle.isEmpty()); } private Q_SLOTS: void initTestCase() { - LogHandler::getInstance().init(); + Env::getSingleton()->init(); + const auto readerManager = Env::getSingleton(); + readerManager->init(); + readerManager->getPlugInInfos(); // just to wait until initialization finished + } + + void cleanupTestCase() + { + Env::getSingleton()->shutdown(); } @@ -126,102 +101,505 @@ class test_ServerMessageHandler void cleanup() { - LogHandler::getInstance().resetBacklog(); + Env::getSingleton()->resetBacklog(); } void checkLogOnInvalidContext() { - QSignalSpy spyLog(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy spyLog(Env::getSingleton(), &LogHandler::fireLog); ServerMessageHandlerImpl serverMessageHandler(mDataChannel); IfdConnectResponse unexpectedMsg(QStringLiteral("RemoteReader")); - mDataChannel->onReceived(unexpectedMsg.toJson(QStringLiteral("invalidConextHandle")).toJson()); + mDataChannel->onReceived(unexpectedMsg.toByteArray(QStringLiteral("invalidConextHandle"))); QVERIFY(TestFileHelper::containsLog(spyLog, QLatin1String("Invalid context handle received"))); } void checkLogOnUnexpectedMessageWithContext() { - QSignalSpy spyLog(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy spyLog(Env::getSingleton(), &LogHandler::fireLog); QSignalSpy spyContextHandle(mDataChannel.data(), &MockDataChannel::fireSend); ServerMessageHandlerImpl serverMessageHandler(mDataChannel); IfdEstablishContext establishContext(QStringLiteral("IFDInterface_WebSocket_v0"), DeviceInfo::getName()); - mDataChannel->onReceived(establishContext.toJson(QString()).toJson()); + mDataChannel->onReceived(establishContext.toByteArray(QString())); const QJsonDocument& doc = QJsonDocument::fromJson(spyContextHandle.at(0).at(0).toByteArray()); const QString& contextHandle = doc.object().value(QLatin1String("ContextHandle")).toString(); IfdConnectResponse unexpectedMsg(QStringLiteral("RemoteReader")); - mDataChannel->onReceived(unexpectedMsg.toJson(contextHandle).toJson()); + mDataChannel->onReceived(unexpectedMsg.toByteArray(contextHandle)); QVERIFY(TestFileHelper::containsLog(spyLog, QLatin1String("Received an unexpected message of type: IFDConnectResponse"))); } void checkLogOnInvalidMessage() { - QSignalSpy spyLog(&LogHandler::getInstance(), &LogHandler::fireLog); + QSignalSpy spyLog(Env::getSingleton(), &LogHandler::fireLog); ServerMessageHandlerImpl serverMessageHandler(mDataChannel); mDataChannel->onReceived("{\n" " \"ContextHandle\": \"TestContext\",\n" " \"msg\": \"RANDOM_STUFF\"\n" "}\n"); - QVERIFY(TestFileHelper::containsLog(spyLog, QLatin1String("Invalid message received"))); + QVERIFY(TestFileHelper::containsLog(spyLog, QLatin1String("Invalid messageType received"))); } void testUnexpectedMessagesCauseAnIfdErrorMessage() { - QSignalSpy sendSpy(mDataChannel.data(), &MockDataChannel::fireSend); ServerMessageHandlerImpl serverMessageHandler(mDataChannel); + QString contextHandle; + ensureContext(contextHandle); - const QByteArray establishContextMsg("{\n" - " \"msg\": \"IFDEstablishContext\",\n" - " \"Protocol\": \"IFDInterface_WebSocket_v0\",\n" - " \"UDName\": \"MAC-MINI\"\n" - "}"); - - mDataChannel->onReceived(establishContextMsg); - - waitForSignals(&sendSpy, 1, 1000); - const QList& establishContextResponseArguments = sendSpy.last(); - - const QVariant establishContextResponseVariant = establishContextResponseArguments.at(0); - QVERIFY(establishContextResponseVariant.canConvert()); - const IfdEstablishContextResponse establishContextResponse(RemoteMessage::parseByteArray(establishContextResponseVariant.toByteArray())); - QVERIFY(establishContextResponse.isValid()); - QCOMPARE(establishContextResponse.getType(), RemoteCardMessageType::IFDEstablishContextResponse); - - const QByteArray contextHandle = establishContextResponse.getContextHandle().toUtf8(); - QVERIFY(!contextHandle.isEmpty());\ + QSignalSpy sendSpy(mDataChannel.data(), &MockDataChannel::fireSend); // We have a context handle: send unexpected messages and verify that an error message is sent back. sendSpy.clear(); - const QVector serverMessages = makeServerMessages(contextHandle); - for (const QByteArray& serverMessage : serverMessages) + + const QByteArrayList serverMessages({ + IfdStatus("NFC Reader", PaceCapabilities(), 500, true).toByteArray(contextHandle), + IfdConnectResponse("NFC Reader").toByteArray(contextHandle), + IfdDisconnectResponse("NFC Reader").toByteArray(contextHandle), + IfdTransmitResponse("NFC Reader", "9000").toByteArray(contextHandle), + IfdEstablishPaceChannelResponse("My little Reader", "abcd1234").toByteArray(contextHandle) + }); + for (const auto& serverMessage : serverMessages) { mDataChannel->onReceived(serverMessage); - waitForSignals(&sendSpy, 1, 1000); + QTRY_COMPARE(sendSpy.count(), 1); const QList& errorMessageArguments = sendSpy.last(); const QVariant errorMessageVariant = errorMessageArguments.at(0); QVERIFY(errorMessageVariant.canConvert()); const IfdError errorMessage(RemoteMessage::parseByteArray(errorMessageVariant.toByteArray())); - QVERIFY(errorMessage.isValid()); + QVERIFY(!errorMessage.isIncomplete()); QCOMPARE(errorMessage.getType(), RemoteCardMessageType::IFDError); - QCOMPARE(errorMessage.getContextHandle(), QString::fromUtf8(contextHandle)); + QCOMPARE(errorMessage.getContextHandle(), contextHandle); QCOMPARE(errorMessage.getSlotHandle(), QString()); QVERIFY(errorMessage.resultHasError()); - QCOMPARE(errorMessage.getResultMinor(), QStringLiteral("http://www.bsi.bund.de/ecard/api/1.1/resultminor/al/common#unknownAPIFunction")); + QCOMPARE(errorMessage.getResultMinor(), ECardApiResult::Minor::AL_Unkown_API_Function); sendSpy.clear(); } } + void ifdConnectForUnconnectedReaderSendsIFDL_UnknownSlot() + { + ServerMessageHandlerImpl serverMessageHandler(mDataChannel); + QString contextHandle; + ensureContext(contextHandle); + + QSignalSpy sendSpy(mDataChannel.data(), &MockDataChannel::fireSend); + const QByteArray ifdConnectMsg = IfdConnect(QStringLiteral("test-reader"), true).toByteArray(contextHandle); + mDataChannel->onReceived(ifdConnectMsg); + + QTRY_COMPARE(sendSpy.count(), 1); + + const QList& connectResponseArguments = sendSpy.last(); + const QVariant connectResponseVariant = connectResponseArguments.at(0); + QVERIFY(connectResponseVariant.canConvert()); + + const IfdConnectResponse connectResponse(RemoteMessage::parseByteArray(connectResponseVariant.toByteArray())); + QVERIFY(!connectResponse.isIncomplete()); + QCOMPARE(connectResponse.getType(), RemoteCardMessageType::IFDConnectResponse); + QVERIFY(!connectResponse.getContextHandle().isEmpty()); + QCOMPARE(connectResponse.getSlotHandle(), QStringLiteral("test-reader")); + QVERIFY(connectResponse.resultHasError()); + QCOMPARE(connectResponse.getResultMinor(), ECardApiResult::Minor::IFDL_UnknownSlot); + } + + + void ifdConnectForReaderWithoutCardSendsAL_Unknown_Error() + { + ServerMessageHandlerImpl serverMessageHandler(mDataChannel); + QString contextHandle; + ensureContext(contextHandle); + + QSignalSpy sendSpy(mDataChannel.data(), &MockDataChannel::fireSend); + MockReaderManagerPlugIn::getInstance().addReader("test-reader"); + QTRY_COMPARE(sendSpy.count(), 1); + sendSpy.clear(); + + const QByteArray ifdConnectMsg = IfdConnect(QStringLiteral("test-reader"), true).toByteArray(contextHandle); + mDataChannel->onReceived(ifdConnectMsg); + + QTRY_COMPARE(sendSpy.count(), 1); + + const QList& connectResponseArguments = sendSpy.last(); + const QVariant connectResponseVariant = connectResponseArguments.at(0); + QVERIFY(connectResponseVariant.canConvert()); + + const IfdConnectResponse connectResponse(RemoteMessage::parseByteArray(connectResponseVariant.toByteArray())); + QVERIFY(!connectResponse.isIncomplete()); + QCOMPARE(connectResponse.getType(), RemoteCardMessageType::IFDConnectResponse); + QVERIFY(!connectResponse.getContextHandle().isEmpty()); + QCOMPARE(connectResponse.getSlotHandle(), QStringLiteral("test-reader")); + QVERIFY(connectResponse.resultHasError()); + QCOMPARE(connectResponse.getResultMinor(), ECardApiResult::Minor::AL_Unknown_Error); + + removeReaderAndConsumeMessages(QStringLiteral("test-reader")); + } + + + void ifdConnectForReaderWithConnectedCardSendsIFDL_IFD_SharingViolation() + { + QSignalSpy spyLog(Env::getSingleton(), &LogHandler::fireLog); + ServerMessageHandlerImpl serverMessageHandler(mDataChannel); + QString contextHandle; + ensureContext(contextHandle); + + QSignalSpy sendSpy(mDataChannel.data(), &MockDataChannel::fireSend); + + MockReader* reader = MockReaderManagerPlugIn::getInstance().addReader("test-reader"); + reader->setCard(MockCardConfig()); + const CardInfo cardInfo(CardType::EID_CARD, QSharedPointer(), 3, true); + reader->getReaderInfo().setCardInfo(cardInfo); + QTRY_COMPARE(sendSpy.count(), 1); + sendSpy.clear(); + + const QByteArray ifdConnectMsg = IfdConnect(QStringLiteral("test-reader"), true).toByteArray(contextHandle); + mDataChannel->onReceived(ifdConnectMsg); + + QTRY_COMPARE(sendSpy.count(), 1); + + const QList& connectResponse1Arguments = sendSpy.last(); + const QVariant connectResponse1Variant = connectResponse1Arguments.at(0); + QVERIFY(connectResponse1Variant.canConvert()); + + const IfdConnectResponse connectResponse1(RemoteMessage::parseByteArray(connectResponse1Variant.toByteArray())); + QVERIFY(!connectResponse1.isIncomplete()); + QCOMPARE(connectResponse1.getType(), RemoteCardMessageType::IFDConnectResponse); + QVERIFY(!connectResponse1.getContextHandle().isEmpty()); + QVERIFY(!connectResponse1.getSlotHandle().isEmpty()); + + QVERIFY(!connectResponse1.resultHasError()); + QCOMPARE(connectResponse1.getResultMinor(), ECardApiResult::Minor::null); + + sendSpy.clear(); + + // Card connected, try to connect a second time and get an error. + mDataChannel->onReceived(ifdConnectMsg); + QTRY_COMPARE(sendSpy.count(), 1); + + const QList& connectResponse2Arguments = sendSpy.last(); + const QVariant connectResponse2Variant = connectResponse2Arguments.at(0); + QVERIFY(connectResponse2Variant.canConvert()); + + const IfdConnectResponse connectResponse2(RemoteMessage::parseByteArray(connectResponse2Variant.toByteArray())); + QVERIFY(!connectResponse2.isIncomplete()); + QCOMPARE(connectResponse2.getType(), RemoteCardMessageType::IFDConnectResponse); + QVERIFY(!connectResponse2.getContextHandle().isEmpty()); + QCOMPARE(connectResponse2.getSlotHandle(), QStringLiteral("test-reader")); + QVERIFY(connectResponse2.resultHasError()); + QCOMPARE(connectResponse2.getResultMinor(), ECardApiResult::Minor::IFDL_IFD_SharingViolation); + + QVERIFY(TestFileHelper::containsLog(spyLog, QLatin1String("Card is already connected \"test-reader\""))); + + removeReaderAndConsumeMessages(QStringLiteral("test-reader")); + } + + + void ifdDisconnectForReaderWithConnectedCardSendsCorrectResponse() + { + QSignalSpy spyLog(Env::getSingleton(), &LogHandler::fireLog); + ServerMessageHandlerImpl serverMessageHandler(mDataChannel); + QString contextHandle; + ensureContext(contextHandle); + + QSignalSpy sendSpy(mDataChannel.data(), &MockDataChannel::fireSend); + + MockReader* reader = MockReaderManagerPlugIn::getInstance().addReader("test-reader"); + reader->setCard(MockCardConfig()); + const CardInfo cardInfo(CardType::EID_CARD, QSharedPointer(), 3, true); + reader->getReaderInfo().setCardInfo(cardInfo); + QTRY_COMPARE(sendSpy.count(), 1); + sendSpy.clear(); + + const QByteArray ifdConnectMsg = IfdConnect(QStringLiteral("test-reader"), true).toByteArray(contextHandle); + mDataChannel->onReceived(ifdConnectMsg); + + QTRY_COMPARE(sendSpy.count(), 1); + + const QList& connectResponseArguments = sendSpy.last(); + const QVariant connectResponseVariant = connectResponseArguments.at(0); + QVERIFY(connectResponseVariant.canConvert()); + + const IfdConnectResponse connectResponse(RemoteMessage::parseByteArray(connectResponseVariant.toByteArray())); + QVERIFY(!connectResponse.isIncomplete()); + QCOMPARE(connectResponse.getType(), RemoteCardMessageType::IFDConnectResponse); + QCOMPARE(connectResponse.getContextHandle(), contextHandle); + QVERIFY(!connectResponse.getSlotHandle().isEmpty()); + + QVERIFY(!connectResponse.resultHasError()); + QCOMPARE(connectResponse.getResultMinor(), ECardApiResult::Minor::null); + + sendSpy.clear(); + + // Card connected, try to disconnect. + const QByteArray ifdDisconnectMsg = IfdDisconnect(QStringLiteral("test-reader")).toByteArray(contextHandle); + mDataChannel->onReceived(ifdDisconnectMsg); + QTRY_COMPARE(sendSpy.count(), 1); + + const QList& disconnectResponseArguments = sendSpy.last(); + const QVariant disconnectResponseVariant = disconnectResponseArguments.at(0); + QVERIFY(disconnectResponseVariant.canConvert()); + + const IfdDisconnectResponse disconnectResponse(RemoteMessage::parseByteArray(disconnectResponseVariant.toByteArray())); + QVERIFY(!disconnectResponse.isIncomplete()); + QCOMPARE(disconnectResponse.getType(), RemoteCardMessageType::IFDDisconnectResponse); + QCOMPARE(disconnectResponse.getContextHandle(), contextHandle); + QCOMPARE(disconnectResponse.getSlotHandle(), connectResponse.getSlotHandle()); + QVERIFY(!disconnectResponse.resultHasError()); + QCOMPARE(disconnectResponse.getResultMinor(), ECardApiResult::Minor::null); + + removeReaderAndConsumeMessages(QStringLiteral("test-reader")); + } + + + void ifdDisconnectForReaderWithWrongReaderNameSendsIFDL_InvalidSlotHandle() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + ServerMessageHandlerImpl serverMessageHandler(mDataChannel); + QString contextHandle; + ensureContext(contextHandle); + + QSignalSpy sendSpy(mDataChannel.data(), &MockDataChannel::fireSend); + + MockReader* reader = MockReaderManagerPlugIn::getInstance().addReader("test-reader"); + reader->setCard(MockCardConfig()); + const CardInfo cardInfo(CardType::EID_CARD, QSharedPointer(), 3, true); + reader->getReaderInfo().setCardInfo(cardInfo); + QTRY_COMPARE(sendSpy.count(), 1); + sendSpy.clear(); + + const QByteArray ifdConnectMsg = IfdConnect(QStringLiteral("test-reader"), true).toByteArray(contextHandle); + mDataChannel->onReceived(ifdConnectMsg); + + QTRY_COMPARE(sendSpy.count(), 1); + + const QList& connectResponseArguments = sendSpy.last(); + const QVariant connectResponseVariant = connectResponseArguments.at(0); + QVERIFY(connectResponseVariant.canConvert()); + + const IfdConnectResponse connectResponse(RemoteMessage::parseByteArray(connectResponseVariant.toByteArray())); + QVERIFY(!connectResponse.isIncomplete()); + QCOMPARE(connectResponse.getType(), RemoteCardMessageType::IFDConnectResponse); + QCOMPARE(connectResponse.getContextHandle(), contextHandle); + QVERIFY(!connectResponse.getSlotHandle().isEmpty()); + + QVERIFY(!connectResponse.resultHasError()); + QCOMPARE(connectResponse.getResultMinor(), ECardApiResult::Minor::null); + + sendSpy.clear(); + + // Card connected, try to disconnect from wrong reader. + const QByteArray ifdDisconnectMsg = IfdDisconnect(QStringLiteral("wrong-reader")).toByteArray(contextHandle); + mDataChannel->onReceived(ifdDisconnectMsg); + QTRY_COMPARE(sendSpy.count(), 1); + + const QList& disconnectResponseArguments = sendSpy.last(); + const QVariant disconnectResponseVariant = disconnectResponseArguments.at(0); + QVERIFY(disconnectResponseVariant.canConvert()); + + const IfdDisconnectResponse disconnectResponse(RemoteMessage::parseByteArray(disconnectResponseVariant.toByteArray())); + QVERIFY(!disconnectResponse.isIncomplete()); + QCOMPARE(disconnectResponse.getType(), RemoteCardMessageType::IFDDisconnectResponse); + QCOMPARE(connectResponse.getContextHandle(), contextHandle); + QCOMPARE(disconnectResponse.getSlotHandle(), QStringLiteral("wrong-reader")); + QVERIFY(disconnectResponse.resultHasError()); + QCOMPARE(disconnectResponse.getResultMinor(), ECardApiResult::Minor::IFDL_InvalidSlotHandle); + + QVERIFY(TestFileHelper::containsLog(logSpy, QLatin1String("Card is not connected \"wrong-reader\""))); + + removeReaderAndConsumeMessages(QStringLiteral("test-reader")); + } + + + void ifdTransmitWithWrongReaderNameSendsIFDL_InvalidSlotHandle() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + ServerMessageHandlerImpl serverMessageHandler(mDataChannel); + QString contextHandle; + ensureContext(contextHandle); + + QSignalSpy sendSpy(mDataChannel.data(), &MockDataChannel::fireSend); + + MockReader* reader = MockReaderManagerPlugIn::getInstance().addReader("test-reader"); + reader->setCard(MockCardConfig()); + const CardInfo cardInfo(CardType::EID_CARD, QSharedPointer(), 3, true); + reader->getReaderInfo().setCardInfo(cardInfo); + QTRY_COMPARE(sendSpy.count(), 1); + sendSpy.clear(); + + const QByteArray ifdConnectMsg = IfdConnect(QStringLiteral("test-reader"), true).toByteArray(contextHandle); + mDataChannel->onReceived(ifdConnectMsg); + + QTRY_COMPARE(sendSpy.count(), 1); + + const QList& connectResponseArguments = sendSpy.last(); + const QVariant connectResponseVariant = connectResponseArguments.at(0); + QVERIFY(connectResponseVariant.canConvert()); + + const IfdConnectResponse connectResponse(RemoteMessage::parseByteArray(connectResponseVariant.toByteArray())); + QVERIFY(!connectResponse.isIncomplete()); + QCOMPARE(connectResponse.getType(), RemoteCardMessageType::IFDConnectResponse); + QCOMPARE(connectResponse.getContextHandle(), contextHandle); + QVERIFY(!connectResponse.getSlotHandle().isEmpty()); + + QVERIFY(!connectResponse.resultHasError()); + QCOMPARE(connectResponse.getResultMinor(), ECardApiResult::Minor::null); + + sendSpy.clear(); + + // Card connected, try to transmit to wrong reader. + const QByteArray ifdTransmitMsg = IfdTransmit(QStringLiteral("wrong-reader"), QByteArray()).toByteArray(contextHandle); + mDataChannel->onReceived(ifdTransmitMsg); + QTRY_COMPARE(sendSpy.count(), 1); + + const QList& transmitResponseArguments = sendSpy.last(); + const QVariant transmitResponseVariant = transmitResponseArguments.at(0); + QVERIFY(transmitResponseVariant.canConvert()); + + const IfdTransmitResponse transmitResponse(RemoteMessage::parseByteArray(transmitResponseVariant.toByteArray())); + QVERIFY(!transmitResponse.isIncomplete()); + QCOMPARE(transmitResponse.getType(), RemoteCardMessageType::IFDTransmitResponse); + QCOMPARE(transmitResponse.getContextHandle(), contextHandle); + QCOMPARE(transmitResponse.getSlotHandle(), QStringLiteral("wrong-reader")); + QVERIFY(transmitResponse.resultHasError()); + QCOMPARE(transmitResponse.getResultMinor(), ECardApiResult::Minor::IFDL_InvalidSlotHandle); + + QVERIFY(TestFileHelper::containsLog(logSpy, QLatin1String("Card is not connected \"wrong-reader\""))); + + removeReaderAndConsumeMessages(QStringLiteral("test-reader")); + } + + + void ifdEstablishPACEChannelWithWrongReaderNameSendsIFDL_InvalidSlotHandle() + { + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + ServerMessageHandlerImpl serverMessageHandler(mDataChannel); + QString contextHandle; + ensureContext(contextHandle); + + QSignalSpy sendSpy(mDataChannel.data(), &MockDataChannel::fireSend); + + MockReader* reader = MockReaderManagerPlugIn::getInstance().addReader("test-reader"); + reader->setCard(MockCardConfig()); + const CardInfo cardInfo(CardType::EID_CARD, QSharedPointer(), 3, true); + reader->getReaderInfo().setCardInfo(cardInfo); + QTRY_COMPARE(sendSpy.count(), 1); + sendSpy.clear(); + + const QByteArray ifdConnectMsg = IfdConnect(QStringLiteral("test-reader"), true).toByteArray(contextHandle); + mDataChannel->onReceived(ifdConnectMsg); + + QTRY_COMPARE(sendSpy.count(), 1); + + const QList& connectResponseArguments = sendSpy.last(); + const QVariant connectResponseVariant = connectResponseArguments.at(0); + QVERIFY(connectResponseVariant.canConvert()); + + const IfdConnectResponse connectResponse(RemoteMessage::parseByteArray(connectResponseVariant.toByteArray())); + QVERIFY(!connectResponse.isIncomplete()); + QCOMPARE(connectResponse.getType(), RemoteCardMessageType::IFDConnectResponse); + QCOMPARE(connectResponse.getContextHandle(), contextHandle); + QVERIFY(!connectResponse.getSlotHandle().isEmpty()); + + QVERIFY(!connectResponse.resultHasError()); + QCOMPARE(connectResponse.getResultMinor(), ECardApiResult::Minor::null); + + sendSpy.clear(); + + // Card connected, try to establish PACE with the wrong reader. + const QByteArray ifdEstablishPACEChannelMsg = IfdEstablishPaceChannel(QStringLiteral("wrong-reader"), QByteArray()).toByteArray(contextHandle); + mDataChannel->onReceived(ifdEstablishPACEChannelMsg); + QTRY_COMPARE(sendSpy.count(), 1); + + const QList& ifdEstablishPACEChannelResponseArguments = sendSpy.last(); + const QVariant ifdEstablishPACEChannelResponseVariant = ifdEstablishPACEChannelResponseArguments.at(0); + QVERIFY(ifdEstablishPACEChannelResponseVariant.canConvert()); + + const IfdEstablishPaceChannelResponse ifdEstablishPACEChannelResponse(RemoteMessage::parseByteArray(ifdEstablishPACEChannelResponseVariant.toByteArray())); + QVERIFY(!ifdEstablishPACEChannelResponse.isIncomplete()); + QCOMPARE(ifdEstablishPACEChannelResponse.getType(), RemoteCardMessageType::IFDEstablishPACEChannelResponse); + QCOMPARE(ifdEstablishPACEChannelResponse.getContextHandle(), contextHandle); + QCOMPARE(ifdEstablishPACEChannelResponse.getSlotHandle(), QStringLiteral("wrong-reader")); + QVERIFY(ifdEstablishPACEChannelResponse.resultHasError()); + QCOMPARE(ifdEstablishPACEChannelResponse.getResultMinor(), ECardApiResult::Minor::IFDL_InvalidSlotHandle); + + QVERIFY(TestFileHelper::containsLog(logSpy, QLatin1String("Card is not connected \"wrong-reader\""))); + + removeReaderAndConsumeMessages(QStringLiteral("test-reader")); + } + + + void ifdEstablishPACEChannelWithBasicReaderNameSendsAL_Unknown_Error() + { + const bool pinpadModeToSave = Env::getSingleton()->getRemoteServiceSettings().getPinPadMode(); + Env::getSingleton()->getRemoteServiceSettings().setPinPadMode(false); + + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + ServerMessageHandlerImpl serverMessageHandler(mDataChannel); + QString contextHandle; + ensureContext(contextHandle); + + QSignalSpy sendSpy(mDataChannel.data(), &MockDataChannel::fireSend); + + MockReader* reader = MockReaderManagerPlugIn::getInstance().addReader("test-reader"); + reader->setCard(MockCardConfig()); + const CardInfo cardInfo(CardType::EID_CARD, QSharedPointer(), 3, true); + reader->getReaderInfo().setCardInfo(cardInfo); + QTRY_COMPARE(sendSpy.count(), 1); + sendSpy.clear(); + + const QByteArray ifdConnectMsg = IfdConnect(QStringLiteral("test-reader"), true).toByteArray(contextHandle); + mDataChannel->onReceived(ifdConnectMsg); + + QTRY_COMPARE(sendSpy.count(), 1); + + const QList& connectResponseArguments = sendSpy.last(); + const QVariant connectResponseVariant = connectResponseArguments.at(0); + QVERIFY(connectResponseVariant.canConvert()); + + const IfdConnectResponse connectResponse(RemoteMessage::parseByteArray(connectResponseVariant.toByteArray())); + QVERIFY(!connectResponse.isIncomplete()); + QCOMPARE(connectResponse.getType(), RemoteCardMessageType::IFDConnectResponse); + QCOMPARE(connectResponse.getContextHandle(), contextHandle); + QVERIFY(!connectResponse.getSlotHandle().isEmpty()); + + QVERIFY(!connectResponse.resultHasError()); + QCOMPARE(connectResponse.getResultMinor(), ECardApiResult::Minor::null); + + sendSpy.clear(); + + // Card connected, try to establish PACE with basic reader while not in pinpad mode. + const QByteArray ifdEstablishPACEChannelMsg = IfdEstablishPaceChannel(QStringLiteral("test-reader"), QByteArray()).toByteArray(contextHandle); + mDataChannel->onReceived(ifdEstablishPACEChannelMsg); + QTRY_COMPARE(sendSpy.count(), 1); + + const QList& ifdEstablishPACEChannelResponseArguments = sendSpy.last(); + const QVariant ifdEstablishPACEChannelResponseVariant = ifdEstablishPACEChannelResponseArguments.at(0); + QVERIFY(ifdEstablishPACEChannelResponseVariant.canConvert()); + + const IfdEstablishPaceChannelResponse ifdEstablishPACEChannelResponse(RemoteMessage::parseByteArray(ifdEstablishPACEChannelResponseVariant.toByteArray())); + QVERIFY(!ifdEstablishPACEChannelResponse.isIncomplete()); + QCOMPARE(ifdEstablishPACEChannelResponse.getType(), RemoteCardMessageType::IFDEstablishPACEChannelResponse); + QCOMPARE(ifdEstablishPACEChannelResponse.getContextHandle(), contextHandle); + QCOMPARE(ifdEstablishPACEChannelResponse.getSlotHandle(), connectResponse.getSlotHandle()); + QVERIFY(ifdEstablishPACEChannelResponse.resultHasError()); + QCOMPARE(ifdEstablishPACEChannelResponse.getResultMinor(), ECardApiResult::Minor::AL_Unknown_Error); + + QVERIFY(TestFileHelper::containsLog(logSpy, QLatin1String("EstablishPaceChannel is only available in pin pad mode."))); + + removeReaderAndConsumeMessages(QStringLiteral("test-reader")); + Env::getSingleton()->getRemoteServiceSettings().setPinPadMode(pinpadModeToSave); + } + + }; diff --git a/test/qt/securestorage/test_SecureStorage.cpp b/test/qt/securestorage/test_SecureStorage.cpp index b7ab656..e7683f0 100644 --- a/test/qt/securestorage/test_SecureStorage.cpp +++ b/test/qt/securestorage/test_SecureStorage.cpp @@ -15,6 +15,9 @@ using namespace governikus; +Q_DECLARE_METATYPE(SecureStorage::TlsSuite) +Q_DECLARE_METATYPE(QSsl::SslProtocol) + class test_SecureStorage : public QObject { @@ -43,7 +46,7 @@ class test_SecureStorage configFile.close(); if (parseError.error != QJsonParseError::NoError) { - qCritical() << "Parse error while reading SecureStorage on position " << parseError.offset << ": " << parseError.errorString(); + qCritical() << "Parse error while reading SecureStorage on position" << parseError.offset << ':' << parseError.errorString(); return QStringList(); } @@ -76,7 +79,7 @@ class test_SecureStorage private Q_SLOTS: void testGetCVRootCertificatesUnique() { - static const int EXPECTED_CERTIFICATE_COUNT = 10; + static const int EXPECTED_CERTIFICATE_COUNT = 12; QVector > cvcs = CVCertificate::fromHex(mSecureStorage.getCVRootCertificates(true)) + CVCertificate::fromHex(mSecureStorage.getCVRootCertificates(false)); @@ -116,8 +119,8 @@ class test_SecureStorage QTest::addColumn("isProductive"); QTest::addColumn("commentName"); - QTest::newRow("production") << 3 << true << "_comment_2"; - QTest::newRow("test") << 7 << false << "_comment_4"; + QTest::newRow("production") << 4 << true << "_comment_2"; + QTest::newRow("test") << 8 << false << "_comment_4"; } @@ -200,6 +203,12 @@ class test_SecureStorage } + void testWhitelistServerBaseUrl() + { + QVERIFY(mSecureStorage.getWhitelistServerBaseUrl().isValid()); + } + + void testAppcast() { QCOMPARE(mSecureStorage.getAppcastUpdateUrl(), QUrl("https://appl.governikus-asp.de/ausweisapp2/Appcast.json")); @@ -225,37 +234,33 @@ class test_SecureStorage void testSignatureAlgorithms() { - #ifndef GOVERNIKUS_QT + #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) QSKIP("SignatureAlgorithms not supported"); #endif const auto& tlsSettings = mSecureStorage.getTlsConfig(); QCOMPARE(tlsSettings.getSignatureAlgorithms().size(), 12); - QCOMPARE(tlsSettings.getSignatureAlgorithms().constFirst().first, QSsl::KeyAlgorithm::Rsa); - QCOMPARE(tlsSettings.getSignatureAlgorithms().constFirst().second, QCryptographicHash::Algorithm::Sha512); - QCOMPARE(tlsSettings.getSignatureAlgorithms().constLast().first, QSsl::KeyAlgorithm::Ec); - QCOMPARE(tlsSettings.getSignatureAlgorithms().constLast().second, QCryptographicHash::Algorithm::Sha224); + QCOMPARE(tlsSettings.getSignatureAlgorithms().constFirst(), QByteArray("RSA+SHA512")); + QCOMPARE(tlsSettings.getSignatureAlgorithms().constLast(), QByteArray("ECDSA+SHA224")); } void testSignatureAlgorithmsPsk() { - #ifndef GOVERNIKUS_QT + #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) QSKIP("SignatureAlgorithms not supported"); #endif const auto& tlsSettings = mSecureStorage.getTlsConfig(SecureStorage::TlsSuite::PSK); QCOMPARE(tlsSettings.getSignatureAlgorithms().size(), 4); - QCOMPARE(tlsSettings.getSignatureAlgorithms().constFirst().first, QSsl::KeyAlgorithm::Rsa); - QCOMPARE(tlsSettings.getSignatureAlgorithms().constFirst().second, QCryptographicHash::Algorithm::Sha512); - QCOMPARE(tlsSettings.getSignatureAlgorithms().constLast().first, QSsl::KeyAlgorithm::Rsa); - QCOMPARE(tlsSettings.getSignatureAlgorithms().constLast().second, QCryptographicHash::Algorithm::Sha224); + QCOMPARE(tlsSettings.getSignatureAlgorithms().constFirst(), QByteArray("RSA+SHA512")); + QCOMPARE(tlsSettings.getSignatureAlgorithms().constLast(), QByteArray("RSA+SHA224")); } void testSignatureAlgorithmsRemoteReader() { - #ifndef GOVERNIKUS_QT + #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) QSKIP("SignatureAlgorithms not supported"); #endif @@ -271,10 +276,10 @@ class test_SecureStorage { const auto& ciphersForwardSecrecy = mSecureStorage.getTlsConfig().getCiphers(); QCOMPARE(ciphersForwardSecrecy.first(), QSslCipher("ECDHE-ECDSA-AES256-GCM-SHA384")); - QCOMPARE(ciphersForwardSecrecy.last(), QSslCipher("DHE-RSA-AES128-SHA")); + QCOMPARE(ciphersForwardSecrecy.last(), QSslCipher("DHE-RSA-AES128-SHA256")); const auto& ciphersPsk = mSecureStorage.getTlsConfig(SecureStorage::TlsSuite::PSK).getCiphers(); - QCOMPARE(ciphersPsk.count(), 5); + QVERIFY(ciphersPsk.count() > 0); QCOMPARE(ciphersPsk.first(), QSslCipher("RSA-PSK-AES256-GCM-SHA384")); QCOMPARE(ciphersPsk.last(), QSslCipher("RSA-PSK-AES256-CBC-SHA")); @@ -292,21 +297,28 @@ class test_SecureStorage QCOMPARE(ciphersEcRemoteReaderPairing.count(), 0); const auto& ciphersRemoteReader = mSecureStorage.getTlsConfigRemote(SecureStorage::TlsSuite::PSK).getCiphers(); - QCOMPARE(ciphersRemoteReader.count(), 5); + QVERIFY(ciphersRemoteReader.count() > 0); QCOMPARE(ciphersRemoteReader.first(), QSslCipher("RSA-PSK-AES256-GCM-SHA384")); QCOMPARE(ciphersRemoteReader.last(), QSslCipher("RSA-PSK-AES256-CBC-SHA")); } - void getSslProtocolVersion() + void getSslProtocolVersion_data() { - QCOMPARE(mSecureStorage.getTlsConfig().getProtocolVersion(), QSsl::SslProtocol::TlsV1_0OrLater); + QTest::addColumn("suite"); + QTest::addColumn("protocol"); + + QTest::newRow("default") << SecureStorage::TlsSuite::DEFAULT << QSsl::SslProtocol::TlsV1_2; + QTest::newRow("PSK") << SecureStorage::TlsSuite::PSK << QSsl::SslProtocol::TlsV1_2; } - void getSslProtocolVersionPsk() + void getSslProtocolVersion() { - QCOMPARE(mSecureStorage.getTlsConfig(SecureStorage::TlsSuite::PSK).getProtocolVersion(), QSsl::SslProtocol::TlsV1_1OrLater); + QFETCH(SecureStorage::TlsSuite, suite); + QFETCH(QSsl::SslProtocol, protocol); + + QCOMPARE(mSecureStorage.getTlsConfig(suite).getProtocolVersion(), protocol); } @@ -315,12 +327,7 @@ class test_SecureStorage QTest::addColumn("configuration"); QTest::addColumn("cipherSize"); -#if OPENSSL_VERSION_NUMBER < 0x10100000L - QTest::newRow("ciphers non PSK") << mSecureStorage.getTlsConfig().getConfiguration() << 24; -#else - QTest::newRow("ciphers non PSK") << mSecureStorage.getTlsConfig().getConfiguration() << 18; -#endif - + QTest::newRow("ciphers non PSK") << mSecureStorage.getTlsConfig().getConfiguration() << 12; QTest::newRow("ciphers for PSK") << mSecureStorage.getTlsConfig(SecureStorage::TlsSuite::PSK).getConfiguration() << 5; QTest::newRow("remote reader") << mSecureStorage.getTlsConfigRemote().getConfiguration() << 7; QTest::newRow("remote reader pairing") << mSecureStorage.getTlsConfigRemote(SecureStorage::TlsSuite::PSK).getConfiguration() << 5; diff --git a/test/qt/securestorage/test_TlsConfiguration.cpp b/test/qt/securestorage/test_TlsConfiguration.cpp index 2490dda..83659f5 100644 --- a/test/qt/securestorage/test_TlsConfiguration.cpp +++ b/test/qt/securestorage/test_TlsConfiguration.cpp @@ -60,8 +60,8 @@ class test_TlsConfiguration mTlsConfiguration.load(QJsonDocument::fromJson(config).object()); QCOMPARE(mTlsConfiguration.getCiphers().size(), 2); - QCOMPARE(mTlsConfiguration.getCiphers()[0], QSslCipher("ECDHE-ECDSA-AES256-GCM-SHA384")); - QCOMPARE(mTlsConfiguration.getCiphers()[1], QSslCipher("DHE-RSA-AES256-SHA256")); + QCOMPARE(mTlsConfiguration.getCiphers().at(0), QSslCipher("ECDHE-ECDSA-AES256-GCM-SHA384")); + QCOMPARE(mTlsConfiguration.getCiphers().at(1), QSslCipher("DHE-RSA-AES256-SHA256")); } @@ -74,30 +74,27 @@ class test_TlsConfiguration mTlsConfiguration.load(QJsonDocument::fromJson(config).object()); QCOMPARE(mTlsConfiguration.getEllipticCurves().size(), 2); - QCOMPARE(mTlsConfiguration.getEllipticCurves()[0], QSslEllipticCurve::fromLongName("brainpoolP512r1")); - QCOMPARE(mTlsConfiguration.getEllipticCurves()[1], QSslEllipticCurve::fromLongName("brainpoolP384r1")); + QCOMPARE(mTlsConfiguration.getEllipticCurves().at(0), QSslEllipticCurve::fromLongName("brainpoolP512r1")); + QCOMPARE(mTlsConfiguration.getEllipticCurves().at(1), QSslEllipticCurve::fromLongName("brainpoolP384r1")); } void testLoadSignatureAlgorithms() { - #ifndef GOVERNIKUS_QT + #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) QSKIP("SignatureAlgorithms not supported"); #endif QByteArray config("{" - " \"signatureAlgorithms\": [\"Rsa+Sha512\", \"Dsa+Sha384\", \"Ec+Sha256\"]" + " \"signatureAlgorithms\": [\"RSA+SHA512\", \"DSA+SHA384\", \"ECDSA+SHA256\"]" "}"); mTlsConfiguration.load(QJsonDocument::fromJson(config).object()); QCOMPARE(mTlsConfiguration.getSignatureAlgorithms().size(), 3); - QCOMPARE(mTlsConfiguration.getSignatureAlgorithms()[0].first, QSsl::Rsa); - QCOMPARE(mTlsConfiguration.getSignatureAlgorithms()[0].second, QCryptographicHash::Sha512); - QCOMPARE(mTlsConfiguration.getSignatureAlgorithms()[1].first, QSsl::Dsa); - QCOMPARE(mTlsConfiguration.getSignatureAlgorithms()[1].second, QCryptographicHash::Sha384); - QCOMPARE(mTlsConfiguration.getSignatureAlgorithms()[2].first, QSsl::Ec); - QCOMPARE(mTlsConfiguration.getSignatureAlgorithms()[2].second, QCryptographicHash::Sha256); + QCOMPARE(mTlsConfiguration.getSignatureAlgorithms().at(0), QByteArray("RSA+SHA512")); + QCOMPARE(mTlsConfiguration.getSignatureAlgorithms().at(1), QByteArray("DSA+SHA384")); + QCOMPARE(mTlsConfiguration.getSignatureAlgorithms().at(2), QByteArray("ECDSA+SHA256")); } @@ -124,7 +121,7 @@ class test_TlsConfiguration " \"protocolVersion\": \"TlsV1_0OrLater\"," " \"ciphers\": [\"ECDHE-ECDSA-AES256-GCM-SHA384\",\"DHE-RSA-AES256-SHA256\"]," " \"ellipticCurves\": [\"brainpoolP512r1\", \"brainpoolP384r1\"]," -#ifdef GOVERNIKUS_QT +#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) " \"signatureAlgorithms\": [\"Rsa+Sha512\", \"Dsa+Sha384\", \"Ec+Sha256\"]" #endif "}"}); @@ -134,10 +131,10 @@ class test_TlsConfiguration if (i > 0) { const auto& name1 = QStringLiteral("config%1 != config%2").arg(i - 1).arg(i).toLatin1(); - QTest::newRow(name1.data()) << configs[i - 1] << configs[i] << false; + QTest::newRow(name1.data()) << configs.at(i - 1) << configs.at(i) << false; } const auto& name2 = QStringLiteral("config%1 == config%1").arg(i).toLatin1(); - QTest::newRow(name2.data()) << configs[i] << configs[i] << true; + QTest::newRow(name2.data()) << configs.at(i) << configs.at(i) << true; } } diff --git a/test/qt/services/test_AppUpdatr.cpp b/test/qt/services/test_AppUpdatr.cpp index 0e8d093..8b57424 100644 --- a/test/qt/services/test_AppUpdatr.cpp +++ b/test/qt/services/test_AppUpdatr.cpp @@ -9,10 +9,8 @@ #include "Env.h" #include "GlobalStatus.h" -#include "LogHandler.h" #include "MockDownloader.h" #include "SecureStorage.h" -#include "TestFileHelper.h" #include "VersionNumber.h" #include @@ -53,7 +51,7 @@ const char* test_jsonData = "{" " ]" "}"; const char* test_releaseNotes = "Release Notes"; -} +} // namespace using namespace governikus; @@ -153,7 +151,6 @@ class test_AppUpdater void cleanup() { - } diff --git a/test/qt/settings/test_AppSettings.cpp b/test/qt/settings/test_AppSettings.cpp deleted file mode 100644 index 12b17be..0000000 --- a/test/qt/settings/test_AppSettings.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/*! - * \brief Unit tests for \ref AppSettings - * - * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany - */ - -#include -#include -#include - -#include "AppSettings.h" - - -using namespace governikus; - -class test_AppSettings - : public QObject -{ - Q_OBJECT - - private: - AppSettings* settings = nullptr; - - private Q_SLOTS: - void init() - { - AbstractSettings::mTestDir.clear(); - settings = new AppSettings; - } - - - void cleanup() - { - delete settings; - } - - -}; - -QTEST_GUILESS_MAIN(test_AppSettings) -#include "test_AppSettings.moc" diff --git a/test/qt/settings/test_GeneralSettings.cpp b/test/qt/settings/test_GeneralSettings.cpp index 0e25125..eafa7b6 100644 --- a/test/qt/settings/test_GeneralSettings.cpp +++ b/test/qt/settings/test_GeneralSettings.cpp @@ -54,7 +54,6 @@ class test_GeneralSettings mSettings->setAutoUpdateCheck(initial); QCOMPARE(mSettings->isAutoUpdateCheck(), initial); mSettings->save(); - } @@ -67,7 +66,6 @@ class test_GeneralSettings QCOMPARE(mSettings->isAutoStart(), !initial); mSettings->save(); - mSettings->setAutoStart(initial); QCOMPARE(mSettings->isAutoStart(), initial); mSettings->save(); @@ -89,7 +87,6 @@ class test_GeneralSettings mSettings->setUseScreenKeyboard(initial); QCOMPARE(mSettings->isUseScreenKeyboard(), initial); mSettings->save(); - } @@ -203,6 +200,20 @@ class test_GeneralSettings } + void testStoreFeedbackRequested() + { +#if defined(Q_OS_IOS) + QCOMPARE(mSettings->isRequestStoreFeedback(), false); +#else + QCOMPARE(mSettings->isRequestStoreFeedback(), true); +#endif + + mSettings->setRequestStoreFeedback(false); + mSettings->save(); + QCOMPARE(mSettings->isRequestStoreFeedback(), false); + } + + }; QTEST_GUILESS_MAIN(test_GeneralSettings) diff --git a/test/qt/settings/test_HistorySettings.cpp b/test/qt/settings/test_HistorySettings.cpp index 05bd26f..4ffc136 100644 --- a/test/qt/settings/test_HistorySettings.cpp +++ b/test/qt/settings/test_HistorySettings.cpp @@ -1,7 +1,7 @@ /*! * \brief Unit tests for History. * - * All tests ends with _QTEST to be able to identify them later. + * All tests end with _QTEST to be able to identify them later. * All original history entry from AusweisApp2 do not have this. * * \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany @@ -47,7 +47,7 @@ class test_HistorySettings void testHistoryEntries() { QVector initial = settings->getHistoryInfos(); - HistoryInfo info("pSubjectName", "pSubjectUrl", "pUsage", QDateTime(), "pTermOfUsage", "pRequestedData"); + HistoryInfo info("pSubjectName", "pSubjectUrl", "pUsage", QDateTime(), "pTermOfUsage", {"pRequestedData"}); QVector newValue(initial); newValue.prepend(info); // new values will be prepended, so that it appears on top @@ -58,7 +58,7 @@ class test_HistorySettings void testDeleteHistory() { - HistoryInfo info("pSubjectName", "pSubjectUrl", "pUsage", QDateTime(), "pTermOfUsage", "pRequestedData"); + HistoryInfo info("pSubjectName", "pSubjectUrl", "pUsage", QDateTime(), "pTermOfUsage", {"pRequestedData"}); settings->addHistoryInfo(info); QCOMPARE(settings->getHistoryInfos().size(), 1); @@ -73,7 +73,7 @@ class test_HistorySettings { const auto file = AbstractSettings::mTestDir->path() + QStringLiteral("/dummy/Test_settings_HistorySettings.ini"); - HistoryInfo info("pSubjectXYZ", "pSubjectUrlXYZ", "pUsageXYZ", QDateTime(), "pTermOfUsageXYZ", "pRequestedDataXYZ"); + HistoryInfo info("pSubjectXYZ", "pSubjectUrlXYZ", "pUsageXYZ", QDateTime(), "pTermOfUsageXYZ", {"pRequestedDataXYZ"}); settings->addHistoryInfo(info); settings->addHistoryInfo(info); settings->addHistoryInfo(info); diff --git a/test/qt/settings/test_RemoteServiceSettings.cpp b/test/qt/settings/test_RemoteServiceSettings.cpp index 3a22b40..6bdc81e 100644 --- a/test/qt/settings/test_RemoteServiceSettings.cpp +++ b/test/qt/settings/test_RemoteServiceSettings.cpp @@ -8,7 +8,6 @@ #include "DeviceInfo.h" #include "KeyPair.h" -#include "TestFileHelper.h" #include @@ -147,6 +146,29 @@ class test_RemoteServiceSettings } + void testCheckAndGenerateKey() + { + RemoteServiceSettings settings; + QCOMPARE(settings.getKey(), QSslKey()); + QCOMPARE(settings.getCertificate(), QSslCertificate()); + + QVERIFY(settings.checkAndGenerateKey()); + const auto& key = settings.getKey(); + const auto& cert = settings.getCertificate(); + QVERIFY(!key.isNull()); + QVERIFY(!cert.isNull()); + + QVERIFY(settings.checkAndGenerateKey()); + QCOMPARE(settings.getKey(), key); + QCOMPARE(settings.getCertificate(), cert); + + QCOMPARE(settings.getCertificate().effectiveDate(), QDateTime::fromString(QStringLiteral("1970-01-01T00:00:00Z"), Qt::ISODate)); + QCOMPARE(settings.getCertificate().expiryDate(), QDateTime::fromString(QStringLiteral("9999-12-31T23:59:59Z"), Qt::ISODate)); + QVERIFY(settings.getCertificate().effectiveDate().isValid()); + QVERIFY(settings.getCertificate().expiryDate().isValid()); + } + + void testKey() { RemoteServiceSettings settings; diff --git a/test/qt/websocket/test_UIPlugInWebSocket.cpp b/test/qt/websocket/test_UIPlugInWebSocket.cpp index 8bfedd3..a88d3b7 100644 --- a/test/qt/websocket/test_UIPlugInWebSocket.cpp +++ b/test/qt/websocket/test_UIPlugInWebSocket.cpp @@ -5,6 +5,8 @@ */ #include "UIPlugInWebSocket.h" + +#include "PortFile.h" #include "WebSocketHelper.h" #include @@ -49,7 +51,6 @@ class test_UIPlugInWebSocket QStringList args; args << "--ui" << "websocket"; args << "--port" << "0"; - args << "--port-websocket" << "0"; #ifndef Q_OS_WIN args << "-platform" << "offscreen"; #endif @@ -63,12 +64,8 @@ class test_UIPlugInWebSocket mApp2->waitForStarted(PROCESS_TIMEOUT); QCOMPARE(mApp2->state(), QProcess::Running); - QFile portInfoFile(WEBSOCKET_PORT_FILENAME(mApp2->processId())); - for (int i = 0; i < PROCESS_TIMEOUT && portInfoFile.size() == 0; i += 200) - { - QThread::msleep(200); - } - QVERIFY(portInfoFile.size() != 0); + QFile portInfoFile(PortFile::getPortFilename(QString(), mApp2->processId(), QStringLiteral("AusweisApp2"))); + QTRY_COMPARE_WITH_TIMEOUT(portInfoFile.exists(), true, PROCESS_TIMEOUT); QVERIFY(portInfoFile.open(QIODevice::ReadOnly)); quint16 webSocketPort = 0; @@ -82,17 +79,27 @@ class test_UIPlugInWebSocket void cleanup() { + const QString portFile = PortFile::getPortFilename(QString(), mApp2->processId(), QStringLiteral("AusweisApp2")); + QVERIFY(QFile::exists(portFile)); mHelper.reset(); + QCOMPARE(mApp2->state(), QProcess::Running); + mApp2->terminate(); mApp2->waitForFinished(PROCESS_TIMEOUT); - QCOMPARE(mApp2->state(), QProcess::NotRunning); - QCOMPARE(mApp2->exitCode(), 0); if (mApp2->state() != QProcess::NotRunning) { mApp2->kill(); } + QCOMPARE(mApp2->state(), QProcess::NotRunning); + + if (mApp2->exitCode() != 0) + { + qDebug().noquote() << "Error output from AusweisApp2 process:\n" << mApp2->readAllStandardError(); + } + QCOMPARE(mApp2->exitCode(), 0); + QVERIFY(!QFile::exists(portFile)); } @@ -109,7 +116,6 @@ class test_UIPlugInWebSocket pMessage["VersionInfo"].toObject()["Name"] == "AusweisApp2"; })); - mHelper->sendMessage("{\"cmd\": \"GET_API_LEVEL\"}"); QVERIFY(mHelper->waitForMessage([](const QJsonObject& pMessage){ return pMessage["msg"] == "API_LEVEL" && diff --git a/test/qt/whitelist_client/test_Survey.cpp b/test/qt/whitelist_client/test_Survey.cpp new file mode 100644 index 0000000..177517f --- /dev/null +++ b/test/qt/whitelist_client/test_Survey.cpp @@ -0,0 +1,68 @@ +/*! + * \copyright Copyright (c) 2018 Governikus GmbH & Co. KG, Germany + */ + +#include "Survey.h" + +#include + +using namespace governikus; + + +class test_Survey + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void testValidInvalid() + { + const Survey nonNullSurvey( + QStringLiteral("37.0.A.2.248"), + QStringLiteral("6.0"), + QStringLiteral("3.18.19+"), + 128, + QStringLiteral("Sony"), + QStringLiteral("F3311"), + QStringLiteral("XPERIA E5"), + QStringLiteral("1.15.1")); + QVERIFY(!nonNullSurvey.isNull()); + + const Survey nullSurvey; + QVERIFY(nullSurvey.isNull()); + } + + + void testToJSON() + { + const Survey survey( + QStringLiteral("37.0.A.2.248"), + QStringLiteral("6.0"), + QStringLiteral("3.18.19+"), + 128, + QStringLiteral("Sony"), + QStringLiteral("F3311"), + QStringLiteral("XPERIA E5"), + QStringLiteral("1.15.1")); + + const QByteArray& byteArray = survey.toJsonByteArray(); + QCOMPARE(byteArray, + QByteArray("{\n" + " \"AusweisAppVersionNumber\": \"1.15.1\",\n" + " \"ModelName\": \"XPERIA E5\",\n" + " \"ModelNumber\": \"F3311\",\n" + " \"Rom\": {\n" + " \"AndroidVersion\": \"6.0\",\n" + " \"BuildNumber\": \"37.0.A.2.248\",\n" + " \"KernelVersion\": \"3.18.19+\",\n" + " \"MaximumNfcPacketLength\": 128\n" + " },\n" + " \"Vendor\": \"Sony\"\n" + "}\n")); + } + + +}; + +QTEST_GUILESS_MAIN(test_Survey) +#include "test_Survey.moc" diff --git a/test/qt/widget/test_HelpAction.cpp b/test/qt/widget/test_HelpAction.cpp index d47be92..64e877b 100644 --- a/test/qt/widget/test_HelpAction.cpp +++ b/test/qt/widget/test_HelpAction.cpp @@ -8,7 +8,7 @@ #include -#include "generic/HelpAction.h" +#include "HelpAction.h" #include "LanguageLoader.h" using namespace governikus; diff --git a/uncrustify.cfg b/uncrustify.cfg index dd5fe78..3403312 100644 --- a/uncrustify.cfg +++ b/uncrustify.cfg @@ -454,7 +454,7 @@ nl_split_if_one_liner = false nl_split_for_one_liner = false nl_split_while_one_liner = false nl_max = 3 -nl_max_blank_in_func = 0 +nl_max_blank_in_func = 2 nl_after_func_proto = 0 nl_after_func_proto_group = 0 nl_after_func_class_proto = 0 @@ -467,7 +467,7 @@ nl_after_func_body_one_liner = 0 nl_before_block_comment = 2 nl_before_c_comment = 0 nl_before_cpp_comment = 0 -nl_after_multiline_comment = false +nl_after_multiline_comment = true nl_after_label_colon = false nl_after_struct = 0 nl_before_class = 0 @@ -596,7 +596,7 @@ mod_pawn_semicolon = false mod_full_paren_if_bool = false mod_remove_extra_semicolon = true mod_add_long_function_closebrace_comment = 0 -mod_add_long_namespace_closebrace_comment = 0 +mod_add_long_namespace_closebrace_comment = 1 mod_add_long_class_closebrace_comment = 0 mod_add_long_switch_closebrace_comment = 0 mod_add_long_ifdef_endif_comment = 0 @@ -604,7 +604,7 @@ mod_add_long_ifdef_else_comment = 0 mod_sort_import = true mod_sort_using = false mod_sort_include = true -mod_move_case_break = false +mod_move_case_break = true mod_case_brace = ignore mod_remove_empty_return = false mod_sort_oc_properties = false diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt deleted file mode 100644 index 2aebc28..0000000 --- a/utils/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -# Do not compile for mobile: QProcess is not supported under iOS. -IF(DESKTOP AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/testbedtool") - ADD_SUBDIRECTORY(testbedtool) -ENDIF() - -IF(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tlscheck") - ADD_SUBDIRECTORY(tlscheck) -ENDIF() - -IF(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/fuzzing") - ADD_SUBDIRECTORY(fuzzing) -ENDIF() diff --git a/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_2.cvcert b/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_2.cvcert deleted file mode 100644 index 5dea228..0000000 Binary files a/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_2.cvcert and /dev/null differ diff --git a/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_2.cvcert.hex b/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_2.cvcert.hex deleted file mode 100644 index 2167e90..0000000 --- a/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_2.cvcert.hex +++ /dev/null @@ -1 +0,0 @@ -7F218201BA7F4E8201725F290100421044454356434165494443544C303430317F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7864104010B0FAC592117B65302BACA1EA38DAC8ECE106FDF318508D647065458B666026716DAD1D5E506957FDA2337B972F333ABD470686C289177AA57A07E32F754748701015F201044454356434165494443544C303430317F4C12060904007F0007030102025305FE1FFFFFFF5F25060106010200055F24060109010200055F37402B5CE09185600A035372D34EE7FE254C4177B9E187EF70C60ABEF566884C9C81430561E0DE8D29EF008A1BBEED28B372265F2EC4316405EF80890C14F6DD624A \ No newline at end of file diff --git a/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_3.cvcert b/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_3.cvcert deleted file mode 100644 index 1334782..0000000 Binary files a/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_3.cvcert and /dev/null differ diff --git a/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_3.cvcert.hex b/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_3.cvcert.hex deleted file mode 100644 index bef3b39..0000000 --- a/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_3.cvcert.hex +++ /dev/null @@ -1 +0,0 @@ -7F218201BA7F4E8201725F290100421044454356434165494443544C303430327F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A78641046E5EB3B0EF17F41BB78E4A387732BC21DFE223200E3EEF6958A2D3FEC769E92353D1CB375478A1A8E61CA55045EC0A6F02205365B51A53172213A6520AB701D48701015F201044454356434165494443544C303430327F4C12060904007F0007030102025305FE1FFFFFFF5F25060106010200055F24060109010200055F37401DCEA27703304B3B340370859C8FD3C24A63C958DD543FF1E28F93E2CD5A93AD589D4AE22381B9873D00B2FA419EC75F97ED8AB1294365DDEBE63305337F90AE \ No newline at end of file diff --git a/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_4.cvcert b/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_4.cvcert deleted file mode 100644 index e859bd9..0000000 Binary files a/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_4.cvcert and /dev/null differ diff --git a/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_4.cvcert.hex b/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_4.cvcert.hex deleted file mode 100644 index 9e90a86..0000000 --- a/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_4.cvcert.hex +++ /dev/null @@ -1 +0,0 @@ -7F218201BA7F4E8201725F290100421044454356434165494443544C303430337F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A786410408DA7B7D4D4AF28A6DA4F9C6210423FDBB718810394EEEDFF6EF423A4245E8395A5C11DC6288EA564C4D5109E67827B2C722972754E14CE5DAC099EAD5F5947F8701015F201044454356434165494443544C303430337F4C12060904007F0007030102025305FE1FFFFFFF5F25060106010200055F24060109010200055F37405E92D635A229B1BD14EA61D1904A59CEBD18E656396994B512FD485D0D25DF7451D2A1F81694A0F7F812ED1A51810749797EC12F7C52BAC70290769A5087D9C2 \ No newline at end of file diff --git a/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_5.cvcert b/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_5.cvcert deleted file mode 100644 index 0e1eb35..0000000 Binary files a/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_5.cvcert and /dev/null differ diff --git a/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_5.cvcert.hex b/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_5.cvcert.hex deleted file mode 100644 index e64a6d6..0000000 --- a/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_5.cvcert.hex +++ /dev/null @@ -1 +0,0 @@ -7F218201BA7F4E8201725F290100421044454356434165494443544C303430347F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7864104458DE59DEB9BE08733CABBE6A752EEA4DC5FC243AF2FEF793C17B6C66B2C96DB679249FFF7EDC48E58E3B72630D2DC46195060DAB832704864E4C95677467A838701015F201044454356434165494443544C303430347F4C12060904007F0007030102025305FE1FFFFFFF5F25060106010200055F24060109010200055F374013FC6405BA64752316499DA9BF1A294D383B736388DEED7DB188A448CF406CA18D626315154B2A71D687C48B2C9ED8A14C04B62FE3C06E463022D0B11C5B0413 \ No newline at end of file diff --git a/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_6.cvcert b/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_6.cvcert deleted file mode 100644 index 86fa4ae..0000000 Binary files a/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_6.cvcert and /dev/null differ diff --git a/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_6.cvcert.hex b/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_6.cvcert.hex deleted file mode 100644 index 6a58361..0000000 --- a/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_6.cvcert.hex +++ /dev/null @@ -1 +0,0 @@ -7F218201BA7F4E8201725F290100421044454356434165494443544C303430357F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A786410477C42620CAF69F29913C6D2E23C4C7DC01FEA4BBDEC97F152E76FC2519625CB66E6A48CCE243DE56694F817B4C2047C82C6607C1087043C944EB1B45A4204A668701015F201044454356434165494443544C303430357F4C12060904007F0007030102025305FE1FFFFFFF5F25060106010200055F24060109010200055F3740919933BFD2B279FBBAAB68D8D3747417724E77914E0E590B35B6F22A59B14FA418E5312D1A31C9C107CB32B0B84EA33AAFA69ED848D32234FB059060AAE5A823 \ No newline at end of file diff --git a/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_7.cvcert b/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_7.cvcert deleted file mode 100644 index c08c1b4..0000000 Binary files a/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_7.cvcert and /dev/null differ diff --git a/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_7.cvcert.hex b/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_7.cvcert.hex deleted file mode 100644 index ce2733b..0000000 --- a/utils/testbed/resources/CVCA/CERT_CV_CVCA_4_7.cvcert.hex +++ /dev/null @@ -1 +0,0 @@ -7F218201BA7F4E8201725F290100421044454356434165494443544C303430367F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A78641041BDBDC7A309B4B790AF0DDC6D9EF2CF0B73BE9B387350DFB247640F904237BFD2CF39E7C72B3A59513B95C708E150E4EBDB0A12DCC059C4B82D8F236177187E38701015F201044454356434165494443544C303430367F4C12060904007F0007030102025305FE1FFFFFFF5F25060106010200055F24060109010200055F37404BC4AF8B311C1EDCAD56DC674D6CACD42200C24A978C7EFB6AFE60B94BDF123E4F291F404D6D18A787F2873883F23D9074F9C0D89B867660D0EF0AB6DBACF625 \ No newline at end of file diff --git a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_1.bin b/utils/testbed/resources/CVCA/CERT_CV_LINK_4_1.bin deleted file mode 100644 index 10d16f6..0000000 Binary files a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_1.bin and /dev/null differ diff --git a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_1.bin.cv b/utils/testbed/resources/CVCA/CERT_CV_LINK_4_1.bin.cv deleted file mode 100644 index 619d1d3..0000000 Binary files a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_1.bin.cv and /dev/null differ diff --git a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_1.cvcert b/utils/testbed/resources/CVCA/CERT_CV_LINK_4_1.cvcert deleted file mode 100644 index 8e396d1..0000000 Binary files a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_1.cvcert and /dev/null differ diff --git a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_1.cvcert.hex b/utils/testbed/resources/CVCA/CERT_CV_LINK_4_1.cvcert.hex deleted file mode 100644 index dd775d9..0000000 --- a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_1.cvcert.hex +++ /dev/null @@ -1 +0,0 @@ -7F218201BA7F4E8201725F2901004210444543564341654944435430303030317F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7864104010B0FAC592117B65302BACA1EA38DAC8ECE106FDF318508D647065458B666026716DAD1D5E506957FDA2337B972F333ABD470686C289177AA57A07E32F754748701015F201044454356434165494443544C303430317F4C12060904007F0007030102025305FE1FFFFFFF5F25060106010200055F24060109010200055F37407C62254D60160948DFCDF37BB22630CE809DA3A567A37652FCF88B6C29C098B23C36514BAD3C241A69659484C7A76F39A4CDDAC6DC8CACFA95B8383577B3CC6A \ No newline at end of file diff --git a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_1_KEY.pkcs8 b/utils/testbed/resources/CVCA/CERT_CV_LINK_4_1_KEY.pkcs8 deleted file mode 100644 index 3ed9512..0000000 Binary files a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_1_KEY.pkcs8 and /dev/null differ diff --git a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_2.bin b/utils/testbed/resources/CVCA/CERT_CV_LINK_4_2.bin deleted file mode 100644 index 4c75820..0000000 Binary files a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_2.bin and /dev/null differ diff --git a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_2.bin.cv b/utils/testbed/resources/CVCA/CERT_CV_LINK_4_2.bin.cv deleted file mode 100644 index 8dcc56b..0000000 Binary files a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_2.bin.cv and /dev/null differ diff --git a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_2.cvcert b/utils/testbed/resources/CVCA/CERT_CV_LINK_4_2.cvcert deleted file mode 100644 index 8186b86..0000000 Binary files a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_2.cvcert and /dev/null differ diff --git a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_2.cvcert.hex b/utils/testbed/resources/CVCA/CERT_CV_LINK_4_2.cvcert.hex deleted file mode 100644 index a2d2088..0000000 --- a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_2.cvcert.hex +++ /dev/null @@ -1 +0,0 @@ -7F218201BA7F4E8201725F290100421044454356434165494443544C303430317F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A78641046E5EB3B0EF17F41BB78E4A387732BC21DFE223200E3EEF6958A2D3FEC769E92353D1CB375478A1A8E61CA55045EC0A6F02205365B51A53172213A6520AB701D48701015F201044454356434165494443544C303430327F4C12060904007F0007030102025305FE1FFFFFFF5F25060106010200055F24060109010200055F37405EAABA9B6620286BD40A56F43AFA0ACF87B1F14735BEDCDDA929613E43A3D5AA8D645289FDE4FB00373E49A8D422D3A646BCB3AE580D861E119632607BC55D0C \ No newline at end of file diff --git a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_2_KEY.pkcs8 b/utils/testbed/resources/CVCA/CERT_CV_LINK_4_2_KEY.pkcs8 deleted file mode 100644 index f44405e..0000000 Binary files a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_2_KEY.pkcs8 and /dev/null differ diff --git a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_3.bin b/utils/testbed/resources/CVCA/CERT_CV_LINK_4_3.bin deleted file mode 100644 index f699962..0000000 Binary files a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_3.bin and /dev/null differ diff --git a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_3.bin.cv b/utils/testbed/resources/CVCA/CERT_CV_LINK_4_3.bin.cv deleted file mode 100644 index 3a46321..0000000 Binary files a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_3.bin.cv and /dev/null differ diff --git a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_3.cvcert b/utils/testbed/resources/CVCA/CERT_CV_LINK_4_3.cvcert deleted file mode 100644 index d99c770..0000000 Binary files a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_3.cvcert and /dev/null differ diff --git a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_3.cvcert.hex b/utils/testbed/resources/CVCA/CERT_CV_LINK_4_3.cvcert.hex deleted file mode 100644 index e249761..0000000 --- a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_3.cvcert.hex +++ /dev/null @@ -1 +0,0 @@ -7F218201BA7F4E8201725F290100421044454356434165494443544C303430327F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A786410408DA7B7D4D4AF28A6DA4F9C6210423FDBB718810394EEEDFF6EF423A4245E8395A5C11DC6288EA564C4D5109E67827B2C722972754E14CE5DAC099EAD5F5947F8701015F201044454356434165494443544C303430337F4C12060904007F0007030102025305FE1FFFFFFF5F25060106010200055F24060109010200055F37406728218409A24CF0523156C3CA6C188DD4A59CDACA2E52606C976695C646C0AE170A5431DF6472E905828950378CDF74B92915211A22C2EE7B390DF3D1E12A75 \ No newline at end of file diff --git a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_3_KEY.pkcs8 b/utils/testbed/resources/CVCA/CERT_CV_LINK_4_3_KEY.pkcs8 deleted file mode 100644 index 2d8712f..0000000 Binary files a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_3_KEY.pkcs8 and /dev/null differ diff --git a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_4.bin b/utils/testbed/resources/CVCA/CERT_CV_LINK_4_4.bin deleted file mode 100644 index dfbabde..0000000 Binary files a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_4.bin and /dev/null differ diff --git a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_4.bin.cv b/utils/testbed/resources/CVCA/CERT_CV_LINK_4_4.bin.cv deleted file mode 100644 index f02734e..0000000 Binary files a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_4.bin.cv and /dev/null differ diff --git a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_4.cvcert b/utils/testbed/resources/CVCA/CERT_CV_LINK_4_4.cvcert deleted file mode 100644 index d44a71e..0000000 Binary files a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_4.cvcert and /dev/null differ diff --git a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_4.cvcert.hex b/utils/testbed/resources/CVCA/CERT_CV_LINK_4_4.cvcert.hex deleted file mode 100644 index c01950e..0000000 --- a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_4.cvcert.hex +++ /dev/null @@ -1 +0,0 @@ -7F218201BA7F4E8201725F290100421044454356434165494443544C303430337F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7864104458DE59DEB9BE08733CABBE6A752EEA4DC5FC243AF2FEF793C17B6C66B2C96DB679249FFF7EDC48E58E3B72630D2DC46195060DAB832704864E4C95677467A838701015F201044454356434165494443544C303430347F4C12060904007F0007030102025305FE1FFFFFFF5F25060106010200055F24060109010200055F37402F95D375791871DA7A28D4D3DCD6795238CA801ED9A568D827E762510F772F6046C11E564DED1DEDC9A2037B0EA81A4A8476190D39D669FE20F18E09D7FCF163 \ No newline at end of file diff --git a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_4_KEY.pkcs8 b/utils/testbed/resources/CVCA/CERT_CV_LINK_4_4_KEY.pkcs8 deleted file mode 100644 index 3e31694..0000000 Binary files a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_4_KEY.pkcs8 and /dev/null differ diff --git a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_5.bin b/utils/testbed/resources/CVCA/CERT_CV_LINK_4_5.bin deleted file mode 100644 index 05256d1..0000000 Binary files a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_5.bin and /dev/null differ diff --git a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_5.bin.cv b/utils/testbed/resources/CVCA/CERT_CV_LINK_4_5.bin.cv deleted file mode 100644 index 3d88e6b..0000000 Binary files a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_5.bin.cv and /dev/null differ diff --git a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_5.cvcert b/utils/testbed/resources/CVCA/CERT_CV_LINK_4_5.cvcert deleted file mode 100644 index e89df7a..0000000 Binary files a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_5.cvcert and /dev/null differ diff --git a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_5.cvcert.hex b/utils/testbed/resources/CVCA/CERT_CV_LINK_4_5.cvcert.hex deleted file mode 100644 index 9e70754..0000000 --- a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_5.cvcert.hex +++ /dev/null @@ -1 +0,0 @@ -7F218201BA7F4E8201725F290100421044454356434165494443544C303430347F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A786410477C42620CAF69F29913C6D2E23C4C7DC01FEA4BBDEC97F152E76FC2519625CB66E6A48CCE243DE56694F817B4C2047C82C6607C1087043C944EB1B45A4204A668701015F201044454356434165494443544C303430357F4C12060904007F0007030102025305FE1FFFFFFF5F25060106010200055F24060109010200055F3740859EA541BACBAD86C18BE4C6E1DA13C0937874B120D8601235B4BB11FE53197A5185148AD4C5AD75B94AAFCD932A561B122A85D95790A155AD717A50E2E56C9F \ No newline at end of file diff --git a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_5_KEY.pkcs8 b/utils/testbed/resources/CVCA/CERT_CV_LINK_4_5_KEY.pkcs8 deleted file mode 100644 index 728535c..0000000 Binary files a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_5_KEY.pkcs8 and /dev/null differ diff --git a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_6.bin b/utils/testbed/resources/CVCA/CERT_CV_LINK_4_6.bin deleted file mode 100644 index 7744180..0000000 Binary files a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_6.bin and /dev/null differ diff --git a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_6.bin.cv b/utils/testbed/resources/CVCA/CERT_CV_LINK_4_6.bin.cv deleted file mode 100644 index 8a381fa..0000000 Binary files a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_6.bin.cv and /dev/null differ diff --git a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_6.cvcert b/utils/testbed/resources/CVCA/CERT_CV_LINK_4_6.cvcert deleted file mode 100644 index 824b00f..0000000 Binary files a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_6.cvcert and /dev/null differ diff --git a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_6.cvcert.hex b/utils/testbed/resources/CVCA/CERT_CV_LINK_4_6.cvcert.hex deleted file mode 100644 index f2b0473..0000000 --- a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_6.cvcert.hex +++ /dev/null @@ -1 +0,0 @@ -7F218201BA7F4E8201725F290100421044454356434165494443544C303430357F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A78641041BDBDC7A309B4B790AF0DDC6D9EF2CF0B73BE9B387350DFB247640F904237BFD2CF39E7C72B3A59513B95C708E150E4EBDB0A12DCC059C4B82D8F236177187E38701015F201044454356434165494443544C303430367F4C12060904007F0007030102025305FE1FFFFFFF5F25060106010200055F24060109010200055F374080EA97438956C18ED1D762AFD6E973765A158329FF4D79322D126C7FFA0CF340943F4CCD5661C104B5EE0310E535ADC1E8B9E64B31F269682842618CFE30AF6B \ No newline at end of file diff --git a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_6_KEY.pkcs8 b/utils/testbed/resources/CVCA/CERT_CV_LINK_4_6_KEY.pkcs8 deleted file mode 100644 index f94ee76..0000000 Binary files a/utils/testbed/resources/CVCA/CERT_CV_LINK_4_6_KEY.pkcs8 and /dev/null differ diff --git a/utils/testbed/resources/CVCA/DECVCAeIDCT00001.bin b/utils/testbed/resources/CVCA/DECVCAeIDCT00001.bin deleted file mode 100644 index 77b506d..0000000 Binary files a/utils/testbed/resources/CVCA/DECVCAeIDCT00001.bin and /dev/null differ diff --git a/utils/testbed/resources/CVCA/DECVCAeIDCT00001.bin.cv b/utils/testbed/resources/CVCA/DECVCAeIDCT00001.bin.cv deleted file mode 100644 index 0df0201..0000000 Binary files a/utils/testbed/resources/CVCA/DECVCAeIDCT00001.bin.cv and /dev/null differ diff --git a/utils/testbed/resources/CVCA/DECVCAeIDCT00001.cvcert b/utils/testbed/resources/CVCA/DECVCAeIDCT00001.cvcert deleted file mode 100644 index c1d96a3..0000000 Binary files a/utils/testbed/resources/CVCA/DECVCAeIDCT00001.cvcert and /dev/null differ diff --git a/utils/testbed/resources/CVCA/DECVCAeIDCT00001.cvcert.hex b/utils/testbed/resources/CVCA/DECVCAeIDCT00001.cvcert.hex deleted file mode 100644 index 52e9b58..0000000 --- a/utils/testbed/resources/CVCA/DECVCAeIDCT00001.cvcert.hex +++ /dev/null @@ -1 +0,0 @@ -7F218201BA7F4E8201725F2901004210444543564341654944435430303030317F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7864104A86AF8DCFDECB1D3093D93B3AEDC29F28EDE13F0AAC59D23AA9715CD299C1D32356289097550E212BD3FD948518430E9DD88BB468DD8ECB833C2F5D16B738E768701015F2010444543564341654944435430303030317F4C12060904007F0007030102025305FE1FFFFFFF5F25060106010200055F24060109010200055F37409DE197127555CFB5E3D0DEF2DE727FFBCBFFF00E4FDD47CD5BEAC3983487C1B904019E6D3680DE4A615016D8A55AB6AC3B71D910F622286F2656AF5839873BE5 \ No newline at end of file diff --git a/utils/testbed/resources/CVCA/DECVCAeIDCT00001.pkcs8 b/utils/testbed/resources/CVCA/DECVCAeIDCT00001.pkcs8 deleted file mode 100644 index f5b1101..0000000 Binary files a/utils/testbed/resources/CVCA/DECVCAeIDCT00001.pkcs8 and /dev/null differ