1 /*
2  * @file gtkcertmgr.c GTK+ Certificate Manager API
3  * @ingroup pidgin
4  */
5 
6 /* pidgin
7  *
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.
11  *
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.
16  *
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.
21  *
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
25  *
26  */
27 
28 #include "internal.h"
29 #include "core.h"
30 #include "pidgin.h"
31 #include "pidginstock.h"
32 
33 #include "certificate.h"
34 #include "debug.h"
35 #include "notify.h"
36 #include "request.h"
37 
38 #include "gtkblist.h"
39 #include "gtkutils.h"
40 
41 #include "gtkcertmgr.h"
42 
43 /*****************************************************************************
44  * X.509 tls_peers management interface                                      *
45  *****************************************************************************/
46 
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;
57 
58 tls_peers_mgmt_data *tpm_dat = NULL;
59 
60 /* Columns
61    See http://developer.gnome.org/doc/API/2.0/gtk/TreeWidget.html */
62 enum
63 {
64 	TPM_HOSTNAME_COLUMN,
65 	TPM_N_COLUMNS
66 };
67 
68 static void
tls_peers_mgmt_destroy(GtkWidget * mgmt_widget,gpointer data)69 tls_peers_mgmt_destroy(GtkWidget *mgmt_widget, gpointer data)
70 {
71 	purple_debug_info("certmgr",
72 			  "tls peers self-destructs\n");
73 
74 	purple_signals_disconnect_by_handle(tpm_dat);
75 	purple_request_close_with_handle(tpm_dat);
76 	g_free(tpm_dat); tpm_dat = NULL;
77 }
78 
79 static void
tls_peers_mgmt_repopulate_list(void)80 tls_peers_mgmt_repopulate_list(void)
81 {
82 	GtkTreeView *listview = tpm_dat->listview;
83 	PurpleCertificatePool *tls_peers;
84 	GList *idlist, *l;
85 
86 	GtkListStore *store = GTK_LIST_STORE(
87 		gtk_tree_view_get_model(GTK_TREE_VIEW(listview)));
88 
89 	/* First, delete everything in the list */
90 	gtk_list_store_clear(store);
91 
92 	/* Locate the "tls_peers" pool */
93 	tls_peers = purple_certificate_find_pool("x509", "tls_peers");
94 	g_return_if_fail(tls_peers);
95 
96 	/* Grab the loaded certificates */
97 	idlist = purple_certificate_pool_get_idlist(tls_peers);
98 
99 	/* Populate the listview */
100 	for (l = idlist; l; l = l->next) {
101 		GtkTreeIter iter;
102 		gtk_list_store_append(store, &iter);
103 
104 		gtk_list_store_set(GTK_LIST_STORE(store), &iter,
105 				   TPM_HOSTNAME_COLUMN, l->data,
106 				   -1);
107 	}
108 	purple_certificate_pool_destroy_idlist(idlist);
109 }
110 
111 static void
tls_peers_mgmt_mod_cb(PurpleCertificatePool * pool,const gchar * id,gpointer data)112 tls_peers_mgmt_mod_cb(PurpleCertificatePool *pool, const gchar *id, gpointer data)
113 {
114 	g_assert (pool == tpm_dat->tls_peers);
115 
116 	tls_peers_mgmt_repopulate_list();
117 }
118 
119 static void
tls_peers_mgmt_select_chg_cb(GtkTreeSelection * ignored,gpointer data)120 tls_peers_mgmt_select_chg_cb(GtkTreeSelection *ignored, gpointer data)
121 {
122 	GtkTreeSelection *select = tpm_dat->listselect;
123 	GtkTreeIter iter;
124 	GtkTreeModel *model;
125 
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);
137 
138 	}
139 }
140 
141 static void
tls_peers_mgmt_import_ok2_cb(gpointer data,const char * result)142 tls_peers_mgmt_import_ok2_cb(gpointer data, const char *result)
143 {
144 	PurpleCertificate *crt = (PurpleCertificate *) data;
145 
146 	/* TODO: Perhaps prompt if you're overwriting a cert? */
147 
148 	/* Drop the certificate into the pool */
149 	if (result && *result)
150 		purple_certificate_pool_store(tpm_dat->tls_peers, result, crt);
151 
152 	/* And this certificate is not needed any more */
153 	purple_certificate_destroy(crt);
154 }
155 
156 static void
tls_peers_mgmt_import_cancel2_cb(gpointer data,const char * result)157 tls_peers_mgmt_import_cancel2_cb(gpointer data, const char *result)
158 {
159 	PurpleCertificate *crt = (PurpleCertificate *) data;
160 	purple_certificate_destroy(crt);
161 }
162 
163 static void
tls_peers_mgmt_import_ok_cb(gpointer data,const char * filename)164 tls_peers_mgmt_import_ok_cb(gpointer data, const char *filename)
165 {
166 	PurpleCertificateScheme *x509;
167 	PurpleCertificate *crt;
168 
169 	/* Load the scheme of our tls_peers pool (ought to be x509) */
170 	x509 = purple_certificate_pool_get_scheme(tpm_dat->tls_peers);
171 
172 	/* Now load the certificate from disk */
173 	crt = purple_certificate_import(x509, filename);
174 
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*/
199 				     );
200 
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;
207 
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);
214 	}
215 }
216 
217 static void
tls_peers_mgmt_import_cb(GtkWidget * button,gpointer data)218 tls_peers_mgmt_import_cb(GtkWidget *button, gpointer data)
219 {
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. */
228 }
229 
230 static void
tls_peers_mgmt_export_ok_cb(gpointer data,const char * filename)231 tls_peers_mgmt_export_ok_cb(gpointer data, const char *filename)
232 {
233 	PurpleCertificate *crt = (PurpleCertificate *) data;
234 
235 	g_assert(filename);
236 
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;
242 
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);
249 	}
250 
251 	purple_certificate_destroy(crt);
252 }
253 
254 static void
tls_peers_mgmt_export_cancel_cb(gpointer data,const char * filename)255 tls_peers_mgmt_export_cancel_cb(gpointer data, const char *filename)
256 {
257 	PurpleCertificate *crt = (PurpleCertificate *) data;
258 	/* Pressing cancel just frees the duplicated certificate */
259 	purple_certificate_destroy(crt);
260 }
261 
262 static void
tls_peers_mgmt_export_cb(GtkWidget * button,gpointer data)263 tls_peers_mgmt_export_cb(GtkWidget *button, gpointer data)
264 {
265 	PurpleCertificate *crt;
266 	GtkTreeSelection *select = tpm_dat->listselect;
267 	GtkTreeIter iter;
268 	GtkTreeModel *model;
269 	gchar *id;
270 
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;
276 	}
277 
278 	/* Retrieve the selected hostname */
279 	gtk_tree_model_get(model, &iter, TPM_HOSTNAME_COLUMN, &id, -1);
280 
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);
284 
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;
291 	}
292 	g_free(id);
293 
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 */
303 }
304 
305 static void
tls_peers_mgmt_info_cb(GtkWidget * button,gpointer data)306 tls_peers_mgmt_info_cb(GtkWidget *button, gpointer data)
307 {
308 	GtkTreeSelection *select = tpm_dat->listselect;
309 	GtkTreeIter iter;
310 	GtkTreeModel *model;
311 	gchar *id;
312 	PurpleCertificate *crt;
313 
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;
319 	}
320 
321 	/* Retrieve the selected hostname */
322 	gtk_tree_model_get(model, &iter, TPM_HOSTNAME_COLUMN, &id, -1);
323 
324 	/* Now retrieve the certificate */
325 	crt = purple_certificate_pool_retrieve(tpm_dat->tls_peers, id);
326 	g_return_if_fail(crt);
327 
328 	/* Fire the notification */
329 	purple_certificate_display_x509(crt);
330 
331 	g_free(id);
332 	purple_certificate_destroy(crt);
333 }
334 
335 static void
tls_peers_mgmt_delete_confirm_cb(gchar * id,gint choice)336 tls_peers_mgmt_delete_confirm_cb(gchar *id, gint choice)
337 {
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);
345 		};
346 	}
347 
348 	g_free(id);
349 }
350 
351 static void
tls_peers_mgmt_delete_cb(GtkWidget * button,gpointer data)352 tls_peers_mgmt_delete_cb(GtkWidget *button, gpointer data)
353 {
354 	GtkTreeSelection *select = tpm_dat->listselect;
355 	GtkTreeIter iter;
356 	GtkTreeModel *model;
357 
358 	/* See if things are selected */
359 	if (gtk_tree_selection_get_selected(select, &model, &iter)) {
360 
361 		gchar *id;
362 		gchar *primary;
363 
364 		/* Retrieve the selected hostname */
365 		gtk_tree_model_get(model, &iter, TPM_HOSTNAME_COLUMN, &id, -1);
366 
367 		/* Prompt to confirm deletion */
368 		primary = g_strdup_printf(
369 			_("Really delete certificate for %s?"), id );
370 
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 );
378 
379 		g_free(primary);
380 
381 	} else {
382 		purple_debug_warning("gtkcertmgr/tls_peers_mgmt",
383 				     "Delete clicked with no selection?\n");
384 		return;
385 	}
386 }
387 
388 static GtkWidget *
tls_peers_mgmt_build(void)389 tls_peers_mgmt_build(void)
390 {
391 	GtkWidget *bbox;
392 	GtkListStore *store;
393 
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;
403 
404 	/* Create a struct to store context information about this window */
405 	tpm_dat = g_new0(tls_peers_mgmt_data, 1);
406 
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);
413 
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);
418 
419 	/* List view */
420 	store = gtk_list_store_new(TPM_N_COLUMNS, G_TYPE_STRING);
421 
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));
425 
426 	{
427 		GtkCellRenderer *renderer;
428 		GtkTreeViewColumn *column;
429 
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);
438 
439 		gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store),
440 				TPM_HOSTNAME_COLUMN, GTK_SORT_ASCENDING);
441 	}
442 
443 	/* Get the treeview selector into the struct */
444 	tpm_dat->listselect = select =
445 		gtk_tree_view_get_selection(GTK_TREE_VIEW(listview));
446 
447 	/* Force the selection mode */
448 	gtk_tree_selection_set_mode(select, GTK_SELECTION_SINGLE);
449 
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);
454 
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 */
458 			0);
459 	gtk_widget_show(GTK_WIDGET(listview));
460 
461 	/* Fill the list for the first time */
462 	tls_peers_mgmt_repopulate_list();
463 
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 */
468 			 0);
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);
472 
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);
481 
482 
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);
491 
492 
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);
500 
501 
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);
509 
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);
513 
514 	/* Bind us to the tls_peers pool */
515 	tpm_dat->tls_peers = purple_certificate_find_pool("x509", "tls_peers");
516 
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);
525 
526 	return mgmt_widget;
527 }
528 
529 const PidginCertificateManager tls_peers_mgmt = {
530 	tls_peers_mgmt_build, /* Widget creation function */
531 	N_("SSL Servers")
532 };
533 
534 /*****************************************************************************
535  * GTK+ main certificate manager                                             *
536  *****************************************************************************/
537 typedef struct
538 {
539 	GtkWidget *window;
540 	GtkWidget *notebook;
541 
542 	GtkWidget *closebutton;
543 } CertMgrDialog;
544 
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;
548 
549 static gboolean
certmgr_close_cb(GtkWidget * w,CertMgrDialog * dlg)550 certmgr_close_cb(GtkWidget *w, CertMgrDialog *dlg)
551 {
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;
556 }
557 
558 void
pidgin_certmgr_show(void)559 pidgin_certmgr_show(void)
560 {
561 	CertMgrDialog *dlg;
562 	GtkWidget *win;
563 	GtkWidget *vbox;
564 
565 	/* Enumerate all the certificates on file */
566 	{
567 		GList *idlist, *poollist;
568 
569 		for ( poollist = purple_certificate_get_pools();
570 		      poollist;
571 		      poollist = poollist->next ) {
572 			PurpleCertificatePool *pool = poollist->data;
573 			GList *l;
574 
575 			purple_debug_info("gtkcertmgr",
576 					  "Pool %s found for scheme %s -"
577 					  "Enumerating certificates:\n",
578 					  pool->name, pool->scheme_name);
579 
580 			idlist = purple_certificate_pool_get_idlist(pool);
581 
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 */
589 	}
590 
591 
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;
596 	}
597 
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);
601 
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);
609 
610 
611 	/* TODO: Retrieve the user-set window size and use it */
612 	gtk_window_set_default_size(GTK_WINDOW(win), 400, 400);
613 
614 	/* Main vbox */
615 	vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, PIDGIN_HIG_BORDER);
616 
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 */
621 			   0);
622 	gtk_widget_show(dlg->notebook);
623 
624 	/* Close button */
625 	dlg->closebutton = pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CLOSE,
626 			G_CALLBACK(certmgr_close_cb), dlg);
627 
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)) );
634 
635 	gtk_widget_show(win);
636 }
637 
638 void
pidgin_certmgr_hide(void)639 pidgin_certmgr_hide(void)
640 {
641 	/* If it isn't open, do nothing */
642 	if (certmgr_dialog == NULL) {
643 		return;
644 	}
645 
646 	purple_signals_disconnect_by_handle(certmgr_dialog);
647 	purple_prefs_disconnect_by_handle(certmgr_dialog);
648 
649 	gtk_widget_destroy(certmgr_dialog->window);
650 	g_free(certmgr_dialog);
651 	certmgr_dialog = NULL;
652 }
653