diff --git a/src/cm-dinsig.c b/src/cm-dinsig.c index a8d1e14..3d9ba71 100644 --- a/src/cm-dinsig.c +++ b/src/cm-dinsig.c @@ -1,374 +1,384 @@ /* cm-dinsig.c - Widget to show information about a DINSIG card * Copyright (C) 2009 g10 Code GmbH * * This file is part of GPA. * - * GPA 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 3 of the License, or - * (at your option) any later version. + * GPA is free software; you can redistribute and/or modify this part + * of GPA under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. * * GPA 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, see . */ /* DINSIG is the old and still used standard for smartcards to create qualified signatures. We provide this widget because modern German banking cards are prepared for that and due to the application precedence rules of Scdaemon this application will show up and not the Geldkarte which is also available on those cards. */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include "gpa.h" #include "gtktools.h" #include "convert.h" #include "cm-object.h" #include "cm-dinsig.h" /* Identifiers for the entry fields. */ enum { ENTRY_SERIALNO, ENTRY_LAST }; /* Object's class definition. */ struct _GpaCMDinsigClass { GpaCMObjectClass parent_class; }; /* Object definition. */ struct _GpaCMDinsig { GpaCMObject parent_instance; GtkWidget *warning_frame; /* The frame used to display warnings etc. */ GtkWidget *entries[ENTRY_LAST]; int reloading; /* Sentinel to avoid recursive reloads. */ }; /* The parent class. */ static GObjectClass *parent_class; /* Local prototypes */ static void gpa_cm_dinsig_finalize (GObject *object); /************************************************************ ******************* Implementation ********************* ************************************************************/ /* Clears the info contained in the card widget. */ static void clear_card_data (GpaCMDinsig *card) { int idx; for (idx=0; idx < ENTRY_LAST; idx++) gtk_label_set_text (GTK_LABEL (card->entries[idx]), ""); } struct scd_getattr_parm { GpaCMDinsig *card; /* The object. */ const char *name; /* Name of expected attribute. */ int entry_id; /* The identifier for the entry. */ void (*updfnc) (GpaCMDinsig *card, int entry_id, char *string); }; static gpg_error_t scd_getattr_cb (void *opaque, const char *status, const char *args) { struct scd_getattr_parm *parm = opaque; int entry_id; /* g_debug ("STATUS_CB: status=`%s' args=`%s'", status, args); */ if (!strcmp (status, parm->name) ) { entry_id = parm->entry_id; if (entry_id < ENTRY_LAST) { char *tmp = xstrdup (args); percent_unescape (tmp, 1); if (parm->updfnc) parm->updfnc (parm->card, entry_id, tmp); else if (GTK_IS_LABEL (parm->card->entries[entry_id])) gtk_label_set_text (GTK_LABEL (parm->card->entries[entry_id]), tmp); else gtk_entry_set_text (GTK_ENTRY (parm->card->entries[entry_id]), tmp); xfree (tmp); } } return 0; } /* Use the assuan machinery to load the bulk of the OpenPGP card data. */ static void reload_data (GpaCMDinsig *card) { static struct { const char *name; int entry_id; void (*updfnc) (GpaCMDinsig *card, int entry_id, char *string); } attrtbl[] = { { "SERIALNO", ENTRY_SERIALNO }, { NULL } }; int attridx; gpg_error_t err, operr; char command[100]; struct scd_getattr_parm parm; gpgme_ctx_t gpgagent; gpgagent = GPA_CM_OBJECT (card)->agent_ctx; g_return_if_fail (gpgagent); card->reloading++; parm.card = card; for (attridx=0; attrtbl[attridx].name; attridx++) { parm.name = attrtbl[attridx].name; parm.entry_id = attrtbl[attridx].entry_id; parm.updfnc = attrtbl[attridx].updfnc; snprintf (command, sizeof command, "SCD GETATTR %s", parm.name); err = gpgme_op_assuan_transact_ext (gpgagent, command, NULL, NULL, NULL, NULL, scd_getattr_cb, &parm, &operr); if (!err) err = operr; if (err) { if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT) ; /* Lost the card. */ else { g_debug ("assuan command `%s' failed: %s <%s>\n", command, gpg_strerror (err), gpg_strsource (err)); } clear_card_data (card); break; } } card->reloading--; } /* Helper for construct_data_widget. Returns the label widget. */ static GtkLabel * add_table_row (GtkWidget *table, int *rowidx, const char *labelstr, GtkWidget *widget, GtkWidget *widget2, int readonly) { GtkWidget *label; int is_label = GTK_IS_LABEL (widget); label = gtk_label_new (labelstr); gtk_label_set_width_chars (GTK_LABEL (label), 22); gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 0, 1, *rowidx, *rowidx + 1, GTK_FILL, GTK_SHRINK, 0, 0); if (is_label) gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5); if (readonly) { if (!is_label && GTK_IS_ENTRY (widget)) { gtk_entry_set_has_frame (GTK_ENTRY (widget), FALSE); gtk_entry_set_editable (GTK_ENTRY (widget), FALSE); } } else { if (is_label) gtk_label_set_selectable (GTK_LABEL (widget), TRUE); } gtk_table_attach (GTK_TABLE (table), widget, 1, 2, *rowidx, *rowidx + 1, GTK_FILL, GTK_SHRINK, 0, 0); if (widget2) gtk_table_attach (GTK_TABLE (table), widget2, 2, 3, *rowidx, *rowidx + 1, GTK_FILL, GTK_SHRINK, 5, 0); ++*rowidx; return GTK_LABEL (label); } /* This function constructs the container holding all widgets making up this data widget. It is called during instance creation. */ static void construct_data_widget (GpaCMDinsig *card) { GtkWidget *frame; GtkWidget *table; GtkWidget *vbox; GtkWidget *label; int rowidx; char *text; /* General frame. */ frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE); label = gtk_label_new (_("General")); gtk_label_set_use_markup (GTK_LABEL (label), TRUE); gtk_frame_set_label_widget (GTK_FRAME (frame), label); table = gtk_table_new (2, 3, FALSE); gtk_container_set_border_width (GTK_CONTAINER (table), 10); gtk_container_add (GTK_CONTAINER (frame), table); rowidx = 0; card->entries[ENTRY_SERIALNO] = gtk_label_new (NULL); add_table_row (table, &rowidx, _("Serial number:"), card->entries[ENTRY_SERIALNO], NULL, 0); gtk_box_pack_start (GTK_BOX (card), frame, FALSE, TRUE, 0); /* Info frame. */ frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE); vbox = gtk_vbox_new (FALSE, 5); text = g_strdup_printf (_("There is not much information to display for a %s card. " "You may want to use the application selector button to " "switch to another application available on this card."), "DINSIG"); label = gtk_label_new (text); gtk_label_set_use_markup (GTK_LABEL (label), TRUE); gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0); gtk_container_add (GTK_CONTAINER (frame), vbox); gtk_box_pack_start (GTK_BOX (card), frame, FALSE, TRUE, 0); } /************************************************************ ****************** Object Management ******************** ************************************************************/ static void gpa_cm_dinsig_class_init (void *class_ptr, void *class_data) { GpaCMDinsigClass *klass = class_ptr; parent_class = g_type_class_peek_parent (klass); G_OBJECT_CLASS (klass)->finalize = gpa_cm_dinsig_finalize; } static void gpa_cm_dinsig_init (GTypeInstance *instance, void *class_ptr) { GpaCMDinsig *card = GPA_CM_DINSIG (instance); construct_data_widget (card); } static void gpa_cm_dinsig_finalize (GObject *object) { /* GpaCMDinsig *card = GPA_CM_DINSIG (object); */ parent_class->finalize (object); } /* Construct the class. */ GType gpa_cm_dinsig_get_type (void) { static GType this_type = 0; if (!this_type) { static const GTypeInfo this_info = { sizeof (GpaCMDinsigClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, gpa_cm_dinsig_class_init, (GClassFinalizeFunc) NULL, NULL, /* class_data */ sizeof (GpaCMDinsig), 0, /* n_preallocs */ gpa_cm_dinsig_init }; this_type = g_type_register_static (GPA_CM_OBJECT_TYPE, "GpaCMDinsig", &this_info, 0); } return this_type; } /************************************************************ ********************** Public API ************************ ************************************************************/ GtkWidget * gpa_cm_dinsig_new () { return GTK_WIDGET (g_object_new (GPA_CM_DINSIG_TYPE, NULL)); } /* If WIDGET is of Type GpaCMDinsig do a data reload through the assuan connection. */ void gpa_cm_dinsig_reload (GtkWidget *widget, gpgme_ctx_t gpgagent) { if (GPA_IS_CM_DINSIG (widget)) { GPA_CM_OBJECT (widget)->agent_ctx = gpgagent; if (gpgagent) reload_data (GPA_CM_DINSIG (widget)); } } diff --git a/src/cm-dinsig.h b/src/cm-dinsig.h index 35ed749..a77b601 100644 --- a/src/cm-dinsig.h +++ b/src/cm-dinsig.h @@ -1,57 +1,72 @@ /* cm-dinsig.h - Widget to show information about a DINSIG card. * Copyright (C) 2009 g10 Code GmbH * * This file is part of GPA. * + * GPA is free software; you can redistribute and/or modify this part + * of GPA under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * * GPA 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 3 of the License, or * (at your option) any later version. * * GPA 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, see . + * along with this program; if not, see . */ #ifndef CM_DINSIG_H #define CM_DINSIG_H #include /* Declare the Object. */ typedef struct _GpaCMDinsig GpaCMDinsig; typedef struct _GpaCMDinsigClass GpaCMDinsigClass; GType gpa_cm_dinsig_get_type (void) G_GNUC_CONST; #define GPA_CM_DINSIG_TYPE (gpa_cm_dinsig_get_type ()) #define GPA_CM_DINSIG(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GPA_CM_DINSIG_TYPE, GpaCMDinsig)) #define GPA_CM_DINSIG_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ GPA_CM_DINSIG_TYPE, GpaCMDinsigClass)) #define GPA_IS_CM_DINSIG(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GPA_CM_DINSIG_TYPE)) #define GPA_IS_CM_DINSIG_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), GPA_CM_DINSIG_TYPE)) #define GPA_CM_DINSIG_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ GPA_CM_DINSIG_TYPE, GpaCMDinsigClass)) /* The class specific API. */ GtkWidget *gpa_cm_dinsig_new (void); void gpa_cm_dinsig_reload (GtkWidget *widget, gpgme_ctx_t gpgagent); #endif /*CM_DINSIG_H*/ diff --git a/src/cm-geldkarte.c b/src/cm-geldkarte.c index 577b4c8..4c56abe 100644 --- a/src/cm-geldkarte.c +++ b/src/cm-geldkarte.c @@ -1,393 +1,403 @@ /* cm-geldkarte.c - Widget to show information about a Geldkarte. * Copyright (C) 2009 g10 Code GmbH * * This file is part of GPA. * - * GPA 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 3 of the License, or - * (at your option) any later version. + * GPA is free software; you can redistribute and/or modify this part + * of GPA under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. * * GPA 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, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include "gpa.h" #include "convert.h" #include "cm-object.h" #include "cm-geldkarte.h" /* Identifiers for the entry fields. */ enum { ENTRY_KBLZ, ENTRY_BANKTYPE, ENTRY_CARDNO, ENTRY_EXPIRES, ENTRY_VALIDFROM, ENTRY_COUNTRY, ENTRY_CURRENCY, ENTRY_ZKACHIPID, ENTRY_OSVERSION, ENTRY_BALANCE, ENTRY_MAXAMOUNT, ENTRY_MAXAMOUNT1, ENTRY_LAST }; /* Object's class definition. */ struct _GpaCMGeldkarteClass { GpaCMObjectClass parent_class; }; /* Object definition. */ struct _GpaCMGeldkarte { GpaCMObject parent_instance; GtkWidget *amount_frame; GtkWidget *general_frame; GtkWidget *entries[ENTRY_LAST]; }; /* The parent class. */ static GObjectClass *parent_class; /* Local prototypes */ static void gpa_cm_geldkarte_finalize (GObject *object); /************************************************************ ******************* Implementation ********************* ************************************************************/ /* Clears the info contained in the card widget. */ static void clear_card_data (GpaCMGeldkarte *card) { int idx; for (idx=0; idx < ENTRY_LAST; idx++) gtk_label_set_text (GTK_LABEL (card->entries[idx]), ""); } struct scd_getattr_parm { GpaCMGeldkarte *card; /* The object. */ const char *name; /* Name of expected attribute. */ int entry_id; /* The identifier for the entry. */ void (*updfnc) (GpaCMGeldkarte *card, int entry_id, const char *string); }; static gpg_error_t scd_getattr_cb (void *opaque, const char *status, const char *args) { struct scd_getattr_parm *parm = opaque; int entry_id; /* g_debug ("STATUS_CB: status=`%s' args=`%s'", status, args); */ if (!strcmp (status, parm->name) ) { entry_id = parm->entry_id; if (entry_id < ENTRY_LAST) { if (parm->updfnc) parm->updfnc (parm->card, entry_id, args); else gtk_label_set_text (GTK_LABEL (parm->card->entries[entry_id]), args); } } return 0; } /* Use the assuan machinery to load the bulk of the OpenPGP card data. */ static void reload_data (GpaCMGeldkarte *card, gpgme_ctx_t gpgagent) { static struct { const char *name; int entry_id; void (*updfnc) (GpaCMGeldkarte *card, int entry_id, const char *string); } attrtbl[] = { { "X-KBLZ", ENTRY_KBLZ }, { "X-BANKINFO", ENTRY_BANKTYPE }, { "X-CARDNO", ENTRY_CARDNO }, { "X-EXPIRES", ENTRY_EXPIRES }, { "X-VALIDFROM", ENTRY_VALIDFROM }, { "X-COUNTRY", ENTRY_COUNTRY }, { "X-CURRENCY", ENTRY_CURRENCY }, { "X-ZKACHIPID", ENTRY_ZKACHIPID }, { "X-OSVERSION", ENTRY_OSVERSION }, { "X-BALANCE", ENTRY_BALANCE }, { "X-MAXAMOUNT", ENTRY_MAXAMOUNT }, { "X-MAXAMOUNT1",ENTRY_MAXAMOUNT1 }, { NULL } }; int attridx; gpg_error_t err, operr; char command[100]; struct scd_getattr_parm parm; parm.card = card; for (attridx=0; attrtbl[attridx].name; attridx++) { parm.name = attrtbl[attridx].name; parm.entry_id = attrtbl[attridx].entry_id; parm.updfnc = attrtbl[attridx].updfnc; snprintf (command, sizeof command, "SCD GETATTR %s", parm.name); err = gpgme_op_assuan_transact_ext (gpgagent, command, NULL, NULL, NULL, NULL, scd_getattr_cb, &parm, &operr); if (!err) err = operr; if (err) { if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT) ; /* Lost the card. */ else { g_debug ("assuan command `%s' failed: %s <%s>\n", command, gpg_strerror (err), gpg_strsource (err)); } clear_card_data (card); break; } } } /* Helper for construct_data_widget. */ static GtkWidget * add_table_row (GtkWidget *table, int *rowidx, const char *labelstr) { GtkWidget *widget; GtkWidget *label; widget = gtk_label_new (NULL); label = gtk_label_new (labelstr); gtk_label_set_width_chars (GTK_LABEL (label), 22); gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 0, 1, *rowidx, *rowidx + 1, GTK_FILL, GTK_SHRINK, 0, 0); gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5); gtk_label_set_selectable (GTK_LABEL (widget), TRUE); gtk_table_attach (GTK_TABLE (table), widget, 1, 2, *rowidx, *rowidx + 1, GTK_FILL, GTK_SHRINK, 0, 0); ++*rowidx; return widget; } /* This function constructs the container holding all widgets making up this data widget. It is called during instance creation. */ static void construct_data_widget (GpaCMGeldkarte *card) { GtkWidget *amount_frame; GtkWidget *amount_table; GtkWidget *general_frame; GtkWidget *general_table; GtkWidget *label; int rowidx; /* Create frames and tables. */ general_frame = card->general_frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (general_frame), GTK_SHADOW_NONE); label = gtk_label_new (_("General")); gtk_label_set_use_markup (GTK_LABEL (label), TRUE); gtk_frame_set_label_widget (GTK_FRAME (general_frame), label); general_table = gtk_table_new (9, 3, FALSE); gtk_container_set_border_width (GTK_CONTAINER (general_table), 10); amount_frame = card->amount_frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (amount_frame), GTK_SHADOW_NONE); label = gtk_label_new (_("Amount")); gtk_label_set_use_markup (GTK_LABEL (label), TRUE); gtk_frame_set_label_widget (GTK_FRAME (amount_frame), label); amount_table = gtk_table_new (3, 3, FALSE); gtk_container_set_border_width (GTK_CONTAINER (amount_table), 10); /* General frame. */ rowidx = 0; card->entries[ENTRY_CARDNO] = add_table_row (general_table, &rowidx, _("Card number: ")); card->entries[ENTRY_KBLZ] = add_table_row (general_table, &rowidx, _("Short Bank Code number: ")); card->entries[ENTRY_BANKTYPE] = add_table_row (general_table, &rowidx, _("Bank type: ")); card->entries[ENTRY_VALIDFROM] = add_table_row (general_table, &rowidx, _("Card valid from: ")); card->entries[ENTRY_EXPIRES] = add_table_row (general_table, &rowidx, _("Card expires: ")); card->entries[ENTRY_COUNTRY] = add_table_row (general_table, &rowidx, _("Issuing country: ")); card->entries[ENTRY_CURRENCY] = add_table_row (general_table, &rowidx, _("Currency: ")); card->entries[ENTRY_ZKACHIPID] = add_table_row (general_table, &rowidx, _("ZKA chip Id: ")); card->entries[ENTRY_OSVERSION] = add_table_row (general_table, &rowidx, _("Chip OS version: ")); gtk_container_add (GTK_CONTAINER (general_frame), general_table); /* Amount frame. */ rowidx = 0; card->entries[ENTRY_BALANCE] = add_table_row (amount_table, &rowidx, _("Balance: ")); card->entries[ENTRY_MAXAMOUNT] = add_table_row (amount_table, &rowidx, _("General limit: ")); card->entries[ENTRY_MAXAMOUNT1] = add_table_row (amount_table, &rowidx, _("Transaction limit: ")); gtk_container_add (GTK_CONTAINER (amount_frame), amount_table); /* Put all frames together. */ gtk_box_pack_start (GTK_BOX (card), amount_frame, FALSE, TRUE, 0); gtk_box_pack_start (GTK_BOX (card), general_frame, FALSE, TRUE, 0); } /************************************************************ ****************** Object Management ******************** ************************************************************/ static void gpa_cm_geldkarte_class_init (void *class_ptr, void *class_data) { GpaCMGeldkarteClass *klass = class_ptr; parent_class = g_type_class_peek_parent (klass); G_OBJECT_CLASS (klass)->finalize = gpa_cm_geldkarte_finalize; } static void gpa_cm_geldkarte_init (GTypeInstance *instance, void *class_ptr) { GpaCMGeldkarte *card = GPA_CM_GELDKARTE (instance); construct_data_widget (card); } static void gpa_cm_geldkarte_finalize (GObject *object) { /* GpaCMGeldkarte *card = GPA_CM_GELDKARTE (object); */ parent_class->finalize (object); } /* Construct the class. */ GType gpa_cm_geldkarte_get_type (void) { static GType this_type = 0; if (!this_type) { static const GTypeInfo this_info = { sizeof (GpaCMGeldkarteClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, gpa_cm_geldkarte_class_init, (GClassFinalizeFunc) NULL, NULL, /* class_data */ sizeof (GpaCMGeldkarte), 0, /* n_preallocs */ gpa_cm_geldkarte_init }; this_type = g_type_register_static (GPA_CM_OBJECT_TYPE, "GpaCMGeldkarte", &this_info, 0); } return this_type; } /************************************************************ ********************** Public API ************************ ************************************************************/ GtkWidget * gpa_cm_geldkarte_new () { return GTK_WIDGET (g_object_new (GPA_CM_GELDKARTE_TYPE, NULL)); } /* If WIDGET is of Type GpaCMGeldkarte do a data reload through the assuan connection. */ void gpa_cm_geldkarte_reload (GtkWidget *widget, gpgme_ctx_t gpgagent) { if (GPA_IS_CM_GELDKARTE (widget)) reload_data (GPA_CM_GELDKARTE (widget), gpgagent); } diff --git a/src/cm-geldkarte.h b/src/cm-geldkarte.h index 6fff652..d8ab6bf 100644 --- a/src/cm-geldkarte.h +++ b/src/cm-geldkarte.h @@ -1,57 +1,67 @@ /* cm-geldkarte.h - Widget to show information about a Geldkarte. * Copyright (C) 2009 g10 Code GmbH * * This file is part of GPA. * - * GPA 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 3 of the License, or - * (at your option) any later version. + * GPA is free software; you can redistribute and/or modify this part + * of GPA under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. * * GPA 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, see . + * along with this program; if not, see . */ #ifndef CM_GELDKARTE_H #define CM_GELDKARTE_H #include /* Declare the Object. */ typedef struct _GpaCMGeldkarte GpaCMGeldkarte; typedef struct _GpaCMGeldkarteClass GpaCMGeldkarteClass; GType gpa_cm_geldkarte_get_type (void) G_GNUC_CONST; #define GPA_CM_GELDKARTE_TYPE (gpa_cm_geldkarte_get_type ()) #define GPA_CM_GELDKARTE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GPA_CM_GELDKARTE_TYPE, GpaCMGeldkarte)) #define GPA_CM_GELDKARTE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ GPA_CM_GELDKARTE_TYPE, GpaCMGeldkarteClass)) #define GPA_IS_CM_GELDKARTE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GPA_CM_GELDKARTE_TYPE)) #define GPA_IS_CM_GELDKARTE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), GPA_CM_GELDKARTE_TYPE)) #define GPA_CM_GELDKARTE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ GPA_CM_GELDKARTE_TYPE, GpaCMGeldkarteClass)) /* The class specific API. */ GtkWidget *gpa_cm_geldkarte_new (void); void gpa_cm_geldkarte_reload (GtkWidget *widget, gpgme_ctx_t gpgagent); #endif /*CM_GELDKARTE_H*/ diff --git a/src/cm-netkey.c b/src/cm-netkey.c index 5f027cc..405c105 100644 --- a/src/cm-netkey.c +++ b/src/cm-netkey.c @@ -1,1085 +1,1095 @@ /* cm-netkey.c - Widget to show information about a Netkey card. * Copyright (C) 2009 g10 Code GmbH * * This file is part of GPA. * - * GPA 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 3 of the License, or - * (at your option) any later version. + * GPA is free software; you can redistribute and/or modify this part + * of GPA under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. * * GPA 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, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include "gpa.h" #include "gtktools.h" #include "convert.h" #include "gpa-key-details.h" #include "cm-object.h" #include "cm-netkey.h" /* Identifiers for the entry fields. */ enum { ENTRY_SERIALNO, ENTRY_NKS_VERSION, ENTRY_PIN_RETRYCOUNTER, ENTRY_PUK_RETRYCOUNTER, ENTRY_SIGG_PIN_RETRYCOUNTER, ENTRY_SIGG_PUK_RETRYCOUNTER, ENTRY_LAST }; /* A structure for PIN information. */ struct pininfo_s { int valid; /* Valid information. */ int nullpin; /* The NullPIN is activ. */ int blocked; /* The PIN is blocked. */ int nopin; /* No such PIN. */ int tries_left; /* How many verification tries are left. */ }; /* Object's class definition. */ struct _GpaCMNetkeyClass { GpaCMObjectClass parent_class; }; /* Object definition. */ struct _GpaCMNetkey { GpaCMObject parent_instance; GtkWidget *warning_frame; /* The frame used to display warnings etc. */ GtkWidget *keys_frame; /* The frame containing the keys. */ GtkWidget *entries[ENTRY_LAST]; GtkWidget *change_pin_btn; /* The button to change the PIN for NKS. */ GtkWidget *change_puk_btn; /* The button to change the PUK for NKS. */ GtkWidget *change_sigg_pin_btn; /* The button to change the PIN for SigG. */ GtkWidget *change_sigg_puk_btn; /* The button to change the PUK for SigG. */ /* Information about the PINs. */ struct pininfo_s pininfo[4]; int reloading; /* Sentinel to avoid recursive reloads. */ }; /* The parent class. */ static GObjectClass *parent_class; /* Local prototypes */ static void learn_keys_clicked_cb (GtkButton *widget, void *user_data); static void gpa_cm_netkey_finalize (GObject *object); /************************************************************ ******************* Implementation ********************* ************************************************************/ /* Clears the info contained in the card widget. */ static void clear_card_data (GpaCMNetkey *card) { int idx; for (idx=0; idx < ENTRY_LAST; idx++) if (GTK_IS_LABEL (card->entries[idx])) gtk_label_set_text (GTK_LABEL (card->entries[idx]), ""); else gtk_entry_set_text (GTK_ENTRY (card->entries[idx]), ""); } /* Put the PIN information into the field with ENTRY_ID. If BUTTON is not NULL its sensitivity is set as well. */ static void update_entry_retry_counter (GpaCMNetkey *card, int entry_id, struct pininfo_s *info, int any_isnull, int is_puk, GtkWidget *button) { char numbuf[50]; const char *string; if (!info->valid) string = _("unknown"); else if (info->nullpin) string = "NullPIN"; else if (info->blocked) string = _("blocked"); else if (info->nopin) string = "n/a"; else { snprintf (numbuf, sizeof numbuf, "%d", info->tries_left); string = numbuf; } gtk_label_set_text (GTK_LABEL (card->entries[entry_id]), string); if (button) { gtk_button_set_label (GTK_BUTTON (button), (info->valid && !info->nullpin && (info->blocked || !info->tries_left)) ? (is_puk? _("Reset PUK") : _("Reset PIN")) : (is_puk? _("Change PUK"): _("Change PIN"))); gtk_widget_set_sensitive (button, (info->valid && !any_isnull && !info->nullpin && !info->blocked && !info->nopin)); } } /* Update the CHV status fields. This function may modify STRING. */ static void update_entry_chv_status (GpaCMNetkey *card, int entry_id, char *string) { struct { int info_idx; int is_puk; int entry_id; GtkWidget *widget; } tbl[] = { { 0, 0, ENTRY_PIN_RETRYCOUNTER }, { 1, 1, ENTRY_PUK_RETRYCOUNTER }, { 2, 0, ENTRY_SIGG_PIN_RETRYCOUNTER }, { 3, 1, ENTRY_SIGG_PUK_RETRYCOUNTER }, { -1, 0 } }; int idx; int any_isnull = 0; tbl[0].widget = card->change_pin_btn; tbl[1].widget = card->change_puk_btn; tbl[2].widget = card->change_sigg_pin_btn; tbl[3].widget = card->change_sigg_puk_btn; (void)entry_id; /* Not used. */ for (idx=0; idx < DIM (card->pininfo); idx++) memset (&card->pininfo[idx], 0, sizeof card->pininfo[0]); while (spacep (string)) string++; for (idx=0; *string && idx < DIM (card->pininfo); idx++) { int value = atoi (string); while (*string && !spacep (string)) string++; while (spacep (string)) string++; card->pininfo[idx].valid = 1; if (value >= 0) card->pininfo[idx].tries_left = value; else if (value == -4) { card->pininfo[idx].nullpin = 1; any_isnull = 1; } else if (value == -3) card->pininfo[idx].blocked = 1; else if (value == -2) card->pininfo[idx].nopin = 1; else card->pininfo[idx].valid = 0; } for (idx=0; tbl[idx].info_idx != -1; idx++) if (tbl[idx].info_idx < DIM (card->pininfo)) update_entry_retry_counter (card, tbl[idx].entry_id, &card->pininfo[tbl[idx].info_idx], any_isnull, tbl[idx].is_puk, tbl[idx].widget); gtk_widget_set_no_show_all (card->warning_frame, FALSE); if (any_isnull) gtk_widget_show_all (card->warning_frame); else { gtk_widget_hide_all (card->warning_frame); gtk_widget_set_no_show_all (card->warning_frame, TRUE); } } /* Structure form comminucation between reload_more_data and reload_more_data_cb. */ struct reload_more_data_parm { GpaCMNetkey *card; /* self */ gpgme_ctx_t ctx; /* A prepared context for relaod_more_data_cb. */ int any_unknown; /* Set if at least one key is not known. */ }; /* Helper for relaod_more_data. This is actually an Assuan status callback */ static gpg_error_t reload_more_data_cb (void *opaque, const char *status, const char *args) { struct reload_more_data_parm *parm = opaque; gpgme_key_t key = NULL; const char *s; char pattern[100]; int idx; gpg_error_t err; const char *keyid; int any = 0; g_debug (" reload_more_data (%s=%s)", status, args); if (strcmp (status, "KEYPAIRINFO") ) return 0; g_debug (" start search"); idx = 0; pattern[idx++] = '&'; for (s=args; hexdigitp (s) && idx < sizeof pattern - 1; s++) pattern[idx++] = *s; pattern[idx] = 0; if (!spacep (s) || (s - args != 40)) return 0; /* Invalid formatted keygrip. */ while (spacep (s)) s++; keyid = s; if (!(err=gpgme_op_keylist_start (parm->ctx, pattern, 0))) { if (!(err=gpgme_op_keylist_next (parm->ctx, &key))) { GtkWidget *vbox, *expander, *details, *hbox, *label; vbox = gtk_bin_get_child (GTK_BIN (parm->card->keys_frame)); if (!vbox) g_debug ("Ooops, vbox missing in key frame"); else { expander = gtk_expander_new (keyid); details = gpa_key_details_new (); gtk_container_add (GTK_CONTAINER (expander), details); gpa_key_details_update (details, key, 1); hbox = gtk_hbox_new (FALSE, 0); label = gtk_label_new (NULL); gtk_label_set_width_chars (GTK_LABEL (label), 22); gtk_misc_set_alignment (GTK_MISC (label), 0, 0); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (hbox), expander, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); any = 1; } gpgme_key_unref (key); } } gpgme_op_keylist_end (parm->ctx); if (!any) parm->any_unknown = 1; g_debug (" ready"); return 0; } /* Reload more data. This function is called from the idle handler. */ static void reload_more_data (GpaCMNetkey *card) { gpg_error_t err, operr; gpgme_ctx_t gpgagent; GtkWidget *vbox; struct reload_more_data_parm parm; g_debug ("start reload_more_data (count=%d)", card->reloading); gpgagent = GPA_CM_OBJECT (card)->agent_ctx; g_return_if_fail (gpgagent); g_return_if_fail (card->keys_frame); g_debug (" gpgagent=%p", gpgagent); /* We remove any existing children of the keys frame and then we add a new vbox to be filled with new widgets by the callback. */ vbox = gtk_bin_get_child (GTK_BIN (card->keys_frame)); if (vbox) gtk_widget_destroy (vbox); vbox = gtk_vbox_new (FALSE, 5); gtk_container_add (GTK_CONTAINER (card->keys_frame), vbox); /* Create a context for key listings. */ parm.any_unknown = 0; parm.card = card; err = gpgme_new (&parm.ctx); if (err) { /* We don't want an error window because we are run from an idle handler and the information is not that important. */ g_debug ("failed to create a context: %s", gpg_strerror (err)); return; } gpgme_set_protocol (parm.ctx, GPGME_PROTOCOL_CMS); /* We include ephemeral keys in the listing. */ gpgme_set_keylist_mode (parm.ctx, GPGME_KEYLIST_MODE_EPHEMERAL); g_debug (" parm.ctx=%p", parm.ctx); err = gpgme_op_assuan_transact_ext (gpgagent, "SCD LEARN --keypairinfo", NULL, NULL, NULL, NULL, reload_more_data_cb, &parm, &operr); g_debug (" assuan ret=%d", err); if (!err) err = operr; if (err) g_debug ("SCD LEARN failed: %s", gpg_strerror (err)); if (parm.any_unknown) { GtkWidget *button, *align; align = gtk_alignment_new (0.5, 0, 0, 0); button = gtk_button_new_with_label (_("Learn keys")); gpa_add_tooltip (button, _("For some or all of the keys available on the card, " "the GnuPG crypto engine does not yet know the " "corresponding certificates.\n" "\n" "If you click this button, GnuPG will be asked to " "\"learn\" " "this card and import all certificates stored on the " "card into its own certificate store. This is not done " "automatically because it may take several seconds to " "read all certificates from the card.\n" "\n" "If you are unsure what to do, just click the button.")); gtk_container_add (GTK_CONTAINER (align), button); gtk_box_pack_start (GTK_BOX (vbox), align, FALSE, FALSE, 5); gtk_box_reorder_child (GTK_BOX (vbox), align, 0); g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (learn_keys_clicked_cb), card); } gpgme_release (parm.ctx); gtk_widget_show_all (card->keys_frame); g_debug ("end reload_more_data (count=%d)", card->reloading); return; } /* Idle queue callback to reload more data. */ static gboolean reload_more_data_idle_cb (void *user_data) { GpaCMNetkey *card = user_data; if (card->reloading) { g_debug ("already reloading (count=%d)", card->reloading); return TRUE; /* Just in case we are still reloading, wait for the next idle slot. */ } card->reloading++; reload_more_data (card); g_object_unref (card); card->reloading--; return FALSE; /* Remove us from the idle queue. */ } struct scd_getattr_parm { GpaCMNetkey *card; /* The object. */ const char *name; /* Name of expected attribute. */ int entry_id; /* The identifier for the entry. */ void (*updfnc) (GpaCMNetkey *card, int entry_id, char *string); }; static gpg_error_t scd_getattr_cb (void *opaque, const char *status, const char *args) { struct scd_getattr_parm *parm = opaque; int entry_id; /* g_debug ("STATUS_CB: status=`%s' args=`%s'", status, args); */ if (!strcmp (status, parm->name) ) { entry_id = parm->entry_id; if (entry_id < ENTRY_LAST) { char *tmp = xstrdup (args); percent_unescape (tmp, 1); if (parm->updfnc) parm->updfnc (parm->card, entry_id, tmp); else if (GTK_IS_LABEL (parm->card->entries[entry_id])) gtk_label_set_text (GTK_LABEL (parm->card->entries[entry_id]), tmp); else gtk_entry_set_text (GTK_ENTRY (parm->card->entries[entry_id]), tmp); xfree (tmp); } } return 0; } /* Use the assuan machinery to load the bulk of the OpenPGP card data. */ static void reload_data (GpaCMNetkey *card) { static struct { const char *name; int entry_id; void (*updfnc) (GpaCMNetkey *card, int entry_id, char *string); } attrtbl[] = { { "SERIALNO", ENTRY_SERIALNO }, { "NKS-VERSION", ENTRY_NKS_VERSION }, { "CHV-STATUS", ENTRY_PIN_RETRYCOUNTER, update_entry_chv_status }, { NULL } }; int attridx; gpg_error_t err = 0; gpg_error_t operr; char command[100]; struct scd_getattr_parm parm; gpgme_ctx_t gpgagent; gpgagent = GPA_CM_OBJECT (card)->agent_ctx; g_return_if_fail (gpgagent); card->reloading++; g_debug ("uped reloading counter (count=%d)", card->reloading); /* Show all attributes. */ parm.card = card; for (attridx=0; attrtbl[attridx].name; attridx++) { parm.name = attrtbl[attridx].name; parm.entry_id = attrtbl[attridx].entry_id; parm.updfnc = attrtbl[attridx].updfnc; snprintf (command, sizeof command, "SCD GETATTR %s", parm.name); err = gpgme_op_assuan_transact_ext (gpgagent, command, NULL, NULL, NULL, NULL, scd_getattr_cb, &parm, &operr); if (!err) err = operr; if (err && attrtbl[attridx].entry_id == ENTRY_NKS_VERSION) { /* The NKS-VERSION is only supported by GnuPG > 2.0.11 thus we ignore the error. */ gtk_label_set_text (GTK_LABEL (card->entries[attrtbl[attridx].entry_id]), _("unknown")); } else if (err) { if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT) ; /* Lost the card. */ else { g_debug ("assuan command `%s' failed: %s <%s>\n", command, gpg_strerror (err), gpg_strsource (err)); } clear_card_data (card); break; } } if (!err) { g_object_ref (card); g_idle_add (reload_more_data_idle_cb, card); } card->reloading--; g_debug ("downed reloading counter (count=%d)", card->reloading); } /* A structure used to pass data to the learn_keys_gpg_status_cb. */ struct learn_keys_gpg_status_parm { GpaCMNetkey *card; GtkWidget *button; GtkProgressBar *pbar; }; /* Helper for learn_keys_clicked_cb. */ static gboolean learn_keys_gpg_status_cb (void *opaque, char *line) { struct learn_keys_gpg_status_parm *parm = opaque; if (!line) { /* We are finished with the command. */ /* Trigger a reload of the key data. */ g_object_ref (parm->card); g_idle_add (reload_more_data_idle_cb, parm->card); /* Cleanup. */ gtk_widget_destroy (parm->button); g_object_unref (parm->button); g_object_unref (parm->card); xfree (parm); return FALSE; /* (The return code does not matter here.) */ } if (!strncmp (line, "PROGRESS", 8)) gtk_progress_bar_pulse (parm->pbar); return TRUE; /* Keep on running. */ } /* Fire up the learn command. */ static void learn_keys_clicked_cb (GtkButton *button, void *user_data) { GpaCMNetkey *card = user_data; GtkWidget *widget; gpg_error_t err; struct learn_keys_gpg_status_parm *parm; parm = xcalloc (1, sizeof *parm); gtk_widget_set_sensitive (GTK_WIDGET (button), FALSE); widget = gtk_bin_get_child (GTK_BIN (button)); if (widget) gtk_widget_destroy (widget); widget = gtk_progress_bar_new (); gtk_container_add (GTK_CONTAINER (button), widget); gtk_progress_bar_set_text (GTK_PROGRESS_BAR (widget), _("Learning keys ...")); gtk_widget_show_all (GTK_WIDGET (button)); g_object_ref (card); parm->card = card; g_object_ref (button); parm->button = GTK_WIDGET (button); parm->pbar = GTK_PROGRESS_BAR (widget); err = gpa_start_simple_gpg_command (learn_keys_gpg_status_cb, parm, GPGME_PROTOCOL_CMS, 1, "--learn-card", "-v", NULL); if (err) { g_debug ("error starting gpg: %s", gpg_strerror (err)); learn_keys_gpg_status_cb (parm, NULL); return; } } /* Run the dialog to change the NullPIN. */ static void change_nullpin (GpaCMNetkey *card) { gpg_error_t err, operr; GtkWidget *dialog; gpgme_ctx_t gpgagent; int is_sigg; char *string; int okay; gpgagent = GPA_CM_OBJECT (card)->agent_ctx; g_return_if_fail (gpgagent); if (card->pininfo[0].valid && card->pininfo[0].nullpin) is_sigg = 0; else if (card->pininfo[2].valid && card->pininfo[2].nullpin) is_sigg = 1; else return; /* Oops: No NullPIN. */ string = g_strdup_printf (_("Setting the Initial PIN (%s)\n\n" "You selected to set the initial PIN of your card. " "The PIN is currently set to the NullPIN. Setting an " "initial PIN is required but can't be reverted.\n\n" "Please check the documentation of your card to learn " "for what the NullPIN is good.\n\n" "If you proceed you will be asked to enter a new PIN " "and later to repeat that PIN. Make sure that you " "will remember that PIN - it will not be possible to " "recover the PIN if it has been entered wrongly more " "than %d times.\n\n%s"), is_sigg? "SigG" : "NKS", 2, is_sigg ? _("You are now setting the PIN for the SigG key used to create " "qualified signatures. You may want to set the " "PIN to the same value as used for the NKS keys.") : _("You are now setting the PIN for the NKS keys used for standard " "signatures, encryption and authentication.")); /* FIXME: How do we figure out our GtkWindow? */ dialog = gtk_message_dialog_new_with_markup (NULL /*GTK_WINDOW (card)*/, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_OK_CANCEL, NULL); gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dialog), string); g_free (string); okay = (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK); if (okay) { err = gpgme_op_assuan_transact_ext (gpgagent, is_sigg ? "SCD PASSWD --nullpin PW1.CH.SIG" : "SCD PASSWD --nullpin PW1.CH", NULL, NULL, NULL, NULL, NULL, NULL, &operr); if (!err) err = operr; if (gpg_err_code (err) == GPG_ERR_CANCELED) okay = 0; /* No need to reload the data. */ else if (err) { char *message = g_strdup_printf (_("Error changing the NullPIN.\n" "(%s <%s>)"), gpg_strerror (err), gpg_strsource (err)); gpa_window_error (message, NULL); xfree (message); } } gtk_widget_destroy (GTK_WIDGET (dialog)); if (okay) reload_data (card); } static void change_or_reset_pin (GpaCMNetkey *card, int info_idx) { gpg_error_t err, operr; GtkWidget *dialog; gpgme_ctx_t gpgagent; int reset_mode; const char *string; int is_puk; int okay; const char *pwidstr; gpgagent = GPA_CM_OBJECT (card)->agent_ctx; g_return_if_fail (gpgagent); g_return_if_fail (info_idx < DIM (card->pininfo)); if (!card->pininfo[info_idx].valid || card->pininfo[info_idx].nopin || card->pininfo[info_idx].nullpin) { g_debug ("oops: NullPIN or not valid"); return; } reset_mode = (card->pininfo[info_idx].blocked || !card->pininfo[info_idx].tries_left); is_puk = !!(info_idx & 1); g_debug ("%s PIN at idx %d", reset_mode?"Resetting":"Changing", info_idx); switch (info_idx) { case 0: pwidstr = "PW1.CH"; break; case 1: pwidstr = "PW2.CH"; break; case 2: pwidstr = "PW1.CH.SIG"; break; case 3: pwidstr = "PW2.CH.SIG"; break; default: return; /* Ooops. */ } string = (!reset_mode ? _("Changing a PIN or PUK\n" "\n" "If you proceed you will be asked to enter " "the current value and then to enter a new " "value and repeat that value at another prompt.\n" "\n" "Entering a wrong value for the current value " "decrements the retry counter. If the retry counters " "of the PIN and the corresponding PUK are both down " "to zero, the keys controlled by the PIN are not anymore " "usable and there is no way to unblock them!") : is_puk ? _("Resetting a PUK\n" "\n" "Although PUK stands for PIN Unblocking Code " "the TCOS operating system of the NetKey card implements it " "as an alternative PIN and thus it is possible to use the " "PIN to unblock the PUK.\n" "\n" "If the PUK is blocked (the retry counter is down to zero), " "you may unblock it by using the non-blocked PIN. The retry " "counter is then set back to its initial value.\n" "\n" "If you proceed you will be asked to enter the current " "value of the PIN and then to enter a new value for the " "blocked PUK and repeat that new value at another prompt.") : _("Resetting a PIN\n" "\n" "If the PIN is blocked (the retry counter is down to zero), " "you may unblock it by using the non-blocked PUK. The retry " "counter is then set back to its initial value.\n" "\n" "If you proceed you will be asked to enter the current " "value of the PUK and then to enter a new value for the " "blocked PIN and repeat that new value at another prompt.") ); /* FIXME: How do we figure out our GtkWindow? */ dialog = gtk_message_dialog_new_with_markup (NULL /*GTK_WINDOW (card)*/, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_OK_CANCEL, NULL); gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dialog), string); okay = (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK); if (okay) { char command[100]; snprintf (command, sizeof command, "SCD PASSWD%s %s", reset_mode? " --reset":"", pwidstr); err = gpgme_op_assuan_transact_ext (gpgagent, command, NULL, NULL, NULL, NULL, NULL, NULL, &operr); if (!err) err = operr; if (gpg_err_code (err) == GPG_ERR_CANCELED) okay = 0; /* No need to reload the data. */ else if (err) { char *message = g_strdup_printf (_("Error changing or resetting the PIN/PUK.\n" "(%s <%s>)"), gpg_strerror (err), gpg_strsource (err)); gpa_window_error (message, NULL); xfree (message); } } gtk_widget_destroy (GTK_WIDGET (dialog)); if (okay) reload_data (card); } static void set_initial_pin_clicked_cb (GtkButton *widget, void *user_data) { GpaCMNetkey *card = user_data; change_nullpin (card); } static void change_pin_clicked_cb (void *widget, void *user_data) { GpaCMNetkey *card = user_data; if (widget == card->change_pin_btn) change_or_reset_pin (card, 0); else if (widget == card->change_puk_btn) change_or_reset_pin (card, 1); else if (widget == card->change_sigg_pin_btn) change_or_reset_pin (card, 2); else if (widget == card->change_sigg_puk_btn) change_or_reset_pin (card, 3); } /* Helper for construct_data_widget. Returns the label widget. */ static GtkLabel * add_table_row (GtkWidget *table, int *rowidx, const char *labelstr, GtkWidget *widget, GtkWidget *widget2, int readonly) { GtkWidget *label; int is_label = GTK_IS_LABEL (widget); label = gtk_label_new (labelstr); gtk_label_set_width_chars (GTK_LABEL (label), 22); gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 0, 1, *rowidx, *rowidx + 1, GTK_FILL, GTK_SHRINK, 0, 0); if (is_label) gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5); if (readonly) { if (!is_label && GTK_IS_ENTRY (widget)) { gtk_entry_set_has_frame (GTK_ENTRY (widget), FALSE); gtk_entry_set_editable (GTK_ENTRY (widget), FALSE); } } else { if (is_label) gtk_label_set_selectable (GTK_LABEL (widget), TRUE); } gtk_table_attach (GTK_TABLE (table), widget, 1, 2, *rowidx, *rowidx + 1, GTK_FILL, GTK_SHRINK, 0, 0); if (widget2) gtk_table_attach (GTK_TABLE (table), widget2, 2, 3, *rowidx, *rowidx + 1, GTK_FILL, GTK_SHRINK, 5, 0); ++*rowidx; return GTK_LABEL (label); } /* This function constructs the container holding all widgets making up this data widget. It is called during instance creation. */ static void construct_data_widget (GpaCMNetkey *card) { GtkWidget *frame; GtkWidget *table; GtkWidget *vbox, *hbox; GtkWidget *label; GtkWidget *button; int rowidx; /* General frame. */ frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE); label = gtk_label_new (_("General")); gtk_label_set_use_markup (GTK_LABEL (label), TRUE); gtk_frame_set_label_widget (GTK_FRAME (frame), label); table = gtk_table_new (2, 3, FALSE); gtk_container_set_border_width (GTK_CONTAINER (table), 10); gtk_container_add (GTK_CONTAINER (frame), table); rowidx = 0; card->entries[ENTRY_SERIALNO] = gtk_label_new (NULL); add_table_row (table, &rowidx, _("Serial number:"), card->entries[ENTRY_SERIALNO], NULL, 0); card->entries[ENTRY_NKS_VERSION] = gtk_label_new (NULL); add_table_row (table, &rowidx, _("Card version:"), card->entries[ENTRY_NKS_VERSION], NULL, 0); gtk_box_pack_start (GTK_BOX (card), frame, FALSE, TRUE, 0); /* Warning frame. */ frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE); vbox = gtk_vbox_new (FALSE, 5); label = gtk_label_new (_("A NullPIN is still active on this card.\n" "You need to set a real PIN before you can make use of the card.")); gtk_label_set_use_markup (GTK_LABEL (label), TRUE); gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0); hbox = gtk_alignment_new (0.5, 0.5, 0, 0); button = gtk_button_new_with_label (_("Set initial PIN")); gtk_container_add (GTK_CONTAINER (hbox), button); gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, FALSE, 0); gtk_container_add (GTK_CONTAINER (frame), vbox); gtk_box_pack_start (GTK_BOX (card), frame, FALSE, TRUE, 0); card->warning_frame = frame; g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (set_initial_pin_clicked_cb), card); /* Keys frame. */ frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE); label = gtk_label_new (_("Keys")); gtk_label_set_use_markup (GTK_LABEL (label), TRUE); gtk_frame_set_label_widget (GTK_FRAME (frame), label); /* There is only a label widget yet in the frame for now. The real widgets are added while figuring out the keys of the card. */ label = gtk_label_new (_("scanning ...")); gtk_container_add (GTK_CONTAINER (frame), label); gtk_box_pack_start (GTK_BOX (card), frame, FALSE, TRUE, 0); card->keys_frame = frame; /* PIN frame. */ frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE); label = gtk_label_new (_("PIN")); gtk_label_set_use_markup (GTK_LABEL (label), TRUE); gtk_frame_set_label_widget (GTK_FRAME (frame), label); table = gtk_table_new (4, 3, FALSE); gtk_container_set_border_width (GTK_CONTAINER (table), 10); gtk_container_add (GTK_CONTAINER (frame), table); rowidx = 0; card->entries[ENTRY_PIN_RETRYCOUNTER] = gtk_label_new (NULL); button = gtk_button_new (); add_table_row (table, &rowidx, _("PIN retry counter:"), card->entries[ENTRY_PIN_RETRYCOUNTER], button, 1); card->change_pin_btn = button; g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (change_pin_clicked_cb), card); card->entries[ENTRY_PUK_RETRYCOUNTER] = gtk_label_new (NULL); button = gtk_button_new (); add_table_row (table, &rowidx, _("PUK retry counter:"), card->entries[ENTRY_PUK_RETRYCOUNTER], button, 1); card->change_puk_btn = button; g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (change_pin_clicked_cb), card); card->entries[ENTRY_SIGG_PIN_RETRYCOUNTER] = gtk_label_new (NULL); button = gtk_button_new (); add_table_row (table, &rowidx, _("SigG PIN retry counter:"), card->entries[ENTRY_SIGG_PIN_RETRYCOUNTER], button, 1); card->change_sigg_pin_btn = button; g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (change_pin_clicked_cb), card); card->entries[ENTRY_SIGG_PUK_RETRYCOUNTER] = gtk_label_new (NULL); button = gtk_button_new (); add_table_row (table, &rowidx, _("SigG PUK retry counter:"), card->entries[ENTRY_SIGG_PUK_RETRYCOUNTER], button, 1); card->change_sigg_puk_btn = button; g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (change_pin_clicked_cb), card); gtk_box_pack_start (GTK_BOX (card), frame, FALSE, TRUE, 0); } /************************************************************ ****************** Object Management ******************** ************************************************************/ static void gpa_cm_netkey_class_init (void *class_ptr, void *class_data) { GpaCMNetkeyClass *klass = class_ptr; parent_class = g_type_class_peek_parent (klass); G_OBJECT_CLASS (klass)->finalize = gpa_cm_netkey_finalize; } static void gpa_cm_netkey_init (GTypeInstance *instance, void *class_ptr) { GpaCMNetkey *card = GPA_CM_NETKEY (instance); construct_data_widget (card); } static void gpa_cm_netkey_finalize (GObject *object) { /* GpaCMNetkey *card = GPA_CM_NETKEY (object); */ parent_class->finalize (object); } /* Construct the class. */ GType gpa_cm_netkey_get_type (void) { static GType this_type = 0; if (!this_type) { static const GTypeInfo this_info = { sizeof (GpaCMNetkeyClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, gpa_cm_netkey_class_init, (GClassFinalizeFunc) NULL, NULL, /* class_data */ sizeof (GpaCMNetkey), 0, /* n_preallocs */ gpa_cm_netkey_init }; this_type = g_type_register_static (GPA_CM_OBJECT_TYPE, "GpaCMNetkey", &this_info, 0); } return this_type; } /************************************************************ ********************** Public API ************************ ************************************************************/ GtkWidget * gpa_cm_netkey_new () { return GTK_WIDGET (g_object_new (GPA_CM_NETKEY_TYPE, NULL)); } /* If WIDGET is of Type GpaCMNetkey do a data reload through the assuan connection. */ void gpa_cm_netkey_reload (GtkWidget *widget, gpgme_ctx_t gpgagent) { if (GPA_IS_CM_NETKEY (widget)) { GPA_CM_OBJECT (widget)->agent_ctx = gpgagent; if (gpgagent) reload_data (GPA_CM_NETKEY (widget)); } } diff --git a/src/cm-netkey.h b/src/cm-netkey.h index e5d3719..68140b1 100644 --- a/src/cm-netkey.h +++ b/src/cm-netkey.h @@ -1,57 +1,67 @@ /* cm-netkey.h - Widget to show information about a Netkey card. * Copyright (C) 2009 g10 Code GmbH * * This file is part of GPA. * - * GPA 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 3 of the License, or - * (at your option) any later version. + * GPA is free software; you can redistribute and/or modify this part + * of GPA under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. * * GPA 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, see . + * along with this program; if not, see . */ #ifndef CM_NETKEY_H #define CM_NETKEY_H #include /* Declare the Object. */ typedef struct _GpaCMNetkey GpaCMNetkey; typedef struct _GpaCMNetkeyClass GpaCMNetkeyClass; GType gpa_cm_netkey_get_type (void) G_GNUC_CONST; #define GPA_CM_NETKEY_TYPE (gpa_cm_netkey_get_type ()) #define GPA_CM_NETKEY(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GPA_CM_NETKEY_TYPE, GpaCMNetkey)) #define GPA_CM_NETKEY_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ GPA_CM_NETKEY_TYPE, GpaCMNetkeyClass)) #define GPA_IS_CM_NETKEY(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GPA_CM_NETKEY_TYPE)) #define GPA_IS_CM_NETKEY_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), GPA_CM_NETKEY_TYPE)) #define GPA_CM_NETKEY_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ GPA_CM_NETKEY_TYPE, GpaCMNetkeyClass)) /* The class specific API. */ GtkWidget *gpa_cm_netkey_new (void); void gpa_cm_netkey_reload (GtkWidget *widget, gpgme_ctx_t gpgagent); #endif /*CM_NETKEY_H*/ diff --git a/src/cm-object.c b/src/cm-object.c index 28e36b0..be3d7fe 100644 --- a/src/cm-object.c +++ b/src/cm-object.c @@ -1,169 +1,179 @@ /* cm-object.c - Top object for card parts of the card manager. * Copyright (C) 2009 g10 Code GmbH * * This file is part of GPA. * - * GPA 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 3 of the License, or - * (at your option) any later version. + * GPA is free software; you can redistribute and/or modify this part + * of GPA under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. * * GPA 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, see . + * along with this program; if not, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include -#include "gpa.h" +#include "gpa.h" #include "cm-object.h" /* Signals */ enum { UPDATE_STATUS, ALERT_DIALOG, LAST_SIGNAL }; /* The parent class. */ static GObjectClass *parent_class; /* The signal vector. */ static guint signals [LAST_SIGNAL]; /* Local prototypes */ static void gpa_cm_object_finalize (GObject *object); -/************************************************************ +/************************************************************ ******************* Implementation ********************* ************************************************************/ -/************************************************************ +/************************************************************ ****************** Object Management ******************** ************************************************************/ static void gpa_cm_object_class_init (void *class_ptr, void *class_data) { GpaCMObjectClass *klass = class_ptr; parent_class = g_type_class_peek_parent (klass); G_OBJECT_CLASS (klass)->finalize = gpa_cm_object_finalize; signals[UPDATE_STATUS] = g_signal_new ("update-status", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, G_STRUCT_OFFSET (GpaCMObjectClass, update_status), NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); signals[ALERT_DIALOG] = g_signal_new ("alert-dialog", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, G_STRUCT_OFFSET (GpaCMObjectClass, alert_dialog), NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); } static void gpa_cm_object_init (GTypeInstance *instance, void *class_ptr) { /* GpaCMObject *card = GPA_CM_OBJECT (instance); */ } static void gpa_cm_object_finalize (GObject *object) -{ +{ /* GpaCMObject *card = GPA_CM_OBJECT (object); */ - + parent_class->finalize (object); } /* Construct the class. */ GType gpa_cm_object_get_type (void) { static GType this_type = 0; - + if (!this_type) { static const GTypeInfo this_info = { sizeof (GpaCMObjectClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, gpa_cm_object_class_init, (GClassFinalizeFunc) NULL, NULL, /* class_data */ sizeof (GpaCMObject), 0, /* n_preallocs */ gpa_cm_object_init }; - + this_type = g_type_register_static (GTK_TYPE_VBOX, "GpaCMObject", &this_info, 0); } - + return this_type; } -/************************************************************ +/************************************************************ ********************** Public API ************************ ************************************************************/ /* Emit a status line names STATUSNAME plus space delimited arguments. */ void gpa_cm_object_update_status (GpaCMObject *obj, const char *text) { g_return_if_fail (obj); g_return_if_fail (GPA_CM_OBJECT (obj)); - + if (!text) text = ""; g_signal_emit (obj, signals[UPDATE_STATUS], 0, text); } /* Emit the error message MSG. */ void gpa_cm_object_alert_dialog (GpaCMObject *obj, const gchar *messageg) { g_return_if_fail (obj); g_return_if_fail (GPA_CM_OBJECT (obj)); - + g_signal_emit (obj, signals[ALERT_DIALOG], 0, messageg); } diff --git a/src/cm-object.h b/src/cm-object.h index dbe3118..ab22894 100644 --- a/src/cm-object.h +++ b/src/cm-object.h @@ -1,80 +1,90 @@ /* cm-object.h - Top object for card parts of the card manager. * Copyright (C) 2009 g10 Code GmbH * * This file is part of GPA. * - * GPA 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 3 of the License, or - * (at your option) any later version. + * GPA is free software; you can redistribute and/or modify this part + * of GPA under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. * * GPA 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, see . + * along with this program; if not, see . */ #ifndef CM_OBJECT_H #define CM_OBJECT_H #include /* Declare the Object. */ typedef struct _GpaCMObject GpaCMObject; typedef struct _GpaCMObjectClass GpaCMObjectClass; GType gpa_cm_object_get_type (void) G_GNUC_CONST; #define GPA_CM_OBJECT_TYPE (gpa_cm_object_get_type ()) #define GPA_CM_OBJECT(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GPA_CM_OBJECT_TYPE, GpaCMObject)) #define GPA_CM_OBJECT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ GPA_CM_OBJECT_TYPE, GpaCMObjectClass)) #define GPA_IS_CM_OBJECT(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GPA_CM_OBJECT_TYPE)) #define GPA_IS_CM_OBJECT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), GPA_CM_OBJECT_TYPE)) #define GPA_CM_OBJECT_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ GPA_CM_OBJECT_TYPE, GpaCMObjectClass)) /* Object's class definition. */ -struct _GpaCMObjectClass +struct _GpaCMObjectClass { GtkVBoxClass parent_class; /* Signal handlers */ void (*update_status) (GpaCMObject *obj, gchar *status); void (*alert_dialog) (GpaCMObject *obj, gchar *message); }; /* Object definition. */ struct _GpaCMObject { GtkVBox parent_instance; /* Private. Fixme: Hide them. */ gpgme_ctx_t agent_ctx; }; /* The class specific API. */ void gpa_cm_object_update_status (GpaCMObject *obj, const char *text); void gpa_cm_object_alert_dialog (GpaCMObject *obj, const gchar *messageg); #endif /*CM_OBJECT_H*/ diff --git a/src/cm-openpgp.c b/src/cm-openpgp.c index 0c81bf7..857baed 100644 --- a/src/cm-openpgp.c +++ b/src/cm-openpgp.c @@ -1,1715 +1,1725 @@ /* cm-openpgp.c - OpenPGP card part for the card manager. * Copyright (C) 2008, 2009 g10 Code GmbH * * This file is part of GPA. * - * GPA 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 3 of the License, or - * (at your option) any later version. + * GPA is free software; you can redistribute and/or modify this part + * of GPA under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. * * GPA 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, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include "gpa.h" #include "gtktools.h" #include "convert.h" #include "gpa-key-details.h" #include "cm-object.h" #include "cm-openpgp.h" /* Identifiers for the entry fields. */ enum { ENTRY_SERIALNO, ENTRY_VERSION, ENTRY_MANUFACTURER, ENTRY_LOGIN, ENTRY_LANGUAGE, ENTRY_PUBKEY_URL, ENTRY_FIRST_NAME, ENTRY_LAST_NAME, ENTRY_SEX, ENTRY_KEY_SIG, ENTRY_KEY_ENC, ENTRY_KEY_AUTH, ENTRY_SIG_COUNTER, ENTRY_PIN_RETRYCOUNTER, ENTRY_PUK_RETRYCOUNTER, ENTRY_ADMIN_PIN_RETRYCOUNTER, ENTRY_SIG_FORCE_PIN, ENTRY_LAST }; /* Object's class definition. */ struct _GpaCMOpenpgpClass { GpaCMObjectClass parent_class; }; /* Object definition. */ struct _GpaCMOpenpgp { GpaCMObject parent_instance; GtkWidget *general_frame; GtkWidget *personal_frame; GtkWidget *keys_frame; GtkWidget *pin_frame; GtkWidget *entries[ENTRY_LAST]; gboolean changed[ENTRY_LAST]; /* An array of 3 widgets to hold the key_details widget. */ GtkWidget *key_details[3]; /* The key attributes. */ struct { int algo; int nbits; } key_attr[3]; /* A malloced string with the key attributes in a human readable format. */ char *key_attributes; GtkLabel *puk_label; /* The label of the PUK field. */ /* An array with the current value of the retry counters. */ int retry_counter[3]; /* An array for the buttons to change the 3 PINs. */ GtkWidget *change_pin_btn[3]; /* True is this is a version 2 card. */ int is_v2; /* This flag is set while we are reloading data. */ int reloading; }; /* The parent class. */ static GObjectClass *parent_class; /* Local prototypes */ static void gpa_cm_openpgp_finalize (GObject *object); /************************************************************ ******************* Implementation ********************* ************************************************************/ /* NOTE: This code has been copied from GnuPG/g10/card-util.c. */ static const char * get_manufacturer (unsigned int no) { /* Note: Make sure that there is no colon or linefeed in the string. */ switch (no) { case 0x0001: return "PPC Card Systems"; case 0x0002: return "Prism"; case 0x0003: return "OpenFortress"; case 0x0004: return "Wewid"; case 0x0005: return "ZeitControl"; case 0x0006: return "Yubico"; case 0x0007: return "OpenKMS"; case 0x0008: return "LogoEmail"; case 0x002A: return "Magrathea"; case 0x1337: return "Warsaw Hackerspace"; case 0xF517: return "FSIJ"; /* 0x0000 and 0xFFFF are defined as test cards per spec, 0xFF00 to 0xFFFE are assigned for use with randomly created serial numbers. */ case 0x0000: case 0xffff: return "test card"; default: return (no & 0xff00) == 0xff00? "unmanaged S/N range":"unknown"; } } /* If not yet done show a warning to tell the user about the Admin PIN. Returns true if the operation shall continue. */ static int show_admin_pin_notice (GpaCMOpenpgp *card) { static int shown; GtkWidget *dialog; const char *string; int okay; if (shown) return 1; string = _("Admin-PIN Required\n" "\n" "Depending on the previous operations you may now " "be asked for the Admin-PIN. Entering a wrong value " "for the Admin-PIN decrements the corresponding retry counter. " "If the retry counter is down to zero, the Admin-PIN can't " "be restored anymore and thus the data on the card can't be " "modified.\n" "\n" "Unless changed, a fresh standard card has set the Admin-PIN " "to the value 12345678. " "However, the issuer of your card might " "have initialized the card with a different Admin-PIN and " "that Admin-PIN might only be known to the issuer. " "Please check the instructions of your issuer.\n" "\n" "This notice will be shown only once per session."); /* FIXME: How do we figure out our GtkWindow? */ dialog = gtk_message_dialog_new_with_markup (NULL /*GTK_WINDOW (card)*/, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_OK_CANCEL, NULL); gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dialog), string); okay = (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK); gtk_widget_destroy (GTK_WIDGET (dialog)); if (okay) shown = 1; return okay; } /* Clears the info contained in the card widget. */ static void clear_card_data (GpaCMOpenpgp *card) { int idx; for (idx=0; idx < ENTRY_LAST; idx++) if (idx == ENTRY_SEX) gtk_combo_box_set_active (GTK_COMBO_BOX (card->entries[idx]), 2); else if (idx == ENTRY_SIG_FORCE_PIN) gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (card->entries[idx]), 0); else if (GTK_IS_LABEL (card->entries[idx])) gtk_label_set_text (GTK_LABEL (card->entries[idx]), ""); else gtk_entry_set_text (GTK_ENTRY (card->entries[idx]), ""); } static void clear_changed_flags (GpaCMOpenpgp *card) { int idx; for (idx=0; idx < ENTRY_LAST; idx++) card->changed[idx] = 0; } /* Show the error string TEXT in the status bar. With TEXT of NULL clear the edit error message. */ static void show_edit_error (GpaCMOpenpgp *card, const char *text) { gpa_cm_object_update_status (GPA_CM_OBJECT(card), text); } /* Update the the serialno field. This also updates the version and the manufacturer field. */ static void update_entry_serialno (GpaCMOpenpgp *card, int entry_id, const char *string) { char version_buffer[6]; char serialno_buffer[8+1]; char *p; const char *serialno = ""; const char *vendor = ""; const char *version = ""; (void)entry_id; /* Not used. */ if (strncmp (string, "D27600012401", 12) || strlen (string) != 32 ) { /* Not a proper OpenPGP card serialnumber. Display the full serialnumber. */ serialno = string; } else { /* A proper OpenPGP AID. */ strncpy (serialno_buffer, string + 20, 8); serialno_buffer[8] = 0; serialno = serialno_buffer; /* Reformat the version number to be better human readable. */ p = version_buffer; if (string[12] != '0') *p++ = string[12]; *p++ = string[13]; *p++ = '.'; if (string[14] != '0') *p++ = string[14]; *p++ = string[15]; *p++ = '\0'; version = version_buffer; /* Get the manufactorer. */ vendor = get_manufacturer (xtoi_2(string+16)*256 + xtoi_2 (string+18)); } gtk_label_set_text (GTK_LABEL (card->entries[ENTRY_SERIALNO]), serialno); gtk_label_set_text (GTK_LABEL (card->entries[ENTRY_MANUFACTURER]), vendor); gtk_label_set_text (GTK_LABEL (card->entries[ENTRY_VERSION]), version); card->is_v2 = !((*version == '1' || *version == '0') && version[1] == '.'); gtk_label_set_text (card->puk_label, (card->is_v2 ? _("PUK retry counter:") : _("CHV2 retry counter: "))); } static void update_entry_name (GpaCMOpenpgp *card, int entry_id, const char *string) { const char *first = ""; const char *last = ""; char *buffer = NULL; (void)entry_id; /* Not used. */ if (*string) { char *p, *given; buffer = xstrdup (string); given = strstr (buffer, "<<"); for (p=buffer; *p; p++) if (*p == '<') *p = ' '; if (given && given[2]) { *given = 0; given += 2; first = given; } last = buffer; } gtk_entry_set_text (GTK_ENTRY (card->entries[ENTRY_FIRST_NAME]), first); gtk_entry_set_text (GTK_ENTRY (card->entries[ENTRY_LAST_NAME]), last); xfree (buffer); } static void update_entry_sex (GpaCMOpenpgp *card, int entry_id, const char *string) { int idx; if (*string == '1') idx = 0; /* 'm' */ else if (*string == '2') idx = 1; /* 'f' */ else idx = 2; /* 'u' is the default. */ gtk_combo_box_set_active (GTK_COMBO_BOX (card->entries[entry_id]), idx); } static void set_integer (GtkWidget *entry, int value) { char numbuf[35]; snprintf (numbuf, sizeof numbuf, "%d", value); gtk_label_set_text (GTK_LABEL (entry), numbuf); } /* Update the CVH (PIN) status fields. STRING is the space separated list of the decimal encoded values from the DO 0xC4. */ static void update_entry_chv_status (GpaCMOpenpgp *card, int entry_id, const char *string) { int force_pw1; int pw_maxlen[3]; int pw_retry[3]; int i; (void)entry_id; /* Not used. */ while (spacep (string)) string++; force_pw1 = !atoi (string); while (*string && !spacep (string)) string++; while (spacep (string)) string++; for (i=0; *string && i < DIM (pw_maxlen); i++) { pw_maxlen[i] = atoi (string); while (*string && !spacep (string)) string++; while (spacep (string)) string++; } for (i=0; *string && i < DIM (pw_retry); i++) { pw_retry[i] = atoi (string); while (*string && !spacep (string)) string++; while (spacep (string)) string++; } gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (card->entries[ENTRY_SIG_FORCE_PIN]), !!force_pw1); set_integer (card->entries[ENTRY_PIN_RETRYCOUNTER], pw_retry[0]); set_integer (card->entries[ENTRY_PUK_RETRYCOUNTER], pw_retry[1]); set_integer (card->entries[ENTRY_ADMIN_PIN_RETRYCOUNTER], pw_retry[2]); for (i=0; i < 3; i++) card->retry_counter[i] = pw_retry[i]; /* Set the Change button label to reset or change depending on the current retry count. Make the button insensitive if the Admin PIN is blocked. */ gtk_button_set_label (GTK_BUTTON (card->change_pin_btn[0]), (!pw_retry[0] || (!pw_retry[1] && !card->is_v2) ? _("Reset PIN") : _("Change PIN"))); gtk_widget_set_sensitive (card->change_pin_btn[0], !!pw_retry[2]); /* For version 1 cards, PIN2 is usually synchronized with PIN1 thus we don't need the Change PIN button. Version 2 cards use a PUK (actually a Reset Code) with entirely different semantics, thus a button is required. */ if (card->is_v2) { gtk_button_set_label (GTK_BUTTON (card->change_pin_btn[1]), (pw_retry[1]? _("Change PUK"):_("Reset PUK"))); gtk_widget_set_sensitive (card->change_pin_btn[1], !!pw_retry[2]); gtk_widget_show_all (card->change_pin_btn[1]); } else gtk_widget_hide_all (card->change_pin_btn[1]); gtk_widget_set_no_show_all (card->change_pin_btn[1], !card->is_v2); /* The Admin PIN has always the same label. If the Admin PIN is blocked the button is made insensitive. Fixme: In the case of a blocked Admin PIN we should have a button to allow a factory reset of v2 cards. */ gtk_button_set_label (GTK_BUTTON (card->change_pin_btn[2]), _("Change PIN")); gtk_widget_set_sensitive (card->change_pin_btn[2], !!pw_retry[2]); } /* Format the raw hex fingerprint STRING and set it into ENTRY_ID. */ static void update_entry_fpr (GpaCMOpenpgp *card, int entry_id, const char *string) { char *buf; buf = gpa_gpgme_key_format_fingerprint (string); if (GTK_IS_EXPANDER (card->entries[entry_id])) gtk_expander_set_label (GTK_EXPANDER (card->entries[entry_id]), buf); else gtk_label_set_text (GTK_LABEL (card->entries[entry_id]), buf); xfree (buf); } /* Update the key attributes. We don't have a regular entry field but append it to the version number field. We are called for each key in turn and thus we only collect the information here and print them if we care called with STRING set to NULL. */ static void update_entry_key_attr (GpaCMOpenpgp *card, int entry_id, const char *string) { int keyno, algo, nbits; char *buf, *tmpbuf; const char *s; int items; (void)entry_id; /* Not used. */ if (string) { sscanf (string, "%d %d %d", &keyno, &algo, &nbits); keyno--; if (keyno >= 0 && keyno < DIM (card->key_attr)) { card->key_attr[keyno].algo = algo; card->key_attr[keyno].nbits = nbits; } } else { for (keyno=1; keyno < DIM (card->key_attr); keyno++) if (card->key_attr[0].algo != card->key_attr[keyno].algo || card->key_attr[0].nbits != card->key_attr[keyno].nbits) break; if (keyno < DIM (card->key_attr)) items = DIM (card->key_attr); /* Attributes are mixed. */ else items = 1; /* All the same attributes. */ buf = NULL; for (keyno=0; keyno < items; keyno++) { tmpbuf = g_strdup_printf ("%s%s%s-%d", buf? buf : "", buf? ", ":"", card->key_attr[keyno].algo == 1?"RSA": card->key_attr[keyno].algo == 17?"DSA":"?", card->key_attr[keyno].nbits); g_free (buf); buf = tmpbuf; } g_free (card->key_attributes); card->key_attributes = buf; s = gtk_label_get_text (GTK_LABEL (card->entries[ENTRY_VERSION])); if (!s) s = ""; buf = g_strdup_printf ("%s (%s)", s, card->key_attributes); gtk_label_set_text (GTK_LABEL (card->entries[ENTRY_VERSION]), buf); g_free (buf); } } struct scd_getattr_parm { GpaCMOpenpgp *card; /* The object. */ const char *name; /* Name of expected attribute. */ int entry_id; /* The identifier for the entry. */ void (*updfnc) (GpaCMOpenpgp *card, int entry_id, const char *string); }; static gpg_error_t scd_getattr_cb (void *opaque, const char *status, const char *args) { struct scd_getattr_parm *parm = opaque; int entry_id; /* g_debug ("STATUS_CB: status=`%s' args=`%s'", status, args); */ if (!strcmp (status, parm->name) ) { entry_id = parm->entry_id; if (entry_id == ENTRY_LAST && !strcmp (status, "KEY-FPR")) { /* Special entry ID for the fingerprints: We need to figure out what entry is actually to be used. */ if (*args == '1') entry_id = ENTRY_KEY_SIG; else if (*args == '2') entry_id = ENTRY_KEY_ENC; else if (*args == '3') entry_id = ENTRY_KEY_AUTH; else { /* Ooops. */ } if (*args) { for (args++; spacep (args); args++) ; } } if (entry_id < ENTRY_LAST) { char *tmp = xstrdup (args); percent_unescape (tmp, 1); if (parm->updfnc) parm->updfnc (parm->card, entry_id, tmp); else if (GTK_IS_LABEL (parm->card->entries[entry_id])) gtk_label_set_text (GTK_LABEL (parm->card->entries[entry_id]), tmp); else gtk_entry_set_text (GTK_ENTRY (parm->card->entries[entry_id]), tmp); xfree (tmp); } else if (entry_id == ENTRY_LAST && parm->updfnc) { char *tmp = xstrdup (args); percent_unescape (tmp, 1); parm->updfnc (parm->card, entry_id, tmp); xfree (tmp); } } return 0; } /* Use the assuan machinery to load the bulk of the OpenPGP card data. */ static void reload_data (GpaCMOpenpgp *card) { static struct { const char *name; int entry_id; void (*updfnc) (GpaCMOpenpgp *card, int entry_id, const char *string); } attrtbl[] = { { "SERIALNO", ENTRY_SERIALNO, update_entry_serialno }, { "DISP-NAME", ENTRY_LAST_NAME, update_entry_name }, { "DISP-LANG", ENTRY_LANGUAGE }, { "DISP-SEX", ENTRY_SEX, update_entry_sex }, { "PUBKEY-URL", ENTRY_PUBKEY_URL }, { "LOGIN-DATA", ENTRY_LOGIN }, { "SIG-COUNTER",ENTRY_SIG_COUNTER }, { "CHV-STATUS", ENTRY_PIN_RETRYCOUNTER, update_entry_chv_status }, { "KEY-FPR", ENTRY_LAST, update_entry_fpr }, /* { "CA-FPR", }, */ { "KEY-ATTR", ENTRY_LAST, update_entry_key_attr }, { NULL } }; int attridx; gpg_error_t err, operr; char command[100]; struct scd_getattr_parm parm; gpgme_ctx_t gpgagent; show_edit_error (card, NULL); gpgagent = GPA_CM_OBJECT (card)->agent_ctx; g_return_if_fail (gpgagent); card->reloading++; parm.card = card; for (attridx=0; attrtbl[attridx].name; attridx++) { parm.name = attrtbl[attridx].name; parm.entry_id = attrtbl[attridx].entry_id; parm.updfnc = attrtbl[attridx].updfnc; snprintf (command, sizeof command, "SCD GETATTR %s", parm.name); err = gpgme_op_assuan_transact_ext (gpgagent, command, NULL, NULL, NULL, NULL, scd_getattr_cb, &parm, &operr); if (!err) err = operr; if (err) { if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT) ; /* Lost the card. */ else { g_debug ("assuan command `%s' failed: %s <%s>\n", command, gpg_strerror (err), gpg_strsource (err)); } clear_card_data (card); break; } } update_entry_key_attr (card, 0, NULL); /* Append ky attributes. */ clear_changed_flags (card); card->reloading--; } static gpg_error_t save_attr (GpaCMOpenpgp *card, const char *name, const char *value, int is_escaped) { gpg_error_t err, operr; char *command; gpgme_ctx_t gpgagent; g_return_val_if_fail (*name && value, gpg_error (GPG_ERR_BUG)); gpgagent = GPA_CM_OBJECT (card)->agent_ctx; g_return_val_if_fail (gpgagent,gpg_error (GPG_ERR_BUG)); if (!show_admin_pin_notice (card)) return gpg_error (GPG_ERR_CANCELED); if (is_escaped) command = g_strdup_printf ("SCD SETATTR %s %s", name, value); else { char *p; p = percent_escape (value, NULL, 1); command = g_strdup_printf ("SCD SETATTR %s %s", name, p); xfree (p); } err = gpgme_op_assuan_transact_ext (gpgagent, command, NULL, NULL, NULL, NULL, NULL, NULL, &operr); if (!err) err = operr; if (err && !(gpg_err_code (err) == GPG_ERR_CANCELED && gpg_err_source (err) == GPG_ERR_SOURCE_PINENTRY)) { char *message = g_strdup_printf (_("Error saving the changed values.\n" "(%s <%s>)"), gpg_strerror (err), gpg_strsource (err)); gpa_cm_object_alert_dialog (GPA_CM_OBJECT (card), message); xfree (message); } xfree (command); return err; } /* Check the constraints for NAME and return a string with an error description or NULL if the name is fine. */ static const char * check_one_name (const char *name) { int i; for (i=0; name[i] && name[i] >= ' ' && name[i] <= 126; i++) ; /* The name must be in Latin-1 and not UTF-8 - lacking the code to ensure this we restrict it to ASCII. */ if (name[i]) return _("Only plain ASCII is currently allowed."); else if (strchr (name, '<')) return _("The \"<\" character may not be used."); else if (strstr (name, " ")) return _("Double spaces are not allowed."); else return NULL; } /* Save the first and last name fields. Returns true on error. */ static gboolean save_entry_name (GpaCMOpenpgp *card) { const char *first, *last; const char *errstr; int failed = ENTRY_FIRST_NAME; first = gtk_entry_get_text (GTK_ENTRY (card->entries[ENTRY_FIRST_NAME])); last = gtk_entry_get_text (GTK_ENTRY (card->entries[ENTRY_LAST_NAME])); errstr = check_one_name (first); if (!errstr) { errstr = check_one_name (last); if (errstr) failed = ENTRY_LAST_NAME; } if (!errstr) { char *buffer, *p; buffer = g_strdup_printf ("%s<<%s", last, first); for (p=buffer; *p; p++) if (*p == ' ') *p = '<'; if (strlen (buffer) > 39) errstr = _("Total length of first and last name " "may not be longer than 39 characters."); else if (save_attr (card, "DISP-NAME", buffer, 0)) errstr = _("Saving the field failed."); g_free (buffer); } if (errstr) { show_edit_error (card, errstr); gtk_widget_grab_focus (GTK_WIDGET (card->entries[failed])); } return !!errstr; } /* Save the language field. Returns true on error. */ static gboolean save_entry_language (GpaCMOpenpgp *card) { const char *errstr = NULL; const char *value, *s; value = gtk_entry_get_text (GTK_ENTRY (card->entries[ENTRY_LANGUAGE])); if (strlen (value) > 8 || (strlen (value) & 1)) { errstr = _("Invalid length of the language preference."); goto leave; } for (s=value; *s && *s >= 'a' && *s <= 'z'; s++) ; if (*s) { errstr = _("The language preference may only contain " "the letters 'a' through 'z'."); goto leave; } if (save_attr (card, "DISP-LANG", value, 0)) errstr = _("Saving the field failed."); leave: if (errstr) show_edit_error (card, errstr); return !!errstr; } /* Save the salutation field. Returns true on error. */ static gpg_error_t save_entry_sex (GpaCMOpenpgp *card) { int idx; const char *string; const char *errstr = NULL; idx = gtk_combo_box_get_active (GTK_COMBO_BOX (card->entries[ENTRY_SEX])); if (!idx) string = "1"; else if (idx == 1) string = "2"; else string = "9"; if (save_attr (card, "DISP-SEX", string, 0)) errstr = _("Saving the field failed."); if (errstr) show_edit_error (card, errstr); return !!errstr; } /* Save the pubkey url field. Returns true on error. */ static gboolean save_entry_pubkey_url (GpaCMOpenpgp *card) { const char *errstr = NULL; const char *value; value = gtk_entry_get_text (GTK_ENTRY (card->entries[ENTRY_PUBKEY_URL])); if (strlen (value) > 254) { errstr = _("The field may not be longer than 254 characters."); goto leave; } if (save_attr (card, "PUBKEY-URL", value, 0)) errstr = _("Saving the field failed."); leave: if (errstr) show_edit_error (card, errstr); return !!errstr; } /* Save the login field. Returns true on error. */ static gboolean save_entry_login (GpaCMOpenpgp *card) { const char *errstr = NULL; const char *value; value = gtk_entry_get_text (GTK_ENTRY (card->entries[ENTRY_LOGIN])); if (strlen (value) > 254) { errstr = _("The field may not be longer than 254 characters."); goto leave; } if (save_attr (card, "LOGIN-DATA", value, 0)) errstr = _("Saving the field failed."); leave: if (errstr) show_edit_error (card, errstr); return !!errstr; } /* Save the force signature PIN field. */ static gpg_error_t save_entry_sig_force_pin (GpaCMOpenpgp *card) { int value; const char *errstr = NULL; value = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (card->entries[ENTRY_SIG_FORCE_PIN])); if (save_attr (card, "CHV-STATUS-1", value? "%00":"%01", 1)) errstr = _("Saving the field failed."); if (errstr) show_edit_error (card, errstr); return !!errstr; } /* Callback for the "changed" signal connected to entry fields. We figure out the entry field for which this signal has been emitted and set a flag to know wether we have unsaved data. */ static void edit_changed_cb (GtkEditable *editable, void *opaque) { GpaCMOpenpgp *card = opaque; int idx; for (idx=0; idx < ENTRY_LAST; idx++) if (GTK_IS_EDITABLE (card->entries[idx]) && GTK_EDITABLE (card->entries[idx]) == editable) break; if (!(idx < ENTRY_LAST)) return; if (!card->changed[idx]) { card->changed[idx] = TRUE; /* g_debug ("changed signal for entry %d", idx); */ } } /* Callback for the "changed" signal connected to combo boxes. We figure out the entry field for which this signal has been emitted and set a flag to know ehether we have unsaved data. */ static void edit_changed_combo_box_cb (GtkComboBox *cbox, void *opaque) { GpaCMOpenpgp *card = opaque; int idx; for (idx=0; idx < ENTRY_LAST; idx++) if (GTK_IS_COMBO_BOX (card->entries[idx]) && GTK_COMBO_BOX (card->entries[idx]) == cbox) break; if (!(idx < ENTRY_LAST)) return; if (!card->changed[idx]) { int result; card->changed[idx] = TRUE; /* g_debug ("changed signal for combo box %d", idx); */ if (!card->reloading) { result = save_entry_sex (card); /* Fixme: How should we handle errors? Trying it this way: Always clear the changed flag, so that we will receive one again and grab the focus on error. fails. We may need to make use of the keynav-changed signal. */ card->changed[idx] = 0; if (!result) show_edit_error (card, NULL); else gtk_widget_grab_focus (GTK_WIDGET (cbox)); } } } /* Callback for the "changed" signal connected to entry fields. We figure out the entry field for which this signal has been emitted and set a flag to know ehether we have unsaved data. */ static void edit_toggled_cb (GtkToggleButton *toggle, void *opaque) { GpaCMOpenpgp *card = opaque; int idx; for (idx=0; idx < ENTRY_LAST; idx++) if (GTK_IS_TOGGLE_BUTTON (card->entries[idx]) && GTK_TOGGLE_BUTTON (card->entries[idx]) == toggle) break; if (!(idx < ENTRY_LAST)) return; if (!card->changed[idx]) { card->changed[idx] = TRUE; /* g_debug ("toggled signal for entry %d", idx); */ } } /* Callback for the "focus" signal connected to entry fields. We figure out the entry field for which this signal has been emitted and whether we are receiving or losing focus. If a the change flag has been set and we are losing focus we call the save function to store the data on the card. If the change function returns true, an error occurred and we keep the focus at the current field. */ static gboolean edit_focus_cb (GtkWidget *widget, GtkDirectionType direction, void *opaque) { GpaCMOpenpgp *card = opaque; int idx; int result = FALSE; /* Continue calling other handlers. */ for (idx=0; idx < ENTRY_LAST; idx++) if (card->entries[idx] == widget) break; if (!(idx < ENTRY_LAST)) return FALSE; /* FIXME: The next test does not work for combo boxes. Any hints on how to solve that?. */ if (gtk_widget_is_focus (widget)) { /* Entry IDX is about to lose the focus. */ /* g_debug ("entry %d is about to lose the focus", idx); */ if (card->changed[idx]) { switch (idx) { case ENTRY_FIRST_NAME: case ENTRY_LAST_NAME: result = save_entry_name (card); break; case ENTRY_LANGUAGE: result = save_entry_language (card); break; case ENTRY_SEX: /* We never get to here due to the ComboBox. Instead we use the changed signal directly. */ /* result = save_entry_sex (card);*/ break; case ENTRY_PUBKEY_URL: result = save_entry_pubkey_url (card); break; case ENTRY_LOGIN: result = save_entry_login (card); break; case ENTRY_SIG_FORCE_PIN: result = save_entry_sig_force_pin (card); break; } if (!result) { card->changed[idx] = 0; show_edit_error (card, NULL); } } } else { /* Entry IDX is about to receive the focus. */ /* g_debug ("entry %d is about to receive the focus", idx); */ show_edit_error (card, NULL); } return result; } /* The button to change the PIN with number PINNO has been clicked. */ static void change_pin (GpaCMOpenpgp *card, int pinno) { gpg_error_t err, operr; GtkWidget *dialog; gpgme_ctx_t gpgagent; int reset_mode = 0; int unblock_pin = 0; const char *string; int okay; gpgagent = GPA_CM_OBJECT (card)->agent_ctx; g_return_if_fail (gpgagent); g_return_if_fail (pinno >= 0 && pinno < DIM (card->change_pin_btn)); if (!card->is_v2 && pinno == 1) return; /* ooops, we should never get to here. */ if (pinno == 0 && !card->is_v2) reset_mode = (!card->retry_counter[0] || !card->retry_counter[1]); else if (pinno == 0 && !card->retry_counter[0] && card->retry_counter[1]) { unblock_pin = 1; pinno = 1; } else if (!card->retry_counter[pinno]) reset_mode = 1; /* g_debug ("%s pin for PIN %d", reset_mode? "reset":"change", pinno); */ if (unblock_pin) string = _("Unblocking the PIN\n" "\n" "The retry counter of the PIN is down to zero " "but a Reset Code has been set.\n" "\n" "The Reset Code is similar to a PUK (PIN Unblocking Code)" "and used to unblock a PIN without the need to know the " "Admin-PIN.\n" "\n" "If you proceed you will be asked to enter the current " "value of the Reset Code and then to enter a new " "value for the PIN and repeat that new value at another " "prompt."); else if (!reset_mode && pinno == 0) string = _("Changing the PIN\n" "\n" "If you proceed you will be asked to enter " "the current value of the PIN and then to enter a new " "value and repeat that value at another prompt.\n" "\n" "Entering a wrong value for the PIN " "decrements the retry counter. If the retry counters " "of the PIN and of the Reset Code are both down " "to zero, the PIN can still be reset by using the " "Admin-PIN.\n" "\n" "A fresh standard card has set the PIN to the value " "123456. However, the issuer of your card might " "have initialized the card with a different PIN. " "Please check the instructions of your issuer."); else if (!reset_mode && pinno == 1) string = _("Changing the Reset Code\n" "\n" "The Reset Code is similar to a PUK (PIN Unblocking Code) " "and used to unblock a PIN without the need to know the " "Admin-PIN.\n" "\n" "If you proceed you will be asked to enter the current " "value of the PIN and then to enter a new value for the " "Reset Code and repeat that new value at another prompt."); else if (reset_mode && pinno < 2) string = _("Resetting the PIN or the Reset Code\n" "\n" "If the retry counters of the PIN and of the Reset Code are " "both down to zero, it is only possible to reset them if you " "have access to the Admin-PIN.\n" "\n" "A fresh standard card has set the Admin-PIN to the value " "12345678. However, the issuer of your card might " "have initialized the card with a different Admin-PIN and " "that Admin-PIN might only be known to the issuer. " "Please check the instructions of your issuer.\n" "\n" "If you proceed you will be asked to enter the current " "value of the Admin-PIN and then to enter a new " "value for the PIN or the Reset Code and repeat that new " "value at another prompt."); else if (pinno == 2) string = _("Changing the Admin-PIN\n" "\n" "If you know the Admin-PIN you may change the Admin-PIN.\n" "\n" "The Admin-PIN is required to create keys on the card and to " "change other data. You may or may not know the Admin-PIN. " "A fresh standard card has set the Admin-PIN to the value " "12345678. However, the issuer of your card might " "have initialized the card with a different Admin-PIN and " "that Admin-PIN might only be known to the issuer. " "Please check the instructions of your issuer.\n" "\n" "If you proceed you will be asked to enter the current " "value of the Admin-PIN and then to enter a new " "value for that Admin-PIN and repeat that new " "value at another prompt."); else string = "oops"; /* FIXME: How do we figure out our GtkWindow? */ dialog = gtk_message_dialog_new_with_markup (NULL /*GTK_WINDOW (card)*/, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_OK_CANCEL, NULL); gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dialog), string); okay = (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK); if (okay) { char command[100]; snprintf (command, sizeof command, "SCD PASSWD%s %d", reset_mode? " --reset":"", pinno+1); err = gpgme_op_assuan_transact_ext (gpgagent, command, NULL, NULL, NULL, NULL, NULL, NULL, &operr); if (!err) err = operr; if (gpg_err_code (err) == GPG_ERR_CANCELED) okay = 0; /* No need to reload the data. */ else if (err) { char *message = g_strdup_printf (_("Error changing or resetting the PIN/PUK.\n" "(%s <%s>)"), gpg_strerror (err), gpg_strsource (err)); gpa_window_error (message, NULL); xfree (message); } } gtk_widget_destroy (GTK_WIDGET (dialog)); if (okay) reload_data (card); } /* Handler for the "clicked" signal connected to the Change PIN buttons. */ static void change_pin_clicked_cb (void *widget, void *user_data) { GpaCMOpenpgp *card = user_data; int idx; for (idx=0; idx < 3; idx++) if (widget == card->change_pin_btn[idx]) { change_pin (card, idx); break; } } static void entry_fpr_expanded_cb (GObject *object, GParamSpec *dummy, void *user_data) { GtkExpander *expander = GTK_EXPANDER (object); GpaCMOpenpgp *card = user_data; int keyidx; (void)dummy; if (GTK_WIDGET (expander) == card->entries[ENTRY_KEY_SIG]) keyidx = 0; else if (GTK_WIDGET (expander) == card->entries[ENTRY_KEY_ENC]) keyidx = 1; else if (GTK_WIDGET (expander) == card->entries[ENTRY_KEY_AUTH]) keyidx = 2; else g_return_if_fail (!"invalid key entry"); if (gtk_expander_get_expanded (expander)) { const char *fpr, *src; char *rawfpr, *dst; if (!card->key_details[keyidx]) { card->key_details[keyidx] = gpa_key_details_new (); gtk_container_add (GTK_CONTAINER (expander), card->key_details[keyidx]); } fpr = gtk_expander_get_label (expander); if (fpr) { rawfpr = dst = xmalloc (2 + strlen (fpr) + 1); *dst++ = '0'; *dst++ = 'x'; for (src=fpr; *src; src++) if (*src != ' ' && *src != ':') *dst++ = *src; *dst = 0; } else rawfpr = NULL; if (rawfpr) gpa_key_details_find (card->key_details[keyidx], rawfpr); else gpa_key_details_update (card->key_details[keyidx], NULL, 0); xfree (rawfpr); } else { if (card->key_details[keyidx]) gtk_widget_destroy (card->key_details[keyidx]); card->key_details[keyidx] = NULL; } } /* Menu function to fetch the key from the URL. */ static void pubkey_url_menu_fetch_key (GtkMenuItem *menuitem, void *opaque) { GpaCMOpenpgp *card = opaque; const char *url; gpg_error_t err; gpgme_data_t urllist; gpgme_ctx_t ctx; (void)menuitem; url = gtk_entry_get_text (GTK_ENTRY (card->entries[ENTRY_PUBKEY_URL])); if (!url || !*url) return; err = gpgme_data_new_from_mem (&urllist, url, strlen (url), 1); if (err) gpa_gpgme_error (err); gpgme_data_set_encoding (urllist, GPGME_DATA_ENCODING_URL0); ctx = gpa_gpgme_new (); err = gpgme_op_import (ctx, urllist); if (err) { char *message = g_strdup_printf (_("Error fetching the key.\n" "(%s <%s>)"), gpg_strerror (err), gpg_strsource (err)); gpa_cm_object_alert_dialog (GPA_CM_OBJECT (card), message); xfree (message); } else { gpgme_import_result_t impres; impres = gpgme_op_import_result (ctx); if (impres) { char *message = g_strdup_printf (_("Keys found: %d, imported: %d, unchanged: %d"), impres->considered, impres->imported, impres->unchanged); gpa_cm_object_update_status (GPA_CM_OBJECT(card), message); xfree (message); } } gpgme_release (ctx); gpgme_data_release (urllist); } /* Add a context menu item to the pubkey URL box. */ static void pubkey_url_populate_popup_cb (GtkEntry *entry, GtkMenu *menu, void *opaque) { GtkWidget *menuitem; menuitem = gtk_separator_menu_item_new (); gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menuitem); gtk_widget_show (menuitem); menuitem = gtk_menu_item_new_with_label (_("Fetch Key")); gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menuitem); gtk_widget_show (menuitem); g_signal_connect (menuitem, "activate", G_CALLBACK (pubkey_url_menu_fetch_key), opaque); } /* Helper for construct_data_widget. Returns the label widget. */ static GtkLabel * add_table_row (GtkWidget *table, int *rowidx, const char *labelstr, GtkWidget *widget, GtkWidget *widget2, int readonly, int compact) { GtkWidget *label; int is_label = GTK_IS_LABEL (widget); label = gtk_label_new (labelstr); gtk_label_set_width_chars (GTK_LABEL (label), 22); gtk_misc_set_alignment (GTK_MISC (label), 0, 0); gtk_table_attach (GTK_TABLE (table), label, 0, 1, *rowidx, *rowidx + 1, GTK_FILL, GTK_FILL, 0, 0); if (is_label) gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5); if (readonly) { if (!is_label && GTK_IS_ENTRY (widget)) { gtk_entry_set_has_frame (GTK_ENTRY (widget), FALSE); gtk_entry_set_editable (GTK_ENTRY (widget), FALSE); } } else { if (is_label) gtk_label_set_selectable (GTK_LABEL (widget), TRUE); } gtk_table_attach (GTK_TABLE (table), widget, 1, 2, *rowidx, *rowidx + 1, (compact ? GTK_FILL : (GTK_FILL | GTK_EXPAND)), GTK_SHRINK, 0, 0); if (widget2) gtk_table_attach (GTK_TABLE (table), widget2, 2, 3, *rowidx, *rowidx + 1, GTK_SHRINK, GTK_SHRINK, 0, 0); ++*rowidx; return GTK_LABEL (label); } /* This function constructs the container holding all widgets making up this data widget. It is called during instance creation. */ static void construct_data_widget (GpaCMOpenpgp *card) { GtkWidget *label; GtkWidget *general_frame; GtkWidget *general_table; GtkWidget *personal_frame; GtkWidget *personal_table; GtkWidget *keys_frame; GtkWidget *keys_table; GtkWidget *pin_frame; GtkWidget *pin_table; GtkWidget *button; int rowidx; int idx; /* General frame. */ general_frame = card->general_frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (general_frame), GTK_SHADOW_NONE); label = gtk_label_new (_("General")); gtk_label_set_use_markup (GTK_LABEL (label), TRUE); gtk_frame_set_label_widget (GTK_FRAME (general_frame), label); general_table = gtk_table_new (4, 3, FALSE); gtk_container_set_border_width (GTK_CONTAINER (general_table), 10); rowidx = 0; card->entries[ENTRY_SERIALNO] = gtk_label_new (NULL); add_table_row (general_table, &rowidx, _("Serial number:"), card->entries[ENTRY_SERIALNO], NULL, 0, 1); card->entries[ENTRY_VERSION] = gtk_label_new (NULL); add_table_row (general_table, &rowidx, _("Card version:"), card->entries[ENTRY_VERSION], NULL, 0, 1); card->entries[ENTRY_MANUFACTURER] = gtk_label_new (NULL); add_table_row (general_table, &rowidx, _("Manufacturer:"), card->entries[ENTRY_MANUFACTURER], NULL, 0, 1); gtk_container_add (GTK_CONTAINER (general_frame), general_table); /* Personal frame. */ personal_frame = card->personal_frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (personal_frame), GTK_SHADOW_NONE); label = gtk_label_new (_("Personal")); gtk_label_set_use_markup (GTK_LABEL (label), TRUE); gtk_frame_set_label_widget (GTK_FRAME (personal_frame), label); personal_table = gtk_table_new (6, 3, FALSE); gtk_container_set_border_width (GTK_CONTAINER (personal_table), 10); rowidx = 0; card->entries[ENTRY_SEX] = gtk_combo_box_new_text (); for (idx=0; "mfu"[idx]; idx++) gtk_combo_box_append_text (GTK_COMBO_BOX (card->entries[ENTRY_SEX]), gpa_sex_char_to_string ("mfu"[idx])); add_table_row (personal_table, &rowidx, _("Salutation:"), card->entries[ENTRY_SEX], NULL, 0, 0); card->entries[ENTRY_FIRST_NAME] = gtk_entry_new (); gtk_entry_set_max_length (GTK_ENTRY (card->entries[ENTRY_FIRST_NAME]), 35); add_table_row (personal_table, &rowidx, _("First name:"), card->entries[ENTRY_FIRST_NAME], NULL, 0, 0); card->entries[ENTRY_LAST_NAME] = gtk_entry_new (); gtk_entry_set_max_length (GTK_ENTRY (card->entries[ENTRY_LAST_NAME]), 35); add_table_row (personal_table, &rowidx, _("Last name:"), card->entries[ENTRY_LAST_NAME], NULL, 0, 0); card->entries[ENTRY_LANGUAGE] = gtk_entry_new (); gtk_entry_set_max_length (GTK_ENTRY (card->entries[ENTRY_LANGUAGE]), 8); add_table_row (personal_table, &rowidx, _("Language:"), card->entries[ENTRY_LANGUAGE], NULL, 0, 0); card->entries[ENTRY_LOGIN] = gtk_entry_new (); gtk_entry_set_max_length (GTK_ENTRY (card->entries[ENTRY_LOGIN]), 254); add_table_row (personal_table, &rowidx, _("Login data:"), card->entries[ENTRY_LOGIN], NULL, 0, 0); card->entries[ENTRY_PUBKEY_URL] = gtk_entry_new (); gtk_entry_set_max_length (GTK_ENTRY (card->entries[ENTRY_PUBKEY_URL]), 254); add_table_row (personal_table, &rowidx, _("Public key URL:"), card->entries[ENTRY_PUBKEY_URL], NULL, 0, 0); gtk_container_add (GTK_CONTAINER (personal_frame), personal_table); /* Keys frame. */ keys_frame = card->keys_frame = gtk_expander_new (NULL); label = gtk_label_new (_("Keys")); gtk_label_set_use_markup (GTK_LABEL (label), TRUE); gtk_expander_set_label_widget (GTK_EXPANDER (keys_frame), label); gtk_expander_set_expanded (GTK_EXPANDER (keys_frame), TRUE); keys_table = gtk_table_new (4, 3, FALSE); gtk_container_set_border_width (GTK_CONTAINER (keys_table), 10); rowidx = 0; /* Fixme: A problem using an expander instead of a plain label is that the label is not selectable. Any ideas on how to fix that? Is that really required given that we can use the details page for selection. We might always want to switch between expander and label depending on whether we have a key for the fingerprint. Check whether gtk_expander_set_label_wdiget works for us. */ card->entries[ENTRY_KEY_SIG] = gtk_expander_new (NULL); add_table_row (keys_table, &rowidx, _("Signature key:"), card->entries[ENTRY_KEY_SIG], NULL, 0, 0); card->entries[ENTRY_KEY_ENC] = gtk_expander_new (NULL); add_table_row (keys_table, &rowidx, _("Encryption key:"), card->entries[ENTRY_KEY_ENC], NULL, 0, 0); card->entries[ENTRY_KEY_AUTH] = gtk_expander_new (NULL); add_table_row (keys_table, &rowidx, _("Authentication key:"), card->entries[ENTRY_KEY_AUTH], NULL, 0, 0); card->entries[ENTRY_SIG_COUNTER] = gtk_label_new (NULL); add_table_row (keys_table, &rowidx, _("Signature counter:"), card->entries[ENTRY_SIG_COUNTER], NULL, 0, 0); gtk_container_add (GTK_CONTAINER (keys_frame), keys_table); /* PIN frame. */ pin_frame = card->pin_frame = gtk_expander_new (NULL); label = gtk_label_new (_("PIN")); gtk_label_set_use_markup (GTK_LABEL (label), TRUE); gtk_expander_set_label_widget (GTK_EXPANDER (pin_frame), label); gtk_expander_set_expanded (GTK_EXPANDER (pin_frame), TRUE); pin_table = gtk_table_new (4, 3, FALSE); gtk_container_set_border_width (GTK_CONTAINER (pin_table), 10); rowidx = 0; card->entries[ENTRY_SIG_FORCE_PIN] = gtk_check_button_new (); add_table_row (pin_table, &rowidx, _("Force signature PIN:"), card->entries[ENTRY_SIG_FORCE_PIN], NULL, 0, 1); card->entries[ENTRY_PIN_RETRYCOUNTER] = gtk_label_new (NULL); button = gtk_button_new (); add_table_row (pin_table, &rowidx, _("PIN retry counter:"), card->entries[ENTRY_PIN_RETRYCOUNTER], button, 1, 1); card->change_pin_btn[0] = button; card->entries[ENTRY_PUK_RETRYCOUNTER] = gtk_label_new (NULL); button = gtk_button_new (); card->puk_label = add_table_row (pin_table, &rowidx, "", /* The label depends on the card version. */ card->entries[ENTRY_PUK_RETRYCOUNTER], button, 1, 1); card->change_pin_btn[1] = button; card->entries[ENTRY_ADMIN_PIN_RETRYCOUNTER] = gtk_label_new (NULL); button = gtk_button_new (); add_table_row (pin_table, &rowidx, _("Admin-PIN retry counter:"), card->entries[ENTRY_ADMIN_PIN_RETRYCOUNTER], button, 1, 1); card->change_pin_btn[2] = button; gtk_container_add (GTK_CONTAINER (pin_frame), pin_table); /* Put all frames together. */ gtk_box_pack_start (GTK_BOX (card), general_frame, FALSE, TRUE, 0); gtk_box_pack_start (GTK_BOX (card), personal_frame, FALSE, TRUE, 0); gtk_box_pack_start (GTK_BOX (card), keys_frame, FALSE, TRUE, 0); gtk_box_pack_start (GTK_BOX (card), pin_frame, FALSE, TRUE, 0); /* Setup signals */ for (idx=0; idx < ENTRY_LAST; idx++) { if (!card->entries[idx]) continue; switch (idx) { case ENTRY_SEX: g_signal_connect (G_OBJECT (card->entries[idx]), "changed", G_CALLBACK (edit_changed_combo_box_cb), card); g_signal_connect (G_OBJECT (card->entries[idx]), "focus", G_CALLBACK (edit_focus_cb), card); break; case ENTRY_SIG_FORCE_PIN: g_signal_connect (G_OBJECT (card->entries[idx]), "toggled", G_CALLBACK (edit_toggled_cb), card); g_signal_connect (G_OBJECT (card->entries[idx]), "focus", G_CALLBACK (edit_focus_cb), card); break; case ENTRY_KEY_SIG: case ENTRY_KEY_ENC: case ENTRY_KEY_AUTH: g_signal_connect (G_OBJECT (card->entries[idx]), "notify::expanded", G_CALLBACK (entry_fpr_expanded_cb), card); break; default: if (!GTK_IS_LABEL (card->entries[idx])) { g_signal_connect (G_OBJECT (card->entries[idx]), "changed", G_CALLBACK (edit_changed_cb), card); g_signal_connect (G_OBJECT (card->entries[idx]), "focus", G_CALLBACK (edit_focus_cb), card); } break; } } for (idx=0; idx < 3; idx++) g_signal_connect (G_OBJECT (card->change_pin_btn[idx]), "clicked", G_CALLBACK (change_pin_clicked_cb), card); g_signal_connect (G_OBJECT (card->entries[ENTRY_PUBKEY_URL]), "populate-popup", G_CALLBACK (pubkey_url_populate_popup_cb), card); } /************************************************************ ****************** Object Management ******************** ************************************************************/ static void gpa_cm_openpgp_class_init (void *class_ptr, void *class_data) { GpaCMOpenpgpClass *klass = class_ptr; parent_class = g_type_class_peek_parent (klass); G_OBJECT_CLASS (klass)->finalize = gpa_cm_openpgp_finalize; } static void gpa_cm_openpgp_init (GTypeInstance *instance, void *class_ptr) { GpaCMOpenpgp *card = GPA_CM_OPENPGP (instance); construct_data_widget (card); } static void gpa_cm_openpgp_finalize (GObject *object) { GpaCMOpenpgp *card = GPA_CM_OPENPGP (object); xfree (card->key_attributes); card->key_attributes = NULL; parent_class->finalize (object); } /* Construct the class. */ GType gpa_cm_openpgp_get_type (void) { static GType this_type = 0; if (!this_type) { static const GTypeInfo this_info = { sizeof (GpaCMOpenpgpClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, gpa_cm_openpgp_class_init, (GClassFinalizeFunc) NULL, NULL, /* class_data */ sizeof (GpaCMOpenpgp), 0, /* n_preallocs */ gpa_cm_openpgp_init }; this_type = g_type_register_static (GPA_CM_OBJECT_TYPE, "GpaCMOpenpgp", &this_info, 0); } return this_type; } /************************************************************ ********************** Public API ************************ ************************************************************/ GtkWidget * gpa_cm_openpgp_new () { return GTK_WIDGET (g_object_new (GPA_CM_OPENPGP_TYPE, NULL)); } /* If WIDGET is of type GpaCMOpenpgp do a data reload through the Assuan connection identified by GPGAGENT. This will keep a reference to GPGAGENT for later processing. Passing NULL for GPGAGENT removes this reference. */ void gpa_cm_openpgp_reload (GtkWidget *widget, gpgme_ctx_t gpgagent) { if (GPA_IS_CM_OPENPGP (widget)) { GPA_CM_OBJECT (widget)->agent_ctx = gpgagent; if (gpgagent) reload_data (GPA_CM_OPENPGP (widget)); } } char * gpa_cm_openpgp_get_key_attributes (GtkWidget *widget) { g_return_val_if_fail (GPA_IS_CM_OPENPGP (widget), NULL); return g_strdup (GPA_CM_OPENPGP (widget)->key_attributes); } diff --git a/src/cm-openpgp.h b/src/cm-openpgp.h index e34c359..0c82003 100644 --- a/src/cm-openpgp.h +++ b/src/cm-openpgp.h @@ -1,58 +1,68 @@ /* cm-openpgp.h - OpenPGP card part for the card manager. * Copyright (C) 2009 g10 Code GmbH * * This file is part of GPA. * - * GPA 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 3 of the License, or - * (at your option) any later version. + * GPA is free software; you can redistribute and/or modify this part + * of GPA under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. * * GPA 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, see . + * along with this program; if not, see . */ #ifndef CM_OPENPGP_H #define CM_OPENPGP_H #include /* Declare the Object. */ typedef struct _GpaCMOpenpgp GpaCMOpenpgp; typedef struct _GpaCMOpenpgpClass GpaCMOpenpgpClass; GType gpa_cm_openpgp_get_type (void) G_GNUC_CONST; #define GPA_CM_OPENPGP_TYPE (gpa_cm_openpgp_get_type ()) #define GPA_CM_OPENPGP(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GPA_CM_OPENPGP_TYPE, GpaCMOpenpgp)) #define GPA_CM_OPENPGP_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ GPA_CM_OPENPGP_TYPE, GpaCMOpenpgpClass)) #define GPA_IS_CM_OPENPGP(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GPA_CM_OPENPGP_TYPE)) #define GPA_IS_CM_OPENPGP_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), GPA_CM_OPENPGP_TYPE)) #define GPA_CM_OPENPGP_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ GPA_CM_OPENPGP_TYPE, GpaCMOpenpgpClass)) /* The class specific API. */ GtkWidget *gpa_cm_openpgp_new (void); void gpa_cm_openpgp_reload (GtkWidget *widget, gpgme_ctx_t gpgagent); char *gpa_cm_openpgp_get_key_attributes (GtkWidget *widget); #endif /*CM_OPENPGP_H*/ diff --git a/src/cm-unknown.c b/src/cm-unknown.c index db27058..e560b84 100644 --- a/src/cm-unknown.c +++ b/src/cm-unknown.c @@ -1,253 +1,263 @@ /* cm-unknown.c - Widget to show information about an unknown card * Copyright (C) 2009, 2011 g10 Code GmbH * * This file is part of GPA. * - * GPA 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 3 of the License, or - * (at your option) any later version. + * GPA is free software; you can redistribute and/or modify this part + * of GPA under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. * * GPA 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, see . */ -/* UNKNOWN is a dummy application used for, weel, unknown cards. It +/* UNKNOWN is a dummy application used for, well, unknown cards. It does only print the the ATR of the card. */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include "gpa.h" #include "gtktools.h" #include "convert.h" #include "membuf.h" #include "cm-object.h" #include "cm-unknown.h" /* Object's class definition. */ struct _GpaCMUnknownClass { GpaCMObjectClass parent_class; }; /* Object definition. */ struct _GpaCMUnknown { GpaCMObject parent_instance; GtkWidget *label; int reloading; /* Sentinel to avoid recursive reloads. */ }; /* The parent class. */ static GObjectClass *parent_class; /* Local prototypes */ static void gpa_cm_unknown_finalize (GObject *object); /************************************************************ ******************* Implementation ********************* ************************************************************/ static gpg_error_t scd_atr_data_cb (void *opaque, const void *data, size_t datalen) { membuf_t *mb = opaque; put_membuf (mb, data, datalen); return 0; } /* Use the assuan machinery to read the ATR. */ static void reload_data (GpaCMUnknown *card) { gpg_error_t err, operr; char command[100]; gpgme_ctx_t gpgagent; membuf_t mb; char *buf; gpgagent = GPA_CM_OBJECT (card)->agent_ctx; g_return_if_fail (gpgagent); card->reloading++; init_membuf (&mb, 512); err = gpgme_op_assuan_transact_ext (gpgagent, "SCD APDU --dump-atr", scd_atr_data_cb, &mb, NULL, NULL, NULL, NULL, &operr); if (!err) err = operr; if (!err) { put_membuf (&mb, "", 1); buf = get_membuf (&mb, NULL); if (buf) { buf = g_strdup_printf ("\n%s\n%s", _("The ATR of the card is:"), buf); gtk_label_set_text (GTK_LABEL (card->label), buf); g_free (buf); } else gtk_label_set_text (GTK_LABEL (card->label), ""); } else { g_free (get_membuf (&mb, NULL)); if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT) ; /* Lost the card. */ else g_debug ("assuan command `%s' failed: %s <%s>\n", command, gpg_strerror (err), gpg_strsource (err)); gtk_label_set_text (GTK_LABEL (card->label), ""); } card->reloading--; } /* This function constructs the container holding all widgets making up this data widget. It is called during instance creation. */ static void construct_data_widget (GpaCMUnknown *card) { GtkWidget *frame; GtkWidget *vbox; GtkWidget *label; frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE); label = gtk_label_new (_("Unknown Card")); gtk_label_set_use_markup (GTK_LABEL (label), TRUE); gtk_frame_set_label_widget (GTK_FRAME (frame), label); gtk_box_pack_start (GTK_BOX (card), frame, FALSE, TRUE, 0); frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE); vbox = gtk_vbox_new (FALSE, 5); card->label = gtk_label_new (NULL); gtk_box_pack_start (GTK_BOX (vbox), card->label, TRUE, TRUE, 0); gtk_container_add (GTK_CONTAINER (frame), vbox); gtk_box_pack_start (GTK_BOX (card), frame, FALSE, TRUE, 0); } /************************************************************ ****************** Object Management ******************** ************************************************************/ static void gpa_cm_unknown_class_init (void *class_ptr, void *class_data) { GpaCMUnknownClass *klass = class_ptr; parent_class = g_type_class_peek_parent (klass); G_OBJECT_CLASS (klass)->finalize = gpa_cm_unknown_finalize; } static void gpa_cm_unknown_init (GTypeInstance *instance, void *class_ptr) { GpaCMUnknown *card = GPA_CM_UNKNOWN (instance); construct_data_widget (card); } static void gpa_cm_unknown_finalize (GObject *object) { /* GpaCMUnknown *card = GPA_CM_UNKNOWN (object); */ parent_class->finalize (object); } /* Construct the class. */ GType gpa_cm_unknown_get_type (void) { static GType this_type = 0; if (!this_type) { static const GTypeInfo this_info = { sizeof (GpaCMUnknownClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, gpa_cm_unknown_class_init, (GClassFinalizeFunc) NULL, NULL, /* class_data */ sizeof (GpaCMUnknown), 0, /* n_preallocs */ gpa_cm_unknown_init }; this_type = g_type_register_static (GPA_CM_OBJECT_TYPE, "GpaCMUnknown", &this_info, 0); } return this_type; } /************************************************************ ********************** Public API ************************ ************************************************************/ GtkWidget * gpa_cm_unknown_new () { return GTK_WIDGET (g_object_new (GPA_CM_UNKNOWN_TYPE, NULL)); } /* If WIDGET is of Type GpaCMUnknown do a data reload through the assuan connection. */ void gpa_cm_unknown_reload (GtkWidget *widget, gpgme_ctx_t gpgagent) { if (GPA_IS_CM_UNKNOWN (widget)) { GPA_CM_OBJECT (widget)->agent_ctx = gpgagent; if (gpgagent) reload_data (GPA_CM_UNKNOWN (widget)); } } diff --git a/src/cm-unknown.h b/src/cm-unknown.h index 41f44ff..352cd97 100644 --- a/src/cm-unknown.h +++ b/src/cm-unknown.h @@ -1,57 +1,67 @@ /* cm-unknown.h - Widget to show information about an unknown card. * Copyright (C) 2009, 2011 g10 Code GmbH * * This file is part of GPA. * - * GPA 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 3 of the License, or - * (at your option) any later version. + * GPA is free software; you can redistribute and/or modify this part + * of GPA under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. * * GPA 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, see . */ #ifndef CM_UNKNOWN_H #define CM_UNKNOWN_H #include /* Declare the Object. */ typedef struct _GpaCMUnknown GpaCMUnknown; typedef struct _GpaCMUnknownClass GpaCMUnknownClass; GType gpa_cm_unknown_get_type (void) G_GNUC_CONST; #define GPA_CM_UNKNOWN_TYPE (gpa_cm_unknown_get_type ()) #define GPA_CM_UNKNOWN(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GPA_CM_UNKNOWN_TYPE, GpaCMUnknown)) #define GPA_CM_UNKNOWN_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ GPA_CM_UNKNOWN_TYPE, GpaCMUnknownClass)) #define GPA_IS_CM_UNKNOWN(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GPA_CM_UNKNOWN_TYPE)) #define GPA_IS_CM_UNKNOWN_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), GPA_CM_UNKNOWN_TYPE)) #define GPA_CM_UNKNOWN_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ GPA_CM_UNKNOWN_TYPE, GpaCMUnknownClass)) /* The class specific API. */ GtkWidget *gpa_cm_unknown_new (void); void gpa_cm_unknown_reload (GtkWidget *widget, gpgme_ctx_t gpgagent); #endif /*CM_UNKNOWN_H*/