/* gnome-about-me-fingerprint.h * Copyright (C) 2008 Bastien Nocera * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include #include #include #include #include "um-fingerprint-dialog.h" #include "fingerprint-strings.h" /* Retrieve a widget from the UI object */ #define WID(s) GTK_WIDGET (gtk_builder_get_object (dialog, s)) /* Translate fprintd strings */ #define TR(s) dgettext("fprintd", s) /* This must match the number of images on the 2nd page in the UI file */ #define MAX_ENROLL_STAGES 5 static GDBusProxy *manager = NULL; static GDBusConnection *connection = NULL; static gboolean is_disable = FALSE; enum { STATE_NONE, STATE_CLAIMED, STATE_ENROLLING }; typedef struct { GtkWidget *label1; GtkWidget *label2; GtkWidget *ass; GtkBuilder *dialog; GDBusProxy *device; gboolean is_swipe; int num_enroll_stages; int num_stages_done; char *name; const char *finger; gint state; } EnrollData; static void create_manager (void) { GError *error = NULL; connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); if (connection == NULL) { g_warning ("Failed to connect to session bus: %s", error->message); g_error_free (error); return; } manager = g_dbus_proxy_new_sync (connection, G_DBUS_PROXY_FLAGS_NONE, NULL, "net.reactivated.Fprint", "/net/reactivated/Fprint/Manager", "net.reactivated.Fprint.Manager", NULL, &error); if (manager == NULL) { g_warning ("Failed to create fingerprint manager proxy: %s", error->message); g_error_free (error); } } static GDBusProxy * get_first_device (void) { GDBusProxy *device; GVariant *result; char *device_str = NULL; GError *error = NULL; result = g_dbus_proxy_call_sync (manager, "GetDefaultDevice", g_variant_new ("()"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL); if (result == NULL) return NULL; if (!g_variant_is_of_type (result, G_VARIANT_TYPE ("(o)"))) g_warning ("net.reactivated.Fprint.Manager.GetDefaultDevice returns unknown result %s", g_variant_get_type_string (result)); else g_variant_get (result, "(o)", &device_str); g_variant_unref (result); if (device_str == NULL) return NULL; device = g_dbus_proxy_new_sync (connection, G_DBUS_PROXY_FLAGS_NONE, NULL, "net.reactivated.Fprint", device_str, "net.reactivated.Fprint.Device", NULL, &error); if (device == NULL) { g_warning ("Failed to create fingerprint device proxy: %s", error->message); g_error_free (error); } g_free (device_str); return device; } static const char * get_reason_for_error (const char *dbus_error) { if (g_str_equal (dbus_error, "net.reactivated.Fprint.Error.PermissionDenied")) return N_("You are not allowed to access the device. Contact your system administrator."); if (g_str_equal (dbus_error, "net.reactivated.Fprint.Error.AlreadyInUse")) return N_("The device is already in use."); if (g_str_equal (dbus_error, "net.reactivated.Fprint.Error.Internal")) return N_("An internal error occurred."); return NULL; } static GtkWidget * get_error_dialog (const char *title, const char *dbus_error, GtkWindow *parent) { GtkWidget *error_dialog; const char *reason; if (dbus_error == NULL) g_warning ("get_error_dialog called with reason == NULL"); error_dialog = gtk_message_dialog_new (parent, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", title); reason = get_reason_for_error (dbus_error); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (error_dialog), "%s", reason ? _(reason) : _(dbus_error)); gtk_window_set_title (GTK_WINDOW (error_dialog), ""); /* as per HIG */ gtk_container_set_border_width (GTK_CONTAINER (error_dialog), 5); gtk_dialog_set_default_response (GTK_DIALOG (error_dialog), GTK_RESPONSE_OK); gtk_window_set_modal (GTK_WINDOW (error_dialog), TRUE); gtk_window_set_position (GTK_WINDOW (error_dialog), GTK_WIN_POS_CENTER_ON_PARENT); return error_dialog; } gboolean set_fingerprint_label (GtkWidget *label1, GtkWidget *label2) { GDBusProxy *device; GVariant *result; GVariantIter *fingers; GError *error = NULL; if (manager == NULL) { create_manager (); if (manager == NULL) { return FALSE; } } device = get_first_device (); if (device == NULL) return FALSE; result = g_dbus_proxy_call_sync (device, "ListEnrolledFingers", g_variant_new ("(s)", ""), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (!result) { if (!g_dbus_error_is_remote_error (error) || strcmp (g_dbus_error_get_remote_error(error), "net.reactivated.Fprint.Error.NoEnrolledPrints") != 0) { g_object_unref (device); return FALSE; } } if (result && g_variant_is_of_type (result, G_VARIANT_TYPE ("(as)"))) g_variant_get (result, "(as)", &fingers); else fingers = NULL; if (fingers == NULL || g_variant_iter_n_children (fingers) == 0) { is_disable = FALSE; gtk_label_set_text (GTK_LABEL (label1), _("Disabled")); gtk_label_set_text (GTK_LABEL (label2), _("Disabled")); } else { is_disable = TRUE; gtk_label_set_text (GTK_LABEL (label1), _("Enabled")); gtk_label_set_text (GTK_LABEL (label2), _("Enabled")); } if (result != NULL) g_variant_unref (result); if (fingers != NULL) g_variant_iter_free (fingers); g_object_unref (device); return TRUE; } static void delete_fingerprints (void) { GDBusProxy *device; GVariant *result; if (manager == NULL) { create_manager (); if (manager == NULL) return; } device = get_first_device (); if (device == NULL) return; result = g_dbus_proxy_call_sync (device, "DeleteEnrolledFingers", g_variant_new ("(s)", ""), G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL); if (result) g_variant_unref (result); g_object_unref (device); } static void delete_fingerprints_question (GtkWindow *parent, GtkWidget *label1, GtkWidget *label2, ActUser *user) { GtkWidget *question; GtkWidget *button; question = gtk_message_dialog_new (parent, GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, _("Delete registered fingerprints?")); gtk_dialog_add_button (GTK_DIALOG (question), _("_Cancel"), GTK_RESPONSE_CANCEL); gtk_window_set_modal (GTK_WINDOW (question), TRUE); button = gtk_button_new_with_mnemonic (_("_Delete Fingerprints")); gtk_button_set_image (GTK_BUTTON (button), gtk_image_new_from_icon_name ("edit-delete", GTK_ICON_SIZE_BUTTON)); gtk_widget_set_can_default (button, TRUE); gtk_widget_show (button); gtk_dialog_add_action_widget (GTK_DIALOG (question), button, GTK_RESPONSE_OK); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (question), _("Do you want to delete your registered fingerprints so fingerprint login is disabled?")); gtk_container_set_border_width (GTK_CONTAINER (question), 5); gtk_dialog_set_default_response (GTK_DIALOG (question), GTK_RESPONSE_OK); gtk_window_set_position (GTK_WINDOW (question), GTK_WIN_POS_CENTER_ON_PARENT); gtk_window_set_modal (GTK_WINDOW (question), TRUE); if (gtk_dialog_run (GTK_DIALOG (question)) == GTK_RESPONSE_OK) { delete_fingerprints (); set_fingerprint_label (label1, label2); } gtk_widget_destroy (question); } static gboolean enroll_start (EnrollData *data, GError **error) { GVariant *result; result = g_dbus_proxy_call_sync (data->device, "EnrollStart", g_variant_new ("(s)", data->finger), G_DBUS_CALL_FLAGS_NONE, -1, NULL, error); if (result == NULL) return FALSE; g_variant_unref (result); return TRUE; } static gboolean enroll_stop (EnrollData *data, GError **error) { GVariant *result; result = g_dbus_proxy_call_sync (data->device, "EnrollStop", g_variant_new ("()"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, error); if (result == NULL) return FALSE; g_variant_unref (result); return TRUE; } static gboolean claim (EnrollData *data, GError **error) { GVariant *result; result = g_dbus_proxy_call_sync (data->device, "Claim", g_variant_new ("(s)", ""), G_DBUS_CALL_FLAGS_NONE, -1, NULL, error); if (result == NULL) return FALSE; g_variant_unref (result); return TRUE; } static gboolean release (EnrollData *data, GError **error) { GVariant *result; result = g_dbus_proxy_call_sync (data->device, "Release", g_variant_new ("()"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, error); if (result == NULL) return FALSE; g_variant_unref (result); return TRUE; } static void enroll_data_destroy (EnrollData *data) { switch (data->state) { case STATE_ENROLLING: enroll_stop (data, NULL); /* fall-through */ case STATE_CLAIMED: release (data, NULL); /* fall-through */ case STATE_NONE: g_free (data->name); g_object_unref (data->device); g_object_unref (data->dialog); gtk_widget_destroy (data->ass); g_free (data); } } static const char * selected_finger (GtkBuilder *dialog) { int index; if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (WID ("radiobutton1")))) { gtk_widget_set_sensitive (WID ("finger_combobox"), FALSE); return "right-index-finger"; } if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (WID ("radiobutton2")))) { gtk_widget_set_sensitive (WID ("finger_combobox"), FALSE); return "left-index-finger"; } gtk_widget_set_sensitive (WID ("finger_combobox"), TRUE); index = gtk_combo_box_get_active (GTK_COMBO_BOX (WID ("finger_combobox"))); switch (index) { case 0: return "left-thumb"; case 1: return "left-middle-finger"; case 2: return "left-ring-finger"; case 3: return "left-little-finger"; case 4: return "right-thumb"; case 5: return "right-middle-finger"; case 6: return "right-ring-finger"; case 7: return "right-little-finger"; default: g_assert_not_reached (); } return NULL; } static void finger_radio_button_toggled (GtkToggleButton *button, EnrollData *data) { GtkBuilder *dialog = data->dialog; char *msg; data->finger = selected_finger (data->dialog); msg = g_strdup_printf (TR(finger_str_to_msg (data->finger, data->is_swipe)), data->name); gtk_label_set_text (GTK_LABEL (WID("enroll-label")), msg); g_free (msg); } static void finger_combobox_changed (GtkComboBox *combobox, EnrollData *data) { GtkBuilder *dialog = data->dialog; char *msg; data->finger = selected_finger (data->dialog); msg = g_strdup_printf (TR(finger_str_to_msg (data->finger, data->is_swipe)), data->name); gtk_label_set_text (GTK_LABEL (WID("enroll-label")), msg); g_free (msg); } static void assistant_cancelled (GtkAssistant *ass, EnrollData *data) { GtkWidget *label1, *label2; label1 = data->label1; label2 = data->label2; enroll_data_destroy (data); set_fingerprint_label (label1, label2); } static void enroll_result (EnrollData *data, const char *result, gboolean done) { GtkBuilder *dialog = data->dialog; char *msg; if (g_str_equal (result, "enroll-completed") || g_str_equal (result, "enroll-stage-passed")) { char *name, *path; data->num_stages_done++; name = g_strdup_printf ("image%d", data->num_stages_done); path = g_strdup_printf ("/org/gnome/control-center/user-accounts/print_ok.png"); gtk_image_set_from_resource (GTK_IMAGE (WID (name)), path); g_free (name); g_free (path); } if (g_str_equal (result, "enroll-completed")) { gtk_label_set_text (GTK_LABEL (WID ("status-label")), _("Done!")); gtk_label_set_text (GTK_LABEL (WID("enroll-label")), ""); gtk_assistant_set_page_complete (GTK_ASSISTANT (data->ass), WID ("page2"), TRUE); } if (done != FALSE) { enroll_stop (data, NULL); data->state = STATE_CLAIMED; if (g_str_equal (result, "enroll-completed") == FALSE) { /* The enrollment failed, restart it */ enroll_start (data, NULL); data->state = STATE_ENROLLING; result = "enroll-retry-scan"; } else { return; } } msg = g_strdup_printf (TR(enroll_result_str_to_msg (result, data->is_swipe)), data->name); gtk_label_set_text (GTK_LABEL (WID ("status-label")), msg); g_free (msg); } static void device_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, EnrollData *data) { if (strcmp (signal_name, "EnrollStatus") == 0) { if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sb)"))) { gchar *result; gboolean done; g_variant_get (parameters, "(&sb)", &result, &done); enroll_result (data, result, done); } } } static void assistant_prepare (GtkAssistant *ass, GtkWidget *page, EnrollData *data) { const char *name; name = g_object_get_data (G_OBJECT (page), "name"); if (name == NULL) return; if (g_str_equal (name, "enroll")) { GError *error = NULL; GtkBuilder *dialog = data->dialog; char *path; guint i; GVariant *result; gint num_enroll_stages; if (!claim (data, &error)) { GtkWidget *d; char *msg; /* translators: * The variable is the name of the device, for example: * "Could you not access "Digital Persona U.are.U 4000/4000B" device */ msg = g_strdup_printf (_("Could not access '%s' device"), data->name); d = get_error_dialog (msg, error->message, GTK_WINDOW (data->ass)); g_error_free (error); gtk_dialog_run (GTK_DIALOG (d)); gtk_widget_destroy (d); g_free (msg); enroll_data_destroy (data); return; } data->state = STATE_CLAIMED; result = g_dbus_connection_call_sync (connection, "net.reactivated.Fprint", g_dbus_proxy_get_object_path (data->device), "org.freedesktop.DBus.Properties", "Get", g_variant_new ("(ss)", "net.reactivated.Fprint.Device", "num-enroll-stages"), G_VARIANT_TYPE ("(v)"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); num_enroll_stages = 0; if (result) { GVariant *v; g_variant_get (result, "(v)", &v); num_enroll_stages = g_variant_get_int32 (v); g_variant_unref (result); g_variant_unref (v); } if (num_enroll_stages < 1) { GtkWidget *d; char *msg; /* translators: * The variable is the name of the device, for example: * "Could you not access "Digital Persona U.are.U 4000/4000B" device */ msg = g_strdup_printf (_("Could not access '%s' device"), data->name); d = get_error_dialog (msg, "net.reactivated.Fprint.Error.Internal", GTK_WINDOW (data->ass)); gtk_dialog_run (GTK_DIALOG (d)); gtk_widget_destroy (d); g_free (msg); enroll_data_destroy (data); return; } data->num_enroll_stages = num_enroll_stages; /* Hide the extra "bulbs" if not needed */ for (i = MAX_ENROLL_STAGES; i > data->num_enroll_stages; i--) { char *name; name = g_strdup_printf ("image%d", i); gtk_widget_hide (WID (name)); g_free (name); } /* And set the right image */ { path = g_strdup_printf ("/org/gnome/control-center/user-accounts/%s.png", data->finger); } for (i = 1; i <= data->num_enroll_stages; i++) { char *name; name = g_strdup_printf ("image%d", i); gtk_image_set_from_resource (GTK_IMAGE (WID (name)), path); g_free (name); } g_free (path); g_signal_connect (data->device, "g-signal", G_CALLBACK (device_signal_cb), data); if (!enroll_start (data, &error)) { GtkWidget *d; char *msg; /* translators: * The variable is the name of the device, for example: * "Could you not access "Digital Persona U.are.U 4000/4000B" device */ msg = g_strdup_printf (_("Could not start finger capture on '%s' device"), data->name); d = get_error_dialog (msg, error->message, GTK_WINDOW (data->ass)); g_error_free (error); gtk_dialog_run (GTK_DIALOG (d)); gtk_widget_destroy (d); g_free (msg); enroll_data_destroy (data); return; } data->state = STATE_ENROLLING;; } else { if (data->state == STATE_ENROLLING) { enroll_stop (data, NULL); data->state = STATE_CLAIMED; } if (data->state == STATE_CLAIMED) { release (data, NULL); data->state = STATE_NONE; } } } static void enroll_fingerprints (GtkWindow *parent, GtkWidget *label1, GtkWidget *label2, ActUser *user) { GDBusProxy *device; GtkBuilder *dialog; EnrollData *data; GtkWidget *ass; char *msg; GVariant *result; GError *error = NULL; device = NULL; if (manager == NULL) { create_manager (); if (manager != NULL) device = get_first_device (); } else { device = get_first_device (); } if (manager == NULL || device == NULL) { GtkWidget *d; d = get_error_dialog (_("Could not access any fingerprint readers"), _("Please contact your system administrator for help."), parent); gtk_dialog_run (GTK_DIALOG (d)); gtk_widget_destroy (d); return; } data = g_new0 (EnrollData, 1); data->device = device; data->label1 = label1; data->label2 = label2; /* Get some details about the device */ result = g_dbus_connection_call_sync (connection, "net.reactivated.Fprint", g_dbus_proxy_get_object_path (data->device), "org.freedesktop.DBus.Properties", "GetAll", g_variant_new ("(s)", "net.reactivated.Fprint.Device"), G_VARIANT_TYPE ("(a{sv})"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL); if (result) { GVariant *props; gchar *scan_type; g_variant_get (result, "(@a{sv})", &props); g_variant_lookup (props, "name", "s", &data->name); g_variant_lookup (props, "scan-type", "s", &scan_type); if (g_strcmp0 (scan_type, "swipe") == 0) data->is_swipe = TRUE; g_free (scan_type); g_variant_unref (props); g_variant_unref (result); } dialog = gtk_builder_new (); if (!gtk_builder_add_from_resource (dialog, "/org/gnome/control-center/user-accounts/account-fingerprint.ui", &error)) { g_error ("%s", error->message); g_error_free (error); return; } data->dialog = dialog; ass = WID ("assistant"); gtk_window_set_title (GTK_WINDOW (ass), _("Enable Fingerprint Login")); gtk_window_set_transient_for (GTK_WINDOW (ass), parent); gtk_window_set_modal (GTK_WINDOW (ass), TRUE); gtk_window_set_resizable (GTK_WINDOW (ass), FALSE); gtk_window_set_type_hint (GTK_WINDOW (ass), GDK_WINDOW_TYPE_HINT_DIALOG); g_signal_connect (G_OBJECT (ass), "cancel", G_CALLBACK (assistant_cancelled), data); g_signal_connect (G_OBJECT (ass), "close", G_CALLBACK (assistant_cancelled), data); g_signal_connect (G_OBJECT (ass), "prepare", G_CALLBACK (assistant_prepare), data); /* Page 1 */ gtk_combo_box_set_active (GTK_COMBO_BOX (WID ("finger_combobox")), 0); g_signal_connect (G_OBJECT (WID ("radiobutton1")), "toggled", G_CALLBACK (finger_radio_button_toggled), data); g_signal_connect (G_OBJECT (WID ("radiobutton2")), "toggled", G_CALLBACK (finger_radio_button_toggled), data); g_signal_connect (G_OBJECT (WID ("radiobutton3")), "toggled", G_CALLBACK (finger_radio_button_toggled), data); g_signal_connect (G_OBJECT (WID ("finger_combobox")), "changed", G_CALLBACK (finger_combobox_changed), data); data->finger = selected_finger (dialog); g_object_set_data (G_OBJECT (WID("page1")), "name", "intro"); /* translators: * The variable is the name of the device, for example: * "To enable fingerprint login, you need to save one of your fingerprints, using the * 'Digital Persona U.are.U 4000/4000B' device." */ msg = g_strdup_printf (_("To enable fingerprint login, you need to save one of your fingerprints, using the '%s' device."), data->name); gtk_label_set_text (GTK_LABEL (WID("intro-label")), msg); g_free (msg); gtk_assistant_set_page_complete (GTK_ASSISTANT (ass), WID("page1"), TRUE); gtk_assistant_set_page_title (GTK_ASSISTANT (ass), WID("page1"), _("Selecting finger")); gtk_assistant_set_page_title (GTK_ASSISTANT (ass), WID("page2"), _("Enrolling fingerprints")); gtk_assistant_set_page_title (GTK_ASSISTANT (ass), WID("page3"), _("Summary")); /* Page 2 */ g_object_set_data (G_OBJECT (WID("page2")), "name", "enroll"); msg = g_strdup_printf (TR(finger_str_to_msg (data->finger, data->is_swipe)), data->name); gtk_label_set_text (GTK_LABEL (WID("enroll-label")), msg); g_free (msg); /* Page 3 */ g_object_set_data (G_OBJECT (WID("page3")), "name", "summary"); data->ass = ass; gtk_widget_show_all (ass); } void fingerprint_button_clicked (GtkWindow *parent, GtkWidget *label1, GtkWidget *label2, ActUser *user) { bindtextdomain ("fprintd", GNOMELOCALEDIR); bind_textdomain_codeset ("fprintd", "UTF-8"); if (is_disable != FALSE) { delete_fingerprints_question (parent, label1, label2, user); } else { enroll_fingerprints (parent, label1, label2, user); } }