pax_global_header00006660000000000000000000000064145545003450014517gustar00rootroot0000000000000052 comment=fc45258a16d221e9cd4f731f198a7704924826cf biometryd-0.3.1/000077500000000000000000000000001455450034500135165ustar00rootroot00000000000000biometryd-0.3.1/.gitignore000066400000000000000000000002451455450034500155070ustar00rootroot00000000000000CMakeLists.txt.user CMakeCache.txt CMakeFiles CMakeScripts Testing Makefile cmake_install.cmake install_manifest.txt compile_commands.json CTestTestfile.cmake _deps biometryd-0.3.1/AUTHORS000066400000000000000000000003341455450034500145660ustar00rootroot00000000000000Alfred Neumayer Brandon Boese Dan Chapman Erfan Abdi Florian Leeber Guido Berhoerster Jami Kettunen Ken VanDine Łukasz 'sil2100' Zemczak Marius Gripsgard Mike Gabriel OPNA2608 Ratchanan Srirattanamet Rodney Thomas Voß biometryd-0.3.1/CMakeLists.txt000066400000000000000000000045621455450034500162650ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5) project(biometryd) set(PROJECT_VERSION 0.3.1) set(BIOMETRYD_VERSION_MAJOR 1) set(BIOMETRYD_VERSION_MINOR 0) set(BIOMETRYD_VERSION_PATCH 1) option(ENABLE_WERROR "Treat all build warnings as errors" ON) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -pedantic -Wextra -fPIC -fvisibility=hidden -pthread") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall -fno-strict-aliasing -fvisibility=hidden -fvisibility-inlines-hidden -pedantic -Wextra -fPIC -pthread -DBOOST_ASIO_DISABLE_EPOLL") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined") if(ENABLE_WERROR) add_compile_options("-Werror") endif() include(GNUInstallDirs) set( BIOMETRYD_DEFAULT_PLUGIN_DIRECTORY "${CMAKE_INSTALL_FULL_LIBDIR}/biometryd/plugins" CACHE STRING "Default plugin installation directory") set( BIOMETRYD_CUSTOM_PLUGIN_DIRECTORY "/custom/vendor/biometryd/plugins" CACHE STRING "Custom plugin installation directory") enable_testing() find_package(PkgConfig) find_package(Boost COMPONENTS filesystem program_options system REQUIRED) pkg_check_modules(DBUS_CPP dbus-cpp REQUIRED) pkg_check_modules(DBUS dbus-1 REQUIRED) pkg_check_modules(LIBAPPARMOR libapparmor REQUIRED) pkg_check_modules(PROCESS_CPP process-cpp REQUIRED) pkg_check_modules(SQLITE3 sqlite3 REQUIRED) # Opt-out of depending on systemd at build-time option(USE_SYSTEMD "Install systemd service" ON) if (USE_SYSTEMD) pkg_check_modules(SYSTEMD systemd) if (NOT SYSTEMD_FOUND) message(FATAL_ERROR "systemd pkg-config not found (required for figuring out service install location)") endif() endif() # Opt-in to enable hybris support option(WITH_HYBRIS "Enable libhybris integration support" OFF) if (WITH_HYBRIS) # Try to find hybris, and disable hybris from build if not found find_library(Hybris NAMES hybris-common REQUIRED ) endif() include_directories( include src # Make sure that files generated during build are available for compilation. # Most notably, this refers to include/biometry/version.h ${CMAKE_BINARY_DIR}/include ${Boost_INCLUDE_DIRS} ${DBUS_CPP_INCLUDE_DIRS} ${DBUS_INCLUDE_DIRS} ${LIBAPPARMOR_INCLUDE_DIRS} ${PROCESS_CPP_INCLUDE_DIRS} ${SQLITE3_INCLUDE_DIRS}) add_subdirectory(data) add_subdirectory(doc) add_subdirectory(include) add_subdirectory(src) add_subdirectory(tests) biometryd-0.3.1/COPYING000066400000000000000000000167431455450034500145640ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. biometryd-0.3.1/ChangeLog000066400000000000000000000521611455450034500152750ustar00rootroot000000000000002024-01-25 Mike Gabriel * Release 0.3.1 (HEAD -> main, tag: 0.3.1) 2023-11-15 Ratchanan Srirattanamet * Merge branch 'fix/useGnuinstalldirsVariables' into 'main' (db870de) 2023-11-14 OPNA2608 * Make it possible to opt out of systemd dependency (12890aa) * debian/control: Add build-time dependency on systemd (0a7881b) * Query systemd pkg-config for unit install dir (43de9ef) * data: Use configure_file instead of assuming installation paths (262ba38) * data/CMakeLists: Use GNUInstallDirs variables more (2470160) 2023-11-14 Ratchanan Srirattanamet * Merge branch 'fix/qt5_use_modules' into 'main' (a3e8d0c) 2023-11-14 OPNA2608 * Stop using qt5_use_modules (9e52fad) 2023-11-10 Marius Gripsgard * Merge branch 'missing-headers' into 'main' (bc6f1a7) 2023-11-04 Brandon Boese * qml/Biometryd/user.cpp: Add missing include for (627ddb3) * Add missing headers for gcc 13 (745ae06) 2023-01-30 Mike Gabriel * Merge branch 'personal/gberh/dh-12' into 'main' (2878a23) 2023-01-30 Guido Berhoerster * Update to dh version 12 (5511d47) 2023-01-19 Mike Gabriel * Release 0.3.0 (3d3ed4e) (tag: 0.3.0) 2023-01-10 Mike Gabriel * Merge branch 'personal/mariogrip/fixnohybris' into 'main' (a8818c2) 2023-01-10 Marius Gripsgard * src: Fix building without hybris (1343656) * Set libhybris as required if WITH_HYBIRS option is set on (f57fdda) 2023-01-05 Guido Berhoerster * Merge branch 'personal/sunweaver/fix-ENABLE-WERROR-CMake-option' into 'main' (872dfab) 2023-01-04 Mike Gabriel * d/rules: When building for UBports repos, enable -Werror via CMake option. (78cb1f2) * CMakeLists.txt: Don't hard-core -Werror anymore. We have a CMake option for this now. (3af8aee) 2023-01-05 Guido Berhoerster * Merge branch 'personal/sunweaver/set-project-version' into 'main' (e7e862b) 2023-01-04 Mike Gabriel * CMakeLists.txt: Set PROJECT_VERSION, so that our release script does not pick the SOVERSION numbers instead. (2a81ead) * CMake: Bump mininum version requirement to 3.5 (bde35e4) 2022-12-12 Marius Gripsgard * Merge branch 'personal/fredldotme/android9forwardport' into 'main' (d01cfd3) 2022-12-12 Alfred Neumayer * debian & CMake: Fix WITH_HYBRIS option and use (bca9c46) 2022-12-11 Alfred Neumayer * CMake: Disable ASIO epoll (da1e677) * debian: Enable hybris support (a5800b5) * CMake: Turn hybris support into opt-in affair (2e95231) 2022-12-09 Alfred Neumayer * tests: Increase timing by 1 sec (fade729) 2022-07-02 Alfred Neumayer * Reduce DBus timeouts (312ecda) 2022-01-25 Florian Leeber * We only need it for the size operation. If applied to list operation it blocks print deletion in UI (4377eab) 2022-01-24 Florian Leeber * Fix missing initialization (e81dba0) 2022-01-23 Florian Leeber * Fix compile (d8a03e2) * set => enable for the enable method name :) (7416313) * Whitespace (65c26f9) 2022-01-22 Florian Leeber * Cleanup (019e5ed) 2022-01-19 Florian Leeber * Cleanup (9cda308) * More debug (e2f674f) * Try with constructor (36e866f) * Bugfix (fbe7824) * Try to fix the boolean var (6c4d47b) 2022-01-18 Florian Leeber * Debugging only (ef52920) * Add a quirks mechanism to pretend that 1 finger is stored (d419db6) 2021-05-13 Ratchanan Srirattanamet * Move Jenkinsfile to debian/ per the new guideline (4ba8fc4) 2020-05-14 Erfan Abdi * Revert "[TMP] Drop -Werror" (1986b84) 2020-05-12 Erfan Abdi * We’re UBports foundation (d409cea) * Drop unused header (097472b) 2020-05-10 Erfan Abdi * Find TmpDir based on device first api level (8bf3e39) 2020-05-09 Erfan Abdi * Hardcode UserID on authenticate (e5e65dc) * Revert "Verify UserID on authenticated" (8ef5e8e) * Fix List and size when there’s only one finger (deac945) * Verify UserID on authenticated (121a2cd) * Set real User ID on enroll (b382bb7) * Fail if finger not recognized (01ba7b7) * Separate Authentication operations (0bba9d2) * Clear list before enumerate (10e28f4) * Filter finger 0 authenticated (e7460a3) * Fix Enroll and List percentage (4b76953) * Use gatekeeper to verify and get hat (f471c2f) * Fix FP service not avaliable error (d3bf06d) * Combine preEnroll to Enroll (157f65b) * Revert "Auth with keystore" (2460525) 2020-05-07 Erfan Abdi * Allow to re-setNotify (8da850d) * Fix wrong callback on Authenticate Operation (5a5d5e8) * Drop androidFailedOperation (46081a3) * Remove finger id 0 is clear all (e591503) * Auth with keystore (6425829) * Use network byte for auth key (da2476a) 2020-03-29 Erfan Abdi * print debugings (1a0af23) 2020-05-06 Erfan Abdi * Suppress warnings (af80d36) 2018-02-19 Marius Gripsgard * Disable tests (for now) (9e2885a) 2020-03-26 Erfan Abdi * Implement android hal wrapper as device (d3c6951) * Setup hybris bridge for libbiometry_fp_api.so (a21452e) 2020-03-25 Erfan Abdi * Move observer to android.cpp (24e08fd) * Complete android part of api (1d0b0e3) * Add very basic android hybris fp implementation (59bb8cf) 2020-03-24 Erfan Abdi * Use android hal wrapper as default device (fafcf76) * create null device (a8b8678) * set configuration for enroll (b9fc17a) * Fix device arg duplicate in enroll (7a8ba11) 2020-03-23 Erfan Abdi * Add android device (f496235) * Add Cmake gitignore (98873c9) 2018-02-19 Marius Gripsgard * Fix gcc 7 (1880159) 2022-11-25 Ratchanan Srirattanamet * Merge branch 'personal/sunweaver/prepare-release-plus-debian-folder-sync-from-debian' into 'main' (4d8024d) 2022-10-04 Mike Gabriel * debian/: Sync over packaging improvements from official Debian upload. (0e1bfaa) 2022-10-04 Guido Berhoerster * Merge branch 'personal/sunweaver/enable-Werror-cmake-option' into 'main' (3d7106c) 2022-10-03 Mike Gabriel * CMake: Add ENABLE_WERROR boolean build option. (637978d) * Merge branch 'fix-compile' into 'main' (39b529a) 2022-10-04 Jami Kettunen * util/dynamic_library: Add missing include for (3c07f09) 2022-10-03 Mike Gabriel * Merge branch 'systemd-migration' into 'main' (3c10032) 2022-08-15 Guido Berhoerster * Update packaging metadata (e7aa6bf) 2022-08-12 Guido Berhoerster * Remove obsolete snap packaging (5b4847c) * Remove obsolete bzr-builddeb files (e5f3b09) * Add systemd service file (94b5f77) 2022-09-06 Guido Berhoerster * Indicate missing device configuration through exit code and prevent respawn (a5e8c27) * Revert "Prevent from restarting in case of missing device configuration." (2d47aa3) 2021-07-21 Rodney * Merge branch 'personal/peat-psuwit/no-cross-qmltypes' into 'main' (9cf1e18) 2021-05-19 Marius Gripsgard * Merge branch 'personal/peat-psuwit/stub-worker-threads' into 'main' (5c7be3b) 2021-04-23 Ratchanan Srirattanamet * service: make sure there's a worker thread for stubs (747c535) * qml: don't generate qmltypes when cross compiling (18d4cda) 2021-05-14 Rodney * Merge branch 'personal/peat-psuwit/make-it-multidist' into 'main' (ce19b7c) 2021-05-13 Ratchanan Srirattanamet * tests: replace stub's Runtime with a simple worker thread (64c4cbf) 2021-05-12 Ratchanan Srirattanamet * Rename DBus namespaces to com.ubports (dd8f0c9) 2021-04-23 Ratchanan Srirattanamet * tests: replace skeleton's Runtime with a simple worker thread (5cbb29a) 2021-04-01 Ratchanan Srirattanamet * tests: build GMock as shared libraries (394467e) 2021-03-31 Ratchanan Srirattanamet * runtime: narrow the pool_size's type to uint16_t (b0d664d) * d/control: update QML B-D to use qml-module format (b7efe2c) 2021-05-12 Ratchanan Srirattanamet * Move Jenkinsfile to debian/ per the new guideline (ee9fe9f) 2021-02-08 Rodney * Merge pull request #4 from ubports/xenial_-_crash-less (dc1c73e) 2021-02-06 Marius Gripsgard * tests: Do not build empty test (8dd8d9a) * tests: Include gmock headers (3432736) * Update jenkinsfile (ccabeed) * debian: Cleanup debian pkg cruft (b5297dc) 2018-02-19 Marius Gripsgard * Build in parallel (d2c80ca) 2021-02-02 Marius Gripsgard * Do not create runtime for dbus stubs (5909653) * Fix newer gcc by specifying type (8512c42) * Add missing includes (54c8a92) 2018-02-20 Dan Chapman * Update changelog (1e82bbe) 2018-01-07 Dan Chapman * Imported to UBports (f648b3b) 2017-04-06 Bileto Bot * Releasing 0.0.1+17.04.20170406.2-0ubuntu1 (f354459) 2017-04-06 Łukasz 'sil2100' Zemczak * Switch to using cmake-extras for gmock and gtest. (LP: #1680153) (9c57822) * The leftover add_subdirectory of gmock makes CMake still bail out. Thanks to Pete Woods\! (912138f) * First try on fixing the gmock related build failure. (3d36b76) 2016-09-22 Bileto Bot * Releasing 0.0.1+16.10.20160922.3-0ubuntu1 (3a56b6c) 2016-09-22 Ken VanDine * Added shlibs:Depends; Use split mode instead of native (0e66461) * Skip qml test on powerpc, segfaults. LP: #1606927; Cast enum to int, to use in another enum. Fixes FTBFS with gcc6. Rebuild for boost soname change.; No-change rebuild for boost soname change. (4ab404d) 2016-09-21 Ken VanDine * dropped symbols file (fb57dbd) * Added symbols file (516fa8f) * Added shlibs:Depends (44d4687) * Use split mode instead of native (08f667c) 2016-06-28 Bileto Bot * Releasing 0.0.1+16.10.20160628-0ubuntu1 (71afe63) 2016-06-28 Thomas Voß * Add sphinx documentation. (58823c7) * Add an initial snapcraft.yaml setup for biometryd. (16f393a) 2016-06-27 Bileto Bot * Releasing 0.0.1+16.10.20160627.2-0ubuntu1 (66ad960) 2016-06-27 Thomas Voß * If default device cannot be instantiated, exit cleanly with success to prevent respawn watchdog from triggering. (34ab5f6) * Prevent from restarting in case of missing device configuration. (3de1a30) * Fix up test case. (4058d87) * If default device cannot be instantiated, exit cleanly with success to prevent respawn watchdog from triggering. (b6aa3fa) 2016-06-26 Thomas Voß * [ Thomas Voß ]; Verify incoming requests. (LP: #1593383. Immediately cancel operations instead of enqueuing cancellation.; Only for landing purposes.; cmds::Run now tries to make an educated guess for configuring the default device.; Also consider /custom/vendor/biometryd/plugins when scanning for plugins. [ Ken VanDine ]; Added COPYING file for the LGPL-3 and updated copyright for the single file licensed under MIT (a085d8b) 2016-06-24 Thomas Voß * [ Thomas Voß ]; Verify incoming requests. (LP: #1593383. Immediately cancel operations instead of enqueuing cancellation.; Only for landing purposes.; cmds::Run now tries to make an educated guess for configuring the default device.; Also consider /custom/vendor/biometryd/plugins when scanning for plugins. [ Ken VanDine ]; Added COPYING file for the LGPL-3 and updated copyright for the single file licensed under MIT (9dad138) * Address reviewer comments. (c76064e) 2016-06-23 Thomas Voß * Add an initial snapcraft.yaml setup for biometryd. (ea05662) 2016-06-21 Bileto Bot * Releasing 0.0.1+16.10.20160621.1-0ubuntu1 (3e3a158) 2016-06-21 Thomas Voß * Also consider /custom/vendor/biometryd/plugins when scanning for plugins. (8ffb015) * cmds::Run now tries to make an educated guess for configuring the default device. Install a default upstart configuration. (08e0a70) 2016-06-21 Ken VanDine * Added COPYING file for the LGPL-3 and updated copyright for the single file licensed under MIT (423ff3d) 2016-06-21 Thomas Voß * Only for landing purposes. (fdefc71) * Immediately cancel operations instead of enqueuing cancellation. (575947b) * Verify incoming requests. (LP: #1593383) (1b60f68) * Make sure that we consider all configured plugin directories instead of returning early. (94799c4) * Add missing break on switch for cmds::Config::Flag. (a0a054d) * Also consider /custom/vendor/biometryd/plugins when scanning for plugins. (233abee) 2016-06-20 Thomas Voß * Limit respawn attempts to 10. (c79b730) * cmds::Run now tries to make an educated guess for configuring the default device. Install a default upstart configuration. (2f18eff) 2016-06-19 Thomas Voß * Add images illustrating key system aspects. (3bfa863) * Adjust author/copyright holder. (0e901c6) * Leave comment about documentation being generated in the source tree. (f989e25) * Add section on cli for testing. (14d626c) * Refactor doc folder setup. (e297623) * Install breathe. (6aada40) * Adjust input pathes. (97bbdb3) * Add Doxyfile to rtd source folder. (ed5d578) * Adjustments for builds on readthedocs.org. (88ee2aa) * Add sphinx documentation. (27b8ae6) 2016-06-17 Ken VanDine * Added COPYING file for the LGPL-3 and updated copyright for the single file licensed under MIT (19fbf1c) 2016-06-17 Thomas Voß * Add a manual test plan. (12e788f) * Immediately cancel operations instead of enqueuing cancellation. (bc1d0ff) * Make sure that test cases pass if run in virtualized environments without securityfs being mounted. (8d94813) * Verify incoming requests. (1b729e6) 2016-06-15 Thomas Voß * Fix a race on cancellation/destruction with the Observer::on_cancelled implementation accessing a dangling QObject pointer. (3483e18) * Support List/Removal operations in qml bindings. (13d9a26) * Merge trunk. (2abefcd) * Merge lp:~thomas-voss/biometryd/robustify-comms/ (d4266fa) 2016-06-14 Thomas Voß * Merge trunk and apply comm fixes to newly added methods. (e87149b) * Merge lp:~thomas-voss/biometryd/extend-template-store-interface (e85048e) * Fix up test cases and make sure that TemplateStore::list and TemplateStore::remove is exercised. (0066928) * Extend biometry::TemplateStore: * list lists all known templates by id. * remove erases a specific template by id from the template store. (411de0b) 2016-06-13 Thomas Voß * Dummy enrollment/identification operations now take 5 seconds in total. (3d37cff) 2016-06-07 Thomas Voß * Robustify comms with the service side of things. Adjust cmds::Test to account for async operations. (ba27d41) 2016-06-06 Thomas Voß * Merge lp:~thomas-voss/biometryd/test-against-service-if-no-config-given (a34dde2) * Test against actual service if no explicit config is given. (2ce3a1f) 2016-06-02 Thomas Voß * Merge lp:~thomas-voss/biometryd/calculate-statistics-for-cmd-test (1e55d26) * Calculate timing statistics for fingerprint identification. (6e6d3bd) 2016-05-31 Thomas Voß * Gracefully handle the service not being present in QML bindings. (6a1ca5a) 2016-05-25 Thomas Voß * Merge lp:~thomas-voss/biometryd/fix-y-build (ea4cc49) * Fix Y build. (8df03f8) * Switch to QRectF. (d424dde) * Merge trunk. (ddf626f) * Merge lp:~thomas-voss/biometryd/floating-point-mask-coordinates (b158c38) * Switch to a normalized coordinate system for communicating masks. (496e050) 2016-05-24 Thomas Voß * Fix typo. (b0a0d7b) * QRect assumes (x,y) and (w,h) on construction. (6f7ea46) 2016-05-19 Thomas Voß * Merge lp:~thomas-voss/biometryd/identify-in-a-loop-for-test (432fe03) * Adjust doc string. (0d85ed5) * For cmds::Test, enable multiple independent trials for identifying the user. (1619698) 2016-05-17 Thomas Voß * Merge lp:~thomas-voss/biometryd/beautify-pretty-print (62b6364) * Beautify terminal output for biometry::TracingOperationObserver. (5d98ded) * Merge lp:~thomas-voss/biometryd/fix-properties-in-qml-bindings (f4f3a3b) * Merge lp:~thomas-voss/biometryd/pretty-print-details-in-tracing-observer (c52dbc2) * Add pretty printing of actual values passed to TracingObserver::*. (242fff9) 2016-05-13 Thomas Voß * Mark properties as constant/changing as appropriate. (02da630) 2016-05-11 Thomas Voß * Merge lp:~thomas-voss/biometryd/add-dbus-config (7a1d8ca) * Add data/com.ubuntu.biometryd.Service.conf (32d3410) * Add and install dbus configuration file for biometryd. (60d879c) * Merge lp:~thomas-voss/biometryd/fix-qml-module-installation-path (f2fe8ee) * Fix qml module installation path. (3fe9ea4) * Merge lp:~thomas-voss/biometryd/ensure-that-biometryd-bin-is-pulled-in (a586b19) * Ensure that biometryd-bin is pulled in. (5b52d81) * Merge lp:~thomas-voss/biometryd/fix-patch-version (f2dfa2c) * Fix typo in CMakeLists.txt. (5f7710d) * Merge lp:~thomas-voss/biometryd/add-test-command (b36c78c) * Add documentation. Add test for overall daemon functionality. Introduce explicit operator bool() const to util::Configuration::Node. (0d0a68c) * Add a command biometryd test --config=[path] --user=[id]. (962d1cd) * Merge lp:~ŧhomas-voss/biometryd/add-config-command (79d30c8) 2016-05-10 Thomas Voß * Merge lp:~thomas-voss/biometryd/fix-powerpc-build (4d8d5f5) * Fix linker issue on powerpc 32bit builds on vivid. (0c416f4) * Add a command biometryd config --flag=*. (56f6ab5) * Merge lp:~thomas-voss/biometryd/restructure-cli (2b01abb) * Alter cli::* interface and provide a cli::Command::Context to run invocations. Restructure setup of default help options and commands. (a288648) 2016-05-09 Thomas Voß * Merge lp:~thomas-voss/biometryd/factor-out-cli (5af0315) * Factor out Daemon::Command and friends to util::cli::*. (8d13dd0) * Merge lp:~thomas-voss/biometryd/remove-hybris-dependency (2deac46) * Remove hybris dependency. (198f60a) * Merge lp:~thomas-voss/biometryd/populate-registry-from-well-known-directories (02244a9) * Add test cases covering device registration on startup. (9773504) * biometry::Daemon now populates the list of known devices from a well-known plugin directory. (c35145e) 2016-05-07 Thomas Voß * Merge lp:~thomas-voss/biometryd/introduce-qml-module (edc3168) * Introduce qml-module-biometryd. (f9d12a2) 2016-05-05 Thomas Voß * Make the testing backend stateful and keep track of number of enrolled templates. (4383db7) 2016-05-04 Thomas Voß * Merge lp:~thomas-voss/biometryd/fix-pkgconfig-setup (4aca051) * Fix pkgconfig setup. (568c526) * Merge lp:~thomas-voss/biometryd/register-plugin-device-with-registry (33e7042) * Make sure that devices::plugin::* is known to the device registry. (bcfcff0) * Correctly propagate result of operations to qml::Biometryd::Observer. Correctly propagete FingerprintReader::is_finger_present hint to qml::Biometryd::Observer. (b2c5e71) * Merge lp:~thomas-voss/biometryd/remove-obsolete-configuration-header (48b11f3) * Remove obsolete entry for biometry/configuration.h (8cd62b6) * Merge lp:~thomas-voss/biometryd/add-run-cmd (6468276) * Move around some files and pull in util to biometry::. Implement cmds::Run and add test cases covering its functionaltiy. (6051c2b) 2016-05-03 Thomas Voß * Expose additional hints for fingerprint readers. Add documentation to qml test case. (45b7a1d) 2016-05-02 Thomas Voß * Factor FingerprintReaderHints . (981d782) * Add a qml::for_testing::* stack that enables users of the QML bindings to test async behavior. Factor out conversion from biometry::* to biometry::qml::* types. (3a2470a) * Fix up pkgconfig file. (1ffa350) * Initial commit. (b3c49ba) biometryd-0.3.1/android/000077500000000000000000000000001455450034500151365ustar00rootroot00000000000000biometryd-0.3.1/android/hybris/000077500000000000000000000000001455450034500164365ustar00rootroot00000000000000biometryd-0.3.1/android/hybris/Android.mk000066400000000000000000000026601455450034500203530ustar00rootroot00000000000000LOCAL_PATH := $(call my-dir) ANDROID_VERSION_MAJOR := $(word 1, $(subst ., , $(PLATFORM_VERSION))) ANDROID_VERSION_MINOR := $(word 2, $(subst ., , $(PLATFORM_VERSION))) ANDROID_VERSION_PATCH := $(word 3, $(subst ., , $(PLATFORM_VERSION))) ifeq ($(ANDROID_VERSION_MINOR),) ANDROID_VERSION_MINOR := 0 endif ifeq ($(ANDROID_VERSION_PATCH),) ANDROID_VERSION_PATCH := 0 endif IS_ANDROID_8 := $(shell test $(ANDROID_VERSION_MAJOR) -ge 8 && echo true) include $(CLEAR_VARS) LOCAL_CFLAGS += \ -DANDROID_VERSION_MAJOR=$(ANDROID_VERSION_MAJOR) \ -DANDROID_VERSION_MINOR=$(ANDROID_VERSION_MINOR) \ -DANDROID_VERSION_PATCH=$(ANDROID_VERSION_PATCH) UPAPI_PATH := $(LOCAL_PATH)/../../ ifneq ($(IS_ANDROID_8),true) LOCAL_CFLAGS += -std=gnu++0x endif ifeq ($(IS_ANDROID_8),true) LOCAL_CFLAGS += \ -Wno-unused-parameter endif LOCAL_C_INCLUDES := \ $(UPAPI_PATH)/include ifeq ($(IS_ANDROID_8),true) LOCAL_SRC_FILES += \ biometry_fp_hidl_for_hybris.cpp else $(error "Android pre oreo not supported") endif LOCAL_MODULE := libbiometry_fp_api LOCAL_MODULE_TAGS := optional LOCAL_SHARED_LIBRARIES := \ libbinder \ libcutils \ libinput \ liblog \ libutils \ libhardware \ libhardware_legacy \ libdl ifeq ($(IS_ANDROID_8),true) LOCAL_SHARED_LIBRARIES += \ libhidlbase \ libhidltransport \ libsensor \ android.hardware.biometrics.fingerprint@2.1 \ android.hardware.gatekeeper@1.0 endif include $(BUILD_SHARED_LIBRARY) biometryd-0.3.1/android/hybris/biometry_fp_hidl_for_hybris.cpp000066400000000000000000000413061455450034500247130ustar00rootroot00000000000000/* * Copyright © 2020 UBports foundation Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Erfan Abdi */ #include #include #include #include // android stuff #include #include #include using android::OK; using android::sp; using android::wp; using android::status_t; using android::hardware::biometrics::fingerprint::V2_1::IBiometricsFingerprint; using android::hardware::biometrics::fingerprint::V2_1::IBiometricsFingerprintClientCallback; using android::hardware::biometrics::fingerprint::V2_1::RequestStatus; using android::hardware::biometrics::fingerprint::V2_1::FingerprintAcquiredInfo; using android::hardware::biometrics::fingerprint::V2_1::FingerprintError; using android::hardware::gatekeeper::V1_0::IGatekeeper; using android::hardware::gatekeeper::V1_0::GatekeeperStatusCode; using android::hardware::gatekeeper::V1_0::GatekeeperResponse; using android::hardware::Return; using android::hardware::Void; using android::hardware::hidl_vec; using android::hardware::hidl_string; using android::hidl::base::V1_0::IBase; sp fpHal = nullptr; struct UHardwareBiometry_ { UHardwareBiometry_(); ~UHardwareBiometry_(); bool init(); uint64_t setNotify(); uint64_t preEnroll(); UHardwareBiometryRequestStatus enroll(uint32_t gid, uint32_t timeoutSec, uint32_t uid); UHardwareBiometryRequestStatus postEnroll(); uint64_t getAuthenticatorId(); UHardwareBiometryRequestStatus cancel(); UHardwareBiometryRequestStatus enumerate(); UHardwareBiometryRequestStatus remove(uint32_t gid, uint32_t fid); UHardwareBiometryRequestStatus setActiveGroup(uint32_t gid, char *storePath); UHardwareBiometryRequestStatus authenticate(uint64_t operationId, uint32_t gid); }; struct UHardwareBiometryCallback_ { UHardwareBiometryCallback_(UHardwareBiometryParams* params); UHardwareBiometryEnrollResult enrollresult_cb; UHardwareBiometryAcquired acquired_cb; UHardwareBiometryAuthenticated authenticated_cb; UHardwareBiometryError error_cb; UHardwareBiometryRemoved removed_cb; UHardwareBiometryEnumerate enumerate_cb; void* context; }; namespace { UHardwareBiometry hybris_fp_instance = NULL; UHardwareBiometryCallback hybris_fp_instance_cb = NULL; struct BiometricsFingerprintClientCallback : public IBiometricsFingerprintClientCallback { Return onEnrollResult(uint64_t deviceId, uint32_t fingerId, uint32_t groupId, uint32_t remaining) override; Return onAcquired(uint64_t deviceId, FingerprintAcquiredInfo acquiredInfo, int32_t vendorCode) override; Return onAuthenticated(uint64_t deviceId, uint32_t fingerId, uint32_t groupId, const hidl_vec& token) override; Return onError(uint64_t deviceId, FingerprintError error, int32_t vendorCode) override; Return onRemoved(uint64_t deviceId, uint32_t fingerId, uint32_t groupId, uint32_t remaining) override; Return onEnumerate(uint64_t deviceId, uint32_t fingerId, uint32_t groupId, uint32_t remaining) override; }; UHardwareBiometryFingerprintAcquiredInfo HIDLToUFingerprintAcquiredInfo(FingerprintAcquiredInfo info) { switch(info) { case FingerprintAcquiredInfo::ACQUIRED_GOOD: return ACQUIRED_GOOD; case FingerprintAcquiredInfo::ACQUIRED_PARTIAL: return ACQUIRED_PARTIAL; case FingerprintAcquiredInfo::ACQUIRED_INSUFFICIENT: return ACQUIRED_INSUFFICIENT; case FingerprintAcquiredInfo::ACQUIRED_IMAGER_DIRTY: return ACQUIRED_IMAGER_DIRTY; case FingerprintAcquiredInfo::ACQUIRED_TOO_SLOW: return ACQUIRED_TOO_SLOW; case FingerprintAcquiredInfo::ACQUIRED_TOO_FAST: return ACQUIRED_TOO_FAST; case FingerprintAcquiredInfo::ACQUIRED_VENDOR: return ACQUIRED_VENDOR; default: return ACQUIRED_GOOD; } } UHardwareBiometryFingerprintError HIDLToUFingerprintError(FingerprintError error) { switch(error) { case FingerprintError::ERROR_NO_ERROR: return ERROR_NO_ERROR; case FingerprintError::ERROR_HW_UNAVAILABLE: return ERROR_HW_UNAVAILABLE; case FingerprintError::ERROR_UNABLE_TO_PROCESS: return ERROR_UNABLE_TO_PROCESS; case FingerprintError::ERROR_TIMEOUT: return ERROR_TIMEOUT; case FingerprintError::ERROR_NO_SPACE: return ERROR_NO_SPACE; case FingerprintError::ERROR_CANCELED: return ERROR_CANCELED; case FingerprintError::ERROR_UNABLE_TO_REMOVE: return ERROR_UNABLE_TO_REMOVE; case FingerprintError::ERROR_LOCKOUT: return ERROR_LOCKOUT; case FingerprintError::ERROR_VENDOR: return ERROR_VENDOR; default: return ERROR_NO_ERROR; } } Return BiometricsFingerprintClientCallback::onEnrollResult(uint64_t deviceId, uint32_t fingerId, uint32_t groupId, uint32_t remaining) { if (hybris_fp_instance_cb && hybris_fp_instance_cb->enrollresult_cb) { hybris_fp_instance_cb->enrollresult_cb(deviceId, fingerId, groupId, remaining, hybris_fp_instance_cb->context); } return Void(); } Return BiometricsFingerprintClientCallback::onAcquired(uint64_t deviceId, FingerprintAcquiredInfo acquiredInfo, int32_t vendorCode) { if (hybris_fp_instance_cb && hybris_fp_instance_cb->acquired_cb) { hybris_fp_instance_cb->acquired_cb(deviceId, HIDLToUFingerprintAcquiredInfo(acquiredInfo), vendorCode, hybris_fp_instance_cb->context); } return Void(); } Return BiometricsFingerprintClientCallback::onAuthenticated(uint64_t deviceId, uint32_t fingerId, uint32_t groupId, const hidl_vec& token) { if (hybris_fp_instance_cb && hybris_fp_instance_cb->authenticated_cb) { hybris_fp_instance_cb->authenticated_cb(deviceId, fingerId, groupId, hybris_fp_instance_cb->context); } return Void(); } Return BiometricsFingerprintClientCallback::onError(uint64_t deviceId, FingerprintError error, int32_t vendorCode) { if (hybris_fp_instance_cb && hybris_fp_instance_cb->error_cb) { hybris_fp_instance_cb->error_cb(deviceId, HIDLToUFingerprintError(error), vendorCode, hybris_fp_instance_cb->context); } return Void(); } Return BiometricsFingerprintClientCallback::onRemoved(uint64_t deviceId, uint32_t fingerId, uint32_t groupId, uint32_t remaining) { if (hybris_fp_instance_cb && hybris_fp_instance_cb->removed_cb) { hybris_fp_instance_cb->removed_cb(deviceId, fingerId, groupId, remaining, hybris_fp_instance_cb->context); } return Void(); } Return BiometricsFingerprintClientCallback::onEnumerate(uint64_t deviceId, uint32_t fingerId, uint32_t groupId, uint32_t remaining) { if (hybris_fp_instance_cb && hybris_fp_instance_cb->enumerate_cb) { hybris_fp_instance_cb->enumerate_cb(deviceId, fingerId, groupId, remaining, hybris_fp_instance_cb->context); } return Void(); } } UHardwareBiometry_::UHardwareBiometry_() { } UHardwareBiometry_::~UHardwareBiometry_() { if (fpHal != nullptr) { fpHal->cancel(); } } UHardwareBiometryCallback_::UHardwareBiometryCallback_(UHardwareBiometryParams* params) : enrollresult_cb(params->enrollresult_cb), acquired_cb(params->acquired_cb), authenticated_cb(params->authenticated_cb), error_cb(params->error_cb), removed_cb(params->removed_cb), enumerate_cb(params->enumerate_cb), context(params->context) { } UHardwareBiometryRequestStatus HIDLToURequestStatus(RequestStatus req) { switch(req) { case RequestStatus::SYS_UNKNOWN: return SYS_UNKNOWN; case RequestStatus::SYS_OK: return SYS_OK; case RequestStatus::SYS_ENOENT: return SYS_ENOENT; case RequestStatus::SYS_EINTR: return SYS_EINTR; case RequestStatus::SYS_EIO: return SYS_EIO; case RequestStatus::SYS_EAGAIN: return SYS_EAGAIN; case RequestStatus::SYS_ENOMEM: return SYS_ENOMEM; case RequestStatus::SYS_EACCES: return SYS_EACCES; case RequestStatus::SYS_EFAULT: return SYS_EFAULT; case RequestStatus::SYS_EBUSY: return SYS_EBUSY; case RequestStatus::SYS_EINVAL: return SYS_EINVAL; case RequestStatus::SYS_ENOSPC: return SYS_ENOSPC; case RequestStatus::SYS_ETIMEDOUT: return SYS_ETIMEDOUT; default: return SYS_UNKNOWN; } } bool UHardwareBiometry_::init() { /* Initializes the FP service handle. */ fpHal = IBiometricsFingerprint::getService(); if (fpHal == nullptr) { ALOGE("Unable to get FP service\n"); return false; } return true; } uint64_t UHardwareBiometry_::setNotify() { if (fpHal == nullptr) { ALOGE("Unable to get FP service\n"); return 0; } sp fpCbIface = new BiometricsFingerprintClientCallback(); return fpHal->setNotify(fpCbIface); } uint64_t UHardwareBiometry_::preEnroll() { if (fpHal == nullptr) { ALOGE("Unable to get FP service\n"); return 0; } return fpHal->preEnroll(); } UHardwareBiometryRequestStatus UHardwareBiometry_::enroll(uint32_t gid, uint32_t timeoutSec, uint32_t user_id) { if (fpHal == nullptr) { ALOGE("Unable to get FP service\n"); return SYS_UNKNOWN; } uint8_t *auth_token; uint32_t auth_token_len; int ret = 0; uint64_t challange = fpHal->preEnroll(); std::string Password = "default_password"; bool request_reenroll = false; sp gk_device; gk_device = IGatekeeper::getService(); if (gk_device == nullptr) { ALOGE("Unable to get Gatekeeper service\n"); return SYS_UNKNOWN; } hidl_vec curPwdHandle; hidl_vec enteredPwd; enteredPwd.setToExternal(const_cast((const uint8_t *)Password.c_str()), Password.size()); Return hwRet = gk_device->enroll(user_id, NULL, NULL, enteredPwd, [&ret, &curPwdHandle] (const GatekeeperResponse &rsp) { ret = static_cast(rsp.code); // propagate errors if (rsp.code >= GatekeeperStatusCode::STATUS_OK) { curPwdHandle.setToExternal(const_cast((const uint8_t *)rsp.data.data()), rsp.data.size()); ret = 0; // all success states are reported as 0 } else if (rsp.code == GatekeeperStatusCode::ERROR_RETRY_TIMEOUT && rsp.timeout > 0) { ret = rsp.timeout; } } ); if (!hwRet.isOk()) { ALOGE("Unable to Enroll on Gatekeeper\n"); return SYS_UNKNOWN; } hwRet = gk_device->verify(user_id, challange, curPwdHandle, enteredPwd, [&ret, &request_reenroll, &auth_token, &auth_token_len] (const GatekeeperResponse &rsp) { ret = static_cast(rsp.code); // propagate errors if (rsp.code >= GatekeeperStatusCode::STATUS_OK) { auth_token = new uint8_t[rsp.data.size()]; auth_token_len = rsp.data.size(); memcpy(auth_token, rsp.data.data(), auth_token_len); request_reenroll = (rsp.code == GatekeeperStatusCode::STATUS_REENROLL); ret = 0; // all success states are reported as 0 } else if (rsp.code == GatekeeperStatusCode::ERROR_RETRY_TIMEOUT && rsp.timeout > 0) { ret = rsp.timeout; } } ); if (!hwRet.isOk()) { ALOGE("Unable to Verify on Gatekeeper\n"); return SYS_UNKNOWN; } return HIDLToURequestStatus(fpHal->enroll(auth_token, gid, timeoutSec)); } UHardwareBiometryRequestStatus UHardwareBiometry_::postEnroll() { if (fpHal == nullptr) { ALOGE("Unable to get FP service\n"); return SYS_UNKNOWN; } return HIDLToURequestStatus(fpHal->postEnroll()); } uint64_t UHardwareBiometry_::getAuthenticatorId() { if (fpHal == nullptr) { ALOGE("Unable to get FP service\n"); return 0; } return fpHal->getAuthenticatorId(); } UHardwareBiometryRequestStatus UHardwareBiometry_::cancel() { if (fpHal == nullptr) { ALOGE("Unable to get FP service\n"); return SYS_UNKNOWN; } return HIDLToURequestStatus(fpHal->cancel()); } UHardwareBiometryRequestStatus UHardwareBiometry_::enumerate() { if (fpHal == nullptr) { ALOGE("Unable to get FP service\n"); return SYS_UNKNOWN; } return HIDLToURequestStatus(fpHal->enumerate()); } UHardwareBiometryRequestStatus UHardwareBiometry_::remove(uint32_t gid, uint32_t fid) { if (fpHal == nullptr) { ALOGE("Unable to get FP service\n"); return SYS_UNKNOWN; } return HIDLToURequestStatus(fpHal->remove(gid, fid)); } UHardwareBiometryRequestStatus UHardwareBiometry_::setActiveGroup(uint32_t gid, char *storePath) { if (fpHal == nullptr) { ALOGE("Unable to get FP service\n"); return SYS_UNKNOWN; } return HIDLToURequestStatus(fpHal->setActiveGroup(gid, storePath)); } UHardwareBiometryRequestStatus UHardwareBiometry_::authenticate(uint64_t operationId, uint32_t gid) { if (fpHal == nullptr) { ALOGE("Unable to get FP service\n"); return SYS_UNKNOWN; } return HIDLToURequestStatus(fpHal->authenticate(operationId, gid)); } ///////////////////////////////////////////////////////////////////// // Implementation of the C API UHardwareBiometry u_hardware_biometry_new() { if (hybris_fp_instance != NULL) return NULL; UHardwareBiometry u_hardware_biometry = new UHardwareBiometry_(); hybris_fp_instance = u_hardware_biometry; // Try ten times to initialize the FP HAL interface, // sleeping for 200ms per iteration in case of issues. for (unsigned int i = 0; i < 50; i++) if (u_hardware_biometry->init()) return hybris_fp_instance = u_hardware_biometry; else // Sleep for some time and leave some time for the system // to finish initialization. ::usleep(200 * 1000); // This is the error case, as we did not succeed in initializing the FP interface. delete u_hardware_biometry; return hybris_fp_instance; } uint64_t u_hardware_biometry_setNotify(UHardwareBiometry self, UHardwareBiometryParams *params) { UHardwareBiometryCallback u_hardware_biometry_cb = new UHardwareBiometryCallback_(params); hybris_fp_instance_cb = u_hardware_biometry_cb; return self->setNotify(); } uint64_t u_hardware_biometry_preEnroll(UHardwareBiometry self) { return self->preEnroll(); } UHardwareBiometryRequestStatus u_hardware_biometry_enroll(UHardwareBiometry self, uint32_t gid, uint32_t timeoutSec, uint32_t uid) { return self->enroll(gid, timeoutSec, uid); } UHardwareBiometryRequestStatus u_hardware_biometry_postEnroll(UHardwareBiometry self) { return self->postEnroll(); } uint64_t u_hardware_biometry_getAuthenticatorId(UHardwareBiometry self) { return self->getAuthenticatorId(); } UHardwareBiometryRequestStatus u_hardware_biometry_cancel(UHardwareBiometry self) { return self->cancel(); } UHardwareBiometryRequestStatus u_hardware_biometry_enumerate(UHardwareBiometry self) { return self->enumerate(); } UHardwareBiometryRequestStatus u_hardware_biometry_remove(UHardwareBiometry self, uint32_t gid, uint32_t fid) { return self->remove(gid, fid); } UHardwareBiometryRequestStatus u_hardware_biometry_setActiveGroup(UHardwareBiometry self, uint32_t gid, char *storePath) { return self->setActiveGroup(gid, storePath); } UHardwareBiometryRequestStatus u_hardware_biometry_authenticate(UHardwareBiometry self, uint64_t operationId, uint32_t gid) { return self->authenticate(operationId, gid); } biometryd-0.3.1/data/000077500000000000000000000000001455450034500144275ustar00rootroot00000000000000biometryd-0.3.1/data/CMakeLists.txt000066400000000000000000000014131455450034500171660ustar00rootroot00000000000000configure_file( biometryd.conf.in biometryd.conf @ONLY ) configure_file( biometryd.pc.in biometryd.pc @ONLY ) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/biometryd.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig ) install( FILES ${CMAKE_CURRENT_SOURCE_DIR}/com.ubports.biometryd.Service.conf DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/dbus-1/system.d/ ) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/biometryd.conf DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/init ) if (USE_SYSTEMD) pkg_get_variable(SYSTEMD_SYSTEM_UNIT_DIR systemd systemdsystemunitdir) configure_file( biometryd.service.in biometryd.service @ONLY ) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/biometryd.service DESTINATION ${SYSTEMD_SYSTEM_UNIT_DIR} ) endif() biometryd-0.3.1/data/biometryd.conf.in000066400000000000000000000007501455450034500177030ustar00rootroot00000000000000description "biometryd mediates access to biometric devices" start on android and started dbus respawn respawn limit 10 5 script # wait for Android properties system to be ready while [ ! -e /dev/socket/property_service ]; do sleep 0.1; done exec @CMAKE_INSTALL_FULL_BINDIR@/biometryd run end script post-stop script # do not respawn if exited due to missing configuration if [ "${EXIT_STATUS}" -eq 78 ]; then initctl stop exit 0 fi end script biometryd-0.3.1/data/biometryd.pc.in000066400000000000000000000005321455450034500173560ustar00rootroot00000000000000prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=${prefix} libdir=${exec_prefix}/lib includedir=${exec_prefix}/include Name: @CMAKE_PROJECT_NAME@ Description: biometryd mediates/multiplexes to biometric devices Version: @BIOMETRYD_VERSION_MAJOR@.@BIOMETRYD_VERSION_MINOR@.@BIOMETRYD_VERSION_PATCH@ Libs: -L${libdir} -lbiometry Cflags: -I${includedir} biometryd-0.3.1/data/biometryd.service.in000066400000000000000000000004711455450034500204160ustar00rootroot00000000000000[Unit] Description=Mediate access to biometric devices Wants=lxc-android-config.service After=lxc-android-config.service [Service] Type=dbus BusName=com.ubports.biometryd.Service ExecStart=@CMAKE_INSTALL_FULL_BINDIR@/biometryd run Restart=always RestartPreventExitStatus=78 [Install] WantedBy=multi-user.target biometryd-0.3.1/data/com.ubports.biometryd.Service.conf000066400000000000000000000036571455450034500231600ustar00rootroot00000000000000 biometryd-0.3.1/debian/000077500000000000000000000000001455450034500147405ustar00rootroot00000000000000biometryd-0.3.1/debian/Jenkinsfile000066400000000000000000000004251455450034500171250ustar00rootroot00000000000000@Library('ubports-build-tools') _ buildAndProvideDebianPackage() // Or if the package consists entirely of arch-independent packages: // (optiional optimization, will confuse BlueOcean's live view at build stage) // buildAndProvideDebianPackage(/* isArchIndependent */ true) biometryd-0.3.1/debian/biometryd-bin.install000066400000000000000000000001051455450034500210700ustar00rootroot00000000000000usr/bin/biometryd etc/dbus-1/system.d/ etc/init/ lib/systemd/system/ biometryd-0.3.1/debian/changelog000066400000000000000000000066351455450034500166240ustar00rootroot00000000000000biometryd (0.3.1) unstable; urgency=medium * Upstream-provided Debian package for biometryd. See upstream ChangeLog for recent changes. -- UBports developers Thu, 25 Jan 2024 16:40:21 +0100 biometryd (0.3.0) unstable; urgency=medium [ Mike Gabriel ] * Various build improvements (cross compilation, unit tests, etc.). * runtime: narrow the pool_size's type to uint16_t. * Rename DBus namespaces to com.ubports. * Drop snap packaging. * Migrate to systemd. * util/dynamic_library: Add missing include for . * debian/: + Port over packaging changes from official Debian upload. Esp.: Rename libbiometryd* pkgs to libbiometry* and make lib:pkg and dev:pkg match the library's SONAME. [ UBports developers ] * Upstream-provided Debian package for biometryd. See upstream ChangeLog for recent changes. -- UBports developers Thu, 19 Jan 2023 09:34:18 +0100 biometryd (0.0.2+ubports1) xenial; urgency=medium * No change rebuild to publish sources to ubports repo -- Dan Chapman Tue, 20 Feb 2018 09:33:23 +0000 biometryd (0.0.2+ubports) xenial; urgency=medium * Imported to UBports -- UBports auto importer Sun, 07 Jan 2018 06:48:46 +0000 biometryd (0.0.1+17.04.20170406.2-0ubuntu1) zesty; urgency=medium [ Łukasz 'sil2100' Zemczak ] * Switch to using cmake-extras for gmock and gtest. (LP: #1680153) -- Łukasz Zemczak Thu, 06 Apr 2017 16:41:56 +0000 biometryd (0.0.1+16.10.20160922.3-0ubuntu1) yakkety; urgency=medium * Added shlibs:Depends -- Ken VanDine Thu, 22 Sep 2016 19:42:16 +0000 biometryd (0.0.1+16.10.20160628-0ubuntu4) yakkety; urgency=medium * Skip qml test on powerpc, segfaults. LP: #1606927 -- Dimitri John Ledkov Thu, 18 Aug 2016 15:44:55 +0100 biometryd (0.0.1+16.10.20160628-0ubuntu3) yakkety; urgency=medium * Cast enum to int, to use in another enum. Fixes FTBFS with gcc6. * Rebuild for boost soname change. -- Dimitri John Ledkov Thu, 18 Aug 2016 11:13:07 +0100 biometryd (0.0.1+16.10.20160628-0ubuntu2) yakkety; urgency=medium * No-change rebuild for boost soname change. -- Matthias Klose Thu, 04 Aug 2016 08:11:16 +0000 biometryd (0.0.1+16.10.20160628-0ubuntu1) yakkety; urgency=medium * Add an initial snapcraft.yaml setup for biometryd. * Add sphinx documentation. -- Thomas Voß Tue, 28 Jun 2016 06:20:19 +0000 biometryd (0.0.1+16.10.20160627.2-0ubuntu1) yakkety; urgency=medium * If default device cannot be instantiated, exit cleanly with success to prevent respawn watchdog from triggering. -- Thomas Voß Mon, 27 Jun 2016 13:59:40 +0000 biometryd (0.0.1+16.10.20160621.1-0ubuntu1) yakkety; urgency=medium [ Thomas Voß ] * Initial release. * Verify incoming requests. (LP: #1593383) * Immediately cancel operations instead of enqueuing cancellation. * Only for landing purposes. * cmds::Run now tries to make an educated guess for configuring the default device. * Also consider /custom/vendor/biometryd/plugins when scanning for plugins. [ Ken VanDine ] * Added COPYING file for the LGPL-3 and updated copyright for the single file licensed under MIT -- Thomas Voß Tue, 21 Jun 2016 09:53:19 +0000 biometryd-0.3.1/debian/control000066400000000000000000000064761455450034500163600ustar00rootroot00000000000000Source: biometryd Priority: optional Maintainer: UBports Developers Build-Depends: cmake, cmake-extras, debhelper-compat (= 12), doxygen, google-mock, graphviz, libapparmor-dev, libboost-filesystem-dev, libboost-program-options-dev, libboost-system-dev, libboost-test-dev, libdbus-cpp-dev (>= 4.0.0), libdbus-1-dev, libelf-dev, libgtest-dev, libhybris-common-dev, libprocess-cpp-dev, libsqlite3-dev, lsb-release, pkg-config, qml-module-qttest, qt5-default, qtbase5-dev, qtdeclarative5-dev, qtdeclarative5-dev-tools, systemd, Standards-Version: 3.9.5 Section: libs Homepage: https://gitlab.com/ubports/development/core/biometryd Vcs-Git: https://gitlab.com/ubports/development/core/biometryd.git Vcs-Browser: https://gitlab.com/ubports/development/core/biometryd Package: libbiometry1 Architecture: any Multi-Arch: same Pre-Depends: ${misc:Pre-Depends}, Depends: ${misc:Depends}, ${shlibs:Depends}, Breaks: libbiometryd1 (<< 0.0.3~), Replaces: libiometryd1 (<< 0.0.3~), Recommends: biometryd-bin, Description: biometryd mediates/multiplexes to biometric devices - runtime library biometryd mediates and multiplexes access to biometric devices present on the system, enabling applications and system components to leverage them for identification and verification of users. . This package includes the biometryd runtime libraries. Package: libbiometry-dev Section: libdevel Architecture: any Multi-Arch: same Pre-Depends: ${misc:Pre-Depends}, Depends: libbiometry1 (= ${binary:Version}), ${misc:Depends}, Breaks: libbiometryd-dev (<< 0.0.3~), Replaces: libiometryd-dev (<< 0.0.3~), Description: biometryd mediates/multiplexes to biometric devices - development headers biometryd mediates and multiplexes access to biometric devices present on the system, enabling applications and system components to leverage them for identification and verification of users. . This package includes all the development headers and libraries for biometryd. Package: biometryd-bin Section: devel Architecture: any Multi-Arch: foreign Depends: libbiometry1 (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends}, Description: biometryd mediates/multiplexes to biometric devices - daemon/helper binaries biometryd mediates and multiplexes access to biometric devices present on the system, enabling applications and system components to leverage them for identification and verification of users. . Daemon and helper binaries to be used by services. Package: qml-module-biometryd Section: devel Architecture: any Depends: libbiometry1 (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends}, Description: biometryd mediates/multiplexes to biometric devices - QML bindings biometryd mediates and multiplexes access to biometric devices present on the system, enabling applications and system components to leverage them for identification and verification of users. . This package contains the qtdeclarative bindings for biometryd. biometryd-0.3.1/debian/copyright000066400000000000000000000042721455450034500167000ustar00rootroot00000000000000Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: biometryd Source: https://gitlab.com/ubports/development/core/biometryd/ Files: * Copyright: 2016, Canonical Ltd. License: LGPL-3.0 Files: src/biometry/util/json.hpp Copyright: 2013-2015, Niels Lohmann License: MIT Files: debian/* Copyright: 2016, Canonical Ltd. 2022, Mike Gabriel License: LGPL-3.0 License: LGPL-3.0 This package is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, version 3 of the License. . This package is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. . You should have received a copy of the GNU General Public License along with this program. If not, see . . On Debian systems, the complete text of the GNU Lesser General Public License can be found in /usr/share/common-licenses/LGPL-3. License: MIT Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: . The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. . THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 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. biometryd-0.3.1/debian/libbiometry-dev.install000066400000000000000000000001061455450034500214220ustar00rootroot00000000000000usr/include/biometry/* usr/lib/*/libbiometry.so usr/lib/*/pkgconfig/* biometryd-0.3.1/debian/libbiometry1.install000066400000000000000000000000241455450034500207260ustar00rootroot00000000000000usr/lib/*/lib*.so.* biometryd-0.3.1/debian/qml-module-biometryd.install000066400000000000000000000000201455450034500223700ustar00rootroot00000000000000usr/lib/*/qt5/* biometryd-0.3.1/debian/rules000077500000000000000000000016321455450034500160220ustar00rootroot00000000000000#!/usr/bin/make -f # -*- makefile -*- export DPKG_GENSYMBOLS_CHECK_LEVEL=4 export DEB_BUILD_MAINT_OPTIONS = hardening=+all include /usr/share/dpkg/default.mk # The home directory of user buildd does not exist on the builders export XDG_DATA_HOME=/tmp %: dh $@ override_dh_missing: dh_missing --fail-missing override_dh_auto_configure: # DEB_TARGET_ARCH is to avoid running qml tests on powerpc dh_auto_configure -- -DDEB_TARGET_ARCH=$(DEB_TARGET_ARCH) \ -DENABLE_WERROR=ON \ -DWITH_HYBRIS=1 \ $(NULL) override_dh_install: # Nasty CMakeLists.txt appearing in include directory # See: https://gitlab.com/ubports/development/core/biometryd/-/issues/2 rm -v debian/tmp/usr/include/biometry/CMakeLists.txt dh_install override_dh_auto_test: # we have seen test failures when building with 8 cores instead of 4 dh_auto_test --no-parallel biometryd-0.3.1/debian/upstream/000077500000000000000000000000001455450034500166005ustar00rootroot00000000000000biometryd-0.3.1/debian/upstream/metadata000066400000000000000000000005101455450034500202770ustar00rootroot00000000000000Bug-Database: https://gitlab.com/ubports/development/core/biometryd/issues Bug-Submit: https://gitlab.com/ubports/development/core/biometryd/issues/new Donation: https://ubports.com/donate Repository: https://gitlab.com/ubports/development/core/biometryd Repository-Browse: https://gitlab.com/ubports/development/core/biometryd biometryd-0.3.1/doc/000077500000000000000000000000001455450034500142635ustar00rootroot00000000000000biometryd-0.3.1/doc/CMakeLists.txt000066400000000000000000000015311455450034500170230ustar00rootroot00000000000000option( BIOMETRYD_ENABLE_DOC_GENERATION "Generate package documentation with doxygen" OFF ) if (BIOMETRYD_ENABLE_DOC_GENERATION) # Please note that we are creating documentation within the source tree # as we are hosting our documentation on readthedocs.org. For their build machinery to work # properly, we need to generate doxygen XML output within the source tree. find_package(Doxygen) if (DOXYGEN_FOUND) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY) add_custom_target(doc ALL ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMENT "Generating API documentation with Doxygen" VERBATIM) install( DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION ${CMAKE_INSTALL_DOCDIR}) endif () endif () biometryd-0.3.1/doc/Doxyfile000066400000000000000000002343401455450034500157770ustar00rootroot00000000000000# Doxyfile 1.8.3.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" "). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or sequence of words) that should # identify the project. Note that if you do not use Doxywizard you need # to put quotes around the project name if it contains spaces. PROJECT_NAME = @CMAKE_PROJECT_NAME@ # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = 0.0.1 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer # a quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = "biometryd mediates/multiplexes to biometric devices" # With the PROJECT_LOGO tag one can specify an logo or icon that is # included in the documentation. The maximum height of the logo should not # exceed 55 pixels and the maximum width should not exceed 200 pixels. # Doxygen will copy the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. Note that you specify absolute paths here, but also # relative paths, which will be relative from the directory where doxygen is # started. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful if your file system # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding # "class=itcl::class" will allow you to use the command class in the # itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, # and language is one of the parsers supported by doxygen: IDL, Java, # Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, # C++. For instance to make doxygen treat .inc files as Fortran files (default # is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note # that for custom extensions you also need to set FILE_PATTERNS otherwise the # files are not read by doxygen. EXTENSION_MAPPING = # If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all # comments according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you # can mix doxygen, HTML, and XML commands with Markdown formatting. # Disable only in case of backward compatibilities issues. MARKDOWN_SUPPORT = YES # When enabled doxygen tries to link words that correspond to documented classes, # or namespaces to their corresponding documentation. Such a link can be # prevented in individual cases by by putting a % sign in front of the word or # globally by setting AUTOLINK_SUPPORT to NO. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also makes the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES (the # default) will make doxygen replace the get and set methods by a property in # the documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and # unions are shown inside the group in which they are included (e.g. using # @ingroup) instead of on a separate page (for HTML and Man pages) or # section (for LaTeX and RTF). INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and # unions with only public data fields will be shown inline in the documentation # of the scope in which they are defined (i.e. file, namespace, or group # documentation), provided this scope is documented. If set to NO (the default), # structs, classes, and unions are shown on a separate page (for HTML and Man # pages) or section (for LaTeX and RTF). INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penalty. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will roughly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols. SYMBOL_CACHE_SIZE = 0 # Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be # set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given # their name and scope. Since this can be an expensive process and often the # same symbol appear multiple times in the code, doxygen keeps a cache of # pre-resolved symbols. If the cache is too small doxygen will become slower. # If the cache is too large, memory is wasted. The cache size is given by this # formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal # scope will be included in the documentation. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespaces are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = YES # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to # do proper type resolution of all parameters of a function it will reject a # match between the prototype and the implementation of a member function even # if there is only one candidate or it is obvious which candidate to choose # by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen # will still accept a match between prototype and implementation in such cases. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if section-label ... \endif # and \cond section-label ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or macro consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and macros in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files # containing the references data. This must be a list of .bib files. The # .bib extension is automatically appended if omitted. Using this command # requires the bibtex tool to be installed. See also # http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style # of the bibliography can be controlled using LATEX_BIB_STYLE. To use this # feature you need bibtex and perl available in the search path. Do not use # file names with spaces, bibtex cannot handle them. CITE_BIB_FILES = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_NO_PARAMDOC option can be enabled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = ../include ../src/biometry/qml # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py # *.f90 *.f *.for *.vhd *.vhdl FILE_PATTERNS = # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = @CMAKE_CURRENT_SOURCE_DIR@/../tests # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = YES # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty or if # non of the patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) # and it is also possible to disable source filtering for a specific pattern # using *.ext= (so without naming a filter). This option only has effect when # FILTER_SOURCE_FILES is enabled. FILTER_SOURCE_PATTERNS = # If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page (index.html). # This can be useful if you have a project on for instance GitHub and want reuse # the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = @CMAKE_SOURCE_DIR@/README.md #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C, C++ and Fortran comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = YES # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. Note that when using a custom header you are responsible # for the proper inclusion of any scripts and style sheets that doxygen # needs, which is dependent on the configuration options used. # It is advised to generate a default header using "doxygen -w html # header.html footer.html stylesheet.css YourConfigFile" and then modify # that header. Note that the header is subject to change so you typically # have to redo this when upgrading to a newer version of doxygen or when # changing the value of configuration settings such as GENERATE_TREEVIEW! HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If left blank doxygen will # generate a default style sheet. Note that it is recommended to use # HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this # tag will in the future become obsolete. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify an additional # user-defined cascading style sheet that is included after the standard # style sheets created by doxygen. Using this option one can overrule # certain style aspects. This is preferred over using HTML_STYLESHEET # since it does not replace the standard style sheet and is therefor more # robust against future updates. Doxygen will copy the style sheet file to # the output directory. HTML_EXTRA_STYLESHEET = @CMAKE_CURRENT_SOURCE_DIR@/extra.css # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that # the files will be copied as-is; there are no commands or markers available. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the style sheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. HTML_DYNAMIC_SECTIONS = YES # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of # entries shown in the various tree structured indices initially; the user # can expand and collapse entries dynamically later on. Doxygen will expand # the tree to such a level that at most the specified number of entries are # visible (unless a fully collapsed tree already exceeds this amount). # So setting the number of entries 1 will produce a full collapsed tree by # default. 0 is a special value representing an infinite number of entries # and will result in a full expanded tree by default. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely # identify the documentation publisher. This should be a reverse domain-name # style string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) # at top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. Since the tabs have the same information as the # navigation tree you can set this option to NO if you already set # GENERATE_TREEVIEW to YES. DISABLE_INDEX = YES # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. # Since the tree basically has the same information as the tab index you # could consider to set DISABLE_INDEX to NO when enabling this option. GENERATE_TREEVIEW = YES # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values # (range [0,1..20]) that doxygen will group on one line in the generated HTML # documentation. Note that a value of 0 will completely suppress the enum # values from appearing in the overview section. ENUM_VALUES_PER_LINE = 1 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax # (see http://www.mathjax.org) which uses client side Javascript for the # rendering instead of using prerendered bitmaps. Use this if you do not # have LaTeX installed or if you want to formulas look prettier in the HTML # output. When enabled you may also need to install MathJax separately and # configure the path to it using the MATHJAX_RELPATH option. USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # thA MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and # SVG. The default value is HTML-CSS, which is slower, but has the best # compatibility. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the # HTML output directory using the MATHJAX_RELPATH option. The destination # directory should contain the MathJax.js script. For instance, if the mathjax # directory is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to # the MathJax Content Delivery Network so you can quickly see the result without # installing MathJax. # However, it is strongly recommended to install a local # copy of MathJax from http://www.mathjax.org before deployment. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension # names that should be enabled during MathJax rendering. MATHJAX_EXTENSIONS = # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = NO # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a web server instead of a web client using Javascript. # There are two flavours of web server based search depending on the # EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for # searching and an index file used by the script. When EXTERNAL_SEARCH is # enabled the indexing and searching needs to be provided by external tools. # See the manual for details. SERVER_BASED_SEARCH = NO # When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP # script for searching. Instead the search results are written to an XML file # which needs to be processed by an external indexer. Doxygen will invoke an # external search engine pointed to by the SEARCHENGINE_URL option to obtain # the search results. Doxygen ships with an example indexer (doxyindexer) and # search engine (doxysearch.cgi) which are based on the open source search engine # library Xapian. See the manual for configuration details. EXTERNAL_SEARCH = NO # The SEARCHENGINE_URL should point to a search engine hosted by a web server # which will returned the search results when EXTERNAL_SEARCH is enabled. # Doxygen ships with an example search engine (doxysearch) which is based on # the open source search engine library Xapian. See the manual for configuration # details. SEARCHENGINE_URL = # When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed # search data is written to a file for indexing by an external tool. With the # SEARCHDATA_FILE tag the name of this file can be specified. SEARCHDATA_FILE = searchdata.xml # When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the # EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is # useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple # projects and redirect the results back to the right project. EXTERNAL_SEARCH_ID = # The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen # projects other than the one defined by this configuration file, but that are # all added to the same external search index. Each project needs to have a # unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id # of to a relative location where the documentation can be found. # The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ... EXTRA_SEARCH_MAPPINGS = #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = YES # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = YES # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4 # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for # the generated latex document. The footer should contain everything after # the last chapter. If it is left blank doxygen will generate a # standard footer. Notice: only use this tag if you know what you are doing! LATEX_FOOTER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See # http://en.wikipedia.org/wiki/BibTeX for more info. LATEX_BIB_STYLE = plain #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load style sheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = YES # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = YES # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # pointed to by INCLUDE_PATH will be searched when a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition that # overrules the definition found in the source code. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all references to function-like macros # that are alone on a line, have an all uppercase name, and do not end with a # semicolon, because these will confuse the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. For each # tag file the location of the external documentation should be added. The # format of a tag file without this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths # or URLs. Note that each tag file must have a unique name (where the name does # NOT include the path). If a tag file is not located in the directory in which # doxygen is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option also works with HAVE_DOT disabled, but it is recommended to # install and use dot, since it yields more powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = NO # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = YES # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 1 # By default doxygen will use the Helvetica font for all dot files that # doxygen generates. When you want a differently looking font you can specify # the font name using DOT_FONTNAME. You need to make sure dot is able to find # the font, which can be done by putting it in a standard location or by setting # the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the # directory containing the font. DOT_FONTNAME = Helvetica # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the Helvetica font. # If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to # set the path where dot can find it. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = YES # If the UML_LOOK tag is enabled, the fields and methods are shown inside # the class node. If there are many fields or methods and many nodes the # graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS # threshold limits the number of items for each type to make the size more # managable. Set this to 0 for no limit. Note that the threshold may be # exceeded by 50% before the limit is enforced. UML_LIMIT_NUM_FIELDS = 10 # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = YES # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will generate a graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are svg, png, jpg, or gif. # If left blank png will be used. If you choose svg you need to set # HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible in IE 9+ (other browsers do not have this requirement). DOT_IMAGE_FORMAT = png # If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to # enable generation of interactive SVG images that allow zooming and panning. # Note that this requires a modern browser other than Internet Explorer. # Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you # need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible. Older versions of IE do not have SVG support. INTERACTIVE_SVG = YES # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the # \mscfile command). MSCFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = YES # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = YES # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES biometryd-0.3.1/doc/architecture.md000066400000000000000000000047221455450034500172740ustar00rootroot00000000000000# Architecture & Technology This section presents a high-level overview of the system design. The system is devided into a set of core components and concepts that are exposed via DBus to client applications. Please note that we designed the core components such that other types of (remote) interfaces are possible, e.g., a REST API. At the time of this writing, DBus is the primary interface though. The primary implementation language is C++11, and we offer both a C++11 client library as well as QML bindings. Other languages, runtimes and toolkits can easily consume Biomtryd by either leveraging theq aforementioned client bindings or by directly consuming the DBus API. The following diagram gives an overview of the main interfaces as further described in this secion: ![Biometryd overview](images/biometryd.png) ## Device A Device abstracts an arbitrary biometric device. It bundles together access to a set of interfaces that enable client applications to: - enroll and query information about known templates - identify a user from a set of candidate users - verify that a given user is actually interacting with a device ### Template Store A template store enables applications to manage and query information about enrolled templates. A template is device-specific and its actual data is *not* available to applications. Instead, it is referred to and uniquely identified by a numeric id in the context of one specific device implementation. Applications can: - add (enroll) a template to the template store - remove an individual template from the template store - clear out all templates - list all enrolled templates ### Identifier An identifier enables applications to identify one user from a given set of candidate users. ### Verifier A verifier enables applications to verify that a specific user is interacting with a device. ## Operation The overall system and access to its functionality is structured around the notion of an asynchronous operation. An operation is a state machine as shown in: ![Operation state machine](images/state_machine.png) Client applications can start and cancel an operation, all other state transitions are triggered by the device implementation executing an operation. An operation and its state transitions can be observed by client applications and certain type of devices hand out detailed information about the ongoing operation. Please note that both the service and device implementations might cancel an ongoing operation, too. biometryd-0.3.1/doc/conf.py000066400000000000000000000232131455450034500155630ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # biometryd documentation build configuration file, created by # sphinx-quickstart on Fri Jun 17 21:36:48 2016. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # import os import sys # sys.path.insert(0, os.path.abspath('.')) on_rtd = os.environ.get('READTHEDOCS', None) == 'True' if on_rtd: from subprocess import call call('doxygen') # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = ['breathe'] breathe_projects = { "biometryd": "xml" } breathe_default_project = "biometryd" # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] source_parsers = { '.md': 'recommonmark.parser.CommonMarkParser', } source_suffix = ['.rst', '.md'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] # source_suffix = '.md' # The encoding of source files. # # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'biometryd' copyright = u'2016, Canonical Ltd.' author = u'Thomas Voß' # 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 = u'0.0.1' # The full version, including alpha/beta/rc tags. release = u'0.0.1' # 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 = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # # today = '' # # Else, today_fmt is used as the format for a strftime call. # # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. # # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. # keep_warnings = False # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # # html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] # The name for this set of Sphinx documents. # " v documentation" by default. # # html_title = u'biometryd v0.0.1' # A shorter title for the navigation bar. Default is the same as html_title. # # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # # html_logo = None # The name of an image file (relative to this directory) to use as a favicon of # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. # # html_extra_path = [] # If not None, a 'Last updated on:' timestamp is inserted at every page # bottom, using the given strftime format. # The empty string is equivalent to '%b %d, %Y'. # # html_last_updated_fmt = None # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # # html_additional_pages = {} # If false, no module index is generated. # # html_domain_indices = True # If false, no index is generated. # # html_use_index = True # If true, the index is split into individual pages for each letter. # # html_split_index = False # If true, links to the reST sources are added to the pages. # # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Language to be used for generating the HTML full-text search index. # Sphinx supports the following languages: # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr', 'zh' # # html_search_language = 'en' # A dictionary with options for the search language support, empty by default. # 'ja' uses this config value. # 'zh' user can custom change `jieba` dictionary path. # # html_search_options = {'type': 'default'} # The name of a javascript file (relative to the configuration directory) that # implements a search results scorer. If empty, the default will be used. # # html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. htmlhelp_basename = 'biometryddoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # 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, 'biometryd.tex', u'biometryd Documentation', u'Thomas Voß', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # # latex_logo = None # 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 = False # If true, show URL addresses after external links. # # latex_show_urls = False # Documents to append as an appendix to all manuals. # # latex_appendices = [] # If false, no module index is generated. # # latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'biometryd', u'biometryd Documentation', [author], 1) ] # If true, show URL addresses after external links. # # man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'biometryd', u'biometryd Documentation', author, 'biometryd', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. # # texinfo_appendices = [] # If false, no module index is generated. # # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # # texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. # # texinfo_no_detailmenu = False biometryd-0.3.1/doc/extending_biometryd.md000066400000000000000000000033251455450034500206530ustar00rootroot00000000000000# Extending Biometryd Biometryd can be extended by implementing the interface `biometry::Device`. We support both in-tree and out-of-tree plugins. In-tree plugin authors should add their device implementation in the folder `${BIOMETRYD_ROOT}/src/biometry/devices` and submit their code contribution as a merge proposal to `https://launchpad.net/biometryd`. Out-of-tree plugin authors should rely on - `BIOMETRYD_DEVICES_PLUGIN_DESCRIBE(name, author, desc, major, minor, patch)` - `name` The name of the plugin - `author` The author of the plugin - `desc` Human-readable description of the plugin - `major` Major revision of the plugin - `minor` Minor revision of the plugin - `patch` Patch level of the plugin - `BIOMETRYD_DEVICES_PLUGIN_CREATE` - `BIOMETRYD_DEVICES_PLUGIN_DESTROY` to describe, instantiate and destroy their plugin, respectively. The following snippet demonstrates a complete plugin definition. The resulting shared object file should be installed to `biometryd config --flag default_plugin_directory`. Once the plugin is installed, it can be referenced by its name as passed to `BIOMETRYD_DEVICES_PLUGIN_DESCRIBE`. ```c++ #include #include "mock_device.h" /// [Defining the create function] BIOMETRYD_DEVICES_PLUGIN_CREATE { return new testing::MockDevice(); } /// [Defining the create function] /// [Defining the destroy function] BIOMETRYD_DEVICES_PLUGIN_DESTROY { delete d; } /// [Defining the destroy function] /// [Describing the plugin] BIOMETRYD_DEVICES_PLUGIN_DESCRIBE( "TestPlugin", "Thomas Voß ", "Just a plugin for testing purposes", 0, 0, 0) /// [Describing the plugin] ``` biometryd-0.3.1/doc/images/000077500000000000000000000000001455450034500155305ustar00rootroot00000000000000biometryd-0.3.1/doc/images/biometryd.png000066400000000000000000001660751455450034500202530ustar00rootroot00000000000000PNG  IHDR- IDATx^TGƟE*XQQF1{;X(*S/1&jbbE{ED;3Y9yqw3}wl$@$@$@$@$@$@$@I"HR/v"      E4 $EtA PDs @ PD' E4 $EtA PDs @ PD' @zP 5>%I@ XJ.$@$@$)gHH Ԩ^Uq( D(~~3HHRB":% !^>ժVJÑ9 &M[b}  H $@$(" $N$v! HEt؁HҗEt$"@͵@$@$(ӂ"  TN<>J @ XJ$@$E4 d2L1(sL( +tIH qq|h)\(qAI$@DPF$@$ h. dq0|$֎g.K4 ] ##C<pCu{;Jј5w zvremR=@$@$1(33g! (c CǮk7,,c|p/^wۊz}8n_N}(^&&&??yK[ڪ,22=g//9[x-VE> aU._a_N>Sضc?ƌ q߱ccSJ~1 ,,LShaDEE!uj("нnѺSzD$@A":3sN  Ltq'~>tԭGO":*zcb?q6򚙢\u`jjQ[lY6enѬcʌE+cd}&i5 Vlέd9>DV鳗0nz P|ǰ_n9X,XB?y[/cZ4on;'7}N$@iG":Xr$ Hmy?Rz 5uj٣]R܊s";c**/~/GgvIݡ,Y0M8y̟=Sg,DrѻgG98g,2bɿOcT| hy|Qhk|!(,ص{LL-*&,(xl-߽üYmpu 5Q'vU+x[w엾,O?cOaTU\d?zSɬ܆.B8CEt <& 4&s&McHRJ"?rdm`6k& #QDQL2ՖEkq޷s"keURf߸C_QQR A-*pu)L Q=|h,=}X1dA2[60ӄڇ!_o%8Y?P2^C(&OZ[QC '['k6m6$U ,j\rthx"Z'H'HH PDgzh @N'@=V~)˗+1{s&gZI3NIH PD5QG$@$@L`ӧܽP3Y]G":Ō @V$@BHrn:(3>& "@C+$@ٓEt~(_h1 dEY1*H G"Z %E29w:KIףqPB1/`FF͹HHtEĒ dS5(l$@B@ "C&$$@$@:Gjӹ! F":Ef{J.~  L!@)9) GgH cLtp,$@$(u=H !:B"ZGI7HH PDgr8= PDs @ΜHtEG dyY>D4PGPDH  d2L' hÙ ? ,O "022D-::7nEhH(ʕydx TS5I?yaddڪDS=|ȟ"Ѿ=PDg8J (zh H=~:~[|lJ%&MWPdq4!~U#,:.v DFF"<LMM=NRl6hQL02>jv0t&wB;stvCNm:1y||Ky{{!""yRc75m US-QѶ<5çd=RDUd8 l9;H H}=-2kҦ)=e>)åFDl3~ҩurw<Ϟ(?q_;x=gHD [C(E6%WߠG'=#$ĩ.=J*p=|׬!\r%i؝?5ڪd!bׯp=OG4 " M":GΓ dIOkP%֮߁֭Dz+Wn"~ uvY?X KmJ[!Cʡp;@_ Y??xXfvqMx(hѬ rB2 K"s~ -c-\s_[U? ^ BbEPbyq|C\1ԪYMBϾ#1{eGd ͠ի@bի7ak[V~.=DacSJ=K| ˕[p8۩gX`xr[>yJ%PB}-)f޹P- Z5UAN?w<-7B^g"&w`W\/^§h"|mɾw!, D{6HfU*)j !;zŁ=Xg=~kŋF)o<@$@ٟEt!= "s5WRUw۹]"ը]:߸2^|bWQ2\Wn""3N` YPZGD^ŋʭ J>%6>=Cu帣NCv-ahĺ >k1<} 夈ssCЛo$YCj{ŋGv4y 1mLk8TG "R{wHa{wUFhuGD`[&"?F1bl߼B7oX˥ob+|.0p@w8"Z}?~'X-9O勊3`d)7m _Ҟ.a՚Ϛ/\ [˥ j֨eJ[c<.ؽ}5o܅y2?lJ,{NmbĔ":$@$EPDg@  K 1ݳU}ytu&g Gw EގX=V zyYC$KDk"/8;ccY@f8UMXd6.a]3O"d[s`jf$ݱZli../\V}m\|TdxR v G 4ѨS<^KghB Xdd{(͋7gE"qEƴ;czh <h1$Ѣ"MML68n9GaB/<{yPp!y6Dq2qֵPAKy7y@l9KpM!;Fh#ذi<+8PFG#2* ;6{k)Y/[pս3/<]a ?~-x9L6Exb}byW[a1;kf"E}fU]UXKܵ>>Hu~2BDkWG>ӯȓP~hqV0bxjT-vK!mmI5sٺc?Y  dQ8h+K"geӧ/-k"iSm;W(^{foCG$@كEt$ahbO#J߅|ye,m-$4 aa061>ĶhqS^3 ar\[c6_ZBa✬fm1_ls;v(EȊW93\ АP(/AoQ E[/1 33SCJc'̂LM`ϟ?˳/_DA13Sccrsdjv&4_r#EtrǾ$@$@PDsm @&Hd3L|z<~4#&x7BkҔh}SԮ bϛ=JhSe3g/ÿ{Ae]"ZHHH PDg~ h @']Ed4v.G*'~jrpb@'@EHHH@PD\H @zHLDרf\SDT+WDT^eB.Nf0=b"":IHH e(SƍO `h eI?@5.ц"W\Jg'(Q":/0N$@$ PDg8  H 1]z %^8Noo_\zX}/TbT^u4Etb= dcϙIH)DEC (>],\tpF,P>1)#IHH s PDg.N$@$ $*WuRYb`[^ QJ(] 61w)#IHH s PDg.N$@$ $&UV^?Wy(.\C:?55U eĥKWj@ M& Q(sT, @ZHLD9F@P޽۟;T=2Κ?SDEd8 ?gHHt@RDpjժJi``]kkk#hq: 1  "T^s\   #@y93 @6%@MGIHH PDDA$@$PDx[  $@@$@$`ooo }zHP|<PX+'U344MDGG|Pɶ\LQI ( IH@G PDh` @N[vcJDGGc%ٽʕM[ss4sy?E@>ؖ/ 33y^,X-7N|0(Ί=IH@ PDrt @N>"""#Ę@Tbhۺ9 ϗ>\Yo.i6\Ŋӧ/p}L7_ծ99k\BC#@<^M$@J"ZW#KH2Et9z _ u)%'zoߩ ?SVM{mw=jՊ^ ssTa;c.mǢwvRJ o^S=ߗTbAoÇY?yYlOoy<NWgЏ'nÎ04 |Z2 ䷐slY/^!2* - Ȭ]/} + CQhxKhQ)sF% $F":1BH!@KcxʠT8q4&Mp[ߍ;{50c9jTg_Ad,Ohe WCBp=53Erqb ::"xu*_bt(Q=@UsW3)~U?~D*qfMw}/]|M0{x7nu<ؽzzzO"CX5_bi00Їe\fߥXxUF-3OYx3SSX-wsݗn_׆] gbb!#RD@M HE4 Et*,/ZMK67p;c\fU˦hk{QGcJ<~Tcٓ<SX1=_"i22i5mܹw/Z:Gmݦ{΃g^?b,|aY(\RfMHyCļYcj?}c=(Vz[aE [GЛ`8 +F_^x Y}rȈ9$tH(HH } PD=_Q0 3TwBBC40oX [̵>aѮW?f6aE(^x1} Z,Y87an Sxhެ!@~&Yh?DZp0fm2֥ >]}:c`|?<%MO$3b8>uB|]:oc'Dfo?[b2ܴI})2oܼ=g /G6Ю%ZmZ2?lK<8 =Gu",U QH!rޡ `ЀPE+N݆b13¯qQխy^hѼ! XG ee\G̹hݪm؁K@u _ =޼_2䢉L>Z9gDkzJ$@\$@$(;峀 r{(ȵsOx)&?!0:̜y3~:zv&bE`Cr<֭pL2mˢm t*eϟ?T7g7n\/ؼ2#'ʪ}[BbED12!goݾu#44 ܧg8u6]""ay |䉣P|\|s|租ñ㧰c| GKh(sV- h#L4 Et*z\Tg4Æ(5i|ԯW[հǺZj=;굛[WɑvveY#&uRp?jf! \GV^FFp#a_~J,fQ?<޲XD'ys&ߠCۖrDa'߅w'LM6b@h;vT#D<<{ m߷6Z9k4ozK$@\$@$N("KkaW]mMdE8o^3M5Wp;V]%jy`n7!CB0i|4o@nM;aUÅh" -ʕ+W\/rLfdMC:SD@M H3\"$@$Jѩ_?e]ЩClM)蔒s$@$[(u+H PDgLRdEDbr5[!@sbMOIH !\$@$JѩI &$@$(0'}ѺczHE4 @u@$@$@J| l(I$@L":sx 'P]Ot-\5oCȡYߎ7T]tH @ *P|H@ +.P:i:H$@_\$@$Jի-U@lbl2R9'Ȫw>ᘉΪQ]$@$(ӟ1g q<{$?<ͥ@$@$ PDs @* PD 'lB":f @:NgH@ PD~! \$@$@Ds @Nl$H$@@ )Ht@VѸq.ʗA\t/aax%J(wP5 uIF:X|<)3rq. Ⱥ(nlh @6!Dx'dG!!he~ܷyΙ^v M?놂 `XfZ c8vy|bjдf2BC?ƱH(S:|I &ɖ+Wo՛ȥ elJZP(x.b#brsx9jtuIRD')D$@:O"ZCLIқ@b"sZXZC9~RNzu쏿q?cZq?{01΃HD[CHKp&٬ZϟB:5|]4fjhݪYZI]c}Z9Q[;MA(w!ㄣw$:FJ:lٶWW5kZ2 w(3$@${(u/H $&/]2-5AnT(_FZ: \-PV~&;:(HfUBTt5W=gePhxEp]DGGbrjͮAڪJ$HSd2+l_7?g˗/ܹ 7*v-"3n޼WAA0Γuj{VjjOǞ>rr%[̯]^9KB`?bkի7ak[6{Tⰱ)GK| ˕A|`oyMc?F<鿵UI|s1<–JMOfF?nf;ڶDְgſlϮCǡGhڤ~ ^ BϾ#dtyEeV:ޥjײǤ)ѾmKs|":r: Ȣ(h`h @!x Ź XkUQ5\: w>024B epC)gpJ֮iCvmZlw,+7O_ɨh[BDǶKdd;j*˾uC捵cZܸqG oa1Bg*ٖ 2q,-#?|&x"݆PJz=ߪֳHM!_Pt2tG#remp:SYz38jx:6FnW1܆8{fyMQ aax9z/Vc{/1ذi|}Kњv !1q6.V!zö+ƱWQp1PP<1s{._Q0nk-x>"""##u,Z |ŰiyhҨ7kP)d5Jپe`lKC3GЛ`4KD"c0_i={Gʩ>=;̻6s!X]41 M4-b(DtF1!8QoNȮ˳^EکJDknN$vELcy,_0EtN'IH Qщ"b H@rE0w>Df:E%aq!pzzP۫׮@pBq e0S Ѣԏ?Qv e7>,\J+ȹ">E@C7}c˶hڸ.:obE +^=:7u0"s> #eߴ>+=b3sTZ۴ sQzjaֽb0ѯqúhҸ~ =_B\ߧ`-X<`m]=8WvnMVR(_,GbUӋ" \$@$Jh5J%C>]7,,Գ)R\!p}~ (VH=jTܦDb󄱎(_4<аP)c H.1)GLhvx5>|=ۻ9Z\#']7ѳbl^,N}maˤ9yNhGg7tjJRe5X>cdG6^1(rtL9ͬKPr"A'&>-^2,4p!-4 1<{b~`ȁf'MwAߢ7uн~;] Hh% äӃ8>yp ѩeIH@GPDH  @HLDoܼ?TѲpxiRt@lg|coMLD]@?q,W87Dhhܵ}ܞsxѱ C1i]CU٥lHfwz¦uK BmyB̟= rAd * p޽jȤw1T^Uڲ-u> 满UTU}xX-P=`4&8Nm\~ gLL$m\_~gr5X;9OFnܰTAMXBbkLjܠ| !~BkLOSqqeBFѶ^lߕ6I+x9O+҉#ѫ{{݅":~rf (txh @v ǹΆ2:QQر_^xHB qrRBǏp5k(蝻a'z>*h)ϛ~S<].^r YQycu>Hh8eE,m b9u|"Ә xBD-)ŴِkL\E`MI̛51^6{vİ68yY![lc/|7~E#EFY(+*:ZV5T&#sXhM՟%d8?m3l/BYd58-L2!qUgŎ]?b92/^T-NWXL >&:- HV\bc|lb?+1ϞJ[a|Ij,k5!$TQvl1W|v>²@>)5+sd#ujד\!;c̈́"Z= H"$b7 F )":w7P'263|Er f!p}o*3"k|^+kWlΞ>}s&Έ55Ѿt>蔒s$@$[(u+H FD!(Ux,猯߼embdDq.BBBPbI\"KS3_5[l5NjR!z/^Tq*dGIKDal,J$@H":rh A 1}#Ro7֤a.yFI1,!YRr(&M)8 ]dq':K|&ɞSG=e;L@|277KXV=$ Nb8'{p2U r톺(f1_6m *X Eӈz ZX"Uvuqߺc?~WTd ~g](/o'd# IDAT QF>)u2tHM": $DGE^ZqХVDz=8ŸC'8}]:ƭ[6;xO˱&%%sݽPL)tVvW9,ݓxET&)I'粵EBT]U>1NǴ1΃a__غ}z w gOD7oee6!EAΝZcѭ5at?E΅ @PD" $&h?X4=_pe~TgE$KWdƱZJh/_rE>s8T,4{% ~K~\w68s+Vm{V~'jS}#| GJ6dQ{ǖ OǞ>1 ͛6\dCMڒCu,vlN|QёbWIe[aXzNh T(2 YXms*:eF򊧏jֳHMC AU*J1-DBɺ.^ D.mѩ}+DEEa%x9?9F":5zܘ2JTzVVVm^N zj}cw{woo}5;ܽ>VhA>z$Uӧ/nhټq ?y&̯\>_VT":y^-k wnsUˌf68괄Xk68V(!w~K(s $@$@5@$@iB 9"c̪@ߴKf ş/I G5rTjTfKک-~Sznc%c>zx{c ,: lP+@S]]%ߋAqoO<MCQ2$Yj0~fMw!=A(L_}-x5O8'ܱ \ 7o_Rr\w  cj2P٫X|vu[%2F ۩U(l&Df&Z, !<ό;rCIBDqέY=[%Q_5&VX[.2c]fB\91o2AX024qMUF$@$NGH gH8t<,y&z ȕKOnGhm -7ƏvS#ٽk8"z6DFFqj3,AժѾMD[$\ع;vQ4M]V"2f޾s`uX &2ya$h!{)F`AK)\b~2/UyB_G|(wflؼ ؂ūPXa!U":!E8rƏ.w=Y1u ܌4Et 9"tH = $GDmhjhuT[yaY J{;CdillGD=}<ϫٴ۾ v<5kTdmM)*s/\Rٖ/+\Iqw?aaa:J Ϟcl(e<~ܹ/"k,嶍^2 [B.^EKV;řjqxx!tFV ov㛦>y/X񥈚MIL4:":!זxz7LMˇqmtI1q;ZN$@iI":-ir, I 1!USyoV߽{̵=h#aff$uN!rarYsm9tCy+,&:4 B7~񢣣eA13Sca_īoE L,|ΟBVV i/+!5N\7TDFCH(,+)"g=eF|v'e.]) E4G+\ugD$@$E4W CbݪbkNŐIzTdo޺,Iht[vJw% ䷈wy2{ƛ7op%tEϲ:. WŋWϥ\[G!foܺTXA}vZ1ҏw8VBO~q$@$(3;H V(  E#@iP^ B4_8=Em"+N&˪yO3xGv(_tyƺ̀;N:Sikb ~-Y\}YQZ9MmX2/DWܫ 'yY%j߾}ujWO4/b҄2>i|z$+':K|&ISQdyJf6*D D+'B$4v|فH )I$-jzJR MT"XLntT޽w!\ 9Ԧ6/ `%uB2Эs[yD j$꺜*e5N]whqG9?Xj#vnr3w=餶?tn݃9Jqxr(^ϿlC@ۙh{^#QTJs26eR8FpBT~~~xQIHRC":5, @#zVzz {@iP2ÜxS*'"Ze  ţPѶz!)(E8̠VZI}f򺦏a\6fJw-Qk[\ɱRrK2jb+**Rב@6V%ѥPLuZ5&i*ŋq{ʕjŪMp7\iSJg ?OuQ@>|߶~ڷsFU622J|AG X E%J d @:NgH _)J=)kN}(GGG  JHDUz9brܹl^QQQ: Z#c8^|IܦC|]ō|-BYy>fL ՕWZ XkUQY9nݚR<W^O~Bx6ݹGw{w(9vM{yΝ{;66bdu8>ƂyQXO$w 1-ħBm] /ʢ^ڷWA2] y^߷h]ٵ6Z0`X w?P6i;Js|0ڍۘ8yvm]B"ZY@RaUx=V]EiÐAg/(X0?5~Au!; M7x9NrNlHNh* з>D[fS".RU: Mԗ]:u3xM`>ȱSѪeS4U4LǷ}ZSDaCW5df[oG^*MfkE/X[cco:rHovm[\u~#2f(6wl`nIbkѓ{jBC?{Vŋʘ4nXzܙ|mTD;TBua.ܹnjٞ>/Moa̞X}2 dNV(/Iq;)؇H t,~-/ǁY'1-tubIYq,V:yݶ;*MLtѮ̛|]SUH!\r-ޱ ?y*{-Z,ʣ Eǎv{「bWb^X-Ė`o` *X(̐8z??۝}wݙˑ'w8ׇ.&K۰yrs֭pk*6aƥh֟tx8O߽ fsD0rx?&TY3yʞ}pѢY#{Wd/nצ#S&qc6rʉE\HtpV* h*VASTSGS||ESZArms' @"bk^B\$d=o9ɣX wʕ93eEVKN4ݑuPvLvӿmݾ>Kgk$26v v 5Kl)H PxQnk˽en 3ӝ-ӽ#o\>$f$#/Bٝ9<"B3 n<{[6xs+ \terڱh$: UlėDӝhCo]1wh5߱_9\9JK BSՒ" $L'F,R.5ٔ^怓1$| 5xU/Z6^gaؾe%)h"MĬK!XfLy co& رs?r̎ר#smmM,^F%1q+ӵfDŒ!ys篈HV0q귿} >, !цOt@C.~jըn șX Nɩb}k+JQ^LsKTz,؊b}!q+  `6Y @\zTƐ1S;̑=F4섎-44?#]ev}Vo?ҥǨj(U"kw7f5΀VoCàDEqCEDDPeVx}긍P\i=6bȗG2R, |&ČI$:6dB{NB`"AQ*%Hvע<))_z^SYIB-}A(Y8%DdzD{,$5M28q ȝ{Dk\h Tb5߮"a!em,/U 5jjEQ@I%A@0DL@ !IZr*SP/JA19~tMk2_ӽԚ,7nt)61 :ʊs`CQL'-RTE!8J)J i A@HN3,CܯvVVtMa9:2 N $psG@HtP 6V`'Hk 7BHj@Z e#$fAd!I6VVVEldTJFI#Q!~~sJ  B@HtAӠ^*{cܯ(⧶*GTVeSyT|y֘<-fOq,9_CL%`+ TPmU('Ev Mt+X$irfYM-GbT@9_8,&y<l8)lN8`tyDHN.C6$EB$M*愴!Zv*JI/' ꤯gNVPJe? HɔF@Ht Ot6Hܠ^QA~6֍|VV'HBq5ycH⧨ XХ ` uP/JUPT >TBRFVVf:ƙ^!IP8VbE@H,A@H+$VASTVM405D.[#}+ TN*[Q dMJuRS Y]B@HtJ!/ 9!&P)V*'+J;UQ!QQC\9 $feͤj1T U~}Rr*[@G@Ht_2|A@0 ֕&l"P)GA(R ! $:O-ݺ89 KQ܁ UQH(E߽ xڵ7e2A@!ѲA@H: *U+dpJt! $:M-;;;lRA՛(ʩ(%j$)%KfȖ-   )D @2!VjWp%J@-E0+DtX0)},l9w>4춙MEDh˙WTC@Hq8I-A@LEY["OTJ|w5v NJtSi-J{mc^.Ύ#$:. '_.ɂse Ee9߀6ҩt"h#jGSLeeo(hY5 ZZgV%悀IsMD0bSO{j@H `!1As.iуSBSt&`\)SA!3ҋ $?BsQjG `{>~C@Fl6י1S[[&V9b'$L'Q# $:J ĉZR19|@*Ʉd:tcQHtju "`.A@⇀&O%=BTՃ(T52A@' <& 5,NV-3Qf?2A@H8  @\ !=R[h,tDlA@HTD&*Ҙ z]+ \m3cr23ӉA Y]pKg F]F'-$&ɜDefN!@J! 0~A -! 6-Ͷe(Xɲ&& *;[ r2iV- E_͖&+9yN]k|Hdɜ)XIV]Zy $'kmDkoǏ1|hu]hբ1ȓ^ ?l-rC'O~Nn9.恍5Ə++Cp毳;=ֶ%̜׮۬Yp- ܋-ݽ\B#6($:μ۔]>^v ' `<kJj&/BNuɓE X" whh(|.JeU>}:M@p*gCŲKxPro}Fӯ`w"E !s^S"wͪ)|#&OER52ܼ;wx(f_$۷& *;UDL޾0vjƍ@P|2lٲ"{? 8}\TP:1/^BC1ӧGrMJ5ݹŠJ*NȘ1>{"gpT1+'u<.$:ụtؕ,Y2ClT8_4'yBڴ;>r! w3,[)SxWԨ w"k1W0{28;gSxhӱ[M_}76+J*mfܡBB`ԹL&s΅~غa >| GמPّGAןogFre`k/^ڷiuzsp~8uO ehTʵ8iŚ\/[L;fNzݻK^1i4~8sxYH.Oo6-E?g7>˕t66?C9wrYK@0 ?{f jwώ|5g jըwaa_ʼntFнK12 kcG:m+~Sv!$t A@HMoΣ!tzlٰ[ٴ;&Fqh޴!׭}0h@7ԯ[ ߿G}㛕!SzuN1g;ʖ)G3X8o x K,L?~ z,•7XK3㧣yhؠؠ_θz&Zm!""Rs?Xq/@b})ݻ8q  Ǻb&ːu㶉`%IepV uL|wm먧N_2eJj\UPp+M ~Si0 _6_~i<ǹɓgIxϟo߱k}1޶{v37Sb!t;;b}ػ0[.CƣΨ^͙يH ڇϿa=X|Nɖf}6|?1}X)]"'u<*$:ụtة[m: ZY技 YdX}ըA87CV[Q >yLCO7E,Mgr6ye,XӸ6- w(f_*u۶n.k|@|{k~ rʡ49̜%3`HF@Hl˸A 9]hK_ $,x91\*;$˾ht^i) !ѩk>e4#`V 6ke=+Buf(G9w…8tJ&_ԇ72"q8I-A@ڄ'&%F1gG8vxGR"m ! $&LM4dchPJC D@t,sE@HΌ%XB-`D$A@6vI4*@ Dʂ0WD̈\ R$ɩU^jP٫)`"(Gj_pr K}r'eDצOGEEx2" *,E5 @r_@Bƥ~T?GsF@F/I.?GD@Q{?GI(6K<E =IQ9Wv8R͓˝MK[ X  4yCLO6бbZ̜ ` h԰.*UЏ<Ɗb}Ç;h &uCMBPꟴPyMH \| 66\"J7EU_A4"#"Ѿ j3`ɝ U\$Օ"?{'on8T(卵$ՄFDTՋovay1~ŒYѭK[*Y,Ћ۷aȞ6p @\9cIm:ǪsP`~]Dd$dbtX[+W!w=2}h˘'srڭP +?Ų0}XO.i ̟gQɡf͢i}uɓ M4uJ/9sCf fN%Tny|0 CLh6Zđ(Wdϟ:'?e3bNq U"<<3g/V ??Ϣi<)"ƻDZhTM}QQ]j5o1dD)\'P,ڶnW׭0,?-X(?g;x p\wc+;w#sxflS'`K0=3cGa;YmD˗JXL)u148 *V(٨s_O "22J7%"ыxzBg^bԈ7y!b/_Y.$,Ţ>שc^&STqi|&O[`=y 7nq=x*rȮ"=|fEcvC1ӧGr[ FDzD7dȐJ>r&]jh_čx=}b=Ŕ'=L.: c. +ռҧKyssog|h.T8;};v5}:e߇> uߠҩl{ㇻE.zu.p-֥EDBq-ƃqێ>~8reGٲ%Sdʔ/^ m6iꤙ$D YI4@#NC*yHU8z>"K2z .Yh2v *iws\:3f.Ir.XvjͧF3eIbL2=}+9LΛL3j%I/ePO2g"Ԇʊ]|ښ bl̛= ˕UhC$zhڶi5gjժp ѭSkԫ[8ľҌM{zCNmЬic^r#+3&,> ˗ÇOxF" ɫ@A˶}P3dɂuk^s2x 凧h&RliąAصDqr{gOFlohM`9Ț%f0pt(+Vmf <++t6:i$\&GQ2/[|`ۙ0s8ǒnFmE$+ ÿ|b5uMHGMG,4wMIZp$cfG7zo\r2gڵD5T~XOԭ[7G{INX7r44hPmZ6QN,wWsDX6>g'&Us7 4C$&K?mX˧}걩I 쁥3> C5mٰۤu ΔI# G:5Ъ]ވiuV 7jwǟ1ܹ$lDx$Ə^ mIicyM?p.\F/jy/Ʀ-aZCJfuw}.mҽOc%M+bL7l8e#|/=V_7Y /:L7hxK^}6ƴ׍?wcԘn.uDLXAY|=lXymބ7~ݚ tC؛E>Jܿ71<}cv93QLI>l;~ !kY$Uq"{b/ OG& 5.ФKۤ_άZm!""Rs?Y^8T,N[bђ| 8i4?6f*/b]۽x%}MVxR|몑U}+LG/㋍[Ú ,sBe "/_`n bY=}/_?6ܲm7^K{6s[d ?;wN3V\d &[ߴ͔)s <L3Z nj$Qy\پ@ھ9:W'qUO ?:s$%?Jɕn섙Ȕ1Z $OD)L~.GtҠ{he]ި s.Zt&dLr&7 oܾݻ'YK? עy#H0ld~Qz +\HY4w;#?5 ?!/\E>رuAyitI4mbǖY(;kh¤{vn~0gr>%B2CVx"S'mL?;6WG/Kr>?P?w_ֽWOs絫B5TDO>'WNmӔ+E'U*U=ZOOxB @Bex Ayi7o6V:&׺}_۽-$O> ]hѶ7[RȻ \p>gPg|@Iz ]ԯM[ݯڍ,bڠ͓!]ܮ̚>{mWe1a,> ]j{9%7BA|Ȳp|i)7I%ٿOgVe`7;e=YuŇеKߺ}Yi`lt֤q}&~Thݶu3̙ Ŷf+"#1lhCq=X|Aw-4g >ܤnD( _]PKA{9Q.&CjU+-+7O.fNFO #?~d-%B 5H CjDS=Rpd;BMVCRr.s$p]9)ahR?Ġ`lZ˗o_ҫ.}nݪÀm&]ȂCw${vkv8ADNVnbv{tzx }' Y>f.Drcl*uݹ㧓Pm{! Z{"A!Lqw`ޢؾy=yLCO7osY!2:ykr>U]f͖3 2h1' F} :=t;ń 2p6<5?^HxV-`o{ʵL^t6´ѧɢ_>/]W/\5.tKVdng.[թ0&d"UX̾*;U`.eYJ4\ovil֮AVN-+Wn`8^2ww}PGRН[`1u+R#L۰&t E!88#=0_wK Ӫ}_^1# {OܹiLG Pe7kMoJ3wrL8-dQ~W,}D=&:#OQp=}st,T@n\7 Wa%f] 9YUhq%y!LZys cl$Z̔EwIS2u087m$c/]p=T&] ||L+(gi3r`-bJ`1gt;<"BܹSD'@3x\Ś6(_aM`͏wkxP\Qc[vhPV y\t]ԹL&N3%FSD;t)]{pdV|w:ujWC<"ځĴ& 9\~!2CO(Y(t& D٣[E6a¤YW&TjC ntxI5Fi ^UTw +'}A4e!"hꌀyUΘc"tP,ʎѡ׼]io}p7uK2 sBoի(@N۱v 1i\.ld͂zC\U̚} Iޖ3c6ͪ!vAhoM4<u-Ѥ|)ͩ5?xw(INpT5m,uVC |5-]ǹ*:v7̮G#DEa Ti_, تLAYv9i0y֗bڲwMD'P N fxНlyC,L ·Do(qTxZ4o|xNE wDl sǾ2@-Ny?3eZo&d c<6 cy쑢HLsHɳySHܬ)|s"ؤCdT$3),Zs#"kOqD"c8V=E4Fvͪo%X.YA@޽?3X"B<P 7#oܬ_hЩCK[zHX2$3;Y[hoJ{JJxȨFW ?tNMj!Mv}9(71:ipoݺА$yKKV"X J@\o0 ‹-fͬI's՗1W3Y 8JۻKŋWȖ֠^kDiHՇd^tx/p"IEʖ@(CY2go}2cʜ%s,AϞwV;aة` ǭ } 3msd~|r lh̾P!݆>HLp6UҕJJ[cd#ەdd%DB]ҥIJよ,vL\( =z)/lJU@R5kf9%%'R`*NEj+xBe:mi!i{S)'naR,ˑd_7K!E$C@HtA+ ))`}dq0ad%tԦcwS*rvTQPȺJZ«]((cY&1k"};ktzﺁĴۉKDMd>c atfZӵ&A ._ *WA  Di+zdšIm 8xn^B}"C:T(k> ;S#+߅K6CRklJZWHΌȕPZE.RYo"|''y>eKI@J_/[ذ璔@h TE ERΝ. ^GcH+55B@?I:"gb#q Kt#/V`(V͉SYAwzwźhTMwԁ"'`5֝ѝ㦣~ȓ;f͘Yl]_Htr#.  DjA HQ}vrLA (hSbEPXQ0(Q$]tlɣNv3B5t53=5,xf .D)` Bw(-C[NzE7-[))f}}ycy;EWBwRiRq~<ҧNE,Q*@Q9Y*wz)pZhܔ_3XhL sC88=&+ŋ/9xX2b'/Mx=waQ|ip! ^|>(6)o :lpot|7=Je*DB)o/w 4.=,=eʑ/aYw6P71}"nEm4_A6.F}4%0JBd"D A@HN.$B EIvj9ES}zubp Ou%#]6hjl!݂ kعMnIrɨ\?`GG2e՛"RJ΄ڰVYPUښ4,ϛ=~Y;aJG|yx-+u>m!GxYZ "<5:F5y}<|~ 3eIbD{ujf:$xb2x B" K(O6Eʥ(4k*{F\׮Dӽqc+V~~0mj׬ʍMkpaIW9'l:+[th-$Ѧ;gGߺeP0I4};{玭guqc׹KA}VR8:C) E|kF)y_\TvTzvWar- T!c8wp=bDqqvTڱ;>{'ZMfʖ.^Gawbk&'Oܽ8tlt;<¸C8:R.Ԯ%խaGDݺ5оM$wQܩ[ǩ1РAw~ "T(?9ɜ% 6|N7J;"7 4R%JBwBګXĎDѸ)"%y_=˖ rJ74St)Fn_ y]ao_9z";Rͷ{fetZdv|g}c^PEOJE@/\_Ff_jDgݑd:x>Thu#4nT׫(DDoۼ D)۪[>g#^7R$ڥrgPY(DXA@ EIռn}ar' oJuhK1#єK֨] y2hr&>~ٱFM{_ܣۣvøx@`#:XP=4.Z4odwھ893*H4\BwG~: k~B_I K;o߱svOp%xZ$z&e{о,#{aݪxQcɣPHtvuDm\E?fdʘ  uvۏn!SD;WvS̓ "b @"#pѷvőܼD+ %Wҏ I]}ۙSM Y>yӒ+gAԯWƖmg/c$S}VDe>6G ԐjU+-+v][:Mmґ;d-=>ӧLݶIMKW|[>K4գt͛4;BMlCeHCǴ7y]K4/JoH^K}lŽ17><ޛy8xxwߛ M(ƌ"oAXo^Awbhr W[}8xW)\jN6e|9z?|KCM?<ɯDDkN`gc׸%KA}RR޵z;%,QJ fH;YWgGV͛wՅփ\Nugj361eSffS"hK1Wd%Zr)ڲa/cq WQ [)ۘ 3DE!"2;kX,bkX3r'wKP gM2WB6,L۷kicix*?|M߮ԋMwٳ~EhX(oYɄzæ|x)S&ŊN4uFwb1ip}\跟93ko"?r5(/{ IDATۥ>#u%/]e,Y2<;9sdǸI8xT5m,ކێc9s&cӵk[\DՖh:.mS֝-~y6galBMEL  xN<,%A@DCNw `r͖-(u!R*8l[4r&]u:`OF}?<3a6rK%(27Y[(Qpk&Q[][۬ncup/MELL;:MN_vק+"#mH^]!>tW"k< 3oްrS֝!y3Fvc٘v,hK)SR#BSʘA H\tI[(^~KCDD$z InI](Gn*}2QI)۷a) E^xŢC<މNh{|\{Sk<S̎i˫a$|:4o܂ *+[ӛ[ s}UwCd͚9sդ CUʨ@q+hGJ0v&Cu ȇ>3vٳ!c IC{tf7V(eʔ.k~)W-D"I4 Ɣ^HѢtBj.\6ţyg8-^(un뺉"#"Ѿ׉}f"ӵ&&J2hMʑDŸ]^^\:\)Y7) \B9>ӓ\r]KR5O FQ~?gx5MzXXfA y_P;Q\I4o԰.*}(|KRic{m@p#&F/]t;rrʙIOcYE0}{"Qnl$Dg% ^| 66\"JlMSBgGX6m/_hG:>Sޣl];6T+7^yC2ϮJ_~a8W'PEeQDQt%mqjb+?gfO Pr_ڣ>1k,!yY< EZU:jᬹQDt*9 ǖK-aH42)%W@VQn3U|S?Gyw܏5\?gY<9>}B1/ѵkw=_cI-ݑ%RcD/1I]?]zx-/^×_ư}q9m&4d$zPE 2h}k?xdUJ#1q$ʕ-gǧN`XFc80BRL ϜoZ5'5?k7h"m[׶Ȕ1#h%W%SP &+N]QɠP\K!w٭}x>[l).Tdx3܍J a-)WeyI Cz߸Woe&W_&7b+gs?E4[|G8M:T!hts-)IiH1e1PDS0ݼ㛕Fy]eJ}#{a}^L\v(P*H.^CdT*Xӵǟ5Kp[7OnTrGrrA 5u(Uܞ#k0tޣ|RJdQ;$ w >Z7EͦeKМӝvWP:Fv])a{os~xw: 񘹒hzrʥyg#~|nhK4?.]GΜv1Wfv<~Z7JV?j^Et kn_t_CxI.ZtUɱFPJW8;]`F-Эp禮.nHt03<[t烬d% #Y)u wǛoY[գ'FB]Ğ:t3KJx,]4]IsC8P/_rFҙ5 kI:it@DXp4~{I>:ThuX}o>|Hw}Pz/t4z/[9|ŵvI4`P_!k#w ]su-[dɜ-Ϟ=׻Qty,ѸKd$AUX/V7%=5u銪U*W:aƴ>'OVvm$zAx1 ց7L/uNJ Ș!JՎq;hZ(J4U4^]Ens42Wvp %kq-Y9=#Pߏ#PDQ&]K{!?]qQQiGEL^yʕ*椫~侯Kiz7?|DDx8n߭DzWbV:ޣ }:>S-"@2% ƍ@{2dc9/wC+ℙ3g&2ˍk';cFJ_Чwg4mX@w?x\6M @Ѣ8}R-kwfh>q ~< du)8ysgM2ٛdOVʼnܺy&BӶRӇaD/ΝӥC?{2/~M'ӽqc[k׬‘g]VwU̙êFCekmV"LisH.ztD_GT% ι!MsQ H|Kn΃0|hoTP eQN]{oA`Ƽ c$Z*JցޫT 1aPv禹^ժ:sNҥm?~|=7IgÖ3Q*w˗x^}tɳw:&r6~P ׯwT6k2ԪU?Ѓķ;!]6^ro+XFh?Oʕp>,[8o [H<dVr(WtCV{"BTZeB ?LS0Cs?o$>| NB߽x+f״ҙeЭS4k0dVnC3&/~E&L"!c' :y4_ItrGݠ6=hGJ_cB{vf7s/Gh+|Qڕ yCDk~0f#^ QQp֏JAS ut߃k2KTW}Yw~wS~.:ҷ{ؿ{сiIo <vgv~}_it ux.d֜;Æo}3nB  eRJT }GW'zo]} 7vcۦCTt:y);@ZlCcOBG~>˗cD8󔹳&Q|L4J3Di/Ï?#=l^7nb,ط@|xsrm!Mj{G`FNM< 㳼yxNW_?k?~Zl,/:D3b_G˫yӆ1g|7 εKS$+MРy~cdzl߼"$qz16UmNm%bkӱ?~ػ.=y¤{Yd٘3%m^f07-:MV QtjMtFGb!<}'O-zhu5+s2"tDN:OXdbQ$?iq&7: K3{veCB;9E'H$^P}-+p;0(F*r[av|_OnP\:4}Aؿ{=Bޣ[a1ܹ? Dv/FRP&r^;Y#s:7z!~e467RsϜHAȖ-ڊ%k, U[x};9͝,U;xֵj!#Yt#_78iW_.jzS"%h"@}zv܁D{V >c(#Kv75/6@zCXr.oXH?ېk\l$&F;`l:X;8w/|ըIw?z eCw]Kԫ[7zn|PzΝZU3 AkJk!HbV*ZScE\SPCM1%DIH$ID$D$ !Hc(UJ(ڢULϦr72fz~?hoh㦯Ldۥ=1N%~{Ú/P5rnehSY0a*ФB&% KSQt*{52cnW։<+/NZwx6vmW}4=2lj?S~![7KEI): ~b;P~xNU`n=cQOQky ɬA& ZQR, a1bif n@/DN损H;u{B{J^z{cMBqݏLKG;w1'JKW.Ke'f]z EgM~ 3N0qRA(7mD-{ϏzowST "#Z;HtjR玭^+Į]J RsۦdUmMYCNњꈘřnUǖO;9SܸukQ؊+i H1шEߑ=_ y/hıdcSSo cثfN*U*%/xdC$!A3NZԳ{'14>%'?߷G4pwo|8U>\A"/ `. s1:qȴz:v Br9j߮%{. +H4<'/nԭxC??#˓b ַϙv9A(RLa;jJ| +Gz"D?z# x^XD!L:hPV@x/W4Nla5e*YI4)(p$7&~qI&{H4=ཀ=ON[lx04(}|蟝ҳpsUޫ<ao_۳3~UT|9bׇ_ף{rA܊-I^Mi?S"E<\GP& a{2T~v>8Vwzww ԅNCC~}&I4 *Dc Z#V9!GyѠu|FSw=]k|o]tmZ_ɿM"X +;$Q^DBO\T!&PrqQbnHe^'츮>;5K Qrb2;o)-^0So iuV?9q(iaVpjܰ.4{xҺ lȸT{7M9wWʰeKǶVݳgF]r&ڸe;]9yҶP&}k֧jWٯ _F^Ghj2] 8};Cȶ0~?,\;pX)u'Ǡ& xb̝[ڐIކF\ɆY<MmiܹOL9.,gi"> 奋fb$GP YJJ1::'ogymS.r|V|'OTѧ33/&ffƛ_CU..~9ήRP 79~#Sb9G^+Vn bAXI k6%#z <8jY 9&Pi#߀p~/6{ʒ^I1uD!ў@qE,lz AZ7o҄nє og C$Z$:!ahHªֆ%g# s&}h6vE{$?OV֚H4N.d=-$ p 2DpL֜SdmUq^4x3y@i58t}oSI48p0YYYU!91ܚ3%Վhd+?z̧n#t.v|˛]lʫ[LoA3%wn(ΎI`RJPpÀ|$te^C @7;w3rHk;Ύ^SǮjFѣG8J*Z{!j96cθy Aʖ7nG5&xd;fܭK;jisMPy y5EMX $ka>RH4ĴcL'Ҙqz1uз 1ԸqUښn78;a,~{92{pCXF76S+_ެUswݥdClCbDcط/+x0Ғ.\"876_iq:C+9x<^XNJ )hMlʿcRDuH+!$Yff|MD*THkyHG<4nTK$NdkUHG0! feil,H!FP\_Jsmj'N@AV~#MqSYe,ZU^%2 R-pۺTN߻`m-Sڀs4YsMADsߑ| Zv#>rV>3؃#@MHE;Sa=|9u5uFX\*cl΄G8 9ό;TL)>2T0'@[*c)ɒin̙R oҾ_s>2a/_3*޽)+ #$<ƏfG={&GPRv-s/9"fql媽9+'L$E+G.^L'ѳ<:bjv0ji[ P4]0ThAP*4؇ %I*hCxItZLllP@fy<*%Ɯ[~lԡ7ѥs[ֵ$<wnQyU|:hi^1s$tֆ ˖?R'8qQʯsu':&/~)aQe$'C+WyWA+4ݸIP[9DY.̙έy]|~^̧͙ @ /hoݍ"NF!d!fHeEo[H^l)UkxOv+TZU'| W$¥y`^[Г'tqzݼ(&މg Tv {X}+*jV1oۊ+pJXt]vnж%?Ns<ܹ톍SxSb.Iu ?Zɓ8!=29]"J 꽯WaU8] ̯Zp0&-Ky\V\Zc 4|s`@.y1tVc y!rkѩ?ϰdmWhl 0P}O;S!PH.Ӫ5eP3 ]hD /D W|%w9>V-?U+ӸATZʸwEbfK.~[PL9iX]yBfS6L@})qɗҸyI߰Pnկ_~ ڏXvmؼfO2ABF+?NȒKTvM&`#!\ne{rH$pÿuuaG9GS5kGgSz1a gBX +9<ԏ!H4r.c=Frj%$S\mΛy NM6ڞ[XPMtm3WBVPҕtQ0 0f [nqM{'g|72X_$Glp s}t2ym]šJXYQÆu8JN?eV9s.t};<^{BF_0j֬1 "OCVt2[wm~#y(+BlʦV ՛UA"߻(@࿈DҎ{w ]x@@ PCsiG楕dG:iܾey͘Oŋ\`]='RNmR*x Ë\բׯow$}$jӦuЊ9A0cs:޴8! -Hb9L||ƺ;fktfaYWK?-_2G:<ڷvnǒ}:їa [)&j [c HH]\i=w,GЈh;zIKmYB'S5pdiig%snSwpȠ̻'oP9q< wB760Kg06lv۶K;]Z0h$zwtHor5ZcL&ԱWΊțah, mZP{q>d{B޴y(aEm?u1I4ʹ)6;it1JݶKϝX_hKt>hq@@ xExJ䑖v$*' }=@ u0HyMA .(gNp޶)mcNpߑV-׹#sJASf^d7̕х8mჇqh/'<2{q=az.TDõK!L_#sbSzJϬu"bz>|m>lAGꆽRaY.VudA0؅}ݨ\4ɋVx$y5|Lz:y ׋]jע3hT3oB:ϱȆH4%-]E Fe ji3jӺf0p{3uyrF U*R0`7vcRÇ>8ԕ|f~UQzDdp_ukO<|t%m5oք>lPn`*[F!.a uѡCGۅ]ēPrer0\L[v!I,dOkUߡm֎./+UhJkJy tv8V[oq4yUt4lH_~]~#h?&8puKW WzM=IDq qpE\:LSfq1vqW;ܣm{֬OլD?c%oW*U$MC];c dEn>~/ĄY:L\mjެ)K?n?&mO85ܝ8 h7Ըa] u+X@dAnTښݸ W=>jژ%d7)y5 ˅@jb,78 Z,I$&, "u0m 6⻡r7{*[SVm;wӼi,mMDX f10uz-N;q;S#c:lAzira1zLeJ89$C&ᬔr&ں}*R}^y8Zl$1F+D{a;z!!zw;=,,5I#nVv w{):v1-7۴ƘMZ$X vhKqWU{:~ n];PTLcvEDQ(c r rzQܹ= @@ LE OH4b1ZzaÄT}1΢7nk4~]Z uQVК-^s3,KBK`ߟtlwΞ gc<h^l8URIWN.cfl TZ+7E1vW^EntZj#D1DXLO33YGUdfFƈ6TG[\W+J+ K!,qPn߶N*Wx,eWԩ3ݨa]Vci"+W=VQZ*kקғOw9`AiF#3j= B;g0`C7nVPɮQzu4坂DF @UB" Cc)iQ Y+Z.@*j"\lף*64`hR<ϓa@pL:ijڴ%xԩcvmϏ[}qٙO2Y=/ rANʏF.bz̬)I4Tmwܡ#NPy3 3@@ ^&rD9{~4!UizhX׎n޺MujRfzfuzrɓʵkaFx^3zcEyW ժrY*QŠ?hTQNyʖ)ÿ _O[\:0͉[^+Wͪ7nC4"ۮ97b E>ԨA,I4,7q1ү56H￟ ӏMmUWr2 ~9A!^&|P|YB @@!c˃sh֔W煋S &LaLoV()|?~B.^"esY, VL5K4 菚6f؝m[f4|K]wdPXol 5}lqJN<'S8͛h?9:QD{LnTv-nߟ2ann߽=?W6:xgZpE+?OQcUpM~>n,4g4'j mj}"KmZ5׬/u+I4bz}]9 vEDQ(*R0axj/['̤ːA-}Ip޾[ڰi;Ck\!69`.W910ztHm۴z TPA?c\kl*I4","u@m|V0WAA.H!"@@ Dq}?yBch"7)\q5rFܸ~8OD\/W\DxCgͿu81O UЪ$r 2Dh0R LʖSkL"{e. % E7nܢPNmY)\I(, ΧԈhJO,,U1(Gڣb̙WZID#`{7rq(R۔bт7=RJ7Hשp(#3`m֪^q-NZIo/17/J 3^ٳ(3(d8>bf<:Z5hv7dV~ 8A>fn"!wDі_Ӣ %Jʊ=!cus3ͱ$*a1^cx`;6)q@@ @h JǏɓL A1+K ]!ĻwɢO 2?ɋM2K$#7 Rw GɅw2LRL 7o2Ԯ TIk=+^誥K%qO K&,&m5V^$RXn-(SgGPe%kk+k:80S{4ffl{vF^4iSretff:R iJ*"oR1uljK> ?],zAUL@@ @hht-,Va*6bߩV~T--;`v7ύbH(+7/" H۷e@@ t$ֈֲfʛ +dqbLCbZH%Vyz9*SY[[>#}Qvh@@ 0/h@! HtQ!@@ @$pw @@ |F]@$@$@$@$@$(l< E @T(j$@$@$@$@$@E$@$@$@$@$@QM(Ɠ P 8 XMc([_"2[;##k1 @(Tϲ]$%c] `1)!W\nmLhF$@$@$@aA(,D#I 0q] 4!} r<;fۢWx @( w~(&ѹ8#S[llyϐ @(( %oĄT!DVg7 +)E>O$@$@$P(JL$+Dr(cu$@$@$@!@Q,B,bkjiYZ& #   ( "lVEM 1!>[)۲RY&"   `( iCL x/ fH22R6 %   ( 0`OB{k<ӀiC۴HHHE$sTTB(,HHH (M@HJ4Ec3M$@$@$@ @Q,Bj@%Y&   @( UI!F(BsHHHBEQHƐ@0|.plY2 @x( oz-x7 D(s8;z}ϖ N=@ oRnHȲF J6HHH PEC(@bB|oJoȀ5!0vM#  (1#d$@$@$@$@$@L(GI @VKVr@-+%@հX    E!ACW)פed h EDkHJ.=#+9DIHHHE"$@n(!HHHEQy%"P Dh8KE$@$@$@$FHW!HHHEQy% WH &"   h#h8K\)b     p}HHHRľ@$Fs$@$@$@F(<@(EHHHEQy%"   p#@QA$F+E$@$@$@F(<WHHHH+E$@J{ @JQy% WHR>@$@$@$@\)b_ p#9v   h#@Qmg{IE"$@$@$@F(<E (b p#"v   h#@Qmg{I+E$@$@$@$" pHHH p(<WHHHH+E$@ XZ'mM"ccf!C   \)O$P Iָv0{QSOe.H%&ل!aKL(Fu|HHHBEQHF@p$&ƏhU15 b,6gr]zFVrRB\ *-=cGp\ZHHH@ @f$֦6}s9x! " =ryZFVPm"   _ PJ@HLKBr4Kaܪ>L3di6F6HHH PEtPq]VT؜^&| 2N$==#3HHHH PE7(&xMRJ>DH`@zzbVHHHH $ P[h @bbt+Vi`:($@$@$@F(7,H pXkZc,0JC& X@@MeH76@HlaT;5LBM$@$@( YE"9 PDRڥkmLM$@$@$P(JzשCDeCQ6ۖIHH PE}.$k\;)2!Zs|h4jPu%uj0F!H߳q m;>$oJ)#fZ$  Ea74-ѩA^ ݇O?ʽ6l3`   $@Qn F%&4e \zq3@P+HNž,w# iE!0N9ZBƎ*+EF (ǎ/!s󵛲=CJHH P C*!e8)A4u6֑@4dS=F< a@J$@$(B kDRBZG92W"lY iYo k$  %@Q y9Da(FÑcԳpvlsѓHHP(uHe2W>t8Ym!EƛŠndž͸EWR>yY\/  qFKV5bu\6ۯKR^G^2ekS09hqhsmĐSJ߷mǞ=QBy4TR6eu6k *V(C9xCO wvEص,i԰l9~) @(pVq]!}M(:v6ĠKnΞ 2|SP 3?;wq[wu;z1TP>V6wކ;oov9K>Gi>7HPҔ]mY)!|B$@$@!L(Φ%%ĥ@Ϊ =w}m9ވ"UXVZe濚u~QMH/<?Xt1_1MGШQ,:z .q`X.,w;v$.ϝ;#GE5kҭ"P5aނryZFVm)k'  @Q~ ;+RS&BknC͛5? 8f6E\ne|]._Qs5rwؿkynXǏ-bǮ=PVT5b!-= ۶+Pv &j֛P#%ʗ/?J+!P}U;wHQTZZބ?wc'puj֌AFl{t~*W93^`Eb•<F–ŞpYԾ"tCڱs7& NW7u[ j*Ǵ ޾u͚5pۭ7zjY(qu g^Z ԇomg2!qenn۶n7qIqMBoAl޼ٳgư_Qm-ip!}ky xf/[z4ygܷߧFL 'Nfۈ&Mj?ysm oRKJ9C$@$@N({@ڟMY/Q]EP#G{ }k϶n݆&䩓Zڽ1իcҋ#$ԵjƠJ*P0ʔ)ӄ=z_|vacШa09Ͼ' Î]{aI G?܉J*Ϫi/oek[jԨc /kW#&O\z|z듚cĨ_y7?Ug]y՝gϞCzQxi(a& == /E`CS̞TTJx(!3S hO==TQlYdo*Vyt ˣ\O>{ɦN_~V9S=ѭ[?P 9TΝc{%1y-QBbŊ0nUAh ijU|պ8UԪYƾ}*scmY:0t;S4`X·xKv;0 LBWxMuB'aKUHHH %@QIQSQ4whҸ~V-_ƍy7ݐ*ijg;FA{cqSNzN?&O:u.Ƣ\ص{Y7b3^4 ?o̗ǡp3 Ë>WP8Z3`zdE/Св :$/3kU=8*l/&a_%J:y ӧ՟ެ`ǟ| -\*jK{CX{HY&fNjqԯą#T(9ONj}r/Wz+W&txcRq:O:rUsqV2T no^܋f~{o.ѡ~X}?Cöa#)|},Z*VrPΚ_VGgro& Vp!\W ys4n @6b0|Hoܞٺ> 3f/C`'P^8K-ǥ:y4fT'q{ZdeZ*Ѱa}9P&ihjM]?[o />ϫWH_~C#ѫ{'o{7.j?'<նPQA)QУp6|poDрcpQ,]YtMǍ] :,xԈG*ͻZ^j=E[;TO~︔P}[6,Cૉ:g?õ{sN(R՞77xe.˓O?^_^y,mEJh۾3q8*R{hRVTJ_pQĂڷV KY惏@|U5=qjþb)?AV$G`Q0+[gm܂_ZX8wWcKl>{^S{#En.|]礔3Dz] o$  #@Q}>JhS0 v)>d-^w۬a #[nw2?w |:g*[lxIo~M6)01'3\aW%Kս3]sޏY棝F?vjs&]7*M6YTOy#GɁicZ*t,^SvԿʖDJ}EjsђUzYޟ7D Ey'  PgSrw]$;WQ\'jr4jɲ;YVb"#OUM& a\:QTS߶,x~ʜuS< =?k䖯|S'rP!d*\Aȱg3Oo O~~WAm;9+[S&&vPjԢHu˳q/NF wDOTyw; X)RH؈勧;E>wuVlc^q3jOO o&  '"v3^~^/ޱc7/{ {ütuyW. ={t5M`Æ0J}Wk&Ejjo+{B6wr9.RcNP(r:5 VѪ$6Yݣ/ >TF ௿ǟÇ}ZB~7tN;{~̙ e,*VPYԡOv}Xg>SLSGg,º/ƈjWCHibsP&v`\r81$Z]Ie{8ЩZ)@-]0ՙ Uו"oW7~WbC׾hftxTR 8W&Sv܃gGOr&Zp| |1/y gΞ}~߮oR}$@$@$=E"`Zc,w}E*nμq5itsfO;~3f-Xԁw~+z y+ E*تӫ('Nf wMVF윔*9}6ီG~y/!~KOT(*N9] =EYcެW7Woܴf.ڳ5tp/N:I)c+)3Z1t%>lqbu}.Pu\(!p|osn̟3ɹʛRmUƚfkmc7-h IDAT   ϱ@RBjVUSŘ?g"TZã^}G`[E]@$@$@$@n(!HpHHH PE^J p}H`\)b   6\)6$"   J WHHHHA+E $@n>A$@$@$m(l/ A]HHH PE^(b    7E$@nRA$@$@$m(l/ p}HHHR>@$P0w DE^J p}H+E$@$@$@$ " 7 c   6Eq @Q.B$@$@$mJ]Y 0d"Yĸ9ATH0m .@P4!  &2 $_ #%rM d"[J*yfdqB$P"v    h#P*jj{B 6D̖6a dvmKv c$I("ɛl 7.q]!'%C 4T\xQ4"RZ%#hMG!]ll9.@( g)$@$@$@^(G1^Yr4(k@ LDkY~p<Ӕ6p3D@*{} QD'V   MNrE`ʳҖ;1a$ )c%+PE +5>q`lfZ](JL !C–$@$@$@$ 7ҹȒ\o#Hudc87]1D) aKJNJK6gmNHHH eb6wnb$RA9{8޿Լ媌xyT8a:b P-Fm΢%lLsTCA"m2I$@$@$@B (m[=DB'RpKV"'78㕦ĄT%4' VĔf!$W>vlmmLffl; @d(|m76(1Ve;N? R/i:@')2=#3    (-E0M7P5iYJN<7;^ ҉3/ތUJzD$RJ>DH`@zz' D`0t.%JReG:qp<Ӊ{Yfbbt+u[T{{   pQBU8NH'^j/=]w\p!3Btu HH@E&n@9҉3;^}Oz&\laO$@$@$P.xX?e";7;"ׄ R'Y8 "l D7J'nēۦzo1HlwXm^v-ѩAh%d ٻ+^y~&2g4HHJEQ); ROL/i4lp c#l 4zTi3Ϛ P  R @QT #J2Ch ;z0TiMe{H l;~Ǿ̍[l P  ( "HJ%U0D "%NMeH , 2)#SI`t%&  :̩9%B gÅZ1kK(\>\짝$@$@$ Eus,Ѽy'pQ ojQÝfkc>$@$@%@Q_QU*27dP/idm܂ E5z7 sgrMZFVȧIHH rPE/c1ÎJW.uퟶlEa #W݋ iGnڶPlض=@:ڵ-O,Xݞx4+aiic~NMf NDcIHH @(6ҋMuXkroEsX44W/1eҨw=>sl?ի倾BNiݗ`ę2'q[PJeݧ?=y4et[VJ$@$@$P(J~ם!:& x+?ˬ\eƹ V_E2eѣ[$%4%! B2̙.Y#h6UQa;ڵkUDk>¼+rJ<-#KfHHHE> $&ĥ !ZL8 C }vD/C[C2efM?BV7_6>w~7ls'rrb1<M4Ɵ~K4o~ l^ P\Yx}bsޣ&m 'l2ط 6oފٳhzUc\\"dg8TXY:n3n*~Hۀy&z٫ws[1x`O;U-[]8~Wx\vi z1|;8xu/-oʕuޗ_mۑi[BExcmq:u26kDLLug=ǎǗuÕc߱{>h5b}_l+ܹ Grj*h&8p [(?o_?v6 EKjTmuŸPB/m6l OALjh \ٸw/̟̰qyHKJ.HHEQ4x9mLL&bUѾ'R;DwժQb%ݷ*Xjªa&S>e_0la/C9Z }7ZQ([.]T3srpIԯ__VG?À>O➻pL1q|vl;v =^jޚPZ}Zpv`jB5Zj!cN3Ozu/0o ']5aX"&%Zx)CXl67kAk$uԬQUTƞq|V=I:mvԿ.Mr\~m)̘ Pjeر 'O u8صd[nQqI-vރ1NAFF-2˗/{łIFk[]9_k ,{]]F5錩اij OK5U}x㚦Meed&x0o$  &@Q dӒ㥣O?|ͧ:fl؄&?Ov?E.ǟBMժ/N+#34O/X|Jόѿ«>k1{?Z瞟~}) Y1\zi7nOn,᯹3_D .ia s=?[;B][vW@G)J6zdT'# j,HH PEĄl!P_P9OQ*o:_9q@nc;ݫ {*Ke3 Vԍ_;{q:̙.W=*z;ue]4IHҢD[ia-s^qg.ĦM?Mp cӏ&jZX8g+hVN/|sB{]TgΞElX0'7 Q[~;*V(2e8Px+S̜Mvۻ#!BBչbXxJoQV1tzt|,7,1?^>C417c7ȳm*ynzEsR Y7S8$@$@$(ޅ$ZE7~^ʕ*M wދOS?hZv޿W_1_v+,2Om=i[EJջ\ܨ}*} SbN Czz6-q*iuӂujϒJt\A.VϞ2{ڌEЯ27;W(Wg Gw6]+59sO 1{x\ue#*RKE+*(Zd' S7+Q#0*j4 OyJ#Hbƾ6fe>sku(UX?MŌY0q?ᥓu_c1:?SwW"N:U;mJ~!6$o%fq8y"^CDrʨkɲ;YV̀wW+f:CCMU>sf\==EL]ґϓ D*Hl[{uZ%x񿯿ǰgߪ|H;h={0U@?QB%=EެT* y=Ԯ9sƙn\7v42p[g~4WV@W\j_|ΝNaMsw:^G#)!RPUZjbִq:Ӝ(}$╷g,սT7xxl+t_2et7`ufO}tORRڥ.h Nᔟ(R+vg/'~|N={٥|ɌB9+_Q͘FgSLչK{vAބ72xݗ6;v­?T*lÆ%Q|/}NTV*UҫJvz>4jlžHHH ZPEN}Emhv7թK=ΨqU{cvދ*U*_J)ѥ:Rԓ'O*U7ׇ}%_NJ3~)UX9rս;v?[R犎}sG'NixSؿ.fUϕ?qlߩz "/`%ZW,'T?ѠA=/_^:ź:4כVim<{HHH PEƤBSb̟31UE}.1jժmaVա J@ep%ٗY-nw~(ߕLUbaf"[% >g&,VW0  0 @QN e֦ @ueg1esl2m#"\)τ0rW*R (Hl@)٦I@y~ 6dm>!p>"rL!=ͶI PD>ڄF gcaM@!CgNW Z% JǿPi o/)#+ #! @g$@$@n(!JD 1!>[/6wކNݯ",v{Ūwg_U*|@'@hdK#tHp&R:Ηv^  (b(YXV;o|/]w&a\"WJD@."T.|]lYL1D, -h# IDAT@^jJ[؋"(N @(WnCn؍x*FveR˳C9 BA"JHI8m8F̷ٹ${+@"7H5jiSF8ؓUjH+jߋΜ&d*LjǙ5ٞofN"zF$( @BXB̤q!(ێH`#5p'$ĵB$ )!ąZl}'^(,%oYUjB\"wDHgcpRHP`UM/HP'1MV@]k VR.KK@PM W!uE/t^oDrW ~% }~H“EQxjGZdn 8"} BdUZA2?gja ,~۔k !R)SRE@b !8eO%lRTD*`YǾ pMs.e7=$@B(T 5ˠ9$t[V/ev/EHH7QW"*2QD|H.VCdS!CܒTq^tdrw+c<1bk]mX4ނr!Th%djn&P Q_dIzH)S32֑% @89&M!)<u@4(;sĀc p~mUo0; BOhbSuWG gXV(H %oЖ }Q%q4%(# Q_4W)פed Y ܐ K\dC GFiE,JRvOm_,Xb=$@Ҝ# ߗ'gEDŐ@([E@e7% %nip7Sf*VqVEPi:uERP00!M>!AQ9REC#r$tT &}f7. q6Ǜp + ZT%-77N {ΧF;oCgWxy@1HʁyF?賌p#& baq pD0y@ GYC,SSa <jڐ&sϊ(*@ewiK-$񬠒$p!b$$ˎD4)mB 'w }$HAEE٥Ѻ4Ԅݐ"VW[y>6D`75M6,r=+ߓ@"(3]3؛Hwᔡ"w p(gÒDљX1w_n0SnE}=xVPQ{.\ެ #ैWZ¦a+NـLs/,'M)X.atx9Ww;"䅻8dÐdYA1 X?~$y"g$&VHlK8`pmTAE #l?Ȯv wl/ay|r:'J̶̹݊<0(R= ➑d5|1>E %J侸dlm ])[LaY-ꗼ -50Өwm)&[bfG$;$k\;i伕BHRuHf\{M|H cs-##k]( ATDQ0v_̳LLU|{+C}Y!IxVX oHnJ H b }kHk%M] !PjaGBa#=R~n6FtaY!.4~"H L8DrdM&ͧ$@ BbTI24-co4@"1u r] k&P!]Ý%dJzzVPv /3) ]yGx?T( U.HpDG=BI L $%ĥ@%0#==S?" p=:lSveho@]SE/ldRBڼpEK (l6 BSM5W}!{IA ~ƋEX!bkOi= @RBjV'ejZFV`zHHA w O/j?JB$YU"i6 }Qn*\-*%WZzzߣB}OS |!FP [x<&/Bc4bࡖyPxV@i=,\:,11~FNB,9T_Z4\+- /N9g[r6G(Ʀgq%9N􋒴$BYD An҉/ ˙Ži$P <B0;LIKK wJdVI$<@cxQZ9Nw?7@C8}C2/3Fi3e02݌  qHhmHm74BIkgy Nkh @o!8$ǜ'/!8 OQ$ХQMbB|sЫ@ֆ0;&7gϝVR%GZn)==#34le$@Ksb)DX^X▲)rCס"G|Fw4}9YD.]CrdB[]J49EP~qi?& j30Qaq[\{~h 4q8i¸sHz`CQXZg\a$!p@D2VN!K"xKiY]B14H 19'(9Nػ9 ( *WvH^a1Y.F p  W[VᩉxY8x6@'}J-( %o$k\;"6e4[" $@QK 1!zPb~ 3Q 'q:**(7nF Q D(1τ 'LPb,H{xϊwN( zfD&3Bt6@ 28  p=+IQ}͈j#&&X.B$569-0y# p,JQ͈nd(l& kD L,HWJG(JE`!JzI#_&X_D$q|Ż'@QE=s3"Ow"糩$G%\LG,HWJ{(қraLS" adHXcrLJh4r8pnr@8^DY͒-Bt2 LK9fۘSau+LJr{l' ?W~E:(b(jyvͶ%-WE@ p $]M$O&&7)2!fMѢ hԠ>ԩK' EaK߳q m;>$oí-R"GH5͖:lg8LJp ${M"G-L _DQ5 LwuJ;ZS("m~%g>xm|Wf˜ʂXLJ fU$P { @W%E0Aa+0xPO4n[*X) ;Vdةط)!!DI  p!D3/Z;z0T.Li' $cO`ؗqyL)!ٵh Jc; @xUlQ6M\cDS'‚!cH12噄pH!,$ pa -:̩9%B.݇v {.truZzVPC{@8 WEX5o! C;Î{nis8>]$/=$@BUD[`enȠ^^1;u46nAP뢚^=ÛHΝ;CTƒjUڼYV536JJhؠ~L:v츶'>jf6-{?pI^SҹϢjX •fxv-[os$%EK^ݴG^Uض= @:UseVQ?q*߷ '1Ѡ2LuX,5p9XG|2i׎Ǎ;w`ɭ8%(r N'ĐA=|)7`7B_ص!tmnoA(RtzWiʮ鶬o󞒌ފ"=q/ZO>vzu/E@Bk"*h|VdaQtCi"=;9K>/.󯘻` ű'XóE%|EI q)gN^{[Qtq]fʕ.;7A_E2eѣ[$%4%! B2̙.Y#h6UQa;ڵkU?n N,;ve˖ϖJ5aނryZFVJ2>(Ӽy[3ᗭk:*W_~ Z}q0$Z_]ȿ3DǞ~^9W]3eAl|8sXWRڢ$Ϣ(1!.UJylQhsnbwehqu(S&w⩮6*ބ~}*&j߰ϝ9 @b84imƟ~K4o~ l^ P\Yx}b`?Ywdyؼy+:gϢUqq'ܖؕ*AacMi0o\Qr7{U;|w~~C.U-[]8~Wx\vi z1|;8xu/-oʕu޷mCs_Ozmۑi[BExcmqR1j?ε\zvޫ}z)\$7\ංn;vFLutcOz SUy~={3hq N4ͯu"#11pۭ7i;]/U]{:=7ŧ9Uކ̟DX]RuY^tcIeEoc7bUE[n@U /7^U5/\6>#[y,Co(W ۋ9ܣiiYW63^>WԼSէ?9W,J:W*(&bUž'R;DwժQb%ݷ*XUV~PS'Oa0y@UTd3ѪG٪C#sr?v`Is9)%&N/֮L;cP/5Z\Եl4:4M=yV]|hݪ ;Szr[bEL7L}r:<6w(ѓc^}c4*LlIYbٻv+N#GOҟ/~۶]OM*@;p|Ԯ&ѥC۝a%m؄*U+cǎ]8y4^x~SiǮ%蓵9{TV *TPMx~(*TȍV #c˗=Do҄UM цF?U6&T;+[NTNCQz']"j)rr`/B  Î]{N|kÞǮtѿ>._2H22 $&|PMP̕?ԄT] g廉G8E:}!ZԮ}L=pFPd{j o|KVatBu~cO1tqʫU&>}ccOawmm>j"N:'vALGx_Jĉݫ {WヺIk|jTCL޵B1ϛ1:E{.18G^|[T 3go1-a=?E*U!Լbɝ=3\řb9?Vs@MxvX?=֫˛>Nc60+د~K_Co*="TlŶ '*UQ+ SQ oy|^jU_99$ZjzWJD"s?kNd}ry} {f3{fZsNkj֬Zܙ;kv 1#R?_~M2|}~wISfwU/p7mڔV,4^e[ ]M錼0ɧ+<#8?@k+϶ESQԾ0_jqɭQb:guoiYh>wVgɲ}P[vF֯^kKc}umYb`/?N(Lm19OK.0/)S?ͽ '4m oT ̖Ѫs--~p = to]toj2^MY3'I;d,oz\W#W}bƟQcB~͝5%عZdž]~wa!M8R>[4yl_m7r=;XĻgЁʃb,BlN@u>bO PF2g?=<\M#FOV~ ZFsv΍[vͻ6ylR櫈ꅍxW~#c~]3;tL5ch欅4tC׀;x'͝=xMl׼ xU>ԩEaYyr4[{k&a'koҞkS9sTs8?kݼ 3[)CUAe7~ºcJI>hC?~.?G+jdu.ɨp""Ȟ):w\n+ Y8e=, IDATkE֪>Rq_{ U`ٷ=nvZ`Zl}.$.lBo{v0qq2dM2E7~@ڥ0cY~qbl5n D53'=bySҬ˜Y9[Nao?uX˱1.'Zp"Xxt?P{^|pQR Sqo=WIa0n=)%{6JuKF 'nT걷O#iJ>❘E+#,-XAm+-nؿԂfϘdkY??<R~U}$tsCF~& ~qoiH!ڐQsR8 u.:У*ckAiԈkbon F5h-^0=^[dWUL?~qn.M6v^g|hh^Du^AG S=|S,N6cǾ7Ti;aƭ.nl;1#Rk\b5sRJo#ﴲ0tiכ72ΣLJtC+cVkKBr!L^1uɇpcĿgGT 'ANVDW-?$SF)e;v4Ysu"<'2:yC2jA؏j*du9=vLu"ϱ@jUiCK;Aele+`{)Mb="ssJ"En=<,Q.=q6jEZvX$߯ew2\úc느l9qZ泞cEJ1E'ZEBv>F%;VAe 4n؛nqmy8k?~OO,b7xw2-IWTpʳg +lLs{uCȮB6'M~eMTw6zY R["va#唸 c'!3 &qVxܴukYq>sμe+ӣC3J߸6mCRev ׹~㎸"`?Q8ތ:58ơߠjS e۶@WL7x}FQcI`)z44}ʵ:/zIV+n2beA6Sڼ~ډk'4'J>3eb .#bwG)gym9o8C]R+ \/Z;q_tmNw6(b94hbK<'2:dQ(]7 79=v$"z5zwWz˯+k9Sv璡"{(%;V+(u>U0K^|kKcjB.Pg+vk1EZv,[tJ(f.ZFpHJ*dn9u9^n68-J5V(])j(v/=I]W&ؠhsnᠷ8iAŬS<~]ڰvQoA['JS@}hjl{q/N/Wh"b.ڽ Bep^}Gtɉ*F\OI7?;$;v=€Y( ̽?iD] S難}M""v]Fs8 khiw@m_(w†q^쟝^%֝xLFRxC8ńcx-9Uq0s ' |@P?]BcG :?"L=ly7rʏ/'J>#v挤Z S9䷍3"Ůw= ԄESOڸvI=D91ÉLxc/R!{c81vyVڤbV2s htheO83貢1TNt,fcscݽ9°QBZ"NF3bzryw޹F͹/!YFlj z܍DQQm_Ni;;w?I/:}He+NpS.J4(E br^)u%SE~tnyF+رct>g%Si۞׆N>G rJ߁c_<+ǚm33} z}zu$A[PVҒ3T9.ZXuZMg%æT;ʿ^6qLܿ_OI*UzBNTҀzSK~mܮyQǟ >m]4GcgB⨜(Nϙ3귍wp؟wTّ#GUZC;E7N?ɏGM[vM;nuP6Gӗ^y%t5WϕD}w8&l>KnpF_znK4q:/XSӃj XԇW>M7ҼŢ{oUNpR?ߥ v)ʇM띖Dɇp '3` uԧWah(]Z Ou8wZz#]ݹS0гE?qNqy[|ṽ]pz;& S u*6(/.쳼l,e83%]t5=}|/z\fQNh֬^JnrzW6o~z(K)8WYq?fu=:zQ6 ©>nh0xZ%dsza>|e'nPJYcxNܛkC&T]8]] >nLٍdE9sA3=$5_ cJ +# ٕN|:<"N gq[d-Q/Z7e.:e GZduPgd"s"E:ؐ$~]|dPK챛sd;dciU*.]vl={F8:΋_~YM#[n~M3zE=Bd%SwX'eRL3| ,|#\"-5_V+isNN}㧝vj,v"gՕb2tx \t VE `J>>Vg 6wd1 H9w. ViZ]M |n>IdԜ$37Dȫ"޾ P)Dζ5hC~%>MZ)7b'TVk"v$xvb Ϊd.=CywOKl=miMݤvCp~>~FDu6 $@UF67sN;_ 0 rRGUjHΓ OTIi /Dɫ"@{o^W=3lsVe] Gyt` Cv+W[F := n!hyQ  oWs3eJFbϯsc9>_E]ʡLkt@>dL "LH(3wW\ށGD&$7lE}!bDV!\ J=@!Ly;W*NQ;9mZI'xf*YM&Cw\|ȪθdtOb4HJQģEӅ:+]1tM@ʽ5qnHNI:H#Ȟ4G QHJQ;)bJ]#L>(a| $IT<@ )$bGޖ*uD|kK!DCAehAT%%S17|n8@@1}p+@*URRCe/̗2_5\ `%PX_$%QWQJ  NqIuC)vFpb0`ajd86"j"wWܭ! 8;E E:NF uC_!0ㇻA`a&d86"j"wWܭ! s@  @AqQ`hTDP3@S9 idڈ=05^q#nxv0@2t 5 F+MHE<@;E F:N]wkҘ  $@ xmss<'nb̤qG[`c‚*!5?0:o&x@! 5:  c<7VJyW^uEȌ!(c"5 ),+B\NO tYHFQ*.&"+i ( YYh+=@`sjV JHk}| Wr(J׌}ko'\7ƨ)gl0$(#FI:4iwaō}0Бe啝 @ `>0h__NT &G`ͷYWT!'@ `7j'PX?N-E)̔X (k_觻@ (J1pT P@aa4ATe| b!(jYpm5]0|Y=t@ `%0 09DkPAn̘8 (o5$U I k^Un <0O'PPwԕ* "Ctā9Q,02TEBÆ!Ba)s@d 'Eq a B% z'V0dNW&PQ/,?"'}/Brq  (r .^o\!Zz )nxh X46"~=r%q"&)G%<'X:ڵ Ye2/@FQ %B$)I%@`y= 謊D삮\ϓx (rtf`c U؀{$iA΁1MՋumKR95 2H \< &k[" l0MZ6LPۄTUr)Lpc=ynr|}.Xz ֬hq<^>7+σTՉg@Q9c4rx&ӡa(|&TP?8x[},Kͣ9BD Hi)x$$:U"DH<䔫5 (r @z Xذ$-#x8iT%HV?61LS9tVu}q* 3'pMd D HG `,[rg>x7nRWQ Bڵ|#ZO[LmyW { EIDATDFQ6&h :I'(/]2}$*2 "$pJ3%\@I,wRC"p@h]1yXarm˕ߑ|| BTQ|27p4F0㨣ύ@35\$+Wotۍ#,gU4A,c4ڵ#Wz|D`Èq|CKA-`eNH"8r[Ͱ3IC=] i Sw=LzDͧBf\;C;}XjR1 pnq ":`vD<Dڵ Pq=4Xhd3S?-L͊u#v7~!<F T NvB6CDI 6 OxL[!(s-W> D @?0 Jj:aȭʰ;K;Mpj&5wKYЭBA{ !~`P~kjd4B@QQ(34!AM̭jhWY!D.%о7𴱾gjdKn鯔R󄤪&jˤK'  P L dYm]CX)i3*7h_d%ܦ!ebo _E;%]Z'b䪃scQ"K& p@< %ǜ,J`C65H5ffIz<$e3HJYZV^)gZp+En9\B T{I!QS %%GDjAywLEA:t3W>"A52dM'y9PMGHRiUroYyem=Z #(q,$vQ믦S'믏҆ͻhާ`t&pH5HkեfDMFQpd?‚<BtRs*:鸿m$]h|:zWd?5@@`unHN ϝ=%h^[{ۜӚ?m~ĻgD>_EQǂ@\`Ņ7@h_N枱܈}U'޷_D 6:_ȫl冠++N>Z@6Q >@9D{qTa*w䨖Kf#1: V,q\  )$(Qd:h,/F&y:j>ҭX:[];m=0bZ  `e  9%gN+Ί+%QgWFe  qx+U*Zf~BHFQcu@ PX_$Kt&9q;y3 ա\$QWQ   `e⨠M  fy~!D;nI'DESǓ7!bÇf,GK)+i`ar#ڛ)%oz5ګ1+_}6nEK2ƨfT02ud.H3;È/?< jwjʃtgt`]R@ay Q=30qU  ( auڕ)v3;DN:tQN@\B68;]WKHE3A@E   9F#: C+K? @Q!fpG     BFQ      0dE2h@z(Jw      !`e@     !(=Q+@Q!fpG     BFQ      0dE2h@z(Jw      !`e@     !(=Q+@վd IENDB`biometryd-0.3.1/doc/index.rst000066400000000000000000000002551455450034500161260ustar00rootroot00000000000000Welcome to biometryd's documentation ------------------------------------ .. toctree:: intro architecture interfaces extending_biometryd manual_test_plan biometryd-0.3.1/doc/interfaces.rst000066400000000000000000000004461455450034500171440ustar00rootroot00000000000000Interfaces ---------- .. doxygenclass:: biometry::Device :members: .. doxygenclass:: biometry::TemplateStore :members: .. doxygenclass:: biometry::Identifier :members: .. doxygenclass:: biometry::Verifier :members: .. doxygenclass:: biometry::Operation :members: biometryd-0.3.1/doc/intro.md000066400000000000000000000016171455450034500157450ustar00rootroot00000000000000# Introduction Biometryd multiplexes and mediates access to devices for biometric identification and verification. A fingerprint reader is an example of such a device, but the overall system is designed with arbitrary devices and mechanisms in mind. Security and privacy is one the most important design goal. For that, design, API and implementation do make sure that actual template boundary is never exposed to client applications. More to this, Biometryd and its API are designed such that actual template data is not needed for operation (unless really needed by a device). Instead, the API focuses on controlling and monitoring devices and operations instead of dealing with handling actual template data. ## Coordinates - Project: - Code: - Docs: - Bugs: biometryd-0.3.1/doc/manual_test_plan.md000066400000000000000000000035751455450034500201450ustar00rootroot00000000000000# Manual Test Plan This section lists manual test cases that should be executed prior to landing. The test cases exercise the main functionality and aim to guarantee a baseline level of functionality that should not regress across releases. Please note that individual landings might require specific testing steps in addition to the ones listed here. We assume that testers use a freshly bootstrapped device. ## Turbo ### Enrolling a New Template - Boot the phone - Unlock the greeter/complete the wizard - Start "System Settings" - Switch to the "Security & Privacy" page - Select "Fingerprint ID" - Select "Add Fingerprint" + Enroll a new template according to the onscreen instructions. + Make sure that feedback given during enrollment is meaningful and reasonable. + After completion, check if the list of enrolled fingerprints has grown by 1. - Select the recently enrolled fingerprint and rename it: + Ensure that the name of the fingerprint is persistent across restarts of "System Settings" ### Identifying With A Fingerprint - In "System Settings", choose Fingerprint ID as lock security. - Lock the screen. - Wake up the phone by pressing the power button. - Try to identify with your previously enrolled fingerprint. - Lock the screen again. - Wake up with the home button. - Try to identify with your previously enrolled fingerprint. - Lock the screen again. - Wake up the screen and try to identify with a finger that hasn't been enrolled previously. The attempts should fail and the device should fall back to your passcode. ### Removing a Previously Enrolled Template - Start "System Settings" - Switch to "Security & Privacy" page - Remove at least one enrolled fingerprint + Make sure that the fingerprint is removed from the list - Lock the screen and try to identify with the fingerprint. The attemtps should fail. biometryd-0.3.1/doc/manual_test_plan.md.moved000066400000000000000000000054711455450034500212530ustar00rootroot00000000000000# Manual Test Plan This section lists manual test cases that should be executed prior to landing. The test cases exercise the main functionality and aim to guarantee a baseline level of functionality that should not regress across releases. Please note that individual landings might require specific testing steps in addition to the ones listed here. We assume that testers use a freshly bootstrapped device. ## CLI ### Testing Against a Running Service Instance Execute the following command line and follow the on-screen instructions: ```bash > biometryd test ``` You should receive output similar to: ```bash > biometryd test We are about to execute a test run for a biometric device. Please note that we are executing the test in a production environment and you should consider the test to be harmful to the device configuration: User: User[32011] Config: Default device Would you really like to proceed (y/n)?y Clearing template store: [=================] 100.00 % Enrolling new template: [=================] 100.00 % Identifying user: [=================] 100.00 % min: 267488.00 [µs] mean: 497875.12 [µs] std.dev.: 198079.29 [µs] max: 1172946.00 [µs] ``` ## Fingerprint Reader With Guidance The following section provides testing instructions for fingerprint readers featuring guided enrollment processes. ### Enrolling a New Template - Boot the phone - Unlock the greeter/complete the wizard - Start "System Settings" - Switch to the "Security & Privacy" page - Select "Fingerprint ID" - Select "Add Fingerprint" + Enroll a new template according to the onscreen instructions. + Make sure that feedback given during enrollment is meaningful and reasonable. + After completion, check if the list of enrolled fingerprints has grown by 1. - Select the recently enrolled fingerprint and rename it: + Ensure that the name of the fingerprint is persistent across restarts of "System Settings" ### Identifying With A Fingerprint - In "System Settings", choose Fingerprint ID as lock security. - Lock the screen. - Wake up the phone by pressing the power button. - Try to identify with your previously enrolled fingerprint. - Lock the screen again. - Wake up with the home button. - Try to identify with your previously enrolled fingerprint. - Lock the screen again. - Wake up the screen and try to identify with a finger that hasn't been enrolled previously. The attempts should fail and the device should fall back to your passcode. ### Removing a Previously Enrolled Template - Start "System Settings" - Switch to "Security & Privacy" page - Remove at least one enrolled fingerprint + Make sure that the fingerprint is removed from the list - Lock the screen and try to identify with the fingerprint. The attemtps should fail. biometryd-0.3.1/doc/requirements.txt000066400000000000000000000000101455450034500175360ustar00rootroot00000000000000breathe biometryd-0.3.1/include/000077500000000000000000000000001455450034500151415ustar00rootroot00000000000000biometryd-0.3.1/include/CMakeLists.txt000066400000000000000000000001461455450034500177020ustar00rootroot00000000000000add_subdirectory(biometry) install( DIRECTORY biometry DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) biometryd-0.3.1/include/biometry/000077500000000000000000000000001455450034500167735ustar00rootroot00000000000000biometryd-0.3.1/include/biometry/CMakeLists.txt000066400000000000000000000002631455450034500215340ustar00rootroot00000000000000configure_file(version.h.in version.h) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/version.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/biometry ) add_subdirectory(hardware)biometryd-0.3.1/include/biometry/application.h000066400000000000000000000033361455450034500214540ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_APPLICATION_H_ #define BIOMETRYD_APPLICATION_H_ #include #include namespace biometry { /// @brief Application describes an application instance. class BIOMETRY_DLL_PUBLIC Application { public: /// @brief system returns an application instance referring to the overall system. static Application system(); /// @brief Application initializes the instance with system(). Application(); /// @brief Application initializes an instance with the given string. explicit Application(const std::string& s); /// @brief as_string returns a string representation of this instance. const std::string& as_string() const; /// @brief operator std::string returns a string representation of this instance. operator std::string() const; private: std::string value; }; /// @brief operator== returns true if lhs has the same name as rhs. BIOMETRY_DLL_PUBLIC bool operator==(const Application& lhs, const Application& rhs); } #endif // BIOMETRYD_APPLICATION_H_ biometryd-0.3.1/include/biometry/dbus/000077500000000000000000000000001455450034500177305ustar00rootroot00000000000000biometryd-0.3.1/include/biometry/dbus/service.h000066400000000000000000000024571455450034500215510ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_DBUS_SERVICE_H_ #define BIOMETRYD_DBUS_SERVICE_H_ #include #include namespace biometry { /// @cond class Service; /// @endcond namespace dbus { struct BIOMETRY_DLL_PUBLIC Service { // We just use Service as a structuring entity. Service() = delete; /// @brief create_stub returns an implementation of biometry::Service /// transparently accessing a remote instance exposed via dbus. /// @throws std::runtime_error in case of issues. static std::shared_ptr create_stub(); }; } } #endif // BIOMETRYD_DBUS_SERVICE_H_ biometryd-0.3.1/include/biometry/device.h000066400000000000000000000047431455450034500204130ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRY_DEVICE_H_ #define BIOMETRY_DEVICE_H_ #include #include #include #include #include namespace biometry { /// @cond class Identifier; class TemplateStore; class Verifier; namespace util { class Configuration; } /// @endcond /// @brief Device models a biometric device. class BIOMETRY_DLL_PUBLIC Device : public DoNotCopyOrMove { public: /// @brief Id is the unique name of a device. typedef std::string Id; /// @brief Descriptor bundles details about a device. class Descriptor : public DoNotCopyOrMove { public: // Safe us some typing. typedef std::shared_ptr Ptr; /// @brief create returns an instance of the device. virtual std::shared_ptr create(const util::Configuration&) = 0; /// @brief name returns the human-readable name of the device. virtual std::string name() const = 0; /// @brief author returns the name of the author of the device implementation. virtual std::string author() const = 0; /// @brief description returns a one-line summary of the device implementation. virtual std::string description() const = 0; protected: /// @cond Descriptor() = default; /// @endcond }; /// @brief enroller returns a device-specific template_store implementation. virtual TemplateStore& template_store() = 0; /// @brief identifier returns a device-specific Identifier implementation. virtual Identifier& identifier() = 0; /// @brief verifier returns a device-specific Verifier implementation. virtual Verifier& verifier() = 0; protected: /// @cond Device() = default; /// @endcond }; } #endif // BIOMETRY_DEVICE_H_ biometryd-0.3.1/include/biometry/devices/000077500000000000000000000000001455450034500204155ustar00rootroot00000000000000biometryd-0.3.1/include/biometry/devices/fingerprint_reader.h000066400000000000000000000146331455450034500244460ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_DEVICES_FINGERPRINT_READER_H_ #define BIOMETRYD_DEVICES_FINGERPRINT_READER_H_ #include #include #include #include #include #include namespace biometry { namespace devices { /// @brief FingerprintReader specializes a biometry::Device, providing /// methods and structures to handle specific feedback during enrollment of /// fingerprints. class BIOMETRY_DLL_PUBLIC FingerprintReader : public Device { public: /// @brief Direction enumerates all known direction hints. enum class Direction { not_available = 0, south_west = 1, south = 2, south_east = 3, north_west = 4, north = 5, north_east = 6, east = 7, west = 8 }; /// @brief GuidedEnrollment describes a guided enrollment operation with the /// underlying hw/driver stack providing guidance data to users of the device /// such that enrollment can happen as fast as possible. struct GuidedEnrollment { struct Hints { /// @cond static constexpr const char* key_is_finger_present { "FingerprintReader::Hints::is_finger_present" }; static constexpr const char* key_is_main_cluster_identified { "FingerprintReader::Hints::is_main_cluster_identified" }; static constexpr const char* key_suggested_next_direction { "FingerprintReader::Hints::suggested_next_direction" }; static constexpr const char* key_estimated_finger_size { "FingerprintReader::Hints::estimated_finger_size" }; static constexpr const char* key_masks { "FingerprintReader::Hints::masks" }; /// @endcond /// @brief from_dictionary decodes guidance data from dict. void from_dictionary(const Dictionary& dict); /// @brief to_dictionary encodes guidance data to dict. Dictionary to_dictionary() const; Optional is_finger_present{}; ///< If set: true indicates that the user's finger is present. Optional is_main_cluster_identified{}; ///< If set: true indicates that the main cluster of the fingerprint has been identified. Optional suggested_next_direction{}; ///< If set: Direction of the next touch. Optional> masks{}; ///< If set: A vector of rectangles marking all the regions that have been scanned and accepted. }; /// @brief ProgressWithGuidance bundles guidance data meant for visualization purposes /// to help the user when enrolling a new fingerprint. struct ProgressWithGuidance { Percent percent{Percent::from_raw_value(0.f)}; ///< Percentage completed. Hints guidance; }; typedef ProgressWithGuidance Progress; typedef biometry::TemplateStore::Enrollment::Reason Reason; typedef biometry::TemplateStore::Enrollment::Error Error; typedef biometry::TemplateStore::Enrollment::Result Result; }; /// @brief TemplateStore is an implementation of biometry::TemplateStore providing /// additional guidance data during the enrollment process. class TemplateStore : public biometry::TemplateStore { public: /// @brief TemplateStore initializes a new instance with the given impl. explicit TemplateStore(const std::reference_wrapper& impl); /// @brief guided_enroll returns a GuidedEnrollment operation ready to be started. Operation::Ptr guided_enroll(const Application& app, const User& user); // From biometry::TemplateStore. Operation::Ptr size(const Application& app, const User& user) override; Operation::Ptr list(const Application& app, const User& user) override; Operation::Ptr enroll(const Application& app, const User& user) override; Operation::Ptr remove(const Application& app, const User& user, TemplateStore::TemplateId id) override; Operation::Ptr clear(const Application& app, const User& user) override; private: /// @cond std::reference_wrapper impl; /// @endcond }; /// @brief FingerprintReader initializes a new instance with the given device instance. explicit FingerprintReader(const std::shared_ptr& device); /// @brief template_store_with_guided_enrollment returns a TemplateStore providing support for guided enrollments. FingerprintReader::TemplateStore& template_store_with_guided_enrollment(); /// @brief enroller returns a device-specific template_store implementation. biometry::TemplateStore& template_store() override; /// @brief identifier returns a device-specific Identifier implementation. biometry::Identifier& identifier() override; /// @brief verifier returns a device-specific Verifier implementation. biometry::Verifier& verifier() override; private: /// @cond std::shared_ptr device_; FingerprintReader::TemplateStore template_store_; /// @endcond }; /// @brief operator== returns true if lhs and rhs compare equal. BIOMETRY_DLL_PUBLIC bool operator==( const FingerprintReader::GuidedEnrollment::Hints& lhs, const FingerprintReader::GuidedEnrollment::Hints& rhs); } } #endif // BIOMETRYD_DEVICES_FINGERPRINT_READER_H_ biometryd-0.3.1/include/biometry/devices/plugin/000077500000000000000000000000001455450034500217135ustar00rootroot00000000000000biometryd-0.3.1/include/biometry/devices/plugin/interface.h000066400000000000000000000063511455450034500240310ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_DEVICES_PLUGIN_INTERFACE_H_ #define BIOMETRYD_DEVICES_PLUGIN_INTERFACE_H_ #include #define BIOMETRYD_DEVICES_PLUGIN_DESCRIPTOR_SECTION "BIOMETRYD_DEVICES_PLUGIN_DESCRIPTOR" namespace biometry { class Device; namespace devices { namespace plugin { static constexpr const std::uint32_t name_length = 256; static constexpr const std::uint32_t author_length = 256; static constexpr const std::uint32_t description_length = 256; struct Version { std::uint32_t major, minor, patch; }; struct Descriptor { const char name[name_length]; const char author[author_length]; const char description[description_length]; struct { Version host; Version plugin; } const version; }; } } } /// @brief BiometrydPluginDeviceCreate defines the function used to create biometry::Device instances. typedef biometry::Device* (*BiometrydPluginDeviceCreate) (); /// @brief BiometrydPluginDeviceDestroy defines the function used to destroy biometry::Device instances. typedef void (*BiometrydPluginDeviceDestroy) (biometry::Device*); /// @snippet tests/biometryd_devices_plugin_dl.cpp Describing the plugin #define BIOMETRYD_DEVICES_PLUGIN_DESCRIBE(name, author, description, major, minor, patch) \ biometry::devices::plugin::Descriptor biometryd_devices_plugin_descriptor __attribute((section(BIOMETRYD_DEVICES_PLUGIN_DESCRIPTOR_SECTION))) = \ { name, author, description, {{biometry::build::version_major, biometry::build::version_minor, biometry::build::version_patch}, {major, minor, patch}}}; /// @brief Starts the implementation of the create function exposed from a dynamic library. /// /// @snippet tests/biometryd_devices_plugin_dl.cpp Defining the create function #define BIOMETRYD_DEVICES_PLUGIN_CREATE \ extern "C" __attribute__ ((visibility ("default"))) biometry::Device* CreateBiometrydDevice() /// @snippet tests/biometryd_devices_plugin_dl.cpp Defining the destroy function /// @brief Starts implementation of the destroy function exposed from a dynamic library. /// /// @code /// @endcode #define BIOMETRYD_DEVICES_PLUGIN_DESTROY \ extern "C" __attribute__ ((visibility ("default"))) void DestroyBiometrydDevice(biometry::Device* d) /// @brief Tags the symbol name of the create function. #define BIOMETRYD_DEVICES_PLUGIN_CREATE_SYMBOL_NAME "CreateBiometrydDevice" /// @brief Tags the symbol name of the destroy function. #define BIOMETRYD_DEVICES_PLUGIN_DESTROY_SYMBOL_NAME "DestroyBiometrydDevice" #endif // BIOMETRYD_DEVICES_PLUGIN_INTERFACE_H_ biometryd-0.3.1/include/biometry/dictionary.h000066400000000000000000000016661455450034500213220ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRY_DICTIONARY_H_ #define BIOMETRY_DICTIONARY_H_ #include #include #include namespace biometry { typedef std::map Dictionary; } #endif // BIOMETRY_DICTIONARY_H_ biometryd-0.3.1/include/biometry/do_not_copy_or_move.h000066400000000000000000000023221455450034500232050ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRY_DO_NOT_COPY_OR_MOVE_H_ #define BIOMETRY_DO_NOT_COPY_OR_MOVE_H_ namespace biometry { /// @cond class DoNotCopyOrMove { public: DoNotCopyOrMove(const DoNotCopyOrMove&) = delete; DoNotCopyOrMove(DoNotCopyOrMove&&) = delete; virtual ~DoNotCopyOrMove() = default; DoNotCopyOrMove& operator=(const DoNotCopyOrMove&) = delete; DoNotCopyOrMove& operator=(DoNotCopyOrMove&&) = delete; protected: DoNotCopyOrMove() = default; }; /// @endcond } #endif // BIOMETRY_DO_NOT_COPY_OR_MOVE_H_ biometryd-0.3.1/include/biometry/geometry.h000066400000000000000000000051561455450034500210060ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRY_GEOMETRY_H_ #define BIOMETRY_GEOMETRY_H_ #include #include #include namespace biometry { /// @brief Point models a point in a normalized space [0,1]^2. struct BIOMETRY_DLL_PUBLIC Point { /// @brief Point initializes a new instance with x, y. /// @ŧhrows std::runtime_error if x or y is not in [0,1]. Point(double x = 0.f, double y = 0.f); double x; double y; }; /// @brief Rectangle models a rectangular region in a 2d space, /// defined by its top_left (t) and bottom_right (b) corners. /// /// The origin of the coordinate system is located in the top-left corner /// extending to the right and downwards. /// /// o-------------------- [x] /// | /// | (t)····· /// | · · /// | · · /// | ·····(b) /// | ///[y] /// struct BIOMETRY_DLL_PUBLIC Rectangle { Point top_left; ///< 2d coordinate of the top-left corner. Point bottom_right; ///< 2d coordinate of the bottom-right corner. }; /// @brief operator== returns true if lhs and rhs compare equal component-wise. BIOMETRY_DLL_PUBLIC bool operator==(const Point&, const Point&); /// @brief operator== returns true if lhs and rhs compare not equal in any of the components. BIOMETRY_DLL_PUBLIC bool operator!=(const Point&, const Point&); /// @brief operator== returns true if lhs and rhs compare equal component-wise. BIOMETRY_DLL_PUBLIC bool operator==(const Rectangle&, const Rectangle&); /// @brief operator== returns true if lhs and rhs compare not-equal in any of the components. BIOMETRY_DLL_PUBLIC bool operator!=(const Rectangle&, const Rectangle&); /// @brief operator<< inserts point into out. BIOMETRY_DLL_PUBLIC std::ostream& operator<<(std::ostream& out, const Point& point); /// @brief operator<< inserts rectangle into out. BIOMETRY_DLL_PUBLIC std::ostream& operator<<(std::ostream& out, const Rectangle& rectangle); } #endif // BIOMETRY_GEOMETRY_H_ biometryd-0.3.1/include/biometry/hardware/000077500000000000000000000000001455450034500205705ustar00rootroot00000000000000biometryd-0.3.1/include/biometry/hardware/CMakeLists.txt000066400000000000000000000002351455450034500233300ustar00rootroot00000000000000set( BIOMETRY_HARDWARE_HEADERS biometry.h ) install( FILES ${BIOMETRY_HARDWARE_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/biometry/hardware ) biometryd-0.3.1/include/biometry/hardware/biometry.h000066400000000000000000000121651455450034500226000ustar00rootroot00000000000000/* * Copyright © 2020 UBports foundation Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Erfan Abdi */ #ifndef BIOMETRY_HARDWARE_BIOMETRY_H_ #define BIOMETRY_HARDWARE_BIOMETRY_H_ #include #include #include #ifdef __cplusplus extern "C" { #endif typedef int UHardwareBiometryFingerprintAcquiredInfo; enum { ACQUIRED_GOOD = 0, /** sensor needs more data, i.e. longer swipe. */ ACQUIRED_PARTIAL = 1, /** * image doesn't contain enough detail for recognition*/ ACQUIRED_INSUFFICIENT = 2, /** sensor needs to be cleaned */ ACQUIRED_IMAGER_DIRTY = 3, /** mostly swipe-type sensors; not enough data collected */ ACQUIRED_TOO_SLOW = 4, /** vendor-specific acquisition messages start here */ ACQUIRED_TOO_FAST = 5, /** vendor-specific acquisition messages */ ACQUIRED_VENDOR = 6 }; typedef int UHardwareBiometryFingerprintError; enum { /** Used for testing, no error returned */ ERROR_NO_ERROR = 0, /** The hardware has an error that can't be resolved. */ ERROR_HW_UNAVAILABLE = 1, /** Bad data; operation can't continue */ ERROR_UNABLE_TO_PROCESS = 2, /** The operation has timed out waiting for user input. */ ERROR_TIMEOUT = 3, /** No space available to store a template */ ERROR_NO_SPACE = 4, /** The current operation has been canceled */ ERROR_CANCELED = 5, /** Unable to remove a template */ ERROR_UNABLE_TO_REMOVE = 6, /** The hardware is in lockout due to too many attempts */ ERROR_LOCKOUT = 7, /** Vendor-specific error message */ ERROR_VENDOR = 8 }; typedef int UHardwareBiometryRequestStatus; enum { SYS_UNKNOWN = 1, SYS_OK = 0, SYS_ENOENT = -2, SYS_EINTR = -4, SYS_EIO = -5, SYS_EAGAIN = -11, SYS_ENOMEM = -12, SYS_EACCES = -13, SYS_EFAULT = -14, SYS_EBUSY = -16, SYS_EINVAL = -22, SYS_ENOSPC = -28, SYS_ETIMEDOUT = -110, }; typedef struct UHardwareBiometry_* UHardwareBiometry; typedef struct UHardwareBiometryCallback_* UHardwareBiometryCallback; typedef void (*UHardwareBiometryEnrollResult)(uint64_t deviceId, uint32_t fingerId, uint32_t groupId, uint32_t remaining, void *context); typedef void (*UHardwareBiometryAcquired)(uint64_t deviceId, UHardwareBiometryFingerprintAcquiredInfo acquiredInfo, int32_t vendorCode, void *context); typedef void (*UHardwareBiometryAuthenticated)(uint64_t deviceId, uint32_t fingerId, uint32_t groupId, void *context); typedef void (*UHardwareBiometryError)(uint64_t deviceId, UHardwareBiometryFingerprintError error, int32_t vendorCode, void *context); typedef void (*UHardwareBiometryRemoved)(uint64_t deviceId, uint32_t fingerId, uint32_t groupId, uint32_t remaining, void *context); typedef void (*UHardwareBiometryEnumerate)(uint64_t deviceId, uint32_t fingerId, uint32_t groupId, uint32_t remaining, void *context); typedef struct { UHardwareBiometryEnrollResult enrollresult_cb; UHardwareBiometryAcquired acquired_cb; UHardwareBiometryAuthenticated authenticated_cb; UHardwareBiometryError error_cb; UHardwareBiometryRemoved removed_cb; UHardwareBiometryEnumerate enumerate_cb; void* context; } UHardwareBiometryParams; /* You must create only one instance per process/application. */ BIOMETRY_DLL_PUBLIC UHardwareBiometry u_hardware_biometry_new(); BIOMETRY_DLL_PUBLIC uint64_t u_hardware_biometry_setNotify(UHardwareBiometry self, UHardwareBiometryParams *params); BIOMETRY_DLL_PUBLIC uint64_t u_hardware_biometry_preEnroll(UHardwareBiometry self); BIOMETRY_DLL_PUBLIC UHardwareBiometryRequestStatus u_hardware_biometry_enroll(UHardwareBiometry self, uint32_t gid, uint32_t timeoutSec, uint32_t uid); BIOMETRY_DLL_PUBLIC UHardwareBiometryRequestStatus u_hardware_biometry_postEnroll(UHardwareBiometry self); BIOMETRY_DLL_PUBLIC uint64_t u_hardware_biometry_getAuthenticatorId(UHardwareBiometry self); BIOMETRY_DLL_PUBLIC UHardwareBiometryRequestStatus u_hardware_biometry_cancel(UHardwareBiometry self); BIOMETRY_DLL_PUBLIC UHardwareBiometryRequestStatus u_hardware_biometry_enumerate(UHardwareBiometry self); BIOMETRY_DLL_PUBLIC UHardwareBiometryRequestStatus u_hardware_biometry_remove(UHardwareBiometry self, uint32_t gid, uint32_t fid); BIOMETRY_DLL_PUBLIC UHardwareBiometryRequestStatus u_hardware_biometry_setActiveGroup(UHardwareBiometry self, uint32_t gid, char *storePath); BIOMETRY_DLL_PUBLIC UHardwareBiometryRequestStatus u_hardware_biometry_authenticate(UHardwareBiometry self, uint64_t operationId, uint32_t gid); #ifdef __cplusplus } #endif #endif // BIOMETRY_HARDWARE_BIOMETRY_H_ biometryd-0.3.1/include/biometry/identifier.h000066400000000000000000000040541455450034500212710ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRY_IDENTIFIER_H_ #define BIOMETRY_IDENTIFIER_H_ #include #include #include #include #include namespace biometry { /// @cond class Application; class Reason; class User; /// @endcond /// @brief Verification bundles the types passed to an observer of verification operations. struct BIOMETRY_DLL_PUBLIC Identification { typedef biometry::Progress Progress; ///< Progress information about the completion status of an operation. typedef std::string Reason; ///< Details about cancelation of an operation. typedef std::string Error; ///< Describes error conditions. typedef User Result; ///< Describes the result of an identification operation. }; /// @brief Verifier abstracts verification of a user. class BIOMETRY_DLL_PUBLIC Identifier : private DoNotCopyOrMove { public: // Safe us some typing typedef std::shared_ptr Ptr; /// @brief identify_user returns an operation that represents the identification of a user given a set of candidates with the given reason. virtual Operation::Ptr identify_user(const Application& app, const Reason& reason) = 0; protected: /// @cond Identifier() = default; /// @endcond }; } #endif // BIOMETRY_IDENTIFIER_H_ biometryd-0.3.1/include/biometry/operation.h000066400000000000000000000063361455450034500211540ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRY_OPERATION_H_ #define BIOMETRY_OPERATION_H_ #include #include #include namespace biometry { /// @brief An Operation models an asynchronous operation that can be started and cancelled, /// as well as observed. template class BIOMETRY_DLL_PUBLIC Operation : private DoNotCopyOrMove { public: typedef std::shared_ptr> Ptr; typedef typename T::Progress Progress; typedef typename T::Reason Reason; typedef typename T::Error Error; typedef typename T::Result Result; /// @brief An Observer enables client code to monitor an ongoing operation. class Observer : private DoNotCopyOrMove { public: // Safe us some typing. typedef std::shared_ptr Ptr; typedef typename T::Progress Progress; typedef typename T::Reason Reason; typedef typename T::Error Error; typedef typename T::Result Result; /// @brief on_state_changed is called whenever the state of an operation changed, /// handing the current (new) and previous state to the observer. virtual void on_started() = 0; /// @brief on_progress is called whenever an operation advances. /// /// @param progress contains details describing the progress. virtual void on_progress(const Progress& progress) = 0; /// @brief on_canceled is called when an operation is cancelled. /// /// @param reason contains details explaing the reson for cancelling. virtual void on_canceled(const Reason& reason) = 0; /// @brief on_failed is called when an operation fails. /// /// @param error provides details describing the error condition. virtual void on_failed(const Error& error) = 0; /// @brief on_succeeded is called when an operation succeeds. /// /// @param result provides details handing the result of the operation to observers. virtual void on_succeeded(const Result& result) = 0; protected: /// @cond Observer() = default; /// @endcond }; /// @brief start_with_observer starts the operation, handing updates to 'observer'. virtual void start_with_observer(const typename Observer::Ptr& observer) = 0; /// @brief cancel stops the operation, confirming cancellation to the installed observer. virtual void cancel() = 0; protected: /// @cond Operation() = default; /// @endcond }; } #endif // BIOMETRY_OPERATION_H_ biometryd-0.3.1/include/biometry/optional.h000066400000000000000000000016771455450034500210040ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRY_OPTIONAL_H_ #define BIOMETRY_OPTIONAL_H_ #include #include namespace biometry { template using Optional = boost::optional; } #endif // BIOMETRY_OPTIONAL_H_ biometryd-0.3.1/include/biometry/percent.h000066400000000000000000000032241455450034500206050ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRY_PERCENT_H_ #define BIOMETRY_PERCENT_H_ #include #include namespace biometry { /// @brief Percent represents a value from the interval [0,1]. class BIOMETRY_DLL_PUBLIC Percent { public: /// @brief from_raw_value returns a new Percent instance, initialized to the given value. /// Throws std::runtime_error if value is not in [0,1]. static Percent from_raw_value(double value); /// @brief Percent initializes the instance to undefined. Percent(); /// @brief operator* returns the raw value. double operator*() const; private: explicit Percent(double value); double value; }; /// @brief operator<< inserts percent into out. BIOMETRY_DLL_PUBLIC std::ostream& operator<<(std::ostream& out, const Percent& percent); /// @brief operator== compares lhs and rhs for equality. BIOMETRY_DLL_PUBLIC bool operator==(const Percent& lhs, const Percent& rhs); } #endif // BIOMETRY_PERCENT_H_ biometryd-0.3.1/include/biometry/progress.h000066400000000000000000000031141455450034500210070ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRY_PROGRESS_H_ #define BIOMETRY_PROGRESS_H_ #include #include #include #include namespace biometry { /// @brief Progress bundles information about the current progress of an operation. struct BIOMETRY_DLL_PUBLIC Progress { /// @brief none returns a Progress instance representing no progress. static Progress none(); Percent percent; ///< Percent completed. Dictionary details; ///< Extended information about the current state of the operation. }; /// @brief operator<< inserts progress into out. BIOMETRY_DLL_PUBLIC std::ostream& operator<<(std::ostream& out, const Progress& progress); /// @brief operator== returns true if lhs equals rhs component-wise. BIOMETRY_DLL_PUBLIC bool operator==(const Progress& lhs, const Progress& rhs); } #endif // BIOMETRY_PROGRESS_H_ biometryd-0.3.1/include/biometry/reason.h000066400000000000000000000033701455450034500204360ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_REASON_H_ #define BIOMETRYD_REASON_H_ #include #include namespace biometry { /// @brief Reason models the reason for requesting an identification or verification operation. class BIOMETRY_DLL_PUBLIC Reason { public: /// @brief unknown returns a Reason instance referring to the unknown reason. static Reason unknown(); /// @brief Reason initializes an instance to be equal to unknown(). Reason(); /// @brief Reason initializes an instance with the given string s. explicit Reason(const std::string& s); /// @brief as_string returns the string representation of this instance. const std::string& as_string() const; /// @brief operator std::string returns the string representation of this instance. operator std::string() const; private: /// @cond std::string value; /// @endcond }; /// @brief operator== returns true if lhs has the same name as rhs. BIOMETRY_DLL_PUBLIC bool operator==(const Reason& lhs, const Reason& rhs); } #endif // BIOMETRYD_REASON_H_ biometryd-0.3.1/include/biometry/service.h000066400000000000000000000025311455450034500206050ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_SERVICE_H_ #define BIOMETRYD_SERVICE_H_ #include #include #include namespace biometry { /// @cond class Device; /// @endcond /// /// @brief Service mediates/multiplexes access to the biometric devices known to the system. class BIOMETRY_DLL_PUBLIC Service : public DoNotCopyOrMove { public: /// @brief default_device returns the system-wide default device that should be used /// for identification/verification purposes. virtual std::shared_ptr default_device() const = 0; protected: Service() = default; }; } #endif // BIOMETRYD_SERVICE_H_ biometryd-0.3.1/include/biometry/template_store.h000066400000000000000000000130151455450034500221730ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRY_TEMPLATE_STORE_H_ #define BIOMETRY_TEMPLATE_STORE_H_ #include #include #include namespace biometry { /// @cond class Application; class User; /// @endcond /// /// @brief TemplateStore models maintenance of a device-specific template store in way that /// ensures that no template data ever crosses over the wire (or would need to be extracted from a TEE). class TemplateStore : public DoNotCopyOrMove { public: /// @brief TemplateId is a numeric uniquely identifying a biometric template. typedef std::uint64_t TemplateId; /// @brief SizeQuery bundles the types passed to an observer of a size operation. struct SizeQuery { typedef biometry::Progress Progress; ///< Progress information about the completion status of an operation. typedef std::string Reason; ///< Details about cancelation of an operation. typedef std::string Error; ///< Describes error conditions. typedef std::uint32_t Result; ///< Describes the result of a SizeQuery operation. }; /// @brief List bundles the types passed to an observer of a size operation. struct List { typedef biometry::Progress Progress; ///< Progress information about the completion status of an operation. typedef std::string Reason; ///< Details about cancelation of an operation. typedef std::string Error; ///< Describes error conditions. typedef std::vectorResult; ///< Describes the result of a List operation. }; /// @brief Enrollment bundles the types passed to an observer of enrollment operations. struct Enrollment { typedef biometry::Progress Progress; ///< Progress information about the completion status of an operation. typedef std::string Reason; ///< Details about cancelation of an operation. typedef std::string Error; ///< Describes error conditions. typedef TemplateId Result; ///< Describes the result of an Enrollment operation. }; /// @brief Remove bundles the types passed to an observer of a removal operation. struct Removal { typedef biometry::Progress Progress; ///< Progress information about the completion status of an operation. typedef std::string Reason; ///< Details about cancelation of an operation. typedef std::string Error; ///< Describes error conditions. typedef TemplateId Result; ///< Describes the result of an Enrollment operation. }; /// @brief Clearance bundles the types passed to an observer of clearance operations. struct Clearance { typedef biometry::Progress Progress; ///< Progress information about the completion status of an operation. typedef std::string Reason; ///< Details about cancelation of an operation. typedef std::string Error; ///< Describes error conditions. typedef Void Result; ///< Describes the result of a Clearance operation. }; /// @brief size() returns the number of templates known for user. /// @param app The application requesting the information. /// @param user The user for which we want to query the number of known templates. virtual Operation::Ptr size(const Application& app, const User& user) = 0; /// @brief list returns an operation that yields the list of all templates enrolled for app and user. /// @param app The application requesting the information. /// @param user The user for which we want to query all enrolled templates. virtual Operation::Ptr list(const Application& app, const User& user) = 0; /// @brief enroll returns an operation that represents the enrollment of a new template for a user. /// @param app The application requesting the enrollment operation. /// @param user The user for which we want to enroll the new template. virtual Operation::Ptr enroll(const Application& app, const User& user) = 0; /// @brief remove returns an operation that represents the removal of an individual template. /// @param app The application requesting the removal operation. /// @param user The user for which we want to remove a specific template. /// @param id The id of the template that should be removed. virtual Operation::Ptr remove(const Application& app, const User& user, TemplateId id) = 0; /// @brief clear returns an operation that represents removal of all templates associated to user. /// @param app The application requesting the clear operation. /// @param user The user for which we want to clear templates for. virtual Operation::Ptr clear(const Application& app, const User& user) = 0; }; } #endif // BIOMETRY_TEMPLATE_STORE_H_ biometryd-0.3.1/include/biometry/user.h000066400000000000000000000035351455450034500201300ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRY_USER_H_ #define BIOMETRY_USER_H_ #include #include #include namespace biometry { /// @brief User models struct BIOMETRY_DLL_PUBLIC User { /// @brief root returns an instance of User corresponding to the system's root user. static const User& root(); /// @brief current returns an instance of User corresponding to the currently active user. static const User& current(); /// @brief User initializes a new instance with the given id. User(uid_t id = root().id); uid_t id; ///< @brief The numeric id of the user. }; /// @brief operator< returns true iff lhs's id is smaller than rhs's id. BIOMETRY_DLL_PUBLIC bool operator<(const User& lhs, const User& rhs); /// @brief operator== returns true iff lhs's id equals rhs's id. BIOMETRY_DLL_PUBLIC bool operator==(const User& lhs, const User& rhs); /// @brief operator<< inserts user into out. BIOMETRY_DLL_PUBLIC std::ostream& operator<<(std::ostream& out, const User& user); /// @brief operator>> extracts user from in. BIOMETRY_DLL_PUBLIC std::istream& operator>>(std::istream& in, User& user); } #endif // BIOMETRY_USER_H_ biometryd-0.3.1/include/biometry/variant.h000066400000000000000000000057531455450034500206220ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_VARIANT_H_ #define BIOMETRYD_VARIANT_H_ #include #include #include #include #include #include #include namespace biometry { class BIOMETRY_DLL_PUBLIC Variant { public: /// @brief None models the unset value. struct None {}; enum class Type { none, boolean, integer, floating_point, rectangle, string, blob, vector }; static Variant b(bool value); static Variant i(std::int64_t value); static Variant d(double value); static Variant r(const biometry::Rectangle& value); static Variant s(const std::string& value); static Variant bl(const std::vector& value); static Variant v(const std::vector& value); Variant(); Variant(const Variant&); explicit Variant(bool b); explicit Variant(std::int64_t i); explicit Variant(double d); explicit Variant(const biometry::Rectangle& value); explicit Variant(const std::string& s); explicit Variant(const std::vector& b); explicit Variant(const std::vector& b); ~Variant(); Variant& operator=(const Variant&); bool operator==(const Variant&) const; std::ostream& print(std::ostream&) const; Type type() const; const bool& boolean() const; void boolean(bool value); std::int64_t integer() const; void integer(std::int64_t value); double floating_point() const; void floating_point(double value); const biometry::Rectangle& rectangle() const; void rectangle(const biometry::Rectangle& rectangle); const std::string& string() const; void string(const std::string& value); const std::vector& blob() const; void blob(const std::vector& value); const std::vector& vector() const; void vector(const std::vector& vector); private: struct Private; std::unique_ptr p; }; BIOMETRY_DLL_PUBLIC bool operator==(const Variant::None&, const Variant::None&); BIOMETRY_DLL_PUBLIC std::ostream& operator<<(std::ostream& out, const Variant::None&); BIOMETRY_DLL_PUBLIC std::ostream& operator<<(std::ostream& out, const Variant& v); } #endif // UTIL_VARIANT_H_ biometryd-0.3.1/include/biometry/verifier.h000066400000000000000000000043071455450034500207630ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRY_VERIFIER_H_ #define BIOMETRY_VERIFIER_H_ #include #include #include namespace biometry { /// @cond class Application; class Reason; class User; /// @endcond /// @brief Verification bundles the types passed to an observer of verification operations. struct BIOMETRY_DLL_PUBLIC Verification { /// @brief Result enumerates all possible results of a verification operation. enum class Result { verified, ///< The given user could be verified. not_verified ///< The given user could not be verified. }; typedef biometry::Progress Progress; ///< Progress information about the completion status of an operation. typedef std::string Reason; ///< Details about cancelation of an operation. typedef std::string Error; ///< Describes error conditions. typedef Result Result; ///< Describes the result of a verification operation. }; /// @brief Verifier abstracts verification of a user. class BIOMETRY_DLL_PUBLIC Verifier : private DoNotCopyOrMove { public: // Safe us some typing typedef std::shared_ptr Ptr; /// @brief verify_user returns an operation that represents the verification of 'user' for 'reason'. virtual Operation::Ptr verify_user(const Application& app, const User& user, const Reason& reason) = 0; protected: /// @cond Verifier() = default; /// @endcond }; } #endif // BIOMETRY_VERIFIER_H_ biometryd-0.3.1/include/biometry/version.h.in000066400000000000000000000034701455450034500212420ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_VERSION_H_ #define BIOMETRYD_VERSION_H_ #include #include namespace biometry { namespace build { /// @brief version_major marks the major version of the library. The constant is meant to be used /// by client code both at build and runtime, enabling version checks. static constexpr const std::uint32_t version_major{@BIOMETRYD_VERSION_MAJOR@}; /// @brief version_major marks the minor version of the library. The constant is meant to be used /// by client code both at build and runtime, enabling version checks. static constexpr const std::uint32_t version_minor{@BIOMETRYD_VERSION_MINOR@}; /// @brief version_patch marks the major version of the library. The constant is meant to be used /// by client code both at build and runtime, enabling version checks. static constexpr const std::uint32_t version_patch{@BIOMETRYD_VERSION_PATCH@}; } /// @brief version queries the version of the library, placing the result in major, minor and patch. BIOMETRY_DLL_PUBLIC void version(std::uint32_t& major, std::uint32_t& minor, std::uint32_t& patch); } #endif // BIOMETRYD_VERSION_H_ biometryd-0.3.1/include/biometry/visibility.h000066400000000000000000000016641455450034500213420ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRY_VISIBILITY_H_ #define BIOMETRY_VISIBILITY_H_ #define BIOMETRY_DLL_PUBLIC __attribute__ ((visibility ("default"))) #define BIOMETRY_DLL_LOCAL __attribute__ ((visibility ("hidden"))) #endif // BIOMETRY_VISIBILITY_H_ biometryd-0.3.1/include/biometry/void.h000066400000000000000000000020111455450034500200770ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRY_VOID_H_ #define BIOMETRY_VOID_H_ #include namespace biometry { /// @brief Void marks an empty type. struct BIOMETRY_DLL_PUBLIC Void {}; /// @cond BIOMETRY_DLL_PUBLIC inline bool operator==(const Void&, const Void&) { return true; } /// @endcond } #endif // BIOMETRY_VOID_H_ biometryd-0.3.1/src/000077500000000000000000000000001455450034500143055ustar00rootroot00000000000000biometryd-0.3.1/src/CMakeLists.txt000066400000000000000000000000641455450034500170450ustar00rootroot00000000000000add_subdirectory(biometry) # add_subdirectory(util) biometryd-0.3.1/src/biometry/000077500000000000000000000000001455450034500161375ustar00rootroot00000000000000biometryd-0.3.1/src/biometry/CMakeLists.txt000066400000000000000000000071531455450034500207050ustar00rootroot00000000000000file(GLOB_RECURSE BIOMETRYD_PUBLIC_HEADERS ${CMAKE_SOURCE_DIR}/include/*.h) set(symbol_map "${CMAKE_SOURCE_DIR}/symbols.map") add_subdirectory(qml) configure_file(daemon_configuration.cpp.in daemon_configuration.cpp) if(WITH_HYBRIS) set(HYBRIS_SRC devices/android.h devices/android.cpp hardware/biometry_fp_api.cpp hardware/android_hw_module.h ) add_definitions(-DWITH_HYBRIS) endif() add_library( biometry SHARED application.cpp daemon.h daemon.cpp device_registrar.h device_registrar.cpp device_registry.h device_registry.cpp dispatching_service.h dispatching_service.cpp geometry.cpp percent.cpp progress.cpp reason.cpp runtime.h runtime.cpp tracing_operation_observer.h user.cpp variant.cpp version.cpp "${CMAKE_CURRENT_BINARY_DIR}/daemon_configuration.cpp" cmds/config.h cmds/config.cpp cmds/enroll.h cmds/enroll.cpp cmds/identify.h cmds/identify.cpp cmds/list_devices.h cmds/list_devices.cpp cmds/run.h cmds/run.cpp cmds/test.h cmds/test.cpp cmds/version.h cmds/version.cpp dbus/codec.h dbus/interface.h dbus/service.cpp dbus/stub/service.h dbus/stub/service.cpp dbus/stub/device.h dbus/stub/device.cpp dbus/stub/template_store.h dbus/stub/template_store.cpp dbus/stub/identifier.h dbus/stub/identifier.cpp dbus/stub/observer.h dbus/stub/operation.h dbus/skeleton/credentials_resolver.h dbus/skeleton/daemon_credentials_resolver.h dbus/skeleton/daemon_credentials_resolver.cpp dbus/skeleton/request_verifier.h dbus/skeleton/request_verifier.cpp dbus/skeleton/service.h dbus/skeleton/service.cpp dbus/skeleton/device.h dbus/skeleton/device.cpp dbus/skeleton/template_store.h dbus/skeleton/template_store.cpp dbus/skeleton/identifier.h dbus/skeleton/identifier.cpp dbus/skeleton/observer.h dbus/skeleton/operation.h devices/dispatching.h devices/dispatching.cpp devices/dummy.h devices/dummy.cpp devices/fingerprint_reader.cpp devices/forwarding.h devices/forwarding.cpp devices/plugin/device.h devices/plugin/device.cpp devices/plugin/enumerator.h devices/plugin/enumerator.cpp devices/plugin/loader.h devices/plugin/loader.cpp devices/plugin/verifier.h devices/plugin/verifier.cpp util/atomic_counter.h util/atomic_counter.cpp util/benchmark.h util/benchmark.cpp util/cli.h util/cli.cpp util/configuration.h util/configuration.cpp util/dispatcher.h util/dispatcher.cpp util/dynamic_library.h util/dynamic_library.cpp util/json_configuration_builder.h util/json_configuration_builder.cpp util/not_implemented.h util/not_implemented.cpp util/not_reachable.h util/not_reachable.cpp util/once.h util/property_store.h util/property_store.cpp util/statistics.h util/statistics.cpp util/streaming_configuration_builder.h util/synchronized.h bridge/bridge_defs.h bridge/bridge.h bridge/hybris_bridge_defs.h ${HYBRIS_SRC} ${BIOMETRYD_PUBLIC_HEADERS}) set_target_properties( biometry PROPERTIES LINK_FLAGS "${ldflags} -Wl,--version-script,${symbol_map}" LINK_DEPENDS ${symbol_map} VERSION ${BIOMETRYD_VERSION_MAJOR}.${BIOMETRYD_VERSION_MINOR}.${BIOMETRYD_VERSION_PATCH} SOVERSION ${BIOMETRYD_VERSION_MAJOR} ) target_link_libraries( biometry util dl elf ${Boost_LIBRARIES} ${DBUS_CPP_LIBRARIES} ${PROCESS_CPP_LIBRARIES} ${SQLITE3_LIBRARIES} ${Hybris}) add_executable( biometryd daemon_main.cpp) target_link_libraries( biometryd biometry) install( TARGETS biometry LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ) install( TARGETS biometryd RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) biometryd-0.3.1/src/biometry/application.cpp000066400000000000000000000023761455450034500211560ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include biometry::Application biometry::Application::system() { return Application{"system"}; } biometry::Application::Application() : value{"system"} { } biometry::Application::Application(const std::string& s) : value{s} { } const std::string& biometry::Application::as_string() const { return value; } biometry::Application::operator std::string() const { return value; } bool biometry::operator==(const biometry::Application& lhs, const biometry::Application& rhs) { return lhs.as_string() == rhs.as_string(); } biometryd-0.3.1/src/biometry/bridge/000077500000000000000000000000001455450034500173735ustar00rootroot00000000000000biometryd-0.3.1/src/biometry/bridge/bridge.h000066400000000000000000000040641455450034500210040ustar00rootroot00000000000000/* * Copyright (C) 2012 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voss * Ricardo Mendoza */ #ifndef BASE_BRIDGE_H_ #define BASE_BRIDGE_H_ #include #include #include #include #include #include #define MAX_MODULE_NAME 32 #define HIDDEN_SYMBOL __attribute__ ((visibility ("hidden"))) namespace internal { template class HIDDEN_SYMBOL Bridge { public: static Bridge& instance() { static Bridge bridge; return bridge; } void* resolve_symbol(const char* symbol, const char* module = "") const { static const char* test_modules = secure_getenv("UBUNTU_PLATFORM_API_TEST_OVERRIDE"); if (test_modules && strstr(test_modules, module)) { printf("Platform API: INFO: Overriding symbol '%s' with test version\n", symbol); return Scope::dlsym_fn(lib_override_handle, symbol); } else { return Scope::dlsym_fn(lib_handle, symbol); } } protected: Bridge() : lib_handle(Scope::dlopen_fn(Scope::path(), RTLD_LAZY)) { if (Scope::override_path() && secure_getenv("UBUNTU_PLATFORM_API_TEST_OVERRIDE")) lib_override_handle = (Scope::dlopen_fn(Scope::override_path(), RTLD_LAZY)); } ~Bridge() { } void* lib_handle; void* lib_override_handle; }; } #endif // BRIDGE_H_ biometryd-0.3.1/src/biometry/bridge/bridge_defs.h000066400000000000000000000145121455450034500220040ustar00rootroot00000000000000/* * Copyright (C) 2012-2015 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voss * Ricardo Mendoza */ #ifndef BRIDGE_DEFS_H_ #define BRIDGE_DEFS_H_ // Must be included after the Bridge class is defined #ifdef __cplusplus extern "C" { #endif /**********************************************************/ /*********** Implementation starts here *******************/ /**********************************************************/ // this allows DLSYM to return NULL (happens if the backend is not available), // and returns NULL in that case; return_type must be a pointer! #define IMPLEMENT_CTOR0(module, return_type, symbol) \ return_type symbol() \ { \ static return_type (*f)() = NULL; \ DLSYM(&f, #symbol, #module); \ return f ? f() : NULL;} #define IMPLEMENT_FUNCTION0(module, return_type, symbol) \ return_type symbol() \ { \ static return_type (*f)() = NULL; \ DLSYM(&f, #symbol, #module); \ return f();} #define IMPLEMENT_VOID_FUNCTION0(module, symbol) \ void symbol() \ { \ static void (*f)() = NULL; \ DLSYM(&f, #symbol, #module); \ f();} #define IMPLEMENT_FUNCTION1(module, return_type, symbol, arg1) \ return_type symbol(arg1 _1) \ { \ static return_type (*f)(arg1) = NULL; \ DLSYM(&f, #symbol, #module); \ return f(_1); } #define IMPLEMENT_VOID_FUNCTION1(module, symbol, arg1) \ void symbol(arg1 _1) \ { \ static void (*f)(arg1) = NULL; \ DLSYM(&f, #symbol, #module); \ f(_1); } #define IMPLEMENT_FUNCTION2(module, return_type, symbol, arg1, arg2) \ return_type symbol(arg1 _1, arg2 _2) \ { \ static return_type (*f)(arg1, arg2) = NULL; \ DLSYM(&f, #symbol, #module); \ return f(_1, _2); } #define IMPLEMENT_VOID_FUNCTION2(module, symbol, arg1, arg2) \ void symbol(arg1 _1, arg2 _2) \ { \ static void (*f)(arg1, arg2) = NULL; \ DLSYM(&f, #symbol, #module); \ f(_1, _2); } #define IMPLEMENT_FUNCTION3(module, return_type, symbol, arg1, arg2, arg3) \ return_type symbol(arg1 _1, arg2 _2, arg3 _3) \ { \ static return_type (*f)(arg1, arg2, arg3) = NULL; \ DLSYM(&f, #symbol, #module); \ return f(_1, _2, _3); } #define IMPLEMENT_VOID_FUNCTION3(module, symbol, arg1, arg2, arg3) \ void symbol(arg1 _1, arg2 _2, arg3 _3) \ { \ static void (*f)(arg1, arg2, arg3) = NULL; \ DLSYM(&f, #symbol, #module); \ f(_1, _2, _3); } #define IMPLEMENT_VOID_FUNCTION4(module, symbol, arg1, arg2, arg3, arg4) \ void symbol(arg1 _1, arg2 _2, arg3 _3, arg4 _4) \ { \ static void (*f)(arg1, arg2, arg3, arg4) = NULL; \ DLSYM(&f, #symbol, #module); \ f(_1, _2, _3, _4); } #define IMPLEMENT_FUNCTION4(module, return_type, symbol, arg1, arg2, arg3, arg4) \ return_type symbol(arg1 _1, arg2 _2, arg3 _3, arg4 _4) \ { \ static return_type (*f)(arg1, arg2, arg3, arg4) = NULL; \ DLSYM(&f, #symbol, #module); \ return f(_1, _2, _3, _4); } #define IMPLEMENT_FUNCTION6(module, return_type, symbol, arg1, arg2, arg3, arg4, arg5, arg6) \ return_type symbol(arg1 _1, arg2 _2, arg3 _3, arg4 _4, arg5 _5, arg6 _6) \ { \ static return_type (*f)(arg1, arg2, arg3, arg4, arg5, arg6) = NULL; \ DLSYM(&f, #symbol, #module); \ return f(_1, _2, _3, _4, _5, _6); } #define IMPLEMENT_VOID_FUNCTION7(module, symbol, arg1, arg2, arg3, arg4, arg5, arg6, arg7) \ void symbol(arg1 _1, arg2 _2, arg3 _3, arg4 _4, arg5 _5, arg6 _6, arg7 _7) \ { \ static void (*f)(arg1, arg2, arg3, arg4, arg5, arg6, arg7) = NULL; \ DLSYM(&f, #symbol, #module); \ f(_1, _2, _3, _4, _5, _6, _7); } #define IMPLEMENT_VOID_FUNCTION8(module, symbol, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) \ void symbol(arg1 _1, arg2 _2, arg3 _3, arg4 _4, arg5 _5, arg6 _6, arg7 _7, arg8 _8) \ { \ static void (*f)(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) = NULL; \ DLSYM(&f, #symbol, #module); \ f(_1, _2, _3, _4, _5, _6, _7, _8); } #ifdef __cplusplus } #endif #endif // BRIDGE_DEFS_H_ biometryd-0.3.1/src/biometry/bridge/hybris_bridge_defs.h000066400000000000000000000200101455450034500233520ustar00rootroot00000000000000/* * Copyright (C) 2012-2015 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voss * Ricardo Mendoza */ #ifndef HYBRIS_BRIDGE_DEFS_H_ #define HYBRIS_BRIDGE_DEFS_H_ // Must be included after the Bridge class is defined #ifdef __cplusplus extern "C" { #endif /**********************************************************/ /*********** Implementation starts here *******************/ /**********************************************************/ // this allows DLSYM to return NULL (happens if the backend is not available), // and returns NULL in that case; return_type must be a pointer! #define IMPLEMENT_CTOR0(return_type, symbol) \ return_type symbol() \ { \ static return_type (*f)() = NULL; \ DLSYM(&f, #symbol); \ return f ? f() : NULL;} #define IMPLEMENT_FUNCTION0(return_type, symbol) \ return_type symbol() \ { \ static return_type (*f)() = NULL; \ DLSYM(&f, #symbol); \ return f();} #define IMPLEMENT_OPTIONAL_FUNCTION0(return_type, symbol, return_value) \ return_type symbol() \ { \ static return_type (*f)() = NULL; \ DLSYM(&f, #symbol); \ return f ? f() : return_value;} #define IMPLEMENT_VOID_FUNCTION0(symbol) \ void symbol() \ { \ static void (*f)() = NULL; \ DLSYM(&f, #symbol); \ f();} #define IMPLEMENT_OPTIONAL_VOID_FUNCTION0(symbol) \ void symbol() \ { \ static void (*f)() = NULL; \ DLSYM(&f, #symbol); \ if (f) f();} #define IMPLEMENT_FUNCTION1(return_type, symbol, arg1) \ return_type symbol(arg1 _1) \ { \ static return_type (*f)(arg1) = NULL; \ DLSYM(&f, #symbol); \ return f(_1); } #define IMPLEMENT_OPTIONAL_FUNCTION1(return_type, symbol, return_value, arg1) \ return_type symbol(arg1 _1) \ { \ static return_type (*f)(arg1) = NULL; \ DLSYM(&f, #symbol); \ return f ? f(_1) : return_value; } #define IMPLEMENT_VOID_FUNCTION1(symbol, arg1) \ void symbol(arg1 _1) \ { \ static void (*f)(arg1) = NULL; \ DLSYM(&f, #symbol); \ f(_1); } #define IMPLEMENT_OPTIONAL_VOID_FUNCTION1(symbol, arg1) \ void symbol(arg1 _1) \ { \ static void (*f)(arg1) = NULL; \ DLSYM(&f, #symbol); \ if (f) f(_1); } #define IMPLEMENT_FUNCTION2(return_type, symbol, arg1, arg2) \ return_type symbol(arg1 _1, arg2 _2) \ { \ static return_type (*f)(arg1, arg2) = NULL; \ DLSYM(&f, #symbol); \ return f(_1, _2); } #define IMPLEMENT_VOID_FUNCTION2(symbol, arg1, arg2) \ void symbol(arg1 _1, arg2 _2) \ { \ static void (*f)(arg1, arg2) = NULL; \ DLSYM(&f, #symbol); \ f(_1, _2); } #define IMPLEMENT_OPTIONAL_VOID_FUNCTION2(symbol, arg1, arg2) \ void symbol(arg1 _1, arg2 _2) \ { \ static void (*f)(arg1, arg2) = NULL; \ DLSYM(&f, #symbol); \ if (f) f(_1, _2); } #define IMPLEMENT_FUNCTION3(return_type, symbol, arg1, arg2, arg3) \ return_type symbol(arg1 _1, arg2 _2, arg3 _3) \ { \ static return_type (*f)(arg1, arg2, arg3) = NULL; \ DLSYM(&f, #symbol); \ return f(_1, _2, _3); } #define IMPLEMENT_VOID_FUNCTION3(symbol, arg1, arg2, arg3) \ void symbol(arg1 _1, arg2 _2, arg3 _3) \ { \ static void (*f)(arg1, arg2, arg3) = NULL; \ DLSYM(&f, #symbol); \ f(_1, _2, _3); } #define IMPLEMENT_VOID_FUNCTION4(symbol, arg1, arg2, arg3, arg4) \ void symbol(arg1 _1, arg2 _2, arg3 _3, arg4 _4) \ { \ static void (*f)(arg1, arg2, arg3, arg4) = NULL; \ DLSYM(&f, #symbol); \ f(_1, _2, _3, _4); } #define IMPLEMENT_FUNCTION4(return_type, symbol, arg1, arg2, arg3, arg4) \ return_type symbol(arg1 _1, arg2 _2, arg3 _3, arg4 _4) \ { \ static return_type (*f)(arg1, arg2, arg3, arg4) = NULL; \ DLSYM(&f, #symbol); \ return f(_1, _2, _3, _4); } #define IMPLEMENT_FUNCTION6(return_type, symbol, arg1, arg2, arg3, arg4, arg5, arg6) \ return_type symbol(arg1 _1, arg2 _2, arg3 _3, arg4 _4, arg5 _5, arg6 _6) \ { \ static return_type (*f)(arg1, arg2, arg3, arg4, arg5, arg6) = NULL; \ DLSYM(&f, #symbol); \ return f(_1, _2, _3, _4, _5, _6); } #define IMPLEMENT_VOID_FUNCTION7(symbol, arg1, arg2, arg3, arg4, arg5, arg6, arg7) \ void symbol(arg1 _1, arg2 _2, arg3 _3, arg4 _4, arg5 _5, arg6 _6, arg7 _7) \ { \ static void (*f)(arg1, arg2, arg3, arg4, arg5, arg6, arg7) = NULL; \ DLSYM(&f, #symbol); \ f(_1, _2, _3, _4, _5, _6, _7); } #define IMPLEMENT_VOID_FUNCTION8(symbol, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) \ void symbol(arg1 _1, arg2 _2, arg3 _3, arg4 _4, arg5 _5, arg6 _6, arg7 _7, arg8 _8) \ { \ static void (*f)(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) = NULL; \ DLSYM(&f, #symbol); \ f(_1, _2, _3, _4, _5, _6, _7, _8); } #ifdef __cplusplus } #endif #endif // HYBRIS_BRIDGE_DEFS_H_ biometryd-0.3.1/src/biometry/cmds/000077500000000000000000000000001455450034500170655ustar00rootroot00000000000000biometryd-0.3.1/src/biometry/cmds/config.cpp000066400000000000000000000053701455450034500210430ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include namespace cli = biometry::util::cli; namespace { std::string enumerate_flags() { std::stringstream ss; ss << biometry::cmds::Config::Flag::default_plugin_directory << ", " << biometry::cmds::Config::Flag::custom_plugin_directory; return ss.str(); } } biometry::cmds::Config::Config() : CommandWithFlagsAndAction{cli::Name{"config"}, cli::Usage{"queries configuration options of the daemon"}, cli::Description{"queries configuration options of the daemon"}} { flag(cli::make_flag(cli::Name{"flag"}, cli::Description{"one of {" + enumerate_flags() + "}"}, flag_)); action([this](const cli::Command::Context& ctxt) { if (not flag_) throw cli::Command::FlagsMissing{}; switch (*flag_) { case Flag::default_plugin_directory: ctxt.cout << biometry::Daemon::Configuration::default_plugin_directory().string() << std::endl; break; case Flag::custom_plugin_directory: ctxt.cout << biometry::Daemon::Configuration::custom_plugin_directory().string() << std::endl; break; } return EXIT_SUCCESS; }); } namespace { typedef boost::bimap Lut; typedef Lut::value_type Entry; const Lut& lut() { static const auto entries = { Entry{biometry::cmds::Config::Flag::default_plugin_directory, "default_plugin_directory"}, Entry{biometry::cmds::Config::Flag::custom_plugin_directory, "custom_plugin_directory"} }; static const Lut instance{entries.begin(), entries.end()}; return instance; } } std::ostream& biometry::cmds::operator<<(std::ostream& out, biometry::cmds::Config::Flag flag) { return out << lut().left.at(flag); } std::istream& biometry::cmds::operator>>(std::istream& in, biometry::cmds::Config::Flag& flag) { std::string s; in >> s; if (lut().right.count(s) == 0) throw cli::Command::FlagsWithInvalidValue{}; flag = lut().right.at(s); return in; } biometryd-0.3.1/src/biometry/cmds/config.h000066400000000000000000000034471455450034500205130ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_CMDS_CONFIG_H_ #define BIOMETRYD_CMDS_CONFIG_H_ #include #include #include #include #include namespace biometry { namespace cmds { /// @brief Config queries configuration options of biometryd. class BIOMETRY_DLL_PUBLIC Config : public util::cli::CommandWithFlagsAndAction { public: /// @brief Flag enumerates all known configuration flags queryable by the command. enum class Flag { default_plugin_directory, ///< The default plugin installation directory. custom_plugin_directory ///< The custom plugin installation directory. }; /// @brief Config configures a new instance. Config(); private: biometry::Optional flag_; }; /// @brief operator<< inserts flag into out and returns out. BIOMETRY_DLL_PUBLIC std::ostream& operator<<(std::ostream& out, Config::Flag flag); /// @brief operator>> extracts flag from in, and returns in. BIOMETRY_DLL_PUBLIC std::istream& operator>>(std::istream& in, Config::Flag& flag); } } #endif // BIOMETRYD_CMDS_CONFIG_H_ biometryd-0.3.1/src/biometry/cmds/enroll.cpp000066400000000000000000000052371455450034500210730ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include #include #include #include #include #include #include #include #include namespace cli = biometry::util::cli; biometry::cmds::Enroll::Enroll() : CommandWithFlagsAndAction{cli::Name{"enroll"}, cli::Usage{"enroll"}, cli::Description{"enrolls a new template to a device"}}, user(biometry::User::current()) { flag(cli::make_flag(cli::Name{"device"}, cli::Description{"The device to enroll to"}, device)); flag(cli::make_flag(cli::Name{"user"}, cli::Description{"The user to enroll for"}, device)); flag(cli::make_flag(cli::Name{"config"}, cli::Description{"The daemon configuration"}, config)); action([this](const cli::Command::Context& ctxt) { if (device.empty()) { ctxt.cout << "You must specify a device for enrolling a template" << std::endl; return EXIT_FAILURE; } using StreamingJsonConfigurationBuilder = util::StreamingConfigurationBuilder; StreamingJsonConfigurationBuilder builder { config ? StreamingJsonConfigurationBuilder::make_streamer(config.get()) : StreamingJsonConfigurationBuilder::make_streamer(std::cin) }; auto descriptor = biometry::device_registry().at(device); auto device = descriptor->create({}); auto op = device->template_store().enroll(biometry::Application::system(), user); ctxt.cout << "Starting template enrollment for " << user << " to " << descriptor->name() << std::endl; op->start_with_observer(std::make_shared>()); return 0; }); } biometryd-0.3.1/src/biometry/cmds/enroll.h000066400000000000000000000025541455450034500205370ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_CMDS_ENROLL_H_ #define BIOMETRYD_CMDS_ENROLL_H_ #include #include #include #include #include #include #include namespace biometry { namespace cmds { /// @brief Enroll requests enrollment of a new template to a biometric device. class Enroll : public util::cli::CommandWithFlagsAndAction { public: /// @brief Enroll creates a new instance, initializing flags to default values. Enroll(); private: std::string device; Optional config; User user; }; } } #endif // BIOMETRYD_CMDS_ENROLL_H_ biometryd-0.3.1/src/biometry/cmds/identify.cpp000066400000000000000000000051421455450034500214060ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include #include #include #include #include #include #include #include #include #include #include namespace cli = biometry::util::cli; biometry::cmds::Identify::Identify() : CommandWithFlagsAndAction{cli::Name{"identify"}, cli::Usage{"identify"}, cli::Description{"tries to identify the user holding the device"}} { flag(cli::make_flag(cli::Name{"device"}, cli::Description{"The device to enroll to"}, device)); flag(cli::make_flag(cli::Name{"config"}, cli::Description{"The daemon configuration"}, config)); action([this](const cli::Command::Context& ctxt) { if (device.empty()) { ctxt.cout << "You must specify a device for identification" << std::endl; return EXIT_FAILURE; } using StreamingJsonConfigurationBuilder = util::StreamingConfigurationBuilder; StreamingJsonConfigurationBuilder builder { config ? StreamingJsonConfigurationBuilder::make_streamer(config.get()) : StreamingJsonConfigurationBuilder::make_streamer(std::cin) }; auto descriptor = biometry::device_registry().at(device); auto device = descriptor->create({}); auto op = device->identifier().identify_user(biometry::Application::system(), biometry::Reason{"requested by cli"}); ctxt.cout << "Starting identification using device " << descriptor->name() << std::endl; op->start_with_observer(std::make_shared>()); return 0; }); } biometryd-0.3.1/src/biometry/cmds/identify.h000066400000000000000000000025211455450034500210510ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_CMDS_IDENTIFY_H_ #define BIOMETRYD_CMDS_IDENTIFY_H_ #include #include #include #include #include #include #include namespace biometry { namespace cmds { /// @brief Identify requests identification of the user. class Identify : public util::cli::CommandWithFlagsAndAction { public: /// @brief Enroll creates a new instance, initializing flags to default values. Identify(); private: std::string device; Optional config; }; } } #endif // BIOMETRYD_CMDS_IDENTIFY_H_ biometryd-0.3.1/src/biometry/cmds/list_devices.cpp000066400000000000000000000024521455450034500222510ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include namespace cli = biometry::util::cli; biometry::cmds::ListDevices::ListDevices() : CommandWithFlagsAndAction{cli::Name{"list-devices"}, cli::Usage{"list-devices"}, cli::Description{"lists all known devices"}} { action([](const cli::Command::Context& ctxt) { ctxt.cout << "Known devices:" << std::endl; for (const auto& pair : biometry::device_registry()) ctxt.cout << " - " << pair.first << "\t" << pair.second->description() << std::endl; return 0; }); } biometryd-0.3.1/src/biometry/cmds/list_devices.h000066400000000000000000000017551455450034500217230ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_CMDS_LIST_DEVICES_H_ #define BIOMETRYD_CMDS_LIST_DEVICES_H_ #include namespace biometry { namespace cmds { class ListDevices : public util::cli::CommandWithFlagsAndAction { public: ListDevices(); }; } } #endif // BIOMETRYD_CMDS_LIST_DEVICES_H_ biometryd-0.3.1/src/biometry/cmds/run.cpp000066400000000000000000000115541455450034500204030ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace cli = biometry::util::cli; namespace { const std::unordered_map& device_id_lut() { static const std::unordered_map& instance { {"turbo", "meizu::FingerprintReader"} }; return instance; } std::shared_ptr device_from_config(const boost::filesystem::path& config_file) { using StreamingJsonConfigurationBuilder = biometry::util::StreamingConfigurationBuilder; StreamingJsonConfigurationBuilder builder{StreamingJsonConfigurationBuilder::make_streamer(config_file)}; auto configuration = builder.build_configuration(); auto default_device = configuration["defaultDevice"]; biometry::util::Configuration device_config; device_config["config"] = default_device["config"]; auto default_device_descriptor = biometry::device_registry().at(default_device[std::string("id")].value().string()); return default_device_descriptor->create(device_config); } std::shared_ptr device_from_oracle(const biometry::util::PropertyStore& property_store) { auto id = biometry::cmds::Run::ConfigurationOracle{}.make_an_educated_guess(property_store); auto default_device_descriptor = biometry::device_registry().at(id); return default_device_descriptor->create({}); } std::shared_ptr create_default_device(const biometry::Optional& config_file, const biometry::util::PropertyStore& property_store) { return config_file ? device_from_config(*config_file) : device_from_oracle(property_store); } } biometry::Device::Id biometry::cmds::Run::ConfigurationOracle::make_an_educated_guess(const biometry::util::PropertyStore& property_store) const { auto value = property_store.get("ro.product.device"); if (value.empty()) throw std::runtime_error{"Could not identify device"}; if (device_id_lut().count(value) == 0) return "android"; return device_id_lut().at(value); } biometry::cmds::Run::BusFactory biometry::cmds::Run::system_bus_factory() { return []() { return std::make_shared(core::dbus::WellKnownBus::system); }; } biometry::cmds::Run::Run(const std::shared_ptr& property_store, const BusFactory& bus_factory) : CommandWithFlagsAndAction{cli::Name{"run"}, cli::Usage{"run"}, cli::Description{"run the daemon"}}, bus_factory{bus_factory}, property_store{property_store} { flag(cli::make_flag(cli::Name{"config"}, cli::Description{"The daemon configuration"}, config)); action([this](const cli::Command::Context& ctxt) { auto trap = core::posix::trap_signals_for_all_subsequent_threads({core::posix::Signal::sig_term}); trap->signal_raised().connect([trap](const core::posix::Signal&) { trap->stop(); }); std::shared_ptr device; try { device = create_default_device(config, *Run::property_store); } catch (...) { ctxt.cout << "Failed to instantiate device." << std::endl; return EX_CONFIG; } auto runtime = Runtime::create(); auto impl = std::make_shared(biometry::util::create_dispatcher_for_runtime(runtime), device); runtime->start(); auto bus = this->bus_factory(); bus->install_executor(core::dbus::asio::make_executor(bus, runtime->service())); auto skeleton = biometry::dbus::skeleton::Service::create_for_bus(bus, impl); trap->run(); bus->stop(); runtime->stop(); return EXIT_SUCCESS; }); } biometryd-0.3.1/src/biometry/cmds/run.h000066400000000000000000000046261455450034500200520ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_CMDS_RUN_H_ #define BIOMETRYD_CMDS_RUN_H_ #include #include #include #include #include #include #include #include #include #include namespace biometry { namespace cmds { class BIOMETRY_DLL_PUBLIC Run : public util::cli::CommandWithFlagsAndAction { public: /// @brief ConfigurationOracle trys to make an educated on the device that the service is running on. /// /// The implementation right now is quite naive and just queries android properties to figure out a product name class ConfigurationOracle { public: /// @brief make_an_educated_guess returns a Device::Id instance if the oracle /// succeeded in identifying a known device type and map it to an appropriate /// default biometry::Device implementation. Device::Id make_an_educated_guess(const util::PropertyStore& property_store) const; }; /// @brief BusFactory models creation of bus instances. typedef std::function BusFactory; /// @brief system_bus_factory returns a BusFactory creating connections to the system bus. static BusFactory system_bus_factory(); /// @brief Run initializes a new instance with the given bus_factory. Run(const std::shared_ptr& property_store, const BusFactory& bus_factory = system_bus_factory()); private: BusFactory bus_factory; std::shared_ptr property_store; Optional config; }; } } #endif // BIOMETRYD_CMDS_RUN_H_ biometryd-0.3.1/src/biometry/cmds/test.cpp000066400000000000000000000203051455450034500205500ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace cli = biometry::util::cli; namespace { template class SyncingObserver : public biometry::Operation::Observer { public: typedef typename biometry::Operation::Observer Super; SyncingObserver(std::ostream& out, const std::string& name, std::uint32_t width = 80) : pb{out, name, width}, future{promise.get_future()} { } typename Super::Result sync() { return future.get(); } // From biometry::Operation::Observer void on_started() override { pb.update(0); } void on_progress(const typename Super::Progress& progress) override { pb.update(*progress.percent); } void on_canceled(const typename Super::Reason& reason) override { promise.set_exception(std::make_exception_ptr(std::runtime_error{reason})); } void on_failed(const typename Super::Error& error) override { promise.set_exception(std::make_exception_ptr(std::runtime_error{error})); } void on_succeeded(const typename Super::Result& result) override { promise.set_value(result); } private: biometry::util::cli::ProgressBar pb; std::promise promise; std::future future{promise.get_future()}; }; std::shared_ptr device_from_config_file(const boost::filesystem::path& file) { using StreamingJsonConfigurationBuilder = biometry::util::StreamingConfigurationBuilder; StreamingJsonConfigurationBuilder builder{StreamingJsonConfigurationBuilder::make_streamer(file)}; const auto configuration = builder.build_configuration(); static const auto throw_configuration_invalid = [](){ std::throw_with_nested(biometry::cmds::Test::ConfigurationInvalid{});}; const auto id = configuration ("device", throw_configuration_invalid) ("id", throw_configuration_invalid); biometry::util::Configuration config; config["config"] = configuration ("device", throw_configuration_invalid) ["config"]; try { return biometry::device_registry().at(id.value().string())->create(config); } catch(...) { std::throw_with_nested(biometry::cmds::Test::CouldNotInstiantiateDevice{});} } } biometry::cmds::Test::ConfigurationInvalid::ConfigurationInvalid() : std::runtime_error{"Configuration is invalid"} { } biometry::cmds::Test::CouldNotInstiantiateDevice::CouldNotInstiantiateDevice() : std::runtime_error{"Could not instantiate device"} { } biometry::cmds::Test::Test() : CommandWithFlagsAndAction{cli::Name{"test"}, cli::Usage{"executes runtime tests for a device"}, cli::Description{"executes runtime tests for a device"}} { flag(cli::make_flag(cli::Name{"config"}, cli::Description{"configuration file for the test"}, config)); flag(cli::make_flag(cli::Name{"user"}, cli::Description{"The numeric user id for testing purposes"}, user = biometry::User::current())); flag(cli::make_flag(cli::Name{"trials"}, cli::Description{"Number of identification trials"}, trials = 20)); action([this](const cli::Command::Context& ctxt) { auto device = config ? device_from_config_file(*config) : biometry::dbus::Service::create_stub()->default_device(); ctxt.cout << "We are about to execute a test run for a biometric device." << std::endl << "Please note that we are executing the test in a production" << std::endl << "environment and you should consider the test to be harmful to the" << std::endl << "device configuration:" << std::endl << " User: " << user << std::endl << " Config: " << (Test::config ? (*Test::config).string() : "Default device") << std::endl << "Would you really like to proceed (y/n)?"; static constexpr const char yes{'y'}; char answer; ctxt.cin >> answer; return answer == yes ? test_device(user, ctxt, device) : EXIT_FAILURE; return EXIT_FAILURE; }); } int biometry::cmds::Test::test_device(const User& user, const cli::Command::Context& ctxt, const std::shared_ptr& device) { static std::ofstream dev_null{"/dev/null"}; ctxt.cout << std::endl; { auto observer = std::make_shared>(ctxt.cout, "Clearing template store: ", 17); device->template_store().clear(biometry::Application::system(), user)->start_with_observer(observer); try { observer->sync(); } catch(...) { ctxt.cout << " Failed to clear template store, proceeding though..." << std::endl; }; } { auto observer = std::make_shared>(ctxt.cout, "Enrolling new template: ", 17); device->template_store().enroll(biometry::Application::system(), user)->start_with_observer(observer); try { observer->sync(); } catch(...) { ctxt.cout << " Failed to enroll template, aborting ..." << std::endl; return EXIT_FAILURE; }; } { auto observer = std::make_shared>(ctxt.cout, "Querying template count: ", 17); device->template_store().size(biometry::Application::system(), user)->start_with_observer(observer); try { observer->sync(); } catch(...) { ctxt.cout << " Failed to query template count, aborting ..." << std::endl; return EXIT_FAILURE; }; } biometry::util::cli::ProgressBar pb{ctxt.cout, "Identifying user: ", 17}; auto stats = biometry::util::Benchmark{[device, &ctxt]() { auto observer = std::make_shared>(dev_null, " Trial: "); device->identifier().identify_user(biometry::Application::system(), biometry::Reason{"testing"}) ->start_with_observer(observer); try { observer->sync(); } catch(...) { ctxt.cout << " Failed to identify user." << std::endl; }; }}.trials(25).on_progress([&pb](std::size_t current, std::size_t total) { pb.update(current/static_cast(total)); }).run(); ctxt.cout << std::endl << " min: " << std::setw(6) << std::right << std::fixed << std::setprecision(2) << stats.min() << " [µs]" << std::endl << " mean: " << std::setw(6) << std::right << std::fixed << std::setprecision(2) << stats.mean() << " [µs]" << std::endl << " std.dev.: " << std::setw(6) << std::right << std::fixed << std::setprecision(2) << std::sqrt(stats.variance()) << " [µs]" << std::endl << " max: " << std::setw(6) << std::right << std::fixed << std::setprecision(2) << stats.max() << " [µs]" << std::endl; return EXIT_SUCCESS; } biometryd-0.3.1/src/biometry/cmds/test.h000066400000000000000000000036601455450034500202220ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_CMDS_TEST_H_ #define BIOMETRYD_CMDS_TEST_H_ #include #include #include #include #include #include #include #include #include namespace biometry { namespace cmds { // biometryd test --help // NAME: // test - executes runtime tests for a device // // USAGE: // test [command options] [arguments...] // // OPTIONS: // --config configuration file for the test // --user The numeric user id for testing purposes // --trials Number of identification trials class BIOMETRY_DLL_PUBLIC Test : public util::cli::CommandWithFlagsAndAction { public: struct ConfigurationInvalid : public std::runtime_error { ConfigurationInvalid(); }; struct CouldNotInstiantiateDevice : public std::runtime_error { CouldNotInstiantiateDevice(); }; Test(); int test_device(const User& user, const Command::Context& ctxt, const std::shared_ptr& device); private: Optional config; User user; std::uint32_t trials; }; } } #endif // BIOMETRYD_CMDS_TEST_H_ biometryd-0.3.1/src/biometry/cmds/version.cpp000066400000000000000000000023401455450034500212550ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include namespace cli = biometry::util::cli; biometry::cmds::Version::Version() : CommandWithFlagsAndAction{cli::Name{"version"}, cli::Usage{"version"}, cli::Description{"print the version of the daemon"}} { action([](const cli::Command::Context& ctxt) { std::uint32_t major, minor, patch; biometry::version(major, minor, patch); ctxt.cout << "biometryd " << major << "." << minor << "." << patch << std::endl; return 0; }); } biometryd-0.3.1/src/biometry/cmds/version.h000066400000000000000000000020231455450034500207200ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_CMDS_VERSION_H_ #define BIOMETRYD_CMDS_VERSION_H_ #include #include #include #include namespace biometry { namespace cmds { class Version : public util::cli::CommandWithFlagsAndAction { public: Version(); }; } } #endif // BIOMETRYD_CMDS_VERSION_H_ biometryd-0.3.1/src/biometry/daemon.cpp000066400000000000000000000036671455450034500201220ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include #include #include #include #include #include #include #include #include #include #include namespace cli = biometry::util::cli; namespace po = boost::program_options; biometry::Daemon::Daemon() : device_registrar{biometry::devices::plugin::DirectoryEnumerator{Configuration::default_plugin_directories()}}, cmd{cli::Name{"biometryd"}, cli::Usage{"biometryd"}, cli::Description{"biometryd"}} { cmd.command(std::make_shared()) .command(std::make_shared()) .command(std::make_shared()) .command(std::make_shared()) .command(std::make_shared(std::make_shared())) .command(std::make_shared()) .command(std::make_shared()); } int biometry::Daemon::run(const std::vector& args) { return cmd.run({std::cin, std::cout, args}); } biometryd-0.3.1/src/biometry/daemon.h000066400000000000000000000042401455450034500175530ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_DEVICE_DAEMON_H_ #define BIOMETRYD_DEVICE_DAEMON_H_ #include #include #include #include #include #include #include #include #include namespace biometry { /// @brief Daemon implements biometryd. class BIOMETRY_DLL_PUBLIC Daemon : public DoNotCopyOrMove { public: /// @brief Configuration bundles compile time configuration options of the daemon. struct Configuration { /// @brief default_plugin_directory returns the default path under /usr /// that is scanned for biometryd plugins. static boost::filesystem::path default_plugin_directory(); /// @brief custom_plugin_directory returns the default path under /custom /// that is scanned for biometryd plugins. static boost::filesystem::path custom_plugin_directory(); /// @brief default_plugin_directories returns the paths that should be scanned for /// plugins. static std::set default_plugin_directories(); }; /// @brief Daemon creates a new instance, populating the map of known commands. Daemon(); /// @brief run executes the daemon. int run(const std::vector& args); private: DeviceRegistrar device_registrar; util::cli::CommandWithSubcommands cmd; }; } #endif // BIOMETRYD_DEVICE_DAEMON_H_ biometryd-0.3.1/src/biometry/daemon_configuration.cpp.in000066400000000000000000000023041455450034500234410ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include boost::filesystem::path biometry::Daemon::Configuration::default_plugin_directory() { return "@BIOMETRYD_DEFAULT_PLUGIN_DIRECTORY@"; } boost::filesystem::path biometry::Daemon::Configuration::custom_plugin_directory() { return "@BIOMETRYD_CUSTOM_PLUGIN_DIRECTORY@"; } std::set biometry::Daemon::Configuration::default_plugin_directories() { return {Configuration::default_plugin_directory(), Configuration::custom_plugin_directory()}; } biometryd-0.3.1/src/biometry/daemon_main.cpp000066400000000000000000000016431455450034500211160ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include namespace cli = biometry::util::cli; int main(int argc, char** argv) { biometry::Daemon biometryd; return biometryd.run(cli::args(argc, argv)); } biometryd-0.3.1/src/biometry/dbus/000077500000000000000000000000001455450034500170745ustar00rootroot00000000000000biometryd-0.3.1/src/biometry/dbus/codec.h000066400000000000000000000302051455450034500203220ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_DBUS_CODEC_H_ #define BIOMETRYD_DBUS_CODEC_H_ #include #include #include #include #include #include #include #include #include #include #include #include namespace core { namespace dbus { namespace helper { template<> struct TypeMapper { constexpr static inline ArgumentType type_value() { return ArgumentType::structure; } constexpr static bool is_basic_type() { return false; } constexpr static bool requires_signature() { return true; } static std::string signature() { return DBUS_STRUCT_BEGIN_CHAR_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_STRUCT_END_CHAR_AS_STRING; } }; template<> struct TypeMapper { constexpr static inline ArgumentType type_value() { return ArgumentType::structure; } constexpr static bool is_basic_type() { return false; } constexpr static bool requires_signature() { return true; } static std::string signature() { return DBUS_STRUCT_BEGIN_CHAR_AS_STRING + TypeMapper::signature() + TypeMapper::signature() + DBUS_STRUCT_END_CHAR_AS_STRING; } }; template<> struct TypeMapper { constexpr static inline ArgumentType type_value() { return ArgumentType::structure; } constexpr static bool is_basic_type() { return false; } constexpr static bool requires_signature() { return true; } static std::string signature() { return DBUS_STRUCT_BEGIN_CHAR_AS_STRING + TypeMapper::signature() + TypeMapper::signature() + DBUS_STRUCT_END_CHAR_AS_STRING; } }; template<> struct TypeMapper { constexpr static inline ArgumentType type_value() { return ArgumentType::variant; } constexpr static bool is_basic_type() { return false; } constexpr static bool requires_signature() { return true; } static std::string signature() { return DBUS_STRUCT_BEGIN_CHAR_AS_STRING DBUS_TYPE_UINT16_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_STRUCT_END_CHAR_AS_STRING; } }; template<> struct TypeMapper { constexpr static inline ArgumentType type_value() { return ArgumentType::structure; } constexpr static bool is_basic_type() { return false; } constexpr static bool requires_signature() { return true; } static std::string signature() { return DBUS_STRUCT_BEGIN_CHAR_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_STRUCT_END_CHAR_AS_STRING; } }; } template<> struct Codec { static void encode_argument(Message::Writer& out, const biometry::Application& in) { out.push_stringn(in.as_string().c_str(), in.as_string().size()); } static void decode_argument(Message::Reader& in, biometry::Application& out) { out = biometry::Application{in.pop_string()}; } }; template<> struct Codec { static void encode_argument(Message::Writer& out, const biometry::Progress& in) { auto sw = out.open_structure(); { sw.push_floating_point(*in.percent); Codec::encode_argument(sw, in.details); } out.close_structure(std::move(sw)); } static void decode_argument(Message::Reader& in, biometry::Progress& out) { auto sr = in.pop_structure(); { out.percent = biometry::Percent::from_raw_value(sr.pop_floating_point()); Codec::decode_argument(sr, out.details); } } }; template<> struct Codec { static void encode_argument(Message::Writer& out, const biometry::Point& in) { auto sw = out.open_structure(); { sw.push_floating_point(in.x); sw.push_floating_point(in.y); } out.close_structure(std::move(sw)); } static void decode_argument(Message::Reader& in, biometry::Point& out) { auto sr = in.pop_structure(); out.x = sr.pop_floating_point(); out.y = sr.pop_floating_point(); } }; template<> struct Codec { static void encode_argument(Message::Writer& out, const biometry::Rectangle& in) { auto sw = out.open_structure(); { Codec::encode_argument(sw, in.top_left); Codec::encode_argument(sw, in.bottom_right); } out.close_structure(std::move(sw)); } static void decode_argument(Message::Reader& in, biometry::Rectangle& out) { auto sr = in.pop_structure(); Codec::decode_argument(sr, out.top_left); Codec::decode_argument(sr, out.bottom_right); } }; template<> struct Codec { static void encode_argument(Message::Writer& out, const biometry::Reason& in) { out.push_stringn(in.as_string().c_str(), in.as_string().size()); } static void decode_argument(Message::Reader& in, biometry::Reason& out) { out = biometry::Reason{in.pop_string()}; } }; template<> struct Codec { static void encode_argument(Message::Writer& out, const biometry::User& in) { out.push_int32(in.id); } static void decode_argument(Message::Reader& in, biometry::User& out) { out.id = in.pop_int32(); } }; template<> struct Codec { static void encode_argument(Message::Writer& out, const biometry::Variant::None&) { auto sw = out.open_structure(); { static const std::string s{"42"}; sw.push_stringn(s.c_str(), s.size()); } out.close_structure(std::move(sw)); } static void decode_argument(Message::Reader& in, biometry::Variant::None&) { in.pop_structure(); } }; template<> struct Codec { static void encode_argument(Message::Writer& out, const biometry::Variant& in) { auto sw = out.open_structure(); { sw.push_uint16(static_cast(in.type())); switch (in.type()) { case biometry::Variant::Type::none: static const biometry::Variant::None none; Codec::encode_argument(sw, none); break; case biometry::Variant::Type::boolean: { auto vw = sw.open_variant(types::Signature{helper::TypeMapper::signature()}); { vw.push_boolean(in.boolean()); } sw.close_variant(std::move(vw)); break; } case biometry::Variant::Type::integer: { auto vw = sw.open_variant(types::Signature{helper::TypeMapper::signature()}); { vw.push_int64(in.integer()); } sw.close_variant(std::move(vw)); break; } case biometry::Variant::Type::floating_point: { auto vw = sw.open_variant(types::Signature{helper::TypeMapper::signature()}); { vw.push_floating_point(in.floating_point()); } sw.close_variant(std::move(vw)); break; } case biometry::Variant::Type::rectangle: { auto vw = sw.open_variant(types::Signature{helper::TypeMapper::signature()}); { Codec::encode_argument(vw, in.rectangle()); } sw.close_variant(std::move(vw)); break; } case biometry::Variant::Type::string: { auto vw = sw.open_variant(types::Signature{helper::TypeMapper::signature()}); { vw.push_stringn(in.string().c_str(), in.string().size()); } sw.close_variant(std::move(vw)); break; } case biometry::Variant::Type::blob: { break; } case biometry::Variant::Type::vector: { auto vw = sw.open_variant(core::dbus::types::Signature{"a(qv)"}); { auto aw = vw.open_array(core::dbus::types::Signature{"(qv)"}); { for (const auto& v : in.vector()) Codec::encode_argument(aw, v); } vw.close_array(std::move(aw)); } sw.close_variant(std::move(vw)); break; } } } out.close_structure(std::move(sw)); } static void decode_argument(Message::Reader& in, biometry::Variant& out) { auto sr = in.pop_structure(); { auto type = static_cast(sr.pop_uint16()); auto vr = sr.pop_variant(); switch (type) { case biometry::Variant::Type::none: static biometry::Variant::None none; Codec::decode_argument(vr, none); break; case biometry::Variant::Type::boolean: { out.boolean(vr.pop_boolean()); break; } case biometry::Variant::Type::integer: { out.integer(vr.pop_int64()); break; } case biometry::Variant::Type::floating_point: { out.floating_point(vr.pop_floating_point()); break; } case biometry::Variant::Type::rectangle: { biometry::Rectangle r; Codec::decode_argument(vr, r); out.rectangle(r); break; } case biometry::Variant::Type::string: { out.string(vr.pop_string()); break; } case biometry::Variant::Type::blob: { break; } case biometry::Variant::Type::vector: { std::vector v; Codec>::decode_argument(vr, v); out.vector(v); break; } } } } }; template<> struct Codec { static void encode_argument(Message::Writer& out, const biometry::Void&) { auto sw = out.open_structure(); { static const std::string s{"42"}; sw.push_stringn(s.c_str(), s.size()); } out.close_structure(std::move(sw)); } static void decode_argument(Message::Reader& in, biometry::Void&) { in.pop_structure(); } }; } } #endif // BIOMETRYD_DBUS_CODEC_H_ biometryd-0.3.1/src/biometry/dbus/interface.h000066400000000000000000000221201455450034500212020ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_DBUS_INTERFACE_H_ #define BIOMETRYD_DBUS_INTERFACE_H_ #include #include #include #include namespace biometry { namespace dbus { namespace interface { constexpr int timeout_seconds = 1; struct Errors { struct NotPermitted { static inline std::string name() { return "com.ubports.biometryd.Error.NotPermitted"; } }; }; struct Service { static inline std::string name() { return "com.ubports.biometryd.Service"; } static inline core::dbus::types::ObjectPath path() { return core::dbus::types::ObjectPath{"/"}; } struct Methods { Methods() = delete; struct DefaultDevice { static inline std::string name() { return "DefaultDevice"; } typedef biometry::dbus::interface::Service Interface; typedef core::dbus::types::ObjectPath ResultType; inline static const std::chrono::milliseconds default_timeout() { return std::chrono::seconds{timeout_seconds}; } }; }; }; struct Device { static inline const std::string& name() { static const std::string s{"com.ubports.biometryd.Device"}; return s; } struct Methods { Methods() = delete; struct TemplateStore { static inline const std::string& name() { static const std::string s{"TemplateStore"}; return s; } typedef biometry::dbus::interface::Device Interface; typedef core::dbus::types::ObjectPath ResultType; inline static const std::chrono::milliseconds default_timeout() { return std::chrono::seconds{timeout_seconds}; } }; struct Identifier { static inline const std::string& name() { static const std::string s{"Identifier"}; return s; } typedef biometry::dbus::interface::Device Interface; typedef core::dbus::types::ObjectPath ResultType; inline static const std::chrono::milliseconds default_timeout() { return std::chrono::seconds{timeout_seconds}; } }; struct Verifier { static inline const std::string& name() { static const std::string s{"Verifier"}; return s; } typedef biometry::dbus::interface::Device Interface; typedef core::dbus::types::ObjectPath ResultType; inline static const std::chrono::milliseconds default_timeout() { return std::chrono::seconds{timeout_seconds}; } }; }; }; struct Identifier { static inline const std::string& name() { static const std::string s{"com.ubports.biometryd.Identifier"}; return s; } struct Methods { Methods() = delete; struct IdentifyUser { static inline const std::string& name() { static const std::string s{"IdentifyUser"}; return s; } typedef biometry::dbus::interface::Identifier Interface; typedef core::dbus::types::ObjectPath ResultType; inline static const std::chrono::milliseconds default_timeout() { return std::chrono::seconds{timeout_seconds}; } }; }; }; struct TemplateStore { static inline const std::string& name() { static const std::string s{"com.ubports.biometryd.TemplateStore"}; return s; } struct Methods { Methods() = delete; struct Size { static inline const std::string& name() { static const std::string s{"Size"}; return s; } typedef biometry::dbus::interface::TemplateStore Interface; typedef core::dbus::types::ObjectPath ResultType; inline static const std::chrono::milliseconds default_timeout() { return std::chrono::seconds{timeout_seconds}; } }; struct List { static inline const std::string& name() { static const std::string s{"List"}; return s; } typedef biometry::dbus::interface::TemplateStore Interface; typedef core::dbus::types::ObjectPath ResultType; inline static const std::chrono::milliseconds default_timeout() { return std::chrono::seconds{timeout_seconds}; } }; struct Enroll { static inline const std::string& name() { static const std::string s{"Enroll"}; return s; } typedef biometry::dbus::interface::TemplateStore Interface; typedef core::dbus::types::ObjectPath ResultType; inline static const std::chrono::milliseconds default_timeout() { return std::chrono::seconds{timeout_seconds}; } }; struct Remove { static inline const std::string& name() { static const std::string s{"Remove"}; return s; } typedef biometry::dbus::interface::TemplateStore Interface; typedef core::dbus::types::ObjectPath ResultType; inline static const std::chrono::milliseconds default_timeout() { return std::chrono::seconds{timeout_seconds}; } }; struct Clear { static inline const std::string& name() { static const std::string s{"Clear"}; return s; } typedef biometry::dbus::interface::TemplateStore Interface; typedef core::dbus::types::ObjectPath ResultType; inline static const std::chrono::milliseconds default_timeout() { return std::chrono::seconds{timeout_seconds}; } }; }; }; struct Operation { struct Observer { static inline const std::string& name() { static const std::string s{"com.ubports.biometryd.Operation.Observer"}; return s; } struct Methods { Methods() = delete; DBUS_CPP_METHOD_DEF(OnStarted, Observer) DBUS_CPP_METHOD_DEF(OnProgress, Observer) DBUS_CPP_METHOD_DEF(OnCancelled, Observer) DBUS_CPP_METHOD_DEF(OnFailed, Observer) DBUS_CPP_METHOD_DEF(OnSucceeded, Observer) }; }; static inline const std::string& name() { static const std::string s{"com.ubports.biometryd.Operation"}; return s; } struct Methods { Methods() = delete; struct StartWithObserver { static inline const std::string& name() { static const std::string s{"StartWithOberserver"}; return s; } typedef biometry::dbus::interface::Operation Interface; typedef void ResultType; inline static const std::chrono::milliseconds default_timeout() { return std::chrono::seconds{timeout_seconds}; } }; struct Cancel { static inline const std::string& name() { static const std::string s{"Cancel"}; return s; } typedef biometry::dbus::interface::Operation Interface; typedef void ResultType; inline static const std::chrono::milliseconds default_timeout() { return std::chrono::seconds{timeout_seconds}; } }; }; }; } } } namespace core { namespace dbus { namespace traits { template<> struct Service { static inline const std::string& interface_name() { static const std::string s{"com.ubports.biometryd.Service"}; return s; } }; } } } #endif // BIOMETRYD_DBUS_INTERFACE_H_ biometryd-0.3.1/src/biometry/dbus/service.cpp000066400000000000000000000030061455450034500212370ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include #include #include #include std::shared_ptr biometry::dbus::Service::create_stub() { static struct SingletonBus { SingletonBus() : bus(std::make_shared(core::dbus::WellKnownBus::system)) { bus->install_executor(core::dbus::asio::make_executor(bus)); worker = std::thread([this]() { bus->run(); }); } ~SingletonBus() { bus->stop(); worker.join(); } std::shared_ptr bus; std::thread worker; } singleton_bus; return biometry::dbus::stub::Service::create_for_bus(singleton_bus.bus); } biometryd-0.3.1/src/biometry/dbus/skeleton/000077500000000000000000000000001455450034500207205ustar00rootroot00000000000000biometryd-0.3.1/src/biometry/dbus/skeleton/credentials_resolver.h000066400000000000000000000030531455450034500253100ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_DBUS_SKELETON_CREDENTIALS_RESOLVER_H_ #define BIOMETRYD_DBUS_SKELETON_CREDENTIALS_RESOLVER_H_ #include #include #include #include #include namespace biometry { namespace dbus { namespace skeleton { /// @brief CredentialsResolver resolves incoming messages to RequestVerifier::Credentials. class CredentialsResolver : public DoNotCopyOrMove { public: /// @brief resolve_credentials resolves the credentials of the application that sent msg. virtual void resolve_credentials(const core::dbus::Message::Ptr& msg, const std::function&)>& then) = 0; }; } } } #endif // BIOMETRYD_DBUS_SKELETON_CREDENTIALS_RESOLVER_H_ biometryd-0.3.1/src/biometry/dbus/skeleton/daemon_credentials_resolver.cpp000066400000000000000000000113051455450034500271650ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include #include #include #include namespace { bool is_running_in_a_testing_environment() { return core::posix::this_process::env::get("BIOMETRYD_DBUS_SKELETON_IS_RUNNING_UNDER_TESTING", "0") == "1"; } struct DBus { static const std::string& name() { static const std::string s = "org.freedesktop.DBus"; return s; } // Gets the AppArmor confinement string associated with the unique connection name. If // D-Bus is not performing AppArmor mediation, the // org.freedesktop.DBus.Error.AppArmorSecurityContextUnknown error is returned. DBUS_CPP_METHOD_DEF(GetConnectionAppArmorSecurityContext, DBus) // Gets the unix user id of the user that the requesting party is running under. DBUS_CPP_METHOD_DEF(GetConnectionUnixUser, DBus) struct Stub { // Creates a new stub instance for the given object to access // DBus functionality. Stub(const core::dbus::Object::Ptr& object) : object{object} { } // Creates a new stub instance for the given bus connection Stub(const core::dbus::Bus::Ptr& bus) : object { core::dbus::Service::use_service(bus) ->object_for_path(core::dbus::types::ObjectPath{"/org/freedesktop/DBus"}) } { } // Gets the AppArmor confinement string associated with the unique connection name. If // D-Bus is not performing AppArmor mediation, the // org.freedesktop.DBus.Error.AppArmorSecurityContextUnknown error is returned. // // Invokes the given handler on completion. void get_connection_app_armor_security_async(const std::string& name, std::function&)> handler) { object->invoke_method_asynchronously_with_callback([handler](const core::dbus::Result& result) { biometry::Optional label; handler((not result.is_error() ? label = result.value() : is_running_in_a_testing_environment() ? label = "unconfined" : label)); }, name); } // Gets the unix user id of the sender identified by name, invoking handler on completion. void get_connection_unix_user_async(const std::string& name, const std::function)>& handler) { object->invoke_method_asynchronously_with_callback([handler](const core::dbus::Result& result) { biometry::Optional uid; handler((not result.is_error() ? uid = result.value() : uid)); }, name); } core::dbus::Object::Ptr object; }; }; } biometry::dbus::skeleton::DaemonCredentialsResolver::DaemonCredentialsResolver(const core::dbus::Bus::Ptr& bus) : bus{bus} { } void biometry::dbus::skeleton::DaemonCredentialsResolver::resolve_credentials( const core::dbus::Message::Ptr& msg, const std::function&)>& then) { DBus::Stub stub{bus}; stub.get_connection_unix_user_async(msg->sender(), [stub, msg, then](biometry::Optional uid) mutable { stub.get_connection_app_armor_security_async(msg->sender(), [stub, msg, then, uid](const biometry::Optional& label) mutable { Optional credentials; RequestVerifier::Credentials cred = {biometry::Application{label.get()}, biometry::User{uid.get()}}; then((label && uid ? credentials = cred : credentials)); }); }); } biometryd-0.3.1/src/biometry/dbus/skeleton/daemon_credentials_resolver.h000066400000000000000000000032711455450034500266350ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_DBUS_SKELETON_DAEMON_CREDENTIALS_RESOLVER_H_ #define BIOMETRYD_DBUS_SKELETON_DAEMON_CREDENTIALS_RESOLVER_H_ #include #include #include namespace biometry { namespace dbus { namespace skeleton { /// @brief CredentialsResolver resolves incoming messages to RequestVerifier::Credentials. class DaemonCredentialsResolver : public CredentialsResolver { public: /// @brief Initializes a new instance with the given bus connection. explicit DaemonCredentialsResolver(const core::dbus::Bus::Ptr& bus); /// @brief resolve_credentials resolves the credentials of the application that sent msg. void resolve_credentials(const core::dbus::Message::Ptr& msg, const std::function&)>& then) override; private: core::dbus::Bus::Ptr bus; }; } } } #endif // BIOMETRYD_DBUS_SKELETON_DAEMON_CREDENTIALS_RESOLVER_H_ biometryd-0.3.1/src/biometry/dbus/skeleton/device.cpp000066400000000000000000000102021455450034500226560ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include #include #include namespace { struct PrefixedPath { PrefixedPath(const std::string& suffix) : suffix{suffix} { } core::dbus::types::ObjectPath prefix(const core::dbus::types::ObjectPath& path) const { return core::dbus::types::ObjectPath { (boost::format{"%1%/%2%"} % path.as_string() % suffix).str() }; } std::string suffix; }; } /// @brief create_for_bus returns a new skeleton::Device instance connected to bus, forwarding calls to impl. biometry::dbus::skeleton::Device::Ptr biometry::dbus::skeleton::Device::create_for_service_and_object( const core::dbus::Bus::Ptr& bus, const core::dbus::Service::Ptr& service, const core::dbus::Object::Ptr& object, const std::shared_ptr& impl) { return Ptr{new Device{bus, service, object, impl}}; } /// @brief Frees up resources and removes routes to message handlers. biometry::dbus::skeleton::Device::~Device() { object_->uninstall_method_handler(); object_->uninstall_method_handler(); } // From biometry::Device biometry::TemplateStore& biometry::dbus::skeleton::Device::template_store() { return impl_->template_store(); } biometry::Identifier& biometry::dbus::skeleton::Device::identifier() { return impl_->identifier(); } biometry::Verifier& biometry::dbus::skeleton::Device::verifier() { return impl_->verifier(); } biometry::dbus::skeleton::Device::Device( const core::dbus::Bus::Ptr& bus, const core::dbus::Service::Ptr& service, const core::dbus::Object::Ptr& object, const std::shared_ptr& impl) : impl_{impl}, bus_{bus}, service_{service}, object_{object} { object_->install_method_handler([this](const core::dbus::Message::Ptr& msg) { auto path = PrefixedPath{"template_store"}.prefix(object_->path()); template_store_([this, &path]() { return TemplateStore::create_for_service_and_object(bus_, service_, service_->add_object_for_path(path), std::ref(template_store()), std::make_shared(), std::make_shared(bus_)); }); auto reply = core::dbus::Message::make_method_return(msg); reply->writer() << path; this->bus_->send(reply); }); object_->install_method_handler([this](const core::dbus::Message::Ptr& msg) { auto path = PrefixedPath{"identifier"}.prefix(object_->path()); identifier_([this, &path]() { return Identifier::create_for_service_and_object(bus_, service_, service_->add_object_for_path(path), std::ref(identifier()), std::make_shared(), std::make_shared(bus_)); }); auto reply = core::dbus::Message::make_method_return(msg); reply->writer() << path; this->bus_->send(reply); }); } biometryd-0.3.1/src/biometry/dbus/skeleton/device.h000066400000000000000000000047241455450034500223370ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_DBUS_SKELETON_DEVICE_H_ #define BIOMETRYD_DBUS_SKELETON_DEVICE_H_ #include #include #include #include #include #include #include namespace biometry { namespace dbus { namespace skeleton { class BIOMETRY_DLL_PUBLIC Device : public biometry::Device { public: typedef std::shared_ptr Ptr; /// @brief create_for_bus returns a new skeleton::Device instance connected to bus, forwarding calls to impl. static Ptr create_for_service_and_object( const core::dbus::Bus::Ptr& bus, const core::dbus::Service::Ptr& service, const core::dbus::Object::Ptr& object, const std::shared_ptr& impl); /// @brief Frees up resources and removes routes to message handlers. ~Device(); // From biometry::Device biometry::TemplateStore& template_store() override; biometry::Identifier& identifier() override; biometry::Verifier& verifier() override; private: /// @brief Device creates a new instance for the given remote service and object; Device(const core::dbus::Bus::Ptr& bus, const core::dbus::Service::Ptr& service, const core::dbus::Object::Ptr& object, const std::shared_ptr& impl); std::shared_ptr impl_; core::dbus::Bus::Ptr bus_; core::dbus::Service::Ptr service_; core::dbus::Object::Ptr object_; util::Once> template_store_; util::Once> identifier_; }; } } } #endif // BIOMETRYD_DBUS_SKELETON_DEVICE_H_ biometryd-0.3.1/src/biometry/dbus/skeleton/identifier.cpp000066400000000000000000000115221455450034500235470ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include #include #include #include #include namespace { core::dbus::Message::Ptr not_permitted_in_reply_to(const core::dbus::Message::Ptr& msg) { return core::dbus::Message::make_error(msg, biometry::dbus::interface::Errors::NotPermitted::name(), ""); } } bool biometry::dbus::skeleton::Identifier::RequestVerifier::verify_identify_user_request(const biometry::Application& app, const Credentials& provided) { // Requests for the same app are fine. if (app == provided.app) return true; // If app ids do not match: we unconditionally trust unconfined apps, and no one else. return provided.app.as_string() == "unconfined"; } /// @brief create_for_bus returns a new skeleton::Identifier instance connected to bus, forwarding calls to impl. biometry::dbus::skeleton::Identifier::Ptr biometry::dbus::skeleton::Identifier::create_for_service_and_object( const core::dbus::Bus::Ptr& bus, const core::dbus::Service::Ptr& service, const core::dbus::Object::Ptr& object, const std::reference_wrapper& impl, const std::shared_ptr& request_verifier, const std::shared_ptr& credentials_resolver) { return Ptr{new Identifier{bus, service, object, impl, request_verifier, credentials_resolver}}; } // From biometry::Identifier. biometry::Operation::Ptr biometry::dbus::skeleton::Identifier::identify_user(const Application& app, const Reason& reason) { return impl.get().identify_user(app, reason); } biometry::dbus::skeleton::Identifier::Identifier( const core::dbus::Bus::Ptr& bus, const core::dbus::Service::Ptr& service, const core::dbus::Object::Ptr& object, const std::reference_wrapper& impl, const std::shared_ptr& request_verifier, const std::shared_ptr& credentials_resolver) : impl{impl}, request_verifier{request_verifier}, credentials_resolver{credentials_resolver}, bus{bus}, service{service}, object{object} { object->install_method_handler([this](const core::dbus::Message::Ptr& msg) { Identifier::credentials_resolver->resolve_credentials(msg, [this, msg](const Optional& credentials) { if (not credentials) { this->bus->send(not_permitted_in_reply_to(msg)); return; } biometry::Application app = biometry::Application::system(); biometry::Reason reason = biometry::Reason::unknown(); auto reader = msg->reader(); reader >> app >> reason; if (not this->request_verifier->verify_identify_user_request(app, credentials.get())) { this->bus->send(not_permitted_in_reply_to(msg)); return; } auto op = identify_user(app, reason); core::dbus::types::ObjectPath op_path { (boost::format{"%1%/operation/identification/%2%/%3%/%4%"} % this->object->path().as_string() % credentials.get().app.as_string() % credentials.get().user.id % util::counter().increment()).str() }; ops.synchronized([this, op_path, op](IdentificationOps::ValueType& ops) { ops[op_path] = skeleton::Operation::create_for_object(this->bus, this->service->add_object_for_path(op_path), op); }); auto reply = core::dbus::Message::make_method_return(msg); reply->writer() << op_path; this->bus->send(reply); }); }); } biometry::dbus::skeleton::Identifier::~Identifier() { object->uninstall_method_handler(); } biometryd-0.3.1/src/biometry/dbus/skeleton/identifier.h000066400000000000000000000071501455450034500232160ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_DBUS_SKELETON_IDENTIFIER_H_ #define BIOMETRYD_DBUS_SKELETON_IDENTIFIER_H_ #include #include #include #include #include #include namespace biometry { namespace dbus { namespace skeleton { /// @cond class Device; /// @endcond // Identifier is the dbus SKELETON implementation of biometry::Service. class Identifier : public biometry::Identifier { public: // Safe us some typing. typedef std::shared_ptr Ptr; /// @brief RequestVerifier models verification of incoming requests. class RequestVerifier : public biometry::dbus::skeleton::RequestVerifier { public: /// @brief verify_identify_user_request returns true if the requesting app identified by provided /// is allowed to run the request described by requested. virtual bool verify_identify_user_request(const Application& requested, const Credentials& provided); }; /// @brief create_for_bus returns a new skeleton::Identifier instance connected to bus, forwarding calls to impl. static Ptr create_for_service_and_object(const core::dbus::Bus::Ptr& bus, const core::dbus::Service::Ptr& service, const core::dbus::Object::Ptr& object, const std::reference_wrapper& impl, const std::shared_ptr& request_verifier, const std::shared_ptr& credentials_resolver); /// @brief Frees up resources and uninstalls method handlers. ~Identifier(); // From biometry::Identifier. Operation::Ptr identify_user(const Application& app, const Reason& reason) override; private: typedef biometry::util::Synchronized::Ptr>> IdentificationOps; /// @brief Service creates a new instance for the given remote service and object. Identifier(const core::dbus::Bus::Ptr& bus, const core::dbus::Service::Ptr& service, const core::dbus::Object::Ptr& object, const std::reference_wrapper& impl, const std::shared_ptr& request_verifier, const std::shared_ptr& credentials_resolver); std::reference_wrapper impl; std::shared_ptr request_verifier; std::shared_ptr credentials_resolver; core::dbus::Bus::Ptr bus; core::dbus::Service::Ptr service; core::dbus::Object::Ptr object; IdentificationOps ops; }; } } } #endif // BIOMETRYD_DBUS_SKELETON_IDENTIFIER_H_ biometryd-0.3.1/src/biometry/dbus/skeleton/observer.h000066400000000000000000000143131455450034500227220ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_DBUS_SKELETON_OBSERVER_H_ #define BIOMETRYD_DBUS_SKELETON_OBSERVER_H_ #include #include #include #include namespace biometry { namespace dbus { namespace skeleton { template class Observer : public Operation::Observer, public std::enable_shared_from_this> { public: // Safe us some typing typedef std::shared_ptr> Ptr; typedef typename biometry::Operation::Observer Super; using typename Super::Progress; using typename Super::Reason; using typename Super::Error; using typename Super::Result; /// @brief create_for_object returns a new instance on the given object. static Ptr create_for_object(const core::dbus::Bus::Ptr& bus, const core::dbus::Object::Ptr& object, const typename Operation::Observer::Ptr& impl); // From Operation::Observer void on_started() override; void on_progress(const Progress&) override; void on_canceled(const Reason&) override; void on_failed(const Error&) override; void on_succeeded(const Result&) override; private: /// @brief Observer initializes a new instance for the given remote object. Observer(const core::dbus::Bus::Ptr& bus, const core::dbus::Object::Ptr& object, const typename Operation::Observer::Ptr& impl); /// @brief Finalize construction sets up message handlers. Ptr finalize_construction(); /// @brief uninstall_method_handlers removes installed method handlers, preparing destruction of the object. void uninstall_method_handlers(); typename Operation::Observer::Ptr impl; core::dbus::Bus::Ptr bus; core::dbus::Object::Ptr object; }; } } } template typename biometry::dbus::skeleton::Observer::Ptr biometry::dbus::skeleton::Observer::create_for_object( const core::dbus::Bus::Ptr& bus, const core::dbus::Object::Ptr& object, const typename Operation::Observer::Ptr& impl) { return Ptr{new Observer{bus, object, impl}}->finalize_construction(); } template void biometry::dbus::skeleton::Observer::on_started() { impl->on_started(); } template void biometry::dbus::skeleton::Observer::on_progress(const typename Observer::Progress& progress) { impl->on_progress(progress); } template void biometry::dbus::skeleton::Observer::on_canceled(const Reason& reason) { impl->on_canceled(reason); } template void biometry::dbus::skeleton::Observer::on_failed(const Error& error) { impl->on_failed(error); } template void biometry::dbus::skeleton::Observer::on_succeeded(const Result& result) { impl->on_succeeded(result); } template biometry::dbus::skeleton::Observer::Observer( const core::dbus::Bus::Ptr& bus, const core::dbus::Object::Ptr& object, const typename Operation::Observer::Ptr& impl) : impl{impl}, bus{bus}, object{object} { } template void biometry::dbus::skeleton::Observer::uninstall_method_handlers() { object->uninstall_method_handler(); object->uninstall_method_handler(); object->uninstall_method_handler(); object->uninstall_method_handler(); object->uninstall_method_handler(); } template typename biometry::dbus::skeleton::Observer::Ptr biometry::dbus::skeleton::Observer::finalize_construction() { auto thiz = std::enable_shared_from_this>::shared_from_this(); object->install_method_handler([thiz, this](const core::dbus::Message::Ptr& msg) { on_started(); bus->send(core::dbus::Message::make_method_return(msg)); }); object->install_method_handler([thiz, this](const core::dbus::Message::Ptr& msg) { auto progress = Progress::none(); msg->reader() >> progress; on_progress(progress); bus->send(core::dbus::Message::make_method_return(msg)); }); object->install_method_handler([thiz, this](const core::dbus::Message::Ptr& msg) { Reason reason; msg->reader() >> reason; on_canceled(reason); bus->send(core::dbus::Message::make_method_return(msg)); uninstall_method_handlers(); }); object->install_method_handler([thiz, this](const core::dbus::Message::Ptr& msg) { Error error; msg->reader() >> error; on_failed(error); bus->send(core::dbus::Message::make_method_return(msg)); uninstall_method_handlers(); }); object->install_method_handler([thiz, this](const core::dbus::Message::Ptr& msg) { Result result; msg->reader() >> result; on_succeeded(result); bus->send(core::dbus::Message::make_method_return(msg)); uninstall_method_handlers(); }); return thiz; } #endif // BIOMETRYD_DBUS_SKELETON_OBSERVER_H_ biometryd-0.3.1/src/biometry/dbus/skeleton/operation.h000066400000000000000000000103071455450034500230720ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_DBUS_SKELETON_OPERATION_H_ #define BIOMETRYD_DBUS_SKELETON_OPERATION_H_ #include #include #include #include #include #include namespace biometry { namespace dbus { namespace skeleton { // Identifier is the dbus SKELETON implementation of biometry::Service. template class Operation : public biometry::Operation { public: // Safe us some typing typedef std::shared_ptr> Ptr; typedef biometry::Operation Super; using typename Super::Observer; using typename Super::Progress; using typename Super::Reason; using typename Super::Error; using typename Super::Result; /// @brief create_for_object returns a new instance on the given object. static Ptr create_for_object(const core::dbus::Bus::Ptr& bus, const core::dbus::Object::Ptr& object, const typename biometry::Operation::Ptr& impl); /// @brief Frees up resources and uninstall message handlers. ~Operation(); // From biometry::Operation void start_with_observer(const typename Observer::Ptr& observer) override; void cancel() override; private: /// @brief Service creates a new instance for the given remote service and object. Operation(const core::dbus::Bus::Ptr& bus, const core::dbus::Object::Ptr& object, const typename biometry::Operation::Ptr& impl); typename biometry::Operation::Ptr impl; core::dbus::Bus::Ptr bus; core::dbus::Object::Ptr object; }; } } } template typename biometry::dbus::skeleton::Operation::Ptr biometry::dbus::skeleton::Operation::create_for_object( const core::dbus::Bus::Ptr& bus, const core::dbus::Object::Ptr& object, const typename biometry::Operation::Ptr& impl) { return Ptr{new Operation{bus, object, impl}}; } template biometry::dbus::skeleton::Operation::~Operation() { object->uninstall_method_handler(); object->uninstall_method_handler(); } template void biometry::dbus::skeleton::Operation::start_with_observer(const typename Observer::Ptr& observer) { impl->start_with_observer(observer); } template void biometry::dbus::skeleton::Operation::cancel() { impl->cancel(); } template biometry::dbus::skeleton::Operation::Operation( const core::dbus::Bus::Ptr& bus, const core::dbus::Object::Ptr& object, const typename biometry::Operation::Ptr& impl) : impl{impl}, bus{bus}, object{object} { object->install_method_handler([this](const core::dbus::Message::Ptr& msg) { core::dbus::types::ObjectPath path; msg->reader() >> path; auto object = core::dbus::Service::use_service(this->bus, msg->sender())->object_for_path(path); start_with_observer(biometry::dbus::stub::Observer::create_for_object(object)); this->bus->send(core::dbus::Message::make_method_return(msg)); }); object->install_method_handler([this](const core::dbus::Message::Ptr& msg) { cancel(); this->bus->send(core::dbus::Message::make_method_return(msg)); }); } #endif // BIOMETRYD_DBUS_SKELETON_OPERATION_H_ biometryd-0.3.1/src/biometry/dbus/skeleton/request_verifier.cpp000066400000000000000000000023021455450034500250040ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include bool biometry::dbus::skeleton::operator==(const RequestVerifier::Credentials& lhs, const RequestVerifier::Credentials& rhs) { return std::tie(lhs.app, lhs.user) == std::tie(rhs.app, rhs.user); } std::ostream& biometry::dbus::skeleton::operator<<(std::ostream& out, const RequestVerifier::Credentials& credentials) { return out << "[" << credentials.app.as_string() << ", " << credentials.user << "]"; } biometryd-0.3.1/src/biometry/dbus/skeleton/request_verifier.h000066400000000000000000000036331455450034500244610ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_DBUS_SKELETON_REQUEST_VERIFIER_H_ #define BIOMETRYD_DBUS_SKELETON_REQUEST_VERIFIER_H_ #include #include #include #include namespace biometry { namespace dbus { namespace skeleton { /// @brief RequestVerifier is the top-level point of entry to the world /// of verifying incoming requests for different components. /// /// Most importantly, it defines the struct Credentials bundling together /// information about an incoming request. class RequestVerifier : public DoNotCopyOrMove { public: /// @brief Credentials bundles an app and a user. struct Credentials { Application app; ///< The id of the requesting component. User user; ///< The user under which the requesting component is running. }; }; /// @brief operator== returns true iff lhs and rhs compare equal per component. bool operator==(const RequestVerifier::Credentials& lhs, const RequestVerifier::Credentials& rhs); /// @brief operator<< inserts credentials into out. std::ostream& operator<<(std::ostream& out, const RequestVerifier::Credentials& credentials); } } } #endif // BIOMETRYD_DBUS_SKELETON_REQUEST_VERIFIER_H_ biometryd-0.3.1/src/biometry/dbus/skeleton/service.cpp000066400000000000000000000051041455450034500230640ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include namespace { const core::dbus::types::ObjectPath default_device_path("/default_device"); } biometry::dbus::skeleton::Service::Ptr biometry::dbus::skeleton::Service::create_for_bus(const core::dbus::Bus::Ptr& bus, const std::shared_ptr& impl) { auto service = core::dbus::Service::add_service(bus, biometry::dbus::interface::Service::name()); auto object = service->add_object_for_path(biometry::dbus::interface::Service::path()); return Ptr{new Service{bus, service, object, impl}}; } biometry::dbus::skeleton::Service::Service(const core::dbus::Bus::Ptr& bus, const core::dbus::Service::Ptr& service, const core::dbus::Object::Ptr& object, const std::shared_ptr& impl) : impl_{impl}, bus_{bus}, service_{service}, object_{object} { object_->install_method_handler([this](const core::dbus::Message::Ptr& msg) { // Ensure that the device gets created on demand. default_device(); auto reply = core::dbus::Message::make_method_return(msg); reply->writer() << core::dbus::types::ObjectPath(default_device_path); this->bus_->send(reply); }); } biometry::dbus::skeleton::Service::~Service() { object_->uninstall_method_handler(); } std::shared_ptr biometry::dbus::skeleton::Service::default_device() const { return default_device_([this]() { auto object = service_->add_object_for_path(default_device_path); return Device::create_for_service_and_object(bus_, service_, object, impl_->default_device()); }); } biometryd-0.3.1/src/biometry/dbus/skeleton/service.h000066400000000000000000000043311455450034500225320ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_DBUS_SKELETON_SERVICE_H_ #define BIOMETRYD_DBUS_SKELETON_SERVICE_H_ #include #include #include #include #include #include namespace biometry { namespace dbus { namespace skeleton { /// @cond class Device; /// @endcond // Service is the dbus SKELETON implementation of biometry::Service. class BIOMETRY_DLL_PUBLIC Service : public biometry::Service { public: // Safe us some typing. typedef std::shared_ptr Ptr; /// @brief create_for_bus creates a new instance connecting to bus, forwarding incoming calls to impl. static Ptr create_for_bus(const core::dbus::Bus::Ptr& bus_, const std::shared_ptr& impl_); /// @brief Frees up resources and removes routes to message handlers. ~Service(); // From biometry::Service. std::shared_ptr default_device() const override; private: /// @brief Service creates a new instance for the given remote service and object. Service(const core::dbus::Bus::Ptr& bus, const core::dbus::Service::Ptr& service, const core::dbus::Object::Ptr& object, const std::shared_ptr& impl); std::shared_ptr impl_; core::dbus::Bus::Ptr bus_; core::dbus::Service::Ptr service_; core::dbus::Object::Ptr object_; util::Once> default_device_; }; } } } #endif // BIOMETRYD_DBUS_SKELETON_SERVICE_H_ biometryd-0.3.1/src/biometry/dbus/skeleton/template_store.cpp000066400000000000000000000330531455450034500244570ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include #include #include #include #include #include #include namespace { core::dbus::Message::Ptr not_permitted_in_reply_to(const core::dbus::Message::Ptr& msg) { return core::dbus::Message::make_error(msg, biometry::dbus::interface::Errors::NotPermitted::name(), ""); } bool verify(const biometry::dbus::skeleton::RequestVerifier::Credentials& requested, const biometry::dbus::skeleton::RequestVerifier::Credentials& provided) { // In case of a match: good to go. Please note that we // will dispatch to the trust-store in the future, once we // open up the service to all applications. if (requested == provided) return true; // Unconfinded apps are allowed to request operations on behalf of other applications. // We still limit them to their effective uid. if (provided.app.as_string() == "unconfined" && requested.user == provided.user) return true; // All other cases are forbidden. return false; } } bool biometry::dbus::skeleton::TemplateStore::RequestVerifier::verify_size_request(const Credentials& requested, const Credentials& provided) { return verify(requested, provided); } bool biometry::dbus::skeleton::TemplateStore::RequestVerifier::verify_list_request(const Credentials& requested, const Credentials& provided) { return verify(requested, provided); } bool biometry::dbus::skeleton::TemplateStore::RequestVerifier::verify_enroll_request(const Credentials& requested, const Credentials& provided) { return verify(requested, provided); } bool biometry::dbus::skeleton::TemplateStore::RequestVerifier::verify_remove_request(const Credentials& requested, const Credentials& provided) { return verify(requested, provided); } bool biometry::dbus::skeleton::TemplateStore::RequestVerifier::verify_clear_request(const Credentials& requested, const Credentials& provided) { return verify(requested, provided); } biometry::dbus::skeleton::TemplateStore::Ptr biometry::dbus::skeleton::TemplateStore::create_for_service_and_object( const core::dbus::Bus::Ptr& bus, const core::dbus::Service::Ptr& service, const core::dbus::Object::Ptr& object, const std::reference_wrapper& impl, const std::shared_ptr& request_verifier, const std::shared_ptr& credentials_resolver) { return Ptr{new TemplateStore{bus, service, object, impl, request_verifier, credentials_resolver}}; } biometry::Operation::Ptr biometry::dbus::skeleton::TemplateStore::size(const biometry::Application& app, const biometry::User& user) { return impl.get().size(app, user); } biometry::Operation::Ptr biometry::dbus::skeleton::TemplateStore::list(const biometry::Application& app, const biometry::User& user) { return impl.get().list(app, user); } biometry::Operation::Ptr biometry::dbus::skeleton::TemplateStore::enroll(const biometry::Application& app, const biometry::User& user) { return impl.get().enroll(app, user); } biometry::Operation::Ptr biometry::dbus::skeleton::TemplateStore::remove(const biometry::Application& app, const biometry::User& user, biometry::TemplateStore::TemplateId id) { return impl.get().remove(app, user, id); } biometry::Operation::Ptr biometry::dbus::skeleton::TemplateStore::clear(const biometry::Application& app, const biometry::User& user) { return impl.get().clear(app, user); } biometry::dbus::skeleton::TemplateStore::TemplateStore( const core::dbus::Bus::Ptr& bus, const core::dbus::Service::Ptr& service, const core::dbus::Object::Ptr& object, const std::reference_wrapper& impl, const std::shared_ptr& request_verifier, const std::shared_ptr& credentials_resolver) : impl{impl}, request_verifier{request_verifier}, credentials_resolver{credentials_resolver}, bus{bus}, service{service}, object{object} { object->install_method_handler([this](const core::dbus::Message::Ptr& msg) { TemplateStore::credentials_resolver->resolve_credentials(msg, [this, msg](const Optional& credentials) { if (not credentials) { this->bus->send(not_permitted_in_reply_to(msg)); return; } biometry::User user; biometry::Application app = biometry::Application::system(); auto reader = msg->reader(); reader >> app >> user; if (not this->request_verifier->verify_size_request({app, user}, credentials.get())) { this->bus->send(not_permitted_in_reply_to(msg)); return; } auto op = size(app, user); core::dbus::types::ObjectPath op_path { (boost::format{"%1%/operation/size/%2%/%3%/%4%"} % this->object->path().as_string() % credentials.get().app.as_string() % credentials.get().user.id % util::counter().increment()).str() }; ops.size.synchronized([this, op_path, op](SizeOps::ValueType& ops) { ops[op_path] = skeleton::Operation::create_for_object(this->bus, this->service->add_object_for_path(op_path), op); }); auto reply = core::dbus::Message::make_method_return(msg); reply->writer() << op_path; this->bus->send(reply); }); }); object->install_method_handler([this](const core::dbus::Message::Ptr& msg) { TemplateStore::credentials_resolver->resolve_credentials(msg, [this, msg](const Optional& credentials) { if (not credentials) { this->bus->send(not_permitted_in_reply_to(msg)); return; } biometry::User user; biometry::Application app = biometry::Application::system(); auto reader = msg->reader(); reader >> app >> user; if (not this->request_verifier->verify_list_request({app, user}, credentials.get())) { this->bus->send(not_permitted_in_reply_to(msg)); return; } auto op = list(app, user); core::dbus::types::ObjectPath op_path { (boost::format{"%1%/operation/list/%2%/%3%/%4%"} % this->object->path().as_string() % credentials.get().app.as_string() % credentials.get().user.id % util::counter().increment()).str() }; ops.list.synchronized([this, op_path, op](ListOps::ValueType& ops) { ops[op_path] = skeleton::Operation::create_for_object(this->bus, this->service->add_object_for_path(op_path), op); }); auto reply = core::dbus::Message::make_method_return(msg); reply->writer() << op_path; this->bus->send(reply); }); }); object->install_method_handler([this](const core::dbus::Message::Ptr& msg) { TemplateStore::credentials_resolver->resolve_credentials(msg, [this, msg](const Optional& credentials) { if (not credentials) { this->bus->send(not_permitted_in_reply_to(msg)); return; } biometry::User user; biometry::Application app = biometry::Application::system(); auto reader = msg->reader(); reader >> app >> user; if (not this->request_verifier->verify_enroll_request({app, user}, credentials.get())) { this->bus->send(not_permitted_in_reply_to(msg)); return; } auto op = enroll(app, user); core::dbus::types::ObjectPath op_path { (boost::format{"%1%/operation/enroll/%2%/%3%/%4%"} % this->object->path().as_string() % credentials.get().app.as_string() % credentials.get().user.id % util::counter().increment()).str() }; ops.enroll.synchronized([this, op_path, op](EnrollOps::ValueType& ops) { ops[op_path] = skeleton::Operation::create_for_object(this->bus, this->service->add_object_for_path(op_path), op); }); auto reply = core::dbus::Message::make_method_return(msg); reply->writer() << op_path; this->bus->send(reply); }); }); object->install_method_handler([this](const core::dbus::Message::Ptr& msg) { TemplateStore::credentials_resolver->resolve_credentials(msg, [this, msg](const Optional& credentials) { if (not credentials) { this->bus->send(not_permitted_in_reply_to(msg)); return; } biometry::User user; biometry::Application app = biometry::Application::system(); biometry::TemplateStore::TemplateId id{0}; auto reader = msg->reader(); reader >> app >> user >> id; if (not this->request_verifier->verify_remove_request({app, user}, credentials.get())) { this->bus->send(not_permitted_in_reply_to(msg)); return; } auto op = remove(app, user, id); core::dbus::types::ObjectPath op_path { (boost::format{"%1%/operation/remove/%2%/%3%/%4%"} % this->object->path().as_string() % credentials.get().app.as_string() % credentials.get().user.id % util::counter().increment()).str() }; ops.remove.synchronized([this, op_path, op](RemoveOps::ValueType& ops) { ops[op_path] = skeleton::Operation::create_for_object(this->bus, this->service->add_object_for_path(op_path), op); }); auto reply = core::dbus::Message::make_method_return(msg); reply->writer() << op_path; this->bus->send(reply); }); }); object->install_method_handler([this](const core::dbus::Message::Ptr& msg) { TemplateStore::credentials_resolver->resolve_credentials(msg, [this, msg](const Optional& credentials) { if (not credentials) { this->bus->send(not_permitted_in_reply_to(msg)); return; } biometry::User user; biometry::Application app = biometry::Application::system(); auto reader = msg->reader(); reader >> app >> user; if (not this->request_verifier->verify_clear_request({app, user}, credentials.get())) { this->bus->send(not_permitted_in_reply_to(msg)); return; } auto op = clear(app, user); core::dbus::types::ObjectPath op_path { (boost::format{"%1%/operation/clear/%2%/%3%/%4%"} % this->object->path().as_string() % credentials.get().app.as_string() % credentials.get().user.id % util::counter().increment()).str() }; ops.clear.synchronized([this, op_path, op](ClearOps::ValueType& ops) { ops[op_path] = skeleton::Operation::create_for_object(this->bus, this->service->add_object_for_path(op_path), op); }); auto reply = core::dbus::Message::make_method_return(msg); reply->writer() << op_path; this->bus->send(reply); }); }); } biometry::dbus::skeleton::TemplateStore::~TemplateStore() { object->uninstall_method_handler(); object->uninstall_method_handler(); object->uninstall_method_handler(); } biometryd-0.3.1/src/biometry/dbus/skeleton/template_store.h000066400000000000000000000132721455450034500241250ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_DBUS_SKELETON_TEMPLATE_STORE_H_ #define BIOMETRYD_DBUS_SKELETON_TEMPLATE_STORE_H_ #include #include #include #include #include #include #include namespace biometry { namespace dbus { namespace skeleton { /// @cond class Device; /// @endcond // TemplateStore is the dbus SKELETON implementation of biometry::TemplateStore. class TemplateStore : public biometry::TemplateStore { public: // Safe us some typing typedef std::shared_ptr Ptr; /// @brief RequestVerifier models verification of incoming requests. class RequestVerifier : public biometry::dbus::skeleton::RequestVerifier { public: /// @brief verify_size_request returns true if the application identified by credentials /// is permitted to query the number of enrolled templates for the app and user bundled in request. virtual bool verify_size_request(const Credentials& requested, const Credentials& provided); /// @brief verify_list_request returns true if the application identified by credentials is /// allowed to list all templates for the user and app bundled in request. virtual bool verify_list_request(const Credentials& requested, const Credentials& provided); /// @brief verify_enroll_request returns true if the application identified by credentials is /// allowed to enroll a new template for the app and user bundled in request. virtual bool verify_enroll_request(const Credentials& requested, const Credentials& provided); /// @brief verify_remove_request returns true if the application identified by credentials is /// allowed to remove a template for the app and user bundled in request. virtual bool verify_remove_request(const Credentials& requested, const Credentials& provided); /// @brief verify_clear_request returns true if the application identified by credentials is /// allowed to clear all templates for the app and user bundled in request. virtual bool verify_clear_request(const Credentials& requested, const Credentials& provided); }; /// @brief create_for_bus returns a new skeleton::TemplateStore instance connected to bus, forwarding calls to impl. static Ptr create_for_service_and_object( const core::dbus::Bus::Ptr& bus, const core::dbus::Service::Ptr& service, const core::dbus::Object::Ptr& object, const std::reference_wrapper& impl, const std::shared_ptr& request_verifier, const std::shared_ptr& credentials_resolver); /// @brief Frees up resources and uninstall message handlers. ~TemplateStore(); // From biometry::Identifier. // biometry::Operation Operation::Ptr size(const Application&, const User&) override; Operation::Ptr list(const Application& app, const User& user) override; Operation::Ptr enroll(const Application&, const User&) override; Operation::Ptr remove(const Application& app, const User& user, TemplateStore::TemplateId id) override; Operation::Ptr clear(const Application&, const User&) override; private: typedef util::Synchronized::Ptr>> SizeOps; typedef util::Synchronized::Ptr>> ListOps; typedef util::Synchronized::Ptr>> EnrollOps; typedef util::Synchronized::Ptr>> RemoveOps; typedef util::Synchronized::Ptr>> ClearOps; /// @brief TemplateStore creates a new instance for the given remote service and object. TemplateStore(const core::dbus::Bus::Ptr& bus, const core::dbus::Service::Ptr& service, const core::dbus::Object::Ptr& object, const std::reference_wrapper& impl, const std::shared_ptr& request_verifier, const std::shared_ptr& credentials_resolver); std::reference_wrapper impl; std::shared_ptr request_verifier; std::shared_ptr credentials_resolver; core::dbus::Bus::Ptr bus; core::dbus::Service::Ptr service; core::dbus::Object::Ptr object; struct { SizeOps size; ListOps list; EnrollOps enroll; RemoveOps remove; ClearOps clear; } ops; }; } } } #endif // BIOMETRYD_DBUS_SKELETON_TEMPLATE_STORE_H_ biometryd-0.3.1/src/biometry/dbus/stub/000077500000000000000000000000001455450034500200515ustar00rootroot00000000000000biometryd-0.3.1/src/biometry/dbus/stub/device.cpp000066400000000000000000000044101455450034500220130ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include #include /// @brief Device creates a new instance for the given remote service and object; biometry::dbus::stub::Device::Device(const core::dbus::Bus::Ptr& bus, const core::dbus::Service::Ptr& service, const core::dbus::Object::Ptr& object) : bus_{bus}, service_{service}, object_{object} { } // From biometry::Device biometry::TemplateStore& biometry::dbus::stub::Device::template_store() { return *template_store_([this]() { auto result = object_->invoke_method_synchronously< biometry::dbus::interface::Device::Methods::TemplateStore, biometry::dbus::interface::Device::Methods::TemplateStore::ResultType >(); return biometry::dbus::stub::TemplateStore::create_for_service_and_object(bus_, service_, service_->object_for_path(result.value())); }); } biometry::Identifier& biometry::dbus::stub::Device::identifier() { return *identifier_([this]() { auto result = object_->invoke_method_synchronously< biometry::dbus::interface::Device::Methods::Identifier, biometry::dbus::interface::Device::Methods::Identifier::ResultType >(); return biometry::dbus::stub::Identifier::create_for_service_and_object(bus_, service_, service_->object_for_path(result.value())); }); } biometry::Verifier& biometry::dbus::stub::Device::verifier() { throw std::runtime_error{"Not implemented"}; } biometryd-0.3.1/src/biometry/dbus/stub/device.h000066400000000000000000000034171455450034500214660ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_DBUS_STUB_DEVICE_H_ #define BIOMETRYD_DBUS_STUB_DEVICE_H_ #include #include #include #include namespace biometry { namespace dbus { namespace stub { /// @cond class Identifier; class TemplateStore; /// @endcond class Device : public biometry::Device { public: /// @brief Device creates a new instance for the given remote service and object; Device(const core::dbus::Bus::Ptr& bus, const core::dbus::Service::Ptr& service, const core::dbus::Object::Ptr& object); // From biometry::Device biometry::TemplateStore& template_store() override; biometry::Identifier& identifier() override; biometry::Verifier& verifier() override; private: core::dbus::Bus::Ptr bus_; core::dbus::Service::Ptr service_; core::dbus::Object::Ptr object_; util::Once> template_store_; util::Once> identifier_; }; } } } #endif // BIOMETRYD_DBUS_STUB_DEVICE_H_ biometryd-0.3.1/src/biometry/dbus/stub/identifier.cpp000066400000000000000000000035571455450034500227110ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include #include biometry::dbus::stub::Identifier::Ptr biometry::dbus::stub::Identifier::create_for_service_and_object( const core::dbus::Bus::Ptr& bus, const core::dbus::Service::Ptr& service, const core::dbus::Object::Ptr& object) { return Ptr{new Identifier{bus, service, object}}; } biometry::Operation::Ptr biometry::dbus::stub::Identifier::identify_user(const Application& app, const Reason& reason) { auto result = object->invoke_method_synchronously< biometry::dbus::interface::Identifier::Methods::IdentifyUser, biometry::dbus::interface::Identifier::Methods::IdentifyUser::ResultType >(app, reason); return Operation::create_for_object_and_service(bus, service, service->object_for_path(result.value())); } biometry::dbus::stub::Identifier::Identifier(const core::dbus::Bus::Ptr& bus, const core::dbus::Service::Ptr& service, const core::dbus::Object::Ptr& object) : bus{bus}, service{service}, object{object} { } biometryd-0.3.1/src/biometry/dbus/stub/identifier.h000066400000000000000000000036241455450034500223510ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_DBUS_STUB_IDENTIFIER_H_ #define BIOMETRYD_DBUS_STUB_IDENTIFIER_H_ #include #include #include namespace biometry { namespace dbus { namespace stub { // Identifier is the dbus stub implementation of biometry::Service. class Identifier : public biometry::Identifier { public: // Safe us some typing. typedef std::shared_ptr Ptr; /// @brief create_for_bus creates a new instance connecting to bus, forwarding incoming calls to impl. static Ptr create_for_service_and_object(const core::dbus::Bus::Ptr& bus, const core::dbus::Service::Ptr& service, const core::dbus::Object::Ptr& object); // From biometry::Identifier. Operation::Ptr identify_user(const Application& app, const Reason& reason) override; private: /// @brief Service creates a new instance for the given remote service and object. Identifier(const core::dbus::Bus::Ptr& bus, const core::dbus::Service::Ptr& service, const core::dbus::Object::Ptr& object); core::dbus::Bus::Ptr bus; core::dbus::Service::Ptr service; core::dbus::Object::Ptr object; }; } } } #endif // BIOMETRYD_DBUS_STUB_IDENTIFIER_H_ biometryd-0.3.1/src/biometry/dbus/stub/observer.h000066400000000000000000000074361455450034500220630ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_DBUS_STUB_OBSERVER_H_ #define BIOMETRYD_DBUS_STUB_OBSERVER_H_ #include #include #include namespace biometry { namespace dbus { namespace stub { template class Observer : public Operation::Observer { public: // Safe us some typing typedef std::shared_ptr> Ptr; typedef typename biometry::Operation::Observer Super; using typename Super::Progress; using typename Super::Reason; using typename Super::Error; using typename Super::Result; /// @brief create_for_object_and_service returns a new instance on the given object. static Ptr create_for_object(const core::dbus::Object::Ptr& object); // From Operation::Observer void on_started() override; void on_progress(const Progress&) override; void on_canceled(const Reason&) override; void on_failed(const Error&) override; void on_succeeded(const Result&) override; private: /// @brief Observer initializes a new instance for the given remote object. Observer(const core::dbus::Object::Ptr& object); core::dbus::Object::Ptr object; }; } } } template typename biometry::dbus::stub::Observer::Ptr biometry::dbus::stub::Observer::create_for_object(const core::dbus::Object::Ptr& object) { return Ptr{new Observer{object}}; } template void biometry::dbus::stub::Observer::on_started() { object->invoke_method_asynchronously_with_callback< biometry::dbus::interface::Operation::Observer::Methods::OnStarted, void >([](const core::dbus::Result&) { }); } template void biometry::dbus::stub::Observer::on_progress(const typename Observer::Progress& progress) { object->invoke_method_asynchronously_with_callback< biometry::dbus::interface::Operation::Observer::Methods::OnProgress, void >([](const core::dbus::Result&) { }, progress); } template void biometry::dbus::stub::Observer::on_canceled(const Reason& reason) { object->invoke_method_asynchronously_with_callback< biometry::dbus::interface::Operation::Observer::Methods::OnCancelled, void >([](const core::dbus::Result&) { }, reason); } template void biometry::dbus::stub::Observer::on_failed(const Error& error) { object->invoke_method_asynchronously_with_callback< biometry::dbus::interface::Operation::Observer::Methods::OnFailed, void >([](const core::dbus::Result&) { }, error); } template void biometry::dbus::stub::Observer::on_succeeded(const Result& result) { object->invoke_method_asynchronously_with_callback< biometry::dbus::interface::Operation::Observer::Methods::OnSucceeded, void >([](const core::dbus::Result&) { }, result); } template biometry::dbus::stub::Observer::Observer(const core::dbus::Object::Ptr& object) : object{object} { } #endif // BIOMETRYD_DBUS_STUB_OBSERVER_H_ biometryd-0.3.1/src/biometry/dbus/stub/operation.h000066400000000000000000000100261455450034500222210ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_DBUS_STUB_OPERATION_H_ #define BIOMETRYD_DBUS_STUB_OPERATION_H_ #include #include #include #include #include #include #include namespace biometry { namespace dbus { namespace stub { // Identifier is the dbus SKELETON implementation of biometry::Service. template class Operation : public biometry::Operation { public: // Safe us some typing typedef std::shared_ptr> Ptr; typedef biometry::Operation Super; using typename Super::Observer; using typename Super::Progress; using typename Super::Reason; using typename Super::Error; using typename Super::Result; /// @brief create_for_object_and_service returns a new instance on the given object. static Ptr create_for_object_and_service(const core::dbus::Bus::Ptr& bus, const core::dbus::Service::Ptr& service, const core::dbus::Object::Ptr& object); /// @brief Frees up resources and uninstall message handlers. ~Operation(); // From biometry::Operation void start_with_observer(const typename Observer::Ptr& observer) override; void cancel() override; private: /// @brief Operation creates a new instance for the given remote service and object. Operation(const core::dbus::Bus::Ptr& bus, const core::dbus::Service::Ptr& service, const core::dbus::Object::Ptr& object); core::dbus::Bus::Ptr bus; core::dbus::Service::Ptr service; core::dbus::Object::Ptr object; }; } } } template typename biometry::dbus::stub::Operation::Ptr biometry::dbus::stub::Operation::create_for_object_and_service( const core::dbus::Bus::Ptr& bus, const core::dbus::Service::Ptr& service, const core::dbus::Object::Ptr& object) { return Ptr{new Operation{bus, service, object}}; } template biometry::dbus::stub::Operation::~Operation() { } // From biometry::Operation template void biometry::dbus::stub::Operation::start_with_observer(const typename Observer::Ptr& observer) { auto path = core::dbus::types::ObjectPath { (boost::format("%1%/observer") % object->path().as_string()).str() }; auto obs = biometry::dbus::skeleton::Observer::create_for_object(bus, service->add_object_for_path(path), observer); auto result = object->invoke_method_synchronously< biometry::dbus::interface::Operation::Methods::StartWithObserver, biometry::dbus::interface::Operation::Methods::StartWithObserver::ResultType >(path); if (result.is_error()) throw std::runtime_error{result.error().print()}; } template void biometry::dbus::stub::Operation::cancel() { auto result = object->invoke_method_synchronously< biometry::dbus::interface::Operation::Methods::Cancel, biometry::dbus::interface::Operation::Methods::Cancel::ResultType >(); } template biometry::dbus::stub::Operation::Operation( const core::dbus::Bus::Ptr& bus, const core::dbus::Service::Ptr& service, const core::dbus::Object::Ptr& object) : bus{bus}, service{service}, object{object} { } #endif // BIOMETRYD_DBUS_STUB_OPERATION_H_ biometryd-0.3.1/src/biometry/dbus/stub/service.cpp000066400000000000000000000036361455450034500222250ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include biometry::dbus::stub::Service::Ptr biometry::dbus::stub::Service::create_for_bus(const core::dbus::Bus::Ptr& bus) { auto service = core::dbus::Service::use_service(bus, biometry::dbus::interface::Service::name()); auto object = service->object_for_path(biometry::dbus::interface::Service::path()); return Ptr{new Service{bus, service, object}}; } // From biometry::Service. std::shared_ptr biometry::dbus::stub::Service::default_device() const { auto result = object->invoke_method_synchronously< biometry::dbus::interface::Service::Methods::DefaultDevice, biometry::dbus::interface::Service::Methods::DefaultDevice::ResultType >(); if (result.is_error()) throw std::runtime_error{result.error().print()}; return std::make_shared(bus, service, service->object_for_path(result.value())); } biometry::dbus::stub::Service::Service(const core::dbus::Bus::Ptr& bus, const core::dbus::Service::Ptr& service, const core::dbus::Object::Ptr& object) : bus{bus}, service{service}, object{object} { } biometryd-0.3.1/src/biometry/dbus/stub/service.h000066400000000000000000000034011455450034500216600ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_DBUS_STUB_SERVICE_H_ #define BIOMETRYD_DBUS_STUB_SERVICE_H_ #include #include #include #include namespace biometry { namespace dbus { namespace stub { /// @cond class Device; /// @endcond /// @brief Service is the dbus stub implementation of biometry::Service. class BIOMETRY_DLL_PUBLIC Service : public biometry::Service { public: // Safe us some typing. typedef std::shared_ptr Ptr; /// @brief create_for_bus creates a new instance connecting to bus, forwarding incoming calls to impl. static Ptr create_for_bus(const core::dbus::Bus::Ptr& bus); // From biometry::Service. std::shared_ptr default_device() const override; private: Service(const core::dbus::Bus::Ptr& bus, const core::dbus::Service::Ptr& service, const core::dbus::Object::Ptr& object); core::dbus::Bus::Ptr bus; core::dbus::Service::Ptr service; core::dbus::Object::Ptr object; }; } } } #endif // BIOMETRYD_DBUS_STUB_SERVICE_H_ biometryd-0.3.1/src/biometry/dbus/stub/template_store.cpp000066400000000000000000000077661455450034500236240ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include #include #include #include biometry::dbus::stub::TemplateStore::Ptr biometry::dbus::stub::TemplateStore::create_for_service_and_object( const core::dbus::Bus::Ptr& bus, const core::dbus::Service::Ptr& service, const core::dbus::Object::Ptr& object) { return Ptr{new TemplateStore{bus, service, object}}; } biometry::Operation::Ptr biometry::dbus::stub::TemplateStore::size(const biometry::Application& app, const biometry::User& user) { auto result = object->invoke_method_synchronously< biometry::dbus::interface::TemplateStore::Methods::Size, biometry::dbus::interface::TemplateStore::Methods::Size::ResultType >(app, user); return Operation::create_for_object_and_service(bus, service, service->object_for_path(result.value())); } biometry::Operation::Ptr biometry::dbus::stub::TemplateStore::list(const biometry::Application& app, const biometry::User& user) { auto result = object->invoke_method_synchronously< biometry::dbus::interface::TemplateStore::Methods::List, biometry::dbus::interface::TemplateStore::Methods::List::ResultType >(app, user); return Operation::create_for_object_and_service(bus, service, service->object_for_path(result.value())); } biometry::Operation::Ptr biometry::dbus::stub::TemplateStore::enroll(const biometry::Application& app, const biometry::User& user) { auto result = object->invoke_method_synchronously< biometry::dbus::interface::TemplateStore::Methods::Enroll, biometry::dbus::interface::TemplateStore::Methods::Enroll::ResultType >(app, user); return Operation::create_for_object_and_service(bus, service, service->object_for_path(result.value())); } biometry::Operation::Ptr biometry::dbus::stub::TemplateStore::remove(const biometry::Application& app, const biometry::User& user, biometry::TemplateStore::TemplateId id) { auto result = object->invoke_method_synchronously< biometry::dbus::interface::TemplateStore::Methods::Remove, biometry::dbus::interface::TemplateStore::Methods::Remove::ResultType >(app, user, id); return Operation::create_for_object_and_service(bus, service, service->object_for_path(result.value())); } biometry::Operation::Ptr biometry::dbus::stub::TemplateStore::clear(const biometry::Application& app, const biometry::User& user) { auto result = object->invoke_method_synchronously< biometry::dbus::interface::TemplateStore::Methods::Clear, biometry::dbus::interface::TemplateStore::Methods::Clear::ResultType >(app, user); return Operation::create_for_object_and_service(bus, service, service->object_for_path(result.value())); } biometry::dbus::stub::TemplateStore::TemplateStore(const core::dbus::Bus::Ptr& bus, const core::dbus::Service::Ptr& service, const core::dbus::Object::Ptr& object) : bus{bus}, service{service}, object{object} { } biometryd-0.3.1/src/biometry/dbus/stub/template_store.h000066400000000000000000000045701455450034500232570ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_DBUS_STUB_TEMPLATE_STORE_H_ #define BIOMETRYD_DBUS_STUB_TEMPLATE_STORE_H_ #include #include #include namespace biometry { namespace dbus { namespace stub { // TemplateStore is the dbus stub implementation of biometry::TemplateStore. class TemplateStore : public biometry::TemplateStore { public: // Safe us some typing. typedef std::shared_ptr Ptr; /// @brief create_for_bus creates a new instance on the given service and object. static Ptr create_for_service_and_object(const core::dbus::Bus::Ptr& bus, const core::dbus::Service::Ptr& service, const core::dbus::Object::Ptr& object); // From biometry::Identifier. // biometry::Operation Operation::Ptr size(const Application&, const User&) override; Operation::Ptr list(const Application& app, const User& user) override; Operation::Ptr enroll(const Application&, const User&) override; Operation::Ptr remove(const Application& app, const User& user, TemplateStore::TemplateId id) override; Operation::Ptr clear(const Application&, const User&) override; private: /// @brief TemplateStore creates a new instance for the given remote service and object. TemplateStore(const core::dbus::Bus::Ptr& bus, const core::dbus::Service::Ptr& service, const core::dbus::Object::Ptr& object); core::dbus::Bus::Ptr bus; core::dbus::Service::Ptr service; core::dbus::Object::Ptr object; }; } } } #endif // BIOMETRYD_DBUS_STUB_TEMPLATE_STORE_H_ biometryd-0.3.1/src/biometry/device_registrar.cpp000066400000000000000000000033121455450034500221630ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #ifdef WITH_HYBRIS #include #endif #include #include #include namespace plugin = biometry::devices::plugin; biometry::DeviceRegistrar::DeviceRegistrar(const biometry::devices::plugin::Enumerator& enumerator) { biometry::device_registry()[biometry::devices::Dummy::id] = biometry::devices::Dummy::make_descriptor(); biometry::device_registry()[biometry::devices::plugin::id] = biometry::devices::plugin::make_descriptor(); #ifdef WITH_HYBRIS biometry::device_registry()[biometry::devices::android::id] = biometry::devices::android::make_descriptor(); #endif enumerator.enumerate([](const biometry::Device::Descriptor::Ptr& desc) { biometry::device_registry()[desc->name()] = desc; }); } biometry::DeviceRegistrar::~DeviceRegistrar() { biometry::device_registry().clear(); } biometryd-0.3.1/src/biometry/device_registrar.h000066400000000000000000000030631455450034500216330ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_DEVICE_REGISTRAR_H_ #define BIOMETRYD_DEVICE_REGISTRAR_H_ #include #include namespace biometry { namespace devices { namespace plugin { /// @cond class Enumerator; /// @endcond } } /// @brief DeviceRegistrar makes devices known to the device registry. /// /// A DeviceRegistrar adds Device::Descriptor instances to the registry for both: /// - builtin devices /// - plugin devices that place their modules into a well-known location struct BIOMETRY_DLL_PUBLIC DeviceRegistrar { /// @brief DeviceRegistrar initializes the registry for builtin devices and the plugins reported by enumerator. DeviceRegistrar(const biometry::devices::plugin::Enumerator& enumerator); /// @brief DeviceRegistrar cleans out the device registry. ~DeviceRegistrar(); }; } #endif // BIOMETRYD_DEVICE_REGISTRAR_H_ biometryd-0.3.1/src/biometry/device_registry.cpp000066400000000000000000000015441455450034500220360ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include biometry::DeviceRegistry& biometry::device_registry() { static DeviceRegistry registry; return registry; } biometryd-0.3.1/src/biometry/device_registry.h000066400000000000000000000022041455450034500214750ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_DEVICE_REGISTRY_H_ #define BIOMETRYD_DEVICE_REGISTRY_H_ #include #include #include namespace biometry { typedef std::unordered_map DeviceRegistry; /// @brief device_registry returns the process-wide registry of known devices: BIOMETRY_DLL_PUBLIC DeviceRegistry& device_registry(); } #endif // BIOMETRYD_DEVICE_REGISTRY_H_ biometryd-0.3.1/src/biometry/devices/000077500000000000000000000000001455450034500175615ustar00rootroot00000000000000biometryd-0.3.1/src/biometry/devices/android.cpp000066400000000000000000000610741455450034500217150ustar00rootroot00000000000000/* * Copyright (C) 2020 UBports foundation, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Erfan Abdi * */ #include #include #include #include #include #include std::string IntToStringFingerprintError(int error, int vendorCode){ switch(error) { case ERROR_NO_ERROR: return "ERROR_NO_ERROR"; case ERROR_HW_UNAVAILABLE: return "ERROR_HW_UNAVAILABLE"; case ERROR_UNABLE_TO_PROCESS: return "ERROR_UNABLE_TO_PROCESS"; case ERROR_TIMEOUT: return "ERROR_TIMEOUT"; case ERROR_NO_SPACE: return "ERROR_NO_SPACE"; case ERROR_CANCELED: return "ERROR_CANCELED"; case ERROR_UNABLE_TO_REMOVE: return "ERROR_UNABLE_TO_REMOVE"; case ERROR_LOCKOUT: return "ERROR_LOCKOUT"; case ERROR_VENDOR: return "ERROR_VENDOR: " + std::to_string(vendorCode); default: return "ERROR_NO_ERROR"; } } std::string IntToStringRequestStatus(int error){ switch(error) { case SYS_UNKNOWN: return "SYS_UNKNOWN"; case SYS_OK: return "SYS_OK"; case SYS_ENOENT: return "SYS_ENOENT"; case SYS_EINTR: return "SYS_EINTR"; case SYS_EIO: return "SYS_EIO"; case SYS_EAGAIN: return "SYS_EAGAIN"; case SYS_ENOMEM: return "SYS_ENOMEM"; case SYS_EACCES: return "SYS_EACCES"; case SYS_EFAULT: return "SYS_EFAULT"; case SYS_EBUSY: return "SYS_EBUSY"; case SYS_EINVAL: return "SYS_EINVAL"; case SYS_ENOSPC: return "SYS_ENOSPC"; case SYS_ETIMEDOUT: return "SYS_ETIMEDOUT"; default: return "SYS_OK"; } } namespace { class androidEnrollOperation : public biometry::Operation { public: typename biometry::Operation::Observer::Ptr mobserver; int totalrem = 0; androidEnrollOperation(UHardwareBiometry hybris_fp_instance, uid_t user_id) : hybris_fp_instance{hybris_fp_instance}, user_id{user_id} { } void start_with_observer(const typename biometry::Operation::Observer::Ptr& observer) override { mobserver = observer; observer->on_started(); UHardwareBiometryParams fp_params; fp_params.enrollresult_cb = enrollresult_cb; fp_params.acquired_cb = acquired_cb; fp_params.authenticated_cb = authenticated_cb; fp_params.error_cb = error_cb; fp_params.removed_cb = removed_cb; fp_params.enumerate_cb = enumerate_cb; fp_params.context = this; u_hardware_biometry_setNotify(hybris_fp_instance, &fp_params); UHardwareBiometryRequestStatus ret = u_hardware_biometry_enroll(hybris_fp_instance, 0, 60, user_id); if (ret != SYS_OK) observer->on_failed(IntToStringRequestStatus(ret)); } void cancel() override { u_hardware_biometry_cancel(hybris_fp_instance); } private: UHardwareBiometry hybris_fp_instance; uid_t user_id; static void acquired_cb(uint64_t, UHardwareBiometryFingerprintAcquiredInfo, int32_t, void *){} static void authenticated_cb(uint64_t, uint32_t, uint32_t, void *){} static void removed_cb(uint64_t, uint32_t, uint32_t, uint32_t, void *){} static void enumerate_cb(uint64_t, uint32_t, uint32_t, uint32_t, void *){} static void enrollresult_cb(uint64_t, uint32_t fingerId, uint32_t, uint32_t remaining, void *context) { if (remaining > 0) { if (((androidEnrollOperation*)context)->totalrem == 0) ((androidEnrollOperation*)context)->totalrem = remaining + 1; float raw_value = 1 - ((float)remaining / ((androidEnrollOperation*)context)->totalrem); ((androidEnrollOperation*)context)->mobserver->on_progress(biometry::Progress{biometry::Percent::from_raw_value(raw_value), biometry::Dictionary{}}); } else { ((androidEnrollOperation*)context)->mobserver->on_progress(biometry::Progress{biometry::Percent::from_raw_value(1), biometry::Dictionary{}}); UHardwareBiometryRequestStatus ret = u_hardware_biometry_postEnroll(((androidEnrollOperation*)context)->hybris_fp_instance); if (ret == SYS_OK) ((androidEnrollOperation*)context)->mobserver->on_succeeded(fingerId); else ((androidEnrollOperation*)context)->mobserver->on_failed(IntToStringRequestStatus(ret)); } } static void error_cb(uint64_t, UHardwareBiometryFingerprintError error, int32_t vendorCode, void *context) { if (error == 0) return; ((androidEnrollOperation*)context)->mobserver->on_failed(IntToStringFingerprintError(error, vendorCode)); } }; class androidRemovalOperation : public biometry::Operation { public: typename biometry::Operation::Observer::Ptr mobserver; androidRemovalOperation(UHardwareBiometry hybris_fp_instance, uint32_t finger) : hybris_fp_instance{hybris_fp_instance}, finger{finger} { } void start_with_observer(const typename biometry::Operation::Observer::Ptr& observer) override { mobserver = observer; observer->on_started(); UHardwareBiometryParams fp_params; fp_params.enrollresult_cb = enrollresult_cb; fp_params.acquired_cb = acquired_cb; fp_params.authenticated_cb = authenticated_cb; fp_params.error_cb = error_cb; fp_params.removed_cb = removed_cb; fp_params.enumerate_cb = enumerate_cb; fp_params.context = this; u_hardware_biometry_setNotify(hybris_fp_instance, &fp_params); UHardwareBiometryRequestStatus ret = u_hardware_biometry_remove(hybris_fp_instance, 0, finger); if (ret != SYS_OK) observer->on_failed(IntToStringRequestStatus(ret)); } void cancel() override { u_hardware_biometry_cancel(hybris_fp_instance); } private: UHardwareBiometry hybris_fp_instance; uint32_t finger; static void enrollresult_cb(uint64_t, uint32_t, uint32_t, uint32_t, void *){} static void acquired_cb(uint64_t, UHardwareBiometryFingerprintAcquiredInfo, int32_t, void *){} static void authenticated_cb(uint64_t, uint32_t, uint32_t, void *){} static void enumerate_cb(uint64_t, uint32_t, uint32_t, uint32_t, void *){} static void removed_cb(uint64_t, uint32_t fingerId, uint32_t, uint32_t remaining, void *context) { if (fingerId == ((androidRemovalOperation*)context)->finger && remaining == 0) ((androidRemovalOperation*)context)->mobserver->on_succeeded(fingerId); } static void error_cb(uint64_t, UHardwareBiometryFingerprintError error, int32_t vendorCode, void *context) { if (error == 0) return; ((androidRemovalOperation*)context)->mobserver->on_failed(IntToStringFingerprintError(error, vendorCode)); } }; class androidVerificationOperation : public biometry::Operation { public: typename biometry::Operation::Observer::Ptr mobserver; androidVerificationOperation(UHardwareBiometry hybris_fp_instance) : hybris_fp_instance{hybris_fp_instance} { } void start_with_observer(const typename biometry::Operation::Observer::Ptr& observer) override { mobserver = observer; observer->on_started(); UHardwareBiometryParams fp_params; fp_params.enrollresult_cb = enrollresult_cb; fp_params.acquired_cb = acquired_cb; fp_params.authenticated_cb = authenticated_cb; fp_params.error_cb = error_cb; fp_params.removed_cb = removed_cb; fp_params.enumerate_cb = enumerate_cb; fp_params.context = this; u_hardware_biometry_setNotify(hybris_fp_instance, &fp_params); UHardwareBiometryRequestStatus ret = u_hardware_biometry_authenticate(hybris_fp_instance, 0, 0); if (ret != SYS_OK) observer->on_failed(IntToStringRequestStatus(ret)); } void cancel() override { u_hardware_biometry_cancel(hybris_fp_instance); } private: UHardwareBiometry hybris_fp_instance; static void enrollresult_cb(uint64_t, uint32_t, uint32_t, uint32_t, void *){} static void acquired_cb(uint64_t, UHardwareBiometryFingerprintAcquiredInfo, int32_t, void *){} static void removed_cb(uint64_t, uint32_t, uint32_t, uint32_t, void *){} static void enumerate_cb(uint64_t, uint32_t, uint32_t, uint32_t, void *){} static void authenticated_cb(uint64_t, uint32_t fingerId, uint32_t, void *context) { if (fingerId != 0) ((androidVerificationOperation*)context)->mobserver->on_succeeded(biometry::Verification::Result::verified); else ((androidVerificationOperation*)context)->mobserver->on_failed("FINGER_NOT_RECOGNIZED"); } static void error_cb(uint64_t, UHardwareBiometryFingerprintError error, int32_t vendorCode, void *context) { if (error == 0) return; ((androidVerificationOperation*)context)->mobserver->on_failed(IntToStringFingerprintError(error, vendorCode)); } }; class androidIdentificationOperation : public biometry::Operation { public: typename biometry::Operation::Observer::Ptr mobserver; androidIdentificationOperation(UHardwareBiometry hybris_fp_instance) : hybris_fp_instance{hybris_fp_instance} { } void start_with_observer(const typename biometry::Operation::Observer::Ptr& observer) override { mobserver = observer; observer->on_started(); UHardwareBiometryParams fp_params; fp_params.enrollresult_cb = enrollresult_cb; fp_params.acquired_cb = acquired_cb; fp_params.authenticated_cb = authenticated_cb; fp_params.error_cb = error_cb; fp_params.removed_cb = removed_cb; fp_params.enumerate_cb = enumerate_cb; fp_params.context = this; u_hardware_biometry_setNotify(hybris_fp_instance, &fp_params); UHardwareBiometryRequestStatus ret = u_hardware_biometry_authenticate(hybris_fp_instance, 0, 0); if (ret != SYS_OK) observer->on_failed(IntToStringRequestStatus(ret)); } void cancel() override { u_hardware_biometry_cancel(hybris_fp_instance); } private: UHardwareBiometry hybris_fp_instance; static void enrollresult_cb(uint64_t, uint32_t, uint32_t, uint32_t, void *){} static void acquired_cb(uint64_t, UHardwareBiometryFingerprintAcquiredInfo, int32_t, void *){} static void removed_cb(uint64_t, uint32_t, uint32_t, uint32_t, void *){} static void enumerate_cb(uint64_t, uint32_t, uint32_t, uint32_t, void *){} static void authenticated_cb(uint64_t, uint32_t fingerId, uint32_t, void *context) { if (fingerId != 0) ((androidIdentificationOperation*)context)->mobserver->on_succeeded(biometry::User(32011)); else ((androidIdentificationOperation*)context)->mobserver->on_failed("FINGER_NOT_RECOGNIZED"); } static void error_cb(uint64_t, UHardwareBiometryFingerprintError error, int32_t vendorCode, void *context) { if (error == 0) return; ((androidIdentificationOperation*)context)->mobserver->on_failed(IntToStringFingerprintError(error, vendorCode)); } }; class androidListOperation : public biometry::Operation { public: typename biometry::Operation::Observer::Ptr mobserver; int totalrem = 0; std::vector result; androidListOperation(UHardwareBiometry hybris_fp_instance) : hybris_fp_instance{hybris_fp_instance} { } void start_with_observer(const typename biometry::Operation::Observer::Ptr& observer) override { mobserver = observer; observer->on_started(); UHardwareBiometryParams fp_params; fp_params.enrollresult_cb = enrollresult_cb; fp_params.acquired_cb = acquired_cb; fp_params.authenticated_cb = authenticated_cb; fp_params.error_cb = error_cb; fp_params.removed_cb = removed_cb; fp_params.enumerate_cb = enumerate_cb; fp_params.context = this; u_hardware_biometry_setNotify(hybris_fp_instance, &fp_params); UHardwareBiometryRequestStatus ret = u_hardware_biometry_enumerate(hybris_fp_instance); if (ret != SYS_OK) observer->on_failed(IntToStringRequestStatus(ret)); } void cancel() override { u_hardware_biometry_cancel(hybris_fp_instance); } private: UHardwareBiometry hybris_fp_instance; bool fake_finger_quirks; static void enrollresult_cb(uint64_t, uint32_t, uint32_t, uint32_t, void *){} static void acquired_cb(uint64_t, UHardwareBiometryFingerprintAcquiredInfo, int32_t, void *){} static void authenticated_cb(uint64_t, uint32_t, uint32_t, void *){} static void removed_cb(uint64_t, uint32_t, uint32_t, uint32_t, void *){} static void enumerate_cb(uint64_t, uint32_t fingerId, uint32_t, uint32_t remaining, void *context) { if (((androidListOperation*)context)->totalrem == 0) ((androidListOperation*)context)->result.clear(); if (remaining > 0) { if (((androidListOperation*)context)->totalrem == 0) ((androidListOperation*)context)->totalrem = remaining + 1; float raw_value = 1 - ((float)remaining / ((androidListOperation*)context)->totalrem); ((androidListOperation*)context)->mobserver->on_progress(biometry::Progress{biometry::Percent::from_raw_value(raw_value), biometry::Dictionary{}}); ((androidListOperation*)context)->result.push_back(fingerId); } else { if (fingerId != 0) ((androidListOperation*)context)->result.push_back(fingerId); ((androidListOperation*)context)->mobserver->on_progress(biometry::Progress{biometry::Percent::from_raw_value(1), biometry::Dictionary{}}); ((androidListOperation*)context)->mobserver->on_succeeded(((androidListOperation*)context)->result); } } static void error_cb(uint64_t, UHardwareBiometryFingerprintError error, int32_t vendorCode, void *context) { if (error == 0) return; ((androidListOperation*)context)->mobserver->on_failed(IntToStringFingerprintError(error, vendorCode)); } }; class androidSizeOperation : public biometry::Operation { public: typename biometry::Operation::Observer::Ptr mobserver; int totalrem = 0; androidSizeOperation(UHardwareBiometry hybris_fp_instance, bool fake_finger_quirks) : hybris_fp_instance{hybris_fp_instance}, fake_finger_quirks(fake_finger_quirks) { } void start_with_observer(const typename biometry::Operation::Observer::Ptr& observer) override { mobserver = observer; observer->on_started(); UHardwareBiometryParams fp_params; fp_params.enrollresult_cb = enrollresult_cb; fp_params.acquired_cb = acquired_cb; fp_params.authenticated_cb = authenticated_cb; fp_params.error_cb = error_cb; fp_params.removed_cb = removed_cb; fp_params.enumerate_cb = enumerate_cb; fp_params.context = this; if (fake_finger_quirks) { enumerate_cb(0,1,1,0, this); return; } u_hardware_biometry_setNotify(hybris_fp_instance, &fp_params); UHardwareBiometryRequestStatus ret = u_hardware_biometry_enumerate(hybris_fp_instance); if (ret != SYS_OK) observer->on_failed(IntToStringRequestStatus(ret)); } void cancel() override { u_hardware_biometry_cancel(hybris_fp_instance); } private: UHardwareBiometry hybris_fp_instance; bool fake_finger_quirks; static void enrollresult_cb(uint64_t, uint32_t, uint32_t, uint32_t, void *){} static void acquired_cb(uint64_t, UHardwareBiometryFingerprintAcquiredInfo, int32_t, void *){} static void authenticated_cb(uint64_t, uint32_t, uint32_t, void *){} static void removed_cb(uint64_t, uint32_t, uint32_t, uint32_t, void *){} static void enumerate_cb(uint64_t, uint32_t fingerId, uint32_t, uint32_t remaining, void *context) { if (remaining > 0) { if (((androidSizeOperation*)context)->totalrem == 0) ((androidSizeOperation*)context)->totalrem = remaining + 1; float raw_value = 1 - (remaining / ((androidSizeOperation*)context)->totalrem); ((androidSizeOperation*)context)->mobserver->on_progress(biometry::Progress{biometry::Percent::from_raw_value(raw_value), biometry::Dictionary{}}); } else { if (((androidSizeOperation*)context)->totalrem == 0 && fingerId != 0) ((androidSizeOperation*)context)->totalrem++; ((androidSizeOperation*)context)->mobserver->on_progress(biometry::Progress{biometry::Percent::from_raw_value(1), biometry::Dictionary{}}); ((androidSizeOperation*)context)->mobserver->on_succeeded(((androidSizeOperation*)context)->totalrem); } } static void error_cb(uint64_t, UHardwareBiometryFingerprintError error, int32_t vendorCode, void *context) { if (error == 0) return; ((androidSizeOperation*)context)->mobserver->on_failed(IntToStringFingerprintError(error, vendorCode)); } }; class androidClearOperation : public biometry::Operation { public: typename biometry::Operation::Observer::Ptr mobserver; androidClearOperation(UHardwareBiometry hybris_fp_instance) : hybris_fp_instance{hybris_fp_instance} { } void start_with_observer(const typename biometry::Operation::Observer::Ptr& observer) override { mobserver = observer; observer->on_started(); UHardwareBiometryParams fp_params; fp_params.enrollresult_cb = enrollresult_cb; fp_params.acquired_cb = acquired_cb; fp_params.authenticated_cb = authenticated_cb; fp_params.error_cb = error_cb; fp_params.removed_cb = removed_cb; fp_params.enumerate_cb = enumerate_cb; fp_params.context = this; u_hardware_biometry_setNotify(hybris_fp_instance, &fp_params); UHardwareBiometryRequestStatus ret = u_hardware_biometry_remove(hybris_fp_instance, 0, 0); if (ret != SYS_OK) observer->on_failed(IntToStringRequestStatus(ret)); } void cancel() override { u_hardware_biometry_cancel(hybris_fp_instance); } private: UHardwareBiometry hybris_fp_instance; static void enrollresult_cb(uint64_t, uint32_t, uint32_t, uint32_t, void *){} static void acquired_cb(uint64_t, UHardwareBiometryFingerprintAcquiredInfo, int32_t, void *){} static void authenticated_cb(uint64_t, uint32_t, uint32_t, void *){} static void enumerate_cb(uint64_t, uint32_t, uint32_t, uint32_t, void *){} static void removed_cb(uint64_t, uint32_t, uint32_t, uint32_t remaining, void *context) { biometry::Void result; if (remaining == 0) ((androidClearOperation*)context)->mobserver->on_succeeded(result); } static void error_cb(uint64_t, UHardwareBiometryFingerprintError error, int32_t vendorCode, void *context) { if (error == 0) return; ((androidClearOperation*)context)->mobserver->on_failed(IntToStringFingerprintError(error, vendorCode)); } }; } biometry::devices::android::TemplateStore::TemplateStore(UHardwareBiometry hybris_fp_instance) : hybris_fp_instance{hybris_fp_instance}, fake_finger_quirks(false) { } void biometry::devices::android::TemplateStore::enable_fake_finger_quirks() { fake_finger_quirks = true; } biometry::Operation::Ptr biometry::devices::android::TemplateStore::size(const biometry::Application&, const biometry::User&) { return std::make_shared(hybris_fp_instance, fake_finger_quirks); } biometry::Operation::Ptr biometry::devices::android::TemplateStore::list(const biometry::Application&, const biometry::User&) { return std::make_shared(hybris_fp_instance); } biometry::Operation::Ptr biometry::devices::android::TemplateStore::enroll(const biometry::Application&, const biometry::User& user) { return std::make_shared(hybris_fp_instance, user.id); } biometry::Operation::Ptr biometry::devices::android::TemplateStore::remove(const biometry::Application&, const biometry::User&, biometry::TemplateStore::TemplateId id) { return std::make_shared(hybris_fp_instance, id); } biometry::Operation::Ptr biometry::devices::android::TemplateStore::clear(const biometry::Application&, const biometry::User&) { return std::make_shared(hybris_fp_instance); } biometry::devices::android::Identifier::Identifier(UHardwareBiometry hybris_fp_instance) : hybris_fp_instance{hybris_fp_instance} { } biometry::Operation::Ptr biometry::devices::android::Identifier::identify_user(const biometry::Application&, const biometry::Reason&) { return std::make_shared(hybris_fp_instance); } biometry::devices::android::Verifier::Verifier(UHardwareBiometry hybris_fp_instance) : hybris_fp_instance{hybris_fp_instance} { } biometry::Operation::Ptr biometry::devices::android::Verifier::verify_user(const biometry::Application&, const biometry::User&, const biometry::Reason&) { return std::make_shared(hybris_fp_instance); } biometry::devices::android::android(UHardwareBiometry hybris_fp_instance) : template_store_{hybris_fp_instance}, identifier_{hybris_fp_instance}, verifier_{hybris_fp_instance} { biometry::util::AndroidPropertyStore store; UHardwareBiometryRequestStatus ret = SYS_OK; std::string api_level = store.get("ro.product.first_api_level"); if (api_level.empty()) api_level = store.get("ro.build.version.sdk"); if (atoi(api_level.c_str()) <= 27) ret = u_hardware_biometry_setActiveGroup(hybris_fp_instance, 0, (char*)"/data/system/users/0/fpdata/"); else ret = u_hardware_biometry_setActiveGroup(hybris_fp_instance, 0, (char*)"/data/vendor_de/0/fpdata/"); if (ret != SYS_OK) printf("setActiveGroup failed: %s\n", IntToStringRequestStatus(ret).c_str()); std::string fake_finger_quirks = store.get("ro.halium.fingerprint.fakefingerquirks"); if (fake_finger_quirks == "1") template_store_.enable_fake_finger_quirks(); } biometry::TemplateStore& biometry::devices::android::template_store() { return template_store_; } biometry::Identifier& biometry::devices::android::identifier() { return identifier_; } biometry::Verifier& biometry::devices::android::verifier() { return verifier_; } namespace { struct androidDescriptor : public biometry::Device::Descriptor { std::shared_ptr create(const biometry::util::Configuration&) override { return std::make_shared(u_hardware_biometry_new()); } std::string name() const override { return "Android HAL Bridge"; } std::string author() const override { return "Erfan Abdi (erfangplus@gmail.com)"; } std::string description() const override { return "android is a biometry::Device implementation for connecting to android fp hal using hybris."; } }; } biometry::Device::Descriptor::Ptr biometry::devices::android::make_descriptor() { return std::make_shared(); } biometryd-0.3.1/src/biometry/devices/android.h000066400000000000000000000067401455450034500213610ustar00rootroot00000000000000/* * Copyright (C) 2020 UBports foundation, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Erfan Abdi * */ #ifndef BIOMETRYD_DEVICES_ANDROID_H_ #define BIOMETRYD_DEVICES_ANDROID_H_ #include #include #include #include #include namespace biometry { namespace devices { /// @brief android is a biometry::Device. class BIOMETRY_DLL_PUBLIC android : public biometry::Device { public: static constexpr const char* id{"android"}; class TemplateStore : public biometry::TemplateStore { public: TemplateStore(UHardwareBiometry hybris_fp_instance); // From biometry::TemplateStore. biometry::Operation::Ptr size(const biometry::Application& app, const biometry::User& user) override; biometry::Operation::Ptr list(const biometry::Application& app, const biometry::User& user) override; biometry::Operation::Ptr enroll(const biometry::Application& app, const biometry::User& user) override; biometry::Operation::Ptr remove(const biometry::Application& app, const biometry::User& user, biometry::TemplateStore::TemplateId id) override; biometry::Operation::Ptr clear(const biometry::Application& app, const biometry::User& user) override; void enable_fake_finger_quirks(); private: UHardwareBiometry hybris_fp_instance; bool fake_finger_quirks; }; class Identifier : public biometry::Identifier { public: Identifier(UHardwareBiometry hybris_fp_instance); // From biometry::Identifier. biometry::Operation::Ptr identify_user(const biometry::Application& app, const biometry::Reason& reason) override; private: UHardwareBiometry hybris_fp_instance; }; class Verifier : public biometry::Verifier { public: Verifier(UHardwareBiometry hybris_fp_instance); // From biometry::Identifier. Operation::Ptr verify_user(const Application& app, const User& user, const Reason& reason) override; private: UHardwareBiometry hybris_fp_instance; }; /// @brief make_descriptor returns a descriptor instance describing a android device; static Descriptor::Ptr make_descriptor(); /// @brief android initializes a new instance. android(UHardwareBiometry hybris_fp_instance); // From biometry::Device biometry::TemplateStore& template_store() override; biometry::Identifier& identifier() override; biometry::Verifier& verifier() override; private: TemplateStore template_store_; Identifier identifier_; Verifier verifier_; }; } } #endif // BIOMETRYD_DEVICES_ANDROID_H_ biometryd-0.3.1/src/biometry/devices/dispatching.cpp000066400000000000000000000121451455450034500225650ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include #include namespace { template class DispatchingOperation : public biometry::Operation { public: DispatchingOperation(const std::shared_ptr& dispatcher, const std::shared_ptr>& impl) : dispatcher{dispatcher}, impl{impl} { } void start_with_observer(const typename biometry::Operation::Observer::Ptr& observer) override { auto i= impl; dispatcher->dispatch([i, observer]() { i->start_with_observer(observer); }); } void cancel() override { // We immediately forward cancel requests as we would be blocked by a stuck operation otherwise. impl->cancel(); } private: std::shared_ptr dispatcher; std::shared_ptr> impl; }; } biometry::devices::Dispatching::TemplateStore::TemplateStore(const std::shared_ptr& dispatcher, const std::shared_ptr& impl) : dispatcher{dispatcher}, impl{impl} { } biometry::Operation::Ptr biometry::devices::Dispatching::TemplateStore::size(const biometry::Application& app, const biometry::User& user) { return std::make_shared>(dispatcher, impl->template_store().size(app, user)); } biometry::Operation::Ptr biometry::devices::Dispatching::TemplateStore::list(const biometry::Application& app, const biometry::User& user) { return std::make_shared>(dispatcher, impl->template_store().list(app, user)); } biometry::Operation::Ptr biometry::devices::Dispatching::TemplateStore::enroll(const biometry::Application& app, const biometry::User& user) { return std::make_shared>(dispatcher, impl->template_store().enroll(app, user)); } biometry::Operation::Ptr biometry::devices::Dispatching::TemplateStore::remove(const biometry::Application& app, const biometry::User& user, biometry::TemplateStore::TemplateId id) { return std::make_shared>(dispatcher, impl->template_store().remove(app, user, id)); } biometry::Operation::Ptr biometry::devices::Dispatching::TemplateStore::clear(const biometry::Application& app, const biometry::User& user) { return std::make_shared>(dispatcher, impl->template_store().clear(app, user)); } biometry::devices::Dispatching::Identifier::Identifier(const std::shared_ptr& dispatcher, const std::shared_ptr& impl) : dispatcher{dispatcher}, impl{impl} { } biometry::Operation::Ptr biometry::devices::Dispatching::Identifier::identify_user(const biometry::Application& app, const biometry::Reason& reason) { return std::make_shared>(dispatcher, impl->identifier().identify_user(app, reason)); } biometry::devices::Dispatching::Verifier::Verifier(const std::shared_ptr& dispatcher, const std::shared_ptr& impl) : dispatcher{dispatcher}, impl{impl} { } biometry::Operation::Ptr biometry::devices::Dispatching::Verifier::verify_user(const biometry::Application& app, const biometry::User& user, const biometry::Reason& reason) { return std::make_shared>(dispatcher, impl->verifier().verify_user(app, user, reason)); } biometry::devices::Dispatching::Dispatching(const std::shared_ptr& dispatcher, const std::shared_ptr& device) : template_store_{dispatcher, device}, identifier_{dispatcher, device}, verifier_{dispatcher, device} { } biometry::TemplateStore& biometry::devices::Dispatching::template_store() { return template_store_; } biometry::Identifier& biometry::devices::Dispatching::identifier() { return identifier_; } biometry::Verifier& biometry::devices::Dispatching::verifier() { return verifier_; } biometryd-0.3.1/src/biometry/devices/dispatching.h000066400000000000000000000077521455450034500222420ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_DEVICES_DISPATCHING_H_ #define BIOMETRYD_DEVICES_DISPATCHING_H_ #include #include #include #include #include #include #include namespace biometry { namespace devices { /// @brief Dispatching is a biometry::Device that dispatches calls to a second biometry::Device implementation via an executor. class BIOMETRY_DLL_PUBLIC Dispatching : public biometry::Device { public: // Safe us some typing. typedef std::shared_ptr Ptr; class TemplateStore : public biometry::TemplateStore { public: TemplateStore(const std::shared_ptr& dispatcher, const std::shared_ptr& impl); // From biometry::TemplateStore. biometry::Operation::Ptr size(const biometry::Application& app, const biometry::User& user) override; biometry::Operation::Ptr list(const biometry::Application& app, const biometry::User& user) override; biometry::Operation::Ptr enroll(const biometry::Application& app, const biometry::User& user) override; biometry::Operation::Ptr remove(const biometry::Application& app, const biometry::User& user, biometry::TemplateStore::TemplateId id) override; biometry::Operation::Ptr clear(const biometry::Application& app, const biometry::User& user) override; private: std::shared_ptr dispatcher; std::shared_ptr impl; }; class Identifier : public biometry::Identifier { public: Identifier(const std::shared_ptr& dispatcher, const std::shared_ptr& impl); // From biometry::Identifier. biometry::Operation::Ptr identify_user(const biometry::Application& app, const biometry::Reason& reason) override; private: std::shared_ptr dispatcher; std::shared_ptr impl; }; class Verifier : public biometry::Verifier { public: Verifier(const std::shared_ptr& dispatcher, const std::shared_ptr& impl); // From biometry::Identifier. Operation::Ptr verify_user(const Application& app, const User& user, const Reason& reason) override; private: std::shared_ptr dispatcher; std::shared_ptr impl; }; /// @brief Forwarding creates a new instance, forwarding calls to device. /// @throws std::runtime_error if device is null. Dispatching(const std::shared_ptr& dispatcher, const std::shared_ptr& device); // From biometry::Device biometry::TemplateStore& template_store() override; biometry::Identifier& identifier() override; biometry::Verifier& verifier() override; private: TemplateStore template_store_; Identifier identifier_; Verifier verifier_; }; } } #endif // BIOMETRYD_DEVICES_DISPATCHING_H_ biometryd-0.3.1/src/biometry/devices/dummy.cpp000066400000000000000000000070571455450034500214310ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include #include biometry::Operation::Ptr biometry::devices::Dummy::TemplateStore::size(const biometry::Application&, const biometry::User&) { return std::make_shared>(); } biometry::Operation::Ptr biometry::devices::Dummy::TemplateStore::list(const biometry::Application&, const biometry::User&) { return std::make_shared>(); } biometry::Operation::Ptr biometry::devices::Dummy::TemplateStore::enroll(const biometry::Application&, const biometry::User&) { return std::make_shared>(); } biometry::Operation::Ptr biometry::devices::Dummy::TemplateStore::remove(const biometry::Application&, const biometry::User&, biometry::TemplateStore::TemplateId) { return std::make_shared>(); } biometry::Operation::Ptr biometry::devices::Dummy::TemplateStore::clear(const biometry::Application&, const biometry::User&) { return std::make_shared>(); } biometry::Operation::Ptr biometry::devices::Dummy::Identifier::identify_user(const biometry::Application&, const biometry::Reason&) { return std::make_shared>(); } biometry::Operation::Ptr biometry::devices::Dummy::Verifier::verify_user(const Application&, const User&, const Reason&) { return std::make_shared>(); } biometry::devices::Dummy::Dummy() { } biometry::TemplateStore& biometry::devices::Dummy::template_store() { return template_store_; } biometry::Identifier& biometry::devices::Dummy::identifier() { return identifier_; } biometry::Verifier& biometry::devices::Dummy::verifier() { return verifier_; } namespace { struct DummyDescriptor : public biometry::Device::Descriptor { std::shared_ptr create(const biometry::util::Configuration&) override { return std::make_shared(); } std::string name() const override { return "Dummy"; } std::string author() const override { return "Thomas Voß (thomas.voss@canonical.com)"; } std::string description() const override { return "Dummy is a biometry::Device implementation for tesing purposes."; } }; } biometry::Device::Descriptor::Ptr biometry::devices::Dummy::make_descriptor() { return std::make_shared(); } biometryd-0.3.1/src/biometry/devices/dummy.h000066400000000000000000000065541455450034500210770ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_DEVICES_DUMMY_H_ #define BIOMETRYD_DEVICES_DUMMY_H_ #include #include #include #include namespace biometry { namespace devices { /// @brief Dummy is a biometry::Device. class BIOMETRY_DLL_PUBLIC Dummy : public biometry::Device { public: static constexpr const char* id{"Dummy"}; template struct Operation : public biometry::Operation { void start_with_observer(const typename biometry::Operation::Observer::Ptr& observer) override { observer->on_started(); typename biometry::Operation::Result result{}; observer->on_succeeded(result); } void cancel() override { } }; class TemplateStore : public biometry::TemplateStore { public: // From biometry::TemplateStore. biometry::Operation::Ptr size(const biometry::Application& app, const biometry::User& user) override; biometry::Operation::Ptr list(const biometry::Application& app, const biometry::User& user) override; biometry::Operation::Ptr enroll(const biometry::Application& app, const biometry::User& user) override; biometry::Operation::Ptr remove(const biometry::Application& app, const biometry::User& user, biometry::TemplateStore::TemplateId id) override; biometry::Operation::Ptr clear(const biometry::Application& app, const biometry::User& user) override; }; class Identifier : public biometry::Identifier { public: // From biometry::Identifier. biometry::Operation::Ptr identify_user(const biometry::Application& app, const biometry::Reason& reason) override; }; class Verifier : public biometry::Verifier { public: // From biometry::Identifier. Operation::Ptr verify_user(const Application& app, const User& user, const Reason& reason) override; }; /// @brief make_descriptor returns a descriptor instance describing a Dummy device; static Descriptor::Ptr make_descriptor(); /// @brief Dummy initializes a new instance. Dummy(); // From biometry::Device biometry::TemplateStore& template_store() override; biometry::Identifier& identifier() override; biometry::Verifier& verifier() override; private: TemplateStore template_store_; Identifier identifier_; Verifier verifier_; }; } } #endif // BIOMETRYD_DEVICES_DUMMY_H_ biometryd-0.3.1/src/biometry/devices/fingerprint_reader.cpp000066400000000000000000000152711455450034500241440ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include namespace { class GuidedEnrollmentOperation : public biometry::Operation { public: class EnrollmentObserver : public biometry::Operation::Observer { public: using Super = biometry::Operation::Observer; explicit EnrollmentObserver(const GuidedEnrollmentOperation::Observer::Ptr& impl) : impl{impl} { } void on_started() override { impl->on_started(); } void on_progress(const Super::Progress& in) override { biometry::devices::FingerprintReader::GuidedEnrollment::Progress out; out.percent = in.percent; out.guidance.from_dictionary(in.details); impl->on_progress(out); } void on_canceled(const Super::Reason& reason) override { impl->on_canceled(reason); } void on_failed(const Super::Error& error) override { impl->on_failed(error); } void on_succeeded(const Super::Result& result) override { impl->on_succeeded(result); } private: GuidedEnrollmentOperation::Observer::Ptr impl; }; explicit GuidedEnrollmentOperation(const biometry::Operation::Ptr& impl) : impl{impl} { } void start_with_observer(const typename Observer::Ptr& observer) override { impl->start_with_observer(std::make_shared(observer)); } void cancel() override { impl->cancel(); } private: biometry::Operation::Ptr impl; }; } void biometry::devices::FingerprintReader::GuidedEnrollment::Hints::from_dictionary(const biometry::Dictionary& dict) { is_finger_present.reset(); if (dict.count(key_is_finger_present) > 0) is_finger_present = dict.at(key_is_finger_present).boolean(); is_main_cluster_identified.reset(); if (dict.count(key_is_main_cluster_identified) > 0) is_main_cluster_identified = dict.at(key_is_main_cluster_identified).boolean(); suggested_next_direction.reset(); if (dict.count(key_suggested_next_direction) > 0) suggested_next_direction = static_cast(dict.at(key_suggested_next_direction).integer()); if (dict.count(key_masks) > 0) { masks = std::vector{}; auto v = dict.at(key_masks).vector(); for (const auto& m : v) masks->push_back(m.rectangle()); } else { masks.reset(); } } biometry::Dictionary biometry::devices::FingerprintReader::GuidedEnrollment::Hints::to_dictionary() const { biometry::Dictionary dict; if (is_finger_present) dict[key_is_finger_present] = biometry::Variant::b(*is_finger_present); if (is_main_cluster_identified) dict[key_is_main_cluster_identified] = biometry::Variant::b(*is_main_cluster_identified); if (suggested_next_direction) dict[key_suggested_next_direction] = biometry::Variant::i(static_cast(*suggested_next_direction)); if (masks) { std::vector v; for (const auto& r : *masks) v.push_back(biometry::Variant::r(r)); dict[key_masks] = biometry::Variant::v(v); } return dict; } biometry::devices::FingerprintReader::TemplateStore::TemplateStore(const std::reference_wrapper& impl) : impl{impl} { } biometry::Operation::Ptr biometry::devices::FingerprintReader::TemplateStore::guided_enroll(const Application& app, const User& user) { return std::make_shared(enroll(app, user)); } biometry::Operation::Ptr biometry::devices::FingerprintReader::TemplateStore::size(const Application& app, const User& user) { return impl.get().size(app, user); } biometry::Operation::Ptr biometry::devices::FingerprintReader::TemplateStore::list(const Application& app, const User& user) { return impl.get().list(app, user); } biometry::Operation::Ptr biometry::devices::FingerprintReader::TemplateStore::enroll(const Application& app, const User& user) { return impl.get().enroll(app, user); } biometry::Operation::Ptr biometry::devices::FingerprintReader::TemplateStore::remove(const Application& app, const User& user, TemplateId id) { return impl.get().remove(app, user, id); } biometry::Operation::Ptr biometry::devices::FingerprintReader::TemplateStore::clear(const Application& app, const User& user) { return impl.get().clear(app, user); } biometry::devices::FingerprintReader::FingerprintReader(const std::shared_ptr& device) : device_{device}, template_store_{std::ref(device_->template_store())} { } biometry::devices::FingerprintReader::TemplateStore& biometry::devices::FingerprintReader::template_store_with_guided_enrollment() { return template_store_; } biometry::TemplateStore& biometry::devices::FingerprintReader::template_store() { return template_store_; } biometry::Identifier& biometry::devices::FingerprintReader::identifier() { return device_->identifier(); } biometry::Verifier& biometry::devices::FingerprintReader::verifier() { return device_->verifier(); } bool biometry::devices::operator==( const FingerprintReader::GuidedEnrollment::Hints& lhs, const FingerprintReader::GuidedEnrollment::Hints& rhs) { return std::tie(lhs.is_main_cluster_identified, lhs.suggested_next_direction, lhs.masks) == std::tie(rhs.is_main_cluster_identified, rhs.suggested_next_direction, rhs.masks); } biometryd-0.3.1/src/biometry/devices/forwarding.cpp000066400000000000000000000024221455450034500224270ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include biometry::devices::Forwarding::Forwarding(const std::shared_ptr& device) : impl{device} { if (not impl) throw std::runtime_error{"Cannot construct Forwarding device for null impl."}; } biometry::TemplateStore& biometry::devices::Forwarding::template_store() { return impl->template_store(); } biometry::Identifier& biometry::devices::Forwarding::identifier() { return impl->identifier(); } biometry::Verifier& biometry::devices::Forwarding::verifier() { return impl->verifier(); } biometryd-0.3.1/src/biometry/devices/forwarding.h000066400000000000000000000027661455450034500221070ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_DEVICES_FORWARDING_H_ #define BIOMETRYD_DEVICES_FORWARDING_H_ #include namespace biometry { namespace devices { /// @brief Forwarding is a biometry::Device that dispatches calls to a second biometry::Device implementation. class BIOMETRY_DLL_PUBLIC Forwarding : public biometry::Device { public: /// @brief Forwarding creates a new instance, forwarding calls to device. /// @throws std::runtime_error if device is null. Forwarding(const std::shared_ptr& device); // From biometry::Device TemplateStore& template_store() override; Identifier& identifier() override; Verifier& verifier() override; private: std::shared_ptr impl; ///< The actual device we are forwarding to. }; } } #endif // BIOMETRYD_DEVICES_FORWARDING_H_ biometryd-0.3.1/src/biometry/devices/plugin/000077500000000000000000000000001455450034500210575ustar00rootroot00000000000000biometryd-0.3.1/src/biometry/devices/plugin/device.cpp000066400000000000000000000037531455450034500230320ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include namespace plugin = biometry::devices::plugin; std::shared_ptr plugin::load(const std::shared_ptr& api, const boost::filesystem::path& path, const Loader& loader) { return loader.verify_and_load(api, path); } #include namespace { struct PluginDescriptor : public biometry::Device::Descriptor { std::shared_ptr create(const biometry::util::Configuration& config) override { auto api = biometry::util::glibc::dl_api(); biometry::devices::plugin::ElfDescriptorVerifierLoader loader; return biometry::devices::plugin::load(api, config["path"].value().string(), loader); } std::string name() const override { return "Plugin"; } std::string author() const override { return "Thomas Voß (thomas.voss@canonical.com)"; } std::string description() const override { return "Plugin loads device implementations from shared modules."; } }; } /// @brief make_descriptor returns a descriptor instance describing a plugin device; biometry::Device::Descriptor::Ptr plugin::make_descriptor() { return std::make_shared(); } biometryd-0.3.1/src/biometry/devices/plugin/device.h000066400000000000000000000033231455450034500224700ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_DEVICES_PLUGIN_DEVICE_H_ #define BIOMETRYD_DEVICES_PLUGIN_DEVICE_H_ #include #include #include namespace biometry { namespace devices { namespace plugin { /// @brief make_descriptor returns a descriptor instance describing a plugin device; BIOMETRY_DLL_PUBLIC biometry::Device::Descriptor::Ptr make_descriptor(); /// @brief id is the unique name for registering the device type with the device registry. static constexpr const char* id{"Plugin"}; /// @cond using Device = biometry::devices::Forwarding; /// @endcond /// @brief load returns a biometry::Device implementation that has been loaded from a shared object located at path, /// relying on api to open the library and resolve symbols. BIOMETRY_DLL_PUBLIC std::shared_ptr load(const std::shared_ptr& api, const boost::filesystem::path& path, const Loader& loader); } } } #endif // BIOMETRYD_DEVICES_PLUGIN_DEVICE_H_ biometryd-0.3.1/src/biometry/devices/plugin/enumerator.cpp000066400000000000000000000074611455450034500237540ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include namespace plugin = biometry::devices::plugin; namespace { struct PluginDeviceDescriptor : public biometry::Device::Descriptor { typedef std::shared_ptr Ptr; PluginDeviceDescriptor(const boost::filesystem::path& path, const plugin::Descriptor& desc) : path{path}, desc(desc) { } std::shared_ptr create(const biometry::util::Configuration&) override { return plugin::ElfDescriptorVerifierLoader{}.verify_and_load(biometry::util::glibc::dl_api(), path); } std::string name() const override { return desc.name; } std::string author() const override { return desc.author; } std::string description() const override { return desc.description; } boost::filesystem::path path; biometry::devices::plugin::Descriptor desc; }; } plugin::DirectoryEnumerator::DirectoryEnumerator(const std::set& directories) : directories{directories} { } std::size_t plugin::DirectoryEnumerator::enumerate(const Functor& f) const { std::size_t invocations{0}; for (const auto& directory : directories) { if (not boost::filesystem::is_directory(directory)) continue; MajorVersionVerifier verifier; ElfDescriptorLoader loader; for (boost::filesystem::directory_iterator it{directory}, itE; it != itE; ++it) { try { auto desc = verifier.verify(loader.load_with_name(it->path(), BIOMETRYD_DEVICES_PLUGIN_DESCRIPTOR_SECTION)); f(std::make_shared(it->path(), desc)); ++invocations; } catch(const ElfDescriptorLoader::FailedToInitializeElf&) { // We silently ignore the exception here as we expect to encounter // files missing a section describing a biometryd plugin. All other // exceptions will be propagated, though. } catch(const ElfDescriptorLoader::NotAnElfObject&) { // We silently ignore the exception here as we expect to encounter // files missing a section describing a biometryd plugin. All other // exceptions will be propagated, though. } catch(const ElfDescriptorLoader::NoSuchSection&) { // We silently ignore the exception here as we expect to encounter // files missing a section describing a biometryd plugin. All other // exceptions will be propagated, though. } catch(const MajorVersionVerifier::MajorVersionMismatch&) { // We silently ignore major version mismatches as we expect to // encounter plugins of different versions routinely. All other // exceptions will be propagated, though. } } } return invocations; } biometryd-0.3.1/src/biometry/devices/plugin/enumerator.h000066400000000000000000000041301455450034500234070ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_DEVICES_PLUGIN_ENUMERATOR_H_ #define BIOMETRYD_DEVICES_PLUGIN_ENUMERATOR_H_ #include #include #include #include #include #include namespace biometry { namespace devices { namespace plugin { /// @brief Enumerator enumerates known plugins. class BIOMETRY_DLL_PUBLIC Enumerator : public DoNotCopyOrMove { public: /// @brief Functor models a function invoked with a plugin descriptor. typedef std::function Functor; /// @brief enumerate invokes f for every plugin known to the Enumerator. /// @return the number of invocations of f. virtual std::size_t enumerate(const Functor& f) const = 0; protected: /// @cond Enumerator() = default; /// @endcond }; /// @brief DirectoryEnumerator implements Enumerator, enumerating all plugins located in a directory. class BIOMETRY_DLL_PUBLIC DirectoryEnumerator : public Enumerator { public: /// @brief DirectoryEnumerator initializes a new instance with the given directory. explicit DirectoryEnumerator(const std::set& directories); // From Enumerator. std::size_t enumerate(const Functor& f) const override; private: std::set directories; }; } } } #endif // BIOMETRYD_DEVICES_PLUGIN_ENUMERATOR_H_ biometryd-0.3.1/src/biometry/devices/plugin/loader.cpp000066400000000000000000000105051455450034500230320ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include #include #include #include #include #include #include namespace plugin = biometry::devices::plugin; std::shared_ptr plugin::NonVerifyingLoader::verify_and_load(const std::shared_ptr& api, const boost::filesystem::path& path) const { auto dl = std::make_shared(api, path); util::DynamicLibrary::Symbol create = dl->resolve_symbol_or_throw(BIOMETRYD_DEVICES_PLUGIN_CREATE_SYMBOL_NAME); util::DynamicLibrary::Symbol destroy = dl->resolve_symbol_or_throw(BIOMETRYD_DEVICES_PLUGIN_DESTROY_SYMBOL_NAME); return std::shared_ptr{ create.as()(), // We are passing in dl to keep the instance alive for as long as the // shared plugin instance is alive. [dl, destroy](biometry::Device* dev) { if (dev) destroy.as()(dev); } }; } namespace { /// @brief Fd performs resource handling of an opaque file descriptor. struct Fd { /// @brief Constructs a new instance, taking over ownership of handle. /// @throws std::system_error if the handle is < 0. Fd(int handle) : handle{handle} { if (handle < 0) throw std::system_error(errno, std::system_category()); } ~Fd() { if (handle >= 0) ::close(handle); } int handle; }; } plugin::ElfDescriptorLoader::FailedToInitializeElf::FailedToInitializeElf() : std::runtime_error{(boost::format("Failed to initialize elf: %1%") % elf_errmsg(-1)).str()} { } plugin::ElfDescriptorLoader::NotAnElfObject::NotAnElfObject() : std::runtime_error{"Not an elf object"} { } plugin::ElfDescriptorLoader::NoSuchSection::NoSuchSection(const std::string& section) : std::runtime_error{"Failed to resolve section: " + section} { } plugin::Descriptor plugin::ElfDescriptorLoader::load_with_name(const boost::filesystem::path& p, const std::string& section) const { if (elf_version( EV_CURRENT ) == EV_NONE) throw std::runtime_error{"elf version mismatch"}; Fd fd{::open(p.string().c_str(), O_RDONLY)}; Elf* e{nullptr}; if (not (e = elf_begin(fd.handle, ELF_C_READ, nullptr))) throw FailedToInitializeElf{}; if (elf_kind(e) != ELF_K_ELF) throw NotAnElfObject{}; std::size_t shstrndx{0}; if (elf_getshdrstrndx(e, &shstrndx) != 0) throw std::runtime_error{"Failed to retrieve index of the section name string table."}; Elf_Scn* sec{nullptr}; while ((sec = elf_nextscn(e, sec)) != nullptr) { GElf_Shdr hdr; if (&hdr != gelf_getshdr(sec, &hdr)) continue; char* name{nullptr}; if ((name = elf_strptr(e, shstrndx, hdr.sh_name)) == nullptr) continue; if (name != section) continue; Elf_Data* data{nullptr}; if ((data = elf_getdata(sec, data)) && data->d_size == sizeof(plugin::Descriptor)) return *reinterpret_cast(data->d_buf); } throw NoSuchSection(section); } std::shared_ptr plugin::ElfDescriptorVerifierLoader::verify_and_load(const std::shared_ptr& api, const boost::filesystem::path& path) const { MajorVersionVerifier{}.verify(ElfDescriptorLoader{}.load_with_name(path, BIOMETRYD_DEVICES_PLUGIN_DESCRIPTOR_SECTION)); return NonVerifyingLoader{}.verify_and_load(api, path); } biometryd-0.3.1/src/biometry/devices/plugin/loader.h000066400000000000000000000074531455450034500225070ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_DEVICES_PLUGIN_LOADER_H_ #define BIOMETRYD_DEVICES_PLUGIN_LOADER_H_ #include #include #include #include #include namespace biometry { namespace devices { namespace plugin { /// @brief Loader provides means to verify (prior to loading) and loading a biometry::Device impl from a dynamic library. class BIOMETRY_DLL_PUBLIC Loader : public DoNotCopyOrMove { public: /// @brief verify_and_load returns a biometry::Device instance resolved from the dynamic library pointed to by path. /// @throws util::DynamicLibrary::Error in case of issues accessing the dynamic library. virtual std::shared_ptr verify_and_load(const std::shared_ptr& api, const boost::filesystem::path& path) const = 0; protected: /// @cond Loader() = default; /// @endcond }; /// @brief NonVerifyingLoader implements Loader, skipping the verification step altogether. struct BIOMETRY_DLL_PUBLIC NonVerifyingLoader : public Loader { /// @brief verify_and_load tries to load a Device plugin from the dynamic library located at path, leveraging api to /// open the library and resolve symbols from it. std::shared_ptr verify_and_load(const std::shared_ptr& api, const boost::filesystem::path& path) const override; }; /// @brief ElfDescriptorLoader loads a plugin::Descriptor instance from a dynamic library. struct BIOMETRY_DLL_PUBLIC ElfDescriptorLoader { public: /// @brief FailedToInitializeElf is thrown if the loader fails to initialize the elf access library for a given file. struct FailedToInitializeElf : public std::runtime_error { FailedToInitializeElf(); }; /// @brief NotAnElfObject is thrown if the loader is invoked on a file that is not an elf object. struct NotAnElfObject : public std::runtime_error { NotAnElfObject(); }; /// @brief NoSuchSection is thrown if a named section in an elf executable cannot be found. struct NoSuchSection : public std::runtime_error { NoSuchSection(const std::string& section); }; /// @brief load_with_name opens the dynamic library located at p, tries to find section section, interpreting it as a plugin::Descriptor on return. /// @throws NoSuchSection if a section of the given name cannot be found. plugin::Descriptor load_with_name(const boost::filesystem::path& p, const std::string& section) const; }; /// @brief ElfDescriptorVerifierLoader checks if the biometry::Device plugin contained in the library /// located at path is binary compatible with the current runtime version of biometryd. If so, it tries /// to load the library and create a biometry::Device instance from it. struct BIOMETRY_DLL_PUBLIC ElfDescriptorVerifierLoader : public Loader { std::shared_ptr verify_and_load(const std::shared_ptr& api, const boost::filesystem::path& path) const override; }; } } } #endif // BIOMETRYD_DEVICES_PLUGIN_LOADER_H_ biometryd-0.3.1/src/biometry/devices/plugin/verifier.cpp000066400000000000000000000025701455450034500234020ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include namespace plugin = biometry::devices::plugin; plugin::MajorVersionVerifier::MajorVersionMismatch::MajorVersionMismatch(std::uint32_t host, std::uint32_t plugin) : std::runtime_error{(boost::format("Major version mismatch on host: %1% vs. %2%") % host % plugin).str()}, host{host}, plugin{plugin} { } plugin::Descriptor plugin::MajorVersionVerifier::verify(const Descriptor &descriptor) const { if (descriptor.version.host.major != biometry::build::version_major) throw MajorVersionMismatch{descriptor.version.host.major, biometry::build::version_major}; return descriptor; } biometryd-0.3.1/src/biometry/devices/plugin/verifier.h000066400000000000000000000037501455450034500230500ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_DEVICES_PLUGIN_VERIFIER_H_ #define BIOMETRYD_DEVICES_PLUGIN_VERIFIER_H_ #include #include #include #include #include namespace biometry { namespace devices { namespace plugin { /// @brief A Verifier verifies that a plugin fits with biometryd. class BIOMETRY_DLL_PUBLIC Verifier : public DoNotCopyOrMove { public: /// @brief verify returns the given descriptor if the corresponding plugin is likely to fit with the currently /// running biometryd version. virtual Descriptor verify(const Descriptor& descriptor) const = 0; protected: /// @cond Verifier() = default; /// @endcond }; struct BIOMETRY_DLL_PUBLIC MajorVersionVerifier : public Verifier { /// @brief MajorVersionMismatch is thrown if the major version of host and plugin do not match. struct BIOMETRY_DLL_PUBLIC MajorVersionMismatch : public std::runtime_error { MajorVersionMismatch(std::uint32_t host, std::uint32_t plugin); const std::uint32_t host; const std::uint32_t plugin; }; // From MajorVersionVerifier Descriptor verify(const Descriptor &descriptor) const override; }; } } } #endif // BIOMETRYD_DEVICES_PLUGIN_VERIFIER_H_ biometryd-0.3.1/src/biometry/dispatching_service.cpp000066400000000000000000000022501455450034500226570ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include biometry::DispatchingService::DispatchingService(const std::shared_ptr& dispatcher, const std::shared_ptr& default_device) : default_device_{std::make_shared(dispatcher, default_device)} { } std::shared_ptr biometry::DispatchingService::default_device() const { return default_device_; } biometryd-0.3.1/src/biometry/dispatching_service.h000066400000000000000000000027761455450034500223410ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_DISPATCHING_SERVICE_H_ #define BIOMETRYD_DISPATCHING_SERVICE_H_ #include #include #include #include namespace biometry { /// @brief DispatchingService is biometry::Service wrapping device impls as a devices::Dispatching device. class BIOMETRY_DLL_PUBLIC DispatchingService : public Service { public: /// @brief DispatchingService initializes a new instance with the given default_device. DispatchingService(const std::shared_ptr& dispatcher, const std::shared_ptr& default_device); // From Service. std::shared_ptr default_device() const override; protected: devices::Dispatching::Ptr default_device_; }; } #endif // BIOMETRYD_DISPATCHING_SERVICE_H_ biometryd-0.3.1/src/biometry/geometry.cpp000066400000000000000000000035731455450034500205060ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include namespace { double ensure_bounds(double value) { if (value < 0 || value > 1) throw std::runtime_error{"Bounds exceeded"}; return value; } } biometry::Point::Point(double x, double y) : x{ensure_bounds(x)}, y{ensure_bounds(y)} { } bool biometry::operator==(const biometry::Point& lhs, const biometry::Point& rhs) { return lhs.x == rhs.x && lhs.y == rhs.y; } bool biometry::operator!=(const biometry::Point& lhs, const biometry::Point& rhs) { return not (lhs == rhs); } bool biometry::operator==(const Rectangle& lhs, const Rectangle& rhs) { return lhs.top_left == rhs.top_left && lhs.bottom_right == rhs.bottom_right; } bool biometry::operator!=(const Rectangle& lhs, const Rectangle& rhs) { return not (lhs == rhs); } std::ostream& biometry::operator<<(std::ostream& out, const biometry::Point& point) { return out << "(" << point.x << "," << point.y << ")"; } /// @brief operator<< inserts rectangle into out. std::ostream& biometry::operator<<(std::ostream& out, const biometry::Rectangle& rectangle) { return out << "(" << rectangle.top_left << "," << rectangle.bottom_right << ")"; } biometryd-0.3.1/src/biometry/hardware/000077500000000000000000000000001455450034500177345ustar00rootroot00000000000000biometryd-0.3.1/src/biometry/hardware/android_hw_module.h000066400000000000000000000034041455450034500235710ustar00rootroot00000000000000/* * Copyright (C) 2012 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voss * Ricardo Mendoza */ #ifndef ANDROID_HW_MODULE_H_ #define ANDROID_HW_MODULE_H_ #include #ifdef __cplusplus extern "C" { #endif extern void *android_dlopen(const char *filename, int flag); extern void *android_dlsym(void *handle, const char *symbol); #ifdef __cplusplus } #endif namespace internal { struct HIDDEN_SYMBOL ToHybris { static const char* path() { static const char* cache = "libbiometry_fp_api.so"; return cache; } static const char* override_path() { return NULL; } static void* dlopen_fn(const char* path, int flags) { return android_dlopen(path, flags); } static void* dlsym_fn(void* handle, const char* symbol) { return android_dlsym(handle, symbol); } }; } #define DLSYM(fptr, sym) if (*(fptr) == NULL) \ {\ *((void**)fptr) = (void *) internal::Bridge::instance().resolve_symbol(sym);\ } #include #endif // ANDROID_HW_MODULE_H_ biometryd-0.3.1/src/biometry/hardware/biometry_fp_api.cpp000066400000000000000000000036641455450034500236210ustar00rootroot00000000000000/* * Copyright (C) 2020 UBports foundation Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Erfan Abdi */ // C APIs #include #include IMPLEMENT_FUNCTION0( UHardwareBiometry, u_hardware_biometry_new) IMPLEMENT_FUNCTION2( uint64_t, u_hardware_biometry_setNotify, UHardwareBiometry, UHardwareBiometryParams*) IMPLEMENT_FUNCTION1( uint64_t, u_hardware_biometry_preEnroll, UHardwareBiometry) IMPLEMENT_FUNCTION4( UHardwareBiometryRequestStatus, u_hardware_biometry_enroll, UHardwareBiometry, uint32_t, uint32_t, uint32_t) IMPLEMENT_FUNCTION1( UHardwareBiometryRequestStatus, u_hardware_biometry_postEnroll, UHardwareBiometry) IMPLEMENT_FUNCTION1( uint64_t, u_hardware_biometry_getAuthenticatorId, UHardwareBiometry) IMPLEMENT_FUNCTION1( UHardwareBiometryRequestStatus, u_hardware_biometry_cancel, UHardwareBiometry) IMPLEMENT_FUNCTION1( UHardwareBiometryRequestStatus, u_hardware_biometry_enumerate, UHardwareBiometry) IMPLEMENT_FUNCTION3( UHardwareBiometryRequestStatus, u_hardware_biometry_remove, UHardwareBiometry, uint32_t, uint32_t) IMPLEMENT_FUNCTION3( UHardwareBiometryRequestStatus, u_hardware_biometry_setActiveGroup, UHardwareBiometry, uint32_t, char*) IMPLEMENT_FUNCTION3( UHardwareBiometryRequestStatus, u_hardware_biometry_authenticate, UHardwareBiometry, uint64_t, uint32_t) biometryd-0.3.1/src/biometry/percent.cpp000066400000000000000000000030141455450034500203010ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include namespace { double throw_if_out_of_range(double value) { if (value < 0 || value > 1) throw std::runtime_error{std::to_string(value) + " not in [0,1]"}; return value; } } biometry::Percent::Percent() : value{-1} { } biometry::Percent biometry::Percent::from_raw_value(double value) { return biometry::Percent{value}; } biometry::Percent::Percent(double value) : value{throw_if_out_of_range(value)} { } double biometry::Percent::operator*() const { return value; } std::ostream& biometry::operator<<(std::ostream& out, const biometry::Percent& percent) { return out << *percent * 100 << "%"; } bool biometry::operator==(const biometry::Percent& lhs, const biometry::Percent& rhs) { return !(*lhs < *rhs) && !(*lhs > *rhs); } biometryd-0.3.1/src/biometry/progress.cpp000066400000000000000000000024441455450034500205130ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include biometry::Progress biometry::Progress::none() { return Progress{Percent::from_raw_value(0.), Dictionary{}}; } std::ostream& biometry::operator<<(std::ostream& out, const biometry::Progress& progress) { out << "Progress: " << progress.percent; for (const auto& pair : progress.details) out << std::endl << " " << pair.first << ": " << pair.second; return out; } bool biometry::operator==(const biometry::Progress& lhs, const biometry::Progress& rhs) { return lhs.percent == rhs.percent && lhs.details == rhs.details; } biometryd-0.3.1/src/biometry/qml/000077500000000000000000000000001455450034500167305ustar00rootroot00000000000000biometryd-0.3.1/src/biometry/qml/Biometryd/000077500000000000000000000000001455450034500206665ustar00rootroot00000000000000biometryd-0.3.1/src/biometry/qml/Biometryd/CMakeLists.txt000066400000000000000000000035111455450034500234260ustar00rootroot00000000000000set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -pedantic -Wextra -fPIC -fvisibility=hidden -pthread") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall -fno-strict-aliasing -pedantic -Wextra -fPIC -pthread") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined") if(ENABLE_WERROR) add_compile_options("-Werror") endif() find_package(Qt5Core 5.4 REQUIRED) find_package(Qt5Qml 5.4 REQUIRED) find_package(Qt5Quick 5.4 REQUIRED) set(CMAKE_AUTOMOC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) add_library( biometryd-qml SHARED device.h device.cpp converter.h converter.cpp fingerprint_reader.h fingerprint_reader.cpp identifier.h identifier.cpp operation.h operation.cpp service.h service.cpp template_store.h template_store.cpp user.h user.cpp plugin.h plugin.cpp) target_link_libraries(biometryd-qml biometry Qt5::Core Qt5::Qml Qt5::Quick) # We make the module available to qml tests without # requiring installation of the module. With that, we # can test the module within the build env. configure_file(qmldir qmldir COPYONLY) set(PLUGIN_DIR ${CMAKE_INSTALL_LIBDIR}/qt5/qml/Biometryd) install( TARGETS biometryd-qml DESTINATION ${PLUGIN_DIR}) install( FILES qmldir DESTINATION ${PLUGIN_DIR}) if (NOT CMAKE_CROSSCOMPILING) add_custom_target( Content_generated_files ALL SOURCES ${CMAKE_CURRENT_BINARY_DIR}/plugins.qmltypes) add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/plugins.qmltypes COMMAND qmlplugindump -notrelocatable Biometryd 0.0 ../ > ${CMAKE_CURRENT_BINARY_DIR}/plugins.qmltypes DEPENDS biometryd-qml WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" ) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/plugins.qmltypes DESTINATION ${PLUGIN_DIR}) endif() biometryd-0.3.1/src/biometry/qml/Biometryd/converter.cpp000066400000000000000000000027251455450034500234070ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include QRectF biometry::qml::Converter::convert(const biometry::Rectangle& rect) { return QRectF { rect.top_left.x, rect.top_left.y, rect.bottom_right.x - rect.top_left.x, rect.bottom_right.y - rect.top_left.y }; } QVariantList biometry::qml::Converter::convert(const std::vector& rects) { QVariantList result; for (const auto& r : rects) result << QVariant{convert(r)}; return result; } biometry::qml::FingerprintReader::Direction biometry::qml::Converter::convert(biometry::devices::FingerprintReader::Direction dir) { return static_cast(dir); } biometryd-0.3.1/src/biometry/qml/Biometryd/converter.h000066400000000000000000000031201455450034500230420ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_QML_CONVERTER_H_ #define BIOMETRYD_QML_CONVERTER_H_ #include #include #include #include namespace biometry { struct Rectangle; namespace qml { /// @brief Converter maps biometry::* to Qt- and biometry::qml::* types. struct Converter { Converter() = delete; /// @brief convert returns a QRect with the same coordinates as rect. static QRectF convert(const biometry::Rectangle& rect); /// @brief convert returns a QList containing the rectangles given in rects. static QVariantList convert(const std::vector& rects); /// @brief convert returns the enum value correspondig to dir. static FingerprintReader::Direction convert(biometry::devices::FingerprintReader::Direction dir); }; } } #endif // BIOMETRYD_QML_CONVERTER_H_ biometryd-0.3.1/src/biometry/qml/Biometryd/device.cpp000066400000000000000000000023421455450034500226320ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include biometry::qml::Device::Device(const std::shared_ptr& impl, QObject* parent) : QObject{parent}, impl{impl} { } biometry::qml::TemplateStore* biometry::qml::Device::templateStore() { return new TemplateStore{impl->template_store(), this}; } biometry::qml::Identifier* biometry::qml::Device::identifier() { return new Identifier{impl->identifier(), this}; } biometryd-0.3.1/src/biometry/qml/Biometryd/device.h000066400000000000000000000033611455450034500223010ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_QML_DEVICE_H_ #define BIOMETRYD_QML_DEVICE_H_ #include #include #include #include namespace biometry { namespace qml { /// @cond class Identifier; class TemplateStore; /// @endcond /// @brief Device models a biometric device. /// @ingroup qml class BIOMETRY_DLL_PUBLIC Device : public QObject { Q_OBJECT Q_PROPERTY(biometry::qml::TemplateStore* templateStore READ templateStore CONSTANT) Q_PROPERTY(biometry::qml::Identifier* identifier READ identifier CONSTANT) public: /// @brief Device initializes a new instance with device and parent. Device(const std::shared_ptr& impl, QObject* parent); /// @brief templateStore returns a TemplateStore instance. biometry::qml::TemplateStore* templateStore(); /// @brief identifier returns an Identifier instance. biometry::qml::Identifier* identifier(); private: /// @cond std::shared_ptr impl; /// @endcond }; } } #endif // BIOMETRYD_QML_DEVICE_H_ biometryd-0.3.1/src/biometry/qml/Biometryd/fingerprint_reader.cpp000066400000000000000000000036651455450034500252550ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include biometry::qml::FingerprintReader::FingerprintReader(QObject* parent) : QObject{parent} { } QString biometry::qml::FingerprintReader::isFingerPresent() const { static const QString s { biometry::devices::FingerprintReader::GuidedEnrollment::Hints::key_is_finger_present }; return s; } QString biometry::qml::FingerprintReader::hasMainClusterIdentified() const { static const QString s { biometry::devices::FingerprintReader::GuidedEnrollment::Hints::key_is_main_cluster_identified }; return s; } QString biometry::qml::FingerprintReader::suggestedNextDirection() const { static const QString s { biometry::devices::FingerprintReader::GuidedEnrollment::Hints::key_suggested_next_direction }; return s; } QString biometry::qml::FingerprintReader::estimatedFingerSize() const { static const QString s { biometry::devices::FingerprintReader::GuidedEnrollment::Hints::key_estimated_finger_size }; return s; } QString biometry::qml::FingerprintReader::masks() const { static const QString s { biometry::devices::FingerprintReader::GuidedEnrollment::Hints::key_masks }; return s; } biometryd-0.3.1/src/biometry/qml/Biometryd/fingerprint_reader.h000066400000000000000000000061471455450034500247200ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_QML_FINGERPRINT_READER_H_ #define BIOMETRYD_QML_FINGERPRINT_READER_H_ #include #include #include #include namespace biometry { namespace qml { /// @brief A FingerprintReader bundles well-known names of hints passed to Observers. class FingerprintReader : public QObject { Q_OBJECT Q_ENUMS(Direction) Q_PROPERTY(QString isFingerPresent READ isFingerPresent) Q_PROPERTY(QString hasMainClusterIdentified READ hasMainClusterIdentified) Q_PROPERTY(QString suggestedNextDirection READ suggestedNextDirection) Q_PROPERTY(QString masks READ masks) public: /// @brief Direction enumerates all known direction hints. enum class Direction { NotAvailable = int(biometry::devices::FingerprintReader::Direction::not_available), SouthWest = int(biometry::devices::FingerprintReader::Direction::south_west), South = int(biometry::devices::FingerprintReader::Direction::south), SouthEast = int(biometry::devices::FingerprintReader::Direction::south_east), NorthWest = int(biometry::devices::FingerprintReader::Direction::north_west), North = int(biometry::devices::FingerprintReader::Direction::north), NorthEast = int(biometry::devices::FingerprintReader::Direction::north_east), East = int(biometry::devices::FingerprintReader::Direction::east), West = int(biometry::devices::FingerprintReader::Direction::west) }; /// @brief FingerprintReader initializes a new instance with the given parent. FingerprintReader(QObject* parent = nullptr); /// @brief isFingerPresent returns the key of the isFingerPresent hint. Q_INVOKABLE QString isFingerPresent() const; /// @brief hasMainClusterIdentified returns the key of the hasMainClusterIdentified hint. Q_INVOKABLE QString hasMainClusterIdentified() const; /// @brief suggestedNextDirection returns the key of the suggestedNextDirection hint. Q_INVOKABLE QString suggestedNextDirection() const; /// @brief estimatedFingerSize returns the key of the estimatedFingerSize hint. Q_INVOKABLE QString estimatedFingerSize() const; /// @brief masks returns the key of the masks hint. Q_INVOKABLE QString masks() const; }; } } Q_DECLARE_METATYPE(biometry::qml::FingerprintReader::Direction) #endif // BIOMETRYD_QML_FINGERPRINT_READER_H_ biometryd-0.3.1/src/biometry/qml/Biometryd/identifier.cpp000066400000000000000000000026041455450034500235160ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include #include #include /// @brief Identifier initializes a new instance with impl and parent. biometry::qml::Identifier::Identifier(const std::reference_wrapper& impl, QObject* parent) : QObject{parent}, impl{impl} { } /// @brief size returns an operation querying the size of templates enrolled for user. biometry::qml::Identification* biometry::qml::Identifier::identifyUser() { return new Identification{impl.get().identify_user(biometry::Application::system(), biometry::Reason::unknown()), this}; } biometryd-0.3.1/src/biometry/qml/Biometryd/identifier.h000066400000000000000000000036041455450034500231640ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_QML_IDENTIFIER_H_ #define BIOMETRYD_QML_IDENTIFIER_H_ #include #include #include #include #include namespace biometry { namespace qml { class User; /// @cond class BIOMETRY_DLL_PUBLIC Identification : public TypedOperation { Q_OBJECT public: Identification(const biometry::Operation::Ptr& impl, QObject* parent) : TypedOperation{impl, parent} { } }; /// @endcond /// @brief TemplateStore models a collection of templates. /// @ingroup qml class BIOMETRY_DLL_PUBLIC Identifier : public QObject { Q_OBJECT public: /// @brief Identifier initializes a new instance with impl and parent. Identifier(const std::reference_wrapper& impl, QObject* parent); /// @brief size returns an operation querying the size of templates enrolled for user. Q_INVOKABLE biometry::qml::Identification* identifyUser(); private: /// @cond std::reference_wrapper impl; /// @endcond }; } } #endif // BIOMETRYD_QML_IDENTIFIER_H_ biometryd-0.3.1/src/biometry/qml/Biometryd/operation.cpp000066400000000000000000000016521455450034500233760ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include biometry::qml::Observer::Observer(QObject*) : QObject{} { } biometry::qml::Operation::Operation(QObject* parent) : QObject{parent} { } biometryd-0.3.1/src/biometry/qml/Biometryd/operation.h000066400000000000000000000264121455450034500230440ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_QML_OPERATION_H_ #define BIOMETRYD_QML_OPERATION_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace biometry { namespace qml { namespace traits { template struct Result { static QVariant to_variant(const T&); }; template<> struct Result { static QVariant to_variant(const biometry::Void&) { return QVariant{}; } }; template<> struct Result { static QVariant to_variant(const biometry::User& user) { return QVariant{user.id}; } }; template<> struct Result { static QVariant to_variant(const biometry::TemplateStore::SizeQuery::Result& sq) { return QVariant{sq}; } }; template<> struct Result { static QVariant to_variant(const biometry::TemplateStore::TemplateId& id) { return QVariant{qulonglong{id}}; } }; template<> struct Result> { static QVariant to_variant(const std::vector& ids) { QVariantList lids; for (const auto& id : ids) lids.push_back(Result::to_variant(id)); return QVariant::fromValue(lids); } }; } /// @brief DispatcherWithContext dispatches tasks onto the QCoreApplication main loop. template class BIOMETRY_DLL_PUBLIC DispatcherWithContext : public QObject { private: /// @brief Task is the actual event used for transporting dispatched tasks /// from one thread to the other. class Task : public QEvent { public: /// @brief type returns the QEvent::Type assigned to DispatcherWithContext::Task. static QEvent::Type type() { static const QEvent::Type t = static_cast(QEvent::registerEventType()); return t; } /// @brief Task initializes a new instance with the given null-ary void functor. Task(const std::function& f) : QEvent{Task::type()}, f{f} { } /// @brief run puts the contained functor to execution. void run() { f(); } private: std::function f; }; public: /// @brief Dispatcher initializes a default instance. DispatcherWithContext() = default; /// @brief event processes events of type Task. bool event(QEvent* ev) override { if (ev->type() == Task::type()) { reinterpret_cast(ev)->run(); return true; } return QObject::event(ev); } /// @brief finalize schedules deletion of this instance onto the QCoreApplication main loop. void finalize() { QCoreApplication::instance()->postEvent(this, new Task{[this]() { delete this; }}); } /// @brief dispatch enqueues the given task for execution on the QCoreApplication /// main loop. void dispatch(const std::shared_ptr& ctxt, const std::function& task) { QCoreApplication::instance()->postEvent(this, new Task{[ctxt, task]() { task();}}); } }; /// @brief Observer monitors an Operation. class BIOMETRY_DLL_PUBLIC Observer : public QObject { Q_OBJECT public: /// @brief Observer initializes a new instance with the given parent. explicit Observer(QObject* parent = 0); /// @brief started is emitted when the state changes to started. Q_SIGNAL void started(); /// @brief progressed is emitted when the overall operation progresses towards completion. /// @param percent Overall completion status of the operation, in [0,1], -1 indicates indeterminate. /// @param details Additional details about the operation. Q_SIGNAL void progressed(double percent, const QVariantMap& details); /// @brief canceled is emitted when the operation has been canceled. /// @param reason The human readable reason for cancelling the operation. Q_SIGNAL void canceled(const QString& reason); /// @brief failed is emitted when the operation fails to complete. /// @param reason The human readable reason for the failure. Q_SIGNAL void failed(const QString& reason); /// @brief succeeded is emitted when the operation completes successfully. /// @param result The result of the operation, might be empty. Q_SIGNAL void succeeded(const QVariant& result); }; /// @brief Operation models an arbitrary operation as an observable state machine. /// @ingroup qml /// /// States are as follows: /// -> Armed (the state right after construction) /// start() -> Started /// cancel() -> Cancelled /// -> Failed /// -> Succeeded /// class BIOMETRY_DLL_PUBLIC Operation : public QObject { Q_OBJECT public: /// @brief Operation initializes a new instance with the given impl and parent. explicit Operation(QObject* parent); /// @brief start requests the operation to be started. /// @param observer Receiver for status updates about the operation. /// @return true if the issueing the request succeeded, false otherwise. Q_INVOKABLE virtual bool start(Observer* observer) = 0; /// @brief cancel requests the operation to be aborted. /// @return true if issueing the request succeeded, false otherwise. Q_INVOKABLE virtual bool cancel() = 0; }; /// @brief TypedOperation implements Operation dispatching to a biometry::Operation. template class TypedOperation : public Operation { public: class Observer : public biometry::Operation::Observer, public std::enable_shared_from_this { public: typedef std::shared_ptr Ptr; using typename biometry::Operation::Observer::Progress; using typename biometry::Operation::Observer::Reason; using typename biometry::Operation::Observer::Error; using typename biometry::Operation::Observer::Result; static Ptr create(qml::Observer* observer) { return Ptr{new Observer{observer}}; } ~Observer() { // observer is owned by the QML Engine. With that, // we only need to take care of our dispatcher instance. dispatcher->finalize(); } void on_started() override { dispatcher->dispatch(Observer::shared_from_this(), [this]() { if (observer) QMetaObject::invokeMethod(observer, "started", Qt::AutoConnection); }); } void on_progress(const Progress& progress) override { QVariantMap vm; biometry::devices::FingerprintReader::GuidedEnrollment::Hints hints; hints.from_dictionary(progress.details); if (hints.is_finger_present) vm[biometry::devices::FingerprintReader::GuidedEnrollment::Hints::key_is_finger_present] = *hints.is_finger_present; if (hints.is_main_cluster_identified) vm[biometry::devices::FingerprintReader::GuidedEnrollment::Hints::key_is_main_cluster_identified] = *hints.is_main_cluster_identified; if (hints.suggested_next_direction) vm[biometry::devices::FingerprintReader::GuidedEnrollment::Hints::key_suggested_next_direction].setValue( Converter::convert(*hints.suggested_next_direction)); if (hints.masks) vm[biometry::devices::FingerprintReader::GuidedEnrollment::Hints::key_masks] = Converter::convert(*hints.masks); dispatcher->dispatch(Observer::shared_from_this(), [this, progress, vm]() { if (observer) QMetaObject::invokeMethod(observer, "progressed", Qt::AutoConnection, Q_ARG(double, *progress.percent), Q_ARG(QVariantMap, vm)); }); } void on_canceled(const Reason& reason) override { dispatcher->dispatch(Observer::shared_from_this(), [this, reason]() { if (observer) QMetaObject::invokeMethod(observer, "canceled", Qt::AutoConnection, Q_ARG(QString, QString::fromStdString(reason))); }); } void on_failed(const Error& error) override { dispatcher->dispatch(Observer::shared_from_this(), [this, error]() { if (observer) QMetaObject::invokeMethod(observer, "failed", Qt::AutoConnection, Q_ARG(QString, QString::fromStdString(error))); }); } void on_succeeded(const Result& result) override { dispatcher->dispatch(Observer::shared_from_this(), [this, result]() { if (observer) QMetaObject::invokeMethod(observer, "succeeded", Qt::AutoConnection, Q_ARG(QVariant, traits::Result::to_variant(result))); }); } private: Observer(qml::Observer* observer) : observer{observer}, dispatcher{new DispatcherWithContext{}} { } QPointer observer; DispatcherWithContext* dispatcher; }; /// @brief TypedOperation initializes a new instance with the given impl and parent. TypedOperation(const typename biometry::Operation::Ptr& impl, QObject* parent) : Operation{parent}, impl{impl} { } // From operation bool start(qml::Observer* observer) override { try { impl->start_with_observer(Observer::create(observer)); return true; } catch(...) { return false; } } bool cancel() override { try { impl->cancel(); return true; } catch (...) { return false; } } private: /// @cond typename biometry::Operation::Ptr impl; /// @endcond }; } } #endif // BIOMETRYD_QML_OPERATION_H_ biometryd-0.3.1/src/biometry/qml/Biometryd/plugin.cpp000066400000000000000000000342301455450034500226720ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { template std::runtime_error not_implemented(T&&...) { return std::runtime_error{"not implemented"}; } namespace for_testing { std::uint32_t& template_counter() { static std::uint32_t instance{0}; return instance; } // We enable testing of projects using the QML bindings by providing an environment // variable BIOMETRYD_QML_ENABLE_TESTING. If the variable is set to "1", we install a testing // stack emulating the multi-threaded/async behavior of the production system within the test // process. class Dispatcher { public: static Dispatcher& instance() { static Dispatcher dispatcher; return dispatcher; } ~Dispatcher() { service.stop(); if (t.joinable()) t.join(); } void dispatch(const std::function& f) { service.post(f); } private: Dispatcher() : keep_alive{service}, t{[this]() { service.run(); }} { } boost::asio::io_service service; boost::asio::io_service::work keep_alive; std::thread t; }; struct SizeQuery : public biometry::Operation, public std::enable_shared_from_this { void start_with_observer(const typename Observer::Ptr& observer) override { auto thiz = shared_from_this(); Dispatcher::instance().dispatch([observer, thiz]() { observer->on_started(); for (std::size_t i = 1; i <= 100; i++) { if (thiz->canceled) { observer->on_canceled("Canceled due to request from client"); return; } observer->on_progress(biometry::Progress{biometry::Percent::from_raw_value(i/100.f), biometry::Dictionary{}}); std::this_thread::sleep_for(std::chrono::milliseconds{15}); } observer->on_succeeded(template_counter()); }); } void cancel() override { canceled = true; } bool canceled{false}; }; struct List : public biometry::Operation, public std::enable_shared_from_this { void start_with_observer(const typename Observer::Ptr& observer) override { auto thiz = shared_from_this(); Dispatcher::instance().dispatch([observer, thiz]() { observer->on_started(); for (std::size_t i = 1; i <= 100; i++) { if (thiz->canceled) { observer->on_canceled("Canceled due to request from client"); return; } observer->on_progress(biometry::Progress{biometry::Percent::from_raw_value(i/100.f), biometry::Dictionary{}}); std::this_thread::sleep_for(std::chrono::milliseconds{15}); } std::vector ids{template_counter()}; std::iota(ids.begin(), ids.end(), 0); observer->on_succeeded(ids); }); } void cancel() override { canceled = true; } bool canceled{false}; }; struct Enrollment : public biometry::Operation, public std::enable_shared_from_this { void start_with_observer(const typename Observer::Ptr& observer) override { auto thiz = shared_from_this(); Dispatcher::instance().dispatch([observer, thiz]() { observer->on_started(); for (std::size_t i = 1; i <= 100; i++) { if (thiz->canceled) { observer->on_canceled("Canceled due to request from client"); return; } biometry::devices::FingerprintReader::GuidedEnrollment::Hints hints; hints.is_main_cluster_identified = i > 85; hints.is_finger_present = i % 15; hints.suggested_next_direction = static_cast(i % 9); hints.masks = std::vector{ biometry::Rectangle{biometry::Point{10/100.f, 10/100.f}, biometry::Point{50/100.f, 50/100.f}}, biometry::Rectangle{biometry::Point{50/100.f, 50/100.f}, biometry::Point{90/100.f, 90/100.f}} }; observer->on_progress(biometry::Progress{biometry::Percent::from_raw_value(i/100.f), hints.to_dictionary()}); std::this_thread::sleep_for(std::chrono::milliseconds{50}); } observer->on_succeeded(biometry::TemplateStore::TemplateId{template_counter()++}); }); } void cancel() override { canceled = true; } bool canceled{false}; }; struct Identification : public biometry::Operation, public std::enable_shared_from_this { void start_with_observer(const typename Observer::Ptr& observer) override { auto thiz = shared_from_this(); Dispatcher::instance().dispatch([observer, thiz]() { observer->on_started(); for (std::size_t i = 1; i <= 100; i++) { if (thiz->canceled) { observer->on_canceled("Canceled due to request from client"); return; } observer->on_progress(biometry::Progress{biometry::Percent::from_raw_value(i/100.f), biometry::Dictionary{}}); std::this_thread::sleep_for(std::chrono::milliseconds{50}); } observer->on_succeeded(biometry::User{42}); }); } void cancel() override { canceled = true; } bool canceled{false}; }; struct Removal : public biometry::Operation, public std::enable_shared_from_this { Removal(biometry::TemplateStore::TemplateId id) : canceled{false}, id{id} { } void start_with_observer(const typename Observer::Ptr& observer) override { auto thiz = shared_from_this(); Dispatcher::instance().dispatch([observer, thiz, this]() { observer->on_started(); for (std::size_t i = 1; i <= 100; i++) { if (thiz->canceled) { observer->on_canceled("Canceled due to request from client"); return; } observer->on_progress(biometry::Progress{biometry::Percent::from_raw_value(i/100.f), biometry::Dictionary{}}); std::this_thread::sleep_for(std::chrono::milliseconds{15}); } observer->on_succeeded(id); }); } void cancel() override { canceled = true; } bool canceled{false}; biometry::TemplateStore::TemplateId id; }; struct Clearance : public biometry::Operation, public std::enable_shared_from_this { void start_with_observer(const typename Observer::Ptr& observer) override { auto thiz = shared_from_this(); Dispatcher::instance().dispatch([observer, thiz]() { observer->on_started(); for (std::size_t i = 1; i <= 100; i++) { if (thiz->canceled) { observer->on_canceled("Canceled due to request from client"); return; } observer->on_progress(biometry::Progress{biometry::Percent::from_raw_value(i/100.f), biometry::Dictionary{}}); std::this_thread::sleep_for(std::chrono::milliseconds{15}); } template_counter() = 0; observer->on_succeeded(biometry::Void{}); }); } void cancel() override { canceled = true; } bool canceled{false}; }; struct TemplateStore : public biometry::TemplateStore { biometry::Operation::Ptr size(const biometry::Application&, const biometry::User&) override { return std::make_shared(); } biometry::Operation::Ptr list(const biometry::Application&, const biometry::User&) override { return std::make_shared(); } biometry::Operation::Ptr enroll(const biometry::Application&, const biometry::User&) override { return std::make_shared(); } biometry::Operation::Ptr remove(const biometry::Application&, const biometry::User&, biometry::TemplateStore::TemplateId id) override { return std::make_shared(id); } biometry::Operation::Ptr clear(const biometry::Application&, const biometry::User&) override { return std::make_shared(); } }; struct Identifier : public biometry::Identifier { biometry::Operation::Ptr identify_user(const biometry::Application&, const biometry::Reason&) override { return std::make_shared(); } }; struct Device : public biometry::Device { biometry::TemplateStore& template_store() override { return template_store_; } biometry::Identifier& identifier() override { return identifier_; } biometry::Verifier& verifier() override { throw not_implemented(); } TemplateStore template_store_; Identifier identifier_; }; struct Service : public biometry::Service { std::shared_ptr default_device() const { return default_device_; } std::shared_ptr default_device_{std::make_shared()}; }; } } biometry::qml::Plugin::Plugin(QObject* parent) : QQmlExtensionPlugin{parent} { } void biometry::qml::Plugin::registerTypes(const char *uri) { Q_ASSERT(uri == std::string(biometry::qml::Plugin::ns)); qmlRegisterType(uri, Plugin::major, Plugin::minor, "Observer"); qmlRegisterType(uri, Plugin::major, Plugin::minor, "User"); qmlRegisterUncreatableType(uri, Plugin::major, Plugin::minor, "Device", "Rely on Biometryd.instance"); qmlRegisterUncreatableType(uri, Plugin::major, Plugin::minor, "Identification", "Rely on Biometryd.instance"); qmlRegisterUncreatableType(uri, Plugin::major, Plugin::minor, "Identifier", "Rely on Biometryd.instance"); qmlRegisterUncreatableType(uri, Plugin::major, Plugin::minor, "Operation", "Rely on Biometryd.instance"); qmlRegisterUncreatableType(uri, Plugin::major, Plugin::minor, "SizeQuery", "Rely on Biometryd.instance"); qmlRegisterUncreatableType(uri, Plugin::major, Plugin::minor, "List", "Rely on Biometryd.instance"); qmlRegisterUncreatableType(uri, Plugin::major, Plugin::minor, "Enrollment", "Rely on Biometryd.instance"); qmlRegisterUncreatableType(uri, Plugin::major, Plugin::minor, "Removal", "Rely on Biometryd.instance"); qmlRegisterUncreatableType(uri, Plugin::major, Plugin::minor, "Clearance", "Rely on Biometryd.instance"); qmlRegisterUncreatableType(uri, Plugin::major, Plugin::minor, "TemplateStore", "Rely on Biometryd.instance"); qmlRegisterSingletonType( uri, Plugin::major, Plugin::minor, "Biometryd", [](QQmlEngine*, QJSEngine*) -> QObject* { biometry::qml::Service* result{nullptr}; // Trying to create a stub will reach out over the bus to the remote end. // If the remote service is not available, the function throws and we fall back // to a testing implementation again. try { result = new biometry::qml::Service{biometry::dbus::Service::create_stub()}; } catch(...) { result = new biometry::qml::Service{std::make_shared()}; result->setAvailable(qgetenv("BIOMETRYD_QML_ENABLE_TESTING") == QByteArray{"1"}); } return result; }); qmlRegisterSingletonType(uri, Plugin::major, Plugin::minor, "FingerprintReader", [](QQmlEngine*, QJSEngine*) -> QObject* { return new biometry::qml::FingerprintReader{}; }); } biometryd-0.3.1/src/biometry/qml/Biometryd/plugin.h000066400000000000000000000026531455450034500223430ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_QML_PLUGIN_H_ #define BIOMETRYD_QML_PLUGIN_H_ #include #include #include namespace biometry { namespace qml { /// @defgroup qml QML bindings for biometryd /// @cond class BIOMETRY_DLL_PUBLIC Plugin : public QQmlExtensionPlugin { static constexpr const char* ns{"Biometryd"}; static constexpr std::uint32_t major = 0; static constexpr std::uint32_t minor = 0; Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface") public: explicit Plugin(QObject* parent = nullptr); // From QQmlExtensionPlugin void registerTypes(const char *uri) override; }; /// @endcond } } #endif // BIOMETRYD_QML_PLUGIN_H_ biometryd-0.3.1/src/biometry/qml/Biometryd/qmldir000066400000000000000000000000461455450034500221010ustar00rootroot00000000000000module Biometryd plugin biometryd-qml biometryd-0.3.1/src/biometry/qml/Biometryd/service.cpp000066400000000000000000000026421455450034500230360ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include #include biometry::qml::Device* biometry::qml::Service::defaultDevice() const { return defaultDevice_; } bool biometry::qml::Service::isAvailable() const { return available_; } void biometry::qml::Service::setAvailable(bool available) { if (available_ == available) return; Q_EMIT(availableChanged(available_ = available)); } biometry::qml::Service::Service(const std::shared_ptr& impl, QObject* parent) : QObject{parent}, available_{true}, impl_{impl}, defaultDevice_{new Device{impl->default_device(), this}} { } biometryd-0.3.1/src/biometry/qml/Biometryd/service.h000066400000000000000000000040731455450034500225030ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_QML_SERVICE_H_ #define BIOMETRYD_QML_SERVICE_H_ #include #include #include namespace biometry { namespace qml { /// @cond class Device; /// @endcond /// @brief Service provides a single point of entry for accessing a biometry::Service instance. class BIOMETRY_DLL_PUBLIC Service : public QObject { Q_OBJECT Q_PROPERTY(bool available READ isAvailable NOTIFY availableChanged) Q_PROPERTY(biometry::qml::Device* defaultDevice READ defaultDevice CONSTANT) public: /// @brief Service initializes a new instance with impl and parent. Service(const std::shared_ptr& impl, QObject* parent = nullptr); /// @brief defaultDevice returns the default Device instance that should be used for identification. Q_INVOKABLE biometry::qml::Device* defaultDevice() const; /// @brief isAvailable returns true if the service is available. Q_INVOKABLE bool isAvailable() const; /// @brief setAavailable adjusts the value of available to value. Q_INVOKABLE void setAvailable(bool available); /// @brief availableChanged is emitted with the new value of available. Q_SIGNAL void availableChanged(bool); private: bool available_; std::shared_ptr impl_; biometry::qml::Device* defaultDevice_; }; } } #endif // BIOMETRYD_QML_SERVICE_H_ biometryd-0.3.1/src/biometry/qml/Biometryd/template_store.cpp000066400000000000000000000040311455450034500244170ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include #include biometry::qml::TemplateStore::TemplateStore(const std::reference_wrapper& impl, QObject* parent) : QObject{parent}, impl{impl} { } biometry::qml::SizeQuery* biometry::qml::TemplateStore::size(User* user) { return new biometry::qml::SizeQuery{impl.get().size(biometry::Application::system(), biometry::User{user->uid()}), this}; } biometry::qml::List* biometry::qml::TemplateStore::list(User* user) { return new biometry::qml::List{impl.get().list(biometry::Application::system(), biometry::User{user->uid()}), this}; } biometry::qml::Enrollment* biometry::qml::TemplateStore::enroll(User* user) { return new biometry::qml::Enrollment{impl.get().enroll(biometry::Application::system(), biometry::User{user->uid()}), this}; } biometry::qml::Removal* biometry::qml::TemplateStore::remove(User* user, qulonglong id) { return new biometry::qml::Removal{impl.get().remove(biometry::Application::system(), biometry::User{user->uid()}, id), this}; } biometry::qml::Clearance* biometry::qml::TemplateStore::clear(User* user) { return new biometry::qml::Clearance{impl.get().clear(biometry::Application::system(), biometry::User{user->uid()}), this}; } biometryd-0.3.1/src/biometry/qml/Biometryd/template_store.h000066400000000000000000000075241455450034500240760ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_QML_TEMPLATE_STORE_H_ #define BIOMETRYD_QML_TEMPLATE_STORE_H_ #include #include #include #include #include #include namespace biometry { namespace qml { class Operation; class User; /// @cond class BIOMETRY_DLL_PUBLIC SizeQuery : public TypedOperation { Q_OBJECT public: SizeQuery(const biometry::Operation::Ptr& impl, QObject* parent) : TypedOperation{impl, parent} { } }; class BIOMETRY_DLL_PUBLIC List : public TypedOperation { Q_OBJECT public: List(const biometry::Operation::Ptr& impl, QObject* parent) : TypedOperation{impl, parent} { } }; class BIOMETRY_DLL_PUBLIC Enrollment : public TypedOperation { Q_OBJECT public: Enrollment(const biometry::Operation::Ptr& impl, QObject* parent) : TypedOperation{impl, parent} { } }; class BIOMETRY_DLL_PUBLIC Removal : public TypedOperation { Q_OBJECT public: Removal(const biometry::Operation::Ptr& impl, QObject* parent) : TypedOperation{impl, parent} { } }; class BIOMETRY_DLL_PUBLIC Clearance : public TypedOperation { Q_OBJECT public: Clearance(const biometry::Operation::Ptr& impl, QObject* parent) : TypedOperation{impl, parent} { } }; /// @endcond /// @brief TemplateStore models a collection of templates. /// @ingroup qml class BIOMETRY_DLL_PUBLIC TemplateStore : public QObject { Q_OBJECT public: /// @brief TemplateStore initializes a new instance with impl and parent. TemplateStore(const std::reference_wrapper& impl, QObject* parent); /// @brief size returns an operation querying the size of templates enrolled for user. Q_INVOKABLE biometry::qml::SizeQuery* size(biometry::qml::User* user); /// @brief size returns an operation querying all ids of templates enrolled for user. Q_INVOKABLE biometry::qml::List* list(biometry::qml::User* user); /// @brief enroll returns an operation enrolling a new template for user. Q_INVOKABLE biometry::qml::Enrollment* enroll(biometry::qml::User* user); /// @brief remove returns an operation removing the template with id for user. Q_INVOKABLE biometry::qml::Removal* remove(biometry::qml::User* user, qulonglong id); /// @brief clear returns an operation removing all templates for app and user. Q_INVOKABLE biometry::qml::Clearance* clear(biometry::qml::User* user); private: /// @cond std::reference_wrapper impl; /// @endcond }; } } #endif // BIOMETRYD_QML_TEMPLATE_STORE_H_ biometryd-0.3.1/src/biometry/qml/Biometryd/user.cpp000066400000000000000000000017771455450034500223640ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include biometry::qml::User::User(QObject *parent) : QObject{parent} { } uid_t biometry::qml::User::uid() const { return id_; } void biometry::qml::User::setUid(uid_t id) { if (id_ == id) return; Q_EMIT(uidChanged(id_ = id)); } biometryd-0.3.1/src/biometry/qml/Biometryd/user.h000066400000000000000000000031351455450034500220170ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_QML_USER_H_ #define BIOMETRYD_QML_USER_H_ #include #include namespace biometry { namespace qml { /// @brief User models a user known to the system. /// @ingroup qml class BIOMETRY_DLL_PUBLIC User : public QObject { Q_OBJECT Q_PROPERTY(unsigned int uid READ uid WRITE setUid NOTIFY uidChanged) public: User(QObject* parent = nullptr); /// @brief id returns the numeric user id. Q_INVOKABLE unsigned int uid() const; /// @brief setId adjusts the numeric user id. Q_INVOKABLE void setUid(unsigned int id); /// @brief uidChanged is emitted with the newly set id. Q_SIGNAL void uidChanged(unsigned int id); private: /// @cond unsigned int id_{0}; /// @endcond }; class BIOMETRY_DLL_PUBLIC Users : public QObject { Q_OBJECT public: Users(QObject* parent = nullptr); }; } } #endif // BIOMETRYD_QML_USER_H_ biometryd-0.3.1/src/biometry/qml/CMakeLists.txt000066400000000000000000000000331455450034500214640ustar00rootroot00000000000000add_subdirectory(Biometryd)biometryd-0.3.1/src/biometry/reason.cpp000066400000000000000000000023061455450034500201330ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include biometry::Reason biometry::Reason::unknown() { return Reason{"unknown"}; } biometry::Reason::Reason() : value{"unknown"} { } biometry::Reason::Reason(const std::string& s) : value{s} { } const std::string& biometry::Reason::as_string() const { return value; } biometry::Reason::operator std::string() const { return value; } bool biometry::operator==(const biometry::Reason& lhs, const biometry::Reason& rhs) { return lhs.as_string() == rhs.as_string(); } biometryd-0.3.1/src/biometry/runtime.cpp000066400000000000000000000055471455450034500203410ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include namespace { // exception_safe_run runs service, catching all exceptions and // restarting operation until an explicit shutdown has been requested. // // TODO(tvoss): Catching all exceptions is risky as they might signal unrecoverable // errors. We should enable calling code to decide whether an exception should be considered // fatal or not. void exception_safe_run(boost::asio::io_service& service) { while (true) { try { service.run(); // a clean return from run only happens in case of // stop() being called (we are keeping the service alive with // a service::work instance). break; } catch (const std::exception& e) { std::cerr << e.what(); } catch (...) { std::cerr << "Unknown exception caught while executing boost::asio::io_service"; } } } } std::shared_ptr biometry::Runtime::create(std::uint16_t pool_size) { return std::shared_ptr(new biometry::Runtime(pool_size)); } biometry::Runtime::Runtime(std::uint16_t pool_size) : pool_size_{pool_size}, service_{pool_size_}, strand_{service_}, keep_alive_{service_} { } biometry::Runtime::~Runtime() { try { stop(); } catch(...) { // Dropping all exceptions to satisfy the nothrow guarantee. } } void biometry::Runtime::start() { for (unsigned int i = 0; i < pool_size_; i++) workers_.push_back(std::thread{exception_safe_run, std::ref(service_)}); } void biometry::Runtime::stop() { service_.stop(); for (auto& worker : workers_) if (worker.joinable()) worker.join(); } std::function)> biometry::Runtime::to_dispatcher_functional() { // We have to make sure that we stay alive for as long as // calling code requires the dispatcher to work. auto sp = shared_from_this(); return [sp](std::function task) { sp->strand_.post(task); }; } boost::asio::io_service& biometry::Runtime::service() { return service_; } biometryd-0.3.1/src/biometry/runtime.h000066400000000000000000000052761455450034500200050ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_RUNTIME_H_ #define BIOMETRYD_RUNTIME_H_ #include #include #include #include #include #include #include namespace biometry { // We bundle our "global" runtime dependencies here, specifically // a dispatcher to decouple multiple in-process providers from one // another , forcing execution to a well known set of threads. class BIOMETRY_DLL_PUBLIC Runtime : public std::enable_shared_from_this { public: // Our default concurrency setup. static constexpr const std::uint16_t worker_threads = 2; // create returns a Runtime instance with pool_size worker threads // executing the underlying service. static std::shared_ptr create(std::uint16_t pool_size = worker_threads); Runtime(const Runtime&) = delete; Runtime(Runtime&&) = delete; // Tears down the runtime, stopping all worker threads. ~Runtime() noexcept(true); Runtime& operator=(const Runtime&) = delete; Runtime& operator=(Runtime&&) = delete; // start executes the underlying io_service on a thread pool with // the size configured at creation time. void start(); // stop cleanly shuts down a Runtime instance, // joining all worker threads. void stop(); // to_dispatcher_functional returns a function for integration // with components that expect a dispatcher for operation. std::function)> to_dispatcher_functional(); // service returns the underlying boost::asio::io_service that is executed // by the Runtime. boost::asio::io_service& service(); private: // Runtime constructs a new instance, firing up pool_size // worker threads. Runtime(std::uint16_t pool_size); std::uint16_t pool_size_; boost::asio::io_service service_; boost::asio::io_service::strand strand_; boost::asio::io_service::work keep_alive_; std::vector workers_; }; } #endif // BIOMETRYD_RUNTIME_H_ biometryd-0.3.1/src/biometry/tracing_operation_observer.h000066400000000000000000000100231455450034500237220ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRYD_TRACING_OPERATION_OBSERVER_H_ #define BIOMETRYD_TRACING_OPERATION_OBSERVER_H_ #include #include #include #include #include #include #include namespace biometry { namespace detail { // Printer helps in pretty printing a value with a given offset as in: // [ offset * ' ' ]value; // We rely on this customization to make it easy to provide nicely formatted output // without the need to provide a full abstraction on top of a std::ostream while still being // able to only have one TracingObserver without type specializations. template struct Printer { static std::ostream& print(std::ostream& out, const T& value, std::size_t offset) { return out << std::string(offset, ' ') << value; } }; template<> struct Printer { static std::ostream& print(std::ostream& out, const biometry::Dictionary& dict, std::size_t offset) { std::string indent(offset, ' '); for (auto it = dict.begin(); it != dict.end(); ++it) out << indent << it->first << " = " << it->second << (it != std::prev(dict.end()) ? "" : ""); return out; } }; template<> struct Printer { static std::ostream& print(std::ostream& out, const biometry::Progress& progress, std::size_t offset) { std::string indent(offset, ' '); out << indent << progress.percent << " complete"; if (progress.details.empty()) return out; out << ":" << std::endl; Printer::print(out, progress.details, offset + 2); return out; } }; template<> struct Printer { static std::ostream& print(std::ostream& out, const biometry::Void&, std::size_t) { return out; } }; } template class TracingObserver : public biometry::Operation::Observer { public: typedef typename biometry::Operation::Observer Super; TracingObserver(std::size_t offset = 0, std::ostream& out = std::cout) : offset{offset}, indent(offset, ' '), out{out} { } void on_started() override { out << indent << __FUNCTION__ << std::endl; } void on_progress(const typename Super::Progress& progress) override { out << indent << __FUNCTION__<< ": " << std::endl; detail::Printer::print(out, progress, offset + 2) << std::endl; } void on_canceled(const typename Super::Reason& reason) override { out << indent << __FUNCTION__ << ": " << std::endl; detail::Printer::print(out, reason, offset + 2) << std::endl; } void on_failed(const typename Super::Error& error) override { out << indent << __FUNCTION__ << ": " << std::endl; detail::Printer::print(out, error, offset + 2) << std::endl; } void on_succeeded(const typename Super::Result& result) override { out << indent << __FUNCTION__ << std::endl; detail::Printer::print(out, result, offset + 2) << std::endl; } private: std::size_t offset; std::string indent; std::ostream& out; }; } #endif // BIOMETRYD_TRACING_OPERATION_OBSERVER_H_ biometryd-0.3.1/src/biometry/user.cpp000066400000000000000000000027041455450034500176240ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include const biometry::User& biometry::User::root() { static const biometry::User instance{0}; return instance; } const biometry::User& biometry::User::current() { static const biometry::User instance{::getuid()}; return instance; } biometry::User::User(uid_t id) : id{id} { } bool biometry::operator<(const User& lhs, const User& rhs) { return lhs.id < rhs.id; } bool biometry::operator==(const User& lhs, const User& rhs) { return lhs.id == rhs.id; } std::ostream& biometry::operator<<(std::ostream& out, const User& user) { return out << "User[" << user.id << "]"; } std::istream& biometry::operator>>(std::istream& in, User& user) { return in >> user.id; } biometryd-0.3.1/src/biometry/util/000077500000000000000000000000001455450034500171145ustar00rootroot00000000000000biometryd-0.3.1/src/biometry/util/atomic_counter.cpp000066400000000000000000000016401455450034500226340ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include biometry::util::AtomicCounter::AtomicCounter(std::uint32_t value) : counter{value} { } std::uint32_t biometry::util::AtomicCounter::increment() { return counter++; } biometryd-0.3.1/src/biometry/util/atomic_counter.h000066400000000000000000000027551455450034500223110ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRY_UTIL_ATOMIC_COUNTER_H_ #define BIOMETRY_UTIL_ATOMIC_COUNTER_H_ #include #include #include namespace biometry { namespace util { /// @brief AtomicCounter models an atomic counter. class BIOMETRY_DLL_PUBLIC AtomicCounter { public: /// @brief AtomicCounter initializes a new instance to the given value. explicit AtomicCounter(std::uint32_t value = 0); /// @brief increment increments the value of the counter and returns the previously stored value. std::uint32_t increment(); private: /// @cond std::atomic counter; /// @endcond }; template inline AtomicCounter& counter() { static AtomicCounter instance; return instance; } } } #endif // BIOMETRY_UTIL_ATOMIC_COUNTER_H_ biometryd-0.3.1/src/biometry/util/benchmark.cpp000066400000000000000000000051531455450034500215560ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include namespace { template std::chrono::microseconds to_ms(const Duration& duration) { return std::chrono::duration_cast(duration); } } biometry::util::Benchmark::Benchmark(const std::function& operation) : operation_{operation}, trials_{1} { } biometry::util::Benchmark& biometry::util::Benchmark::trials(std::size_t value) { trials_ = value; return *this; } biometry::util::Benchmark biometry::util::Benchmark::trials(std::size_t value) const { auto copy = *this; return copy.trials(value); } biometry::util::Benchmark& biometry::util::Benchmark::on_progress(const std::function& f) { on_progress_ = f; return *this; } biometry::util::Benchmark biometry::util::Benchmark::on_progress(const std::function& f) const { auto copy = *this; return copy.on_progress(f); } biometry::util::Benchmark& biometry::util::Benchmark::on_error(const std::function& f) { on_error_ = f; return *this; } biometry::util::Benchmark biometry::util::Benchmark::on_error(const std::function& f) const { auto copy = *this; return copy.on_error(f); } biometry::util::Statistics biometry::util::Benchmark::run() const { biometry::util::Statistics stats; for (std::size_t i = 1; i <= trials_; i++) { if (on_progress_) on_progress_(i, trials_); auto before = std::chrono::high_resolution_clock::now(); { try { operation_(); } catch(...) { if (on_error_ && on_error_()) continue; std::rethrow_exception(std::current_exception()); } } stats.update(to_ms(std::chrono::high_resolution_clock::now() - before).count()); } return stats; } biometryd-0.3.1/src/biometry/util/benchmark.h000066400000000000000000000044531455450034500212250ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRY_UTIL_BENCHMARK_H_ #define BIOMETRY_UTIL_BENCHMARK_H_ #include #include #include namespace biometry { namespace util { /// @brief Benchmark executes a given operation for a number of trials and returns statistics about /// the runtime of an individual iteration of the operation. class Benchmark { public: /// @brief Benchmark initializes an instance with operation. explicit Benchmark(const std::function& operation); /// @brief trials adjusts the number of trials to value. Benchmark trials(std::size_t value) const; /// @brief trials adjusts the number of trials to value. Benchmark& trials(std::size_t value); /// @brief on_progress installs the given handler function. Benchmark on_progress(const std::function&) const; /// @brief on_progress installs the given handler function. Benchmark& on_progress(const std::function&); /// @brief on_error installs the given handler function. Benchmark on_error(const std::function&) const; /// @brief on_error installs the given handler function. Benchmark& on_error(const std::function&); /// @brief run executes the benchmark, accumulating statistics over the runtime. biometry::util::Statistics run() const; private: /// @cond std::function operation_; std::size_t trials_; std::function on_progress_; std::function on_error_; /// @endcond }; } } #endif // BIOMETRY_UTIL_BENCHMARK_H_ biometryd-0.3.1/src/biometry/util/cli.cpp000066400000000000000000000200111455450034500203610ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include #include namespace cli = biometry::util::cli; namespace po = boost::program_options; namespace { namespace pattern { static constexpr const char* help_for_command_with_subcommands = "NAME:\n" " %1% - %2%\n" "\n" "USAGE:\n" " %3% [command options] [arguments...]"; static constexpr const char* commands = "COMMANDS:"; static constexpr const char* command = " %1% %2%"; static constexpr const char* options = "OPTIONS:"; static constexpr const char* option = " --%1% %2%"; } void add_to_desc_for_flags(po::options_description& desc, const std::set& flags) { for (auto flag : flags) { auto v = po::value()->notifier([flag](const std::string& s) { flag->notify(s); }); desc.add_options()(flag->name().as_string().c_str(), v, flag->description().as_string().c_str()); } } } biometry::util::cli::ProgressBar::ProgressBar(std::ostream& out, const std::string& prefix, std::uint32_t width) : prefix{prefix}, width{width}, out{out} { } biometry::util::cli::ProgressBar::~ProgressBar() { out << std::endl; } void biometry::util::cli::ProgressBar::update(double percentage) { struct CursorState { CursorState(std::ostream& out) : out{out} { out << "\33[?25l"; } ~CursorState() { out << "\33[?25h"; } std::ostream& out; } cs{out}; out << "\r" << prefix << "[" << std::setw(width) << std::left << std::setfill(' ') << std::string(percentage * width, '=') << "] " << std::setw(5) << std::fixed << std::setprecision(2) << percentage * 100 << " %" << std::flush; } std::vector cli::args(int argc, char **argv) { std::vector result; for (int i = 1; i < argc; i++) result.push_back(argv[i]); return result; } const cli::Name& cli::Flag::name() const { return name_; } const cli::Description& cli::Flag::description() const { return description_; } cli::Flag::Flag(const Name& name, const Description& description) : name_{name}, description_{description} { } cli::Command::FlagsWithInvalidValue::FlagsWithInvalidValue() : std::runtime_error{"Flags with invalid value"} { } cli::Command::FlagsMissing::FlagsMissing() : std::runtime_error{"Flags are missing in command invocation"} { } cli::Name cli::Command::name() const { return name_; } cli::Usage cli::Command::usage() const { return usage_; } cli::Description cli::Command::description() const { return description_; } cli::Command::Command(const cli::Name& name, const cli::Usage& usage, const cli::Description& description) : name_(name), usage_(usage), description_(description) { } cli::CommandWithSubcommands::CommandWithSubcommands(const Name& name, const Usage& usage, const Description& description) : Command{name, usage, description} { command(std::make_shared(*this)); } cli::CommandWithSubcommands& cli::CommandWithSubcommands::command(const Command::Ptr& command) { commands_[command->name().as_string()] = command; return *this; } cli::CommandWithSubcommands& cli::CommandWithSubcommands::flag(const Flag::Ptr& flag) { flags_.insert(flag); return *this; } void cli::CommandWithSubcommands::help(std::ostream& out) { out << boost::format(pattern::help_for_command_with_subcommands) % name().as_string() % usage().as_string() % name().as_string() << std::endl; if (flags_.size() > 0) { out << std::endl << pattern::options << std::endl; for (const auto& flag : flags_) out << boost::format(pattern::option) % flag->name() % flag->description() << std::endl; } if (commands_.size() > 0) { out << std::endl << pattern::commands << std::endl; for (const auto& cmd : commands_) out << boost::format(pattern::command) % cmd.second->name() % cmd.second->description() << std::endl; } } int cli::CommandWithSubcommands::run(const cli::Command::Context& ctxt) { po::positional_options_description pdesc; pdesc.add("command", 1); po::options_description desc("Options"); desc.add_options()("command", po::value()->required(), "the command to be executed"); add_to_desc_for_flags(desc, flags_); try { po::variables_map vm; auto parsed = po::command_line_parser(ctxt.args) .options(desc) .positional(pdesc) .style(po::command_line_style::unix_style) .allow_unregistered() .run(); po::store(parsed, vm); po::notify(vm); return commands_[vm["command"].as()]->run(cli::Command::Context { ctxt.cin, ctxt.cout, po::collect_unrecognized(parsed.options, po::include_positional) }); } catch (const po::error& e) { ctxt.cout << e.what() << std::endl; help(ctxt.cout); return EXIT_FAILURE; } return EXIT_FAILURE; } cli::CommandWithFlagsAndAction::CommandWithFlagsAndAction(const Name& name, const Usage& usage, const Description& description) : Command{name, usage, description} { } cli::CommandWithFlagsAndAction& cli::CommandWithFlagsAndAction::flag(const Flag::Ptr& flag) { flags_.insert(flag); return *this; } cli::CommandWithFlagsAndAction& cli::CommandWithFlagsAndAction::action(const Action& action) { action_ = action; return *this; } int cli::CommandWithFlagsAndAction::run(const Context& ctxt) { po::options_description cd(name().as_string()); bool help_requested{false}; cd.add_options()("help", po::bool_switch(&help_requested), "produces a help message"); add_to_desc_for_flags(cd, flags_); try { po::variables_map vm; auto parsed = po::command_line_parser(ctxt.args).options(cd).style(po::command_line_style::unix_style).allow_unregistered().run(); po::store(parsed, vm); po::notify(vm); if (help_requested) { help(ctxt.cout); return EXIT_SUCCESS; } return action_(cli::Command::Context { ctxt.cin, ctxt.cout, po::collect_unrecognized(parsed.options, po::exclude_positional) }); } catch (const po::error& e) { ctxt.cout << e.what() << std::endl; help(ctxt.cout); return EXIT_FAILURE; } return EXIT_FAILURE; } void cli::CommandWithFlagsAndAction::help(std::ostream& out) { out << boost::format(pattern::help_for_command_with_subcommands) % name().as_string() % description().as_string() % name().as_string() << std::endl; if (flags_.size() > 0) { out << std::endl << boost::format(pattern::options) << std::endl; for (const auto& flag : flags_) out << boost::format(pattern::option) % flag->name() % flag->description() << std::endl; } } cli::cmd::Help::Help(Command& cmd) : Command{cli::Name{"help"}, cli::Usage{"prints a short help message"}, cli::Description{"prints a short help message"}}, command{cmd} { } // From Command int cli::cmd::Help::run(const Context &context) { command.help(context.cout); return EXIT_FAILURE; } void cli::cmd::Help::help(std::ostream &out) { command.help(out); } biometryd-0.3.1/src/biometry/util/cli.h000066400000000000000000000274211455450034500200420ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRY_UTIL_CLI_H_ #define BIOMETRY_UTIL_CLI_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include namespace biometry { namespace util { namespace cli { /// @brief ProgressBar prints a progress bar on the terminal. /// /// Looks like: /// Prefix[===============----------] xxx.xx % /// |--------------- width ------------| class BIOMETRY_DLL_PUBLIC ProgressBar { public: /// @brief ProgressBar initializes an instance with out, prefix and width. ProgressBar(std::ostream& out, const std::string& prefix = std::string{}, std::uint32_t width = 80); /// @brief ~ProgressBar cleans up and inserts a std::endl into out. ~ProgressBar(); /// @brief update updates the terminal output for the given percentage. void update(double percentage); private: /// @cond std::string prefix; std::uint32_t width; std::ostream& out; /// @endcond }; template class BIOMETRY_DLL_PUBLIC SizeConstrainedString { public: SizeConstrainedString(const std::string& s) : s{s} { if(s.size() > max) throw std::logic_error{"Max size exceeded " + std::to_string(max)}; } const std::string& as_string() const { return s; } operator std::string() const { return s; } private: std::string s; }; template BIOMETRY_DLL_PUBLIC bool operator<(const SizeConstrainedString& lhs, const SizeConstrainedString& rhs) { return lhs.as_string() < rhs.as_string(); } template BIOMETRY_DLL_PUBLIC bool operator==(const SizeConstrainedString& lhs, const SizeConstrainedString& rhs) { return lhs.as_string() == rhs.as_string(); } template BIOMETRY_DLL_PUBLIC std::ostream& operator<<(std::ostream& out, const SizeConstrainedString& scs) { return out << std::setw(max) << std::left << scs.as_string(); } // We are imposing size constraints to ensure a consistent CLI layout. typedef SizeConstrainedString<15> Name; typedef SizeConstrainedString<60> Usage; typedef SizeConstrainedString<60> Description; /// @brief Flag models an input parameter to a command. class BIOMETRY_DLL_PUBLIC Flag : public DoNotCopyOrMove { public: // Safe us some typing. typedef std::shared_ptr Ptr; /// @brief notify announces a new value to the flag. virtual void notify(const std::string& value) = 0; /// @brief name returns the name of the Flag. const Name& name() const; /// @brief description returns a human-readable description of the flag. const Description& description() const; protected: /// @brief Flag creates a new instance, initializing name and description /// from the given values. Flag(const Name& name, const Description& description); private: Name name_; Description description_; }; /// @brief TypedFlag implements Flag relying on operator<< and operator>> to read/write values to/from strings. template class BIOMETRY_DLL_PUBLIC TypedFlag : public Flag { public: typedef std::shared_ptr> Ptr; TypedFlag(const Name& name, const Description& description) : Flag{name, description} { } /// @brief value installs the given value in the flag. TypedFlag& value(const T& value) { value_ = value; return *this; } /// @brief value returns the optional value associated with the flag. const Optional& value() const { return value_; } /// @brief notify tries to unwrap a value of type T from value. void notify(const std::string& s) override { std::stringstream ss{s}; T value; ss >> value; value_ = value; } private: Optional value_; }; /// @brief TypedReferenceFlag implements Flag, relying on operator<> to convert to/from string representations, /// updating the given mutable reference to a value of type T. template class BIOMETRY_DLL_PUBLIC TypedReferenceFlag : public Flag { public: // Safe us some typing. typedef std::shared_ptr> Ptr; /// @brief TypedReferenceFlag initializes a new instance with name, description and value. TypedReferenceFlag(const Name& name, const Description& description, T& value) : Flag{name, description}, value_{value} { } /// @brief notify tries to unwrap a value of type T from value, /// relying on operator>> to read from given string s. void notify(const std::string& s) override { std::stringstream ss{s}; ss >> value_.get(); } private: std::reference_wrapper value_; }; /// @brief OptionalTypedReferenceFlag handles Optional references, making sure that /// a value is always read on notify, even if the Optional wasn't initialized previously. template class BIOMETRY_DLL_PUBLIC OptionalTypedReferenceFlag : public Flag { public: typedef std::shared_ptr> Ptr; OptionalTypedReferenceFlag(const Name& name, const Description& description, Optional& value) : Flag{name, description}, value_{value} { } /// @brief notify tries to unwrap a value of type T from value. void notify(const std::string& s) override { std::stringstream ss{s}; T value; ss >> value; value_.get() = value; } private: std::reference_wrapper> value_; }; /// @brief Command abstracts an individual command available from the daemon. class BIOMETRY_DLL_PUBLIC Command : public DoNotCopyOrMove { public: // Safe us some typing typedef std::shared_ptr Ptr; /// @brief FlagsMissing is thrown if at least one required flag is missing. struct FlagsMissing : public std::runtime_error { /// @brief FlagsMissing initializes a new instance. FlagsMissing(); }; /// @brief FlagsWithWrongValue is thrown if a value passed on the command line is invalid. struct FlagsWithInvalidValue : public std::runtime_error { /// @brief FlagsWithInvalidValue initializes a new instance. FlagsWithInvalidValue(); }; /// @brief Context bundles information passed to Command::run invocations. struct Context { std::istream& cin; ///< The std::istream that should be used for reading. std::ostream& cout; ///< The std::ostream that should be used for writing. std::vector args; ///< The command line args. }; /// @brief name returns the Name of the command. virtual Name name() const; /// @brief usage returns a short usage string for the command. virtual Usage usage() const; /// @brief description returns a longer string explaining the command. virtual Description description() const; /// @brief run puts the command to execution. virtual int run(const Context& context) = 0; /// @brief help prints information about a command to out. virtual void help(std::ostream& out) = 0; protected: /// @brief Command initializes a new instance with the given name, usage and description. Command(const Name& name, const Usage& usage, const Description& description); /// @brief name adjusts the name of the command to n. // virtual void name(const Name& n); /// @brief usage adjusts the usage string of the comand to u. // virtual void usage(const Usage& u); /// @brief description adjusts the description string of the command to d. // virtual void description(const Description& d); private: Name name_; Usage usage_; Description description_; }; /// @brief CommandWithSubcommands implements Command, selecting one of a set of actions. class BIOMETRY_DLL_PUBLIC CommandWithSubcommands : public Command { public: typedef std::shared_ptr Ptr; typedef std::function Action; /// @brief CommandWithSubcommands initializes a new instance with the given name, usage and description CommandWithSubcommands(const Name& name, const Usage& usage, const Description& description); /// @brief command adds the given command to the set of known commands. CommandWithSubcommands& command(const Command::Ptr& command); /// @brief flag adds the given flag to the set of known flags. CommandWithSubcommands& flag(const Flag::Ptr& flag); // From Command int run(const Context& context) override; void help(std::ostream &out) override; private: std::unordered_map commands_; std::set flags_; }; /// @brief CommandWithFlagsAction implements Command, executing an Action after handling class BIOMETRY_DLL_PUBLIC CommandWithFlagsAndAction : public Command { public: typedef std::shared_ptr Ptr; typedef std::function Action; /// @brief CommandWithFlagsAndAction initializes a new instance with the given name, usage and description CommandWithFlagsAndAction(const Name& name, const Usage& usage, const Description& description); /// @brief flag adds the given flag to the set of known flags. CommandWithFlagsAndAction& flag(const Flag::Ptr& flag); /// @brief action installs the given action. CommandWithFlagsAndAction& action(const Action& action); // From Command int run(const Context& context) override; void help(std::ostream &out) override; private: std::set flags_; Action action_; }; namespace cmd { /// @brief HelpFor prints a help message for the given command on execution. class Help : public Command { public: /// @brief HelpFor initializes a new instance with the given reference to a cmd. explicit Help(Command& cmd); // From Command int run(const Context &context) override; void help(std::ostream &out) override; private: /// @cond Command& command; /// @endcond }; } /// @brief args returns a vector of strings assembled from argc and argv. BIOMETRY_DLL_PUBLIC std::vector args(int argc, char** argv); /// @brief make_flag returns a flag with the given name and description. template BIOMETRY_DLL_PUBLIC typename TypedFlag::Ptr make_flag(const Name& name, const Description& description) { return std::make_shared>(name, description); } /// @brief make_flag returns a flag with the given name and description, notifying updates to value. template BIOMETRY_DLL_PUBLIC typename TypedReferenceFlag::Ptr make_flag(const Name& name, const Description& desc, T& value) { return std::make_shared>(name, desc, value); } /// @brief make_flag returns a flag with the given name and description, updating the given optional value. template BIOMETRY_DLL_PUBLIC typename OptionalTypedReferenceFlag::Ptr make_flag(const Name& name, const Description& desc, Optional& value) { return std::make_shared>(name, desc, value); } } } } #endif // BIOMETRY_UTIL_CLI_H_ biometryd-0.3.1/src/biometry/util/configuration.cpp000066400000000000000000000102411455450034500224650ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include #include #include #include #include namespace { const biometry::util::Configuration::Node& null() { static const biometry::util::Configuration::Node instance; return instance; } } biometry::util::Configuration::Node::Node(const Variant &value) : value_{value} { } biometry::util::Configuration::Node::operator bool() const { return value_.type() != Variant::Type::none; } const biometry::Variant& biometry::util::Configuration::Node::value() const { return value_; } biometry::util::Configuration::Node& biometry::util::Configuration::Node::value(const Variant& value) { value_ = value; return *this; } const biometry::util::Configuration::Children& biometry::util::Configuration::Node::children() const { return children_; } biometry::util::Configuration::Children& biometry::util::Configuration::Node::children() { return children_; } biometry::util::Configuration::Node& biometry::util::Configuration::Node::operator()(const std::string& name, const std::function& catcher) { try { return children_.at(name); } catch(...) { catcher(); } util::not_reachable(__FUNCTION__, __FILE__, __LINE__); } const biometry::util::Configuration::Node& biometry::util::Configuration::Node::operator()(const std::string& name, const std::function& catcher) const { try { return children_.at(name); } catch (...) { catcher(); } util::not_reachable(__FUNCTION__, __FILE__, __LINE__); } biometry::util::Configuration::Node& biometry::util::Configuration::Node::operator[](const std::string& name) { return children_[name]; } const biometry::util::Configuration::Node& biometry::util::Configuration::Node::operator[](const std::string& name) const { auto it = children_.find(name); if (it != children_.end()) return it->second; return null(); } /*biometry::util::Configuration::Node& biometry::util::Configuration::Node::operator[](const char* name) { return (*this)[std::string{name}]; } const biometry::util::Configuration::Node& biometry::util::Configuration::Node::operator[](const char* name) const { return (*this)[std::string{name}]; }*/ const biometry::util::Configuration::Children& biometry::util::Configuration::children() const { return children_; } biometry::util::Configuration::Children& biometry::util::Configuration::children() { return children_; } biometry::util::Configuration::Node& biometry::util::Configuration::operator()(const std::string& name, const std::function& catcher) { try { return children_.at(name); } catch(...) { catcher(); } util::not_reachable(__FUNCTION__, __FILE__, __LINE__); } const biometry::util::Configuration::Node& biometry::util::Configuration::operator()(const std::string& name, const std::function& catcher) const { try { return children_.at(name); } catch (...) { catcher(); } util::not_reachable(__FUNCTION__, __FILE__, __LINE__); } biometry::util::Configuration::Node& biometry::util::Configuration::operator[](const std::string& name) { return children_[name]; } const biometry::util::Configuration::Node& biometry::util::Configuration::operator[](const std::string& name) const { auto it = children_.find(name); if (it != children_.end()) return it->second; return null(); } biometryd-0.3.1/src/biometry/util/configuration.h000066400000000000000000000117271455450034500221440ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRY_UTIL_CONFIGURATION_H_ #define BIOMETRY_UTIL_CONFIGURATION_H_ #include #include #include #include #include #include namespace biometry { namespace util { class BIOMETRY_DLL_PUBLIC Configuration { public: /// @cond class Node; typedef std::map Children; /// @endcond /// @brief Node represents a single named configuration value. class Node { public: /// @brief Node constructs a new instance with the given name, /// initializing the contained value to value. Node(const Variant& value = Variant{}); /// @brief operator bool returns true if the contained value is not empty. explicit operator bool() const; /// @brief value returns an immutable reference to the contained value. virtual const Variant& value() const; /// @brief value adjusts the contained value. virtual Node& value(const Variant& value); /// @brief children returns the set of all children of this node. virtual const Children& children() const; /// @brief children returns a mutable reference to the children of this node. virtual Children& children(); /// @brief Returns a mutable reference to the child with the given name or throws. /// /// catcher is invoked in the catch block such that API users can wrap up the original exception /// in a custom type easily. Node& operator()(const std::string& name, const std::function& catcher); /// @brief Returns an immutable reference to the child with the given name. /// /// catcher is invoked in the catch block such that API users can wrap up the original exception /// in a custom type easily. const Node& operator()(const std::string& name, const std::function& catcher) const; /// @brief Returns a mutable reference to the child with the given name. Node& operator[](const std::string& name); /// @brief Returns a mutable reference to the child with the given name. const Node& operator[](const std::string& name) const; /// @brief Returns a mutable reference to the child with the given name. // Node& operator[](const char* name); /// @brief Returns a mutable reference to the child with the given name. // const Node& operator[](const char* name) const; private: Variant value_; ///< mutable value of the Node. Children children_; ///< mutable set of all children_ of this Node. }; /// @brief children returns the set of all children of this node. virtual const Children& children() const; /// @brief children returns a mutable reference to the children of this node. virtual Children& children(); /// @brief Returns a mutable reference to the child with the given name or throws. /// /// catcher is invoked in the catch block such that API users can wrap up the original exception /// in a custom type easily. Node& operator()(const std::string& name, const std::function& catcher); /// @brief Returns an immutable reference to the child with the given name. /// /// catcher is invoked in the catch block such that API users can wrap up the original exception /// in a custom type easily. const Node& operator()(const std::string& name, const std::function& catcher) const; /// @brief Returns a mutable reference to the child with the given name. Node& operator[](const std::string& name); /// @brief Returns a mutable reference to the child with the given name. const Node& operator[](const std::string& name) const; private: Children children_; ///< mutable set of all children_ of this Node. }; /// @brief ConfigurationBuilder models loading of configuration from arbitrary sources. class BIOMETRY_DLL_PUBLIC ConfigurationBuilder : public biometry::DoNotCopyOrMove { public: /// @brief build_configuration returns a Configuration instance. /// @throws in case of issues. virtual Configuration build_configuration() = 0; protected: /// @cond ConfigurationBuilder() = default; /// @endcond }; } } #endif // BIOMETRY_UTIL_CONFIGURATION_H_ biometryd-0.3.1/src/biometry/util/dispatcher.cpp000066400000000000000000000025131455450034500217470ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include namespace { struct AsioStrandDispatcher : public biometry::util::Dispatcher { public: AsioStrandDispatcher(const std::shared_ptr& rt) : rt{rt}, strand{rt->service()} { } void dispatch(const Task &task) override { strand.post(task); } private: std::shared_ptr rt; boost::asio::io_service::strand strand; }; } std::shared_ptr biometry::util::create_dispatcher_for_runtime(const std::shared_ptr& rt) { return std::make_shared(rt); } biometryd-0.3.1/src/biometry/util/dispatcher.h000066400000000000000000000031121455450034500214100ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRY_UTIL_DISPATCHER_H_ #define BIOMETRY_UTIL_DISPATCHER_H_ #include #include #include #include namespace biometry { namespace util { /// @brief A Dispatcher enqueues tasks for execution. class BIOMETRY_DLL_PUBLIC Dispatcher : public DoNotCopyOrMove { public: /// @brief A Task is dispatched by a dispatcher. typedef std::function Task; /// @brief dispatch enqueues the given task for execution. virtual void dispatch(const Task& task) = 0; protected: /// @cond Dispatcher() = default; /// @endcond }; /// @brief create_dispatcher_for_runtime creates a dispatcher enqueuing to the runtime's service. BIOMETRY_DLL_PUBLIC std::shared_ptr create_dispatcher_for_runtime(const std::shared_ptr&); } } #endif // BIOMETRY_UTIL_DISPATCHER_H_ biometryd-0.3.1/src/biometry/util/dynamic_library.cpp000066400000000000000000000062701455450034500227750ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include namespace { namespace glibc { struct DlApi : public biometry::util::DynamicLibrary::Api { // See man dlopen. biometry::util::DynamicLibrary::Handle open(const boost::filesystem::path& path) const override { if (auto handle = ::dlopen(path.string().c_str(), RTLD_NOW | RTLD_LOCAL)) return biometry::util::DynamicLibrary::Handle{handle}; throw biometry::util::DynamicLibrary::Api::Error{*this}; } // See man dlclose. void close(const biometry::util::DynamicLibrary::Handle& handle) const override { if (::dlclose(handle.as<>()) < 0) throw biometry::util::DynamicLibrary::Api::Error{*this}; } // See man dlsym. biometry::util::DynamicLibrary::Symbol sym(const biometry::util::DynamicLibrary::Handle& handle, const std::string& symbol) const override { if (auto sh = ::dlsym(handle.as<>(), symbol.c_str())) return biometry::util::DynamicLibrary::Symbol{sh}; throw biometry::util::DynamicLibrary::Api::Error{*this}; } // See man dlerror. std::string error() const override { if (auto e = ::dlerror()) return e; return std::string{}; } }; } } biometry::util::DynamicLibrary::OpaqueTypeIsEmpty::OpaqueTypeIsEmpty() : std::runtime_error{"Opaque type is empty."} { } biometry::util::DynamicLibrary::Api::Error::Error(const Api& api) : std::runtime_error{api.error()} { } biometry::util::DynamicLibrary::NoSuchSymbol::NoSuchSymbol(const std::string& symbol, const Api::Error& error) : std::runtime_error{(boost::format("Failed to resolve %1%: %2%.") % symbol % error.what()).str()} { } // DynamicLibrary opens the dynamic library located at path, relying on api to do so. // Throws Api::Error in case of issues. biometry::util::DynamicLibrary::DynamicLibrary(const std::shared_ptr& api, const boost::filesystem::path& path) : api{api}, handle{api->open(path)} { } biometry::util::DynamicLibrary::~DynamicLibrary() { api->close(handle); } biometry::util::DynamicLibrary::Symbol biometry::util::DynamicLibrary::resolve_symbol_or_throw(const std::string& symbol) const { try { return api->sym(handle, symbol); } catch(const Api::Error& e) { throw NoSuchSymbol{symbol, e}; } } std::shared_ptr biometry::util::glibc::dl_api() { return std::make_shared< ::glibc::DlApi >(); } biometryd-0.3.1/src/biometry/util/dynamic_library.h000066400000000000000000000066711455450034500224470ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRY_UTIL_DYNAMIC_LIBRARY_H_ #define BIOMETRY_UTIL_DYNAMIC_LIBRARY_H_ #include #include #include #include #include namespace biometry { namespace util { /// @brief DynamicLibrary abstracts dl* operations, and enables integration testing /// of components relying on the respective functionality. class BIOMETRY_DLL_PUBLIC DynamicLibrary : public biometry::DoNotCopyOrMove { public: /// @brief OpaqueTypeIsEmpty is thrown if trying to access an instance of /// TaggedOpaqueType that contains a nullptr. struct OpaqueTypeIsEmpty : public std::runtime_error { OpaqueTypeIsEmpty(); }; template class TaggedOpaqueType { public: TaggedOpaqueType() : p{nullptr} {} template explicit TaggedOpaqueType(T t) : p{reinterpret_cast(t)} {} template T as() const { if (not p) throw OpaqueTypeIsEmpty{}; return (T) p; } private: void* p; }; struct Tag { struct Handle {}; struct Symbol {}; }; typedef TaggedOpaqueType Handle; typedef TaggedOpaqueType Symbol; class Api : public DoNotCopyOrMove { public: // Exposes dlerror to the exception hierarchy. class Error : public std::runtime_error { public: // Queries dlerror() for a human-readable error message. Error(const Api& api); }; // See man dlopen. virtual Handle open(const boost::filesystem::path& path) const = 0; // See man dlclose. virtual void close(const Handle&) const = 0; // See man dlsym. virtual Symbol sym(const Handle&, const std::string& symbol) const = 0; // See man dlerror. virtual std::string error() const = 0; }; /// @brief NoSuchSymbol is thrown if resolving a symbol from a dynamic library fails. class NoSuchSymbol : public std::runtime_error { public: /// @brief NoSuchSymbol creates a new instance, noting down the symbol that failed resolving. NoSuchSymbol(const std::string& symbol, const Api::Error& error); }; // DynamicLibrary opens the dynamic library located at path, relying on api to do so. // Throws Api::Error in case of issues. DynamicLibrary(const std::shared_ptr& api, const boost::filesystem::path& path); ~DynamicLibrary(); Symbol resolve_symbol_or_throw(const std::string& symbol) const; private: std::shared_ptr api; Handle handle; }; namespace glibc { BIOMETRY_DLL_PUBLIC std::shared_ptr dl_api(); } } } #endif // BIOMETRY_UTIL_DYNAMIC_LIBRARY_H_ biometryd-0.3.1/src/biometry/util/json.hpp000066400000000000000000007473231455450034500206160ustar00rootroot00000000000000/*! @mainpage These pages contain the API documentation of JSON for Modern C++, a C++11 header-only JSON class. Class @ref nlohmann::basic_json is a good entry point for the documentation. @copyright The code is licensed under the [MIT License](http://opensource.org/licenses/MIT):
Copyright © 2013-2015 Niels Lohmann.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 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. @author [Niels Lohmann](http://nlohmann.me) @see https://github.com/nlohmann/json to download the source code @version 1.0.0 */ #ifndef NLOHMANN_JSON_HPP #define NLOHMANN_JSON_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // enable ssize_t on MinGW #ifdef __GNUC__ #ifdef __MINGW32__ #include #endif #endif // enable ssize_t for MSVC #ifdef _MSC_VER #include using ssize_t = SSIZE_T; #endif /*! @brief namespace for Niels Lohmann @see https://github.com/nlohmann @since version 1.0.0 */ namespace nlohmann { /*! @brief unnamed namespace with internal helper functions @since version 1.0.0 */ namespace { /*! @brief Helper to determine whether there's a key_type for T. @sa http://stackoverflow.com/a/7728728/266378 */ template struct has_mapped_type { private: template static char test(typename C::mapped_type*); template static int test(...); public: enum { value = sizeof(test(0)) == sizeof(char) }; }; /// "equality" comparison for floating point numbers template static bool approx(const T a, const T b) { return not (a > b or a < b); } } /*! @brief a class to store JSON values @tparam ObjectType type for JSON objects (@c std::map by default; will be used in @ref object_t) @tparam ArrayType type for JSON arrays (@c std::vector by default; will be used in @ref array_t) @tparam StringType type for JSON strings and object keys (@c std::string by default; will be used in @ref string_t) @tparam BooleanType type for JSON booleans (@c `bool` by default; will be used in @ref boolean_t) @tparam NumberIntegerType type for JSON integer numbers (@c `int64_t` by default; will be used in @ref number_integer_t) @tparam NumberFloatType type for JSON floating-point numbers (@c `double` by default; will be used in @ref number_float_t) @tparam AllocatorType type of the allocator to use (@c `std::allocator` by default) @requirement The class satisfies the following concept requirements: - Basic - [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible): JSON values can be default constructed. The result will be a JSON null value. - [MoveConstructible](http://en.cppreference.com/w/cpp/concept/MoveConstructible): A JSON value can be constructed from an rvalue argument. - [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible): A JSON value can be copy-constrcuted from an lvalue expression. - [MoveAssignable](http://en.cppreference.com/w/cpp/concept/MoveAssignable): A JSON value van be assigned from an rvalue argument. - [CopyAssignable](http://en.cppreference.com/w/cpp/concept/CopyAssignable): A JSON value can be copy-assigned from an lvalue expression. - [Destructible](http://en.cppreference.com/w/cpp/concept/Destructible): JSON values can be destructed. - Layout - [StandardLayoutType](http://en.cppreference.com/w/cpp/concept/StandardLayoutType): JSON values have [standard layout](http://en.cppreference.com/w/cpp/language/data_members#Standard_layout): All non-static data members are private and standard layout types, the class has no virtual functions or (virtual) base classes. - Library-wide - [EqualityComparable](http://en.cppreference.com/w/cpp/concept/EqualityComparable): JSON values can be compared with `==`, see @ref operator==(const_reference,const_reference). - [LessThanComparable](http://en.cppreference.com/w/cpp/concept/LessThanComparable): JSON values can be compared with `<`, see @ref operator<(const_reference,const_reference). - [Swappable](http://en.cppreference.com/w/cpp/concept/Swappable): Any JSON lvalue or rvalue of can be swapped with any lvalue or rvalue of other compatible types, using unqualified function call @ref swap(). - [NullablePointer](http://en.cppreference.com/w/cpp/concept/NullablePointer): JSON values can be compared against `std::nullptr_t` objects which are used to model the `null` value. - Container - [Container](http://en.cppreference.com/w/cpp/concept/Container): JSON values can be used like STL containers and provide iterator access. - [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer); JSON values can be used like STL containers and provide reverse iterator access. @internal @note ObjectType trick from http://stackoverflow.com/a/9860911 @endinternal @see RFC 7159 @since version 1.0.0 @nosubgrouping */ template < template class ObjectType = std::map, template class ArrayType = std::vector, class StringType = std::string, class BooleanType = bool, class NumberIntegerType = int64_t, class NumberFloatType = double, template class AllocatorType = std::allocator > class basic_json { private: /// workaround type for MSVC using basic_json_t = basic_json; public: ///////////////////// // container types // ///////////////////// /// @name container types /// @{ /// the type of elements in a basic_json container using value_type = basic_json; /// the type of an element reference using reference = value_type&; /// the type of an element const reference using const_reference = const value_type&; /// a type to represent differences between iterators using difference_type = std::ptrdiff_t; /// a type to represent container sizes using size_type = std::size_t; /// the allocator type using allocator_type = AllocatorType; /// the type of an element pointer using pointer = typename std::allocator_traits::pointer; /// the type of an element const pointer using const_pointer = typename std::allocator_traits::const_pointer; // forward declaration template class json_reverse_iterator; /// an iterator for a basic_json container class iterator; /// a const iterator for a basic_json container class const_iterator; /// a reverse iterator for a basic_json container using reverse_iterator = json_reverse_iterator; /// a const reverse iterator for a basic_json container using const_reverse_iterator = json_reverse_iterator; /// @} /*! @brief returns the allocator associated with the container */ static allocator_type get_allocator() { return allocator_type(); } /////////////////////////// // JSON value data types // /////////////////////////// /// @name JSON value data types /// @{ /*! @brief a type for an object [RFC 7159](http://rfc7159.net/rfc7159) describes JSON objects as follows: > An object is an unordered collection of zero or more name/value pairs, > where a name is a string and a value is a string, number, boolean, null, > object, or array. To store objects in C++, a type is defined by the template parameters described below. @tparam ObjectType the container to store objects (e.g., `std::map` or `std::unordered_map`) @tparam StringType the type of the keys or names (e.g., `std::string`). The comparison function `std::less` is used to order elements inside the container. @tparam AllocatorType the allocator to use for objects (e.g., `std::allocator`) #### Default type With the default values for @a ObjectType (`std::map`), @a StringType (`std::string`), and @a AllocatorType (`std::allocator`), the default value for @a object_t is: @code {.cpp} std::map< std::string, // key_type basic_json, // value_type std::less, // key_compare std::allocator> // allocator_type > @endcode #### Behavior The choice of @a object_t influences the behavior of the JSON class. With the default type, objects have the following behavior: - When all names are unique, objects will be interoperable in the sense that all software implementations receiving that object will agree on the name-value mappings. - When the names within an object are not unique, later stored name/value pairs overwrite previously stored name/value pairs, leaving the used names unique. For instance, `{"key": 1}` and `{"key": 2, "key": 1}` will be treated as equal and both stored as `{"key": 1}`. - Internally, name/value pairs are stored in lexicographical order of the names. Objects will also be serialized (see @ref dump) in this order. For instance, `{"b": 1, "a": 2}` and `{"a": 2, "b": 1}` will be stored and serialized as `{"a": 2, "b": 1}`. - When comparing objects, the order of the name/value pairs is irrelevant. This makes objects interoperable in the sense that they will not be affected by these differences. For instance, `{"b": 1, "a": 2}` and `{"a": 2, "b": 1}` will be treated as equal. #### Limits [RFC 7159](http://rfc7159.net/rfc7159) specifies: > An implementation may set limits on the maximum depth of nesting. In this class, the object's limit of nesting is not constraint explicitly. However, a maximum depth of nesting may be introduced by the compiler or runtime environment. A theoretical limit can be queried by calling the @ref max_size function of a JSON object. #### Storage Objects are stored as pointers in a @ref basic_json type. That is, for any access to object values, a pointer of type `object_t*` must be dereferenced. @sa @ref array_t -- type for an array value @since version 1.0.0 */ using object_t = ObjectType, AllocatorType>>; /*! @brief a type for an array [RFC 7159](http://rfc7159.net/rfc7159) describes JSON arrays as follows: > An array is an ordered sequence of zero or more values. To store objects in C++, a type is defined by the template parameters explained below. @tparam ArrayType container type to store arrays (e.g., `std::vector` or `std::list`) @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`) #### Default type With the default values for @a ArrayType (`std::vector`) and @a AllocatorType (`std::allocator`), the default value for @a array_t is: @code {.cpp} std::vector< basic_json, // value_type std::allocator // allocator_type > @endcode #### Limits [RFC 7159](http://rfc7159.net/rfc7159) specifies: > An implementation may set limits on the maximum depth of nesting. In this class, the array's limit of nesting is not constraint explicitly. However, a maximum depth of nesting may be introduced by the compiler or runtime environment. A theoretical limit can be queried by calling the @ref max_size function of a JSON array. #### Storage Arrays are stored as pointers in a @ref basic_json type. That is, for any access to array values, a pointer of type `array_t*` must be dereferenced. @sa @ref object_t -- type for an object value @since version 1.0.0 */ using array_t = ArrayType>; /*! @brief a type for a string [RFC 7159](http://rfc7159.net/rfc7159) describes JSON strings as follows: > A string is a sequence of zero or more Unicode characters. To store objects in C++, a type is defined by the template parameter described below. Unicode values are split by the JSON class into byte-sized characters during deserialization. @tparam StringType the container to store strings (e.g., `std::string`). Note this container is used for keys/names in objects, see @ref object_t. #### Default type With the default values for @a StringType (`std::string`), the default value for @a string_t is: @code {.cpp} std::string @endcode #### String comparison [RFC 7159](http://rfc7159.net/rfc7159) states: > Software implementations are typically required to test names of object > members for equality. Implementations that transform the textual > representation into sequences of Unicode code units and then perform the > comparison numerically, code unit by code unit, are interoperable in the > sense that implementations will agree in all cases on equality or > inequality of two strings. For example, implementations that compare > strings with escaped characters unconverted may incorrectly find that > `"a\\b"` and `"a\u005Cb"` are not equal. This implementation is interoperable as it does compare strings code unit by code unit. #### Storage String values are stored as pointers in a @ref basic_json type. That is, for any access to string values, a pointer of type `string_t*` must be dereferenced. @since version 1.0.0 */ using string_t = StringType; /*! @brief a type for a boolean [RFC 7159](http://rfc7159.net/rfc7159) implicitly describes a boolean as a type which differentiates the two literals `true` and `false`. To store objects in C++, a type is defined by the template parameter @a BooleanType which chooses the type to use. #### Default type With the default values for @a BooleanType (`bool`), the default value for @a boolean_t is: @code {.cpp} bool @endcode #### Storage Boolean values are stored directly inside a @ref basic_json type. @since version 1.0.0 */ using boolean_t = BooleanType; /*! @brief a type for a number (integer) [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: > The representation of numbers is similar to that used in most programming > languages. A number is represented in base 10 using decimal digits. It > contains an integer component that may be prefixed with an optional minus > sign, which may be followed by a fraction part and/or an exponent part. > Leading zeros are not allowed. (...) Numeric values that cannot be > represented in the grammar below (such as Infinity and NaN) are not > permitted. This description includes both integer and floating-point numbers. However, C++ allows more precise storage if it is known whether the number is an integer or a floating-point number. Therefore, two different types, @ref number_integer_t and @ref number_float_t are used. To store integer numbers in C++, a type is defined by the template parameter @a NumberIntegerType which chooses the type to use. #### Default type With the default values for @a NumberIntegerType (`int64_t`), the default value for @a number_integer_t is: @code {.cpp} int64_t @endcode #### Default behavior - The restrictions about leading zeros is not enforced in C++. Instead, leading zeros in integer literals lead to an interpretation as octal number. Internally, the value will be stored as decimal number. For instance, the C++ integer literal `010` will be serialized to `8`. During deserialization, leading zeros yield an error. - Not-a-number (NaN) values will be serialized to `null`. #### Limits [RFC 7159](http://rfc7159.net/rfc7159) specifies: > An implementation may set limits on the range and precision of numbers. When the default type is used, the maximal integer number that can be stored is `9223372036854775807` (INT64_MAX) and the minimal integer number that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers that are out of range will yield over/underflow when used in a constructor. During deserialization, too large or small integer numbers will be automatically be stored as @ref number_float_t. [RFC 7159](http://rfc7159.net/rfc7159) further states: > Note that when such software is used, numbers that are integers and are > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense > that implementations will agree exactly on their numeric values. As this range is a subrange of the exactly supported range [INT64_MIN, INT64_MAX], this class's integer type is interoperable. #### Storage Integer number values are stored directly inside a @ref basic_json type. @sa @ref number_float_t -- type for number values (floating-point) @since version 1.0.0 */ using number_integer_t = NumberIntegerType; /*! @brief a type for a number (floating-point) [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: > The representation of numbers is similar to that used in most programming > languages. A number is represented in base 10 using decimal digits. It > contains an integer component that may be prefixed with an optional minus > sign, which may be followed by a fraction part and/or an exponent part. > Leading zeros are not allowed. (...) Numeric values that cannot be > represented in the grammar below (such as Infinity and NaN) are not > permitted. This description includes both integer and floating-point numbers. However, C++ allows more precise storage if it is known whether the number is an integer or a floating-point number. Therefore, two different types, @ref number_integer_t and @ref number_float_t are used. To store floating-point numbers in C++, a type is defined by the template parameter @a NumberFloatType which chooses the type to use. #### Default type With the default values for @a NumberFloatType (`double`), the default value for @a number_float_t is: @code {.cpp} double @endcode #### Default behavior - The restrictions about leading zeros is not enforced in C++. Instead, leading zeros in floating-point literals will be ignored. Internally, the value will be stored as decimal number. For instance, the C++ floating-point literal `01.2` will be serialized to `1.2`. During deserialization, leading zeros yield an error. - Not-a-number (NaN) values will be serialized to `null`. #### Limits [RFC 7159](http://rfc7159.net/rfc7159) states: > This specification allows implementations to set limits on the range and > precision of numbers accepted. Since software that implements IEEE > 754-2008 binary64 (double precision) numbers is generally available and > widely used, good interoperability can be achieved by implementations that > expect no more precision or range than these provide, in the sense that > implementations will approximate JSON numbers within the expected > precision. This implementation does exactly follow this approach, as it uses double precision floating-point numbers. Note values smaller than `-1.79769313486232e+308` and values greather than `1.79769313486232e+308` will be stored as NaN internally and be serialized to `null`. #### Storage Floating-point number values are stored directly inside a @ref basic_json type. @sa @ref number_integer_t -- type for number values (integer) @since version 1.0.0 */ using number_float_t = NumberFloatType; /// @} /////////////////////////// // JSON type enumeration // /////////////////////////// /*! @brief the JSON type enumeration This enumeration collects the different JSON types. It is internally used to distinguish the stored values, and the functions @ref is_null(), @ref is_object(), @ref is_array(), @ref is_string(), @ref is_boolean(), @ref is_number(), and @ref is_discarded() rely on it. @since version 1.0.0 */ enum class value_t : uint8_t { null, ///< null value object, ///< object (unordered set of name/value pairs) array, ///< array (ordered collection of values) string, ///< string value boolean, ///< boolean value number_integer, ///< number value (integer) number_float, ///< number value (floating-point) discarded ///< discarded by the the parser callback function }; private: /// helper for exception-safe object creation template static T* create(Args&& ... args) { AllocatorType alloc; auto deleter = [&](T * object) { alloc.deallocate(object, 1); }; std::unique_ptr object(alloc.allocate(1), deleter); alloc.construct(object.get(), std::forward(args)...); return object.release(); } //////////////////////// // JSON value storage // //////////////////////// /*! @brief a JSON value The actual storage for a JSON value of the @ref basic_json class. @since version 1.0.0 */ union json_value { /// object (stored with pointer to save storage) object_t* object; /// array (stored with pointer to save storage) array_t* array; /// string (stored with pointer to save storage) string_t* string; /// boolean boolean_t boolean; /// number (integer) number_integer_t number_integer; /// number (floating-point) number_float_t number_float; /// default constructor (for null values) json_value() noexcept = default; /// constructor for booleans json_value(boolean_t v) noexcept : boolean(v) {} /// constructor for numbers (integer) json_value(number_integer_t v) noexcept : number_integer(v) {} /// constructor for numbers (floating-point) json_value(number_float_t v) noexcept : number_float(v) {} /// constructor for empty values of a given type json_value(value_t t) { switch (t) { case value_t::object: { object = create(); break; } case value_t::array: { array = create(); break; } case value_t::string: { string = create(""); break; } case value_t::boolean: { boolean = boolean_t(false); break; } case value_t::number_integer: { number_integer = number_integer_t(0); break; } case value_t::number_float: { number_float = number_float_t(0.0); break; } default: { break; } } } /// constructor for strings json_value(const string_t& value) { string = create(value); } /// constructor for objects json_value(const object_t& value) { object = create(value); } /// constructor for arrays json_value(const array_t& value) { array = create(value); } }; public: ////////////////////////// // JSON parser callback // ////////////////////////// /*! @brief JSON callback events This enumeration lists the parser events that can trigger calling a callback function of type @ref parser_callback_t during parsing. @since version 1.0.0 */ enum class parse_event_t : uint8_t { /// the parser read `{` and started to process a JSON object object_start, /// the parser read `}` and finished processing a JSON object object_end, /// the parser read `[` and started to process a JSON array array_start, /// the parser read `]` and finished processing a JSON array array_end, /// the parser read a key of a value in an object key, /// the parser finished reading a JSON value value }; /*! @brief per-element parser callback type With a parser callback function, the result of parsing a JSON text can be influenced. When passed to @ref parse(std::istream&, parser_callback_t) or @ref parse(const string_t&, parser_callback_t), it is called on certain events (passed as @ref parse_event_t via parameter @a event) with a set recursion depth @a depth and context JSON value @a parsed. The return value of the callback function is a boolean indicating whether the element that emitted the callback shall be kept or not. We distinguish six scenarios (determined by the event type) in which the callback function can be called. The following table describes the values of the parameters @a depth, @a event, and @a parsed. parameter @a event | description | parameter @a depth | parameter @a parsed ------------------ | ----------- | ------------------ | ------------------- parse_event_t::object_start | the parser read `{` and started to process a JSON object | depth of the parent of the JSON object | a JSON value with type discarded parse_event_t::key | the parser read a key of a value in an object | depth of the currently parsed JSON object | a JSON string containing the key parse_event_t::object_end | the parser read `}` and finished processing a JSON object | depth of the parent of the JSON object | the parsed JSON object parse_event_t::array_start | the parser read `[` and started to process a JSON array | depth of the parent of the JSON array | a JSON value with type discarded parse_event_t::array_end | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array parse_event_t::value | the parser finished reading a JSON value | depth of the value | the parsed JSON value Discarding a value (i.e., returning `false`) has different effects depending on the context in which function was called: - Discarded values in structured types are skipped. That is, the parser will behave as if the discarded value was never read. - In case a value outside a structured type is skipped, it is replaced with `null`. This case happens if the top-level element is skipped. @param[in] depth the depth of the recursion during parsing @param[in] event an event of type parse_event_t indicating the context in the callback function has been called @param[in,out] parsed the current intermediate parse result; note that writing to this value has no effect for parse_event_t::key events @return Whether the JSON value which called the function during parsing should be kept (`true`) or not (`false`). In the latter case, it is either skipped completely or replaced by an empty discarded object. @sa @ref parse(std::istream&, parser_callback_t) or @ref parse(const string_t&, parser_callback_t) for examples @since version 1.0.0 */ using parser_callback_t = std::function; ////////////////// // constructors // ////////////////// /// @name constructors and destructors /// @{ /*! @brief create an empty value with a given type Create an empty JSON value with a given type. The value will be default initialized with an empty value which depends on the type: Value type | initial value ----------- | ------------- null | `null` boolean | `false` string | `""` number | `0` object | `{}` array | `[]` @param[in] value_type the type of the value to create @complexity Constant. @throw std::bad_alloc if allocation for object, array, or string value fails @liveexample{The following code shows the constructor for different @ref value_t values,basic_json__value_t} @sa @ref basic_json(std::nullptr_t) -- create a `null` value @sa @ref basic_json(boolean_t value) -- create a boolean value @sa @ref basic_json(const string_t&) -- create a string value @sa @ref basic_json(const object_t&) -- create a object value @sa @ref basic_json(const array_t&) -- create a array value @sa @ref basic_json(const number_float_t) -- create a number (floating-point) value @sa @ref basic_json(const number_integer_t) -- create a number (integer) value @since version 1.0.0 */ basic_json(const value_t value_type) : m_type(value_type), m_value(value_type) {} /*! @brief create a null object (implicitly) Create a `null` JSON value. This is the implicit version of the `null` value constructor as it takes no parameters. @complexity Constant. @requirement This function satisfies the Container requirements: - The complexity is constant. - As postcondition, it holds: `basic_json().empty() == true`. @liveexample{The following code shows the constructor for a `null` JSON value.,basic_json} @sa @ref basic_json(std::nullptr_t) -- create a `null` value @since version 1.0.0 */ basic_json() noexcept = default; /*! @brief create a null object (explicitly) Create a `null` JSON value. This is the explicitly version of the `null` value constructor as it takes a null pointer as parameter. It allows to create `null` values by explicitly assigning a @c nullptr to a JSON value. The passed null pointer itself is not read -- it is only used to choose the right constructor. @complexity Constant. @liveexample{The following code shows the constructor with null pointer parameter.,basic_json__nullptr_t} @sa @ref basic_json() -- default constructor (implicitly creating a `null` value) @since version 1.0.0 */ basic_json(std::nullptr_t) noexcept : basic_json(value_t::null) {} /*! @brief create an object (explicit) Create an object JSON value with a given content. @param[in] val a value for the object @complexity Linear in the size of the passed @a val. @throw std::bad_alloc if allocation for object value fails @liveexample{The following code shows the constructor with an @ref object_t parameter.,basic_json__object_t} @sa @ref basic_json(const CompatibleObjectType&) -- create an object value from a compatible STL container @since version 1.0.0 */ basic_json(const object_t& val) : m_type(value_t::object), m_value(val) {} /*! @brief create an object (implicit) Create an object JSON value with a given content. This constructor allows any type that can be used to construct values of type @ref object_t. Examples include the types `std::map` and `std::unordered_map`. @tparam CompatibleObjectType an object type whose `key_type` and `value_type` is compatible to @ref object_t @param[in] val a value for the object @complexity Linear in the size of the passed @a val. @throw std::bad_alloc if allocation for object value fails @liveexample{The following code shows the constructor with several compatible object type parameters.,basic_json__CompatibleObjectType} @sa @ref basic_json(const object_t&) -- create an object value @since version 1.0.0 */ template ::value and std::is_constructible::value, int>::type = 0> basic_json(const CompatibleObjectType& val) : m_type(value_t::object) { using std::begin; using std::end; m_value.object = create(begin(val), end(val)); } /*! @brief create an array (explicit) Create an array JSON value with a given content. @param[in] val a value for the array @complexity Linear in the size of the passed @a val. @throw std::bad_alloc if allocation for array value fails @liveexample{The following code shows the constructor with an @ref array_t parameter.,basic_json__array_t} @sa @ref basic_json(const CompatibleArrayType&) -- create an array value from a compatible STL containers @since version 1.0.0 */ basic_json(const array_t& val) : m_type(value_t::array), m_value(val) {} /*! @brief create an array (implicit) Create an array JSON value with a given content. This constructor allows any type that can be used to construct values of type @ref array_t. Examples include the types `std::vector`, `std::list`, and `std::set`. @tparam CompatibleArrayType an object type whose `value_type` is compatible to @ref array_t @param[in] val a value for the array @complexity Linear in the size of the passed @a val. @throw std::bad_alloc if allocation for array value fails @liveexample{The following code shows the constructor with several compatible array type parameters.,basic_json__CompatibleArrayType} @sa @ref basic_json(const array_t&) -- create an array value @since version 1.0.0 */ template ::value and not std::is_same::value and not std::is_same::value and not std::is_same::value and not std::is_same::value and not std::is_same::value and std::is_constructible::value, int>::type = 0> basic_json(const CompatibleArrayType& val) : m_type(value_t::array) { using std::begin; using std::end; m_value.array = create(begin(val), end(val)); } /*! @brief create a string (explicit) Create an string JSON value with a given content. @param[in] val a value for the string @complexity Linear in the size of the passed @a val. @throw std::bad_alloc if allocation for string value fails @liveexample{The following code shows the constructor with an @ref string_t parameter.,basic_json__string_t} @sa @ref basic_json(const typename string_t::value_type*) -- create a string value from a character pointer @sa @ref basic_json(const CompatibleStringType&) -- create a string value from a compatible string container @since version 1.0.0 */ basic_json(const string_t& val) : m_type(value_t::string), m_value(val) {} /*! @brief create a string (explicit) Create a string JSON value with a given content. @param[in] val a literal value for the string @complexity Linear in the size of the passed @a val. @throw std::bad_alloc if allocation for string value fails @liveexample{The following code shows the constructor with string literal parameter.,basic_json__string_t_value_type} @sa @ref basic_json(const string_t&) -- create a string value @sa @ref basic_json(const CompatibleStringType&) -- create a string value from a compatible string container @since version 1.0.0 */ basic_json(const typename string_t::value_type* val) : basic_json(string_t(val)) {} /*! @brief create a string (implicit) Create a string JSON value with a given content. @param[in] val a value for the string @tparam CompatibleStringType an string type which is compatible to @ref string_t @complexity Linear in the size of the passed @a val. @throw std::bad_alloc if allocation for string value fails @liveexample{The following code shows the construction of a string value from a compatible type.,basic_json__CompatibleStringType} @sa @ref basic_json(const string_t&) -- create a string value @sa @ref basic_json(const typename string_t::value_type*) -- create a string value from a character pointer @since version 1.0.0 */ template ::value, int>::type = 0> basic_json(const CompatibleStringType& val) : basic_json(string_t(val)) {} /*! @brief create a boolean (explicit) Creates a JSON boolean type from a given value. @param[in] val a boolean value to store @complexity Constant. @liveexample{The example below demonstrates boolean values.,basic_json__boolean_t} @since version 1.0.0 */ basic_json(boolean_t val) : m_type(value_t::boolean), m_value(val) {} /*! @brief create an integer number (explicit) Create an interger number JSON value with a given content. @tparam T helper type to compare number_integer_t and int (not visible in) the interface. @param[in] val an integer to create a JSON number from @note This constructor would have the same signature as @ref basic_json(const int value), so we need to switch this one off in case number_integer_t is the same as int. This is done via the helper type @a T. @complexity Constant. @liveexample{The example below shows the construction of a JSON integer number value.,basic_json__number_integer_t} @sa @ref basic_json(const int) -- create a number value (integer) @sa @ref basic_json(const CompatibleNumberIntegerType) -- create a number value (integer) from a compatible number type @since version 1.0.0 */ template::value) and std::is_same::value , int>::type = 0> basic_json(const number_integer_t val) : m_type(value_t::number_integer), m_value(val) {} /*! @brief create an integer number from an enum type (explicit) Create an integer number JSON value with a given content. @param[in] val an integer to create a JSON number from @note This constructor allows to pass enums directly to a constructor. As C++ has no way of specifying the type of an anonymous enum explicitly, we can only rely on the fact that such values implicitly convert to int. As int may already be the same type of number_integer_t, we may need to switch off the constructor @ref basic_json(const number_integer_t). @complexity Constant. @liveexample{The example below shows the construction of a JSON integer number value from an anonymous enum.,basic_json__const_int} @sa @ref basic_json(const number_integer_t) -- create a number value (integer) @sa @ref basic_json(const CompatibleNumberIntegerType) -- create a number value (integer) from a compatible number type @since version 1.0.0 */ basic_json(const int val) : m_type(value_t::number_integer), m_value(static_cast(val)) {} /*! @brief create an integer number (implicit) Create an integer number JSON value with a given content. This constructor allows any type that can be used to construct values of type @ref number_integer_t. Examples may include the types `int`, `int32_t`, or `short`. @tparam CompatibleNumberIntegerType an integer type which is compatible to @ref number_integer_t. @param[in] val an integer to create a JSON number from @complexity Constant. @liveexample{The example below shows the construction of several JSON integer number values from compatible types.,basic_json__CompatibleIntegerNumberType} @sa @ref basic_json(const number_integer_t) -- create a number value (integer) @sa @ref basic_json(const int) -- create a number value (integer) @since version 1.0.0 */ template::value and std::numeric_limits::is_integer, CompatibleNumberIntegerType>::type = 0> basic_json(const CompatibleNumberIntegerType val) noexcept : m_type(value_t::number_integer), m_value(static_cast(val)) {} /*! @brief create a floating-point number (explicit) Create a floating-point number JSON value with a given content. @param[in] val a floating-point value to create a JSON number from @note RFC 7159 , section 6 disallows NaN values: > Numeric values that cannot be represented in the grammar below (such > as Infinity and NaN) are not permitted. In case the parameter @a val is not a number, a JSON null value is created instead. @complexity Constant. @liveexample{The following example creates several floating-point values.,basic_json__number_float_t} @sa @ref basic_json(const CompatibleNumberFloatType) -- create a number value (floating-point) from a compatible number type @since version 1.0.0 */ basic_json(const number_float_t val) : m_type(value_t::number_float), m_value(val) { // replace infinity and NAN by null if (not std::isfinite(val)) { m_type = value_t::null; m_value = json_value(); } } /*! @brief create an floating-point number (implicit) Create an floating-point number JSON value with a given content. This constructor allows any type that can be used to construct values of type @ref number_float_t. Examples may include the types `float`. @tparam CompatibleNumberFloatType a floating-point type which is compatible to @ref number_float_t. @param[in] val a floating-point to create a JSON number from @note RFC 7159 , section 6 disallows NaN values: > Numeric values that cannot be represented in the grammar below (such > as Infinity and NaN) are not permitted. In case the parameter @a val is not a number, a JSON null value is created instead. @complexity Constant. @liveexample{The example below shows the construction of several JSON floating-point number values from compatible types.,basic_json__CompatibleNumberFloatType} @sa @ref basic_json(const number_float_t) -- create a number value (floating-point) @since version 1.0.0 */ template::value and std::is_floating_point::value>::type > basic_json(const CompatibleNumberFloatType val) noexcept : basic_json(number_float_t(val)) {} /*! @brief create a container (array or object) from an initializer list Creates a JSON value of type array or object from the passed initializer list @a init. In case @a type_deduction is `true` (default), the type of the JSON value to be created is deducted from the initializer list @a init according to the following rules: 1. If the list is empty, an empty JSON object value `{}` is created. 2. If the list consists of pairs whose first element is a string, a JSON object value is created where the first elements of the pairs are treated as keys and the second elements are as values. 3. In all other cases, an array is created. The rules aim to create the best fit between a C++ initializer list and JSON values. The ratioinale is as follows: 1. The empty initializer list is written as `{}` which is exactly an empty JSON object. 2. C++ has now way of describing mapped types other than to list a list of pairs. As JSON requires that keys must be of type string, rule 2 is the weakest constraint one can pose on initializer lists to interpret them as an object. 3. In all other cases, the initializer list could not be interpreted as JSON object type, so interpreting it as JSON array type is safe. With the rules described above, the following JSON values cannot be expressed by an initializer list: - the empty array (`[]`): use @ref array(std::initializer_list) with an empty initializer list in this case - arrays whose elements satisfy rule 2: use @ref array(std::initializer_list) with the same initializer list in this case @note When used without parentheses around an empty initializer list, @ref basic_json() is called instead of this function, yielding the JSON null value. @param[in] init initializer list with JSON values @param[in] type_deduction internal parameter; when set to `true`, the type of the JSON value is deducted from the initializer list @a init; when set to `false`, the type provided via @a manual_type is forced. This mode is used by the functions @ref array(std::initializer_list) and @ref object(std::initializer_list). @param[in] manual_type internal parameter; when @a type_deduction is set to `false`, the created JSON value will use the provided type (only @ref value_t::array and @ref value_t::object are valid); when @a type_deduction is set to `true`, this parameter has no effect @throw std::domain_error if @a type_deduction is `false`, @a manual_type is `value_t::object`, but @a init contains an element which is not a pair whose first element is a string; example: `"cannot create object from initializer list"` @complexity Linear in the size of the initializer list @a init. @liveexample{The example below shows how JSON values are created from initializer lists,basic_json__list_init_t} @sa @ref array(std::initializer_list) -- create a JSON array value from an initializer list @sa @ref object(std::initializer_list) -- create a JSON object value from an initializer list @since version 1.0.0 */ basic_json(std::initializer_list init, bool type_deduction = true, value_t manual_type = value_t::array) { // the initializer list could describe an object bool is_an_object = true; // check if each element is an array with two elements whose first // element is a string for (const auto& element : init) { if (not element.is_array() or element.size() != 2 or not element[0].is_string()) { // we found an element that makes it impossible to use the // initializer list as object is_an_object = false; break; } } // adjust type if type deduction is not wanted if (not type_deduction) { // if array is wanted, do not create an object though possible if (manual_type == value_t::array) { is_an_object = false; } // if object is wanted but impossible, throw an exception if (manual_type == value_t::object and not is_an_object) { throw std::domain_error("cannot create object from initializer list"); } } if (is_an_object) { // the initializer list is a list of pairs -> create object m_type = value_t::object; m_value = value_t::object; for (auto& element : init) { m_value.object->emplace(std::move(*(element[0].m_value.string)), std::move(element[1])); } } else { // the initializer list describes an array -> create array m_type = value_t::array; m_value.array = create(std::move(init)); } } /*! @brief explicitly create an array from an initializer list Creates a JSON array value from a given initializer list. That is, given a list of values `a, b, c`, creates the JSON value `[a, b, c]`. If the initializer list is empty, the empty array `[]` is created. @note This function is only needed to express two edge cases that cannot be realized with the initializer list constructor (@ref basic_json(std::initializer_list, bool, value_t)). These cases are: 1. creating an array whose elements are all pairs whose first element is a string -- in this case, the initializer list constructor would create an object, taking the first elements as keys 2. creating an empty array -- passing the empty initializer list to the initializer list constructor yields an empty object @param[in] init initializer list with JSON values to create an array from (optional) @return JSON array value @complexity Linear in the size of @a init. @liveexample{The following code shows an example for the @ref array function.,array} @sa @ref basic_json(std::initializer_list, bool, value_t) -- create a JSON value from an initializer list @sa @ref object(std::initializer_list) -- create a JSON object value from an initializer list @since version 1.0.0 */ static basic_json array(std::initializer_list init = std::initializer_list()) { return basic_json(init, false, value_t::array); } /*! @brief explicitly create an object from an initializer list Creates a JSON object value from a given initializer list. The initializer lists elements must be pairs, and their first elments must be strings. If the initializer list is empty, the empty object `{}` is created. @note This function is only added for symmetry reasons. In contrast to the related function @ref array(std::initializer_list), there are no cases which can only be expressed by this function. That is, any initializer list @a init can also be passed to the initializer list constructor @ref basic_json(std::initializer_list, bool, value_t). @param[in] init initializer list to create an object from (optional) @return JSON object value @throw std::domain_error if @a init is not a pair whose first elements are strings; thrown by @ref basic_json(std::initializer_list, bool, value_t) @complexity Linear in the size of @a init. @liveexample{The following code shows an example for the @ref object function.,object} @sa @ref basic_json(std::initializer_list, bool, value_t) -- create a JSON value from an initializer list @sa @ref array(std::initializer_list) -- create a JSON array value from an initializer list @since version 1.0.0 */ static basic_json object(std::initializer_list init = std::initializer_list()) { return basic_json(init, false, value_t::object); } /*! @brief construct an array with count copies of given value Constructs a JSON array value by creating @a cnt copies of a passed value. In case @a cnt is `0`, an empty array is created. As postcondition, `std::distance(begin(),end()) == cnt` holds. @param[in] cnt the number of JSON copies of @a val to create @param[in] val the JSON value to copy @complexity Linear in @a cnt. @liveexample{The following code shows examples for the @ref basic_json(size_type\, const basic_json&) constructor.,basic_json__size_type_basic_json} @since version 1.0.0 */ basic_json(size_type cnt, const basic_json& val) : m_type(value_t::array) { m_value.array = create(cnt, val); } /*! @brief construct a JSON container given an iterator range Constructs the JSON value with the contents of the range `[first, last)`. The semantics depends on the different types a JSON value can have: - In case of primitive types (number, boolean, or string), @a first must be `begin()` and @a last must be `end()`. In this case, the value is copied. Otherwise, std::out_of_range is thrown. - In case of structured types (array, object), the constructor behaves as similar versions for `std::vector`. - In case of a null type, std::domain_error is thrown. @tparam InputIT an input iterator type (@ref iterator or @ref const_iterator) @param[in] first begin of the range to copy from (included) @param[in] last end of the range to copy from (excluded) @throw std::domain_error if iterators are not compatible; that is, do not belong to the same JSON value; example: `"iterators are not compatible"` @throw std::out_of_range if iterators are for a primitive type (number, boolean, or string) where an out of range error can be detected easily; example: `"iterators out of range"` @throw std::bad_alloc if allocation for object, array, or string fails @throw std::domain_error if called with a null value; example: `"cannot use construct with iterators from null"` @complexity Linear in distance between @a first and @a last. @liveexample{The example below shows several ways to create JSON values by specifying a subrange with iterators.,basic_json__InputIt_InputIt} @since version 1.0.0 */ template ::value or std::is_same::value , int>::type = 0> basic_json(InputIT first, InputIT last) : m_type(first.m_object->m_type) { // make sure iterator fits the current value if (first.m_object != last.m_object) { throw std::domain_error("iterators are not compatible"); } // check if iterator range is complete for primitive values switch (m_type) { case value_t::boolean: case value_t::number_float: case value_t::number_integer: case value_t::string: { if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) { throw std::out_of_range("iterators out of range"); } break; } default: { break; } } switch (m_type) { case value_t::number_integer: { m_value.number_integer = first.m_object->m_value.number_integer; break; } case value_t::number_float: { m_value.number_float = first.m_object->m_value.number_float; break; } case value_t::boolean: { m_value.boolean = first.m_object->m_value.boolean; break; } case value_t::string: { m_value = *first.m_object->m_value.string; break; } case value_t::object: { m_value.object = create(first.m_it.object_iterator, last.m_it.object_iterator); break; } case value_t::array: { m_value.array = create(first.m_it.array_iterator, last.m_it.array_iterator); break; } default: { throw std::domain_error("cannot use construct with iterators from " + first.m_object->type_name()); } } } /////////////////////////////////////// // other constructors and destructor // /////////////////////////////////////// /*! @brief copy constructor Creates a copy of a given JSON value. @param[in] other the JSON value to copy @complexity Linear in the size of @a other. @requirement This function satisfies the Container requirements: - The complexity is linear. - As postcondition, it holds: `other == basic_json(other)`. @throw std::bad_alloc if allocation for object, array, or string fails. @liveexample{The following code shows an example for the copy constructor.,basic_json__basic_json} @since version 1.0.0 */ basic_json(const basic_json& other) : m_type(other.m_type) { switch (m_type) { case value_t::object: { m_value = *other.m_value.object; break; } case value_t::array: { m_value = *other.m_value.array; break; } case value_t::string: { m_value = *other.m_value.string; break; } case value_t::boolean: { m_value = other.m_value.boolean; break; } case value_t::number_integer: { m_value = other.m_value.number_integer; break; } case value_t::number_float: { m_value = other.m_value.number_float; break; } default: { break; } } } /*! @brief move constructor Move constructor. Constructs a JSON value with the contents of the given value @a other using move semantics. It "steals" the resources from @a other and leaves it as JSON null value. @param[in,out] other value to move to this object @post @a other is a JSON null value @complexity Constant. @liveexample{The code below shows the move constructor explicitly called via std::move.,basic_json__moveconstructor} @since version 1.0.0 */ basic_json(basic_json&& other) noexcept : m_type(std::move(other.m_type)), m_value(std::move(other.m_value)) { // invalidate payload other.m_type = value_t::null; other.m_value = {}; } /*! @brief copy assignment Copy assignment operator. Copies a JSON value via the "copy and swap" strategy: It is expressed in terms of the copy constructor, destructor, and the swap() member function. @param[in] other value to copy from @complexity Linear. @requirement This function satisfies the Container requirements: - The complexity is linear. @liveexample{The code below shows and example for the copy assignment. It creates a copy of value `a` which is then swapped with `b`. Finally\, the copy of `a` (which is the null value after the swap) is destroyed.,basic_json__copyassignment} @since version 1.0.0 */ reference& operator=(basic_json other) noexcept ( std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable::value and std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable::value ) { using std::swap; swap(m_type, other.m_type); swap(m_value, other.m_value); return *this; } /*! @brief destructor Destroys the JSON value and frees all allocated memory. @complexity Linear. @requirement This function satisfies the Container requirements: - The complexity is linear. - All stored elements are destroyed and all memory is freed. @since version 1.0.0 */ ~basic_json() { switch (m_type) { case value_t::object: { AllocatorType alloc; alloc.destroy(m_value.object); alloc.deallocate(m_value.object, 1); break; } case value_t::array: { AllocatorType alloc; alloc.destroy(m_value.array); alloc.deallocate(m_value.array, 1); break; } case value_t::string: { AllocatorType alloc; alloc.destroy(m_value.string); alloc.deallocate(m_value.string, 1); break; } default: { // all other types need no specific destructor break; } } } /// @} public: /////////////////////// // object inspection // /////////////////////// /// @name object inspection /// @{ /*! @brief serialization Serialization function for JSON values. The function tries to mimick Python's @p json.dumps() function, and currently supports its @p indent parameter. @param[in] indent if indent is nonnegative, then array elements and object members will be pretty-printed with that indent level. An indent level of 0 will only insert newlines. -1 (the default) selects the most compact representation @return string containing the serialization of the JSON value @complexity Linear. @liveexample{The following example shows the effect of different @a indent parameters to the result of the serializaion.,dump} @see https://docs.python.org/2/library/json.html#json.dump @since version 1.0.0 */ string_t dump(const int indent = -1) const { std::stringstream ss; if (indent >= 0) { dump(ss, true, static_cast(indent)); } else { dump(ss, false, 0); } return ss.str(); } /*! @brief return the type of the JSON value (explicit) Return the type of the JSON value as a value from the @ref value_t enumeration. @return the type of the JSON value @complexity Constant. @liveexample{The following code exemplifies @ref type() for all JSON types.,type} @since version 1.0.0 */ value_t type() const noexcept { return m_type; } /*! @brief return whether type is primitive This function returns true iff the JSON type is primitive (string, number, boolean, or null). @return `true` if type is primitive (string, number, boolean, or null), `false` otherwise. @complexity Constant. @liveexample{The following code exemplifies @ref is_primitive for all JSON types.,is_primitive} @since version 1.0.0 */ bool is_primitive() const noexcept { return is_null() or is_string() or is_boolean() or is_number(); } /*! @brief return whether type is structured This function returns true iff the JSON type is structured (array or object). @return `true` if type is structured (array or object), `false` otherwise. @complexity Constant. @liveexample{The following code exemplifies @ref is_structured for all JSON types.,is_structured} @since version 1.0.0 */ bool is_structured() const noexcept { return is_array() or is_object(); } /*! @brief return whether value is null This function returns true iff the JSON value is null. @return `true` if type is null, `false` otherwise. @complexity Constant. @liveexample{The following code exemplifies @ref is_null for all JSON types.,is_null} @since version 1.0.0 */ bool is_null() const noexcept { return m_type == value_t::null; } /*! @brief return whether value is a boolean This function returns true iff the JSON value is a boolean. @return `true` if type is boolean, `false` otherwise. @complexity Constant. @liveexample{The following code exemplifies @ref is_boolean for all JSON types.,is_boolean} @since version 1.0.0 */ bool is_boolean() const noexcept { return m_type == value_t::boolean; } /*! @brief return whether value is a number This function returns true iff the JSON value is a number. This includes both integer and floating-point values. @return `true` if type is number (regardless whether integer or floating-type), `false` otherwise. @complexity Constant. @liveexample{The following code exemplifies @ref is_number for all JSON types.,is_number} @sa @ref is_number_integer() -- check if value is an integer number @sa @ref is_number_float() -- check if value is a floating-point number @since version 1.0.0 */ bool is_number() const noexcept { return is_number_integer() or is_number_float(); } /*! @brief return whether value is an integer number This function returns true iff the JSON value is an integer number. This excludes floating-point values. @return `true` if type is an integer number, `false` otherwise. @complexity Constant. @liveexample{The following code exemplifies @ref is_number_integer for all JSON types.,is_number_integer} @sa @ref is_number() -- check if value is a number @sa @ref is_number_float() -- check if value is a floating-point number @since version 1.0.0 */ bool is_number_integer() const noexcept { return m_type == value_t::number_integer; } /*! @brief return whether value is a floating-point number This function returns true iff the JSON value is a floating-point number. This excludes integer values. @return `true` if type is a floating-point number, `false` otherwise. @complexity Constant. @liveexample{The following code exemplifies @ref is_number_float for all JSON types.,is_number_float} @sa @ref is_number() -- check if value is number @sa @ref is_number_integer() -- check if value is an integer number @since version 1.0.0 */ bool is_number_float() const noexcept { return m_type == value_t::number_float; } /*! @brief return whether value is an object This function returns true iff the JSON value is an object. @return `true` if type is object, `false` otherwise. @complexity Constant. @liveexample{The following code exemplifies @ref is_object for all JSON types.,is_object} @since version 1.0.0 */ bool is_object() const noexcept { return m_type == value_t::object; } /*! @brief return whether value is an array This function returns true iff the JSON value is an array. @return `true` if type is array, `false` otherwise. @complexity Constant. @liveexample{The following code exemplifies @ref is_array for all JSON types.,is_array} @since version 1.0.0 */ bool is_array() const noexcept { return m_type == value_t::array; } /*! @brief return whether value is a string This function returns true iff the JSON value is a string. @return `true` if type is string, `false` otherwise. @complexity Constant. @liveexample{The following code exemplifies @ref is_string for all JSON types.,is_string} @since version 1.0.0 */ bool is_string() const noexcept { return m_type == value_t::string; } /*! @brief return whether value is discarded This function returns true iff the JSON value was discarded during parsing with a callback function (see @ref parser_callback_t). @note This function will always be `false` for JSON values after parsing. That is, discarded values can only occur during parsing, but will be removed when inside a structured value or replaced by null in other cases. @return `true` if type is discarded, `false` otherwise. @complexity Constant. @liveexample{The following code exemplifies @ref is_discarded for all JSON types.,is_discarded} @since version 1.0.0 */ bool is_discarded() const noexcept { return m_type == value_t::discarded; } /*! @brief return the type of the JSON value (implicit) Implicitly return the type of the JSON value as a value from the @ref value_t enumeration. @return the type of the JSON value @complexity Constant. @liveexample{The following code exemplifies the value_t operator for all JSON types.,operator__value_t} @since version 1.0.0 */ operator value_t() const noexcept { return m_type; } /// @} private: ////////////////// // value access // ////////////////// /// get an object (explicit) template ::value and std::is_convertible::value , int>::type = 0> T get_impl(T*) const { if (is_object()) { return T(m_value.object->begin(), m_value.object->end()); } else { throw std::domain_error("type must be object, but is " + type_name()); } } /// get an object (explicit) object_t get_impl(object_t*) const { if (is_object()) { return *(m_value.object); } else { throw std::domain_error("type must be object, but is " + type_name()); } } /// get an array (explicit) template ::value and not std::is_same::value and not std::is_arithmetic::value and not std::is_convertible::value and not has_mapped_type::value , int>::type = 0> T get_impl(T*) const { if (is_array()) { T to_vector; std::transform(m_value.array->begin(), m_value.array->end(), std::inserter(to_vector, to_vector.end()), [](basic_json i) { return i.get(); }); return to_vector; } else { throw std::domain_error("type must be array, but is " + type_name()); } } /// get an array (explicit) template ::value and not std::is_same::value , int>::type = 0> std::vector get_impl(std::vector*) const { if (is_array()) { std::vector to_vector; to_vector.reserve(m_value.array->size()); std::transform(m_value.array->begin(), m_value.array->end(), std::inserter(to_vector, to_vector.end()), [](basic_json i) { return i.get(); }); return to_vector; } else { throw std::domain_error("type must be array, but is " + type_name()); } } /// get an array (explicit) template ::value and not has_mapped_type::value , int>::type = 0> T get_impl(T*) const { if (is_array()) { return T(m_value.array->begin(), m_value.array->end()); } else { throw std::domain_error("type must be array, but is " + type_name()); } } /// get an array (explicit) array_t get_impl(array_t*) const { if (is_array()) { return *(m_value.array); } else { throw std::domain_error("type must be array, but is " + type_name()); } } /// get a string (explicit) template ::value , int>::type = 0> T get_impl(T*) const { if (is_string()) { return *m_value.string; } else { throw std::domain_error("type must be string, but is " + type_name()); } } /// get a number (explicit) template::value , int>::type = 0> T get_impl(T*) const { switch (m_type) { case value_t::number_integer: { return static_cast(m_value.number_integer); } case value_t::number_float: { return static_cast(m_value.number_float); } default: { throw std::domain_error("type must be number, but is " + type_name()); } } } /// get a boolean (explicit) boolean_t get_impl(boolean_t*) const { if (is_boolean()) { return m_value.boolean; } else { throw std::domain_error("type must be boolean, but is " + type_name()); } } /// get a pointer to the value (object) object_t* get_impl_ptr(object_t*) noexcept { return is_object() ? m_value.object : nullptr; } /// get a pointer to the value (object) const object_t* get_impl_ptr(const object_t*) const noexcept { return is_object() ? m_value.object : nullptr; } /// get a pointer to the value (array) array_t* get_impl_ptr(array_t*) noexcept { return is_array() ? m_value.array : nullptr; } /// get a pointer to the value (array) const array_t* get_impl_ptr(const array_t*) const noexcept { return is_array() ? m_value.array : nullptr; } /// get a pointer to the value (string) string_t* get_impl_ptr(string_t*) noexcept { return is_string() ? m_value.string : nullptr; } /// get a pointer to the value (string) const string_t* get_impl_ptr(const string_t*) const noexcept { return is_string() ? m_value.string : nullptr; } /// get a pointer to the value (boolean) boolean_t* get_impl_ptr(boolean_t*) noexcept { return is_boolean() ? &m_value.boolean : nullptr; } /// get a pointer to the value (boolean) const boolean_t* get_impl_ptr(const boolean_t*) const noexcept { return is_boolean() ? &m_value.boolean : nullptr; } /// get a pointer to the value (integer number) number_integer_t* get_impl_ptr(number_integer_t*) noexcept { return is_number_integer() ? &m_value.number_integer : nullptr; } /// get a pointer to the value (integer number) const number_integer_t* get_impl_ptr(const number_integer_t*) const noexcept { return is_number_integer() ? &m_value.number_integer : nullptr; } /// get a pointer to the value (floating-point number) number_float_t* get_impl_ptr(number_float_t*) noexcept { return is_number_float() ? &m_value.number_float : nullptr; } /// get a pointer to the value (floating-point number) const number_float_t* get_impl_ptr(const number_float_t*) const noexcept { return is_number_float() ? &m_value.number_float : nullptr; } public: /// @name value access /// @{ /*! @brief get a value (explicit) Explicit type conversion between the JSON value and a compatible value. @tparam ValueType non-pointer type compatible to the JSON value, for instance `int` for JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for JSON arrays @return copy of the JSON value, converted to type @a ValueType @throw std::domain_error in case passed type @a ValueType is incompatible to JSON; example: `"type must be object, but is null"` @complexity Linear in the size of the JSON value. @liveexample{The example below shows serveral conversions from JSON values to other types. There a few things to note: (1) Floating-point numbers can be converted to integers\, (2) A JSON array can be converted to a standard `std::vector`\, (3) A JSON object can be converted to C++ assiciative containers such as `std::unordered_map`.,get__ValueType_const} @internal The idea of using a casted null pointer to choose the correct implementation is from . @endinternal @sa @ref operator ValueType() const for implicit conversion @sa @ref get() for pointer-member access @since version 1.0.0 */ template::value , int>::type = 0> ValueType get() const { return get_impl(static_cast(nullptr)); } /*! @brief get a pointer value (explicit) Explicit pointer access to the internally stored JSON value. No copies are made. @warning The pointer becomes invalid if the underlying JSON object changes. @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or @ref number_float_t. @return pointer to the internally stored JSON value if the requested pointer type @a PointerType fits to the JSON value; `nullptr` otherwise @complexity Constant. @liveexample{The example below shows how pointers to internal values of a JSON value can be requested. Note that no type conversions are made and a `nullptr` is returned if the value and the requested pointer type does not match.,get__PointerType} @sa @ref get_ptr() for explicit pointer-member access @since version 1.0.0 */ template::value , int>::type = 0> PointerType get() noexcept { // delegate the call to get_ptr return get_ptr(); } /*! @brief get a pointer value (explicit) @copydoc get() */ template::value , int>::type = 0> const PointerType get() const noexcept { // delegate the call to get_ptr return get_ptr(); } /*! @brief get a pointer value (implicit) Implict pointer access to the internally stored JSON value. No copies are made. @warning Writing data to the pointee of the result yields an undefined state. @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or @ref number_float_t. @return pointer to the internally stored JSON value if the requested pointer type @a PointerType fits to the JSON value; `nullptr` otherwise @complexity Constant. @liveexample{The example below shows how pointers to internal values of a JSON value can be requested. Note that no type conversions are made and a `nullptr` is returned if the value and the requested pointer type does not match.,get_ptr} @since version 1.0.0 */ template::value , int>::type = 0> PointerType get_ptr() noexcept { // delegate the call to get_impl_ptr<>() return get_impl_ptr(static_cast(nullptr)); } /*! @brief get a pointer value (implicit) @copydoc get_ptr() */ template::value and std::is_const::type>::value , int>::type = 0> const PointerType get_ptr() const noexcept { // delegate the call to get_impl_ptr<>() const return get_impl_ptr(static_cast(nullptr)); } /*! @brief get a value (implicit) Implict type conversion between the JSON value and a compatible value. The call is realized by calling @ref get() const. @tparam ValueType non-pointer type compatible to the JSON value, for instance `int` for JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for JSON arrays. The character type of @ref string_t as well as an initializer list of this type is excluded to avoid ambiguities as these types implicitly convert to `std::string`. @return copy of the JSON value, converted to type @a ValueType @throw std::domain_error in case passed type @a ValueType is incompatible to JSON, thrown by @ref get() const @complexity Linear in the size of the JSON value. @liveexample{The example below shows serveral conversions from JSON values to other types. There a few things to note: (1) Floating-point numbers can be converted to integers\, (2) A JSON array can be converted to a standard `std::vector`\, (3) A JSON object can be converted to C++ assiciative containers such as `std::unordered_map`.,operator__ValueType} @since version 1.0.0 */ template::value and not std::is_same::value and not std::is_same>::value , int>::type = 0> operator ValueType() const { // delegate the call to get<>() const return get(); } /// @} //////////////////// // element access // //////////////////// /// @name element access /// @{ /*! @brief access specified array element with bounds checking Returns a reference to the element at specified location @a idx, with bounds checking. @param[in] idx index of the element to access @return reference to the element at index @a idx @throw std::domain_error if the JSON value is not an array; example: `"cannot use at() with string"` @throw std::out_of_range if the index @a idx is out of range of the array; that is, `idx >= size()`; example: `"array index 7 is out of range"` @complexity Constant. @liveexample{The example below shows how array elements can be read and written using at.,at__size_type} @since version 1.0.0 */ reference at(size_type idx) { // at only works for arrays if (is_array()) { try { return m_value.array->at(idx); } catch (std::out_of_range& e) { // create better exception explanation throw std::out_of_range("array index " + std::to_string(idx) + " is out of range"); } } else { throw std::domain_error("cannot use at() with " + type_name()); } } /*! @brief access specified array element with bounds checking Returns a const reference to the element at specified location @a idx, with bounds checking. @param[in] idx index of the element to access @return const reference to the element at index @a idx @throw std::domain_error if the JSON value is not an array; example: `"cannot use at() with string"` @throw std::out_of_range if the index @a idx is out of range of the array; that is, `idx >= size()`; example: `"array index 7 is out of range"` @complexity Constant. @liveexample{The example below shows how array elements can be read using at.,at__size_type_const} @since version 1.0.0 */ const_reference at(size_type idx) const { // at only works for arrays if (is_array()) { try { return m_value.array->at(idx); } catch (std::out_of_range& e) { // create better exception explanation throw std::out_of_range("array index " + std::to_string(idx) + " is out of range"); } } else { throw std::domain_error("cannot use at() with " + type_name()); } } /*! @brief access specified object element with bounds checking Returns a reference to the element at with specified key @a key, with bounds checking. @param[in] key key of the element to access @return reference to the element at key @a key @throw std::domain_error if the JSON value is not an object; example: `"cannot use at() with boolean"` @throw std::out_of_range if the key @a key is is not stored in the object; that is, `find(key) == end()`; example: `"key "the fast" not found"` @complexity Logarithmic in the size of the container. @liveexample{The example below shows how object elements can be read and written using at.,at__object_t_key_type} @sa @ref operator[](const typename object_t::key_type&) for unchecked access by reference @sa @ref value() for access by value with a default value @since version 1.0.0 */ reference at(const typename object_t::key_type& key) { // at only works for objects if (is_object()) { try { return m_value.object->at(key); } catch (std::out_of_range& e) { // create better exception explanation throw std::out_of_range("key '" + key + "' not found"); } } else { throw std::domain_error("cannot use at() with " + type_name()); } } /*! @brief access specified object element with bounds checking Returns a const reference to the element at with specified key @a key, with bounds checking. @param[in] key key of the element to access @return const reference to the element at key @a key @throw std::domain_error if the JSON value is not an object; example: `"cannot use at() with boolean"` @throw std::out_of_range if the key @a key is is not stored in the object; that is, `find(key) == end()`; example: `"key "the fast" not found"` @complexity Logarithmic in the size of the container. @liveexample{The example below shows how object elements can be read using at.,at__object_t_key_type_const} @sa @ref operator[](const typename object_t::key_type&) for unchecked access by reference @sa @ref value() for access by value with a default value @since version 1.0.0 */ const_reference at(const typename object_t::key_type& key) const { // at only works for objects if (is_object()) { try { return m_value.object->at(key); } catch (std::out_of_range& e) { // create better exception explanation throw std::out_of_range("key '" + key + "' not found"); } } else { throw std::domain_error("cannot use at() with " + type_name()); } } /*! @brief access specified array element Returns a reference to the element at specified location @a idx. @note If @a idx is beyond the range of the array (i.e., `idx >= size()`), then the array is silently filled up with `null` values to make `idx` a valid reference to the last stored element. @param[in] idx index of the element to access @return reference to the element at index @a idx @throw std::domain_error if JSON is not an array or null; example: `"cannot use operator[] with null"` @complexity Constant if @a idx is in the range of the array. Otherwise linear in `idx - size()`. @liveexample{The example below shows how array elements can be read and written using [] operator. Note the addition of `null` values.,operatorarray__size_type} @since version 1.0.0 */ reference operator[](size_type idx) { // implicitly convert null to object if (is_null()) { m_type = value_t::array; m_value.array = create(); } // [] only works for arrays if (is_array()) { for (size_t i = m_value.array->size(); i <= idx; ++i) { m_value.array->push_back(basic_json()); } return m_value.array->operator[](idx); } else { throw std::domain_error("cannot use operator[] with " + type_name()); } } /*! @brief access specified array element Returns a const reference to the element at specified location @a idx. @param[in] idx index of the element to access @return const reference to the element at index @a idx @throw std::domain_error if JSON is not an array; example: `"cannot use operator[] with null"` @complexity Constant. @liveexample{The example below shows how array elements can be read using the [] operator.,operatorarray__size_type_const} @since version 1.0.0 */ const_reference operator[](size_type idx) const { // at only works for arrays if (is_array()) { return m_value.array->operator[](idx); } else { throw std::domain_error("cannot use operator[] with " + type_name()); } } /*! @brief access specified object element Returns a reference to the element at with specified key @a key. @note If @a key is not found in the object, then it is silently added to the object and filled with a `null` value to make `key` a valid reference. In case the value was `null` before, it is converted to an object. @param[in] key key of the element to access @return reference to the element at key @a key @throw std::domain_error if JSON is not an object or null; example: `"cannot use operator[] with null"` @complexity Logarithmic in the size of the container. @liveexample{The example below shows how object elements can be read and written using the [] operator.,operatorarray__key_type} @sa @ref at(const typename object_t::key_type&) for access by reference with range checking @sa @ref value() for access by value with a default value @since version 1.0.0 */ reference operator[](const typename object_t::key_type& key) { // implicitly convert null to object if (is_null()) { m_type = value_t::object; m_value.object = create(); } // [] only works for objects if (is_object()) { return m_value.object->operator[](key); } else { throw std::domain_error("cannot use operator[] with " + type_name()); } } /*! @brief read-only access specified object element Returns a const reference to the element at with specified key @a key. No bounds checking is performed. @warning If the element with key @a key does not exist, the behavior is undefined. @param[in] key key of the element to access @return const reference to the element at key @a key @throw std::domain_error if JSON is not an object; example: `"cannot use operator[] with null"` @complexity Logarithmic in the size of the container. @liveexample{The example below shows how object elements can be read using the [] operator.,operatorarray__key_type_const} @sa @ref at(const typename object_t::key_type&) for access by reference with range checking @sa @ref value() for access by value with a default value @since version 1.0.0 */ const_reference operator[](const typename object_t::key_type& key) const { // [] only works for objects if (is_object()) { return m_value.object->find(key)->second; } else { throw std::domain_error("cannot use operator[] with " + type_name()); } } /*! @brief access specified object element Returns a reference to the element at with specified key @a key. @note If @a key is not found in the object, then it is silently added to the object and filled with a `null` value to make `key` a valid reference. In case the value was `null` before, it is converted to an object. @note This function is required for compatibility reasons with Clang. @param[in] key key of the element to access @return reference to the element at key @a key @throw std::domain_error if JSON is not an object or null; example: `"cannot use operator[] with null"` @complexity Logarithmic in the size of the container. @liveexample{The example below shows how object elements can be read and written using the [] operator.,operatorarray__key_type} @sa @ref at(const typename object_t::key_type&) for access by reference with range checking @sa @ref value() for access by value with a default value @since version 1.0.0 */ template reference operator[](const T (&key)[n]) { // implicitly convert null to object if (is_null()) { m_type = value_t::object; m_value = value_t::object; } // at only works for objects if (is_object()) { return m_value.object->operator[](key); } else { throw std::domain_error("cannot use operator[] with " + type_name()); } } /*! @brief read-only access specified object element Returns a const reference to the element at with specified key @a key. No bounds checking is performed. @warning If the element with key @a key does not exist, the behavior is undefined. @note This function is required for compatibility reasons with Clang. @param[in] key key of the element to access @return const reference to the element at key @a key @throw std::domain_error if JSON is not an object; example: `"cannot use operator[] with null"` @complexity Logarithmic in the size of the container. @liveexample{The example below shows how object elements can be read using the [] operator.,operatorarray__key_type_const} @sa @ref at(const typename object_t::key_type&) for access by reference with range checking @sa @ref value() for access by value with a default value @since version 1.0.0 */ template const_reference operator[](const T (&key)[n]) const { // at only works for objects if (is_object()) { return m_value.object->find(key)->second; } else { throw std::domain_error("cannot use operator[] with " + type_name()); } } /*! @brief access specified object element with default value Returns either a copy of an object's element at the specified key @a key or a given default value if no element with key @a key exists. The function is basically equivalent to executing @code {.cpp} try { return at(key); } catch(std::out_of_range) { return default_value; } @endcode @note Unlike @ref at(const typename object_t::key_type&), this function does not throw if the given key @a key was not found. @note Unlike @ref operator[](const typename object_t::key_type& key), this function does not implicitly add an element to the position defined by @a key. This function is furthermore also applicable to const objects. @param[in] key key of the element to access @param[in] default_value the value to return if @a key is not found @tparam ValueType type compatible to JSON values, for instance `int` for JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for JSON arrays. Note the type of the expected value at @a key and the default value @a default_value must be compatible. @return copy of the element at key @a key or @a default_value if @a key is not found @throw std::domain_error if JSON is not an object; example: `"cannot use value() with null"` @complexity Logarithmic in the size of the container. @liveexample{The example below shows how object elements can be queried with a default value.,basic_json__value} @sa @ref at(const typename object_t::key_type&) for access by reference with range checking @sa @ref operator[](const typename object_t::key_type&) for unchecked access by reference @since version 1.0.0 */ template ::value , int>::type = 0> ValueType value(const typename object_t::key_type& key, ValueType default_value) const { // at only works for objects if (is_object()) { // if key is found, return value and given default value otherwise const auto it = find(key); if (it != end()) { return *it; } else { return default_value; } } else { throw std::domain_error("cannot use value() with " + type_name()); } } /*! @brief overload for a default value of type const char* @copydoc basic_json::value() */ string_t value(const typename object_t::key_type& key, const char* default_value) const { return value(key, string_t(default_value)); } /*! @brief access the first element Returns a reference to the first element in the container. For a JSON container `c`, the expression `c.front()` is equivalent to `*c.begin()`. @return In case of a structured type (array or object), a reference to the first element is returned. In cast of number, string, or boolean values, a reference to the value is returned. @complexity Constant. @note Calling `front` on an empty container is undefined. @throw std::out_of_range when called on null value @liveexample{The following code shows an example for @ref front.,front} @since version 1.0.0 */ reference front() { return *begin(); } /*! @copydoc basic_json::front() */ const_reference front() const { return *cbegin(); } /*! @brief access the last element Returns a reference to the last element in the container. For a JSON container `c`, the expression `c.back()` is equivalent to `{ auto tmp = c.end(); --tmp; return *tmp; }`. @return In case of a structured type (array or object), a reference to the last element is returned. In cast of number, string, or boolean values, a reference to the value is returned. @complexity Constant. @note Calling `back` on an empty container is undefined. @throw std::out_of_range when called on null value. @liveexample{The following code shows an example for @ref back.,back} @since version 1.0.0 */ reference back() { auto tmp = end(); --tmp; return *tmp; } /*! @copydoc basic_json::back() */ const_reference back() const { auto tmp = cend(); --tmp; return *tmp; } /*! @brief remove element given an iterator Removes the element specified by iterator @a pos. Invalidates iterators and references at or after the point of the erase, including the end() iterator. The iterator @a pos must be valid and dereferenceable. Thus the end() iterator (which is valid, but is not dereferencable) cannot be used as a value for @a pos. If called on a primitive type other than null, the resulting JSON value will be `null`. @param[in] pos iterator to the element to remove @return Iterator following the last removed element. If the iterator @a pos refers to the last element, the end() iterator is returned. @tparam InteratorType an @ref iterator or @ref const_iterator @throw std::domain_error if called on a `null` value; example: `"cannot use erase() with null"` @throw std::domain_error if called on an iterator which does not belong to the current JSON value; example: `"iterator does not fit current value"` @throw std::out_of_range if called on a primitive type with invalid iterator (i.e., any iterator which is not end()); example: `"iterator out of range"` @complexity The complexity depends on the type: - objects: amortized constant - arrays: linear in distance between pos and the end of the container - strings: linear in the length of the string - other types: constant @liveexample{The example shows the result of erase for different JSON types.,erase__IteratorType} @sa @ref erase(InteratorType, InteratorType) -- removes the elements in the given range @sa @ref erase(const typename object_t::key_type&) -- remvoes the element from an object at the given key @sa @ref erase(const size_type) -- removes the element from an array at the given index @since version 1.0.0 */ template ::value or std::is_same::value , int>::type = 0> InteratorType erase(InteratorType pos) { // make sure iterator fits the current value if (this != pos.m_object) { throw std::domain_error("iterator does not fit current value"); } InteratorType result = end(); switch (m_type) { case value_t::boolean: case value_t::number_float: case value_t::number_integer: case value_t::string: { if (not pos.m_it.primitive_iterator.is_begin()) { throw std::out_of_range("iterator out of range"); } if (is_string()) { delete m_value.string; m_value.string = nullptr; } m_type = value_t::null; break; } case value_t::object: { result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator); break; } case value_t::array: { result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator); break; } default: { throw std::domain_error("cannot use erase() with " + type_name()); } } return result; } /*! @brief remove elements given an iterator range Removes the element specified by the range `[first; last)`. Invalidates iterators and references at or after the point of the erase, including the end() iterator. The iterator @a first does not need to be dereferenceable if `first == last`: erasing an empty range is a no-op. If called on a primitive type other than null, the resulting JSON value will be `null`. @param[in] first iterator to the beginning of the range to remove @param[in] last iterator past the end of the range to remove @return Iterator following the last removed element. If the iterator @a second refers to the last element, the end() iterator is returned. @tparam InteratorType an @ref iterator or @ref const_iterator @throw std::domain_error if called on a `null` value; example: `"cannot use erase() with null"` @throw std::domain_error if called on iterators which does not belong to the current JSON value; example: `"iterators do not fit current value"` @throw std::out_of_range if called on a primitive type with invalid iterators (i.e., if `first != begin()` and `last != end()`); example: `"iterators out of range"` @complexity The complexity depends on the type: - objects: `log(size()) + std::distance(first, last)` - arrays: linear in the distance between @a first and @a last, plus linear in the distance between @a last and end of the container - strings: linear in the length of the string - other types: constant @liveexample{The example shows the result of erase for different JSON types.,erase__IteratorType_IteratorType} @sa @ref erase(InteratorType) -- removes the element at a given position @sa @ref erase(const typename object_t::key_type&) -- remvoes the element from an object at the given key @sa @ref erase(const size_type) -- removes the element from an array at the given index @since version 1.0.0 */ template ::value or std::is_same::value , int>::type = 0> InteratorType erase(InteratorType first, InteratorType last) { // make sure iterator fits the current value if (this != first.m_object or this != last.m_object) { throw std::domain_error("iterators do not fit current value"); } InteratorType result = end(); switch (m_type) { case value_t::boolean: case value_t::number_float: case value_t::number_integer: case value_t::string: { if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) { throw std::out_of_range("iterators out of range"); } if (is_string()) { delete m_value.string; m_value.string = nullptr; } m_type = value_t::null; break; } case value_t::object: { result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator, last.m_it.object_iterator); break; } case value_t::array: { result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator, last.m_it.array_iterator); break; } default: { throw std::domain_error("cannot use erase() with " + type_name()); } } return result; } /*! @brief remove element from a JSON object given a key Removes elements from a JSON object with the key value @a key. @param[in] key value of the elements to remove @return Number of elements removed. If ObjectType is the default `std::map` type, the return value will always be `0` (@a key was not found) or `1` (@a key was found). @throw std::domain_error when called on a type other than JSON object; example: `"cannot use erase() with null"` @complexity `log(size()) + count(key)` @liveexample{The example shows the effect of erase.,erase__key_type} @sa @ref erase(InteratorType) -- removes the element at a given position @sa @ref erase(InteratorType, InteratorType) -- removes the elements in the given range @sa @ref erase(const size_type) -- removes the element from an array at the given index @since version 1.0.0 */ size_type erase(const typename object_t::key_type& key) { // this erase only works for objects if (is_object()) { return m_value.object->erase(key); } else { throw std::domain_error("cannot use erase() with " + type_name()); } } /*! @brief remove element from a JSON array given an index Removes element from a JSON array at the index @a idx. @param[in] idx index of the element to remove @throw std::domain_error when called on a type other than JSON array; example: `"cannot use erase() with null"` @throw std::out_of_range when `idx >= size()`; example: `"index out of range"` @complexity Linear in distance between @a idx and the end of the container. @liveexample{The example shows the effect of erase.,erase__size_type} @sa @ref erase(InteratorType) -- removes the element at a given position @sa @ref erase(InteratorType, InteratorType) -- removes the elements in the given range @sa @ref erase(const typename object_t::key_type&) -- remvoes the element from an object at the given key @since version 1.0.0 */ void erase(const size_type idx) { // this erase only works for arrays if (is_array()) { if (idx >= size()) { throw std::out_of_range("index out of range"); } m_value.array->erase(m_value.array->begin() + static_cast(idx)); } else { throw std::domain_error("cannot use erase() with " + type_name()); } } /*! @brief find an element in a JSON object Finds an element in a JSON object with key equivalent to @a key. If the element is not found or the JSON value is not an object, end() is returned. @param[in] key key value of the element to search for @return Iterator to an element with key equivalent to @a key. If no such element is found, past-the-end (see end()) iterator is returned. @complexity Logarithmic in the size of the JSON object. @liveexample{The example shows how find is used.,find__key_type} @since version 1.0.0 */ iterator find(typename object_t::key_type key) { auto result = end(); if (is_object()) { result.m_it.object_iterator = m_value.object->find(key); } return result; } /*! @brief find an element in a JSON object @copydoc find(typename object_t::key_type) */ const_iterator find(typename object_t::key_type key) const { auto result = cend(); if (is_object()) { result.m_it.object_iterator = m_value.object->find(key); } return result; } /*! @brief returns the number of occurrences of a key in a JSON object Returns the number of elements with key @a key. If ObjectType is the default `std::map` type, the return value will always be `0` (@a key was not found) or `1` (@a key was found). @param[in] key key value of the element to count @return Number of elements with key @a key. If the JSON value is not an object, the return value will be `0`. @complexity Logarithmic in the size of the JSON object. @liveexample{The example shows how count is used.,count} @since version 1.0.0 */ size_type count(typename object_t::key_type key) const { // return 0 for all nonobject types return is_object() ? m_value.object->count(key) : 0; } /// @} /////////////// // iterators // /////////////// /// @name iterators /// @{ /*! @brief returns an iterator to the first element Returns an iterator to the first element. @image html range-begin-end.svg "Illustration from cppreference.com" @return iterator to the first element @complexity Constant. @requirement This function satisfies the Container requirements: - The complexity is constant. @liveexample{The following code shows an example for @ref begin.,begin} @since version 1.0.0 */ iterator begin() { iterator result(this); result.set_begin(); return result; } /*! @copydoc basic_json::cbegin() */ const_iterator begin() const { return cbegin(); } /*! @brief returns a const iterator to the first element Returns a const iterator to the first element. @image html range-begin-end.svg "Illustration from cppreference.com" @return const iterator to the first element @complexity Constant. @requirement This function satisfies the Container requirements: - The complexity is constant. - Has the semantics of `const_cast(*this).begin()`. @liveexample{The following code shows an example for @ref cbegin.,cbegin} @since version 1.0.0 */ const_iterator cbegin() const { const_iterator result(this); result.set_begin(); return result; } /*! @brief returns an iterator to one past the last element Returns an iterator to one past the last element. @image html range-begin-end.svg "Illustration from cppreference.com" @return iterator one past the last element @complexity Constant. @requirement This function satisfies the Container requirements: - The complexity is constant. @liveexample{The following code shows an example for @ref end.,end} @since version 1.0.0 */ iterator end() { iterator result(this); result.set_end(); return result; } /*! @copydoc basic_json::cend() */ const_iterator end() const { return cend(); } /*! @brief returns a const iterator to one past the last element Returns a const iterator to one past the last element. @image html range-begin-end.svg "Illustration from cppreference.com" @return const iterator one past the last element @complexity Constant. @requirement This function satisfies the Container requirements: - The complexity is constant. - Has the semantics of `const_cast(*this).end()`. @liveexample{The following code shows an example for @ref cend.,cend} @since version 1.0.0 */ const_iterator cend() const { const_iterator result(this); result.set_end(); return result; } /*! @brief returns an iterator to the reverse-beginning Returns an iterator to the reverse-beginning; that is, the last element. @image html range-rbegin-rend.svg "Illustration from cppreference.com" @complexity Constant. @requirement This function satisfies the ReversibleContainer requirements: - The complexity is constant. - Has the semantics of `reverse_iterator(end())`. @liveexample{The following code shows an example for @ref rbegin.,rbegin} @since version 1.0.0 */ reverse_iterator rbegin() { return reverse_iterator(end()); } /*! @copydoc basic_json::crbegin() */ const_reverse_iterator rbegin() const { return crbegin(); } /*! @brief returns an iterator to the reverse-end Returns an iterator to the reverse-end; that is, one before the first element. @image html range-rbegin-rend.svg "Illustration from cppreference.com" @complexity Constant. @requirement This function satisfies the ReversibleContainer requirements: - The complexity is constant. - Has the semantics of `reverse_iterator(begin())`. @liveexample{The following code shows an example for @ref rend.,rend} @since version 1.0.0 */ reverse_iterator rend() { return reverse_iterator(begin()); } /*! @copydoc basic_json::crend() */ const_reverse_iterator rend() const { return crend(); } /*! @brief returns a const reverse iterator to the last element Returns a const iterator to the reverse-beginning; that is, the last element. @image html range-rbegin-rend.svg "Illustration from cppreference.com" @complexity Constant. @requirement This function satisfies the ReversibleContainer requirements: - The complexity is constant. - Has the semantics of `const_cast(*this).rbegin()`. @liveexample{The following code shows an example for @ref crbegin.,crbegin} @since version 1.0.0 */ const_reverse_iterator crbegin() const { return const_reverse_iterator(cend()); } /*! @brief returns a const reverse iterator to one before the first Returns a const reverse iterator to the reverse-end; that is, one before the first element. @image html range-rbegin-rend.svg "Illustration from cppreference.com" @complexity Constant. @requirement This function satisfies the ReversibleContainer requirements: - The complexity is constant. - Has the semantics of `const_cast(*this).rend()`. @liveexample{The following code shows an example for @ref crend.,crend} @since version 1.0.0 */ const_reverse_iterator crend() const { return const_reverse_iterator(cbegin()); } private: // forward declaration template class iteration_proxy; public: /*! @brief wrapper to access iterator member functions in range-based for This functuion allows to access @ref iterator::key() and @ref iterator::value() during range-based for loops. In these loops, a reference to the JSON values is returned, so there is no access to the underlying iterator. @note The name of this function is not yet final and may change in the future. */ static iteration_proxy iterator_wrapper(reference cont) { return iteration_proxy(cont); } /*! @copydoc iterator_wrapper(reference) */ static iteration_proxy iterator_wrapper(const_reference cont) { return iteration_proxy(cont); } /// @} ////////////// // capacity // ////////////// /// @name capacity /// @{ /*! @brief checks whether the container is empty Checks if a JSON value has no elements. @return The return value depends on the different types and is defined as follows: Value type | return value ----------- | ------------- null | @c true boolean | @c false string | @c false number | @c false object | result of function object_t::empty() array | result of function array_t::empty() @complexity Constant, as long as @ref array_t and @ref object_t satisfy the Container concept; that is, their empty() functions have constant complexity. @requirement This function satisfies the Container requirements: - The complexity is constant. - Has the semantics of `begin() == end()`. @liveexample{The following code uses @ref empty to check if a @ref json object contains any elements.,empty} @since version 1.0.0 */ bool empty() const noexcept { switch (m_type) { case value_t::null: { // null values are empty return true; } case value_t::array: { return m_value.array->empty(); } case value_t::object: { return m_value.object->empty(); } default: { // all other types are nonempty return false; } } } /*! @brief returns the number of elements Returns the number of elements in a JSON value. @return The return value depends on the different types and is defined as follows: Value type | return value ----------- | ------------- null | @c 0 boolean | @c 1 string | @c 1 number | @c 1 object | result of function object_t::size() array | result of function array_t::size() @complexity Constant, as long as @ref array_t and @ref object_t satisfy the Container concept; that is, their size() functions have constant complexity. @requirement This function satisfies the Container requirements: - The complexity is constant. - Has the semantics of `std::distance(begin(), end())`. @liveexample{The following code calls @ref size on the different value types.,size} @since version 1.0.0 */ size_type size() const noexcept { switch (m_type) { case value_t::null: { // null values are empty return 0; } case value_t::array: { return m_value.array->size(); } case value_t::object: { return m_value.object->size(); } default: { // all other types have size 1 return 1; } } } /*! @brief returns the maximum possible number of elements Returns the maximum number of elements a JSON value is able to hold due to system or library implementation limitations, i.e. `std::distance(begin(), end())` for the JSON value. @return The return value depends on the different types and is defined as follows: Value type | return value ----------- | ------------- null | @c 0 (same as size()) boolean | @c 1 (same as size()) string | @c 1 (same as size()) number | @c 1 (same as size()) object | result of function object_t::max_size() array | result of function array_t::max_size() @complexity Constant, as long as @ref array_t and @ref object_t satisfy the Container concept; that is, their max_size() functions have constant complexity. @requirement This function satisfies the Container requirements: - The complexity is constant. - Has the semantics of returning `b.size()` where `b` is the largest possible JSON value. @liveexample{The following code calls @ref max_size on the different value types. Note the output is implementation specific.,max_size} @since version 1.0.0 */ size_type max_size() const noexcept { switch (m_type) { case value_t::array: { return m_value.array->max_size(); } case value_t::object: { return m_value.object->max_size(); } default: { // all other types have max_size() == size() return size(); } } } /// @} /////////////// // modifiers // /////////////// /// @name modifiers /// @{ /*! @brief clears the contents Clears the content of a JSON value and resets it to the default value as if @ref basic_json(value_t) would have been called: Value type | initial value ----------- | ------------- null | `null` boolean | `false` string | `""` number | `0` object | `{}` array | `[]` @note Floating-point numbers are set to `0.0` which will be serialized to `0`. The vale type remains @ref number_float_t. @complexity Linear in the size of the JSON value. @liveexample{The example below shows the effect of @ref clear to different JSON types.,clear} @since version 1.0.0 */ void clear() noexcept { switch (m_type) { case value_t::number_integer: { m_value.number_integer = 0; break; } case value_t::number_float: { m_value.number_float = 0.0; break; } case value_t::boolean: { m_value.boolean = false; break; } case value_t::string: { m_value.string->clear(); break; } case value_t::array: { m_value.array->clear(); break; } case value_t::object: { m_value.object->clear(); break; } default: { break; } } } /*! @brief add an object to an array Appends the given element @a val to the end of the JSON value. If the function is called on a JSON null value, an empty array is created before appending @a val. @param val the value to add to the JSON array @throw std::domain_error when called on a type other than JSON array or null; example: `"cannot use push_back() with number"` @complexity Amortized constant. @liveexample{The example shows how `push_back` and `+=` can be used to add elements to a JSON array. Note how the `null` value was silently converted to a JSON array.,push_back} @since version 1.0.0 */ void push_back(basic_json&& val) { // push_back only works for null objects or arrays if (not(is_null() or is_array())) { throw std::domain_error("cannot use push_back() with " + type_name()); } // transform null object into an array if (is_null()) { m_type = value_t::array; m_value = value_t::array; } // add element to array (move semantics) m_value.array->push_back(std::move(val)); // invalidate object val.m_type = value_t::null; } /*! @brief add an object to an array @copydoc push_back(basic_json&&) */ reference operator+=(basic_json&& val) { push_back(std::move(val)); return *this; } /*! @brief add an object to an array @copydoc push_back(basic_json&&) */ void push_back(const basic_json& val) { // push_back only works for null objects or arrays if (not(is_null() or is_array())) { throw std::domain_error("cannot use push_back() with " + type_name()); } // transform null object into an array if (is_null()) { m_type = value_t::array; m_value = value_t::array; } // add element to array m_value.array->push_back(val); } /*! @brief add an object to an array @copydoc push_back(basic_json&&) */ reference operator+=(const basic_json& val) { push_back(val); return *this; } /*! @brief add an object to an object Inserts the given element @a val to the JSON object. If the function is called on a JSON null value, an empty object is created before inserting @a val. @param[in] val the value to add to the JSON object @throw std::domain_error when called on a type other than JSON object or null; example: `"cannot use push_back() with number"` @complexity Logarithmic in the size of the container, O(log(`size()`)). @liveexample{The example shows how `push_back` and `+=` can be used to add elements to a JSON object. Note how the `null` value was silently converted to a JSON object.,push_back__object_t__value} @since version 1.0.0 */ void push_back(const typename object_t::value_type& val) { // push_back only works for null objects or objects if (not(is_null() or is_object())) { throw std::domain_error("cannot use push_back() with " + type_name()); } // transform null object into an object if (is_null()) { m_type = value_t::object; m_value = value_t::object; } // add element to array m_value.object->insert(val); } /*! @brief add an object to an object @copydoc push_back(const typename object_t::value_type&) */ reference operator+=(const typename object_t::value_type& val) { push_back(val); return operator[](val.first); } /*! @brief inserts element Inserts element @a val before iterator @a pos. @param[in] pos iterator before which the content will be inserted; may be the end() iterator @param[in] val element to insert @return iterator pointing to the inserted @a val. @throw std::domain_error if called on JSON values other than arrays; example: `"cannot use insert() with string"` @throw std::domain_error if @a pos is not an iterator of *this; example: `"iterator does not fit current value"` @complexity Constant plus linear in the distance between pos and end of the container. @liveexample{The example shows how insert is used.,insert} @since version 1.0.0 */ iterator insert(const_iterator pos, const basic_json& val) { // insert only works for arrays if (is_array()) { // check if iterator pos fits to this JSON value if (pos.m_object != this) { throw std::domain_error("iterator does not fit current value"); } // insert to array and return iterator iterator result(this); result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, val); return result; } else { throw std::domain_error("cannot use insert() with " + type_name()); } } /*! @brief inserts element @copydoc insert(const_iterator, const basic_json&) */ iterator insert(const_iterator pos, basic_json&& val) { return insert(pos, val); } /*! @brief inserts elements Inserts @a cnt copies of @a val before iterator @a pos. @param[in] pos iterator before which the content will be inserted; may be the end() iterator @param[in] cnt number of copies of @a val to insert @param[in] val element to insert @return iterator pointing to the first element inserted, or @a pos if `cnt==0` @throw std::domain_error if called on JSON values other than arrays; example: `"cannot use insert() with string"` @throw std::domain_error if @a pos is not an iterator of *this; example: `"iterator does not fit current value"` @complexity Linear in @a cnt plus linear in the distance between @a pos and end of the container. @liveexample{The example shows how insert is used.,insert__count} @since version 1.0.0 */ iterator insert(const_iterator pos, size_type cnt, const basic_json& val) { // insert only works for arrays if (is_array()) { // check if iterator pos fits to this JSON value if (pos.m_object != this) { throw std::domain_error("iterator does not fit current value"); } // insert to array and return iterator iterator result(this); result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); return result; } else { throw std::domain_error("cannot use insert() with " + type_name()); } } /*! @brief inserts elements Inserts elements from range `[first, last)` before iterator @a pos. @param[in] pos iterator before which the content will be inserted; may be the end() iterator @param[in] first begin of the range of elements to insert @param[in] last end of the range of elements to insert @throw std::domain_error if called on JSON values other than arrays; example: `"cannot use insert() with string"` @throw std::domain_error if @a pos is not an iterator of *this; example: `"iterator does not fit current value"` @throw std::domain_error if @a first and @a last do not belong to the same JSON value; example: `"iterators do not fit"` @throw std::domain_error if @a first or @a last are iterators into container for which insert is called; example: `"passed iterators may not belong to container"` @return iterator pointing to the first element inserted, or @a pos if `first==last` @complexity Linear in `std::distance(first, last)` plus linear in the distance between @a pos and end of the container. @liveexample{The example shows how insert is used.,insert__range} @since version 1.0.0 */ iterator insert(const_iterator pos, const_iterator first, const_iterator last) { // insert only works for arrays if (not is_array()) { throw std::domain_error("cannot use insert() with " + type_name()); } // check if iterator pos fits to this JSON value if (pos.m_object != this) { throw std::domain_error("iterator does not fit current value"); } if (first.m_object != last.m_object) { throw std::domain_error("iterators do not fit"); } if (first.m_object == this or last.m_object == this) { throw std::domain_error("passed iterators may not belong to container"); } // insert to array and return iterator iterator result(this); result.m_it.array_iterator = m_value.array->insert( pos.m_it.array_iterator, first.m_it.array_iterator, last.m_it.array_iterator); return result; } /*! @brief inserts elements Inserts elements from initializer list @a ilist before iterator @a pos. @param[in] pos iterator before which the content will be inserted; may be the end() iterator @param[in] ilist initializer list to insert the values from @throw std::domain_error if called on JSON values other than arrays; example: `"cannot use insert() with string"` @throw std::domain_error if @a pos is not an iterator of *this; example: `"iterator does not fit current value"` @return iterator pointing to the first element inserted, or @a pos if `ilist` is empty @complexity Linear in `ilist.size()` plus linear in the distance between @a pos and end of the container. @liveexample{The example shows how insert is used.,insert__ilist} @since version 1.0.0 */ iterator insert(const_iterator pos, std::initializer_list ilist) { // insert only works for arrays if (not is_array()) { throw std::domain_error("cannot use insert() with " + type_name()); } // check if iterator pos fits to this JSON value if (pos.m_object != this) { throw std::domain_error("iterator does not fit current value"); } // insert to array and return iterator iterator result(this); result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, ilist); return result; } /*! @brief exchanges the values Exchanges the contents of the JSON value with those of @a other. Does not invoke any move, copy, or swap operations on individual elements. All iterators and references remain valid. The past-the-end iterator is invalidated. @param[in,out] other JSON value to exchange the contents with @complexity Constant. @liveexample{The example below shows how JSON arrays can be swapped.,swap__reference} @since version 1.0.0 */ void swap(reference other) noexcept ( std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable::value and std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable::value ) { std::swap(m_type, other.m_type); std::swap(m_value, other.m_value); } /*! @brief exchanges the values Exchanges the contents of a JSON array with those of @a other. Does not invoke any move, copy, or swap operations on individual elements. All iterators and references remain valid. The past-the-end iterator is invalidated. @param[in,out] other array to exchange the contents with @throw std::domain_error when JSON value is not an array; example: `"cannot use swap() with string"` @complexity Constant. @liveexample{The example below shows how JSON values can be swapped.,swap__array_t} @since version 1.0.0 */ void swap(array_t& other) { // swap only works for arrays if (is_array()) { std::swap(*(m_value.array), other); } else { throw std::domain_error("cannot use swap() with " + type_name()); } } /*! @brief exchanges the values Exchanges the contents of a JSON object with those of @a other. Does not invoke any move, copy, or swap operations on individual elements. All iterators and references remain valid. The past-the-end iterator is invalidated. @param[in,out] other object to exchange the contents with @throw std::domain_error when JSON value is not an object; example: `"cannot use swap() with string"` @complexity Constant. @liveexample{The example below shows how JSON values can be swapped.,swap__object_t} @since version 1.0.0 */ void swap(object_t& other) { // swap only works for objects if (is_object()) { std::swap(*(m_value.object), other); } else { throw std::domain_error("cannot use swap() with " + type_name()); } } /*! @brief exchanges the values Exchanges the contents of a JSON string with those of @a other. Does not invoke any move, copy, or swap operations on individual elements. All iterators and references remain valid. The past-the-end iterator is invalidated. @param[in,out] other string to exchange the contents with @throw std::domain_error when JSON value is not a string; example: `"cannot use swap() with boolean"` @complexity Constant. @liveexample{The example below shows how JSON values can be swapped.,swap__string_t} @since version 1.0.0 */ void swap(string_t& other) { // swap only works for strings if (is_string()) { std::swap(*(m_value.string), other); } else { throw std::domain_error("cannot use swap() with " + type_name()); } } /// @} ////////////////////////////////////////// // lexicographical comparison operators // ////////////////////////////////////////// /// @name lexicographical comparison operators /// @{ private: /*! @brief comparison operator for JSON types Returns an ordering that is similar to Python: - order: null < boolean < number < object < array < string - furthermore, each type is not smaller than itself @since version 1.0.0 */ friend bool operator<(const value_t lhs, const value_t rhs) { static constexpr std::array order = {{ 0, // null 3, // object 4, // array 5, // string 1, // boolean 2, // integer 2 // float } }; // discarded values are not comparable if (lhs == value_t::discarded or rhs == value_t::discarded) { return false; } return order[static_cast(lhs)] < order[static_cast(rhs)]; } public: /*! @brief comparison: equal Compares two JSON values for equality according to the following rules: - Two JSON values are equal if (1) they are from the same type and (2) their stored values are the same. - Integer and floating-point numbers are automatically converted before comparison. Floating-point numbers are compared indirectly: two floating-point numbers `f1` and `f2` are considered equal if neither `f1 > f2` nor `f2 > f1` holds. - Two JSON null values are equal. @param[in] lhs first JSON value to consider @param[in] rhs second JSON value to consider @return whether the values @a lhs and @a rhs are equal @complexity Linear. @liveexample{The example demonstrates comparing several JSON types.,operator__equal} @since version 1.0.0 */ friend bool operator==(const_reference lhs, const_reference rhs) noexcept { const auto lhs_type = lhs.type(); const auto rhs_type = rhs.type(); if (lhs_type == rhs_type) { switch (lhs_type) { case value_t::array: return *lhs.m_value.array == *rhs.m_value.array; case value_t::object: return *lhs.m_value.object == *rhs.m_value.object; case value_t::null: return true; case value_t::string: return *lhs.m_value.string == *rhs.m_value.string; case value_t::boolean: return lhs.m_value.boolean == rhs.m_value.boolean; case value_t::number_integer: return lhs.m_value.number_integer == rhs.m_value.number_integer; case value_t::number_float: return approx(lhs.m_value.number_float, rhs.m_value.number_float); default: return false; } } else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) { return approx(static_cast(lhs.m_value.number_integer), rhs.m_value.number_float); } else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) { return approx(lhs.m_value.number_float, static_cast(rhs.m_value.number_integer)); } return false; } /*! @brief comparison: equal The functions compares the given JSON value against a null pointer. As the null pointer can be used to initialize a JSON value to null, a comparison of JSON value @a v with a null pointer should be equivalent to call `v.is_null()`. @param[in] v JSON value to consider @return whether @a v is null @complexity Constant. @liveexample{The example compares several JSON types to the null pointer. ,operator__equal__nullptr_t} @since version 1.0.0 */ friend bool operator==(const_reference v, std::nullptr_t) noexcept { return v.is_null(); } /*! @brief comparison: equal @copydoc operator==(const_reference, std::nullptr_t) */ friend bool operator==(std::nullptr_t, const_reference v) noexcept { return v.is_null(); } /*! @brief comparison: not equal Compares two JSON values for inequality by calculating `not (lhs == rhs)`. @param[in] lhs first JSON value to consider @param[in] rhs second JSON value to consider @return whether the values @a lhs and @a rhs are not equal @complexity Linear. @liveexample{The example demonstrates comparing several JSON types.,operator__notequal} @since version 1.0.0 */ friend bool operator!=(const_reference lhs, const_reference rhs) noexcept { return not (lhs == rhs); } /*! @brief comparison: not equal The functions compares the given JSON value against a null pointer. As the null pointer can be used to initialize a JSON value to null, a comparison of JSON value @a v with a null pointer should be equivalent to call `not v.is_null()`. @param[in] v JSON value to consider @return whether @a v is not null @complexity Constant. @liveexample{The example compares several JSON types to the null pointer. ,operator__notequal__nullptr_t} @since version 1.0.0 */ friend bool operator!=(const_reference v, std::nullptr_t) noexcept { return not v.is_null(); } /*! @brief comparison: not equal @copydoc operator!=(const_reference, std::nullptr_t) */ friend bool operator!=(std::nullptr_t, const_reference v) noexcept { return not v.is_null(); } /*! @brief comparison: less than Compares whether one JSON value @a lhs is less than another JSON value @a rhs according to the following rules: - If @a lhs and @a rhs have the same type, the values are compared using the default `<` operator. - Integer and floating-point numbers are automatically converted before comparison - In case @a lhs and @a rhs have different types, the values are ignored and the order of the types is considered, see @ref operator<(const value_t, const value_t). @param[in] lhs first JSON value to consider @param[in] rhs second JSON value to consider @return whether @a lhs is less than @a rhs @complexity Linear. @liveexample{The example demonstrates comparing several JSON types.,operator__less} @since version 1.0.0 */ friend bool operator<(const_reference lhs, const_reference rhs) noexcept { const auto lhs_type = lhs.type(); const auto rhs_type = rhs.type(); if (lhs_type == rhs_type) { switch (lhs_type) { case value_t::array: return *lhs.m_value.array < *rhs.m_value.array; case value_t::object: return *lhs.m_value.object < *rhs.m_value.object; case value_t::null: return false; case value_t::string: return *lhs.m_value.string < *rhs.m_value.string; case value_t::boolean: return lhs.m_value.boolean < rhs.m_value.boolean; case value_t::number_integer: return lhs.m_value.number_integer < rhs.m_value.number_integer; case value_t::number_float: return lhs.m_value.number_float < rhs.m_value.number_float; default: return false; } } else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) { return static_cast(lhs.m_value.number_integer) < rhs.m_value.number_float; } else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) { return lhs.m_value.number_float < static_cast(rhs.m_value.number_integer); } // We only reach this line if we cannot compare values. In that case, // we compare types. Note we have to call the operator explicitly, // because MSVC has problems otherwise. return operator<(lhs_type, rhs_type); } /*! @brief comparison: less than or equal Compares whether one JSON value @a lhs is less than or equal to another JSON value by calculating `not (rhs < lhs)`. @param[in] lhs first JSON value to consider @param[in] rhs second JSON value to consider @return whether @a lhs is less than or equal to @a rhs @complexity Linear. @liveexample{The example demonstrates comparing several JSON types.,operator__greater} @since version 1.0.0 */ friend bool operator<=(const_reference lhs, const_reference rhs) noexcept { return not (rhs < lhs); } /*! @brief comparison: greater than Compares whether one JSON value @a lhs is greater than another JSON value by calculating `not (lhs <= rhs)`. @param[in] lhs first JSON value to consider @param[in] rhs second JSON value to consider @return whether @a lhs is greater than to @a rhs @complexity Linear. @liveexample{The example demonstrates comparing several JSON types.,operator__lessequal} @since version 1.0.0 */ friend bool operator>(const_reference lhs, const_reference rhs) noexcept { return not (lhs <= rhs); } /*! @brief comparison: greater than or equal Compares whether one JSON value @a lhs is greater than or equal to another JSON value by calculating `not (lhs < rhs)`. @param[in] lhs first JSON value to consider @param[in] rhs second JSON value to consider @return whether @a lhs is greater than or equal to @a rhs @complexity Linear. @liveexample{The example demonstrates comparing several JSON types.,operator__greaterequal} @since version 1.0.0 */ friend bool operator>=(const_reference lhs, const_reference rhs) noexcept { return not (lhs < rhs); } /// @} /////////////////// // serialization // /////////////////// /// @name serialization /// @{ /*! @brief serialize to stream Serialize the given JSON value @a j to the output stream @a o. The JSON value will be serialized using the @ref dump member function. The indentation of the output can be controlled with the member variable `width` of the output stream @a o. For instance, using the manipulator `std::setw(4)` on @a o sets the indentation level to `4` and the serialization result is the same as calling `dump(4)`. @param[in,out] o stream to serialize to @param[in] j JSON value to serialize @return the stream @a o @complexity Linear. @liveexample{The example below shows the serialization with different parameters to `width` to adjust the indentation level.,operator_serialize} @since version 1.0.0 */ friend std::ostream& operator<<(std::ostream& o, const basic_json& j) { // read width member and use it as indentation parameter if nonzero const bool pretty_print = (o.width() > 0); const auto indentation = (pretty_print ? o.width() : 0); // reset width to 0 for subsequent calls to this stream o.width(0); // do the actual serialization j.dump(o, pretty_print, static_cast(indentation)); return o; } /*! @brief serialize to stream @copydoc operator<<(std::ostream&, const basic_json&) */ friend std::ostream& operator>>(const basic_json& j, std::ostream& o) { return o << j; } /// @} ///////////////////// // deserialization // ///////////////////// /// @name deserialization /// @{ /*! @brief deserialize from string @param[in] s string to read a serialized JSON value from @param[in] cb a parser callback function of type @ref parser_callback_t which is used to control the deserialization by filtering unwanted values (optional) @return result of the deserialization @complexity Linear in the length of the input. The parser is a predictive LL(1) parser. The complexity can be higher if the parser callback function @a cb has a super-linear complexity. @note A UTF-8 byte order mark is silently ignored. @liveexample{The example below demonstrates the parse function with and without callback function.,parse__string__parser_callback_t} @sa @ref parse(std::istream&, parser_callback_t) for a version that reads from an input stream @since version 1.0.0 */ static basic_json parse(const string_t& s, parser_callback_t cb = nullptr) { return parser(s, cb).parse(); } /*! @brief deserialize from stream @param[in,out] i stream to read a serialized JSON value from @param[in] cb a parser callback function of type @ref parser_callback_t which is used to control the deserialization by filtering unwanted values (optional) @return result of the deserialization @complexity Linear in the length of the input. The parser is a predictive LL(1) parser. The complexity can be higher if the parser callback function @a cb has a super-linear complexity. @note A UTF-8 byte order mark is silently ignored. @liveexample{The example below demonstrates the parse function with and without callback function.,parse__istream__parser_callback_t} @sa @ref parse(const string_t&, parser_callback_t) for a version that reads from a string @since version 1.0.0 */ static basic_json parse(std::istream& i, parser_callback_t cb = nullptr) { return parser(i, cb).parse(); } /*! @copydoc parse(std::istream&, parser_callback_t) */ static basic_json parse(std::istream&& i, parser_callback_t cb = nullptr) { return parser(i, cb).parse(); } /*! @brief deserialize from stream Deserializes an input stream to a JSON value. @param[in,out] i input stream to read a serialized JSON value from @param[in,out] j JSON value to write the deserialized input to @throw std::invalid_argument in case of parse errors @complexity Linear in the length of the input. The parser is a predictive LL(1) parser. @note A UTF-8 byte order mark is silently ignored. @liveexample{The example below shows how a JSON value is constructed by reading a serialization from a stream.,operator_deserialize} @sa parse(std::istream&, parser_callback_t) for a variant with a parser callback function to filter values while parsing @since version 1.0.0 */ friend std::istream& operator<<(basic_json& j, std::istream& i) { j = parser(i).parse(); return i; } /*! @brief deserialize from stream @copydoc operator<<(basic_json&, std::istream&) */ friend std::istream& operator>>(std::istream& i, basic_json& j) { j = parser(i).parse(); return i; } /// @} private: /////////////////////////// // convenience functions // /////////////////////////// /// return the type as string string_t type_name() const { switch (m_type) { case value_t::null: return "null"; case value_t::object: return "object"; case value_t::array: return "array"; case value_t::string: return "string"; case value_t::boolean: return "boolean"; case value_t::discarded: return "discarded"; default: return "number"; } } /*! @brief calculates the extra space to escape a JSON string @param[in] s the string to escape @return the number of characters required to escape string @a s @complexity Linear in the length of string @a s. */ static std::size_t extra_space(const string_t& s) noexcept { std::size_t result = 0; for (const auto& c : s) { switch (c) { case '"': case '\\': case '\b': case '\f': case '\n': case '\r': case '\t': { // from c (1 byte) to \x (2 bytes) result += 1; break; } default: { if (c >= 0x00 and c <= 0x1f) { // from c (1 byte) to \uxxxx (6 bytes) result += 5; } break; } } } return result; } /*! @brief escape a string Escape a string by replacing certain special characters by a sequence of an escape character (backslash) and another character and other control characters by a sequence of "\u" followed by a four-digit hex representation. @param[in] s the string to escape @return the escaped string @complexity Linear in the length of string @a s. */ static string_t escape_string(const string_t& s) noexcept { const auto space = extra_space(s); if (space == 0) { return s; } // create a result string of necessary size string_t result(s.size() + space, '\\'); std::size_t pos = 0; for (const auto& c : s) { switch (c) { // quotation mark (0x22) case '"': { result[pos + 1] = '"'; pos += 2; break; } // reverse solidus (0x5c) case '\\': { // nothing to change pos += 2; break; } // backspace (0x08) case '\b': { result[pos + 1] = 'b'; pos += 2; break; } // formfeed (0x0c) case '\f': { result[pos + 1] = 'f'; pos += 2; break; } // newline (0x0a) case '\n': { result[pos + 1] = 'n'; pos += 2; break; } // carriage return (0x0d) case '\r': { result[pos + 1] = 'r'; pos += 2; break; } // horizontal tab (0x09) case '\t': { result[pos + 1] = 't'; pos += 2; break; } default: { if (c >= 0x00 and c <= 0x1f) { // convert a number 0..15 to its hex representation (0..f) auto hexify = [](const char v) -> char { return (v < 10) ? ('0' + v) : ('a' + v - 10); }; // print character c as \uxxxx for (const char m : { 'u', '0', '0', hexify(c >> 4), hexify(c & 0x0f) }) { result[++pos] = m; } ++pos; } else { // all other characters are added as-is result[pos++] = c; } break; } } } return result; } /*! @brief internal implementation of the serialization function This function is called by the public member function dump and organizes the serializaion internally. The indentation level is propagated as additional parameter. In case of arrays and objects, the function is called recursively. Note that - strings and object keys are escaped using escape_string() - integer numbers are converted implictly via operator<< - floating-point numbers are converted to a string using "%g" format @param[out] o stream to write to @param[in] pretty_print whether the output shall be pretty-printed @param[in] indent_step the indent level @param[in] current_indent the current indent level (only used internally) */ void dump(std::ostream& o, const bool pretty_print, const unsigned int indent_step, const unsigned int current_indent = 0) const { // variable to hold indentation for recursive calls unsigned int new_indent = current_indent; switch (m_type) { case value_t::object: { if (m_value.object->empty()) { o << "{}"; return; } o << "{"; // increase indentation if (pretty_print) { new_indent += indent_step; o << "\n"; } for (auto i = m_value.object->cbegin(); i != m_value.object->cend(); ++i) { if (i != m_value.object->cbegin()) { o << (pretty_print ? ",\n" : ","); } o << string_t(new_indent, ' ') << "\"" << escape_string(i->first) << "\":" << (pretty_print ? " " : ""); i->second.dump(o, pretty_print, indent_step, new_indent); } // decrease indentation if (pretty_print) { new_indent -= indent_step; o << "\n"; } o << string_t(new_indent, ' ') + "}"; return; } case value_t::array: { if (m_value.array->empty()) { o << "[]"; return; } o << "["; // increase indentation if (pretty_print) { new_indent += indent_step; o << "\n"; } for (auto i = m_value.array->cbegin(); i != m_value.array->cend(); ++i) { if (i != m_value.array->cbegin()) { o << (pretty_print ? ",\n" : ","); } o << string_t(new_indent, ' '); i->dump(o, pretty_print, indent_step, new_indent); } // decrease indentation if (pretty_print) { new_indent -= indent_step; o << "\n"; } o << string_t(new_indent, ' ') << "]"; return; } case value_t::string: { o << string_t("\"") << escape_string(*m_value.string) << "\""; return; } case value_t::boolean: { o << (m_value.boolean ? "true" : "false"); return; } case value_t::number_integer: { o << m_value.number_integer; return; } case value_t::number_float: { // 15 digits of precision allows round-trip IEEE 754 // string->double->string; to be safe, we read this value from // std::numeric_limits::digits10 o << std::setprecision(std::numeric_limits::digits10) << m_value.number_float; return; } case value_t::discarded: { o << ""; return; } case value_t::null: { o << "null"; return; } } } private: ////////////////////// // member variables // ////////////////////// /// the type of the current element value_t m_type = value_t::null; /// the value of the current element json_value m_value = {}; private: /////////////// // iterators // /////////////// /*! @brief an iterator for primitive JSON types This class models an iterator for primitive JSON types (boolean, number, string). It's only purpose is to allow the iterator/const_iterator classes to "iterate" over primitive values. Internally, the iterator is modeled by a `difference_type` variable. Value begin_value (`0`) models the begin, end_value (`1`) models past the end. */ class primitive_iterator_t { public: /// set iterator to a defined beginning void set_begin() { m_it = begin_value; } /// set iterator to a defined past the end void set_end() { m_it = end_value; } /// return whether the iterator can be dereferenced bool is_begin() const { return (m_it == begin_value); } /// return whether the iterator is at end bool is_end() const { return (m_it == end_value); } /// return reference to the value to change and compare operator difference_type& () { return m_it; } /// return value to compare operator difference_type () const { return m_it; } private: static constexpr difference_type begin_value = 0; static constexpr difference_type end_value = begin_value + 1; /// iterator as signed integer type difference_type m_it = std::numeric_limits::denorm_min(); }; /*! @brief an iterator value @note This structure could easily be a union, but MSVC currently does not allow unions members with complex constructors, see https://github.com/nlohmann/json/pull/105. */ struct internal_iterator { /// iterator for JSON objects typename object_t::iterator object_iterator; /// iterator for JSON arrays typename array_t::iterator array_iterator; /// generic iterator for all other types primitive_iterator_t primitive_iterator; /// create an uninitialized internal_iterator internal_iterator() : object_iterator(), array_iterator(), primitive_iterator() {} }; /// proxy class for the iterator_wrapper functions template class iteration_proxy { private: /// helper class for iteration class iteration_proxy_internal { private: /// the iterator IteratorType anchor; /// an index for arrays (used to create key names) size_t array_index = 0; public: iteration_proxy_internal(IteratorType it) : anchor(it) {} /// dereference operator (needed for range-based for) iteration_proxy_internal& operator*() { return *this; } /// increment operator (needed for range-based for) iteration_proxy_internal& operator++() { ++anchor; ++array_index; return *this; } /// inequality operator (needed for range-based for) bool operator!= (const iteration_proxy_internal& o) const { return anchor != o.anchor; } /// return key of the iterator typename basic_json::string_t key() const { switch (anchor.m_object->type()) { // use integer array index as key case value_t::array: { return std::to_string(array_index); } // use key from the object case value_t::object: { return anchor.key(); } // use an empty key for all primitive types default: { return ""; } } } /// return value of the iterator typename IteratorType::reference value() const { return anchor.value(); } }; /// the container to iterate typename IteratorType::reference container; public: /// construct iteration proxy from a container iteration_proxy(typename IteratorType::reference cont) : container(cont) {} /// return iterator begin (needed for range-based for) iteration_proxy_internal begin() { return iteration_proxy_internal(container.begin()); } /// return iterator end (needed for range-based for) iteration_proxy_internal end() { return iteration_proxy_internal(container.end()); } }; public: /*! @brief a const random access iterator for the @ref basic_json class This class implements a const iterator for the @ref basic_json class. From this class, the @ref iterator class is derived. @requirement The class satisfies the following concept requirements: - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): The iterator that can be moved to point (forward and backward) to any element in constant time. @since version 1.0.0 */ class const_iterator : public std::iterator { /// allow basic_json to access private members friend class basic_json; public: /// the type of the values when the iterator is dereferenced using value_type = typename basic_json::value_type; /// a type to represent differences between iterators using difference_type = typename basic_json::difference_type; /// defines a pointer to the type iterated over (value_type) using pointer = typename basic_json::const_pointer; /// defines a reference to the type iterated over (value_type) using reference = typename basic_json::const_reference; /// the category of the iterator using iterator_category = std::bidirectional_iterator_tag; /// default constructor const_iterator() = default; /// constructor for a given JSON instance const_iterator(pointer object) : m_object(object) { switch (m_object->m_type) { case basic_json::value_t::object: { m_it.object_iterator = typename object_t::iterator(); break; } case basic_json::value_t::array: { m_it.array_iterator = typename array_t::iterator(); break; } default: { m_it.primitive_iterator = primitive_iterator_t(); break; } } } /// copy constructor given a nonconst iterator const_iterator(const iterator& other) : m_object(other.m_object) { switch (m_object->m_type) { case basic_json::value_t::object: { m_it.object_iterator = other.m_it.object_iterator; break; } case basic_json::value_t::array: { m_it.array_iterator = other.m_it.array_iterator; break; } default: { m_it.primitive_iterator = other.m_it.primitive_iterator; break; } } } /// copy constructor const_iterator(const const_iterator& other) noexcept : m_object(other.m_object), m_it(other.m_it) {} /// copy assignment const_iterator& operator=(const_iterator other) noexcept( std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable::value and std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable::value ) { std::swap(m_object, other.m_object); std::swap(m_it, other.m_it); return *this; } private: /// set the iterator to the first value void set_begin() { switch (m_object->m_type) { case basic_json::value_t::object: { m_it.object_iterator = m_object->m_value.object->begin(); break; } case basic_json::value_t::array: { m_it.array_iterator = m_object->m_value.array->begin(); break; } case basic_json::value_t::null: { // set to end so begin()==end() is true: null is empty m_it.primitive_iterator.set_end(); break; } default: { m_it.primitive_iterator.set_begin(); break; } } } /// set the iterator past the last value void set_end() { switch (m_object->m_type) { case basic_json::value_t::object: { m_it.object_iterator = m_object->m_value.object->end(); break; } case basic_json::value_t::array: { m_it.array_iterator = m_object->m_value.array->end(); break; } default: { m_it.primitive_iterator.set_end(); break; } } } public: /// return a reference to the value pointed to by the iterator reference operator*() const { switch (m_object->m_type) { case basic_json::value_t::object: { return m_it.object_iterator->second; } case basic_json::value_t::array: { return *m_it.array_iterator; } case basic_json::value_t::null: { throw std::out_of_range("cannot get value"); } default: { if (m_it.primitive_iterator.is_begin()) { return *m_object; } else { throw std::out_of_range("cannot get value"); } } } } /// dereference the iterator pointer operator->() const { switch (m_object->m_type) { case basic_json::value_t::object: { return &(m_it.object_iterator->second); } case basic_json::value_t::array: { return &*m_it.array_iterator; } default: { if (m_it.primitive_iterator.is_begin()) { return m_object; } else { throw std::out_of_range("cannot get value"); } } } } /// post-increment (it++) const_iterator operator++(int) { auto result = *this; ++(*this); return result; } /// pre-increment (++it) const_iterator& operator++() { switch (m_object->m_type) { case basic_json::value_t::object: { ++m_it.object_iterator; break; } case basic_json::value_t::array: { ++m_it.array_iterator; break; } default: { ++m_it.primitive_iterator; break; } } return *this; } /// post-decrement (it--) const_iterator operator--(int) { auto result = *this; --(*this); return result; } /// pre-decrement (--it) const_iterator& operator--() { switch (m_object->m_type) { case basic_json::value_t::object: { --m_it.object_iterator; break; } case basic_json::value_t::array: { --m_it.array_iterator; break; } default: { --m_it.primitive_iterator; break; } } return *this; } /// comparison: equal bool operator==(const const_iterator& other) const { // if objects are not the same, the comparison is undefined if (m_object != other.m_object) { throw std::domain_error("cannot compare iterators of different containers"); } switch (m_object->m_type) { case basic_json::value_t::object: { return (m_it.object_iterator == other.m_it.object_iterator); } case basic_json::value_t::array: { return (m_it.array_iterator == other.m_it.array_iterator); } default: { return (m_it.primitive_iterator == other.m_it.primitive_iterator); } } } /// comparison: not equal bool operator!=(const const_iterator& other) const { return not operator==(other); } /// comparison: smaller bool operator<(const const_iterator& other) const { // if objects are not the same, the comparison is undefined if (m_object != other.m_object) { throw std::domain_error("cannot compare iterators of different containers"); } switch (m_object->m_type) { case basic_json::value_t::object: { throw std::domain_error("cannot compare order of object iterators"); } case basic_json::value_t::array: { return (m_it.array_iterator < other.m_it.array_iterator); } default: { return (m_it.primitive_iterator < other.m_it.primitive_iterator); } } } /// comparison: less than or equal bool operator<=(const const_iterator& other) const { return not other.operator < (*this); } /// comparison: greater than bool operator>(const const_iterator& other) const { return not operator<=(other); } /// comparison: greater than or equal bool operator>=(const const_iterator& other) const { return not operator<(other); } /// add to iterator const_iterator& operator+=(difference_type i) { switch (m_object->m_type) { case basic_json::value_t::object: { throw std::domain_error("cannot use offsets with object iterators"); } case basic_json::value_t::array: { m_it.array_iterator += i; break; } default: { m_it.primitive_iterator += i; break; } } return *this; } /// subtract from iterator const_iterator& operator-=(difference_type i) { return operator+=(-i); } /// add to iterator const_iterator operator+(difference_type i) { auto result = *this; result += i; return result; } /// subtract from iterator const_iterator operator-(difference_type i) { auto result = *this; result -= i; return result; } /// return difference difference_type operator-(const const_iterator& other) const { switch (m_object->m_type) { case basic_json::value_t::object: { throw std::domain_error("cannot use offsets with object iterators"); } case basic_json::value_t::array: { return m_it.array_iterator - other.m_it.array_iterator; } default: { return m_it.primitive_iterator - other.m_it.primitive_iterator; } } } /// access to successor reference operator[](difference_type n) const { switch (m_object->m_type) { case basic_json::value_t::object: { throw std::domain_error("cannot use operator[] for object iterators"); } case basic_json::value_t::array: { return *(m_it.array_iterator + n); } case basic_json::value_t::null: { throw std::out_of_range("cannot get value"); } default: { if (m_it.primitive_iterator == -n) { return *m_object; } else { throw std::out_of_range("cannot get value"); } } } } /// return the key of an object iterator typename object_t::key_type key() const { if (m_object->is_object()) { return m_it.object_iterator->first; } else { throw std::domain_error("cannot use key() for non-object iterators"); } } /// return the value of an iterator reference value() const { return operator*(); } private: /// associated JSON instance pointer m_object = nullptr; /// the actual iterator of the associated instance internal_iterator m_it = internal_iterator(); }; /*! @brief a mutable random access iterator for the @ref basic_json class @requirement The class satisfies the following concept requirements: - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): The iterator that can be moved to point (forward and backward) to any element in constant time. - [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator): It is possible to write to the pointed-to element. @since version 1.0.0 */ class iterator : public const_iterator { public: using base_iterator = const_iterator; using pointer = typename basic_json::pointer; using reference = typename basic_json::reference; /// default constructor iterator() = default; /// constructor for a given JSON instance iterator(pointer object) noexcept : base_iterator(object) {} /// copy constructor iterator(const iterator& other) noexcept : base_iterator(other) {} /// copy assignment iterator& operator=(iterator other) noexcept( std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable::value and std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable::value ) { base_iterator::operator=(other); return *this; } /// return a reference to the value pointed to by the iterator reference operator*() { return const_cast(base_iterator::operator*()); } /// dereference the iterator pointer operator->() { return const_cast(base_iterator::operator->()); } /// post-increment (it++) iterator operator++(int) { iterator result = *this; base_iterator::operator++(); return result; } /// pre-increment (++it) iterator& operator++() { base_iterator::operator++(); return *this; } /// post-decrement (it--) iterator operator--(int) { iterator result = *this; base_iterator::operator--(); return result; } /// pre-decrement (--it) iterator& operator--() { base_iterator::operator--(); return *this; } /// add to iterator iterator& operator+=(difference_type i) { base_iterator::operator+=(i); return *this; } /// subtract from iterator iterator& operator-=(difference_type i) { base_iterator::operator-=(i); return *this; } /// add to iterator iterator operator+(difference_type i) { auto result = *this; result += i; return result; } /// subtract from iterator iterator operator-(difference_type i) { auto result = *this; result -= i; return result; } difference_type operator-(const iterator& other) const { return base_iterator::operator-(other); } /// access to successor reference operator[](difference_type n) const { return const_cast(base_iterator::operator[](n)); } /// return the value of an iterator reference value() const { return const_cast(base_iterator::value()); } }; /*! @brief a template for a reverse iterator class @tparam Base the base iterator type to reverse. Valid types are @ref iterator (to create @ref reverse_iterator) and @ref const_iterator (to create @ref const_reverse_iterator). @requirement The class satisfies the following concept requirements: - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): The iterator that can be moved to point (forward and backward) to any element in constant time. - [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator): It is possible to write to the pointed-to element (only if @a Base is @ref iterator). @since version 1.0.0 */ template class json_reverse_iterator : public std::reverse_iterator { public: /// shortcut to the reverse iterator adaptor using base_iterator = std::reverse_iterator; /// the reference type for the pointed-to element using reference = typename Base::reference; /// create reverse iterator from iterator json_reverse_iterator(const typename base_iterator::iterator_type& it) : base_iterator(it) {} /// create reverse iterator from base class json_reverse_iterator(const base_iterator& it) : base_iterator(it) {} /// post-increment (it++) json_reverse_iterator operator++(int) { return base_iterator::operator++(1); } /// pre-increment (++it) json_reverse_iterator& operator++() { base_iterator::operator++(); return *this; } /// post-decrement (it--) json_reverse_iterator operator--(int) { return base_iterator::operator--(1); } /// pre-decrement (--it) json_reverse_iterator& operator--() { base_iterator::operator--(); return *this; } /// add to iterator json_reverse_iterator& operator+=(difference_type i) { base_iterator::operator+=(i); return *this; } /// add to iterator json_reverse_iterator operator+(difference_type i) const { auto result = *this; result += i; return result; } /// subtract from iterator json_reverse_iterator operator-(difference_type i) const { auto result = *this; result -= i; return result; } /// return difference difference_type operator-(const json_reverse_iterator& other) const { return this->base() - other.base(); } /// access to successor reference operator[](difference_type n) const { return *(this->operator+(n)); } /// return the key of an object iterator typename object_t::key_type key() const { auto it = --this->base(); return it.key(); } /// return the value of an iterator reference value() const { auto it = --this->base(); return it.operator * (); } }; private: ////////////////////// // lexer and parser // ////////////////////// /*! @brief lexical analysis This class organizes the lexical analysis during JSON deserialization. The core of it is a scanner generated by re2c that processes a buffer and recognizes tokens according to RFC 7159. */ class lexer { public: /// token types for the parser enum class token_type { uninitialized, ///< indicating the scanner is uninitialized literal_true, ///< the "true" literal literal_false, ///< the "false" literal literal_null, ///< the "null" literal value_string, ///< a string -- use get_string() for actual value value_number, ///< a number -- use get_number() for actual value begin_array, ///< the character for array begin "[" begin_object, ///< the character for object begin "{" end_array, ///< the character for array end "]" end_object, ///< the character for object end "}" name_separator, ///< the name separator ":" value_separator, ///< the value separator "," parse_error, ///< indicating a parse error end_of_input ///< indicating the end of the input buffer }; /// the char type to use in the lexer using lexer_char_t = unsigned char; /// constructor with a given buffer explicit lexer(const string_t& s) noexcept : m_stream(nullptr), m_buffer(s) { m_content = reinterpret_cast(s.c_str()); m_start = m_cursor = m_content; m_limit = m_content + s.size(); } /// constructor with a given stream explicit lexer(std::istream* s) noexcept : m_stream(s), m_buffer() { getline(*m_stream, m_buffer); m_content = reinterpret_cast(m_buffer.c_str()); m_start = m_cursor = m_content; m_limit = m_content + m_buffer.size(); } /// default constructor lexer() = default; // switch off unwanted functions lexer(const lexer&) = delete; lexer operator=(const lexer&) = delete; /*! @brief create a string from a Unicode code point @param[in] codepoint1 the code point (can be high surrogate) @param[in] codepoint2 the code point (can be low surrogate or 0) @return string representation of the code point @throw std::out_of_range if code point is >0x10ffff; example: `"code points above 0x10FFFF are invalid"` @throw std::invalid_argument if the low surrogate is invalid; example: `""missing or wrong low surrogate""` @see */ static string_t to_unicode(const std::size_t codepoint1, const std::size_t codepoint2 = 0) { string_t result; // calculate the codepoint from the given code points std::size_t codepoint = codepoint1; // check if codepoint1 is a high surrogate if (codepoint1 >= 0xD800 and codepoint1 <= 0xDBFF) { // check if codepoint2 is a low surrogate if (codepoint2 >= 0xDC00 and codepoint2 <= 0xDFFF) { codepoint = // high surrogate occupies the most significant 22 bits (codepoint1 << 10) // low surrogate occupies the least significant 15 bits + codepoint2 // there is still the 0xD800, 0xDC00 and 0x10000 noise // in the result so we have to substract with: // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 - 0x35FDC00; } else { throw std::invalid_argument("missing or wrong low surrogate"); } } if (codepoint < 0x80) { // 1-byte characters: 0xxxxxxx (ASCII) result.append(1, static_cast(codepoint)); } else if (codepoint <= 0x7ff) { // 2-byte characters: 110xxxxx 10xxxxxx result.append(1, static_cast(0xC0 | ((codepoint >> 6) & 0x1F))); result.append(1, static_cast(0x80 | (codepoint & 0x3F))); } else if (codepoint <= 0xffff) { // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx result.append(1, static_cast(0xE0 | ((codepoint >> 12) & 0x0F))); result.append(1, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); result.append(1, static_cast(0x80 | (codepoint & 0x3F))); } else if (codepoint <= 0x10ffff) { // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx result.append(1, static_cast(0xF0 | ((codepoint >> 18) & 0x07))); result.append(1, static_cast(0x80 | ((codepoint >> 12) & 0x3F))); result.append(1, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); result.append(1, static_cast(0x80 | (codepoint & 0x3F))); } else { throw std::out_of_range("code points above 0x10FFFF are invalid"); } return result; } /// return name of values of type token_type (only used for errors) static std::string token_type_name(token_type t) { switch (t) { case token_type::uninitialized: return ""; case token_type::literal_true: return "true literal"; case token_type::literal_false: return "false literal"; case token_type::literal_null: return "null literal"; case token_type::value_string: return "string literal"; case token_type::value_number: return "number literal"; case token_type::begin_array: return "'['"; case token_type::begin_object: return "'{'"; case token_type::end_array: return "']'"; case token_type::end_object: return "'}'"; case token_type::name_separator: return "':'"; case token_type::value_separator: return "','"; case token_type::parse_error: return ""; case token_type::end_of_input: return "end of input"; default: { // catch non-enum values return "unknown token"; // LCOV_EXCL_LINE } } } /*! This function implements a scanner for JSON. It is specified using regular expressions that try to follow RFC 7159 as close as possible. These regular expressions are then translated into a deterministic finite automaton (DFA) by the tool re2c . As a result, the translated code for this function consists of a large block of code with goto jumps. @return the class of the next token read from the buffer */ token_type scan() noexcept { // pointer for backtracking information m_marker = nullptr; // remember the begin of the token m_start = m_cursor; { lexer_char_t yych; unsigned int yyaccept = 0; static const unsigned char yybm[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 32, 0, 0, 32, 0, 0, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96, 64, 0, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 0, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, }; if ((m_limit - m_cursor) < 5) { yyfill(); // LCOV_EXCL_LINE; } yych = *m_cursor; if (yych <= ':') { if (yych <= ' ') { if (yych <= '\n') { if (yych <= 0x00) { goto basic_json_parser_28; } if (yych <= 0x08) { goto basic_json_parser_30; } if (yych >= '\n') { goto basic_json_parser_4; } } else { if (yych == '\r') { goto basic_json_parser_2; } if (yych <= 0x1F) { goto basic_json_parser_30; } } } else { if (yych <= ',') { if (yych == '"') { goto basic_json_parser_27; } if (yych <= '+') { goto basic_json_parser_30; } goto basic_json_parser_16; } else { if (yych <= '/') { if (yych <= '-') { goto basic_json_parser_23; } goto basic_json_parser_30; } else { if (yych <= '0') { goto basic_json_parser_24; } if (yych <= '9') { goto basic_json_parser_26; } goto basic_json_parser_18; } } } } else { if (yych <= 'n') { if (yych <= ']') { if (yych == '[') { goto basic_json_parser_8; } if (yych <= '\\') { goto basic_json_parser_30; } goto basic_json_parser_10; } else { if (yych == 'f') { goto basic_json_parser_22; } if (yych <= 'm') { goto basic_json_parser_30; } goto basic_json_parser_20; } } else { if (yych <= '{') { if (yych == 't') { goto basic_json_parser_21; } if (yych <= 'z') { goto basic_json_parser_30; } goto basic_json_parser_12; } else { if (yych <= '}') { if (yych <= '|') { goto basic_json_parser_30; } goto basic_json_parser_14; } else { if (yych == 0xEF) { goto basic_json_parser_6; } goto basic_json_parser_30; } } } } basic_json_parser_2: ++m_cursor; yych = *m_cursor; goto basic_json_parser_5; basic_json_parser_3: { return scan(); } basic_json_parser_4: ++m_cursor; if (m_limit <= m_cursor) { yyfill(); // LCOV_EXCL_LINE; } yych = *m_cursor; basic_json_parser_5: if (yybm[0 + yych] & 32) { goto basic_json_parser_4; } goto basic_json_parser_3; basic_json_parser_6: yyaccept = 0; yych = *(m_marker = ++m_cursor); if (yych == 0xBB) { goto basic_json_parser_64; } basic_json_parser_7: { return token_type::parse_error; } basic_json_parser_8: ++m_cursor; { return token_type::begin_array; } basic_json_parser_10: ++m_cursor; { return token_type::end_array; } basic_json_parser_12: ++m_cursor; { return token_type::begin_object; } basic_json_parser_14: ++m_cursor; { return token_type::end_object; } basic_json_parser_16: ++m_cursor; { return token_type::value_separator; } basic_json_parser_18: ++m_cursor; { return token_type::name_separator; } basic_json_parser_20: yyaccept = 0; yych = *(m_marker = ++m_cursor); if (yych == 'u') { goto basic_json_parser_60; } goto basic_json_parser_7; basic_json_parser_21: yyaccept = 0; yych = *(m_marker = ++m_cursor); if (yych == 'r') { goto basic_json_parser_56; } goto basic_json_parser_7; basic_json_parser_22: yyaccept = 0; yych = *(m_marker = ++m_cursor); if (yych == 'a') { goto basic_json_parser_51; } goto basic_json_parser_7; basic_json_parser_23: yych = *++m_cursor; if (yych <= '/') { goto basic_json_parser_7; } if (yych <= '0') { goto basic_json_parser_50; } if (yych <= '9') { goto basic_json_parser_41; } goto basic_json_parser_7; basic_json_parser_24: yyaccept = 1; yych = *(m_marker = ++m_cursor); if (yych <= 'D') { if (yych == '.') { goto basic_json_parser_43; } } else { if (yych <= 'E') { goto basic_json_parser_44; } if (yych == 'e') { goto basic_json_parser_44; } } basic_json_parser_25: { return token_type::value_number; } basic_json_parser_26: yyaccept = 1; yych = *(m_marker = ++m_cursor); goto basic_json_parser_42; basic_json_parser_27: yyaccept = 0; yych = *(m_marker = ++m_cursor); if (yych <= 0x0F) { goto basic_json_parser_7; } goto basic_json_parser_32; basic_json_parser_28: ++m_cursor; { return token_type::end_of_input; } basic_json_parser_30: yych = *++m_cursor; goto basic_json_parser_7; basic_json_parser_31: ++m_cursor; if (m_limit <= m_cursor) { yyfill(); // LCOV_EXCL_LINE; } yych = *m_cursor; basic_json_parser_32: if (yybm[0 + yych] & 64) { goto basic_json_parser_31; } if (yych <= 0x0F) { goto basic_json_parser_33; } if (yych <= '"') { goto basic_json_parser_35; } goto basic_json_parser_34; basic_json_parser_33: m_cursor = m_marker; if (yyaccept == 0) { goto basic_json_parser_7; } else { goto basic_json_parser_25; } basic_json_parser_34: ++m_cursor; if (m_limit <= m_cursor) { yyfill(); // LCOV_EXCL_LINE; } yych = *m_cursor; if (yych <= 'e') { if (yych <= '/') { if (yych == '"') { goto basic_json_parser_31; } if (yych <= '.') { goto basic_json_parser_33; } goto basic_json_parser_31; } else { if (yych <= '\\') { if (yych <= '[') { goto basic_json_parser_33; } goto basic_json_parser_31; } else { if (yych == 'b') { goto basic_json_parser_31; } goto basic_json_parser_33; } } } else { if (yych <= 'q') { if (yych <= 'f') { goto basic_json_parser_31; } if (yych == 'n') { goto basic_json_parser_31; } goto basic_json_parser_33; } else { if (yych <= 's') { if (yych <= 'r') { goto basic_json_parser_31; } goto basic_json_parser_33; } else { if (yych <= 't') { goto basic_json_parser_31; } if (yych <= 'u') { goto basic_json_parser_37; } goto basic_json_parser_33; } } } basic_json_parser_35: ++m_cursor; { return token_type::value_string; } basic_json_parser_37: ++m_cursor; if (m_limit <= m_cursor) { yyfill(); // LCOV_EXCL_LINE; } yych = *m_cursor; if (yych <= '@') { if (yych <= '/') { goto basic_json_parser_33; } if (yych >= ':') { goto basic_json_parser_33; } } else { if (yych <= 'F') { goto basic_json_parser_38; } if (yych <= '`') { goto basic_json_parser_33; } if (yych >= 'g') { goto basic_json_parser_33; } } basic_json_parser_38: ++m_cursor; if (m_limit <= m_cursor) { yyfill(); // LCOV_EXCL_LINE; } yych = *m_cursor; if (yych <= '@') { if (yych <= '/') { goto basic_json_parser_33; } if (yych >= ':') { goto basic_json_parser_33; } } else { if (yych <= 'F') { goto basic_json_parser_39; } if (yych <= '`') { goto basic_json_parser_33; } if (yych >= 'g') { goto basic_json_parser_33; } } basic_json_parser_39: ++m_cursor; if (m_limit <= m_cursor) { yyfill(); // LCOV_EXCL_LINE; } yych = *m_cursor; if (yych <= '@') { if (yych <= '/') { goto basic_json_parser_33; } if (yych >= ':') { goto basic_json_parser_33; } } else { if (yych <= 'F') { goto basic_json_parser_40; } if (yych <= '`') { goto basic_json_parser_33; } if (yych >= 'g') { goto basic_json_parser_33; } } basic_json_parser_40: ++m_cursor; if (m_limit <= m_cursor) { yyfill(); // LCOV_EXCL_LINE; } yych = *m_cursor; if (yych <= '@') { if (yych <= '/') { goto basic_json_parser_33; } if (yych <= '9') { goto basic_json_parser_31; } goto basic_json_parser_33; } else { if (yych <= 'F') { goto basic_json_parser_31; } if (yych <= '`') { goto basic_json_parser_33; } if (yych <= 'f') { goto basic_json_parser_31; } goto basic_json_parser_33; } basic_json_parser_41: yyaccept = 1; m_marker = ++m_cursor; if ((m_limit - m_cursor) < 3) { yyfill(); // LCOV_EXCL_LINE; } yych = *m_cursor; basic_json_parser_42: if (yybm[0 + yych] & 128) { goto basic_json_parser_41; } if (yych <= 'D') { if (yych != '.') { goto basic_json_parser_25; } } else { if (yych <= 'E') { goto basic_json_parser_44; } if (yych == 'e') { goto basic_json_parser_44; } goto basic_json_parser_25; } basic_json_parser_43: yych = *++m_cursor; if (yych <= '/') { goto basic_json_parser_33; } if (yych <= '9') { goto basic_json_parser_48; } goto basic_json_parser_33; basic_json_parser_44: yych = *++m_cursor; if (yych <= ',') { if (yych != '+') { goto basic_json_parser_33; } } else { if (yych <= '-') { goto basic_json_parser_45; } if (yych <= '/') { goto basic_json_parser_33; } if (yych <= '9') { goto basic_json_parser_46; } goto basic_json_parser_33; } basic_json_parser_45: yych = *++m_cursor; if (yych <= '/') { goto basic_json_parser_33; } if (yych >= ':') { goto basic_json_parser_33; } basic_json_parser_46: ++m_cursor; if (m_limit <= m_cursor) { yyfill(); // LCOV_EXCL_LINE; } yych = *m_cursor; if (yych <= '/') { goto basic_json_parser_25; } if (yych <= '9') { goto basic_json_parser_46; } goto basic_json_parser_25; basic_json_parser_48: yyaccept = 1; m_marker = ++m_cursor; if ((m_limit - m_cursor) < 3) { yyfill(); // LCOV_EXCL_LINE; } yych = *m_cursor; if (yych <= 'D') { if (yych <= '/') { goto basic_json_parser_25; } if (yych <= '9') { goto basic_json_parser_48; } goto basic_json_parser_25; } else { if (yych <= 'E') { goto basic_json_parser_44; } if (yych == 'e') { goto basic_json_parser_44; } goto basic_json_parser_25; } basic_json_parser_50: yyaccept = 1; yych = *(m_marker = ++m_cursor); if (yych <= 'D') { if (yych == '.') { goto basic_json_parser_43; } goto basic_json_parser_25; } else { if (yych <= 'E') { goto basic_json_parser_44; } if (yych == 'e') { goto basic_json_parser_44; } goto basic_json_parser_25; } basic_json_parser_51: yych = *++m_cursor; if (yych != 'l') { goto basic_json_parser_33; } yych = *++m_cursor; if (yych != 's') { goto basic_json_parser_33; } yych = *++m_cursor; if (yych != 'e') { goto basic_json_parser_33; } ++m_cursor; { return token_type::literal_false; } basic_json_parser_56: yych = *++m_cursor; if (yych != 'u') { goto basic_json_parser_33; } yych = *++m_cursor; if (yych != 'e') { goto basic_json_parser_33; } ++m_cursor; { return token_type::literal_true; } basic_json_parser_60: yych = *++m_cursor; if (yych != 'l') { goto basic_json_parser_33; } yych = *++m_cursor; if (yych != 'l') { goto basic_json_parser_33; } ++m_cursor; { return token_type::literal_null; } basic_json_parser_64: yych = *++m_cursor; if (yych != 0xBF) { goto basic_json_parser_33; } ++m_cursor; { return scan(); } } } /// append data from the stream to the internal buffer void yyfill() noexcept { if (not m_stream or not * m_stream) { return; } const ssize_t offset_start = m_start - m_content; const ssize_t offset_marker = m_marker - m_start; const ssize_t offset_cursor = m_cursor - m_start; m_buffer.erase(0, static_cast(offset_start)); std::string line; std::getline(*m_stream, line); m_buffer += "\n" + line; // add line with newline symbol m_content = reinterpret_cast(m_buffer.c_str()); m_start = m_content; m_marker = m_start + offset_marker; m_cursor = m_start + offset_cursor; m_limit = m_start + m_buffer.size() - 1; } /// return string representation of last read token string_t get_token() const noexcept { return string_t(reinterpret_cast(m_start), static_cast(m_cursor - m_start)); } /*! @brief return string value for string tokens The function iterates the characters between the opening and closing quotes of the string value. The complete string is the range [m_start,m_cursor). Consequently, we iterate from m_start+1 to m_cursor-1. We differentiate two cases: 1. Escaped characters. In this case, a new character is constructed according to the nature of the escape. Some escapes create new characters (e.g., @c "\\n" is replaced by @c "\n"), some are copied as is (e.g., @c "\\\\"). Furthermore, Unicode escapes of the shape @c "\\uxxxx" need special care. In this case, to_unicode takes care of the construction of the values. 2. Unescaped characters are copied as is. @return string value of current token without opening and closing quotes @throw std::out_of_range if to_unicode fails */ string_t get_string() const { string_t result; result.reserve(static_cast(m_cursor - m_start - 2)); // iterate the result between the quotes for (const lexer_char_t* i = m_start + 1; i < m_cursor - 1; ++i) { // process escaped characters if (*i == '\\') { // read next character ++i; switch (*i) { // the default escapes case 't': { result += "\t"; break; } case 'b': { result += "\b"; break; } case 'f': { result += "\f"; break; } case 'n': { result += "\n"; break; } case 'r': { result += "\r"; break; } case '\\': { result += "\\"; break; } case '/': { result += "/"; break; } case '"': { result += "\""; break; } // unicode case 'u': { // get code xxxx from uxxxx auto codepoint = std::strtoul(std::string(reinterpret_cast(i + 1), 4).c_str(), nullptr, 16); // check if codepoint is a high surrogate if (codepoint >= 0xD800 and codepoint <= 0xDBFF) { // make sure there is a subsequent unicode if ((i + 6 >= m_limit) or * (i + 5) != '\\' or * (i + 6) != 'u') { throw std::invalid_argument("missing low surrogate"); } // get code yyyy from uxxxx\uyyyy auto codepoint2 = std::strtoul(std::string(reinterpret_cast (i + 7), 4).c_str(), nullptr, 16); result += to_unicode(codepoint, codepoint2); // skip the next 10 characters (xxxx\uyyyy) i += 10; } else { // add unicode character(s) result += to_unicode(codepoint); // skip the next four characters (xxxx) i += 4; } break; } } } else { // all other characters are just copied to the end of the // string result.append(1, static_cast(*i)); } } return result; } /*! @brief return number value for number tokens This function translates the last token into a floating point number. The pointer m_start points to the beginning of the parsed number. We pass this pointer to std::strtod which sets endptr to the first character past the converted number. If this pointer is not the same as m_cursor, then either more or less characters have been used during the comparison. This can happen for inputs like "01" which will be treated like number 0 followed by number 1. @return the result of the number conversion or NAN if the conversion read past the current token. The latter case needs to be treated by the caller function. @throw std::range_error if passed value is out of range */ long double get_number() const { // conversion typename string_t::value_type* endptr; const auto float_val = std::strtold(reinterpret_cast(m_start), &endptr); // return float_val if the whole number was translated and NAN // otherwise return (reinterpret_cast(endptr) == m_cursor) ? float_val : NAN; } private: /// optional input stream std::istream* m_stream; /// the buffer string_t m_buffer; /// the buffer pointer const lexer_char_t* m_content = nullptr; /// pointer to the beginning of the current symbol const lexer_char_t* m_start = nullptr; /// pointer for backtracking information const lexer_char_t* m_marker = nullptr; /// pointer to the current symbol const lexer_char_t* m_cursor = nullptr; /// pointer to the end of the buffer const lexer_char_t* m_limit = nullptr; }; /*! @brief syntax analysis This class implements a recursive decent parser. */ class parser { public: /// constructor for strings parser(const string_t& s, parser_callback_t cb = nullptr) : callback(cb), m_lexer(s) { // read first token get_token(); } /// a parser reading from an input stream parser(std::istream& _is, parser_callback_t cb = nullptr) : callback(cb), m_lexer(&_is) { // read first token get_token(); } /// public parser interface basic_json parse() { basic_json result = parse_internal(true); expect(lexer::token_type::end_of_input); // return parser result and replace it with null in case the // top-level value was discarded by the callback function return result.is_discarded() ? basic_json() : result; } private: /// the actual parser basic_json parse_internal(bool keep) { auto result = basic_json(value_t::discarded); switch (last_token) { case lexer::token_type::begin_object: { if (keep and (not callback or (keep = callback(depth++, parse_event_t::object_start, result)))) { // explicitly set result to object to cope with {} result.m_type = value_t::object; result.m_value = json_value(value_t::object); } // read next token get_token(); // closing } -> we are done if (last_token == lexer::token_type::end_object) { get_token(); if (keep and callback and not callback(--depth, parse_event_t::object_end, result)) { result = basic_json(value_t::discarded); } return result; } // no comma is expected here unexpect(lexer::token_type::value_separator); // otherwise: parse key-value pairs do { // ugly, but could be fixed with loop reorganization if (last_token == lexer::token_type::value_separator) { get_token(); } // store key expect(lexer::token_type::value_string); const auto key = m_lexer.get_string(); bool keep_tag = false; if (keep) { if (callback) { basic_json k(key); keep_tag = callback(depth, parse_event_t::key, k); } else { keep_tag = true; } } // parse separator (:) get_token(); expect(lexer::token_type::name_separator); // parse and add value get_token(); auto value = parse_internal(keep); if (keep and keep_tag and not value.is_discarded()) { result[key] = std::move(value); } } while (last_token == lexer::token_type::value_separator); // closing } expect(lexer::token_type::end_object); get_token(); if (keep and callback and not callback(--depth, parse_event_t::object_end, result)) { result = basic_json(value_t::discarded); } return result; } case lexer::token_type::begin_array: { if (keep and (not callback or (keep = callback(depth++, parse_event_t::array_start, result)))) { // explicitly set result to object to cope with [] result.m_type = value_t::array; result.m_value = json_value(value_t::array); } // read next token get_token(); // closing ] -> we are done if (last_token == lexer::token_type::end_array) { get_token(); if (callback and not callback(--depth, parse_event_t::array_end, result)) { result = basic_json(value_t::discarded); } return result; } // no comma is expected here unexpect(lexer::token_type::value_separator); // otherwise: parse values do { // ugly, but could be fixed with loop reorganization if (last_token == lexer::token_type::value_separator) { get_token(); } // parse value auto value = parse_internal(keep); if (keep and not value.is_discarded()) { result.push_back(std::move(value)); } } while (last_token == lexer::token_type::value_separator); // closing ] expect(lexer::token_type::end_array); get_token(); if (keep and callback and not callback(--depth, parse_event_t::array_end, result)) { result = basic_json(value_t::discarded); } return result; } case lexer::token_type::literal_null: { get_token(); result.m_type = value_t::null; break; } case lexer::token_type::value_string: { const auto s = m_lexer.get_string(); get_token(); result = basic_json(s); break; } case lexer::token_type::literal_true: { get_token(); result.m_type = value_t::boolean; result.m_value = true; break; } case lexer::token_type::literal_false: { get_token(); result.m_type = value_t::boolean; result.m_value = false; break; } case lexer::token_type::value_number: { auto float_val = m_lexer.get_number(); // NAN is returned if token could not be translated // completely if (std::isnan(float_val)) { throw std::invalid_argument(std::string("parse error - ") + m_lexer.get_token() + " is not a number"); } get_token(); // check if conversion loses precision const auto int_val = static_cast(float_val); if (approx(float_val, static_cast(int_val))) { // we would not lose precision -> return int result.m_type = value_t::number_integer; result.m_value = int_val; } else { // we would lose precision -> return float result.m_type = value_t::number_float; result.m_value = static_cast(float_val); } break; } default: { // the last token was unexpected unexpect(last_token); } } if (keep and callback and not callback(depth, parse_event_t::value, result)) { result = basic_json(value_t::discarded); } return result; } /// get next token from lexer typename lexer::token_type get_token() { last_token = m_lexer.scan(); return last_token; } void expect(typename lexer::token_type t) const { if (t != last_token) { std::string error_msg = "parse error - unexpected "; error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token() + "'") : lexer::token_type_name(last_token)); error_msg += "; expected " + lexer::token_type_name(t); throw std::invalid_argument(error_msg); } } void unexpect(typename lexer::token_type t) const { if (t == last_token) { std::string error_msg = "parse error - unexpected "; error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token() + "'") : lexer::token_type_name(last_token)); throw std::invalid_argument(error_msg); } } private: /// current level of recursion int depth = 0; /// callback function parser_callback_t callback; /// the type of the last read token typename lexer::token_type last_token = lexer::token_type::uninitialized; /// the lexer lexer m_lexer; }; }; ///////////// // presets // ///////////// /*! @brief default JSON class This type is the default specialization of the @ref basic_json class which uses the standard template types. @since version 1.0.0 */ using json = basic_json<>; } ///////////////////////// // nonmember functions // ///////////////////////// // specialization of std::swap, and std::hash namespace std { /*! @brief exchanges the values of two JSON objects @since version 1.0.0 */ template <> inline void swap(nlohmann::json& j1, nlohmann::json& j2) noexcept( is_nothrow_move_constructible::value and is_nothrow_move_assignable::value ) { j1.swap(j2); } /// hash value for JSON objects template <> struct hash { /*! @brief return a hash value for a JSON object @since version 1.0.0 */ std::size_t operator()(const nlohmann::json& j) const { // a naive hashing via the string representation const auto& h = hash(); return h(j.dump()); } }; } /*! @brief user-defined string literal for JSON values This operator implements a user-defined string literal for JSON objects. It can be used by adding \p "_json" to a string literal and returns a JSON object if no parse error occurred. @param[in] s a string representation of a JSON object @return a JSON object @since version 1.0.0 */ inline nlohmann::json operator "" _json(const char* s, std::size_t) { return nlohmann::json::parse(reinterpret_cast (const_cast(s))); } #endif biometryd-0.3.1/src/biometry/util/json_configuration_builder.cpp000066400000000000000000000047711455450034500252370ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include #include #include namespace json = nlohmann; namespace { boost::optional build_from_json(const json::json& j) { if (j.is_boolean()) return biometry::util::Configuration::Node{biometry::Variant::b(j)}; if (j.is_number_integer()) return biometry::util::Configuration::Node{biometry::Variant::i(j)}; if (j.is_number_float()) return biometry::util::Configuration::Node{biometry::Variant::d(j)}; if (j.is_string()) return biometry::util::Configuration::Node{biometry::Variant::s(j)}; if (j.is_array()) { biometry::util::Configuration::Node result; for (std::size_t i = 0; i < j.size(); i++) if (auto n = build_from_json(j.at(i))) result[std::to_string(i)] = *n; return result; } if (j.is_object()) { biometry::util::Configuration::Node result; for(auto it = j.begin(); it != j.end(); ++it) if (auto n = build_from_json(*it)) result[it.key()] = *n; return result; } return boost::optional{}; } } biometry::util::JsonConfigurationBuilder::JsonConfigurationBuilder(std::istream& in) : in{in} { if (not in) throw std::runtime_error { "Input stream is not available for reading." }; } biometry::util::Configuration biometry::util::JsonConfigurationBuilder::build_configuration() { util::Configuration result; auto j = json::json::parse(in); for (auto it = j.begin(); it != j.end(); ++it) if (auto n = build_from_json(*it)) result.children()[it.key()] = *n; return result; } biometryd-0.3.1/src/biometry/util/json_configuration_builder.h000066400000000000000000000030101455450034500246650ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRY_UTIL_JSON_CONFIGURATION_BUILDER_H_ #define BIOMETRY_UTIL_JSON_CONFIGURATION_BUILDER_H_ #include #include #include #include #include namespace biometry { namespace util { /// @brief JsonConfigurationBuilder implements ConfigurationBuilder for sources in JSON format. class BIOMETRY_DLL_PUBLIC JsonConfigurationBuilder : public ConfigurationBuilder { public: /// @brief JsonConfigurationBuilder initializes a new instance reading from the given input stream. JsonConfigurationBuilder(std::istream& in); /// build_configuration returns a Configuration assembled from a JSON-formatted file. Configuration build_configuration() override; private: std::istream& in; }; } } #endif // BIOMETRY_UTIL_CONFIGURATION_H_ biometryd-0.3.1/src/biometry/util/not_implemented.cpp000066400000000000000000000017621455450034500230110ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include biometry::util::NotImplemented::NotImplemented(const std::string& function) : std::logic_error{"Missing implementation for " + function} { } void biometry::util::not_implemented(const std::string& function) { throw NotImplemented{function}; } biometryd-0.3.1/src/biometry/util/not_implemented.h000066400000000000000000000025051455450034500224520ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRY_UTIL_NOT_IMPLEMENTED_H_ #define BIOMETRY_UTIL_NOT_IMPLEMENTED_H_ #include #include namespace biometry { namespace util { /// @brief NotImplemented is thrown from not_implemented(), indicating /// a missing implementation. struct NotImplemented : public std::logic_error { /// @brief NotImplemented initializes a new instance for the given function name. explicit NotImplemented(const std::string& function); }; /// @brief not_implemented throws NotImplemented. __attribute__((noreturn)) void not_implemented(const std::string& function); } } #endif // BIOMETRY_UTIL_NOT_IMPLEMENTED_H_ biometryd-0.3.1/src/biometry/util/not_reachable.cpp000066400000000000000000000022411455450034500224050ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include biometry::util::NotReachable::NotReachable(const std::string& function, const std::string& file, std::uint32_t line) : std::logic_error{(boost::format{"Code should not be reachable: %1% in %2%:%3%"} % function % file % line).str()} { } void biometry::util::not_reachable(const std::string& function, const std::string& file, std::uint32_t line) { throw NotReachable{function, file, line}; } biometryd-0.3.1/src/biometry/util/not_reachable.h000066400000000000000000000026531455450034500220610ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRY_UTIL_NOT_REACHABLE_H_ #define BIOMETRY_UTIL_NOT_REACHABLE_H_ #include #include #include #include namespace biometry { namespace util { /// @brief NotReachable is thrown from not_reachable. struct BIOMETRY_DLL_PUBLIC NotReachable : public std::logic_error { /// @brief NotImplemented initializes a new instance for the given function name. NotReachable(const std::string& function, const std::string& file, std::uint32_t line); }; /// @brief not_implemented throws NotImplemented. BIOMETRY_DLL_PUBLIC [[noreturn]] void not_reachable(const std::string& function, const std::string& file, std::uint32_t line); } } #endif // BIOMETRY_UTIL_NOT_REACHABLE_H_ biometryd-0.3.1/src/biometry/util/once.h000066400000000000000000000033201455450034500202070ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRY_UTIL_ONCE_H_ #define BIOMETRY_UTIL_ONCE_H_ #include #include namespace biometry { namespace util { /// @brief Once models an optional value that is initialized at most once. template class Once { public: /// @brief operator() returns an immutable reference to the value managed by this Once instance. /// @throws boost::bad_optional_access if the managed value is empty. const T& operator()() const { return value.value(); } /// @brief operator() returns an immutable reference to the value managed by this Once instance, /// using f to initialize an instance if needed. /// @throws if f throws. template const T& operator()(const F& f) const { std::call_once(init_once, [this, &f]() { value = f(); }); return *value; } private: mutable std::once_flag init_once; mutable boost::optional value; }; } } #endif // BIOMETRY_UTIL_ONCE_H_ biometryd-0.3.1/src/biometry/util/property_store.cpp000066400000000000000000000024511455450034500227220ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include std::string biometry::util::AndroidPropertyStore::get(const std::string& key) const { core::posix::ChildProcess getprop = core::posix::exec("/usr/bin/getprop", {key}, {}, core::posix::StandardStream::stdout); std::string value; getprop.cout() >> value; auto result = getprop.wait_for(core::posix::wait::Flags::untraced); if (result.status == core::posix::wait::Result::Status::exited && result.detail.if_exited.status == core::posix::exit::Status::success) return value; throw std::out_of_range{key}; } biometryd-0.3.1/src/biometry/util/property_store.h000066400000000000000000000030331455450034500223640ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRY_UTIL_PROPERTY_STORE_H_ #define BIOMETRY_UTIL_PROPERTY_STORE_H_ #include #include #include namespace biometry { namespace util { /// @brief PropertyStore provides access to system-wide properties. class BIOMETRY_DLL_PUBLIC PropertyStore : public DoNotCopyOrMove { public: /// @brief get returns the value associated with keys. /// @throws std::out_of_range if no value is known for key. virtual std::string get(const std::string& key) const = 0; }; /// @brief AndroidPropertyStore queries properties from the android property system. class BIOMETRY_DLL_PUBLIC AndroidPropertyStore : public PropertyStore { public: // From PropertyStore. std::string get(const std::string &key) const override; }; } } #endif // BIOMETRY_UTIL_PROPERTY_STORE_H_ biometryd-0.3.1/src/biometry/util/statistics.cpp000066400000000000000000000024301455450034500220110ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include biometry::util::Statistics& biometry::util::Statistics::update(double observation) { accumulator(observation); return *this; } double biometry::util::Statistics::min() const { return boost::accumulators::min(accumulator); } double biometry::util::Statistics::mean() const { return boost::accumulators::mean(accumulator); } double biometry::util::Statistics::variance() const { return boost::accumulators::variance(accumulator); } double biometry::util::Statistics::max() const { return boost::accumulators::max(accumulator); } biometryd-0.3.1/src/biometry/util/statistics.h000066400000000000000000000042661455450034500214670ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRY_UTIL_STATISTICS_H_ #define BIOMETRY_UTIL_STATISTICS_H_ #include #include #include #include #include #include #include namespace biometry { namespace util { /// @brief Statistics helps in tracking min/max/mean/variance of a sample. class BIOMETRY_DLL_PUBLIC Statistics { public: /// @brief update adds the observation to the statistics. Statistics& update(double observation); /// @brief min returns the current min of the sample seen thus far. double min() const; /// @brief mean returns the current mean of the sample seen thus far. double mean() const; /// @brief variance returns the current variance of the sample seen thus far. double variance() const; /// @brief max returns the current max of the sample seen thus far. double max() const; private: /// @cond typedef boost::accumulators::accumulator_set < double, boost::accumulators::stats < boost::accumulators::tag::min, boost::accumulators::tag::max, boost::accumulators::tag::mean, boost::accumulators::tag::variance > > Accumulator; Accumulator accumulator; /// @endcond }; } } #endif // BIOMETRY_UTIL_STATISTICS_H_ biometryd-0.3.1/src/biometry/util/streaming_configuration_builder.h000066400000000000000000000072051455450034500257170ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRY_UTIL_STREAMING_CONFIGURATION_BUILDER_H_ #define BIOMETRY_UTIL_STREAMING_CONFIGURATION_BUILDER_H_ #include #include #include #include #include #include #include namespace biometry { namespace util { /// @brief JsonConfigurationBuilder implements ConfigurationBuilder for sources that take std::istream& on construction. template class BIOMETRY_DLL_PUBLIC StreamingConfigurationBuilder : public ConfigurationBuilder { public: /// @brief Streamer abstracts the lifetime of a reference to a std::istream instance. class Streamer : public biometry::DoNotCopyOrMove { public: /// @brief in returns a mutable reference to the managed std::istream. virtual std::istream& in() = 0; protected: /// @cond Streamer() = default; /// @endcond }; static std::shared_ptr make_streamer(std::istream& in) { return std::make_shared(in); } static std::shared_ptr make_streamer(const boost::filesystem::path& p) { return std::make_shared(p); } /// @brief JsonConfigurationBuilder initializes a new instance reading from the given input stream. StreamingConfigurationBuilder(const std::shared_ptr& streamer) : streamer{streamer}, builder{streamer->in()} { } /// @brief JsonConfigurationBuilder initializes a new instance reading from the given input stream. StreamingConfigurationBuilder(std::istream& in) : StreamingConfigurationBuilder{make_streamer(in)} { } /// @brief JsonConfigurationBuilder initializes a new instance reading from the given file. StreamingConfigurationBuilder(const boost::filesystem::path& path) : StreamingConfigurationBuilder{make_streamer(path)} { } /// build_configuration returns a Configuration assembled from a JSON-formatted file. Configuration build_configuration() override { return builder.build_configuration(); } private: /// @cond class RefStreamer : public Streamer { public: RefStreamer(std::istream& in) : in_{in} { if(not in_) throw std::runtime_error { "Given input stream is not ready for reading." }; } std::istream& in() override { return in_; } private: std::istream& in_; }; class IfStreamer : public Streamer { public: IfStreamer(const boost::filesystem::path& path) : in_{path.string()} { } std::istream& in() override { return in_; } private: std::ifstream in_; }; /// @endcond std::shared_ptr streamer; Builder builder; }; } } #endif // BIOMETRY_UTIL_STREAMING_CONFIGURATION_BUILDER_H_ biometryd-0.3.1/src/biometry/util/synchronized.h000066400000000000000000000027751455450034500220170ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef BIOMETRY_UTIL_SYNCHRONIZED_H_ #define BIOMETRY_UTIL_SYNCHRONIZED_H_ #include #include namespace biometry { namespace util { /// @brief Synchronized bundles together a value and a mutex guarding it. template class Synchronized { public: typedef T ValueType; /// @brief Synchronized creates a new instance, initializing the value to t. explicit Synchronized(const T& t = T{}) : value{t} { } /// @brief synchronized invokes the given functor f with the locked, mutable instance managed by this Synchronized instance. template void synchronized(const F& f) { std::lock_guard lg{guard}; f(value); } private: std::mutex guard; T value; }; } } #endif // BIOMETRY_UTIL_SYNCHRONIZED_H_ biometryd-0.3.1/src/biometry/util/variant.cpp000066400000000000000000000061461455450034500212730ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include namespace { struct Visitor : public boost::static_visitor { Visitor(std::ostream& out) : out{out} { } template std::ostream& operator()(const T& t) { return out << t; } std::ostream& operator()(const std::vector& v) { return out << "@" << &v[0]; } std::ostream& out; }; } util::Variant util::Variant::b(bool value) { return Variant{value}; } util::Variant util::Variant::i(std::int64_t value) { return Variant{value}; } util::Variant util::Variant::d(double value) { return Variant{value}; } util::Variant util::Variant::s(const std::string& value) { return Variant{value}; } util::Variant util::Variant::bl(const std::vector& value) { return Variant{value}; } util::Variant::Variant() : value_{None{}} { } util::Variant::Variant(bool b) : value_{b} { } util::Variant::Variant(std::int64_t i) : value_{i} { } util::Variant::Variant(double d) : value_{d} { } util::Variant::Variant(const std::string& s) : value_{s} { } util::Variant::Variant(const std::vector& b) : value_{b} { } std::ostream& util::Variant::print(std::ostream& out) const { Visitor v{out}; return value_.apply_visitor(v); } util::Variant::Type util::Variant::type() const { return static_cast(value_.which()); } const bool& util::Variant::boolean() const { return boost::get(value_); } void util::Variant::boolean(bool value) { value_ = value; } std::int64_t util::Variant::integer() const { return boost::get(value_); } void util::Variant::integer(std::int64_t value) { value_ = value; } double util::Variant::floating_point() const { return boost::get(value_); } void util::Variant::floating_point(double value) { value_ = value; } const std::string& util::Variant::string() const { return boost::get(value_); } void util::Variant::string(const std::string& value) { value_ = value; } const std::vector& util::Variant::blob() const { return boost::get>(value_); } void util::Variant::blob(const std::vector& value) { value_ = value; } std::ostream& util::operator<<(std::ostream& out, const Variant::None&) { return out << "none"; } std::ostream& util::operator<<(std::ostream& out, const Variant& v) { return v.print(out); } biometryd-0.3.1/src/biometry/util/variant.h000066400000000000000000000046531455450034500207410ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef UTIL_VARIANT_H_ #define UTIL_VARIANT_H_ #include #include #include #include #include #include namespace util { class BIOMETRY_DLL_PUBLIC Variant { public: /// @brief None models the unset value. struct None {}; typedef boost::variant < None, bool, std::int64_t, double, std::string, std::vector > Value; enum class Type { none, boolean, integer, floating_point, string, blob }; static Variant b(bool value); static Variant i(std::int64_t value); static Variant d(double value); static Variant s(const std::string& value); static Variant bl(const std::vector& value); Variant(); explicit Variant(bool b); explicit Variant(std::int64_t i); explicit Variant(double d); explicit Variant(const std::string& s); explicit Variant(const std::vector& b); std::ostream& print(std::ostream&) const; Type type() const; const bool& boolean() const; void boolean(bool value); std::int64_t integer() const; void integer(std::int64_t value); double floating_point() const; void floating_point(double value); const std::string& string() const; void string(const std::string& value); const std::vector& blob() const; void blob(const std::vector& value); private: Value value_; }; BIOMETRY_DLL_PUBLIC std::ostream& operator<<(std::ostream& out, const Variant::None&); BIOMETRY_DLL_PUBLIC std::ostream& operator<<(std::ostream& out, const Variant& v); } #endif // UTIL_VARIANT_H_ biometryd-0.3.1/src/biometry/variant.cpp000066400000000000000000000120561455450034500203130ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include namespace { typedef typename boost::make_recursive_variant < biometry::Variant::None, bool, std::int64_t, double, biometry::Rectangle, std::string, std::vector, std::vector >::type Value; struct Visitor : public boost::static_visitor { Visitor(std::ostream& out) : out{out} { } template std::ostream& operator()(const T& t) { return out << t; } std::ostream& operator()(const std::vector& v) { return out << "@" << &v[0]; } std::ostream& operator()(const std::vector& v) { return out << "@" << &v[0]; } std::ostream& out; }; } struct biometry::Variant::Private { Value value; }; biometry::Variant biometry::Variant::b(bool value) { return Variant(value); } biometry::Variant biometry::Variant::i(std::int64_t value) { return Variant{value}; } biometry::Variant biometry::Variant::d(double value) { return Variant{value}; } biometry::Variant biometry::Variant::r(const biometry::Rectangle& value) { return biometry::Variant{value}; } biometry::Variant biometry::Variant::s(const std::string& value) { return Variant{value}; } biometry::Variant biometry::Variant::bl(const std::vector& value) { return Variant{value}; } biometry::Variant biometry::Variant::v(const std::vector& value) { return Variant{value}; } biometry::Variant::Variant(const Variant& rhs) : p{new Private{rhs.p->value}} { } biometry::Variant::Variant() : p{new Private{None{}}} { } biometry::Variant::Variant(bool b) : p{new Private{b}} { } biometry::Variant::Variant(std::int64_t i) : p{new Private{i}} { } biometry::Variant::Variant(double fp) : p{new Private{fp}} { } biometry::Variant::Variant(const biometry::Rectangle& r) : p{new Private{r}} { } biometry::Variant::Variant(const std::string& s) : p{new Private{s}} { } biometry::Variant::Variant(const std::vector& b) : p{new Private{b}} { } biometry::Variant::Variant(const std::vector& v) : p{new Private{v}} { } biometry::Variant::~Variant() { } biometry::Variant& biometry::Variant::operator=(const biometry::Variant& rhs) { *p = *rhs.p; return *this; } bool biometry::Variant::operator==(const biometry::Variant& rhs) const { return p->value == rhs.p->value; } std::ostream& biometry::Variant::print(std::ostream& out) const { Visitor v{out}; return p->value.apply_visitor(v); } biometry::Variant::Type biometry::Variant::type() const { return static_cast(p->value.which()); } const bool& biometry::Variant::boolean() const { return boost::get(p->value); } void biometry::Variant::boolean(bool value) { p->value = value; } std::int64_t biometry::Variant::integer() const { return boost::get(p->value); } void biometry::Variant::integer(std::int64_t value) { p->value = value; } double biometry::Variant::floating_point() const { return boost::get(p->value); } void biometry::Variant::floating_point(double value) { p->value = value; } const biometry::Rectangle& biometry::Variant::rectangle() const { return boost::get(p->value); } void biometry::Variant::rectangle(const biometry::Rectangle& value) { p->value = value; } const std::string& biometry::Variant::string() const { return boost::get(p->value); } void biometry::Variant::string(const std::string& value) { p->value = value; } const std::vector& biometry::Variant::blob() const { return boost::get>(p->value); } void biometry::Variant::blob(const std::vector& value) { p->value = value; } const std::vector& biometry::Variant::vector() const { return boost::get>(p->value); } void biometry::Variant::vector(const std::vector& value) { p->value = value; } bool biometry::operator==(const biometry::Variant::None&, const biometry::Variant::None&) { return true; } std::ostream& biometry::operator<<(std::ostream& out, const Variant::None&) { return out << "none"; } std::ostream& biometry::operator<<(std::ostream& out, const Variant& v) { return v.print(out); } biometryd-0.3.1/src/biometry/version.cpp000066400000000000000000000017121455450034500203310ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include void biometry::version(std::uint32_t& major, std::uint32_t& minor, std::uint32_t& patch) { major = biometry::build::version_major; minor = biometry::build::version_minor; patch = biometry::build::version_patch; } biometryd-0.3.1/symbols.map000066400000000000000000000004031455450034500157020ustar00rootroot00000000000000{ global: extern "C++" { operator*; biometry::*; typeinfo?for?biometry::*; typeinfo?name?for?biometry::*; VTT?for?biometry::*; virtual?thunk?to?biometry::*; vtable?for?biometry::*; }; local: extern "C++" { *; }; };biometryd-0.3.1/tests/000077500000000000000000000000001455450034500146605ustar00rootroot00000000000000biometryd-0.3.1/tests/CMakeLists.txt000066400000000000000000000066511455450034500174300ustar00rootroot00000000000000find_package(Qt5Core 5.4 REQUIRED) find_package(Qt5Qml 5.4 REQUIRED) find_package(Qt5Quick 5.4 REQUIRED) find_package(Qt5QuickTest 5.4 REQUIRED) configure_file(config.cpp.in config.cpp) set(GMOCK_BUILD_SHARED_LIBS ON) find_package(GMock) include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${GMOCK_INCLUDE_DIRS} ) # Ensure that we have a ready-made fixture maintaining a private bus instance # readily available in unit- and integration tests. add_definitions(-DCORE_DBUS_ENABLE_GOOGLE_TEST_FIXTURE) macro(BIOMETRYD_ADD_TEST test_name src) add_executable( ${test_name} ${src} ${CMAKE_CURRENT_SOURCE_DIR}/did_finish_successfully.h ${CMAKE_CURRENT_SOURCE_DIR}/echo_service.h ${CMAKE_CURRENT_SOURCE_DIR}/echo_service.cpp ${CMAKE_CURRENT_SOURCE_DIR}/config.h ${CMAKE_CURRENT_BINARY_DIR}/config.cpp) target_link_libraries( ${test_name} biometry ${Boost_LIBRARIES} ${DBUS_CPP_LIBRARIES} ${PROCESS_CPP_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${ARGN} ${GMOCK_LIBRARIES}) add_test(${test_name} ${CMAKE_CURRENT_BINARY_DIR}/${test_name} --gtest_filter=*-*requires*) set_tests_properties(${test_name} PROPERTIES ENVIRONMENT "BIOMETRYD_DBUS_SKELETON_IS_RUNNING_UNDER_TESTING=1") endmacro(BIOMETRYD_ADD_TEST) add_library(biometryd_devices_plugin_dl SHARED biometryd_devices_plugin_dl.cpp) target_link_libraries(biometryd_devices_plugin_dl gtest gmock) add_library(biometryd_devices_plugin_dl_version_mismatch SHARED biometryd_devices_plugin_dl_version_mismatch.cpp) target_link_libraries(biometryd_devices_plugin_dl_version_mismatch gtest gmock) BIOMETRYD_ADD_TEST(test_atomic_counter test_atomic_counter.cpp) BIOMETRYD_ADD_TEST(test_configuration test_configuration.cpp) BIOMETRYD_ADD_TEST(test_daemon test_daemon.cpp) BIOMETRYD_ADD_TEST(test_device_registrar test_device_registrar.cpp) BIOMETRYD_ADD_TEST(test_dispatching_device_and_service test_dispatching_service_and_device.cpp) BIOMETRYD_ADD_TEST(test_dbus_codec test_dbus_codec.cpp) BIOMETRYD_ADD_TEST(test_dbus_stub_skeleton test_dbus_stub_skeleton.cpp) BIOMETRYD_ADD_TEST(test_dictionary test_dictionary.cpp) BIOMETRYD_ADD_TEST(test_fingerprint_reader test_fingerprint_reader.cpp) BIOMETRYD_ADD_TEST(test_forwarding test_forwarding.cpp) BIOMETRYD_ADD_TEST(test_geometry test_geometry.cpp) BIOMETRYD_ADD_TEST(test_operation test_operation.cpp) BIOMETRYD_ADD_TEST(test_percent test_percent.cpp) BIOMETRYD_ADD_TEST(test_plugin_device test_plugin_device.cpp) BIOMETRYD_ADD_TEST(test_progress test_progress.cpp) BIOMETRYD_ADD_TEST(test_user test_user.cpp) # TODO implement verifier test, its currently empty #BIOMETRYD_ADD_TEST(test_verifier test_verifier.cpp) BIOMETRYD_ADD_TEST(test_cmds_config cmds/test_config.cpp) BIOMETRYD_ADD_TEST(test_cmds_run cmds/test_run.cpp) BIOMETRYD_ADD_TEST(test_cmds_test cmds/test_test.cpp) if ("DEB_TARGET_ARCH" STREQUAL "powerpc") else() add_executable(test_qml_plugin test_qml_plugin.cpp) target_link_libraries(test_qml_plugin Qt5::Core Qt5::Qml Qt5::Quick Qt5::QuickTest) add_test(test_qml_plugin test_qml_plugin) set_tests_properties(test_qml_plugin PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=minimal;QML2_IMPORT_PATH=${CMAKE_BINARY_DIR}/src/biometry/qml;BIOMETRYD_QML_ENABLE_TESTING=1") file(GLOB BIOMETRYD_QML_TEST_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} tst_*.qml) foreach (QML_FILE ${BIOMETRYD_QML_TEST_FILES}) message(INFO ${QML_FILE}) configure_file(${QML_FILE} ${QML_FILE} COPYONLY) endforeach(QML_FILE) endif() biometryd-0.3.1/tests/biometryd_devices_plugin_dl.cpp000066400000000000000000000023431455450034500231230ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include "mock_device.h" /// [Defining the create function] BIOMETRYD_DEVICES_PLUGIN_CREATE { return new testing::MockDevice(); } /// [Defining the create function] /// [Defining the destroy function] BIOMETRYD_DEVICES_PLUGIN_DESTROY { delete d; } /// [Defining the destroy function] /// [Describing the plugin] BIOMETRYD_DEVICES_PLUGIN_DESCRIBE("TestPlugin", "Thomas Voß ", "Just a plugin for testing purposes", 0, 0, 0) /// [Describing the plugin] biometryd-0.3.1/tests/biometryd_devices_plugin_dl_version_mismatch.cpp000066400000000000000000000026471455450034500265640ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include "mock_device.h" /// [Defining the create function] BIOMETRYD_DEVICES_PLUGIN_CREATE { return new testing::MockDevice(); } /// [Defining the create function] /// [Defining the destroy function] BIOMETRYD_DEVICES_PLUGIN_DESTROY { delete d; } /// [Defining the destroy function] /// Manually describe the plugin, incresing the major version by 1 to indicate an ABI break. biometry::devices::plugin::Descriptor biometryd_devices_plugin_descriptor __attribute((section(BIOMETRYD_DEVICES_PLUGIN_DESCRIPTOR_SECTION))) = { "name", "author", "description", {{biometry::build::version_major+1, biometry::build::version_minor, biometry::build::version_patch}, {0, 0, 0}}}; biometryd-0.3.1/tests/cmds/000077500000000000000000000000001455450034500156065ustar00rootroot00000000000000biometryd-0.3.1/tests/cmds/test_config.cpp000066400000000000000000000033751455450034500206260ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include #include namespace cli = biometry::util::cli; TEST(CmdConfig, returns_correct_result_for_default_plugin_directory) { std::stringstream ss; ss << biometry::cmds::Config::Flag::default_plugin_directory; std::stringstream cout; biometry::cmds::Config config; EXPECT_EQ(EXIT_SUCCESS, config.run(cli::Command::Context{std::cin, cout, {"--flag=" + ss.str()}})); EXPECT_EQ(biometry::Daemon::Configuration::default_plugin_directory().string(), boost::algorithm::trim_copy(cout.str())); } TEST(CmdConfig, throws_flags_missing_for_missing_flag) { biometry::cmds::Config config; EXPECT_THROW(config.run(cli::Command::Context{std::cin, std::cout, {}}), cli::Command::FlagsMissing); } TEST(CmdConfig, throws_flags_with_invalid_value_for_invalid_flag) { biometry::cmds::Config config; EXPECT_THROW(config.run(cli::Command::Context{std::cin, std::cout, {"--flag=invalid"}}), cli::Command::FlagsWithInvalidValue); } biometryd-0.3.1/tests/cmds/test_run.cpp000066400000000000000000000035411455450034500201600ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include #include namespace { struct MockPropertyStore : public biometry::util::PropertyStore { MOCK_CONST_METHOD1(get, std::string(const std::string&)); }; } TEST(CmdRun, configuration_oracle_queries_device_property) { using namespace ::testing; MockPropertyStore mock; EXPECT_CALL(mock, get("ro.product.device")).Times(1).WillOnce(Return("turbo")); biometry::cmds::Run::ConfigurationOracle oracle; EXPECT_EQ("meizu::FingerprintReader", oracle.make_an_educated_guess(mock)); } TEST(CmdRun, queries_configuration_oracle_if_no_config_given) { using namespace ::testing; auto mock = std::make_shared(); EXPECT_CALL(*mock, get("ro.product.device")).Times(1).WillOnce(Return("turbo")); biometry::cmds::Run run{mock, biometry::cmds::Run::system_bus_factory()}; EXPECT_EQ(EX_CONFIG, run.run(biometry::util::cli::Command::Context{std::cin, std::cout, {}})); } TEST(CmdRun, android_property_store_throws_if_getprop_missing) { biometry::util::AndroidPropertyStore store; EXPECT_ANY_THROW(store.get("ro.build.product")); } biometryd-0.3.1/tests/cmds/test_test.cpp000066400000000000000000000053461455450034500203400ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include #include #include #include "did_finish_successfully.h" #include namespace cli = biometry::util::cli; namespace { struct NullEnumerator : public biometry::devices::plugin::Enumerator { std::size_t enumerate(const Functor& ) const override { return 0; } }; struct CmdTest : public ::testing::Test { biometry::DeviceRegistrar device_registrar{NullEnumerator{}}; }; } TEST_F(CmdTest, throws_configuration_invalid_if_device_is_not_given) { {std::remove("invalid.json"); std::ofstream out{"invalid.json"}; out << R"_({ "device": {}})_" << std::endl;} biometry::cmds::Test test; EXPECT_THROW(test.run(cli::Command::Context{std::cin, std::cout, {"--config=invalid.json"}}), biometry::cmds::Test::ConfigurationInvalid); } TEST_F(CmdTest, throws_could_not_instantiate_device_for_unknown_id) { {std::remove("invalid.json"); std::ofstream out{"invalid.json"}; out << R"_({ "device": {"id": "unknown"}})_" << std::endl;} biometry::cmds::Test test; EXPECT_THROW(test.run(cli::Command::Context{std::cin, std::cout, {"--config=invalid.json"}}), biometry::cmds::Test::CouldNotInstiantiateDevice); } TEST_F(CmdTest, runs_successfully_for_dummy_device) { auto d = []() { {std::remove("dummy.json"); std::ofstream out{"dummy.json"}; out << R"_({"device": {"id": "Dummy"}})_" << std::endl;} biometry::cmds::Test test; EXPECT_EQ(EXIT_SUCCESS, test.run(cli::Command::Context{std::cin, std::cout, {"--config=dummy.json"}})); return testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success; }; auto cp = core::posix::fork(d, core::posix::StandardStream::stdin); cp.cin() << 'y' << std::endl; EXPECT_TRUE(testing::did_finish_successfully( cp.wait_for( core::posix::wait::Flags::untraced))); } biometryd-0.3.1/tests/config.cpp.in000066400000000000000000000014731455450034500172430ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include boost::filesystem::path testing::runtime_dir() { return "@CMAKE_CURRENT_BINARY_DIR@"; } biometryd-0.3.1/tests/config.h000066400000000000000000000015721455450034500163030ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef TESTING_CONFIG_H_ #define TESTING_CONFIG_H_ #include namespace testing { boost::filesystem::path runtime_dir(); } #endif // TESTING_CONFIG_H_ biometryd-0.3.1/tests/did_finish_successfully.h000066400000000000000000000077241455450034500217470ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef TESTING_DID_FINISH_SUCCESSFULLY_H_ #define TESTING_DID_FINISH_SUCCESSFULLY_H_ #include #include namespace core { namespace posix { void PrintTo(Signal signal, std::ostream* os) { switch (signal) { case Signal::unknown: *os << "unknown"; break; case Signal::sig_hup: *os << "sig_hup"; break; case Signal::sig_int: *os << "sig_int"; break; case Signal::sig_quit: *os << "sig_quit"; break; case Signal::sig_ill: *os << "sig_ill"; break; case Signal::sig_abrt: *os << "sig_abrt"; break; case Signal::sig_fpe: *os << "sig_fpe"; break; case Signal::sig_kill: *os << "sig_kill"; break; case Signal::sig_segv: *os << "sig_segv"; break; case Signal::sig_pipe: *os << "sig_pipe"; break; case Signal::sig_alrm: *os << "sig_alrm"; break; case Signal::sig_term: *os << "sig_term"; break; case Signal::sig_usr1: *os << "sig_usr1"; break; case Signal::sig_usr2: *os << "sig_usr2"; break; case Signal::sig_chld: *os << "sig_chld"; break; case Signal::sig_cont: *os << "sig_cont"; break; case Signal::sig_stop: *os << "sig_stop"; break; case Signal::sig_tstp: *os << "sig_tstp"; break; case Signal::sig_ttin: *os << "sig_ttin"; break; case Signal::sig_ttou: *os << "sig_ttou"; break; } } namespace wait { void PrintTo(Result::Status status, std::ostream* os) { switch (status) { case core::posix::wait::Result::Status::undefined: *os << "undefined"; break; case core::posix::wait::Result::Status::no_state_change: *os << "no_statue_change"; break; case core::posix::wait::Result::Status::exited: *os << "exited"; break; case core::posix::wait::Result::Status::signaled: *os << "signaled"; break; case core::posix::wait::Result::Status::stopped: *os << "stopped"; break; case core::posix::wait::Result::Status::continued: *os << "continued"; break; } } void PrintTo(const Result& result, std::ostream* os) { *os << testing::PrintToString(result.status); if (result.status == Result::Status::signaled) *os << " with " << testing::PrintToString(result.detail.if_signaled.signal); } } } } namespace testing { ::testing::AssertionResult did_exit(const core::posix::wait::Result& result) { if (result.status != core::posix::wait::Result::Status::exited) ::testing::AssertionFailure() << "Process did not exit: " << testing::PrintToString(result); return ::testing::AssertionSuccess(); } ::testing::AssertionResult did_finish_successfully(const core::posix::wait::Result& result) { if (result.status != core::posix::wait::Result::Status::exited) { return ::testing::AssertionFailure() << "Process did not exit, but: " << testing::PrintToString(result); } if (result.detail.if_exited.status != core::posix::exit::Status::success) return ::testing::AssertionFailure() << "Process did exit with failure."; return ::testing::AssertionSuccess(); } } #endif // TESTING_DID_FINISH_SUCCESSFULLY_H_ biometryd-0.3.1/tests/echo_service.cpp000066400000000000000000000000001455450034500200100ustar00rootroot00000000000000biometryd-0.3.1/tests/echo_service.h000066400000000000000000000231151455450034500174710ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef TESTING_ECHO_SERVICE_H_ #define TESTING_ECHO_SERVICE_H_ #include #include #include #include #include #include namespace testing { struct EchoService { static const std::string& name() { static const std::string s{"just.an.echo.Service"}; return s; } static core::dbus::types::ObjectPath path() { return core::dbus::types::ObjectPath{"/"}; } struct Methods { DBUS_CPP_METHOD_DEF(Application, EchoService) DBUS_CPP_METHOD_DEF(Progress, EchoService) DBUS_CPP_METHOD_DEF(Point, EchoService) DBUS_CPP_METHOD_DEF(Rectangle, EchoService) DBUS_CPP_METHOD_DEF(Reason, EchoService) DBUS_CPP_METHOD_DEF(User, EchoService) DBUS_CPP_METHOD_DEF(None, EchoService) DBUS_CPP_METHOD_DEF(Variant, EchoService) DBUS_CPP_METHOD_DEF(Void, EchoService) }; virtual ~EchoService() = default; virtual void application(const biometry::Application&) = 0; virtual void progress(const biometry::Progress&) = 0; virtual void point(const biometry::Point&) = 0; virtual void rectangle(const biometry::Rectangle&) = 0; virtual void reason(const biometry::Reason&) = 0; virtual void user(const biometry::User&) = 0; virtual void none(const biometry::Variant::None&) = 0; virtual void variant(const biometry::Variant&) = 0; virtual void void_(const biometry::Void&) = 0; }; } namespace core { namespace dbus { namespace traits { template<> struct Service< ::testing::EchoService > { static inline const std::string& interface_name() { return ::testing::EchoService::name(); } }; }}} namespace testing { class SkeletonEchoService : public testing::EchoService { public: SkeletonEchoService(const core::dbus::Bus::Ptr& bus, const core::dbus::Object::Ptr& object, const std::shared_ptr& impl) : bus{bus}, object{object}, impl{impl} { object->install_method_handler([this](const core::dbus::Message::Ptr& msg) { auto value = biometry::Application::system(); msg->reader() >> value; application(value); auto reply = core::dbus::Message::make_method_return(msg); reply->writer() << value; this->bus->send(reply); }); object->install_method_handler([this](const core::dbus::Message::Ptr& msg) { auto value = biometry::Progress::none(); msg->reader() >> value; progress(value); auto reply = core::dbus::Message::make_method_return(msg); reply->writer() << value; this->bus->send(reply); }); object->install_method_handler([this](const core::dbus::Message::Ptr& msg) { biometry::Point value; msg->reader() >> value; point(value); auto reply = core::dbus::Message::make_method_return(msg); reply->writer() << value; this->bus->send(reply); }); object->install_method_handler([this](const core::dbus::Message::Ptr& msg) { biometry::Rectangle value; msg->reader() >> value; rectangle(value); auto reply = core::dbus::Message::make_method_return(msg); reply->writer() << value; this->bus->send(reply); }); object->install_method_handler([this](const core::dbus::Message::Ptr& msg) { auto value = biometry::Reason::unknown(); msg->reader() >> value; reason(value); auto reply = core::dbus::Message::make_method_return(msg); reply->writer() << value; this->bus->send(reply); }); object->install_method_handler([this](const core::dbus::Message::Ptr& msg) { auto value = biometry::User::current(); msg->reader() >> value; user(value); auto reply = core::dbus::Message::make_method_return(msg); reply->writer() << value; this->bus->send(reply); }); object->install_method_handler([this](const core::dbus::Message::Ptr& msg) { biometry::Variant::None value; msg->reader() >> value; none(value); auto reply = core::dbus::Message::make_method_return(msg); reply->writer() << value; this->bus->send(reply); }); object->install_method_handler([this](const core::dbus::Message::Ptr& msg) { biometry::Variant value; msg->reader() >> value; variant(value); auto reply = core::dbus::Message::make_method_return(msg); reply->writer() << value; this->bus->send(reply); }); object->install_method_handler([this](const core::dbus::Message::Ptr& msg) { biometry::Void value; msg->reader() >> value; void_(value); auto reply = core::dbus::Message::make_method_return(msg); reply->writer() << value; this->bus->send(reply); }); } void application(const biometry::Application& value) { impl->application(value); } void progress(const biometry::Progress& value) { impl->progress(value); } void point(const biometry::Point& value) { impl->point(value); } void rectangle(const biometry::Rectangle& value) { impl->rectangle(value); } void reason(const biometry::Reason& value) { impl->reason(value); } void user(const biometry::User& value) { impl->user(value); } void none(const biometry::Variant::None& value) { impl->none(value); } void variant(const biometry::Variant& value) { impl->variant(value); } void void_(const biometry::Void& value) { impl->void_(value); } private: core::dbus::Bus::Ptr bus; core::dbus::Object::Ptr object; std::shared_ptr impl; }; class StubEchoService : public testing::EchoService { public: StubEchoService(const core::dbus::Object::Ptr& object) : object{object} { } void application(const biometry::Application& value) { auto result = object->transact_method(value); if (not(value == result.value())) throw std::logic_error{"mismatch"}; } void progress(const biometry::Progress& value) { auto result = object->transact_method(value); if (not(value == result.value())) throw std::logic_error{"mismatch"}; } void point(const biometry::Point& value) { auto result = object->transact_method(value); if (not(value == result.value())) throw std::logic_error{"mismatch"}; } void rectangle(const biometry::Rectangle& value) { auto result = object->transact_method(value); if (value != result.value()) throw std::logic_error{"mismatch"}; } void reason(const biometry::Reason& value) { auto result = object->transact_method(value); if (!(value == result.value())) throw std::logic_error{"mismatch"}; } void user(const biometry::User& value) { auto result = object->transact_method(value); if (!(value == result.value())) throw std::logic_error{"mismatch"}; } void none(const biometry::Variant::None& value) { auto result = object->transact_method(value); if (!(value == result.value())) throw std::logic_error{"mismatch"}; } void variant(const biometry::Variant& value) { auto msg = core::dbus::Message::make_method_call(EchoService::name(), EchoService::path(), EchoService::name(), "Variant"); auto result = object->transact_method(value); if (!(value == result.value())) throw std::logic_error{"mismatch"}; } void void_(const biometry::Void& value) { auto result = object->transact_method(value); if (!(value == result.value())) throw std::logic_error{"mismatch"}; } private: core::dbus::Object::Ptr object; }; } #endif // TESTING_ECHO_SERVICE_H_ biometryd-0.3.1/tests/mock_device.h000066400000000000000000000066631455450034500173140ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #ifndef TESTING_MOCK_DEVICE_H_ #define TESTING_MOCK_DEVICE_H_ #include #include #include #include #include #include #include #include namespace testing { template struct MockOperation : public biometry::Operation { // Safe us some typing typedef biometry::Operation Super; using typename Super::Observer; using typename Super::Progress; using typename Super::Reason; using typename Super::Error; using typename Super::Result; // From biometry::Operation MOCK_METHOD1_T(start_with_observer, void(const typename Observer::Ptr&)); MOCK_METHOD0(cancel, void()); }; template struct MockObserver : public biometry::Operation::Observer { // Safe us some typing typedef typename biometry::Operation::Observer Super; using typename Super::Progress; using typename Super::Reason; using typename Super::Error; using typename Super::Result; // From Operation::Observer MOCK_METHOD0(on_started, void()); MOCK_METHOD1_T(on_progress, void(const Progress&)); MOCK_METHOD1_T(on_canceled, void(const Reason&)); MOCK_METHOD1_T(on_failed, void(const Error&)); MOCK_METHOD1_T(on_succeeded, void(const Result&)); }; struct MockTemplateStore : public biometry::TemplateStore { MOCK_METHOD2(size, biometry::Operation::Ptr(const biometry::Application&, const biometry::User&)); MOCK_METHOD2(list, biometry::Operation::Ptr(const biometry::Application&, const biometry::User&)); MOCK_METHOD2(enroll, biometry::Operation::Ptr (const biometry::Application&, const biometry::User&)); MOCK_METHOD3(remove, biometry::Operation::Ptr(const biometry::Application&, const biometry::User&, biometry::TemplateStore::TemplateId)); MOCK_METHOD2(clear, biometry::Operation::Ptr(const biometry::Application&, const biometry::User&)); }; struct MockIdentifier : public biometry::Identifier { MOCK_METHOD2(identify_user, biometry::Operation::Ptr(const biometry::Application&, const biometry::Reason&)); }; struct MockVerifier : public biometry::Verifier { MOCK_METHOD3(verify_user, biometry::Operation::Ptr(const biometry::Application&, const biometry::User&, const biometry::Reason&)); }; struct MockDevice : public biometry::Device { // From biometry::Device MOCK_METHOD0(template_store, biometry::TemplateStore&()); MOCK_METHOD0(identifier, biometry::Identifier&()); MOCK_METHOD0(verifier, biometry::Verifier&()); }; } #endif // TESTING_MOCK_DEVICE_H_ biometryd-0.3.1/tests/test_atomic_counter.cpp000066400000000000000000000020511455450034500214340ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include TEST(AtomicCounter, initializes_to_zero_by_default) { biometry::util::AtomicCounter counter; EXPECT_EQ(0, counter.increment()); } TEST(AtomicCounter, returns_previous_value_on_increment) { biometry::util::AtomicCounter counter; EXPECT_EQ(0, counter.increment()); } biometryd-0.3.1/tests/test_configuration.cpp000066400000000000000000000110321455450034500212670ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include #include #include TEST(Configuration, default_constructor_yields_valid_configuration) { biometry::util::Configuration config; } TEST(ConfigurationNode, setting_value_of_node_works) { biometry::util::Configuration config; config.children()["test"] = biometry::util::Configuration::Node{biometry::Variant::i(42)}; ASSERT_EQ(1, config.children().count("test")); ASSERT_EQ(42, config.children().at("test").value().integer()); } TEST(Variant, constructors_yield_correct_type_and_value) { {const bool rv = true; biometry::Variant v{rv}; EXPECT_EQ(biometry::Variant::Type::boolean, v.type()); EXPECT_EQ(rv, v.boolean());} {const std::int64_t rv = 42; biometry::Variant v{rv}; EXPECT_EQ(biometry::Variant::Type::integer, v.type()); EXPECT_EQ(rv, v.integer());} {const double rv = 42.f; biometry::Variant v{rv}; EXPECT_EQ(biometry::Variant::Type::floating_point, v.type()); EXPECT_EQ(rv, v.floating_point());} {const std::string rv = "42"; biometry::Variant v{rv}; EXPECT_EQ(biometry::Variant::Type::string, v.type()); EXPECT_EQ(rv, v.string());} {const std::vector rv = {4, 2}; biometry::Variant v{rv}; EXPECT_EQ(biometry::Variant::Type::blob, v.type()); EXPECT_EQ(rv, v.blob());} } TEST(Variant, named_constructors_yield_correct_type_and_value) { {const bool rv = true; auto v = biometry::Variant::b(rv); EXPECT_EQ(biometry::Variant::Type::boolean, v.type()); EXPECT_EQ(rv, v.boolean());} {const std::int64_t rv = 42; auto v = biometry::Variant::i(rv); EXPECT_EQ(biometry::Variant::Type::integer, v.type()); EXPECT_EQ(rv, v.integer());} {const double rv = 42.f; auto v = biometry::Variant::d(rv); EXPECT_EQ(biometry::Variant::Type::floating_point, v.type()); EXPECT_EQ(rv, v.floating_point());} {const std::string rv = "42"; auto v = biometry::Variant::s(rv); EXPECT_EQ(biometry::Variant::Type::string, v.type()); EXPECT_EQ(rv, v.string());} {const std::vector rv = {4, 2}; auto v = biometry::Variant::bl(rv); EXPECT_EQ(biometry::Variant::Type::blob, v.type()); EXPECT_EQ(rv, v.blob());} } TEST(Variant, stream_insertion_operator_works) { biometry::Variant v; std::cout << v << std::endl; } TEST(JsonConfigurationBuilder, works_for_valid_json) { auto json = R"_({ "devices" : [ { "device.id" : "meizu::FingerprintReader", "device.config": { "templateId.store": "SqliteTemplateIdStore", "templateId.store.config": { "db.path": "/var/lib/biometryd/ids.db" } } }, { "device.id" : "biometryd::Plugin", "device.config" : { "plugin.path": "/usr/lib/x86_64/biometryd/plugins/", "plugin.config": { } } } ] })_"; { std::remove("test.json"); std::ofstream out{"test.json"}; out << json; } std::ifstream in{"test.json"}; biometry::util::StreamingConfigurationBuilder builder{in}; auto config = builder.build_configuration(); EXPECT_EQ(1, config.children().count("devices")); EXPECT_EQ(2, config["devices"].children().size()); auto devices = config["devices"]; auto _0 = devices["0"]; auto _1 = devices["1"]; EXPECT_EQ("meizu::FingerprintReader", _0["device.id"].value().string()); EXPECT_EQ("biometryd::Plugin", _1["device.id"].value().string()); } biometryd-0.3.1/tests/test_daemon.cpp000066400000000000000000000137531455450034500176770ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include #include #include #include #include namespace cli = biometry::util::cli; namespace { struct Daemon : core::dbus::testing::Fixture { }; } TEST(SizeConstrainedString, throws_on_construction_for_string_too_long) { EXPECT_ANY_THROW(cli::SizeConstrainedString<0>{"test"}); } TEST(SizeConstrainedString, construction_succeeds_for_string_of_correct_length) { EXPECT_NO_THROW(cli::SizeConstrainedString<10>{"test"}); } TEST(SizeConstrainedString, operator_lt_works) { cli::SizeConstrainedString<10> a{"a"}; cli::SizeConstrainedString<10> b{"b"}; EXPECT_TRUE(a < b); EXPECT_FALSE(b < a); } TEST(SizeConstrainedString, stores_string_content_passed_on_construction) { cli::SizeConstrainedString<20> a{"a"}; EXPECT_EQ("a", a.as_string()); } TEST(TypedFlag, stores_name_and_desc_passed_on_construction) { cli::Name name{"42"}; cli::Description desc{"43"}; cli::TypedFlag flag{name, desc}; EXPECT_EQ(name, flag.name()); EXPECT_EQ(desc, flag.description()); } TEST(TypedFlag, parses_string_on_notify_and_sets_value) { cli::Name name{"42"}; cli::Description desc{"43"}; cli::TypedFlag flag{name, desc}; EXPECT_FALSE(flag.value().is_initialized()); flag.notify("42"); EXPECT_TRUE(flag.value().is_initialized()); EXPECT_EQ(42, *flag.value()); } TEST_F(Daemon, invoking_help_command_succeeds) { auto d = []() { biometry::Daemon daemon; EXPECT_EQ(EXIT_FAILURE, daemon.run({"help"})); return testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success; }; EXPECT_TRUE(testing::did_exit( core::posix::fork(d, core::posix::StandardStream::empty) .wait_for(core::posix::wait::Flags::untraced))); } TEST_F(Daemon, invoking_version_command_succeeds) { auto d = []() { biometry::Daemon daemon; EXPECT_EQ(EXIT_SUCCESS, daemon.run({"version"})); return testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success; }; EXPECT_TRUE(testing::did_finish_successfully( core::posix::fork(d, core::posix::StandardStream::empty) .wait_for(core::posix::wait::Flags::untraced))); } TEST_F(Daemon, invoking_list_devices_command_succeeds) { auto d = []() { biometry::Daemon daemon; EXPECT_EQ(EXIT_SUCCESS, daemon.run({"list-devices"})); return testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success; }; EXPECT_TRUE(testing::did_finish_successfully( core::posix::fork(d, core::posix::StandardStream::empty) .wait_for(core::posix::wait::Flags::untraced))); } TEST_F(Daemon, invoking_config_command_succeeds) { auto d = []() { biometry::Daemon daemon; EXPECT_EQ(EXIT_SUCCESS, daemon.run({"config", "--flag=default_plugin_directory"})); return testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success; }; auto cp = core::posix::fork(d, core::posix::StandardStream::stdout); std::string result; cp.cout() >> result; EXPECT_EQ(biometry::Daemon::Configuration::default_plugin_directory().string(), result); EXPECT_TRUE(testing::did_finish_successfully( cp.wait_for( core::posix::wait::Flags::untraced))); } TEST_F(Daemon, invoking_test_command_succeeds) { auto d = []() { {std::remove("dummy.json"); std::ofstream out{"dummy.json"}; out << R"_({"device": {"id": "Dummy"}})_" << std::endl;} biometry::Daemon daemon; EXPECT_EQ(EXIT_SUCCESS, daemon.run({"test", "--config=dummy.json"})); return testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success; }; auto cp = core::posix::fork(d, core::posix::StandardStream::stdin); cp.cin() << 'y' << std::endl; EXPECT_TRUE(testing::did_finish_successfully( cp.wait_for( core::posix::wait::Flags::untraced))); } TEST_F(Daemon, invoking_run_succeeds) { auto json = R"_( { "defaultDevice": { "id": "Dummy", "config":{} } })_"; { std::remove("test.json"); std::ofstream out{"test.json"}; out << json; } auto d = []() { biometry::Daemon daemon; auto rc = daemon.run( { "run", "--config=test.json" }); return rc == EXIT_SUCCESS ? core::posix::exit::Status::success : core::posix::exit::Status::failure; }; auto dp = core::posix::fork(d, core::posix::StandardStream::empty); std::this_thread::sleep_for(std::chrono::milliseconds{500}); dp.send_signal_or_throw(core::posix::Signal::sig_term); EXPECT_TRUE(testing::did_finish_successfully(dp.wait_for(core::posix::wait::Flags::untraced))); } biometryd-0.3.1/tests/test_dbus_codec.cpp000066400000000000000000000150641455450034500205230ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include #include #include #include #include #include #include #include #include "did_finish_successfully.h" #include "echo_service.h" namespace { struct VerifyingEchoService : public testing::EchoService { struct Reference { static biometry::Application application() { return biometry::Application{"JustSomeDummyApplication"}; } static biometry::Progress progress() { biometry::Progress result; result.percent = biometry::Percent::from_raw_value(0.42); return result; } static biometry::Point point() { return biometry::Point{0.42, 0.42}; } static biometry::Rectangle rectangle() { return {point(), point()}; } static biometry::Reason reason() { return biometry::Reason{"because we are testing"}; } static biometry::User user() { return biometry::User::current(); } static biometry::Variant::None none() { return biometry::Variant::None{}; } static biometry::Variant variant() { return biometry::Variant::v( { biometry::Variant::b(true), biometry::Variant::i(42), biometry::Variant::d(0.42), biometry::Variant::r(Reference::rectangle()), biometry::Variant::s("42"), biometry::Variant::v( { biometry::Variant::b(true), biometry::Variant::i(42), biometry::Variant::d(0.42), biometry::Variant::r(Reference::rectangle()), biometry::Variant::s("42") }) }); } static biometry::Void void_() { return biometry::Void{}; } }; void application(const biometry::Application& value) { EXPECT_EQ(Reference::application(), value); } void progress(const biometry::Progress& value) { EXPECT_EQ(Reference::progress(), value); } void point(const biometry::Point& value) { EXPECT_EQ(Reference::point(), value); } void rectangle(const biometry::Rectangle& value) { EXPECT_EQ(Reference::rectangle(), value); } void reason(const biometry::Reason& value) { EXPECT_EQ(Reference::reason(), value); } void user(const biometry::User& value) { EXPECT_EQ(Reference::user(), value); } void none(const biometry::Variant::None& value) { EXPECT_EQ(Reference::none(), value); } void variant(const biometry::Variant& value) { EXPECT_EQ(Reference::variant(), value); } void void_(const biometry::Void& value) { EXPECT_EQ(Reference::void_(), value); } }; } struct DbusCodec : public core::dbus::testing::Fixture { }; TEST_F(DbusCodec, encoding_and_decoding_works_for_relevant_types) { auto sk = [this]() { auto trap = core::posix::trap_signals_for_all_subsequent_threads({core::posix::Signal::sig_term}); trap->signal_raised().connect([trap](core::posix::Signal) { trap->stop(); }); auto bus = session_bus(); bus->install_executor(core::dbus::asio::make_executor(bus)); std::thread t{[bus]() { bus->run(); }}; auto impl = std::make_shared(); auto sk = std::make_shared(bus, core::dbus::Service::add_service(bus, testing::EchoService::name()) ->add_object_for_path(testing::EchoService::path()), impl); trap->run(); bus->stop(); if (t.joinable()) t.join(); return Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success; }; auto st = [this]() { auto bus = session_bus(); bus->install_executor(core::dbus::asio::make_executor(bus)); std::thread t{[bus]() { bus->run(); }}; auto st = std::make_shared(core::dbus::Service::use_service(bus, testing::EchoService::name())->object_for_path(testing::EchoService::path())); EXPECT_NO_THROW(st->application(VerifyingEchoService::Reference::application())); EXPECT_NO_THROW(st->progress(VerifyingEchoService::Reference::progress())); EXPECT_NO_THROW(st->point(VerifyingEchoService::Reference::point())); EXPECT_NO_THROW(st->rectangle(VerifyingEchoService::Reference::rectangle())); EXPECT_NO_THROW(st->reason(VerifyingEchoService::Reference::reason())); EXPECT_NO_THROW(st->user(VerifyingEchoService::Reference::user())); EXPECT_NO_THROW(st->none(VerifyingEchoService::Reference::none())); EXPECT_NO_THROW(st->variant(VerifyingEchoService::Reference::variant())); EXPECT_NO_THROW(st->void_(VerifyingEchoService::Reference::void_())); bus->stop(); if (t.joinable()) t.join(); return Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success; }; auto skp = core::posix::fork(sk, core::posix::StandardStream::empty); std::this_thread::sleep_for(std::chrono::milliseconds{500}); auto stp = core::posix::fork(st, core::posix::StandardStream::empty); EXPECT_TRUE(testing::did_finish_successfully(stp.wait_for(core::posix::wait::Flags::untraced))); EXPECT_NO_THROW(skp.send_signal_or_throw(core::posix::Signal::sig_term)); EXPECT_TRUE(testing::did_finish_successfully(skp.wait_for(core::posix::wait::Flags::untraced))); } biometryd-0.3.1/tests/test_dbus_stub_skeleton.cpp000066400000000000000000000153121455450034500223230ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include #include #include #include #include #include #include "did_finish_successfully.h" #include "mock_device.h" namespace { struct MockService : public biometry::Service { MOCK_CONST_METHOD0(default_device, std::shared_ptr()); }; struct TestDbusStubSkeleton : public core::dbus::testing::Fixture { struct StubScope { ~StubScope() { bus->stop(); bus_worker.join(); } core::dbus::Bus::Ptr bus; std::thread bus_worker; }; struct SkeletonScope { ~SkeletonScope() { } core::posix::exit::Status run() { // Replacement for biomerty::Runtime, which causes a crash. std::thread worker([this]() { bus->run(); }); trap->run(); bus->stop(); worker.join(); return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success; } std::shared_ptr trap; core::dbus::Bus::Ptr bus; }; std::shared_ptr skeleton_scope() { auto trap = core::posix::trap_signals_for_all_subsequent_threads({core::posix::Signal::sig_term}); trap->signal_raised().connect([trap](core::posix::Signal) { trap->stop(); }); auto bus = session_bus(); bus->install_executor(core::dbus::asio::make_executor(bus)); return std::shared_ptr{new SkeletonScope{trap, bus}}; } std::shared_ptr stub_scope() { auto bus = session_bus(); bus->install_executor(core::dbus::asio::make_executor(bus)); std::thread bus_worker{[bus]() { bus->run(); }}; return std::shared_ptr{new StubScope{bus, std::move(bus_worker)}}; } }; template struct MockKeepAliveObserver : public testing::MockObserver { MockKeepAliveObserver(const typename biometry::Operation::Ptr& op) : op{op} { } std::promise::Result> promise; typename biometry::Operation::Ptr op; }; template std::future::Observer::Result> start(const typename biometry::Operation::Ptr& op) { auto observer = new MockKeepAliveObserver{op}; op->start_with_observer(typename biometry::Operation::Observer::Ptr{observer}); return observer->promise.get_future(); } } TEST_F(TestDbusStubSkeleton, stub_skeleton_service_can_talk_with_one_another) { using namespace ::testing; auto skeleton = [this]() { auto scope = skeleton_scope(); auto identifier = std::make_shared>(); ON_CALL(*identifier, identify_user(_,_)).WillByDefault(Return(std::make_shared>())); auto template_store = std::make_shared>(); ON_CALL(*template_store, size(_, _)).WillByDefault(Return(std::make_shared>())); ON_CALL(*template_store, list(_, _)).WillByDefault(Return(std::make_shared>())); ON_CALL(*template_store, enroll(_, _)).WillByDefault(Return(std::make_shared>())); ON_CALL(*template_store, remove(_, _, _)).WillByDefault(Return(std::make_shared>())); ON_CALL(*template_store, clear(_, _)).WillByDefault(Return(std::make_shared>())); auto device = std::make_shared>(); ON_CALL(*device, identifier()).WillByDefault(ReturnRef(*identifier)); ON_CALL(*device, template_store()).WillByDefault(ReturnRef(*template_store)); auto service = std::make_shared>(); ON_CALL(*service, default_device()).WillByDefault(Return(device)); auto skeleton = biometry::dbus::skeleton::Service::create_for_bus(scope->bus, service); return scope->run(); }; auto stub = [this]() { auto app = biometry::Application::system(); auto reason = biometry::Reason::unknown(); auto user = biometry::User::current(); auto id = biometry::TemplateStore::TemplateId{42}; auto scope = stub_scope(); auto service = biometry::dbus::stub::Service::create_for_bus(scope->bus); auto device = service->default_device(); auto f1 = start(device->template_store().size(app, user)); auto f2 = start(device->template_store().list(app, user)); auto f3 = start(device->template_store().enroll(app, user)); auto f4 = start(device->template_store().remove(app, user, id)); auto f5 = start(device->template_store().clear(app, user)); auto f6 = start(device->identifier().identify_user(app, reason)); return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success; }; auto cp_skeleton = core::posix::fork(skeleton, core::posix::StandardStream::empty); // std::cout << cp_skeleton.pid() << std::endl; char c; std::cin >> c; std::this_thread::sleep_for(std::chrono::milliseconds{500}); auto cp_stub = core::posix::fork(stub, core::posix::StandardStream::empty); EXPECT_TRUE(did_finish_successfully(cp_stub.wait_for(core::posix::wait::Flags::untraced))); ASSERT_NO_THROW(cp_skeleton.send_signal_or_throw(core::posix::Signal::sig_term)); EXPECT_TRUE(did_finish_successfully(cp_skeleton.wait_for(core::posix::wait::Flags::untraced))); } biometryd-0.3.1/tests/test_device_registrar.cpp000066400000000000000000000041041455450034500217430ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include #include #include #include "config.h" #include namespace { struct MockPluginEnumerator : public biometry::devices::plugin::Enumerator { MOCK_CONST_METHOD1(enumerate, std::size_t(const Functor&)); }; } TEST(DeviceRegistrar, cleans_out_device_registry) { { biometry::DeviceRegistrar dr{biometry::devices::plugin::DirectoryEnumerator{{testing::runtime_dir()}}}; EXPECT_GE(biometry::device_registry().size(), 2); } EXPECT_EQ(0, biometry::device_registry().size()); } TEST(DeviceRegistrar, calls_into_enumerator) { using namespace ::testing; MockPluginEnumerator enumerator; EXPECT_CALL(enumerator, enumerate(_)).Times(1).WillOnce(Return(0)); biometry::DeviceRegistrar dr{enumerator}; } TEST(DeviceRegistrar, adds_dummy_device) { biometry::DeviceRegistrar dr{biometry::devices::plugin::DirectoryEnumerator{{testing::runtime_dir()}}}; EXPECT_EQ(1, biometry::device_registry().count(biometry::devices::Dummy::id)); } TEST(DeviceRegistrar, adds_plugin_device) { biometry::DeviceRegistrar dr{biometry::devices::plugin::DirectoryEnumerator{{testing::runtime_dir()}}}; EXPECT_EQ(1, biometry::device_registry().count(biometry::devices::plugin::id)); } biometryd-0.3.1/tests/test_dictionary.cpp000066400000000000000000000072521455450034500205760ustar00rootroot00000000000000 /* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include TEST(Dictionary, default_constructor_works) { biometry::Dictionary{}; } TEST(Dictionary, insertion_works) { biometry::Dictionary dict; dict["test"] = biometry::Variant::i(42); EXPECT_TRUE(dict.count("test") > 0); EXPECT_EQ(42, dict.at("test").integer()); } TEST(Dictionary, removal_works) { biometry::Dictionary dict; dict["test"] = biometry::Variant::i(42); EXPECT_TRUE(dict.count("test") > 0); dict.erase("test"); EXPECT_TRUE(dict.count("test") == 0); } TEST(Variant, constructors_yield_correct_type_and_value) { {const bool rv = true; biometry::Variant v{rv}; EXPECT_EQ(biometry::Variant::Type::boolean, v.type()); EXPECT_EQ(rv, v.boolean());} {const std::int64_t rv = 42; biometry::Variant v{rv}; EXPECT_EQ(biometry::Variant::Type::integer, v.type()); EXPECT_EQ(rv, v.integer());} {const double rv = 0.42; biometry::Variant v{rv}; EXPECT_EQ(biometry::Variant::Type::floating_point, v.type()); EXPECT_EQ(rv, v.floating_point());} {const biometry::Rectangle rv = {biometry::Point{0.42, 0.42}, biometry::Point{0.42, 0.42}}; biometry::Variant v{rv}; EXPECT_EQ(biometry::Variant::Type::rectangle, v.type()); EXPECT_EQ(rv, v.rectangle());} {const std::string rv = "42"; biometry::Variant v{rv}; EXPECT_EQ(biometry::Variant::Type::string, v.type()); EXPECT_EQ(rv, v.string());} {const std::vector rv = {4, 2}; biometry::Variant v{rv}; EXPECT_EQ(biometry::Variant::Type::blob, v.type()); EXPECT_EQ(rv, v.blob());} {const std::vector rv = {biometry::Variant(std::int64_t(4)), biometry::Variant("2")}; biometry::Variant v{rv}; EXPECT_EQ(biometry::Variant::Type::vector, v.type()); EXPECT_EQ(rv, v.vector());} } TEST(Variant, named_constructors_yield_correct_type_and_value) { {const bool rv = true; auto v = biometry::Variant::b(rv); EXPECT_EQ(biometry::Variant::Type::boolean, v.type()); EXPECT_EQ(rv, v.boolean());} {const std::int64_t rv = 42; auto v = biometry::Variant::i(rv); EXPECT_EQ(biometry::Variant::Type::integer, v.type()); EXPECT_EQ(rv, v.integer());} {const double rv = 0.42; auto v = biometry::Variant::d(rv); EXPECT_EQ(biometry::Variant::Type::floating_point, v.type()); EXPECT_EQ(rv, v.floating_point());} {const std::string rv = "42"; auto v = biometry::Variant::s(rv); EXPECT_EQ(biometry::Variant::Type::string, v.type()); EXPECT_EQ(rv, v.string());} {const biometry::Rectangle rv = {biometry::Point{0.42, 0.42}, biometry::Point{0.42, 0.42}}; auto v = biometry::Variant::r(rv); EXPECT_EQ(biometry::Variant::Type::rectangle, v.type()); EXPECT_EQ(rv, v.rectangle());} {const std::vector rv = {4, 2}; auto v = biometry::Variant::bl(rv); EXPECT_EQ(biometry::Variant::Type::blob, v.type()); EXPECT_EQ(rv, v.blob());} {const std::vector rv = {biometry::Variant::i(4), biometry::Variant::s("2")}; biometry::Variant v{rv}; EXPECT_EQ(biometry::Variant::Type::vector, v.type()); EXPECT_EQ(rv, v.vector());} } biometryd-0.3.1/tests/test_dispatching_service_and_device.cpp000066400000000000000000000156211455450034500246060ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include "mock_device.h" #include namespace { struct MockDispatcher : public biometry::util::Dispatcher { MOCK_METHOD1(dispatch, void(const Task&)); }; } TEST(DispatchingDevice, calls_into_dispatcher_for_template_store_size_query) { using namespace testing; auto mock_observer = std::make_shared>>(); auto template_store = std::make_shared>(); ON_CALL(*template_store, size(_, _)).WillByDefault(Return(std::make_shared>>())); auto device = std::make_shared>(); ON_CALL(*device, template_store()).WillByDefault(ReturnRef(*template_store)); auto dispatcher = std::make_shared>(); EXPECT_CALL(*dispatcher, dispatch(_)).Times(1).WillOnce(Invoke([](const biometry::util::Dispatcher::Task& task) { task(); })); auto dispatching = std::make_shared(dispatcher, device); auto op = dispatching->template_store().size(biometry::Application::system(), biometry::User::current()); op->start_with_observer(mock_observer); } TEST(DispatchingDevice, calls_into_dispatcher_for_template_store_list) { using namespace testing; auto mock_observer = std::make_shared>>(); auto template_store = std::make_shared>(); ON_CALL(*template_store, list(_, _)).WillByDefault(Return(std::make_shared>>())); auto device = std::make_shared>(); ON_CALL(*device, template_store()).WillByDefault(ReturnRef(*template_store)); auto dispatcher = std::make_shared>(); EXPECT_CALL(*dispatcher, dispatch(_)).Times(1).WillOnce(Invoke([](const biometry::util::Dispatcher::Task& task) { task(); })); auto dispatching = std::make_shared(dispatcher, device); auto op = dispatching->template_store().list(biometry::Application::system(), biometry::User::current()); op->start_with_observer(mock_observer); } TEST(DispatchingDevice, calls_into_dispatcher_for_template_store_size_enrollment) { using namespace testing; auto mock_observer = std::make_shared>>(); auto template_store = std::make_shared>(); ON_CALL(*template_store, enroll(_, _)).WillByDefault(Return(std::make_shared>>())); auto device = std::make_shared>(); ON_CALL(*device, template_store()).WillByDefault(ReturnRef(*template_store)); auto dispatcher = std::make_shared>(); EXPECT_CALL(*dispatcher, dispatch(_)).Times(1).WillOnce(Invoke([](const biometry::util::Dispatcher::Task& task) { task(); })); auto dispatching = std::make_shared(dispatcher, device); auto op = dispatching->template_store().enroll(biometry::Application::system(), biometry::User::current()); op->start_with_observer(mock_observer); } TEST(DispatchingDevice, calls_into_dispatcher_for_template_store_removal) { using namespace testing; auto mock_observer = std::make_shared>>(); auto template_store = std::make_shared>(); ON_CALL(*template_store, remove(_, _, _)).WillByDefault(Return(std::make_shared>>())); auto device = std::make_shared>(); ON_CALL(*device, template_store()).WillByDefault(ReturnRef(*template_store)); auto dispatcher = std::make_shared>(); EXPECT_CALL(*dispatcher, dispatch(_)).Times(1).WillOnce(Invoke([](const biometry::util::Dispatcher::Task& task) { task(); })); auto dispatching = std::make_shared(dispatcher, device); auto op = dispatching->template_store().remove(biometry::Application::system(), biometry::User::current(), 42); op->start_with_observer(mock_observer); } TEST(DispatchingDevice, calls_into_dispatcher_for_template_store_clearance) { using namespace testing; auto mock_observer = std::make_shared>>(); auto template_store = std::make_shared>(); ON_CALL(*template_store, clear(_, _)).WillByDefault(Return(std::make_shared>>())); auto device = std::make_shared>(); ON_CALL(*device, template_store()).WillByDefault(ReturnRef(*template_store)); auto dispatcher = std::make_shared>(); EXPECT_CALL(*dispatcher, dispatch(_)).Times(1).WillOnce(Invoke([](const biometry::util::Dispatcher::Task& task) { task(); })); auto dispatching = std::make_shared(dispatcher, device); auto op = dispatching->template_store().clear(biometry::Application::system(), biometry::User::current()); op->start_with_observer(mock_observer); } TEST(DispatchingDevice, calls_into_dispatcher_for_identification) { using namespace testing; auto mock_observer = std::make_shared>>(); auto identifier = std::make_shared>(); ON_CALL(*identifier, identify_user(_, _)).WillByDefault(Return(std::make_shared>>())); auto device = std::make_shared>(); ON_CALL(*device, identifier()).WillByDefault(ReturnRef(*identifier)); auto dispatcher = std::make_shared>(); EXPECT_CALL(*dispatcher, dispatch(_)).Times(1).WillOnce(Invoke([](const biometry::util::Dispatcher::Task& task) { task(); })); auto dispatching = std::make_shared(dispatcher, device); auto op = dispatching->identifier().identify_user(biometry::Application::system(), biometry::Reason::unknown()); op->start_with_observer(mock_observer); } biometryd-0.3.1/tests/test_fingerprint_reader.cpp000066400000000000000000000024521455450034500222770ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include TEST(FingerprintReaderGuidanceHints, to_and_from_dictionary_works) { biometry::devices::FingerprintReader::GuidedEnrollment::Hints g1; g1.is_main_cluster_identified = true; g1.suggested_next_direction = biometry::devices::FingerprintReader::Direction::east; g1.masks = std::vector{biometry::Rectangle{{0.42, 0.42}, {0.43, 0.43}}}; auto dict = g1.to_dictionary(); biometry::devices::FingerprintReader::GuidedEnrollment::Hints g2; g2.from_dictionary(dict); EXPECT_EQ(g1, g2); } biometryd-0.3.1/tests/test_forwarding.cpp000066400000000000000000000044221455450034500205670ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include "mock_device.h" TEST(Forwarding, calls_into_implementation) { using namespace ::testing; MockTemplateStore ts; EXPECT_CALL(ts, size(_, _)).Times(1).WillOnce(Return(biometry::Operation::Ptr{})); EXPECT_CALL(ts, enroll(_, _)).Times(1).WillOnce(Return(biometry::Operation::Ptr{})); EXPECT_CALL(ts, clear(_, _)).Times(1).WillOnce(Return(biometry::Operation::Ptr{})); MockIdentifier identifier; EXPECT_CALL(identifier, identify_user(_, _)).Times(1).WillOnce(Return(biometry::Operation::Ptr{})); MockVerifier verifier; EXPECT_CALL(verifier, verify_user(_, _, _)).Times(1).WillOnce(Return(biometry::Operation::Ptr{})); auto impl = std::make_shared(); EXPECT_CALL(*impl, template_store()).Times(3).WillRepeatedly(ReturnRef(ts)); EXPECT_CALL(*impl, identifier()).Times(1).WillOnce(ReturnRef(identifier)); EXPECT_CALL(*impl, verifier()).Times(1).WillOnce(ReturnRef(verifier)); const biometry::Application app{biometry::Application::system()}; const biometry::Reason reason{biometry::Reason::unknown()}; const biometry::User user = biometry::User::current(); impl->template_store().size(app, user); impl->template_store().enroll(app, user); impl->template_store().clear(app, user); impl->identifier().identify_user(app, reason); impl->verifier().verify_user(app, user, reason); } biometryd-0.3.1/tests/test_geometry.cpp000066400000000000000000000031351455450034500202600ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include TEST(Point, equality_operator_works) { biometry::Point p1{0.42, 0.43}; biometry::Point p2(p1); EXPECT_EQ(p2, p1); biometry::Point p3{0.43, 0.42}; EXPECT_NE(p3, p1); } TEST(Point, stream_insertion_works) { biometry::Point p1{0.42, 0.42}; std::stringstream ss; ss << p1; EXPECT_EQ("(0.42,0.42)", ss.str()); } TEST(Rectangle, equality_operator_works) { biometry::Point p1{0.42, 0.43}; biometry::Point p2(p1); biometry::Rectangle r1{p1, p2}; biometry::Rectangle r2(r1); EXPECT_EQ(r2, r1); biometry::Point p3{0.43, 0.42}; biometry::Rectangle r3{p1, p3}; EXPECT_NE(r3, r1); } TEST(Rectangle, stream_insertion_works) { biometry::Point p1{0.42, 0.43}; biometry::Point p2(p1); biometry::Rectangle r1{p1, p2}; std::stringstream ss; ss << r1; EXPECT_EQ("((0.42,0.43),(0.42,0.43))", ss.str()); } biometryd-0.3.1/tests/test_operation.cpp000066400000000000000000000025601455450034500204260ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include TEST(Operation, surfaces_correct_types) { struct Test { typedef bool Progress; typedef char Reason; typedef short Error; typedef int Result; }; static_assert(std::is_same::Progress>::value, ""); static_assert(std::is_same::Reason>::value, ""); static_assert(std::is_same::Error>::value, ""); static_assert(std::is_same::Result>::value, ""); } biometryd-0.3.1/tests/test_percent.cpp000066400000000000000000000026371455450034500200730ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include TEST(Percent, construction_throws_for_value_out_of_range) { EXPECT_ANY_THROW(biometry::Percent::from_raw_value(2.)); } TEST(Percent, construction_succeeds_for_value_in_range) { EXPECT_NO_THROW(biometry::Percent::from_raw_value(.5)); } TEST(Percent, value_can_be_retrieved) { auto percent = biometry::Percent::from_raw_value(.43); EXPECT_DOUBLE_EQ(.43, *percent); } TEST(Percent, can_be_copy_constructed) { biometry::Percent copy{biometry::Percent::from_raw_value(.43)}; EXPECT_DOUBLE_EQ(.43, *copy); } TEST(Percent, can_be_assigned) { biometry::Percent copy = biometry::Percent::from_raw_value(.43); EXPECT_DOUBLE_EQ(.43, *copy); } biometryd-0.3.1/tests/test_plugin_device.cpp000066400000000000000000000123241455450034500212420ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include #include #include #include #include #include #include #include "config.h" namespace { struct MockDynamicLibraryApi : public biometry::util::DynamicLibrary::Api { MOCK_CONST_METHOD1(open, biometry::util::DynamicLibrary::Handle(const boost::filesystem::path& path)); MOCK_CONST_METHOD1(close, void(const biometry::util::DynamicLibrary::Handle&)); MOCK_CONST_METHOD2(sym, biometry::util::DynamicLibrary::Symbol(const biometry::util::DynamicLibrary::Handle&, const std::string& symbol)); MOCK_CONST_METHOD0(error, std::string()); }; struct MockPluginLoader : public biometry::devices::plugin::Loader { MOCK_CONST_METHOD2( verify_and_load, std::shared_ptr (const std::shared_ptr&, const boost::filesystem::path&)); }; } TEST(PluginDeviceLoad, calls_into_loader) { using namespace testing; std::shared_ptr api = std::make_shared>(); const boost::filesystem::path path{"/tmp/does/not/exist/module.so"}; MockPluginLoader loader; EXPECT_CALL(loader, verify_and_load(api, path)).Times(1).WillOnce(Return(std::shared_ptr{})); biometry::devices::plugin::load(api, path, loader); } TEST(NonVerifyingLoader, can_load_plugin) { const auto p = testing::runtime_dir() / "libbiometryd_devices_plugin_dl.so"; biometry::devices::plugin::NonVerifyingLoader loader; EXPECT_NO_THROW(loader.verify_and_load(biometry::util::glibc::dl_api(), p)); } TEST(ElfDescriptorLoader, can_load_from_plugin) { const auto p = testing::runtime_dir() / "libbiometryd_devices_plugin_dl.so"; biometry::devices::plugin::ElfDescriptorLoader loader; auto desc = loader.load_with_name(p, BIOMETRYD_DEVICES_PLUGIN_DESCRIPTOR_SECTION); EXPECT_STREQ("TestPlugin", desc.name); EXPECT_STREQ("Thomas Voß ", desc.author); EXPECT_STREQ("Just a plugin for testing purposes", desc.description); EXPECT_EQ(0, desc.version.plugin.major); EXPECT_EQ(0, desc.version.plugin.minor); EXPECT_EQ(0, desc.version.plugin.patch); } TEST(ElfDescriptorLoader, throws_for_section_not_being_found) { const auto p = testing::runtime_dir() / "libbiometryd_devices_plugin_dl.so"; biometry::devices::plugin::ElfDescriptorLoader loader; EXPECT_THROW(loader.load_with_name(p, "DoesNotExist"), biometry::devices::plugin::ElfDescriptorLoader::NoSuchSection); } TEST(ElfDescriptorLoader, throws_when_trying_to_load_non_existing_file) { std::remove("test.txt"); biometry::devices::plugin::ElfDescriptorLoader loader; EXPECT_THROW(loader.load_with_name("test.txt", "DoesNotExist"), std::system_error); } TEST(ElfDescriptorLoader, throws_when_trying_to_load_non_elf_object) { std::remove("test.txt"); {std::ofstream out("test.txt"); out << "test";} biometry::devices::plugin::ElfDescriptorLoader loader; EXPECT_THROW(loader.load_with_name("test.txt", "DoesNotExist"), std::runtime_error); } TEST(MajorVersionVerifier, throws_when_verifying_plugin_with_major_host_version_mismatch) { const auto p = testing::runtime_dir() / "libbiometryd_devices_plugin_dl_version_mismatch.so"; biometry::devices::plugin::ElfDescriptorLoader loader; biometry::devices::plugin::MajorVersionVerifier verifier; EXPECT_THROW(verifier.verify(loader.load_with_name(p, BIOMETRYD_DEVICES_PLUGIN_DESCRIPTOR_SECTION)), biometry::devices::plugin::MajorVersionVerifier::MajorVersionMismatch); } TEST(MajorVersionVerifier, does_not_throw_when_verifying_plugin_with_major_host_version_match) { const auto p = testing::runtime_dir() / "libbiometryd_devices_plugin_dl.so"; biometry::devices::plugin::ElfDescriptorLoader loader; biometry::devices::plugin::MajorVersionVerifier verifier; EXPECT_NO_THROW(verifier.verify(loader.load_with_name(p, BIOMETRYD_DEVICES_PLUGIN_DESCRIPTOR_SECTION))); } TEST(DirectoryEnumerator, finds_biometryd_plugins) { biometry::devices::plugin::DirectoryEnumerator enumerator{{testing::runtime_dir()}}; EXPECT_GE(1, enumerator.enumerate([](const biometry::Device::Descriptor::Ptr& ptr) { static const biometry::util::Configuration the_empty_config; EXPECT_NO_THROW(ptr->create(the_empty_config)); })); } biometryd-0.3.1/tests/test_progress.cpp000066400000000000000000000020061455450034500202650ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include TEST(Progress, inserts_correctly_into_output_stream) { biometry::Progress progress{biometry::Percent::from_raw_value(0.5), biometry::Dictionary{}}; std::stringstream ss; ss << progress; EXPECT_EQ("Progress: 50%", ss.str()); } biometryd-0.3.1/tests/test_qml_plugin.cpp000066400000000000000000000014211455450034500205700ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include QUICK_TEST_MAIN(BiometrydQmlPlugin) biometryd-0.3.1/tests/test_user.cpp000066400000000000000000000030741455450034500174050ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include TEST(User, inserts_correctly_into_output_stream) { biometry::User user{0}; std::stringstream ss; ss << user; EXPECT_EQ("User[0]", ss.str()); } TEST(User, root_has_user_id_0) { EXPECT_EQ(0, biometry::User::root().id); } TEST(User, current_user_returns_instance_with_correct_id) { EXPECT_EQ(::getuid(), biometry::User::current().id); } TEST(User, less_than_operator_works) { EXPECT_TRUE(biometry::User{1} < biometry::User{2}); EXPECT_FALSE(biometry::User{2} < biometry::User{1}); } TEST(User, set_of_users_works) { std::set users; users.insert(biometry::User::root()); users.insert(biometry::User{42}); EXPECT_EQ(2, users.size()); EXPECT_TRUE(users.count(biometry::User::root()) > 0); EXPECT_TRUE(users.count(biometry::User{42}) > 0); } biometryd-0.3.1/tests/test_verifier.cpp000066400000000000000000000015151455450034500202400ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ #include #include #include #include biometryd-0.3.1/tests/tst_qml_plugin.qml000066400000000000000000000070111455450034500204330ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß * */ import QtQuick 2.0 import QtTest 1.0 import Biometryd 0.0 TestCase { name: "Biometryd" Observer { id: observer onStarted: { console.log("started") } onCanceled: { console.log("canceled") } onFailed: { console.log("failed") } onProgressed: { // biometryd API users can use details to receive // device/operation-specific information about the // operation. We illustrate the case of a FingerprintReader here. console.log("progressed: ", percent.toFixed(2)); var isFingerPresent = details[FingerprintReader.isFingerPresent] var hasMainClusterIdentified = details[FingerprintReader.hasMainClusterIdentified] var suggestedNextDirection = details[FingerprintReader.suggestedNextDirection] var estimatedFingerSize = details[FingerprintReader.estimatedFingerSize] var masks = details[FingerprintReader.masks] console.log("isFingerPresent:", isFingerPresent, "hasMainClusterIdentified:", hasMainClusterIdentified, "suggestedNextDirection:", suggestedNextDirection, "masks:", masks); } onSucceeded: { console.log("succeeded:", result) } } User { id: user uid: 0 } SignalSpy { id: spy target: observer signalName: "succeeded" } function test_defaultDeviceIsAvailable() { console.log("Biometryd.available:", Biometryd.available); var ts = Biometryd.defaultDevice.templateStore; var id = Biometryd.defaultDevice.identifier; } function test_templateStoreOfDefaultDeviceIsAvailable() { var ts = Biometryd.defaultDevice.templateStore; // The API is structured around the concept of an Operation. // An operation is asynchronous. Its state can be observed with the help // of an Observer. { var op = ts.enroll(user); op.start(observer); spy.wait(6000); } { var op = ts.list(user); op.start(observer); spy.wait(6000); } { op = ts.size(user); op.start(observer); spy.wait(6000); } { op = ts.remove(user, 42); op.start(observer); spy.wait(6000); } { op = ts.clear(user); op.start(observer); spy.wait(6000); } } function test_identifierOfDefaultDeviceIsAvailable() { var identifier = Biometryd.defaultDevice.identifier; var op = identifier.identifyUser(); op.start(observer); spy.wait(6000); } }