1 /* fileverifydlg.c - Dialog for verifying files.
2 Copyright (C) 2000, 2001 G-N-U GmbH.
3 Copyright (C) 2005, 2012 g10 Code GmbH.
4
5 This file is part of GPA
6
7 GPA is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GPA is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GPA; if not, write to the Free Software Foundation,
19 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */
20
21 #include <config.h>
22
23 #include <gdk/gdkkeysyms.h>
24 #include <gtk/gtk.h>
25 #include <string.h>
26 #include <errno.h>
27 #include "gpa.h"
28 #include "gtktools.h"
29 #include "gpawidgets.h"
30 #include "verifydlg.h"
31
32 /* Properties */
33 enum
34 {
35 PROP_0,
36 PROP_WINDOW,
37 };
38
39 static GObjectClass *parent_class = NULL;
40
41 static void
gpa_file_verify_dialog_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)42 gpa_file_verify_dialog_get_property (GObject *object,
43 guint prop_id,
44 GValue *value,
45 GParamSpec *pspec)
46 {
47 GpaFileVerifyDialog *dialog = GPA_FILE_VERIFY_DIALOG (object);
48
49 switch (prop_id)
50 {
51 case PROP_WINDOW:
52 g_value_set_object (value,
53 gtk_window_get_transient_for (GTK_WINDOW (dialog)));
54 break;
55 default:
56 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
57 break;
58 }
59 }
60
61 static void
gpa_file_verify_dialog_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)62 gpa_file_verify_dialog_set_property (GObject *object,
63 guint prop_id,
64 const GValue *value,
65 GParamSpec *pspec)
66 {
67 GpaFileVerifyDialog *dialog = GPA_FILE_VERIFY_DIALOG (object);
68
69 switch (prop_id)
70 {
71 case PROP_WINDOW:
72 gtk_window_set_transient_for (GTK_WINDOW (dialog),
73 g_value_get_object (value));
74 break;
75 default:
76 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
77 break;
78 }
79 }
80
81 static void
gpa_file_verify_dialog_finalize(GObject * object)82 gpa_file_verify_dialog_finalize (GObject *object)
83 {
84 GpaFileVerifyDialog *dialog = GPA_FILE_VERIFY_DIALOG (object);
85
86 g_object_unref (dialog->ctx);
87
88 G_OBJECT_CLASS (parent_class)->finalize (object);
89 }
90
91
92 static void
gpa_file_verify_dialog_init(GpaFileVerifyDialog * dialog)93 gpa_file_verify_dialog_init (GpaFileVerifyDialog *dialog)
94 {
95 }
96
97 static GObject*
gpa_file_verify_dialog_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_properties)98 gpa_file_verify_dialog_constructor (GType type,
99 guint n_construct_properties,
100 GObjectConstructParam *construct_properties)
101 {
102 GObject *object;
103 GpaFileVerifyDialog *dialog;
104
105 /* Invoke parent's constructor */
106 object = parent_class->constructor (type,
107 n_construct_properties,
108 construct_properties);
109 dialog = GPA_FILE_VERIFY_DIALOG (object);
110 /* Initialize */
111
112 dialog->ctx = gpa_context_new ();
113 /* Set up the dialog */
114 gtk_dialog_add_buttons (GTK_DIALOG (dialog),
115 _("_Close"), GTK_RESPONSE_CLOSE, NULL);
116 gpa_window_set_title (GTK_WINDOW (dialog), _("Verify documents"));
117 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE);
118 gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
119
120 dialog->notebook = gtk_notebook_new ();
121 gtk_box_pack_start_defaults (GTK_BOX (GTK_DIALOG (dialog)->vbox),
122 dialog->notebook);
123 /* Hide on response */
124 g_signal_connect (G_OBJECT (dialog), "response",
125 G_CALLBACK (gtk_widget_hide), NULL);
126
127
128 return object;
129 }
130
131 static void
gpa_file_verify_dialog_class_init(GpaFileVerifyDialogClass * klass)132 gpa_file_verify_dialog_class_init (GpaFileVerifyDialogClass *klass)
133 {
134 GObjectClass *object_class = G_OBJECT_CLASS (klass);
135
136 parent_class = g_type_class_peek_parent (klass);
137
138 object_class->constructor = gpa_file_verify_dialog_constructor;
139 object_class->finalize = gpa_file_verify_dialog_finalize;
140 object_class->set_property = gpa_file_verify_dialog_set_property;
141 object_class->get_property = gpa_file_verify_dialog_get_property;
142
143 /* Properties */
144 g_object_class_install_property (object_class,
145 PROP_WINDOW,
146 g_param_spec_object
147 ("window", "Parent window",
148 "Parent window", GTK_TYPE_WIDGET,
149 G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY));
150 }
151
152 GType
gpa_file_verify_dialog_get_type(void)153 gpa_file_verify_dialog_get_type (void)
154 {
155 static GType verify_dialog_type = 0;
156
157 if (!verify_dialog_type)
158 {
159 static const GTypeInfo verify_dialog_info =
160 {
161 sizeof (GpaFileVerifyDialogClass),
162 (GBaseInitFunc) NULL,
163 (GBaseFinalizeFunc) NULL,
164 (GClassInitFunc) gpa_file_verify_dialog_class_init,
165 NULL, /* class_finalize */
166 NULL, /* class_data */
167 sizeof (GpaFileVerifyDialog),
168 0, /* n_preallocs */
169 (GInstanceInitFunc) gpa_file_verify_dialog_init,
170 };
171
172 verify_dialog_type = g_type_register_static (GTK_TYPE_DIALOG,
173 "GpaFileVerifyDialog",
174 &verify_dialog_info, 0);
175 }
176
177 return verify_dialog_type;
178 }
179
180 /* Internal */
181
182
183 typedef struct
184 {
185 gchar *fpr;
186 gpgme_key_t key;
187 gpgme_validity_t validity;
188 unsigned long summary;
189 time_t created;
190 time_t expire;
191 char *sigdesc;
192 char *keydesc;
193 } SignatureData;
194
195 typedef enum
196 {
197 SIG_KEYID_COLUMN,
198 SIG_STATUS_COLUMN,
199 SIG_USERID_COLUMN,
200 SIG_DESC_COLUMN,
201 SIG_N_COLUMNS
202 } SignatureListColumn;
203
204 /* Return the text of the "status" column in pango markup language.
205 */
206 static gchar*
signature_status_label(SignatureData * data)207 signature_status_label (SignatureData *data)
208 {
209 gchar *text = NULL;
210 gchar *color = NULL;
211 gchar *label = NULL;
212
213 if (data->summary & GPGME_SIGSUM_VALID)
214 {
215 text = _("Valid");
216 color = "green";
217 }
218 else if (data->summary & GPGME_SIGSUM_RED)
219 {
220 text = _("Bad");
221 color = "red";
222 }
223 else if (data->summary & GPGME_SIGSUM_KEY_MISSING)
224 {
225 text = _("Unknown Key");
226 color = "red";
227 }
228 else if (data->summary & GPGME_SIGSUM_KEY_REVOKED)
229 {
230 text = _("Revoked Key");
231 color = "red";
232 }
233 else if (data->summary & GPGME_SIGSUM_KEY_EXPIRED)
234 {
235 text = _("Expired Key");
236 color = "orange";
237 }
238 else
239 {
240 /* If we arrived here we know the key is available, the signature is
241 * not bad, but it's not completely valid. So, the signature is good
242 * but the key is not valid. */
243 text = _("Key NOT valid");
244 color = "orange";
245 }
246
247 label = g_strdup_printf ("<span foreground=\"%s\" weight=\"bold\">%s</span>",
248 color, text);
249 return label;
250 }
251
252 /* Add a signature to the list */
253 static void
add_signature_to_model(GtkListStore * store,SignatureData * data)254 add_signature_to_model (GtkListStore *store, SignatureData *data)
255 {
256 GtkTreeIter iter;
257 const gchar *keyid;
258 gchar *userid;
259 gchar *status;
260
261 if (data->key)
262 {
263 keyid = gpa_gpgme_key_get_short_keyid (data->key);
264 }
265 else if (data->fpr && strlen (data->fpr) > 8)
266 {
267 /* We use the last 8 bytes of fingerprint for the keyID. */
268 keyid = data->fpr + strlen (data->fpr) - 8;
269 }
270 else
271 {
272 keyid = "";
273 }
274
275 status = signature_status_label (data);
276
277 userid = data->keydesc;
278 if (!userid)
279 userid = _("[Unknown user ID]");
280
281 gtk_list_store_append (store, &iter);
282 gtk_list_store_set (store, &iter,
283 SIG_KEYID_COLUMN, keyid,
284 SIG_STATUS_COLUMN, status,
285 SIG_USERID_COLUMN, userid,
286 SIG_DESC_COLUMN, data->sigdesc,
287 -1);
288
289 g_free (status);
290
291 gpgme_key_unref (data->key);
292 g_free (data->sigdesc);
293 g_free (data->keydesc);
294 g_free (data);
295 }
296
297 /* Fill the list of signatures with the data from the verification */
298 static void
fill_sig_model(GtkListStore * store,gpgme_signature_t sigs,gpgme_ctx_t ctx)299 fill_sig_model (GtkListStore *store, gpgme_signature_t sigs,
300 gpgme_ctx_t ctx)
301 {
302 SignatureData *data;
303 gpgme_signature_t sig;
304
305 for (sig = sigs; sig; sig = sig->next)
306 {
307 data = g_malloc (sizeof (SignatureData));
308 data->fpr = sig->fpr? g_strdup (sig->fpr) : NULL;
309 data->validity = sig->validity;
310 data->summary = sig->summary;
311 data->created = sig->timestamp;
312 data->expire = sig->exp_timestamp;
313 data->sigdesc = gpa_gpgme_get_signature_desc (ctx, sig,
314 &data->keydesc, &data->key);
315 add_signature_to_model (store, data);
316 }
317 }
318
319
320 /* Create the list of signatures */
321 static GtkWidget *
signature_list(gpgme_signature_t sigs,gpgme_ctx_t ctx)322 signature_list (gpgme_signature_t sigs, gpgme_ctx_t ctx)
323 {
324 GtkTreeViewColumn *column;
325 GtkCellRenderer *renderer;
326 GtkListStore *store;
327 GtkWidget *list;
328
329 store = gtk_list_store_new (SIG_N_COLUMNS, G_TYPE_STRING,
330 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
331 list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
332 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (list), TRUE);
333 gtk_widget_set_size_request (list, 400, 100);
334
335 renderer = gtk_cell_renderer_text_new ();
336 column = gtk_tree_view_column_new_with_attributes (_("Key ID"), renderer,
337 "text", SIG_KEYID_COLUMN,
338 NULL);
339 gtk_tree_view_append_column (GTK_TREE_VIEW (list), column);
340
341 renderer = gtk_cell_renderer_text_new ();
342 column = gtk_tree_view_column_new_with_attributes (_("Status"), renderer,
343 "markup",
344 SIG_STATUS_COLUMN, NULL);
345 gtk_tree_view_append_column (GTK_TREE_VIEW (list), column);
346
347 renderer = gtk_cell_renderer_text_new ();
348 column = gtk_tree_view_column_new_with_attributes (_("User Name"), renderer,
349 "text", SIG_USERID_COLUMN,
350 NULL);
351 gtk_tree_view_append_column (GTK_TREE_VIEW (list), column);
352
353 renderer = gtk_cell_renderer_text_new ();
354 column = gtk_tree_view_column_new_with_attributes (_("Description"), renderer,
355 "text", SIG_DESC_COLUMN,
356 NULL);
357 gtk_tree_view_append_column (GTK_TREE_VIEW (list), column);
358
359 fill_sig_model (store, sigs, ctx);
360
361 return list;
362 }
363
364 static GtkWidget *
verify_file_page(gpgme_signature_t sigs,const gchar * signed_file,const gchar * signature_file,gpgme_ctx_t ctx)365 verify_file_page (gpgme_signature_t sigs, const gchar *signed_file,
366 const gchar *signature_file, gpgme_ctx_t ctx)
367 {
368 GtkWidget *vbox;
369 GtkWidget *list;
370 GtkWidget *label;
371 GtkWidget *scrolled;
372
373 vbox = gtk_vbox_new (FALSE, 5);
374 gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
375
376 if (signed_file)
377 {
378 gchar *text = g_strdup_printf (_("Verified data in file: %s"),
379 signed_file);
380 label = gtk_label_new (text);
381 gtk_box_pack_start_defaults (GTK_BOX (vbox), label);
382 g_free (text);
383 text = g_strdup_printf (_("Signature: %s"), signature_file);
384 label = gtk_label_new (text);
385 gtk_box_pack_start_defaults (GTK_BOX (vbox), label);
386 g_free (text);
387 }
388
389 label = gtk_label_new (_("Signatures:"));
390 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
391 gtk_box_pack_start_defaults (GTK_BOX (vbox), label);
392
393 list = signature_list (sigs, ctx);
394 scrolled = gtk_scrolled_window_new (NULL, NULL);
395 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
396 GTK_SHADOW_IN);
397 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
398 GTK_POLICY_AUTOMATIC,
399 GTK_POLICY_AUTOMATIC);
400 gtk_container_add (GTK_CONTAINER (scrolled), list);
401 gtk_box_pack_start_defaults (GTK_BOX (vbox), scrolled);
402
403 return vbox;
404 }
405
406 /* API */
407
gpa_file_verify_dialog_new(GtkWidget * parent)408 GtkWidget *gpa_file_verify_dialog_new (GtkWidget *parent)
409 {
410 GpaFileVerifyDialog *dialog;
411
412 dialog = g_object_new (GPA_FILE_VERIFY_DIALOG_TYPE,
413 "window", parent,
414 NULL);
415
416 return GTK_WIDGET(dialog);
417 }
418
419 void
gpa_file_verify_dialog_set_title(GpaFileVerifyDialog * dialog,const char * title)420 gpa_file_verify_dialog_set_title (GpaFileVerifyDialog *dialog,
421 const char *title)
422 {
423 if (dialog && title && *title)
424 gpa_window_set_title (GTK_WINDOW (dialog), title);
425 }
426
427
gpa_file_verify_dialog_add_file(GpaFileVerifyDialog * dialog,const gchar * filename,const gchar * signed_file,const gchar * signature_file,gpgme_signature_t sigs)428 void gpa_file_verify_dialog_add_file (GpaFileVerifyDialog *dialog,
429 const gchar *filename,
430 const gchar *signed_file,
431 const gchar *signature_file,
432 gpgme_signature_t sigs)
433 {
434 GtkWidget *page;
435
436 page = verify_file_page (sigs, signed_file, signature_file,
437 dialog->ctx->ctx);
438
439 gtk_notebook_append_page (GTK_NOTEBOOK (dialog->notebook), page,
440 gtk_label_new (filename));
441 }
442