/* * Example fingerprint verification program, which verifies the * finger which has been previously enrolled to disk. * Copyright (C) 2007 Daniel Drake * Copyright (C) 2019 Marco Trevisan * * This library 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; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define FP_COMPONENT "example-verify" #include #include #include #include "storage.h" #include "utilities.h" typedef struct _VerifyData { GMainLoop *loop; GCancellable *cancellable; unsigned int sigint_handler; FpFinger finger; int ret_value; } VerifyData; static void verify_data_free (VerifyData *verify_data) { g_clear_handle_id (&verify_data->sigint_handler, g_source_remove); g_clear_object (&verify_data->cancellable); g_main_loop_unref (verify_data->loop); g_free (verify_data); } G_DEFINE_AUTOPTR_CLEANUP_FUNC (VerifyData, verify_data_free) static void on_device_closed (FpDevice *dev, GAsyncResult *res, void *user_data) { VerifyData *verify_data = user_data; g_autoptr(GError) error = NULL; fp_device_close_finish (dev, res, &error); if (error) g_warning ("Failed closing device %s", error->message); g_main_loop_quit (verify_data->loop); } static void verify_quit (FpDevice *dev, VerifyData *verify_data) { if (!fp_device_is_open (dev)) { g_main_loop_quit (verify_data->loop); return; } fp_device_close (dev, NULL, (GAsyncReadyCallback) on_device_closed, verify_data); } static void start_verification (FpDevice *dev, VerifyData *verify_data); static void on_verify_completed (FpDevice *dev, GAsyncResult *res, void *user_data) { VerifyData *verify_data = user_data; g_autoptr(FpPrint) print = NULL; g_autoptr(GError) error = NULL; char buffer[20]; gboolean match; if (!fp_device_verify_finish (dev, res, &match, &print, &error)) { g_warning ("Failed to verify print: %s", error->message); verify_data->ret_value = EXIT_FAILURE; if (error->domain != FP_DEVICE_RETRY) { verify_quit (dev, verify_data); return; } } g_print ("Verify again? [Y/n]? "); if (fgets (buffer, sizeof (buffer), stdin) && (buffer[0] == 'Y' || buffer[0] == 'y' || buffer[0] == '\n')) { start_verification (dev, verify_data); return; } verify_quit (dev, verify_data); } static void on_match_cb (FpDevice *dev, FpPrint *match, FpPrint *print, gpointer user_data, GError *error) { VerifyData *verify_data = user_data; if (error) { g_warning ("Match report: Finger not matched, retry error reported: %s", error->message); return; } if (print && fp_print_get_image (print) && print_image_save (print, "verify.pgm")) g_print ("Print image saved as verify.pgm\n"); if (match) { char date_str[128]; verify_data->ret_value = EXIT_SUCCESS; g_date_strftime (date_str, G_N_ELEMENTS (date_str), "%Y-%m-%d\0", fp_print_get_enroll_date (match)); g_debug ("Match report: device %s matched finger %s successifully " "with print %s, enrolled on date %s by user %s", fp_device_get_name (dev), finger_to_string (fp_print_get_finger (match)), fp_print_get_description (match), date_str, fp_print_get_username (match)); g_print ("MATCH!\n"); } else { g_debug ("Match report: Finger not matched"); g_print ("NO MATCH!\n"); } } static FpPrint * get_stored_print (FpDevice *dev, VerifyData *verify_data) { FpPrint *verify_print; g_print ("Loading previously enrolled %s finger data...\n", finger_to_string (verify_data->finger)); verify_print = print_data_load (dev, verify_data->finger); if (!verify_print) { g_warning ("Failed to load fingerprint data"); g_warning ("Did you remember to enroll your %s finger first?", finger_to_string (verify_data->finger)); } return verify_print; } static void on_list_completed (FpDevice *dev, GAsyncResult *res, gpointer user_data) { VerifyData *verify_data = user_data; g_autoptr(GPtrArray) prints = NULL; g_autoptr(GError) error = NULL; prints = fp_device_list_prints_finish (dev, res, &error); if (!error) { FpPrint *verify_print = NULL; g_autoptr(FpPrint) stored_print = NULL; guint i; if (!prints->len) { g_warning ("No prints saved on device"); verify_quit (dev, verify_data); return; } stored_print = get_stored_print (dev, verify_data); for (i = 0; i < prints->len; ++i) { FpPrint *print = prints->pdata[i]; if (stored_print && fp_print_equal (stored_print, print)) /* If the private print data matches, let's use the stored print * as it contains more metadata to show */ print = stored_print; if (fp_print_get_finger (print) == verify_data->finger && g_strcmp0 (fp_print_get_username (print), g_get_user_name ()) == 0) { const GDate *verify_print_date = NULL; const GDate *print_date = fp_print_get_enroll_date (print); if (verify_print) verify_print_date = fp_print_get_enroll_date (verify_print); if (!verify_print || !print_date || !verify_print_date || g_date_compare (print_date, verify_print_date) >= 0) verify_print = print; } } if (!verify_print) { verify_quit (dev, verify_data); return; } g_debug ("Comparing print with %s", fp_print_get_description (verify_print)); g_print ("Print loaded. Time to verify!\n"); fp_device_verify (dev, verify_print, verify_data->cancellable, on_match_cb, verify_data, NULL, (GAsyncReadyCallback) on_verify_completed, verify_data); } else { g_warning ("Loading prints failed with error %s", error->message); verify_quit (dev, verify_data); } } static void start_verification (FpDevice *dev, VerifyData *verify_data) { if (verify_data->finger == FP_FINGER_UNKNOWN) { g_print ("Choose the finger to verify:\n"); verify_data->finger = finger_chooser (); } if (verify_data->finger == FP_FINGER_UNKNOWN) { g_warning ("Unknown finger selected"); verify_data->ret_value = EXIT_FAILURE; verify_quit (dev, verify_data); return; } if (fp_device_has_feature (dev, FP_DEVICE_FEATURE_STORAGE)) { g_print ("Creating finger template, using device storage...\n"); fp_device_list_prints (dev, NULL, (GAsyncReadyCallback) on_list_completed, verify_data); } else { g_autoptr(FpPrint) verify_print = get_stored_print (dev, verify_data); if (!verify_print) { verify_quit (dev, verify_data); return; } g_print ("Print loaded. Time to verify!\n"); fp_device_verify (dev, verify_print, verify_data->cancellable, on_match_cb, verify_data, NULL, (GAsyncReadyCallback) on_verify_completed, verify_data); } } static void on_device_opened (FpDevice *dev, GAsyncResult *res, void *user_data) { VerifyData *verify_data = user_data; g_autoptr(GError) error = NULL; if (!fp_device_open_finish (dev, res, &error)) { g_warning ("Failed to open device: %s", error->message); verify_quit (dev, verify_data); return; } g_print ("Opened device. "); start_verification (dev, verify_data); } static gboolean sigint_cb (void *user_data) { VerifyData *verify_data = user_data; g_cancellable_cancel (verify_data->cancellable); return G_SOURCE_CONTINUE; } int main (void) { g_autoptr(FpContext) ctx = NULL; g_autoptr(VerifyData) verify_data = NULL; GPtrArray *devices; FpDevice *dev; setenv ("G_MESSAGES_DEBUG", "all", 0); setenv ("LIBUSB_DEBUG", "3", 0); ctx = fp_context_new (); devices = fp_context_get_devices (ctx); if (!devices) { g_warning ("Impossible to get devices"); return EXIT_FAILURE; } dev = discover_device (devices); if (!dev) { g_warning ("No devices detected."); return EXIT_FAILURE; } verify_data = g_new0 (VerifyData, 1); verify_data->ret_value = EXIT_FAILURE; verify_data->loop = g_main_loop_new (NULL, FALSE); verify_data->cancellable = g_cancellable_new (); verify_data->sigint_handler = g_unix_signal_add_full (G_PRIORITY_HIGH, SIGINT, sigint_cb, verify_data, NULL); fp_device_open (dev, verify_data->cancellable, (GAsyncReadyCallback) on_device_opened, verify_data); g_main_loop_run (verify_data->loop); return verify_data->ret_value; }