Files
libfprint/examples/enroll.c
Dmitrii Shcherbakov 999bca076c Allow FpPrint data to be extended on enrollment.
* Allow FPI_PRINT_NBIS to be extended rather than overridden if a user
  supplies an existing FpPrint template with data;
* Prints will only be extended if a device has the required feature.
  For image-based devices this feature is added by default since they
  typically do not have storage (this can be overridden at the child
  class level).

Extending an existing FpPrint requires passing a template print with
existing data during the enrollment process. This is done because the
caller is responsible for maintaining the fingerprint database and doing
the necessary deserialization operations if needed. The existing
example program is updated to show how to do that.
2021-12-16 12:53:41 +01:00

273 lines
7.8 KiB
C

/*
* Example fingerprint enrollment program
* Enrolls your chosen finger and saves the print to disk
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2019 Marco Trevisan <marco.trevisan@canonical.com>
*
* 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-enroll"
#include <stdio.h>
#include <libfprint/fprint.h>
#include <glib-unix.h>
#include "storage.h"
#include "utilities.h"
typedef struct _EnrollData
{
GMainLoop *loop;
GCancellable *cancellable;
unsigned int sigint_handler;
FpFinger finger;
int ret_value;
gboolean update_fingerprint;
} EnrollData;
static void
enroll_data_free (EnrollData *enroll_data)
{
g_clear_handle_id (&enroll_data->sigint_handler, g_source_remove);
g_clear_object (&enroll_data->cancellable);
g_main_loop_unref (enroll_data->loop);
g_free (enroll_data);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC (EnrollData, enroll_data_free)
static void
on_device_closed (FpDevice *dev, GAsyncResult *res, void *user_data)
{
EnrollData *enroll_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 (enroll_data->loop);
}
static void
on_enroll_completed (FpDevice *dev, GAsyncResult *res, void *user_data)
{
EnrollData *enroll_data = user_data;
g_autoptr(FpPrint) print = NULL;
g_autoptr(GError) error = NULL;
print = fp_device_enroll_finish (dev, res, &error);
if (!error)
{
enroll_data->ret_value = EXIT_SUCCESS;
if (fp_device_has_feature (dev, FP_DEVICE_FEATURE_STORAGE))
g_debug ("Device has storage, saving a print reference locally");
else
g_debug ("Device has not storage, saving print only locally");
/* Even if the device has storage, it may not be able to save all the
* metadata that the print contains, so we can always save a local copy
* containing the handle to the device print */
int r = print_data_save (print, enroll_data->finger,
enroll_data->update_fingerprint);
if (r < 0)
{
g_warning ("Data save failed, code %d", r);
enroll_data->ret_value = EXIT_FAILURE;
}
}
else
{
g_warning ("Enroll failed with error %s", error->message);
}
fp_device_close (dev, NULL, (GAsyncReadyCallback) on_device_closed,
enroll_data);
}
static void
on_enroll_progress (FpDevice *device,
gint completed_stages,
FpPrint *print,
gpointer user_data,
GError *error)
{
if (error)
{
g_warning ("Enroll stage %d of %d failed with error %s",
completed_stages,
fp_device_get_nr_enroll_stages (device),
error->message);
return;
}
if (print && fp_print_get_image (print) &&
print_image_save (print, "enrolled.pgm"))
printf ("Wrote scanned image to enrolled.pgm\n");
printf ("Enroll stage %d of %d passed. Yay!\n", completed_stages,
fp_device_get_nr_enroll_stages (device));
}
static gboolean
should_update_fingerprint (void)
{
int update_choice;
gboolean update_fingerprint = FALSE;
printf ("Should an existing fingerprint be updated instead of being replaced (if present)? "
"Enter Y/y or N/n to make a choice.\n");
update_choice = getchar ();
if (update_choice == EOF)
{
g_warning ("EOF encountered while reading a character");
return EXIT_FAILURE;
}
switch (update_choice)
{
case 'y':
case 'Y':
update_fingerprint = TRUE;
break;
case 'n':
case 'N':
update_fingerprint = FALSE;
break;
default:
g_warning ("Invalid choice %c, should be Y/y or N/n.", update_choice);
return EXIT_FAILURE;
}
return update_fingerprint;
}
static void
on_device_opened (FpDevice *dev, GAsyncResult *res, void *user_data)
{
EnrollData *enroll_data = user_data;
FpPrint *print_template;
g_autoptr(GError) error = NULL;
if (!fp_device_open_finish (dev, res, &error))
{
g_warning ("Failed to open device: %s", error->message);
g_main_loop_quit (enroll_data->loop);
return;
}
printf ("Opened device.\n");
if (fp_device_has_feature (dev, FP_DEVICE_FEATURE_UPDATE_PRINT))
{
printf ("The device supports fingerprint updates.\n");
enroll_data->update_fingerprint = should_update_fingerprint ();
}
else
{
printf ("The device doesn't support fingerprint updates. Old prints will be erased.\n");
enroll_data->update_fingerprint = FALSE;
}
printf ("It's now time to enroll your finger.\n\n");
printf ("You will need to successfully scan your %s finger %d times to "
"complete the process.\n\n", finger_to_string (enroll_data->finger),
fp_device_get_nr_enroll_stages (dev));
printf ("Scan your finger now.\n");
print_template = print_create_template (dev, enroll_data->finger, enroll_data->update_fingerprint);
fp_device_enroll (dev, print_template, enroll_data->cancellable,
on_enroll_progress, NULL, NULL,
(GAsyncReadyCallback) on_enroll_completed,
enroll_data);
}
static gboolean
sigint_cb (void *user_data)
{
EnrollData *enroll_data = user_data;
g_cancellable_cancel (enroll_data->cancellable);
return G_SOURCE_CONTINUE;
}
int
main (void)
{
g_autoptr(FpContext) ctx = NULL;
g_autoptr(EnrollData) enroll_data = NULL;
GPtrArray *devices;
FpDevice *dev;
FpFinger finger;
g_print ("This program will enroll the selected finger overwriting any print for the same"
" finger that was enrolled previously. Fingerprint updates without erasing old data"
" are possible on devices supporting that. Ctrl+C interrupts program execution.\n");
g_print ("Choose the finger to enroll:\n");
finger = finger_chooser ();
if (finger == FP_FINGER_UNKNOWN)
{
g_warning ("Unknown finger selected");
return EXIT_FAILURE;
}
setenv ("G_MESSAGES_DEBUG", "all", 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;
}
enroll_data = g_new0 (EnrollData, 1);
enroll_data->finger = finger;
enroll_data->ret_value = EXIT_FAILURE;
enroll_data->loop = g_main_loop_new (NULL, FALSE);
enroll_data->cancellable = g_cancellable_new ();
enroll_data->sigint_handler = g_unix_signal_add_full (G_PRIORITY_HIGH,
SIGINT,
sigint_cb,
enroll_data,
NULL);
fp_device_open (dev, enroll_data->cancellable,
(GAsyncReadyCallback) on_device_opened,
enroll_data);
g_main_loop_run (enroll_data->loop);
return enroll_data->ret_value;
}