Remove redundant NULL checks
[pidgin-git.git] / pidgin / gtkcertmgr.c
blob381ff2c72bfc860f07be69a9cf374efd473f3f63
1 /*
2 * @file gtkcertmgr.c GTK+ Certificate Manager API
3 * @ingroup pidgin
4 */
6 /* pidgin
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
28 #include "internal.h"
29 #include "core.h"
30 #include "pidgin.h"
31 #include "pidginstock.h"
33 #include "certificate.h"
34 #include "debug.h"
35 #include "notify.h"
36 #include "request.h"
38 #include "gtkblist.h"
39 #include "gtkutils.h"
41 #include "gtkcertmgr.h"
43 /*****************************************************************************
44 * X.509 tls_peers management interface *
45 *****************************************************************************/
47 typedef struct {
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;
60 /* Columns
61 See http://developer.gnome.org/doc/API/2.0/gtk/TreeWidget.html */
62 enum
64 TPM_HOSTNAME_COLUMN,
65 TPM_N_COLUMNS
68 static void
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;
79 static void
80 tls_peers_mgmt_repopulate_list(void)
82 GtkTreeView *listview = tpm_dat->listview;
83 PurpleCertificatePool *tls_peers;
84 GList *idlist, *l;
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) {
101 GtkTreeIter iter;
102 gtk_list_store_append(store, &iter);
104 gtk_list_store_set(GTK_LIST_STORE(store), &iter,
105 TPM_HOSTNAME_COLUMN, l->data,
106 -1);
108 purple_certificate_pool_destroy_idlist(idlist);
111 static void
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();
119 static void
120 tls_peers_mgmt_select_chg_cb(GtkTreeSelection *ignored, gpointer data)
122 GtkTreeSelection *select = tpm_dat->listselect;
123 GtkTreeIter iter;
124 GtkTreeModel *model;
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);
132 } else {
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);
141 static void
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);
156 static void
157 tls_peers_mgmt_import_cancel2_cb(gpointer data, const char *result)
159 PurpleCertificate *crt = (PurpleCertificate *) data;
160 purple_certificate_destroy(crt);
163 static void
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);
175 /* Did it work? */
176 if (crt != NULL) {
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
183 deletion */
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."),
189 default_hostname,
190 FALSE, /* Not multiline */
191 FALSE, /* Not masked? */
192 NULL, /* No hints? */
193 _("OK"),
194 G_CALLBACK(tls_peers_mgmt_import_ok2_cb),
195 _("Cancel"),
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);
202 } else {
203 /* Errors! Oh no! */
204 /* TODO: Perhaps find a way to be specific about what just
205 went wrong? */
206 gchar * secondary;
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"),
212 secondary);
213 g_free(secondary);
217 static void
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"),
223 "certificate.pem",
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. */
230 static void
231 tls_peers_mgmt_export_ok_cb(gpointer data, const char *filename)
233 PurpleCertificate *crt = (PurpleCertificate *) data;
235 g_assert(filename);
237 if (!purple_certificate_export(filename, crt)) {
238 /* Errors! Oh no! */
239 /* TODO: Perhaps find a way to be specific about what just
240 went wrong? */
241 gchar * secondary;
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"),
247 secondary);
248 g_free(secondary);
251 purple_certificate_destroy(crt);
254 static void
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);
262 static void
263 tls_peers_mgmt_export_cb(GtkWidget *button, gpointer data)
265 PurpleCertificate *crt;
266 GtkTreeSelection *select = tpm_dat->listselect;
267 GtkTreeIter iter;
268 GtkTreeModel *model;
269 gchar *id;
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");
275 return;
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);
285 if (NULL == crt) {
286 purple_debug_error("gtkcertmgr/tls_peers_mgmt",
287 "Id %s was not in the peers cache?!\n",
288 id);
289 g_free(id);
290 return;
292 g_free(id);
294 /* TODO: inform user that it will be a PEM? */
295 purple_request_file(tpm_dat,
296 _("PEM X.509 Certificate Export"),
297 "certificate.pem",
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 */
305 static void
306 tls_peers_mgmt_info_cb(GtkWidget *button, gpointer data)
308 GtkTreeSelection *select = tpm_dat->listselect;
309 GtkTreeIter iter;
310 GtkTreeModel *model;
311 gchar *id;
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");
318 return;
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);
331 g_free(id);
332 purple_certificate_destroy(crt);
335 static void
336 tls_peers_mgmt_delete_confirm_cb(gchar *id, gint choice)
338 if (1 == 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",
344 id);
348 g_free(id);
351 static void
352 tls_peers_mgmt_delete_cb(GtkWidget *button, gpointer data)
354 GtkTreeSelection *select = tpm_dat->listselect;
355 GtkTreeIter iter;
356 GtkTreeModel *model;
358 /* See if things are selected */
359 if (gtk_tree_selection_get_selected(select, &model, &iter)) {
361 gchar *id;
362 gchar *primary;
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 */
374 NULL, NULL, NULL,
375 id, /* id ownership passed to callback */
376 tls_peers_mgmt_delete_confirm_cb,
377 tls_peers_mgmt_delete_confirm_cb );
379 g_free(primary);
381 } else {
382 purple_debug_warning("gtkcertmgr/tls_peers_mgmt",
383 "Delete clicked with no selection?\n");
384 return;
388 static GtkWidget *
389 tls_peers_mgmt_build(void)
391 GtkWidget *bbox;
392 GtkListStore *store;
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
415 is closed */
416 g_signal_connect(G_OBJECT(mgmt_widget), "destroy",
417 G_CALLBACK(tls_peers_mgmt_destroy), NULL);
419 /* List view */
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(
433 _("Hostname"),
434 renderer,
435 "text", TPM_HOSTNAME_COLUMN,
436 NULL);
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);
473 /* Import button */
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);
483 /* Export button */
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);
493 /* Info button */
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);
502 /* Delete button */
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),
521 NULL);
522 purple_signal_connect(tpm_dat->tls_peers, "certificate-deleted",
523 tpm_dat, PURPLE_CALLBACK(tls_peers_mgmt_mod_cb),
524 NULL);
526 return mgmt_widget;
529 const PidginCertificateManager tls_peers_mgmt = {
530 tls_peers_mgmt_build, /* Widget creation function */
531 N_("SSL Servers")
534 /*****************************************************************************
535 * GTK+ main certificate manager *
536 *****************************************************************************/
537 typedef struct
539 GtkWidget *window;
540 GtkWidget *notebook;
542 GtkWidget *closebutton;
543 } CertMgrDialog;
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;
549 static gboolean
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();
555 return FALSE;
558 void
559 pidgin_certmgr_show(void)
561 CertMgrDialog *dlg;
562 GtkWidget *win;
563 GtkWidget *vbox;
565 /* Enumerate all the certificates on file */
567 GList *idlist, *poollist;
569 for ( poollist = purple_certificate_get_pools();
570 poollist;
571 poollist = poollist->next ) {
572 PurpleCertificatePool *pool = poollist->data;
573 GList *l;
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",
584 "- %s\n",
585 l->data ? (gchar *) l->data : "(null)");
586 } /* idlist */
587 purple_certificate_pool_destroy_idlist(idlist);
588 } /* poollist */
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));
595 return;
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);
602 win = dlg->window =
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);
614 /* Main vbox */
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);
624 /* Close button */
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);
638 void
639 pidgin_certmgr_hide(void)
641 /* If it isn't open, do nothing */
642 if (certmgr_dialog == NULL) {
643 return;
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;