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