1 /* keygendlg.c - The GNU Privacy Assistant
2 Copyright (C) 2000, 2001 G-N-U GmbH.
3 Copyright (C) 2008, 2009 g10 Code GmbH.
4
5 This file is part of GPA
6
7 GPA is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
11
12 GPA is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
15 License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, see <http://www.gnu.org/licenses/>. */
19
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23
24 #include <sys/stat.h>
25 #include <glib.h>
26 #include <gtk/gtk.h>
27 #include <errno.h>
28 #include <gpgme.h>
29
30 #include "gpa.h"
31 #include "icons.h"
32 #include "gtktools.h"
33 #include "gpawidgets.h"
34 #include "qdchkpwd.h"
35 #include "gpgmetools.h"
36 #include "keygenwizard.h"
37
38
39 #define STANDARD_KEY_LENGTH 2048
40
41
42 /* The key generation wizard.
43
44 New users should not be overwhelmed by too many options most of which
45 are not easily explained and will only confuse them. To solve that
46 problem we use default values for the algorithm and size of the keys
47 and we use a wizard interface to present the necessary options like
48 name and email address in a step by step manner. */
49
50 /* Helper functions. */
51
52 /* Return a copy of string with leading and trailing whitespace
53 stripped. */
54 static char *
string_strip_dup(const char * string)55 string_strip_dup (const char *string)
56 {
57 return g_strstrip (g_strdup (string));
58 }
59
60
61 /* The wizard itself. */
62
63 typedef struct
64 {
65 GtkWidget *window;
66 GtkWidget *wizard;
67 GtkWidget *name_page;
68 GtkWidget *email_page;
69 GtkWidget *wait_page;
70 GtkWidget *final_page;
71 GtkWidget *backup_page;
72 GtkWidget *backup_dir_page;
73
74 GpaKeyGenWizardGenerateCb generate;
75 gpointer generate_data;
76 } GPAKeyGenWizard;
77
78
79 /* Internal API. */
80 static gboolean gpa_keygen_wizard_generate_action (gpointer data);
81
82
83 /* The user ID pages. */
84
85 static GtkWidget *
gpa_keygen_wizard_simple_page(GPAKeyGenWizard * keygen_wizard,const gchar * description_text,const gchar * label_text)86 gpa_keygen_wizard_simple_page (GPAKeyGenWizard *keygen_wizard,
87 const gchar *description_text,
88 const gchar *label_text)
89 {
90 GtkWidget *align;
91 guint pt, pb, pl, pr;
92 GtkWidget *vbox;
93 GtkWidget *description;
94 GtkWidget *hbox;
95 GtkWidget *label;
96 GtkWidget *entry;
97
98 align = gtk_alignment_new (0.5, 0.5, 1, 1);
99 gtk_alignment_get_padding (GTK_ALIGNMENT (align), &pt, &pb, &pl, &pr);
100 gtk_alignment_set_padding (GTK_ALIGNMENT (align),
101 pt + 5, pb + 5, pl + 5, pr + 5);
102
103 vbox = gtk_vbox_new (FALSE, 0);
104 gtk_container_add (GTK_CONTAINER (align), vbox);
105
106 description = gtk_label_new (description_text);
107 gtk_box_pack_start (GTK_BOX (vbox), description, TRUE, TRUE, 0);
108 gtk_misc_set_alignment (GTK_MISC (description), 0.0, 0.0);
109 gtk_label_set_line_wrap (GTK_LABEL (description), TRUE);
110 gtk_label_set_justify (GTK_LABEL (description), GTK_JUSTIFY_LEFT);
111
112 hbox = gtk_hbox_new (FALSE, 0);
113 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 5);
114
115 label = gtk_label_new (label_text);
116 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
117 gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
118
119 entry = gtk_entry_new ();
120 gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
121 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 5);
122
123 g_object_set_data (G_OBJECT (align), "gpa_keygen_entry", entry);
124 g_object_set_data (G_OBJECT (align), "gpa_wizard_focus_child", entry);
125
126 return align;
127 }
128
129
130 static gchar *
gpa_keygen_wizard_simple_get_text(GtkWidget * vbox)131 gpa_keygen_wizard_simple_get_text (GtkWidget *vbox)
132 {
133 GtkWidget *entry;
134
135 entry = g_object_get_data (G_OBJECT (vbox), "gpa_keygen_entry");
136 return string_strip_dup ((gchar *) gtk_entry_get_text (GTK_ENTRY (entry)));
137 }
138
139
140 static void
gpa_keygen_wizard_simple_grab_focus(GtkWidget * vbox)141 gpa_keygen_wizard_simple_grab_focus (GtkWidget *vbox)
142 {
143 GtkWidget *entry;
144
145 entry = g_object_get_data (G_OBJECT (vbox), "gpa_keygen_entry");
146 gtk_widget_grab_focus (entry);
147 }
148
149
150 static gboolean
name_validate_cb(GtkWidget * widget,gpointer data)151 name_validate_cb (GtkWidget *widget, gpointer data)
152 {
153 GPAKeyGenWizard *wizard = data;
154 const gchar *name;
155
156 name = gtk_entry_get_text (GTK_ENTRY (widget));
157 while (*name && g_unichar_isspace (g_utf8_get_char (name)))
158 name = g_utf8_next_char (name);
159 gtk_assistant_set_page_complete (GTK_ASSISTANT (wizard->window),
160 wizard->name_page,
161 !gpa_validate_gpg_name (name));
162
163 return FALSE;
164 }
165
166
167 static GtkWidget *
keygen_wizard_name_page(GPAKeyGenWizard * wizard)168 keygen_wizard_name_page (GPAKeyGenWizard *wizard)
169 {
170 GtkWidget *widget;
171 GtkWidget *entry;
172
173 widget = gpa_keygen_wizard_simple_page
174 (wizard,
175 _("Please insert your full name.\n\n"
176 "Your name will be part of the new key to make it easier for others"
177 " to identify keys."),
178 _("Your Name:"));
179
180 entry = g_object_get_data (G_OBJECT (widget), "gpa_keygen_entry");
181 g_signal_connect (G_OBJECT (entry), "changed",
182 G_CALLBACK (name_validate_cb), wizard);
183 return widget;
184 }
185
186
187 static gboolean
email_validate_cb(GtkWidget * widget,gpointer data)188 email_validate_cb (GtkWidget *widget, gpointer data)
189 {
190 GPAKeyGenWizard *wizard = data;
191 const gchar *email;
192
193 email = gtk_entry_get_text (GTK_ENTRY (widget));
194 while (*email && g_unichar_isspace (g_utf8_get_char (email)))
195 email = g_utf8_next_char (email);
196 gtk_assistant_set_page_complete (GTK_ASSISTANT (wizard->window),
197 wizard->email_page,
198 !gpa_validate_gpg_email (email));
199
200 return FALSE;
201 }
202
203
204 static GtkWidget *
keygen_wizard_email_page(GPAKeyGenWizard * wizard)205 keygen_wizard_email_page (GPAKeyGenWizard *wizard)
206 {
207 GtkWidget *widget;
208 GtkWidget *entry;
209
210 widget = gpa_keygen_wizard_simple_page
211 (wizard,
212 _("Please insert your email address.\n\n"
213 "Your email address will be part of the new key to make it easier"
214 " for others to identify keys. If you have several email addresses,"
215 " you can add further email addresses later."),
216 _("Your Email Address:"));
217
218 entry = g_object_get_data (G_OBJECT (widget), "gpa_keygen_entry");
219 g_signal_connect (G_OBJECT (entry), "changed",
220 G_CALLBACK (email_validate_cb), wizard);
221 return widget;
222 }
223
224 /* Backup copies and revocation certificate. */
225 static GtkWidget *
gpa_keygen_wizard_backup_page(GPAKeyGenWizard * wizard)226 gpa_keygen_wizard_backup_page (GPAKeyGenWizard *wizard)
227 {
228 GtkWidget *vbox;
229 GtkWidget *description;
230 GtkWidget *radio;
231
232 vbox = gtk_vbox_new (FALSE, 0);
233
234 description = gtk_label_new
235 (_("It is recommended that you create a backup copy of your new key,"
236 " once it has been generated.\n\n"
237 "Do you want to create a backup copy?"));
238 gtk_box_pack_start (GTK_BOX (vbox), description, TRUE, TRUE, 0);
239 gtk_misc_set_alignment (GTK_MISC (description), 0.0, 0.0);
240 gtk_label_set_line_wrap (GTK_LABEL (description), TRUE);
241 gtk_label_set_justify (GTK_LABEL (description), GTK_JUSTIFY_LEFT);
242
243 radio = gtk_radio_button_new_with_mnemonic (NULL, _("Create _backup copy"));
244 gtk_box_pack_start (GTK_BOX (vbox), radio, FALSE, TRUE, 5);
245 g_object_set_data (G_OBJECT (vbox), "gpa_keygen_backup", radio);
246
247 radio = gtk_radio_button_new_with_mnemonic_from_widget
248 (GTK_RADIO_BUTTON (radio), _("Do it _later"));
249 gtk_box_pack_start (GTK_BOX (vbox), radio, FALSE, TRUE, 5);
250
251 return vbox;
252 }
253
254
255 static GtkWidget *
gpa_keygen_wizard_message_page(const gchar * description_text)256 gpa_keygen_wizard_message_page (const gchar *description_text)
257 {
258 GtkWidget *vbox;
259 GtkWidget *description;
260
261 vbox = gtk_vbox_new (FALSE, 0);
262
263 description = gtk_label_new (description_text);
264 gtk_box_pack_start (GTK_BOX (vbox), description, TRUE, TRUE, 0);
265 gtk_misc_set_alignment (GTK_MISC (description), 0.0, 0.0);
266 gtk_label_set_line_wrap (GTK_LABEL (description), TRUE);
267 gtk_label_set_justify (GTK_LABEL (description), GTK_JUSTIFY_LEFT);
268
269 return vbox;
270 }
271
272
273 static GtkWidget *
gpa_keygen_wizard_wait_page(GPAKeyGenWizard * wizard)274 gpa_keygen_wizard_wait_page (GPAKeyGenWizard *wizard)
275 {
276 return gpa_keygen_wizard_message_page
277 (_("Your key is being generated.\n\n"
278 "Even on fast computers this may take a while. Please be patient."));
279 }
280
281
282 static GtkWidget *
gpa_keygen_wizard_final_page(GPAKeyGenWizard * keygen_wizard)283 gpa_keygen_wizard_final_page (GPAKeyGenWizard * keygen_wizard)
284 {
285 GtkWidget *widget;
286 char *desc;
287
288 desc = g_strdup_printf
289 (_("Congratulations!\n\n"
290 "You have successfully generated a key."
291 " The key is indefinitely valid and has a length of %d bits."),
292 STANDARD_KEY_LENGTH);
293 widget = gpa_keygen_wizard_message_page (desc);
294 g_free (desc);
295 return widget;
296 }
297
298
299 /* Extract the values the user entered and call gpa_generate_key.
300 Return TRUE if the key was created successfully. */
301 static gboolean
gpa_keygen_wizard_generate_action(gpointer data)302 gpa_keygen_wizard_generate_action (gpointer data)
303 //GtkAssistant *assistant, GtkWidget *page, gpointer data)
304 {
305 GPAKeyGenWizard *wizard = data;
306 gpa_keygen_para_t *para;
307 gboolean do_backup;
308 GtkWidget *radio;
309
310 para = gpa_keygen_para_new ();
311
312 /* Shall we make backups? */
313 radio = g_object_get_data (G_OBJECT (wizard->backup_page),
314 "gpa_keygen_backup");
315 do_backup = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (radio));
316
317 /* The User ID. */
318 para->name = gpa_keygen_wizard_simple_get_text (wizard->name_page);
319 para->email = gpa_keygen_wizard_simple_get_text (wizard->email_page);
320
321 /* Default values for newbie mode. */
322 para->algo = GPA_KEYGEN_ALGO_RSA_RSA;
323 para->keysize = STANDARD_KEY_LENGTH;
324
325 wizard->generate (para, do_backup, wizard->generate_data);
326
327 gpa_keygen_para_free (para);
328
329 return FALSE;
330 }
331
332
333
334 /* Handler for the close button. Destroy the main window */
335 static void
keygen_wizard_close(GtkWidget * widget,gpointer param)336 keygen_wizard_close (GtkWidget *widget, gpointer param)
337 {
338 GPAKeyGenWizard *wizard = param;
339
340 gtk_widget_destroy (wizard->window);
341 }
342
343
344 static void
free_keygen_wizard(gpointer data)345 free_keygen_wizard (gpointer data)
346 {
347 GPAKeyGenWizard *keygen_wizard = data;
348
349 g_free (keygen_wizard);
350 }
351
352
353 void
keygen_wizard_prepare_cb(GtkAssistant * assistant,GtkWidget * page,gpointer data)354 keygen_wizard_prepare_cb (GtkAssistant *assistant, GtkWidget *page,
355 gpointer data)
356 {
357 GPAKeyGenWizard *wizard = data;
358
359 if (page == wizard->name_page || page == wizard->email_page)
360 gpa_keygen_wizard_simple_grab_focus (page);
361 else if (page == wizard->wait_page)
362 gpa_keygen_wizard_generate_action (wizard);
363 }
364
365
366 GtkWidget *
gpa_keygen_wizard_new(GtkWidget * parent,GpaKeyGenWizardGenerateCb generate_action,gpointer data)367 gpa_keygen_wizard_new (GtkWidget *parent,
368 GpaKeyGenWizardGenerateCb generate_action,
369 gpointer data)
370 {
371 GPAKeyGenWizard *wizard;
372 GtkWidget *window;
373 GdkPixbuf *genkey_pixbuf;
374 GdkPixbuf *backup_pixbuf;
375
376
377 wizard = g_malloc (sizeof (*wizard));
378 genkey_pixbuf = gpa_create_icon_pixbuf ("wizard_genkey");
379 backup_pixbuf = gpa_create_icon_pixbuf ("wizard_backup");
380
381 wizard->generate = generate_action;
382 wizard->generate_data = data;
383
384
385 window = gtk_assistant_new ();
386 wizard->window = window;
387 gpa_window_set_title (GTK_WINDOW (window), _("Generate key"));
388 g_object_set_data_full (G_OBJECT (window), "user_data",
389 wizard, free_keygen_wizard);
390
391 /* Set the forward button to be the default. */
392 GTK_WIDGET_SET_FLAGS (GTK_ASSISTANT (window)->forward, GTK_CAN_DEFAULT);
393 gtk_window_set_default (GTK_WINDOW (window), GTK_ASSISTANT (window)->forward);
394
395 wizard->name_page = keygen_wizard_name_page (wizard);
396 gtk_assistant_append_page (GTK_ASSISTANT (window), wizard->name_page);
397 gtk_assistant_set_page_type (GTK_ASSISTANT (window), wizard->name_page,
398 GTK_ASSISTANT_PAGE_CONTENT);
399 gtk_assistant_set_page_title (GTK_ASSISTANT (window), wizard->name_page,
400 /* FIXME */ _("Generate key"));
401 gtk_assistant_set_page_side_image (GTK_ASSISTANT (window), wizard->name_page,
402 genkey_pixbuf);
403
404
405 wizard->email_page = keygen_wizard_email_page (wizard);
406 gtk_assistant_append_page (GTK_ASSISTANT (window), wizard->email_page);
407 gtk_assistant_set_page_type (GTK_ASSISTANT (window), wizard->email_page,
408 GTK_ASSISTANT_PAGE_CONTENT);
409 gtk_assistant_set_page_title (GTK_ASSISTANT (window), wizard->email_page,
410 /* FIXME */ _("Generate key"));
411 gtk_assistant_set_page_side_image (GTK_ASSISTANT (window), wizard->email_page,
412 genkey_pixbuf);
413
414 /* FIXME: A better GUI would have a "Generate backup" button on the
415 finish page after the key was generated. */
416 wizard->backup_page = gpa_keygen_wizard_backup_page (wizard);
417 gtk_assistant_append_page (GTK_ASSISTANT (window), wizard->backup_page);
418 gtk_assistant_set_page_type (GTK_ASSISTANT (window), wizard->backup_page,
419 GTK_ASSISTANT_PAGE_CONTENT);
420 gtk_assistant_set_page_title (GTK_ASSISTANT (window), wizard->backup_page,
421 /* FIXME */ _("Generate key"));
422 gtk_assistant_set_page_side_image (GTK_ASSISTANT (window),
423 wizard->backup_page,
424 backup_pixbuf);
425 gtk_assistant_set_page_complete (GTK_ASSISTANT (wizard->window),
426 wizard->backup_page, TRUE);
427
428
429 /* FIXME: We need to integrate the progress bar for the operation
430 into the page. Also, after the operation completes, the
431 assistant is destroyed, so the final page will never be
432 shown. The whole thing is upside down, and should be redone. */
433 wizard->wait_page = gpa_keygen_wizard_wait_page (wizard);
434 gtk_assistant_append_page (GTK_ASSISTANT (window), wizard->wait_page);
435 gtk_assistant_set_page_type (GTK_ASSISTANT (window), wizard->wait_page,
436 GTK_ASSISTANT_PAGE_PROGRESS);
437 gtk_assistant_set_page_title (GTK_ASSISTANT (window), wizard->wait_page,
438 /* FIXME */ _("Generate key"));
439 gtk_assistant_set_page_side_image (GTK_ASSISTANT (window), wizard->wait_page,
440 genkey_pixbuf);
441
442 /* The final page does not contain information about the generated
443 key. It should also offer a "generate backup" button, then the
444 backup page can be removed. */
445 wizard->final_page = gpa_keygen_wizard_final_page (wizard);
446 gtk_assistant_append_page (GTK_ASSISTANT (window), wizard->final_page);
447 gtk_assistant_set_page_type (GTK_ASSISTANT (window), wizard->final_page,
448 GTK_ASSISTANT_PAGE_SUMMARY);
449 gtk_assistant_set_page_title (GTK_ASSISTANT (window), wizard->final_page,
450 /* FIXME */ _("Generate key"));
451 gtk_assistant_set_page_side_image (GTK_ASSISTANT (window), wizard->final_page,
452 genkey_pixbuf);
453
454 g_signal_connect (G_OBJECT (window), "prepare",
455 G_CALLBACK (keygen_wizard_prepare_cb), wizard);
456 g_signal_connect (G_OBJECT (window), "close",
457 G_CALLBACK (keygen_wizard_close), wizard);
458 g_signal_connect (G_OBJECT (window), "cancel",
459 G_CALLBACK (keygen_wizard_close), wizard);
460
461
462 gtk_window_set_modal (GTK_WINDOW (window), TRUE);
463 gtk_window_set_transient_for (GTK_WINDOW (window), GTK_WINDOW (parent));
464 gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER_ON_PARENT);
465
466 g_object_unref (genkey_pixbuf);
467 g_object_unref (backup_pixbuf);
468
469 return window;
470 }
471