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