1 /*
2  *
3  * This program is free software; you can redistribute it and/or modify it
4  * under the terms of the GNU Lesser General Public License as published by
5  * the Free Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful, but
8  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
9  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
10  * for more details.
11  *
12  * You should have received a copy of the GNU Lesser General Public License
13  * along with this program; if not, see <http://www.gnu.org/licenses/>.
14  *
15  *
16  * Authors:
17  *		Michael Zucchi <notzed@ximian.com>
18  *
19  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
20  *
21  */
22 
23 #include "evolution-config.h"
24 
25 #include <glib/gi18n.h>
26 
27 #include "nss.h"
28 #include "pk11func.h"
29 #include "certdb.h"
30 #include "cert.h"
31 
32 #include "e-cert-selector.h"
33 
34 #include "e-util/e-util.h"
35 #include "e-util/e-util-private.h"
36 
37 /* XXX Hack to disable p11-kit's pkcs11.h header, since
38  *     NSS headers supply the same PKCS #11 definitions. */
39 #define PKCS11_H 1
40 
41 #define GCR_API_SUBJECT_TO_CHANGE
42 #include "gcr/gcr.h"
43 
44 #include "smime/lib/e-cert.h"
45 
46 #define E_CERT_SELECTOR_GET_PRIVATE(obj) \
47 	(G_TYPE_INSTANCE_GET_PRIVATE \
48 	((obj), E_TYPE_CERT_SELECTOR, ECertSelectorPrivate))
49 
50 struct _ECertSelectorPrivate {
51 	CERTCertList *certlist;
52 
53 	GtkWidget *combobox;
54 	GcrCertificateWidget *cert_widget;
55 };
56 
57 enum {
58 	ECS_SELECTED,
59 	ECS_LAST_SIGNAL
60 };
61 
62 static guint ecs_signals[ECS_LAST_SIGNAL];
63 
G_DEFINE_TYPE(ECertSelector,e_cert_selector,GTK_TYPE_DIALOG)64 G_DEFINE_TYPE (ECertSelector, e_cert_selector, GTK_TYPE_DIALOG)
65 
66 /* (this is what mozilla shows)
67  * Issued to:
68  *  Subject: E=notzed@ximian.com, CN=notzed@ximian.com, O=My Company Ltd, L=Adelaide, ST=SA, C=AU
69  *  Serial Number: 03
70  *  Valid from 23/10/03 06:35:29 to 22/10/04 06:35:29
71  *  Purposes: Sign,Encrypt
72  * Issued by:
73  *  Subject: E=notzed@ximian.com, O=company, L=there, ST=Here, C=AU
74  */
75 
76 static CERTCertListNode *
77 ecs_find_current (ECertSelector *ecs)
78 {
79 	struct _ECertSelectorPrivate *p = ecs->priv;
80 	CERTCertListNode *node;
81 	gint n;
82 
83 	if (p->certlist == NULL || CERT_LIST_EMPTY (p->certlist))
84 		return NULL;
85 
86 	n = gtk_combo_box_get_active (GTK_COMBO_BOX (p->combobox));
87 	node = CERT_LIST_HEAD (p->certlist);
88 	while (n > 0 && !CERT_LIST_END (node, p->certlist)) {
89 		n--;
90 		node = CERT_LIST_NEXT (node);
91 	}
92 
93 	g_return_val_if_fail (!CERT_LIST_END (node, p->certlist), NULL);
94 
95 	return node;
96 }
97 
98 static void
e_cert_selector_response(GtkDialog * dialog,gint button)99 e_cert_selector_response (GtkDialog *dialog,
100                           gint button)
101 {
102 	CERTCertListNode *node;
103 
104 	switch (button) {
105 	case GTK_RESPONSE_OK:
106 		node = ecs_find_current ((ECertSelector *) dialog);
107 		break;
108 	default:
109 		node = NULL;
110 		break;
111 	}
112 
113 	g_signal_emit (dialog, ecs_signals[ECS_SELECTED], 0, node ? node->cert->nickname : NULL);
114 }
115 
116 static void
ecs_cert_changed(GtkWidget * w,ECertSelector * ecs)117 ecs_cert_changed (GtkWidget *w,
118                   ECertSelector *ecs)
119 {
120 	struct _ECertSelectorPrivate *p = ecs->priv;
121 	CERTCertListNode *node;
122 
123 	node = ecs_find_current (ecs);
124 	if (node) {
125 		ECert *ecert = e_cert_new (CERT_DupCertificate ((CERTCertificate *) node->cert));
126 		gcr_certificate_widget_set_certificate (p->cert_widget, GCR_CERTIFICATE (ecert));
127 		g_object_unref (ecert);
128 	}
129 }
130 
131 /**
132  * e_cert_selector_new:
133  * @type:
134  * @currentid:
135  *
136  * Create a new ECertSelector dialog.  @type specifies which type of cert to
137  * be selected, E_CERT_SELECTOR_SIGNER for signing certs, and
138  * E_CERT_SELECTOR_RECIPIENT for encrypting certs.
139  *
140  * @currentid is the nickname of the cert currently selected for this user.
141  *
142  * You only need to connect to a single signal "selected" which will
143  * be called with either a NULL nickname if cancelled, or the newly
144  * selected nickname otherwise.
145  *
146  * Return value: A dialogue to be shown.
147  **/
148 GtkWidget *
e_cert_selector_new(gint type,const gchar * currentid)149 e_cert_selector_new (gint type,
150                      const gchar *currentid)
151 {
152 	ECertSelector *ecs;
153 	struct _ECertSelectorPrivate *p;
154 	SECCertUsage usage;
155 	CERTCertList *certlist;
156 	CERTCertListNode *node;
157 	GtkBuilder *builder;
158 	GtkWidget *content_area;
159 	GtkWidget *w;
160 	GtkListStore *store;
161 	GtkTreeIter iter;
162 	gint n = 0, active = 0;
163 
164 	ecs = g_object_new (e_cert_selector_get_type (), NULL);
165 	p = ecs->priv;
166 
167 	builder = gtk_builder_new ();
168 	e_load_ui_builder_definition (builder, "smime-ui.ui");
169 
170 	p->combobox = e_builder_get_widget (builder, "cert_combobox");
171 	p->cert_widget = gcr_certificate_widget_new (NULL);
172 
173 	w = e_builder_get_widget (builder, "cert_selector_vbox");
174 	content_area = gtk_dialog_get_content_area (GTK_DIALOG (ecs));
175 	gtk_container_add (GTK_CONTAINER (w), GTK_WIDGET (p->cert_widget));
176 	gtk_widget_show (GTK_WIDGET (p->cert_widget));
177 	gtk_box_pack_start (GTK_BOX (content_area), w, TRUE, TRUE, 3);
178 	gtk_window_set_title (GTK_WINDOW (ecs), _("Select certificate"));
179 
180 	switch (type) {
181 	case E_CERT_SELECTOR_SIGNER:
182 	default:
183 		usage = certUsageEmailSigner;
184 		break;
185 	case E_CERT_SELECTOR_RECIPIENT:
186 		usage = certUsageEmailRecipient;
187 		break;
188 	}
189 
190 	store = GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (p->combobox)));
191 	gtk_list_store_clear (store);
192 
193 	certlist = CERT_FindUserCertsByUsage (CERT_GetDefaultCertDB (), usage, FALSE, TRUE, NULL);
194 	ecs->priv->certlist = certlist;
195 	if (certlist != NULL) {
196 		node = CERT_LIST_HEAD (certlist);
197 		while (!CERT_LIST_END (node, certlist)) {
198 			if (node->cert->nickname || node->cert->emailAddr) {
199 				gtk_list_store_append (store, &iter);
200 				gtk_list_store_set (
201 					store, &iter,
202 					0, node->cert->nickname ? node->cert->nickname : node->cert->emailAddr,
203 					-1);
204 
205 				if (currentid != NULL
206 				    && ((node->cert->nickname != NULL && strcmp (node->cert->nickname, currentid) == 0)
207 					|| (node->cert->emailAddr != NULL && strcmp (node->cert->emailAddr, currentid) == 0)))
208 					active = n;
209 
210 				n++;
211 			}
212 
213 			node = CERT_LIST_NEXT (node);
214 		}
215 	}
216 
217 	gtk_combo_box_set_active (GTK_COMBO_BOX (p->combobox), active);
218 
219 	g_signal_connect (
220 		p->combobox, "changed",
221 		G_CALLBACK (ecs_cert_changed), ecs);
222 
223 	g_object_unref (builder);
224 
225 	ecs_cert_changed (p->combobox, ecs);
226 
227 	return GTK_WIDGET (ecs);
228 }
229 
230 static void
e_cert_selector_init(ECertSelector * ecs)231 e_cert_selector_init (ECertSelector *ecs)
232 {
233 	gtk_dialog_add_buttons (
234 		GTK_DIALOG (ecs),
235 		_("_Cancel"), GTK_RESPONSE_CANCEL,
236 		_("_OK"), GTK_RESPONSE_OK, NULL);
237 
238 	ecs->priv = E_CERT_SELECTOR_GET_PRIVATE (ecs);
239 }
240 
241 static void
e_cert_selector_finalize(GObject * object)242 e_cert_selector_finalize (GObject *object)
243 {
244 	ECertSelectorPrivate *priv;
245 
246 	priv = E_CERT_SELECTOR_GET_PRIVATE (object);
247 
248 	if (priv->certlist)
249 		CERT_DestroyCertList (priv->certlist);
250 
251 	/* Chain up to parent's finalize() method. */
252 	G_OBJECT_CLASS (e_cert_selector_parent_class)->finalize (object);
253 }
254 
255 static void
e_cert_selector_class_init(ECertSelectorClass * class)256 e_cert_selector_class_init (ECertSelectorClass *class)
257 {
258 	GObjectClass *object_class;
259 	GtkDialogClass *dialog_class;
260 
261 	g_type_class_add_private (class, sizeof (ECertSelectorPrivate));
262 
263 	object_class = G_OBJECT_CLASS (class);
264 	object_class->finalize = e_cert_selector_finalize;
265 
266 	dialog_class = GTK_DIALOG_CLASS (class);
267 	dialog_class->response = e_cert_selector_response;
268 
269 	ecs_signals[ECS_SELECTED] = g_signal_new (
270 		"selected",
271 		G_OBJECT_CLASS_TYPE (class),
272 		G_SIGNAL_RUN_LAST,
273 		G_STRUCT_OFFSET (ECertSelectorClass, selected),
274 		NULL, NULL,
275 		g_cclosure_marshal_VOID__POINTER,
276 		G_TYPE_NONE, 1,
277 		G_TYPE_POINTER);
278 }
279 
280