2 * @file gtkcertmgr.c GTK+ Certificate Manager API
8 * Pidgin is the legal property of its developers, whose names are too numerous
9 * to list here. Please refer to the COPYRIGHT file distributed with this
10 * source distribution.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
31 #include "pidginstock.h"
33 #include "certificate.h"
41 #include "gtkcertmgr.h"
43 /*****************************************************************************
44 * X.509 tls_peers management interface *
45 *****************************************************************************/
48 GtkWidget
*mgmt_widget
;
49 GtkTreeView
*listview
;
50 GtkTreeSelection
*listselect
;
51 GtkWidget
*importbutton
;
52 GtkWidget
*exportbutton
;
53 GtkWidget
*infobutton
;
54 GtkWidget
*deletebutton
;
55 PurpleCertificatePool
*tls_peers
;
56 } tls_peers_mgmt_data
;
58 tls_peers_mgmt_data
*tpm_dat
= NULL
;
61 See http://developer.gnome.org/doc/API/2.0/gtk/TreeWidget.html */
69 tls_peers_mgmt_destroy(GtkWidget
*mgmt_widget
, gpointer data
)
71 purple_debug_info("certmgr",
72 "tls peers self-destructs\n");
74 purple_signals_disconnect_by_handle(tpm_dat
);
75 purple_request_close_with_handle(tpm_dat
);
76 g_free(tpm_dat
); tpm_dat
= NULL
;
80 tls_peers_mgmt_repopulate_list(void)
82 GtkTreeView
*listview
= tpm_dat
->listview
;
83 PurpleCertificatePool
*tls_peers
;
86 GtkListStore
*store
= GTK_LIST_STORE(
87 gtk_tree_view_get_model(GTK_TREE_VIEW(listview
)));
89 /* First, delete everything in the list */
90 gtk_list_store_clear(store
);
92 /* Locate the "tls_peers" pool */
93 tls_peers
= purple_certificate_find_pool("x509", "tls_peers");
94 g_return_if_fail(tls_peers
);
96 /* Grab the loaded certificates */
97 idlist
= purple_certificate_pool_get_idlist(tls_peers
);
99 /* Populate the listview */
100 for (l
= idlist
; l
; l
= l
->next
) {
102 gtk_list_store_append(store
, &iter
);
104 gtk_list_store_set(GTK_LIST_STORE(store
), &iter
,
105 TPM_HOSTNAME_COLUMN
, l
->data
,
108 purple_certificate_pool_destroy_idlist(idlist
);
112 tls_peers_mgmt_mod_cb(PurpleCertificatePool
*pool
, const gchar
*id
, gpointer data
)
114 g_assert (pool
== tpm_dat
->tls_peers
);
116 tls_peers_mgmt_repopulate_list();
120 tls_peers_mgmt_select_chg_cb(GtkTreeSelection
*ignored
, gpointer data
)
122 GtkTreeSelection
*select
= tpm_dat
->listselect
;
126 /* See if things are selected */
127 if (gtk_tree_selection_get_selected(select
, &model
, &iter
)) {
128 /* Enable buttons if something is selected */
129 gtk_widget_set_sensitive(GTK_WIDGET(tpm_dat
->exportbutton
), TRUE
);
130 gtk_widget_set_sensitive(GTK_WIDGET(tpm_dat
->infobutton
), TRUE
);
131 gtk_widget_set_sensitive(GTK_WIDGET(tpm_dat
->deletebutton
), TRUE
);
133 /* Otherwise, disable them */
134 gtk_widget_set_sensitive(GTK_WIDGET(tpm_dat
->exportbutton
), FALSE
);
135 gtk_widget_set_sensitive(GTK_WIDGET(tpm_dat
->infobutton
), FALSE
);
136 gtk_widget_set_sensitive(GTK_WIDGET(tpm_dat
->deletebutton
), FALSE
);
142 tls_peers_mgmt_import_ok2_cb(gpointer data
, const char *result
)
144 PurpleCertificate
*crt
= (PurpleCertificate
*) data
;
146 /* TODO: Perhaps prompt if you're overwriting a cert? */
148 /* Drop the certificate into the pool */
149 if (result
&& *result
)
150 purple_certificate_pool_store(tpm_dat
->tls_peers
, result
, crt
);
152 /* And this certificate is not needed any more */
153 purple_certificate_destroy(crt
);
157 tls_peers_mgmt_import_cancel2_cb(gpointer data
, const char *result
)
159 PurpleCertificate
*crt
= (PurpleCertificate
*) data
;
160 purple_certificate_destroy(crt
);
164 tls_peers_mgmt_import_ok_cb(gpointer data
, const char *filename
)
166 PurpleCertificateScheme
*x509
;
167 PurpleCertificate
*crt
;
169 /* Load the scheme of our tls_peers pool (ought to be x509) */
170 x509
= purple_certificate_pool_get_scheme(tpm_dat
->tls_peers
);
172 /* Now load the certificate from disk */
173 crt
= purple_certificate_import(x509
, filename
);
177 gchar
*default_hostname
;
178 /* Get name to add to pool as */
179 /* Make a guess about what the hostname should be */
180 default_hostname
= purple_certificate_get_subject_name(crt
);
181 /* TODO: Find a way to make sure that crt gets destroyed
182 if the window gets closed unusually, such as by handle
184 /* TODO: Display some more information on the certificate? */
185 purple_request_input(tpm_dat
,
186 _("Certificate Import"),
187 _("Specify a hostname"),
188 _("Type the host name for this certificate."),
190 FALSE
, /* Not multiline */
191 FALSE
, /* Not masked? */
192 NULL
, /* No hints? */
194 G_CALLBACK(tls_peers_mgmt_import_ok2_cb
),
196 G_CALLBACK(tls_peers_mgmt_import_cancel2_cb
),
197 NULL
, NULL
, NULL
, /* No account/who/conv*/
198 crt
/* Pass cert instance to callback*/
201 g_free(default_hostname
);
204 /* TODO: Perhaps find a way to be specific about what just
208 secondary
= g_strdup_printf(_("File %s could not be imported.\nMake sure that the file is readable and in PEM format.\n"), filename
);
209 purple_notify_error(NULL
,
210 _("Certificate Import Error"),
211 _("X.509 certificate import failed"),
218 tls_peers_mgmt_import_cb(GtkWidget
*button
, gpointer data
)
220 /* TODO: need to tell the user that we want a .PEM file! */
221 purple_request_file(tpm_dat
,
222 _("Select a PEM certificate"),
224 FALSE
, /* Not a save dialog */
225 G_CALLBACK(tls_peers_mgmt_import_ok_cb
),
226 NULL
, /* Do nothing if cancelled */
227 NULL
, NULL
, NULL
, NULL
);/* No account,conv,etc. */
231 tls_peers_mgmt_export_ok_cb(gpointer data
, const char *filename
)
233 PurpleCertificate
*crt
= (PurpleCertificate
*) data
;
237 if (!purple_certificate_export(filename
, crt
)) {
239 /* TODO: Perhaps find a way to be specific about what just
243 secondary
= g_strdup_printf(_("Export to file %s failed.\nCheck that you have write permission to the target path\n"), filename
);
244 purple_notify_error(NULL
,
245 _("Certificate Export Error"),
246 _("X.509 certificate export failed"),
251 purple_certificate_destroy(crt
);
255 tls_peers_mgmt_export_cancel_cb(gpointer data
, const char *filename
)
257 PurpleCertificate
*crt
= (PurpleCertificate
*) data
;
258 /* Pressing cancel just frees the duplicated certificate */
259 purple_certificate_destroy(crt
);
263 tls_peers_mgmt_export_cb(GtkWidget
*button
, gpointer data
)
265 PurpleCertificate
*crt
;
266 GtkTreeSelection
*select
= tpm_dat
->listselect
;
271 /* See if things are selected */
272 if (!gtk_tree_selection_get_selected(select
, &model
, &iter
)) {
273 purple_debug_warning("gtkcertmgr/tls_peers_mgmt",
274 "Export clicked with no selection?\n");
278 /* Retrieve the selected hostname */
279 gtk_tree_model_get(model
, &iter
, TPM_HOSTNAME_COLUMN
, &id
, -1);
281 /* Extract the certificate from the pool now to make sure it doesn't
282 get deleted out from under us */
283 crt
= purple_certificate_pool_retrieve(tpm_dat
->tls_peers
, id
);
286 purple_debug_error("gtkcertmgr/tls_peers_mgmt",
287 "Id %s was not in the peers cache?!\n",
294 /* TODO: inform user that it will be a PEM? */
295 purple_request_file(tpm_dat
,
296 _("PEM X.509 Certificate Export"),
298 TRUE
, /* Is a save dialog */
299 G_CALLBACK(tls_peers_mgmt_export_ok_cb
),
300 G_CALLBACK(tls_peers_mgmt_export_cancel_cb
),
301 NULL
, NULL
, NULL
, /* No account,conv,etc. */
302 crt
); /* Pass the certificate on to the callback */
306 tls_peers_mgmt_info_cb(GtkWidget
*button
, gpointer data
)
308 GtkTreeSelection
*select
= tpm_dat
->listselect
;
312 PurpleCertificate
*crt
;
314 /* See if things are selected */
315 if (!gtk_tree_selection_get_selected(select
, &model
, &iter
)) {
316 purple_debug_warning("gtkcertmgr/tls_peers_mgmt",
317 "Info clicked with no selection?\n");
321 /* Retrieve the selected hostname */
322 gtk_tree_model_get(model
, &iter
, TPM_HOSTNAME_COLUMN
, &id
, -1);
324 /* Now retrieve the certificate */
325 crt
= purple_certificate_pool_retrieve(tpm_dat
->tls_peers
, id
);
326 g_return_if_fail(crt
);
328 /* Fire the notification */
329 purple_certificate_display_x509(crt
);
332 purple_certificate_destroy(crt
);
336 tls_peers_mgmt_delete_confirm_cb(gchar
*id
, gint choice
)
339 /* Yes, delete was confirmed */
340 /* Now delete the thing */
341 if (!purple_certificate_pool_delete(tpm_dat
->tls_peers
, id
)) {
342 purple_debug_warning("gtkcertmgr/tls_peers_mgmt",
343 "Deletion failed on id %s\n",
352 tls_peers_mgmt_delete_cb(GtkWidget
*button
, gpointer data
)
354 GtkTreeSelection
*select
= tpm_dat
->listselect
;
358 /* See if things are selected */
359 if (gtk_tree_selection_get_selected(select
, &model
, &iter
)) {
364 /* Retrieve the selected hostname */
365 gtk_tree_model_get(model
, &iter
, TPM_HOSTNAME_COLUMN
, &id
, -1);
367 /* Prompt to confirm deletion */
368 primary
= g_strdup_printf(
369 _("Really delete certificate for %s?"), id
);
371 purple_request_yes_no(tpm_dat
, _("Confirm certificate delete"),
372 primary
, NULL
, /* Can this be NULL? */
373 0, /* "yes" is the default action */
375 id
, /* id ownership passed to callback */
376 tls_peers_mgmt_delete_confirm_cb
,
377 tls_peers_mgmt_delete_confirm_cb
);
382 purple_debug_warning("gtkcertmgr/tls_peers_mgmt",
383 "Delete clicked with no selection?\n");
389 tls_peers_mgmt_build(void)
394 /* This block of variables will end up in tpm_dat */
395 GtkTreeView
*listview
;
396 GtkTreeSelection
*select
;
397 GtkWidget
*importbutton
;
398 GtkWidget
*exportbutton
;
399 GtkWidget
*infobutton
;
400 GtkWidget
*deletebutton
;
401 /** Element to return to the Certmgr window to put in the Notebook */
402 GtkWidget
*mgmt_widget
;
404 /* Create a struct to store context information about this window */
405 tpm_dat
= g_new0(tls_peers_mgmt_data
, 1);
407 tpm_dat
->mgmt_widget
= mgmt_widget
=
408 gtk_hbox_new(FALSE
, /* Non-homogeneous */
409 PIDGIN_HIG_BOX_SPACE
);
410 gtk_container_set_border_width(GTK_CONTAINER(mgmt_widget
),
411 PIDGIN_HIG_BOX_SPACE
);
412 gtk_widget_show(mgmt_widget
);
414 /* Ensure that everything gets cleaned up when the dialog box
416 g_signal_connect(G_OBJECT(mgmt_widget
), "destroy",
417 G_CALLBACK(tls_peers_mgmt_destroy
), NULL
);
420 store
= gtk_list_store_new(TPM_N_COLUMNS
, G_TYPE_STRING
);
422 tpm_dat
->listview
= listview
=
423 GTK_TREE_VIEW(gtk_tree_view_new_with_model(GTK_TREE_MODEL(store
)));
424 g_object_unref(G_OBJECT(store
));
427 GtkCellRenderer
*renderer
;
428 GtkTreeViewColumn
*column
;
430 /* Set up the display columns */
431 renderer
= gtk_cell_renderer_text_new();
432 column
= gtk_tree_view_column_new_with_attributes(
435 "text", TPM_HOSTNAME_COLUMN
,
437 gtk_tree_view_append_column(GTK_TREE_VIEW(listview
), column
);
439 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store
),
440 TPM_HOSTNAME_COLUMN
, GTK_SORT_ASCENDING
);
443 /* Get the treeview selector into the struct */
444 tpm_dat
->listselect
= select
=
445 gtk_tree_view_get_selection(GTK_TREE_VIEW(listview
));
447 /* Force the selection mode */
448 gtk_tree_selection_set_mode(select
, GTK_SELECTION_SINGLE
);
450 /* Use a callback to enable/disable the buttons based on whether
451 something is selected */
452 g_signal_connect(G_OBJECT(select
), "changed",
453 G_CALLBACK(tls_peers_mgmt_select_chg_cb
), NULL
);
455 gtk_box_pack_start(GTK_BOX(mgmt_widget
),
456 pidgin_make_scrollable(GTK_WIDGET(listview
), GTK_POLICY_AUTOMATIC
, GTK_POLICY_ALWAYS
, GTK_SHADOW_IN
, -1, -1),
457 TRUE
, TRUE
, /* Take up lots of space */
459 gtk_widget_show(GTK_WIDGET(listview
));
461 /* Fill the list for the first time */
462 tls_peers_mgmt_repopulate_list();
464 /* Right-hand side controls box */
465 bbox
= gtk_vbutton_box_new();
466 gtk_box_pack_end(GTK_BOX(mgmt_widget
), bbox
,
467 FALSE
, FALSE
, /* Do not take up space */
469 gtk_box_set_spacing(GTK_BOX(bbox
), PIDGIN_HIG_BOX_SPACE
);
470 gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox
), GTK_BUTTONBOX_START
);
471 gtk_widget_show(bbox
);
474 /* TODO: This is the wrong stock button */
475 tpm_dat
->importbutton
= importbutton
=
476 gtk_button_new_from_stock(GTK_STOCK_ADD
);
477 gtk_box_pack_start(GTK_BOX(bbox
), importbutton
, FALSE
, FALSE
, 0);
478 gtk_widget_show(importbutton
);
479 g_signal_connect(G_OBJECT(importbutton
), "clicked",
480 G_CALLBACK(tls_peers_mgmt_import_cb
), NULL
);
484 /* TODO: This is the wrong stock button */
485 tpm_dat
->exportbutton
= exportbutton
=
486 gtk_button_new_from_stock(GTK_STOCK_SAVE
);
487 gtk_box_pack_start(GTK_BOX(bbox
), exportbutton
, FALSE
, FALSE
, 0);
488 gtk_widget_show(exportbutton
);
489 g_signal_connect(G_OBJECT(exportbutton
), "clicked",
490 G_CALLBACK(tls_peers_mgmt_export_cb
), NULL
);
494 tpm_dat
->infobutton
= infobutton
=
495 gtk_button_new_from_stock(PIDGIN_STOCK_INFO
);
496 gtk_box_pack_start(GTK_BOX(bbox
), infobutton
, FALSE
, FALSE
, 0);
497 gtk_widget_show(infobutton
);
498 g_signal_connect(G_OBJECT(infobutton
), "clicked",
499 G_CALLBACK(tls_peers_mgmt_info_cb
), NULL
);
503 tpm_dat
->deletebutton
= deletebutton
=
504 gtk_button_new_from_stock(GTK_STOCK_DELETE
);
505 gtk_box_pack_start(GTK_BOX(bbox
), deletebutton
, FALSE
, FALSE
, 0);
506 gtk_widget_show(deletebutton
);
507 g_signal_connect(G_OBJECT(deletebutton
), "clicked",
508 G_CALLBACK(tls_peers_mgmt_delete_cb
), NULL
);
510 /* Call the "selection changed" callback, which will probably disable
511 all the buttons since nothing is selected yet */
512 tls_peers_mgmt_select_chg_cb(select
, NULL
);
514 /* Bind us to the tls_peers pool */
515 tpm_dat
->tls_peers
= purple_certificate_find_pool("x509", "tls_peers");
517 /**** libpurple signals ****/
518 /* Respond to certificate add/remove by just reloading everything */
519 purple_signal_connect(tpm_dat
->tls_peers
, "certificate-stored",
520 tpm_dat
, PURPLE_CALLBACK(tls_peers_mgmt_mod_cb
),
522 purple_signal_connect(tpm_dat
->tls_peers
, "certificate-deleted",
523 tpm_dat
, PURPLE_CALLBACK(tls_peers_mgmt_mod_cb
),
529 const PidginCertificateManager tls_peers_mgmt
= {
530 tls_peers_mgmt_build
, /* Widget creation function */
534 /*****************************************************************************
535 * GTK+ main certificate manager *
536 *****************************************************************************/
542 GtkWidget
*closebutton
;
545 /* If a certificate manager window is open, this will point to it.
546 So if it is set, don't open another one! */
547 CertMgrDialog
*certmgr_dialog
= NULL
;
550 certmgr_close_cb(GtkWidget
*w
, CertMgrDialog
*dlg
)
552 /* TODO: Ignoring the arguments to this function may not be ideal,
553 but there *should* only be "one dialog to rule them all" at a time*/
554 pidgin_certmgr_hide();
559 pidgin_certmgr_show(void)
565 /* Enumerate all the certificates on file */
567 GList
*idlist
, *poollist
;
569 for ( poollist
= purple_certificate_get_pools();
571 poollist
= poollist
->next
) {
572 PurpleCertificatePool
*pool
= poollist
->data
;
575 purple_debug_info("gtkcertmgr",
576 "Pool %s found for scheme %s -"
577 "Enumerating certificates:\n",
578 pool
->name
, pool
->scheme_name
);
580 idlist
= purple_certificate_pool_get_idlist(pool
);
582 for (l
=idlist
; l
; l
= l
->next
) {
583 purple_debug_info("gtkcertmgr",
585 l
->data
? (gchar
*) l
->data
: "(null)");
587 purple_certificate_pool_destroy_idlist(idlist
);
592 /* If the manager is already open, bring it to the front */
593 if (certmgr_dialog
!= NULL
) {
594 gtk_window_present(GTK_WINDOW(certmgr_dialog
->window
));
598 /* Create the dialog, and set certmgr_dialog so we never create
599 more than one at a time */
600 dlg
= certmgr_dialog
= g_new0(CertMgrDialog
, 1);
603 pidgin_create_dialog(_("Certificate Manager"),/* Title */
604 PIDGIN_HIG_BORDER
, /*Window border*/
605 "certmgr", /* Role */
606 TRUE
); /* Allow resizing */
607 g_signal_connect(G_OBJECT(win
), "delete_event",
608 G_CALLBACK(certmgr_close_cb
), dlg
);
611 /* TODO: Retrieve the user-set window size and use it */
612 gtk_window_set_default_size(GTK_WINDOW(win
), 400, 400);
615 vbox
= pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win
), FALSE
, PIDGIN_HIG_BORDER
);
617 /* Notebook of various certificate managers */
618 dlg
->notebook
= gtk_notebook_new();
619 gtk_box_pack_start(GTK_BOX(vbox
), dlg
->notebook
,
620 TRUE
, TRUE
, /* Notebook should take extra space */
622 gtk_widget_show(dlg
->notebook
);
625 dlg
->closebutton
= pidgin_dialog_add_button(GTK_DIALOG(win
), GTK_STOCK_CLOSE
,
626 G_CALLBACK(certmgr_close_cb
), dlg
);
628 /* Add the defined certificate managers */
629 /* TODO: Find a way of determining whether each is shown or not */
630 /* TODO: Implement this correctly */
631 gtk_notebook_append_page(GTK_NOTEBOOK (dlg
->notebook
),
632 (tls_peers_mgmt
.build
)(),
633 gtk_label_new(_(tls_peers_mgmt
.label
)) );
635 gtk_widget_show(win
);
639 pidgin_certmgr_hide(void)
641 /* If it isn't open, do nothing */
642 if (certmgr_dialog
== NULL
) {
646 purple_signals_disconnect_by_handle(certmgr_dialog
);
647 purple_prefs_disconnect_by_handle(certmgr_dialog
);
649 gtk_widget_destroy(certmgr_dialog
->window
);
650 g_free(certmgr_dialog
);
651 certmgr_dialog
= NULL
;