/* * Copyright (C) 2011 Collabora Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * * Author: Stef Walter */ #include "config.h" #include "gcr/gcr-icons.h" #include "gcr/gcr-gnupg-records.h" #include "gcr/gcr-openpgp.h" #include "gcr/gcr-simple-certificate.h" #include "gcr/gcr-types.h" #include "gcr-display-view.h" #include "gcr-gnupg-renderer.h" #include "gcr-renderer.h" #include "gck/gck.h" #include "egg/egg-hex.h" #include #include #include enum { PROP_0, PROP_RECORDS, PROP_LABEL, PROP_ATTRIBUTES }; struct _GcrGnupgRendererPrivate { GPtrArray *records; GckAttributes *attrs; gchar *label; }; static void _gcr_gnupg_renderer_iface_init (GcrRendererIface *iface); G_DEFINE_TYPE_WITH_CODE (GcrGnupgRenderer, _gcr_gnupg_renderer, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GCR_TYPE_RENDERER, _gcr_gnupg_renderer_iface_init); ); /* ----------------------------------------------------------------------------- * INTERNAL */ static gchar * calculate_label (GcrGnupgRenderer *self) { gchar *userid; gchar *label = NULL; if (self->pv->attrs) { if (gck_attributes_find_string (self->pv->attrs, CKA_LABEL, &label)) return label; } userid = _gcr_gnupg_records_get_user_id (self->pv->records); if (userid != NULL) { if (!_gcr_gnupg_records_parse_user_id (userid, &label, NULL, NULL)) label = NULL; } if (label != NULL) return label; if (self->pv->label) return g_strdup (self->pv->label); return g_strdup (_("PGP Key")); } static void _gcr_gnupg_renderer_init (GcrGnupgRenderer *self) { self->pv = (G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_GNUPG_RENDERER, GcrGnupgRendererPrivate)); } static void _gcr_gnupg_renderer_finalize (GObject *obj) { GcrGnupgRenderer *self = GCR_GNUPG_RENDERER (obj); gck_attributes_unref (self->pv->attrs); g_free (self->pv->label); if (self->pv->records) g_ptr_array_unref (self->pv->records); G_OBJECT_CLASS (_gcr_gnupg_renderer_parent_class)->finalize (obj); } static void _gcr_gnupg_renderer_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) { GcrGnupgRenderer *self = GCR_GNUPG_RENDERER (obj); switch (prop_id) { case PROP_RECORDS: _gcr_gnupg_renderer_set_records (self, g_value_get_boxed (value)); break; case PROP_LABEL: g_free (self->pv->label); self->pv->label = g_value_dup_string (value); g_object_notify (obj, "label"); gcr_renderer_emit_data_changed (GCR_RENDERER (self)); break; case PROP_ATTRIBUTES: _gcr_gnupg_renderer_set_attributes (self, g_value_get_boxed (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void _gcr_gnupg_renderer_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) { GcrGnupgRenderer *self = GCR_GNUPG_RENDERER (obj); switch (prop_id) { case PROP_RECORDS: g_value_set_object (value, self->pv->records); break; case PROP_LABEL: g_value_take_string (value, calculate_label (self)); break; case PROP_ATTRIBUTES: g_value_set_boxed (value, self->pv->attrs); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void _gcr_gnupg_renderer_class_init (GcrGnupgRendererClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GckBuilder builder = GCK_BUILDER_INIT; _gcr_gnupg_renderer_parent_class = g_type_class_peek_parent (klass); g_type_class_add_private (klass, sizeof (GcrGnupgRendererPrivate)); gobject_class->finalize = _gcr_gnupg_renderer_finalize; gobject_class->set_property = _gcr_gnupg_renderer_set_property; gobject_class->get_property = _gcr_gnupg_renderer_get_property; g_object_class_install_property (gobject_class, PROP_RECORDS, g_param_spec_boxed ("records", "Records", "Gnupg records to display", G_TYPE_PTR_ARRAY, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_ATTRIBUTES, g_param_spec_boxed ("attributes", "Attributes", "Certificate pkcs11 attributes", GCK_TYPE_ATTRIBUTES, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_LABEL, g_param_spec_string ("label", "Label", "Certificate Label", "", G_PARAM_READWRITE)); /* Register this as a renderer which can be loaded */ gck_builder_add_ulong (&builder, CKA_CLASS, CKO_GCR_GNUPG_RECORDS); gcr_renderer_register (GCR_TYPE_GNUPG_RENDERER, gck_builder_end (&builder)); } static const gchar * name_for_algo (guint algo) { switch (algo) { case GCR_OPENPGP_ALGO_RSA: case GCR_OPENPGP_ALGO_RSA_E: case GCR_OPENPGP_ALGO_RSA_S: return _("RSA"); case GCR_OPENPGP_ALGO_ELG_E: return _("Elgamal"); case GCR_OPENPGP_ALGO_DSA: return _("DSA"); default: return NULL; } } static const gchar * capability_for_code (gchar code) { switch (code) { case 'e': case 'E': return _("Encrypt"); case 's': case 'S': return _("Sign"); case 'c': case 'C': return _("Certify"); case 'a': case 'A': return _("Authenticate"); case 'D': return C_("capability", "Disabled"); default: return NULL; } } static gchar * capabilities_for_codes (const gchar *codes) { const gchar *cap; GString *result; guint i; result = g_string_new (""); for (i = 0; codes[i] != 0; i++) { if (result->len) g_string_append_unichar (result, GCR_DISPLAY_VIEW_LINE_BREAK); cap = capability_for_code (codes[i]); if (cap != NULL) g_string_append (result, cap); else g_string_append_c (result, codes[i]); } return g_string_free (result, FALSE); } static const gchar * status_for_code (gchar code) { switch (code) { case 'o': return _("Unknown"); case 'i': return _("Invalid"); case 'd': return C_("ownertrust", "Disabled"); case 'r': return _("Revoked"); case 'e': return _("Expired"); case 'q': case '-': return _("Undefined trust"); case 'n': return _("Distrusted"); case 'm': return _("Marginally trusted"); case 'f': return _("Fully trusted"); case 'u': return _("Ultimately trusted"); default: return NULL; } } static const gchar * message_for_code (gchar code, GtkMessageType *message_type) { *message_type = GTK_MESSAGE_OTHER; switch (code) { case 'o': *message_type = GTK_MESSAGE_QUESTION; return _("The information in this key has not yet been verified"); case 'i': *message_type = GTK_MESSAGE_ERROR; return _("This key is invalid"); case 'd': *message_type = GTK_MESSAGE_WARNING; return _("This key has been disabled"); case 'r': *message_type = GTK_MESSAGE_ERROR; return _("This key has been revoked"); case 'e': *message_type = GTK_MESSAGE_ERROR; return _("This key has expired"); case 'q': case '-': return NULL; case 'n': *message_type = GTK_MESSAGE_WARNING; return _("This key is distrusted"); case 'm': *message_type = GTK_MESSAGE_OTHER; return _("This key is marginally trusted"); case 'f': *message_type = GTK_MESSAGE_OTHER; return _("This key is fully trusted"); case 'u': *message_type = GTK_MESSAGE_OTHER; return _("This key is ultimately trusted"); default: return NULL; } } static void append_key_record (GcrGnupgRenderer *self, GcrDisplayView *view, GcrRecord *record, const gchar *title) { GcrRenderer *renderer = GCR_RENDERER (self); const gchar *value; gchar *display; GDateTime *date; gchar code; guint algo; guint bits; _gcr_display_view_append_heading (view, renderer, title); /* Key ID */ value = _gcr_record_get_raw (record, GCR_RECORD_KEY_KEYID); if (value != NULL) _gcr_display_view_append_value (view, renderer, _("Key ID"), value, TRUE); /* Algorithm */ if (_gcr_record_get_uint (record, GCR_RECORD_KEY_ALGO, &algo)) { display = NULL; value = name_for_algo (algo); if (value == NULL) value = display = g_strdup_printf ("%u", algo); _gcr_display_view_append_value (view, renderer, _("Algorithm"), value, FALSE); g_free (display); } /* Key Size */ if (_gcr_record_get_uint (record, GCR_RECORD_KEY_BITS, &bits)) { display = g_strdup_printf ("%u", bits); _gcr_display_view_append_value (view, renderer, _("Key Size"), display, FALSE); g_free (display); } /* Created */ date = _gcr_record_get_date (record, GCR_RECORD_KEY_TIMESTAMP); if (date != NULL) { display = g_date_time_format (date, "%x"); _gcr_display_view_append_value (view, renderer, _("Created"), display, FALSE); g_free (display); g_date_time_unref (date); } /* Expiry */ date = _gcr_record_get_date (record, GCR_RECORD_KEY_EXPIRY); if (date != NULL) { display = g_date_time_format (date, "%x"); _gcr_display_view_append_value (view, renderer, _("Expiry"), display, FALSE); g_free (display); g_date_time_unref (date); } /* Capabilities */ value = _gcr_record_get_raw (record, GCR_RECORD_PUB_CAPS); if (value != NULL && value[0] != '\0') { display = capabilities_for_codes (value); _gcr_display_view_append_value (view, renderer, _("Capabilities"), display, FALSE); g_free (display); } /* Owner Trust */ code = _gcr_record_get_char (record, GCR_RECORD_KEY_OWNERTRUST); if (code != 0) { display = NULL; value = status_for_code (code); if (value == NULL) { value = display = g_new0 (gchar, 2); display[0] = code; } _gcr_display_view_append_value (view, renderer, _("Owner trust"), value, FALSE); g_free (display); } } static void append_uid_record (GcrGnupgRenderer *self, GcrDisplayView *view, GcrRecord *record) { GcrRenderer *renderer = GCR_RENDERER (self); gchar *userid; gchar *name; gchar *comment; gchar *email; GDateTime *date; gchar *display; _gcr_display_view_append_heading (view, renderer, _("User ID")); userid = _gcr_record_get_string (record, GCR_RECORD_UID_USERID); if (userid == NULL) { _gcr_display_view_append_value (view, renderer, _("Value"), _("Unknown"), FALSE); return; } if (_gcr_gnupg_records_parse_user_id (userid, &name, &email, &comment)) { if (name != NULL) _gcr_display_view_append_value (view, renderer, _("Name"), name, FALSE); g_free (name); if (email != NULL) _gcr_display_view_append_value (view, renderer, _("Email"), email, FALSE); g_free (email); if (comment != NULL) _gcr_display_view_append_value (view, renderer, _("Comment"), comment, FALSE); g_free (comment); /* Unparseable user id */ } else { _gcr_display_view_append_value (view, renderer, _("Value"), userid, FALSE); } /* Created */ date = _gcr_record_get_date (record, GCR_RECORD_UID_TIMESTAMP); if (date != NULL) { display = g_date_time_format (date, "%x"); _gcr_display_view_append_value (view, renderer, _("Created"), display, FALSE); g_free (display); g_date_time_unref (date); } /* Expiry */ date = _gcr_record_get_date (record, GCR_RECORD_UID_EXPIRY); if (date != NULL) { display = g_date_time_format (date, "%x"); _gcr_display_view_append_value (view, renderer, _("Expiry"), display, FALSE); g_free (display); g_date_time_unref (date); } g_free (userid); } static void append_uat_record (GcrGnupgRenderer *self, GcrDisplayView *view, GcrRecord *record) { GcrRenderer *renderer = GCR_RENDERER (self); gchar **parts; gchar *display; const gchar *value; GDateTime *date; _gcr_display_view_append_heading (view, renderer, _("User Attribute")); /* Size */ value = _gcr_record_get_raw (record, GCR_RECORD_UAT_COUNT_SIZE); if (value != NULL) { parts = g_strsplit (value, " ", 2); if (parts && parts[0] && parts[1]) _gcr_display_view_append_value (view, renderer, _("Size"), parts[1], FALSE); g_strfreev (parts); } /* Created */ date = _gcr_record_get_date (record, GCR_RECORD_KEY_TIMESTAMP); if (date != NULL) { display = g_date_time_format (date, "%x"); _gcr_display_view_append_value (view, renderer, _("Created"), display, FALSE); g_free (display); g_date_time_unref (date); } /* Expiry */ date = _gcr_record_get_date (record, GCR_RECORD_KEY_EXPIRY); if (date != NULL) { display = g_date_time_format (date, "%x"); _gcr_display_view_append_value (view, renderer, _("Expiry"), display, FALSE); g_free (display); g_date_time_unref (date); } } static const gchar * signature_klass_string (const gchar *klass) { char *end; guint val; val = strtoul (klass, &end, 16); if (end != klass + 2) return NULL; switch (val) { case 0x00: return _("Signature of a binary document"); case 0x01: return _("Signature of a canonical text document"); case 0x02: return _("Standalone signature"); case 0x10: return _("Generic certification of key"); case 0x11: return _("Persona certification of key"); case 0x12: return _("Casual certification of key"); case 0x13: return _("Positive certification of key"); case 0x18: return _("Subkey binding signature"); case 0x19: return _("Primary key binding signature"); case 0x1F: return _("Signature directly on key"); case 0x20: return _("Key revocation signature"); case 0x28: return _("Subkey revocation signature"); case 0x30: return _("Certification revocation signature"); case 0x40: return _("Timestamp signature"); case 0x50: return _("Third-party confirmation signature"); default: return NULL; } } static void append_sig_record (GcrGnupgRenderer *self, GcrDisplayView *view, GcrRecord *record, const gchar *keyid) { GcrRenderer *renderer = GCR_RENDERER (self); const gchar *sigid; gchar *display; const gchar *value; const gchar *klass; guint algo; /* Hide self-signatures. There's so many of them */ sigid = _gcr_record_get_raw (record, GCR_RECORD_SIG_KEYID); if (sigid && keyid && g_str_equal (sigid, keyid)) return; _gcr_display_view_append_heading (view, renderer, _("Signature")); /* Key ID */ if (sigid != NULL) _gcr_display_view_append_value (view, renderer, _("Key ID"), sigid, TRUE); /* Algorithm */ if (_gcr_record_get_uint (record, GCR_RECORD_SIG_ALGO, &algo)) { display = NULL; value = name_for_algo (algo); if (value == NULL) value = display = g_strdup_printf ("%u", algo); _gcr_display_view_append_value (view, renderer, _("Algorithm"), value, FALSE); g_free (display); } /* User ID */ display = _gcr_record_get_string (record, GCR_RECORD_SIG_USERID); if (display != NULL) _gcr_display_view_append_value (view, renderer, _("User ID"), display, FALSE); g_free (display); /* Signature class */ klass = _gcr_record_get_raw (record, GCR_RECORD_SIG_CLASS); if (klass != NULL) { value = NULL; if (strlen (klass) >= 2) { value = signature_klass_string (klass); if (value != NULL) { _gcr_display_view_append_value (view, renderer, _("Class"), value, FALSE); if (klass[2] == 'l') _gcr_display_view_append_value (view, renderer, _("Type"), _("Local only"), FALSE); else if (klass[2] == 'x') _gcr_display_view_append_value (view, renderer, _("Type"), _("Exportable"), FALSE); } } if (value == NULL) _gcr_display_view_append_value (view, renderer, _("Class"), klass, FALSE); } } static void append_rvk_record (GcrGnupgRenderer *self, GcrDisplayView *view, GcrRecord *record) { GcrRenderer *renderer = GCR_RENDERER (self); const gchar *value; gchar *display; guint algo; _gcr_display_view_append_heading (view, renderer, _("Revocation Key")); /* Algorithm */ if (_gcr_record_get_uint (record, GCR_RECORD_RVK_ALGO, &algo)) { display = NULL; value = name_for_algo (algo); if (value == NULL) value = display = g_strdup_printf ("%u", algo); _gcr_display_view_append_value (view, renderer, _("Algorithm"), value, FALSE); g_free (display); } value = _gcr_record_get_raw (record, GCR_RECORD_RVK_FINGERPRINT); if (value != NULL) _gcr_display_view_append_value (view, renderer, _("Fingerprint"), value, TRUE); } static void append_fpr_record (GcrGnupgRenderer *self, GcrDisplayView *view, GcrRecord *record, GQuark last_schema) { GcrRenderer *renderer = GCR_RENDERER (self); const gchar *value; gpointer raw; gsize n_raw; if (last_schema != GCR_RECORD_SCHEMA_PUB && last_schema != GCR_RECORD_SCHEMA_SUB && last_schema != GCR_RECORD_SCHEMA_SEC && last_schema != GCR_RECORD_SCHEMA_SSB) return; value = _gcr_record_get_raw (record, GCR_RECORD_FPR_FINGERPRINT); if (value != NULL) { raw = egg_hex_decode (value, -1, &n_raw); if (raw != NULL) _gcr_display_view_append_hex (view, renderer, _("Fingerprint"), raw, n_raw); else _gcr_display_view_append_value (view, renderer, _("Fingerprint"), value, TRUE); g_free (raw); } } static void _gcr_gnupg_renderer_render (GcrRenderer *renderer, GcrViewer *viewer) { GtkMessageType message_type; GcrGnupgRenderer *self; GcrDisplayView *view; GDateTime *date; const gchar *value; gchar *display; gchar *userid; gchar *email; gchar *comment; GIcon *icon; GQuark schema; GQuark last_schema; gchar code; guint i; self = GCR_GNUPG_RENDERER (renderer); if (GCR_IS_DISPLAY_VIEW (viewer)) { view = GCR_DISPLAY_VIEW (viewer); } else { g_warning ("GcrGnupgRenderer only works with internal specific " "GcrViewer returned by gcr_viewer_new()."); return; } _gcr_display_view_begin (view, renderer); if (self->pv->records == NULL || self->pv->records->len == 0) { _gcr_display_view_end (view, renderer); return; } icon = _gcr_gnupg_records_get_icon (self->pv->records); _gcr_display_view_set_icon (view, GCR_RENDERER (self), icon); g_object_unref (icon); display = calculate_label (self); _gcr_display_view_append_title (view, renderer, display); g_free (display); userid = _gcr_gnupg_records_get_user_id (self->pv->records); if (userid != NULL) { if (_gcr_gnupg_records_parse_user_id (userid, NULL, &email, &comment)) { if (email != NULL) _gcr_display_view_append_content (view, renderer, _("Email"), email); g_free (email); if (comment != NULL) _gcr_display_view_append_content (view, renderer, _("Comment"), comment); g_free (comment); } g_free (userid); } code = _gcr_record_get_char (self->pv->records->pdata[0], GCR_RECORD_TRUST); if (code != 'e') { date = _gcr_record_get_date (self->pv->records->pdata[0], GCR_RECORD_KEY_EXPIRY); if (date != NULL) { display = g_date_time_format (date, "%x"); _gcr_display_view_append_content (view, renderer, _("Expires"), display); g_date_time_unref (date); g_free (display); } } /* The warning or status */ value = message_for_code (code, &message_type); if (value != NULL) _gcr_display_view_append_message (view, renderer, message_type, value); _gcr_display_view_start_details (view, renderer); value = _gcr_gnupg_records_get_keyid (self->pv->records); last_schema = 0; for (i = 0; i < self->pv->records->len; i++) { schema = _gcr_record_get_schema (self->pv->records->pdata[i]); if (schema == GCR_RECORD_SCHEMA_PUB) append_key_record (self, view, self->pv->records->pdata[i], _("Public Key")); else if (schema == GCR_RECORD_SCHEMA_SUB) append_key_record (self, view, self->pv->records->pdata[i], _("Public Subkey")); else if (schema == GCR_RECORD_SCHEMA_SEC) append_key_record (self, view, self->pv->records->pdata[i], _("Secret Key")); else if (schema == GCR_RECORD_SCHEMA_SSB) append_key_record (self, view, self->pv->records->pdata[i], _("Secret Subkey")); else if (schema == GCR_RECORD_SCHEMA_UID) append_uid_record (self, view, self->pv->records->pdata[i]); else if (schema == GCR_RECORD_SCHEMA_UAT) append_uat_record (self, view, self->pv->records->pdata[i]); else if (schema == GCR_RECORD_SCHEMA_SIG) append_sig_record (self, view, self->pv->records->pdata[i], value); else if (schema == GCR_RECORD_SCHEMA_RVK) append_rvk_record (self, view, self->pv->records->pdata[i]); else if (schema == GCR_RECORD_SCHEMA_FPR) append_fpr_record (self, view, self->pv->records->pdata[i], last_schema); last_schema = schema; } _gcr_display_view_end (view, renderer); } static void _gcr_gnupg_renderer_iface_init (GcrRendererIface *iface) { iface->render_view = _gcr_gnupg_renderer_render; } GcrGnupgRenderer * _gcr_gnupg_renderer_new (GPtrArray *records) { g_return_val_if_fail (records != NULL, NULL); return g_object_new (GCR_TYPE_GNUPG_RENDERER, "records", records, NULL); } GcrGnupgRenderer * _gcr_gnupg_renderer_new_for_attributes (const gchar *label, GckAttributes *attrs) { g_return_val_if_fail (attrs != NULL, NULL); return g_object_new (GCR_TYPE_GNUPG_RENDERER, "label", label, "attributes", attrs, NULL); } GPtrArray * _gcr_gnupg_renderer_get_records (GcrGnupgRenderer *self) { g_return_val_if_fail (GCR_IS_GNUPG_RENDERER (self), NULL); return self->pv->records; } void _gcr_gnupg_renderer_set_records (GcrGnupgRenderer *self, GPtrArray *records) { g_return_if_fail (GCR_IS_GNUPG_RENDERER (self)); if (records) g_ptr_array_ref (records); if (self->pv->records) g_ptr_array_unref (self->pv->records); self->pv->records = records; if (self->pv->attrs) { gck_attributes_unref (self->pv->attrs); self->pv->attrs = NULL; g_object_notify (G_OBJECT (self), "attributes"); } gcr_renderer_emit_data_changed (GCR_RENDERER (self)); g_object_notify (G_OBJECT (self), "records"); } GckAttributes* _gcr_gnupg_renderer_get_attributes (GcrGnupgRenderer *self) { g_return_val_if_fail (GCR_IS_GNUPG_RENDERER (self), NULL); return self->pv->attrs; } void _gcr_gnupg_renderer_set_attributes (GcrGnupgRenderer *self, GckAttributes *attrs) { const GckAttribute *attr; GPtrArray *records; g_return_if_fail (GCR_IS_GNUPG_RENDERER (self)); attr = gck_attributes_find (attrs, CKA_VALUE); g_return_if_fail (attr != NULL); records = _gcr_records_parse_colons (attr->value, attr->length); g_return_if_fail (records != NULL); if (attrs) gck_attributes_ref (attrs); gck_attributes_unref (self->pv->attrs); self->pv->attrs = attrs; if (self->pv->records) g_ptr_array_unref (self->pv->records); self->pv->records = records; g_object_notify (G_OBJECT (self), "records"); gcr_renderer_emit_data_changed (GCR_RENDERER (self)); g_object_notify (G_OBJECT (self), "attributes"); }