No issues found
Tool | Failure ID | Location | Function | Message | Data |
---|---|---|---|---|---|
clang-analyzer | no-output-found | certificate-manager.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None | |
clang-analyzer | no-output-found | certificate-manager.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None |
1 /*
2 *
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2 of the License, or (at your option) version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with the program; if not, see <http://www.gnu.org/licenses/>
15 *
16 *
17 * Authors:
18 * Chris Toshok <toshok@ximian.com>
19 *
20 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
21 *
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <gtk/gtk.h>
29
30 #include <glib/gi18n.h>
31
32 #include "ca-trust-dialog.h"
33 #include "cert-trust-dialog.h"
34 #include "certificate-manager.h"
35 #include "certificate-viewer.h"
36
37 #include "e-cert.h"
38 #include "e-cert-trust.h"
39 #include "e-cert-db.h"
40
41 #include "nss.h"
42 #include <cms.h>
43 #include <cert.h>
44 #include <certdb.h>
45 #include <pkcs11.h>
46 #include <pk11func.h>
47
48 #include "shell/e-shell.h"
49 #include "e-util/e-dialog-utils.h"
50 #include "e-util/e-util.h"
51 #include "e-util/e-util-private.h"
52 #include "widgets/misc/e-preferences-window.h"
53
54 #define E_CERT_MANAGER_CONFIG_GET_PRIVATE(obj) \
55 (G_TYPE_INSTANCE_GET_PRIVATE \
56 ((obj), E_TYPE_CERT_MANAGER_CONFIG, ECertManagerConfigPrivate))
57
58 G_DEFINE_TYPE (ECertManagerConfig, e_cert_manager_config, GTK_TYPE_BOX);
59
60 enum {
61 PROP_0,
62 PROP_PREFERENCES_WINDOW
63 };
64
65 #define ECMC_TREE_VIEW(o) ecmc->priv->o->treeview
66 #define PAGE_TREE_VIEW(o) o->treeview
67
68 typedef struct {
69 GType type;
70 const gchar *column_title;
71 const gchar * (*get_cert_data_func) (ECert *cert); /* Prototype to e_cert_get_ * functions */
72 gboolean visible; /* Default visibility of column */
73 } CertTreeColumn;
74
75 static CertTreeColumn yourcerts_columns[] = {
76
77 { G_TYPE_STRING, N_("Certificate Name"), e_cert_get_cn, TRUE },
78 { G_TYPE_STRING, N_("Issued To Organization"), e_cert_get_org, FALSE },
79 { G_TYPE_STRING, N_("Issued To Organizational Unit"), e_cert_get_org_unit, FALSE },
80 { G_TYPE_STRING, N_("Serial Number"), e_cert_get_serial_number, TRUE },
81 { G_TYPE_STRING, N_("Purposes"), e_cert_get_usage, TRUE },
82 { G_TYPE_STRING, N_("Issued By"), e_cert_get_issuer_cn, TRUE },
83 { G_TYPE_STRING, N_("Issued By Organization"), e_cert_get_issuer_org, FALSE },
84 { G_TYPE_STRING, N_("Issued By Organizational Unit"), e_cert_get_issuer_org_unit, FALSE },
85 { G_TYPE_STRING, N_("Issued"), e_cert_get_issued_on, FALSE },
86 { G_TYPE_STRING, N_("Expires"), e_cert_get_expires_on, TRUE },
87 { G_TYPE_STRING, N_("SHA1 Fingerprint"), e_cert_get_sha1_fingerprint, FALSE },
88 { G_TYPE_STRING, N_("MD5 Fingerprint"), e_cert_get_md5_fingerprint, FALSE },
89 { G_TYPE_OBJECT, NULL, NULL, FALSE } /* Hidden column for ECert * object */
90
91 };
92 static const gchar * yourcerts_mime_types[] = { "application/x-x509-user-cert", "application/x-pkcs12", NULL };
93
94 static CertTreeColumn contactcerts_columns[] = {
95
96 { G_TYPE_STRING, N_("Certificate Name"), e_cert_get_cn, TRUE },
97 { G_TYPE_STRING, N_("Email Address"), e_cert_get_email, TRUE },
98 { G_TYPE_STRING, N_("Issued To Organization"), e_cert_get_org, FALSE },
99 { G_TYPE_STRING, N_("Issued To Organizational Unit"), e_cert_get_org_unit, FALSE },
100 { G_TYPE_STRING, N_("Serial Number"), e_cert_get_serial_number, TRUE },
101 { G_TYPE_STRING, N_("Purposes"), e_cert_get_usage, TRUE },
102 { G_TYPE_STRING, N_("Issued By"), e_cert_get_issuer_cn, TRUE },
103 { G_TYPE_STRING, N_("Issued By Organization"), e_cert_get_issuer_org, FALSE },
104 { G_TYPE_STRING, N_("Issued By Organizational Unit"), e_cert_get_issuer_org_unit, FALSE },
105 { G_TYPE_STRING, N_("Issued"), e_cert_get_issued_on, FALSE },
106 { G_TYPE_STRING, N_("Expires"), e_cert_get_expires_on, TRUE },
107 { G_TYPE_STRING, N_("SHA1 Fingerprint"), e_cert_get_sha1_fingerprint, FALSE },
108 { G_TYPE_STRING, N_("MD5 Fingerprint"), e_cert_get_md5_fingerprint, FALSE },
109 { G_TYPE_OBJECT, NULL, NULL, FALSE }
110
111 };
112 static const gchar * contactcerts_mime_types[] = { "application/x-x509-email-cert", "application/x-x509-ca-cert", NULL };
113
114 static CertTreeColumn authoritycerts_columns[] = {
115
116 { G_TYPE_STRING, N_("Certificate Name"), e_cert_get_cn, TRUE },
117 { G_TYPE_STRING, N_("Email Address"), e_cert_get_email, TRUE },
118 { G_TYPE_STRING, N_("Serial Number"), e_cert_get_serial_number, TRUE },
119 { G_TYPE_STRING, N_("Purposes"), e_cert_get_usage, TRUE },
120 { G_TYPE_STRING, N_("Issued By"), e_cert_get_issuer_cn, FALSE },
121 { G_TYPE_STRING, N_("Issued By Organization"), e_cert_get_issuer_org, FALSE },
122 { G_TYPE_STRING, N_("Issued By Organizational Unit"), e_cert_get_issuer_org_unit, FALSE },
123 { G_TYPE_STRING, N_("Issued"), e_cert_get_issued_on, FALSE },
124 { G_TYPE_STRING, N_("Expires"), e_cert_get_expires_on, TRUE },
125 { G_TYPE_STRING, N_("SHA1 Fingerprint"), e_cert_get_sha1_fingerprint, FALSE },
126 { G_TYPE_STRING, N_("MD5 Fingerprint"), e_cert_get_md5_fingerprint, FALSE },
127 { G_TYPE_OBJECT, NULL, NULL, FALSE }
128
129 };
130 static const gchar * authoritycerts_mime_types[] = { "application/x-x509-ca-cert", NULL };
131
132 typedef struct {
133 GtkTreeView *treeview;
134 GtkTreeModel *streemodel;
135 GHashTable *root_hash;
136 GtkMenu *popup_menu;
137 GtkWidget *view_button;
138 GtkWidget *edit_button;
139 GtkWidget *backup_button;
140 GtkWidget *backup_all_button;
141 GtkWidget *import_button;
142 GtkWidget *delete_button;
143
144 CertTreeColumn *columns;
145 gint columns_count;
146
147 ECertType cert_type;
148 const gchar *cert_filter_name;
149 const gchar **cert_mime_types;
150 } CertPage;
151
152 struct _ECertManagerConfigPrivate {
153 GtkBuilder *builder;
154
155 EPreferencesWindow *pref_window;
156
157 CertPage *yourcerts_page;
158 CertPage *contactcerts_page;
159 CertPage *authoritycerts_page;
160 };
161
162 static void view_cert (GtkWidget *button, CertPage *cp);
163 static void edit_cert (GtkWidget *button, CertPage *cp);
164 static void delete_cert (GtkWidget *button, CertPage *cp);
165 static void import_cert (GtkWidget *button, CertPage *cp);
166
167 static void load_certs (CertPage *cp);
168 static void unload_certs (CertPage *cp);
169
170 static void
171 save_treeview_state (GtkTreeView *treeview)
172 {
173 GKeyFile *keyfile;
174 GtkTreeModel *model;
175 GtkTreeSortable *sortable;
176 GtkSortType sort_type;
177 gint columns_count;
178 gint i = 0;
179 gint *list;
180 gchar *cfg_file, *data;
181 const gchar *tree_name;
182
183 g_return_if_fail (treeview && GTK_IS_TREE_VIEW (treeview));
184
185 model = gtk_tree_view_get_model (treeview);
186 g_return_if_fail (model && GTK_IS_TREE_MODEL_SORT (model));
187
188 keyfile = g_key_file_new ();
189 cfg_file = g_build_filename (e_get_user_config_dir (), "cert_trees.ini", NULL);
190 g_key_file_load_from_file (keyfile, cfg_file, 0, NULL);
191
192 tree_name = gtk_widget_get_name (GTK_WIDGET (treeview));
193 sortable = GTK_TREE_SORTABLE (model);
194
195 columns_count = gtk_tree_model_get_n_columns (model) - 1; /* Ignore the last column - the ECert * holder */
196 list = g_new0 (gint, columns_count);
197
198 for (i = 0; i < columns_count; i++) {
199 GtkTreeViewColumn *column = gtk_tree_view_get_column (treeview, i);
200 if (gtk_tree_view_column_get_visible (column)) {
201 list[gtk_tree_view_column_get_sort_column_id (column)] = gtk_tree_view_column_get_width (column);
202 } else {
203 list[gtk_tree_view_column_get_sort_column_id (column)] = 0;
204 }
205 }
206 g_key_file_set_integer_list (keyfile, tree_name, "columns", list, columns_count);
207 g_free (list);
208
209 list = g_new0 (gint, columns_count);
210 for (i = 0; i < columns_count; i++) {
211 GtkTreeViewColumn *column = gtk_tree_view_get_column (treeview, i);
212 list[i] = gtk_tree_view_column_get_sort_column_id (column);
213 }
214 g_key_file_set_integer_list (keyfile, tree_name, "columns-order", list, columns_count);
215 g_free (list);
216
217 gtk_tree_sortable_get_sort_column_id (sortable, &i, &sort_type);
218 g_key_file_set_integer (keyfile, tree_name, "sort-column", i);
219
220 g_key_file_set_integer (keyfile, tree_name, "sort-order", sort_type);
221
222 data = g_key_file_to_data (keyfile, NULL, NULL);
223 g_file_set_contents (cfg_file, data, -1, NULL);
224
225 g_free (data);
226 g_free (cfg_file);
227 g_key_file_free (keyfile);
228 }
229
230 static void
231 load_treeview_state (GtkTreeView *treeview)
232 {
233 GKeyFile *keyfile;
234 gint i, *list;
235 gsize length;
236 GtkTreeSortable *sortable;
237 GtkTreeModel *model;
238 gchar *cfg_file;
239 const gchar *tree_name;
240
241 g_return_if_fail (treeview && GTK_IS_TREE_VIEW (treeview));
242
243 keyfile = g_key_file_new ();
244 cfg_file = g_build_filename (e_get_user_config_dir (), "cert_trees.ini", NULL);
245
246 if (!g_key_file_load_from_file (keyfile, cfg_file, 0, NULL)) {
247 g_key_file_free (keyfile);
248 g_free (cfg_file);
249 return;
250 }
251
252 model = GTK_TREE_MODEL (gtk_tree_view_get_model (treeview));
253 tree_name = gtk_widget_get_name (GTK_WIDGET (treeview));
254 list = g_key_file_get_integer_list (keyfile, tree_name, "columns", &length, NULL);
255
256 if (list) {
257 gboolean all_hidden = TRUE;
258
259 if (length != (gtk_tree_model_get_n_columns (model) - 1)) {
260 g_debug ("%s: Unexpected number of columns in config file", G_STRFUNC);
261 g_free (list);
262 goto exit;
263 }
264
265 for (i = 0; all_hidden && i < length; i++) {
266 all_hidden = list[i] == 0;
267 }
268
269 for (i = 0; !all_hidden && i < length; i++) {
270 GtkTreeViewColumn *column = gtk_tree_view_get_column (treeview, i);
271 if (list[i]) {
272 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
273 gtk_tree_view_column_set_fixed_width (column, list[i]);
274 gtk_tree_view_column_set_visible (column, TRUE);
275 } else {
276 gtk_tree_view_column_set_visible (column, FALSE);
277 }
278 }
279 g_free (list);
280 }
281
282 list = g_key_file_get_integer_list (keyfile, tree_name, "columns-order", &length, NULL);
283
284 if (list) {
285 GList *columns = gtk_tree_view_get_columns (treeview);
286
287 if (length != g_list_length (columns)) {
288 g_debug ("%s: Unexpected number of columns in config file", G_STRFUNC);
289 g_free (list);
290 goto exit;
291 }
292
293 for (i = (length - 1); i >= 0; i--) {
294 if ((list[i] >= 0) && (list[i] < length)) {
295 GtkTreeViewColumn *column = g_list_nth (columns, list[i])->data;
296 gtk_tree_view_move_column_after (treeview, column, NULL);
297 } else {
298 g_warning ("%s: Invalid column number", G_STRFUNC);
299 }
300 }
301 g_free (list);
302 g_list_free (columns);
303 }
304
305 sortable = GTK_TREE_SORTABLE (gtk_tree_view_get_model (treeview));
306 gtk_tree_sortable_set_sort_column_id (
307 sortable,
308 g_key_file_get_integer (keyfile, tree_name, "sort-column", 0),
309 g_key_file_get_integer (keyfile, tree_name, "sort-order", GTK_SORT_ASCENDING));
310
311 exit:
312 g_free (cfg_file);
313 g_key_file_free (keyfile);
314 }
315
316 static void
317 report_and_free_error (CertPage *cp,
318 const gchar *where,
319 GError *error)
320 {
321 g_return_if_fail (cp != NULL);
322
323 e_notice (
324 gtk_widget_get_toplevel (GTK_WIDGET (cp->treeview)),
325 GTK_MESSAGE_ERROR, "%s: %s", where,
326 error ? error->message : _("Unknown error"));
327
328 if (error)
329 g_error_free (error);
330 }
331
332 static gboolean
333 treeview_header_clicked (GtkWidget *widget,
334 GdkEventButton *event,
335 gpointer user_data)
336 {
337 GtkMenu *menu = user_data;
338
339 if (event->button != 3)
340 return FALSE;
341
342 gtk_widget_show_all (GTK_WIDGET (menu));
343 gtk_menu_popup (menu, NULL, NULL, NULL, NULL, event->button, event->time);
344
345 return TRUE;
346 }
347
348 static void
349 header_popup_item_toggled (GtkCheckMenuItem *item,
350 gpointer user_data)
351 {
352 GtkTreeViewColumn *column = user_data;
353
354 gtk_tree_view_column_set_visible (
355 column,
356 gtk_check_menu_item_get_active (item));
357 }
358
359 static void
360 treeview_column_visibility_changed (GtkTreeViewColumn *column,
361 GParamSpec *pspec,
362 gpointer user_data)
363 {
364 GtkCheckMenuItem *menu_item = user_data;
365
366 gtk_check_menu_item_set_active (
367 menu_item,
368 gtk_tree_view_column_get_visible (column));
369
370 }
371
372 static void
373 treeview_selection_changed (GtkTreeSelection *selection,
374 CertPage *cp)
375 {
376 GtkTreeIter iter;
377 gboolean cert_selected = FALSE;
378 GtkTreeModel *model;
379
380 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
381 ECert *cert;
382
383 gtk_tree_model_get (
384 model, &iter,
385 cp->columns_count - 1, &cert,
386 -1);
387
388 if (cert) {
389 cert_selected = TRUE;
390 g_object_unref (cert);
391 }
392 }
393
394 if (cp->delete_button)
395 gtk_widget_set_sensitive (cp->delete_button, cert_selected);
396 if (cp->edit_button)
397 gtk_widget_set_sensitive (cp->edit_button, cert_selected);
398 if (cp->view_button)
399 gtk_widget_set_sensitive (cp->view_button, cert_selected);
400 }
401
402 static void
403 treeview_add_column (CertPage *cp,
404 gint column_index)
405 {
406 GtkCellRenderer *cell;
407 GtkTreeViewColumn *column;
408 GtkWidget *header, *item;
409
410 if (cp->columns[column_index].type != G_TYPE_STRING)
411 return;
412
413 cell = gtk_cell_renderer_text_new ();
414 g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
415 column = gtk_tree_view_column_new_with_attributes (
416 gettext (cp->columns[column_index].column_title),
417 cell, "text", column_index, NULL);
418 gtk_tree_view_column_set_resizable (column, TRUE);
419 gtk_tree_view_column_set_reorderable (column, TRUE);
420 gtk_tree_view_column_set_sort_column_id (column, column_index);
421 gtk_tree_view_column_set_visible (column, cp->columns[column_index].visible);
422 gtk_tree_view_append_column (cp->treeview, column);
423
424 header = gtk_tree_view_column_get_button (column);
425 g_signal_connect (
426 header, "button-release-event",
427 G_CALLBACK (treeview_header_clicked), cp->popup_menu);
428
429 /* The first column should not be concealable so there's no point in displaying
430 * it in the popup menu */
431 if (column_index == 0)
432 return;
433
434 /* Add item to header popup */
435 item = gtk_check_menu_item_new_with_label (
436 gettext (cp->columns[column_index].column_title));
437 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), cp->columns[column_index].visible);
438 gtk_menu_attach (cp->popup_menu, item, 0, 1, column_index - 1, column_index);
439 g_signal_connect (
440 item, "toggled",
441 G_CALLBACK (header_popup_item_toggled), column);
442 g_signal_connect (
443 column, "notify::visible",
444 G_CALLBACK (treeview_column_visibility_changed), item);
445 }
446
447 struct find_cert_data {
448 ECert *cert;
449 GtkTreePath *path;
450 CertPage *cp;
451 };
452
453 static gboolean
454 find_cert_cb (GtkTreeModel *model,
455 GtkTreePath *path,
456 GtkTreeIter *iter,
457 gpointer data)
458 {
459 struct find_cert_data *fcd = data;
460 ECert *cert = NULL;
461
462 g_return_val_if_fail (model != NULL, TRUE);
463 g_return_val_if_fail (iter != NULL, TRUE);
464 g_return_val_if_fail (data != NULL, TRUE);
465
466 /* Get the certificate object from model */
467 gtk_tree_model_get (model, iter, (fcd->cp->columns_count - 1), &cert, -1);
468
469 if (cert && g_strcmp0 (e_cert_get_serial_number (cert), e_cert_get_serial_number (fcd->cert)) == 0
470 && g_strcmp0 (e_cert_get_subject_name (cert), e_cert_get_subject_name (fcd->cert)) == 0
471 && g_strcmp0 (e_cert_get_sha1_fingerprint (cert), e_cert_get_sha1_fingerprint (fcd->cert)) == 0
472 && g_strcmp0 (e_cert_get_md5_fingerprint (cert), e_cert_get_md5_fingerprint (fcd->cert)) == 0) {
473 fcd->path = gtk_tree_path_copy (path);
474 }
475
476 if (cert)
477 g_object_unref (cert);
478
479 return fcd->path != NULL;
480 }
481
482 static void
483 select_certificate (CertPage *cp,
484 ECert *cert)
485 {
486 GtkTreeModel *model;
487 GtkTreeSelection *selection;
488 struct find_cert_data fcd;
489
490 g_return_if_fail (cp != NULL);
491 g_return_if_fail (cert != NULL);
492 g_return_if_fail (E_IS_CERT (cert));
493
494 model = gtk_tree_view_get_model (cp->treeview);
495 g_return_if_fail (model != NULL);
496
497 fcd.cp = cp;
498 fcd.cert = cert;
499 fcd.path = NULL;
500
501 gtk_tree_model_foreach (model, find_cert_cb, &fcd);
502
503 if (fcd.path) {
504 gtk_tree_view_expand_to_path (cp->treeview, fcd.path);
505
506 selection = gtk_tree_view_get_selection (cp->treeview);
507 gtk_tree_selection_select_path (selection, fcd.path);
508
509 gtk_tree_view_scroll_to_cell (cp->treeview, fcd.path, NULL, TRUE, 0.5, 0.5);
510 gtk_tree_path_free (fcd.path);
511 }
512 }
513
514 static void
515 view_cert (GtkWidget *button,
516 CertPage *cp)
517 {
518 GtkTreeIter iter;
519
520 if (gtk_tree_selection_get_selected (gtk_tree_view_get_selection (cp->treeview), NULL, &iter)) {
521 ECert *cert;
522
523 gtk_tree_model_get (
524 GTK_TREE_MODEL (cp->streemodel), &iter,
525 cp->columns_count - 1, &cert,
526 -1);
527
528 if (cert) {
529 GtkWidget *dialog = certificate_viewer_show (cert);
530 g_signal_connect (
531 dialog, "response",
532 G_CALLBACK (gtk_widget_destroy), NULL);
533 gtk_widget_show (dialog);
534 g_object_unref (cert);
535 }
536 }
537 }
538
539 static void
540 edit_cert (GtkWidget *button,
541 CertPage *cp)
542 {
543 GtkTreeIter iter;
544
545 if (gtk_tree_selection_get_selected (gtk_tree_view_get_selection (cp->treeview), NULL, &iter)) {
546 ECert *cert;
547
548 gtk_tree_model_get (
549 GTK_TREE_MODEL (cp->streemodel), &iter,
550 cp->columns_count - 1, &cert,
551 -1);
552
553 if (cert) {
554 GtkWidget *dialog;
555 CERTCertificate *icert = e_cert_get_internal_cert (cert);
556
557 switch (cp->cert_type) {
558 case E_CERT_CA:
559 dialog = ca_trust_dialog_show (cert, FALSE);
560 ca_trust_dialog_set_trust (
561 dialog,
562 e_cert_trust_has_trusted_ca (icert->trust, TRUE, FALSE, FALSE),
563 e_cert_trust_has_trusted_ca (icert->trust, FALSE, TRUE, FALSE),
564 e_cert_trust_has_trusted_ca (icert->trust, FALSE, FALSE, TRUE));
565 break;
566 case E_CERT_CONTACT:
567 dialog = cert_trust_dialog_show (cert);
568 break;
569 default:
570 /* Other cert types cannot be edited */
571 return;
572 }
573
574 if ((gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) &&
575 (cp->cert_type == E_CERT_CA)) {
576 gboolean trust_ssl, trust_email, trust_objsign;
577 CERTCertTrust trust;
578
579 ca_trust_dialog_get_trust (
580 dialog,
581 &trust_ssl, &trust_email, &trust_objsign);
582
583 e_cert_trust_init (&trust);
584 e_cert_trust_set_valid_ca (&trust);
585 e_cert_trust_add_ca_trust (
586 &trust,
587 trust_ssl, trust_email, trust_objsign);
588
589 e_cert_db_change_cert_trust (icert, &trust);
590 }
591
592 gtk_widget_destroy (dialog);
593 g_object_unref (cert);
594 }
595 }
596 }
597
598 static void
599 import_cert (GtkWidget *button,
600 CertPage *cp)
601 {
602 GtkWidget *filesel;
603 GtkFileFilter *filter;
604 gint i;
605
606 filesel = gtk_file_chooser_dialog_new (
607 _("Select a certificate to import..."), NULL,
608 GTK_FILE_CHOOSER_ACTION_OPEN,
609 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
610 GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL);
611 gtk_dialog_set_default_response (GTK_DIALOG (filesel), GTK_RESPONSE_OK);
612
613 filter = gtk_file_filter_new ();
614 gtk_file_filter_set_name (filter, cp->cert_filter_name);
615 for (i = 0; cp->cert_mime_types[i] != NULL; i++) {
616 gtk_file_filter_add_mime_type (filter, cp->cert_mime_types[i]);
617 }
618 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (filesel), filter);
619
620 filter = gtk_file_filter_new ();
621 gtk_file_filter_set_name (filter, _("All files"));
622 gtk_file_filter_add_pattern (filter, "*");
623 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (filesel), filter);
624
625 if (gtk_dialog_run (GTK_DIALOG (filesel)) == GTK_RESPONSE_OK) {
626 gchar *filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (filesel));
627 GSList *imported_certs = NULL;
628 GError *error = NULL;
629 gboolean import;
630
631 /* destroy dialog to get rid of it in the GUI */
632 gtk_widget_destroy (filesel);
633
634 switch (cp->cert_type) {
635 case E_CERT_USER:
636 import = e_cert_db_import_pkcs12_file (e_cert_db_peek (), filename, &error);
637 break;
638 case E_CERT_CONTACT:
639 case E_CERT_CA:
640 import = e_cert_db_import_certs_from_file (
641 e_cert_db_peek (), filename,
642 cp->cert_type, &imported_certs, &error);
643 break;
644 default:
645 g_free (filename);
646 return;
647 }
648
649 if (import) {
650 unload_certs (cp);
651 load_certs (cp);
652
653 if (imported_certs)
654 select_certificate (cp, imported_certs->data);
655
656 } else {
657 report_and_free_error (cp, _("Failed to import certificate"), error);
658 }
659
660 g_slist_foreach (imported_certs, (GFunc) g_object_unref, NULL);
661 g_slist_free (imported_certs);
662 g_free (filename);
663 } else
664 gtk_widget_destroy (filesel);
665 }
666
667 static void
668 delete_cert (GtkWidget *button,
669 CertPage *cp)
670 {
671 GtkTreeIter iter;
672
673 if (gtk_tree_selection_get_selected (gtk_tree_view_get_selection (cp->treeview), NULL, &iter)) {
674 ECert *cert;
675
676 gtk_tree_model_get (
677 GTK_TREE_MODEL (cp->streemodel), &iter,
678 cp->columns_count - 1, &cert,
679 -1);
680
681 if (cert && e_cert_db_delete_cert (e_cert_db_peek (), cert)) {
682 GtkTreeIter child_iter, parent_iter;
683 gboolean has_parent;
684 GtkTreeStore *store = GTK_TREE_STORE (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (cp->streemodel)));
685
686 gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (cp->streemodel), &child_iter, &iter);
687 has_parent = gtk_tree_model_iter_parent (GTK_TREE_MODEL (store), &parent_iter, &child_iter);
688 gtk_tree_store_remove (store, &child_iter);
689
690 /* Remove parent if it became empty */
691 if (has_parent && gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), &parent_iter) == 0)
692 gtk_tree_store_remove (store, &parent_iter);
693
694 /* we need two unrefs here, one to unref the
695 * gtk_tree_model_get above, and one to unref
696 * the initial ref when we created the cert
697 * and added it to the tree */
698 g_object_unref (cert);
699 g_object_unref (cert);
700 } else if (cert) {
701 g_object_unref (cert);
702 }
703 }
704
705 }
706
707 static void
708 add_cert (CertPage *cp,
709 ECert *cert)
710 {
711 GtkTreeIter iter;
712 GtkTreeIter *parent_iter = NULL;
713 const gchar *organization = e_cert_get_org (cert);
714 GtkTreeModel *model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (cp->streemodel));
715 gint i;
716
717 if (organization) {
718 parent_iter = g_hash_table_lookup (cp->root_hash, organization);
719 if (!parent_iter) {
720 /* create a new toplevel node */
721 gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL);
722
723 gtk_tree_store_set (
724 GTK_TREE_STORE (model), &iter,
725 0, organization, -1);
726
727 /* now copy it off into parent_iter and insert it into
728 * the hashtable */
729 parent_iter = gtk_tree_iter_copy (&iter);
730 g_hash_table_insert (cp->root_hash, g_strdup (organization), parent_iter);
731 }
732 }
733
734 gtk_tree_store_append (GTK_TREE_STORE (model), &iter, parent_iter);
735
736 for (i = 0; i < cp->columns_count; i++) {
737 const gchar * (*get_cert_data_func) (ECert *cert);
738
739 /* When e_cert_get_cn() is empty, use _get_nickname() */
740 if ((cp->columns[i].get_cert_data_func == e_cert_get_cn) && (!e_cert_get_cn (cert))) {
741 get_cert_data_func = e_cert_get_nickname;
742 } else {
743 get_cert_data_func = cp->columns[i].get_cert_data_func;
744 }
745
746 if (cp->columns[i].type == G_TYPE_STRING) {
747 gtk_tree_store_set (
748 GTK_TREE_STORE (model), &iter,
749 i, get_cert_data_func (cert), -1);
750 } else if (cp->columns[i].type == G_TYPE_OBJECT) {
751 gtk_tree_store_set (
752 GTK_TREE_STORE (model), &iter,
753 i, cert, -1);
754 }
755 }
756 }
757
758 static void
759 unload_certs (CertPage *cp)
760 {
761 GtkTreeStore *treemodel;
762 GType types[cp->columns_count];
763 gint i;
764
765 g_return_if_fail (cp != NULL);
766
767 for (i = 0; i < cp->columns_count; i++)
768 types[i] = cp->columns[i].type;
769 treemodel = gtk_tree_store_newv (cp->columns_count, types);
770
771 if (cp->streemodel)
772 g_object_unref (cp->streemodel);
773
774 cp->streemodel = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (treemodel));
775
776 g_object_unref (treemodel);
777 gtk_tree_view_set_model (cp->treeview, cp->streemodel);
778
779 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (cp->streemodel), 0, GTK_SORT_ASCENDING);
780
781 if (cp->root_hash)
782 g_hash_table_destroy (cp->root_hash);
783
784 cp->root_hash = g_hash_table_new_full (
785 (GHashFunc) g_str_hash,
786 (GEqualFunc) g_str_equal,
787 (GDestroyNotify) g_free,
788 (GDestroyNotify) gtk_tree_iter_free);
789 }
790
791 static void
792 load_certs (CertPage *cp)
793 {
794 CERTCertList *certList;
795 CERTCertListNode *node;
796
797 g_return_if_fail (cp != NULL);
798
799 certList = PK11_ListCerts (PK11CertListUnique, NULL);
800
801 for (node = CERT_LIST_HEAD (certList);
802 !CERT_LIST_END (node, certList);
803 node = CERT_LIST_NEXT (node)) {
804 ECert *cert = e_cert_new (CERT_DupCertificate ((CERTCertificate *) node->cert));
805 ECertType ct = e_cert_get_cert_type (cert);
806
807 /* show everything else in a contact tab */
808 if (ct == cp->cert_type || (cp->cert_type == E_CERT_CONTACT && ct != E_CERT_CA && ct != E_CERT_USER)) {
809 add_cert (cp, cert);
810 } else {
811 g_object_unref (cert);
812 }
813 }
814
815 CERT_DestroyCertList (certList);
816 }
817
818 static gboolean
819 populate_ui (ECertManagerConfig *ecmc)
820 {
821 /* This is an idle callback. */
822
823 ECertManagerConfigPrivate *priv = ecmc->priv;
824
825 unload_certs (priv->yourcerts_page);
826 load_certs (priv->yourcerts_page);
827
828 unload_certs (priv->contactcerts_page);
829 load_certs (priv->contactcerts_page);
830
831 unload_certs (priv->authoritycerts_page);
832 load_certs (priv->authoritycerts_page);
833
834 /* expand all three trees */
835 gtk_tree_view_expand_all (ECMC_TREE_VIEW (yourcerts_page));
836 gtk_tree_view_expand_all (ECMC_TREE_VIEW (contactcerts_page));
837 gtk_tree_view_expand_all (ECMC_TREE_VIEW (authoritycerts_page));
838
839 /* Now load settings of each treeview */
840 load_treeview_state (ECMC_TREE_VIEW (yourcerts_page));
841 load_treeview_state (ECMC_TREE_VIEW (contactcerts_page));
842 load_treeview_state (ECMC_TREE_VIEW (authoritycerts_page));
843
844 return FALSE;
845 }
846
847 static void
848 initialize_ui (CertPage *cp)
849 {
850 GtkTreeSelection *selection;
851 gint i;
852
853 cp->popup_menu = GTK_MENU (gtk_menu_new ());
854
855 /* Add columns to treeview */
856 for (i = 0; i < cp->columns_count; i++)
857 treeview_add_column (cp, i);
858
859 selection = gtk_tree_view_get_selection (cp->treeview);
860 g_signal_connect (
861 selection, "changed",
862 G_CALLBACK (treeview_selection_changed), cp);
863
864 if (cp->import_button)
865 g_signal_connect (
866 cp->import_button, "clicked",
867 G_CALLBACK (import_cert), cp);
868
869 if (cp->edit_button)
870 g_signal_connect (
871 cp->edit_button, "clicked",
872 G_CALLBACK (edit_cert), cp);
873
874 if (cp->delete_button)
875 g_signal_connect (
876 cp->delete_button, "clicked",
877 G_CALLBACK (delete_cert), cp);
878
879 if (cp->view_button)
880 g_signal_connect (
881 cp->view_button, "clicked",
882 G_CALLBACK (view_cert), cp);
883 }
884
885 static void
886 cert_manager_config_window_hide (ECertManagerConfig *ecmc,
887 EPreferencesWindow *epw)
888 {
889 g_return_if_fail (ecmc);
890
891 save_treeview_state (ECMC_TREE_VIEW (yourcerts_page));
892 save_treeview_state (ECMC_TREE_VIEW (contactcerts_page));
893 save_treeview_state (ECMC_TREE_VIEW (authoritycerts_page));
894 }
895
896 static void
897 free_cert (GtkTreeModel *model,
898 GtkTreePath *path,
899 GtkTreeIter *iter,
900 gpointer user_data)
901 {
902 CertPage *cp = user_data;
903 ECert *cert;
904
905 gtk_tree_model_get (model, iter, cp->columns_count - 1, &cert, -1);
906
907 /* Double unref: one for gtk_tree_model_get() and one for e_cert_new() */
908 g_object_unref (cert);
909 g_object_unref (cert);
910 }
911
912 static void
913 cert_page_free (CertPage *cp)
914 {
915 if (!cp)
916 return;
917
918 if (cp->streemodel) {
919 gtk_tree_model_foreach (GTK_TREE_MODEL (cp->streemodel),
920 (GtkTreeModelForeachFunc) free_cert, cp);
921 g_object_unref (cp->streemodel);
922 cp->streemodel = NULL;
923 }
924
925 if (cp->root_hash) {
926 g_hash_table_unref (cp->root_hash);
927 cp->root_hash = NULL;
928 }
929
930 g_free (cp);
931 }
932
933 static void
934 cert_manager_config_dispose (GObject *object)
935 {
936 ECertManagerConfig *ecmc = E_CERT_MANAGER_CONFIG (object);
937
938 if (ecmc->priv->yourcerts_page) {
939 cert_page_free (ecmc->priv->yourcerts_page);
940 ecmc->priv->yourcerts_page = NULL;
941 }
942
943 if (ecmc->priv->contactcerts_page) {
944 cert_page_free (ecmc->priv->contactcerts_page);
945 ecmc->priv->contactcerts_page = NULL;
946 }
947
948 if (ecmc->priv->authoritycerts_page) {
949 cert_page_free (ecmc->priv->authoritycerts_page);
950 ecmc->priv->authoritycerts_page = NULL;
951 }
952
953 if (ecmc->priv->builder) {
954 g_object_unref (ecmc->priv->builder);
955 ecmc->priv->builder = NULL;
956 }
957
958 if (ecmc->priv->pref_window) {
959 g_signal_handlers_disconnect_matched (ecmc->priv->pref_window, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, ecmc);
960 ecmc->priv->pref_window = NULL;
961 }
962
963 G_OBJECT_CLASS (e_cert_manager_config_parent_class)->dispose (object);
964 }
965
966 static void
967 cert_manager_config_set_property (GObject *object,
968 guint property_id,
969 const GValue *value,
970 GParamSpec *pspec)
971 {
972 ECertManagerConfig *ecmc = E_CERT_MANAGER_CONFIG (object);
973
974 switch (property_id) {
975 case PROP_PREFERENCES_WINDOW:
976 ecmc->priv->pref_window = g_value_get_object (value);
977 /* When the preferences window is "closed" (= hidden), save
978 * state of all treeviews. */
979 g_signal_connect_swapped (
980 ecmc->priv->pref_window, "hide",
981 G_CALLBACK (cert_manager_config_window_hide), ecmc);
982 return;
983 }
984
985 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
986 }
987
988 static void
989 e_cert_manager_config_class_init (ECertManagerConfigClass *class)
990 {
991 GObjectClass *object_class;
992
993 g_type_class_add_private (class, sizeof (ECertManagerConfigPrivate));
994
995 object_class = G_OBJECT_CLASS (class);
996 object_class->set_property = cert_manager_config_set_property;
997 object_class->dispose = cert_manager_config_dispose;
998
999 g_object_class_install_property (
1000 object_class,
1001 PROP_PREFERENCES_WINDOW,
1002 g_param_spec_object (
1003 "preferences-window",
1004 NULL,
1005 NULL,
1006 E_TYPE_PREFERENCES_WINDOW,
1007 G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
1008 }
1009
1010 static void
1011 e_cert_manager_config_init (ECertManagerConfig *ecmc)
1012 {
1013 ECertManagerConfigPrivate *priv;
1014 GtkWidget *parent, *widget;
1015 CertPage *cp;
1016
1017 priv = E_CERT_MANAGER_CONFIG_GET_PRIVATE (ecmc);
1018 ecmc->priv = priv;
1019
1020 /* We need to peek the db here to make sure it (and NSS) are fully initialized. */
1021 e_cert_db_peek ();
1022
1023 priv->builder = gtk_builder_new ();
1024 e_load_ui_builder_definition (priv->builder, "smime-ui.ui");
1025
1026 cp = g_new0 (CertPage, 1);
1027 priv->yourcerts_page = cp;
1028 cp->treeview = GTK_TREE_VIEW (e_builder_get_widget (priv->builder, "yourcerts-treeview"));
1029 cp->streemodel = NULL;
1030 cp->view_button = e_builder_get_widget (priv->builder, "your-view-button");
1031 cp->backup_button = e_builder_get_widget (priv->builder, "your-backup-button");
1032 cp->backup_all_button = e_builder_get_widget (priv->builder, "your-backup-all-button");
1033 cp->edit_button = NULL;
1034 cp->import_button = e_builder_get_widget (priv->builder, "your-import-button");
1035 cp->delete_button = e_builder_get_widget (priv->builder, "your-delete-button");
1036 cp->columns = yourcerts_columns;
1037 cp->columns_count = G_N_ELEMENTS (yourcerts_columns);
1038 cp->cert_type = E_CERT_USER;
1039 cp->cert_filter_name = _("All PKCS12 files");
1040 cp->cert_mime_types = yourcerts_mime_types;
1041 initialize_ui (cp);
1042
1043 cp = g_new0 (CertPage, 1);
1044 priv->contactcerts_page = cp;
1045 cp->treeview = GTK_TREE_VIEW (e_builder_get_widget (priv->builder, "contactcerts-treeview"));
1046 cp->streemodel = NULL;
1047 cp->view_button = e_builder_get_widget (priv->builder, "contact-view-button");
1048 cp->backup_button = NULL;
1049 cp->backup_all_button = NULL;
1050 cp->edit_button = e_builder_get_widget (priv->builder, "contact-edit-button");
1051 cp->import_button = e_builder_get_widget (priv->builder, "contact-import-button");
1052 cp->delete_button = e_builder_get_widget (priv->builder, "contact-delete-button");
1053 cp->columns = contactcerts_columns;
1054 cp->columns_count = G_N_ELEMENTS (contactcerts_columns);
1055 cp->cert_type = E_CERT_CONTACT;
1056 cp->cert_filter_name = _("All email certificate files");
1057 cp->cert_mime_types = contactcerts_mime_types;
1058 initialize_ui (cp);
1059
1060 cp = g_new0 (CertPage, 1);
1061 priv->authoritycerts_page = cp;
1062 cp->treeview = GTK_TREE_VIEW (e_builder_get_widget (priv->builder, "authoritycerts-treeview"));
1063 cp->streemodel = NULL;
1064 cp->view_button = e_builder_get_widget (priv->builder, "authority-view-button");
1065 cp->backup_button = NULL;
1066 cp->backup_all_button = NULL;
1067 cp->edit_button = e_builder_get_widget (priv->builder, "authority-edit-button");
1068 cp->import_button = e_builder_get_widget (priv->builder, "authority-import-button");
1069 cp->delete_button = e_builder_get_widget (priv->builder, "authority-delete-button");
1070 cp->columns = authoritycerts_columns;
1071 cp->columns_count = G_N_ELEMENTS (authoritycerts_columns);
1072 cp->cert_type = E_CERT_CA;
1073 cp->cert_filter_name = _("All CA certificate files");
1074 cp->cert_mime_types = authoritycerts_mime_types;
1075 initialize_ui (cp);
1076
1077 /* Run this in an idle callback so Evolution has a chance to
1078 * fully initialize itself and start its main loop before we
1079 * load certificates, since doing so may trigger a password
1080 * dialog, and dialogs require a main loop.
1081 * Schedule with priority higher than gtk+ uses for animations
1082 * (check docs for G_PRIORITY_HIGH_IDLE). */
1083 g_idle_add_full (G_PRIORITY_DEFAULT, (GSourceFunc) populate_ui, ecmc, NULL);
1084
1085 /* Disconnect cert-manager-notebook from it's window and attach it
1086 * to this ECertManagerConfig */
1087 widget = e_builder_get_widget (priv->builder, "cert-manager-notebook");
1088 parent = gtk_widget_get_parent (widget);
1089 gtk_container_remove (GTK_CONTAINER (parent), widget);
1090 gtk_box_pack_start (GTK_BOX (ecmc), widget, TRUE, TRUE, 0);
1091 gtk_widget_show_all (widget);
1092
1093 /* FIXME: remove when implemented */
1094 gtk_widget_set_sensitive (priv->yourcerts_page->backup_button, FALSE);
1095 gtk_widget_set_sensitive (priv->yourcerts_page->backup_all_button, FALSE);
1096 }
1097
1098 GtkWidget *
1099 e_cert_manager_config_new (EPreferencesWindow *window)
1100 {
1101 ECertManagerConfig *ecmc;
1102
1103 ecmc = g_object_new (E_TYPE_CERT_MANAGER_CONFIG, "preferences-window", window, NULL);
1104
1105 return GTK_WIDGET (ecmc);
1106 }